summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/.cvsignore13
-rw-r--r--sql/CMakeLists.txt441
-rw-r--r--sql/Makefile.am212
-rwxr-xr-xsql/add_errmsg4
-rw-r--r--sql/authors.h15
-rw-r--r--sql/client_settings.h35
-rw-r--r--sql/contributors.h8
-rw-r--r--sql/create_options.cc3
-rw-r--r--sql/create_options.h3
-rw-r--r--sql/custom_conf.h1
-rw-r--r--sql/datadict.cc234
-rw-r--r--sql/datadict.h41
-rw-r--r--sql/debug_sync.cc565
-rw-r--r--sql/debug_sync.h14
-rw-r--r--sql/derror.cc101
-rw-r--r--sql/derror.h25
-rw-r--r--sql/des_key_file.cc18
-rw-r--r--sql/des_key_file.h40
-rw-r--r--sql/discover.cc33
-rw-r--r--sql/discover.h24
-rw-r--r--sql/event_data_objects.cc99
-rw-r--r--sql/event_data_objects.h17
-rw-r--r--sql/event_db_repository.cc211
-rw-r--r--sql/event_db_repository.h10
-rw-r--r--sql/event_parse_data.cc11
-rw-r--r--sql/event_parse_data.h9
-rw-r--r--sql/event_queue.cc72
-rw-r--r--sql/event_queue.h17
-rw-r--r--sql/event_scheduler.cc193
-rw-r--r--sql/event_scheduler.h12
-rw-r--r--sql/events.cc371
-rw-r--r--sql/events.h70
-rw-r--r--[-rwxr-xr-x]sql/examples/CMakeLists.txt4
-rw-r--r--sql/field.cc1525
-rw-r--r--sql/field.h375
-rw-r--r--sql/field_conv.cc21
-rw-r--r--sql/filesort.cc347
-rw-r--r--sql/filesort.h38
-rw-r--r--sql/frm_crypt.cc7
-rw-r--r--sql/frm_crypt.h23
-rw-r--r--sql/gcalc_slicescan.cc6
-rw-r--r--sql/gcalc_tools.cc2
-rw-r--r--sql/gcalc_tools.h1
-rw-r--r--sql/gen_lex_hash.cc86
-rw-r--r--sql/gstream.cc11
-rw-r--r--sql/gstream.h15
-rw-r--r--sql/ha_ndbcluster.cc1166
-rw-r--r--sql/ha_ndbcluster.h40
-rw-r--r--sql/ha_ndbcluster_binlog.cc643
-rw-r--r--sql/ha_ndbcluster_binlog.h34
-rw-r--r--sql/ha_ndbcluster_cond.cc236
-rw-r--r--sql/ha_ndbcluster_cond.h14
-rw-r--r--sql/ha_ndbcluster_tables.h8
-rw-r--r--sql/ha_partition.cc2257
-rw-r--r--sql/ha_partition.h177
-rw-r--r--sql/handler.cc881
-rw-r--r--sql/handler.h552
-rw-r--r--sql/hash_filo.cc6
-rw-r--r--sql/hash_filo.h34
-rw-r--r--sql/hostname.cc671
-rw-r--r--sql/hostname.h30
-rw-r--r--sql/init.cc15
-rw-r--r--sql/init.h24
-rw-r--r--sql/innodb_priv.h36
-rw-r--r--sql/item.cc1010
-rw-r--r--sql/item.h582
-rw-r--r--sql/item_buff.cc12
-rw-r--r--sql/item_cmpfunc.cc425
-rw-r--r--sql/item_cmpfunc.h79
-rw-r--r--sql/item_create.cc219
-rw-r--r--sql/item_create.h10
-rw-r--r--sql/item_func.cc1104
-rw-r--r--sql/item_func.h242
-rw-r--r--sql/item_geofunc.cc16
-rw-r--r--sql/item_geofunc.h19
-rw-r--r--sql/item_row.cc16
-rw-r--r--sql/item_row.h10
-rw-r--r--sql/item_strfunc.cc847
-rw-r--r--sql/item_strfunc.h219
-rw-r--r--sql/item_subselect.cc216
-rw-r--r--sql/item_subselect.h53
-rw-r--r--sql/item_sum.cc1428
-rw-r--r--sql/item_sum.h609
-rw-r--r--sql/item_timefunc.cc309
-rw-r--r--sql/item_timefunc.h169
-rw-r--r--sql/item_xmlfunc.cc44
-rw-r--r--sql/item_xmlfunc.h9
-rw-r--r--sql/key.cc95
-rw-r--r--sql/key.h46
-rw-r--r--sql/keycaches.cc165
-rw-r--r--sql/keycaches.h45
-rw-r--r--sql/lex.h48
-rw-r--r--sql/lex_symbol.h1
-rw-r--r--sql/lock.cc1266
-rw-r--r--sql/lock.h51
-rw-r--r--sql/log.cc2958
-rw-r--r--sql/log.h370
-rw-r--r--sql/log_event.cc1317
-rw-r--r--sql/log_event.h481
-rw-r--r--sql/log_event_old.cc312
-rw-r--r--sql/log_event_old.h9
-rw-r--r--sql/log_slow.h74
-rw-r--r--sql/main.cc26
-rw-r--r--sql/mdl.cc2919
-rw-r--r--sql/mdl.h875
-rw-r--r--sql/message.h8
-rw-r--r--sql/mf_iocache.cc7
-rw-r--r--sql/multi_range_read.cc41
-rw-r--r--sql/multi_range_read.h18
-rw-r--r--sql/my_decimal.cc79
-rw-r--r--sql/my_decimal.h81
-rw-r--r--sql/my_lock.c91
-rw-r--r--sql/mysql_install_db.cc22
-rw-r--r--sql/mysql_priv.h2942
-rw-r--r--sql/mysqld.cc6447
-rw-r--r--sql/mysqld.h560
-rw-r--r--sql/mysqld_suffix.h9
-rw-r--r--sql/net_serv.cc169
-rw-r--r--sql/nt_servc.cc32
-rw-r--r--sql/nt_servc.h15
-rw-r--r--sql/opt_index_cond_pushdown.cc20
-rw-r--r--sql/opt_range.cc1400
-rw-r--r--sql/opt_range.h126
-rw-r--r--sql/opt_range_mrr.cc20
-rw-r--r--sql/opt_subselect.cc105
-rw-r--r--sql/opt_subselect.h24
-rw-r--r--sql/opt_sum.cc52
-rw-r--r--sql/opt_table_elimination.cc26
-rw-r--r--sql/parse_file.cc41
-rw-r--r--sql/parse_file.h15
-rw-r--r--sql/partition_element.h48
-rw-r--r--sql/partition_info.cc1646
-rw-r--r--sql/partition_info.h119
-rw-r--r--sql/password.c7
-rw-r--r--sql/procedure.cc6
-rw-r--r--sql/procedure.h20
-rw-r--r--sql/protocol.cc468
-rw-r--r--sql/protocol.h61
-rw-r--r--sql/records.cc109
-rw-r--r--sql/records.h86
-rw-r--r--sql/repl_failsafe.cc893
-rw-r--r--sql/repl_failsafe.h27
-rw-r--r--sql/replication.h566
-rw-r--r--sql/rpl_filter.cc264
-rw-r--r--sql/rpl_filter.h34
-rw-r--r--sql/rpl_handler.cc515
-rw-r--r--sql/rpl_handler.h213
-rw-r--r--sql/rpl_injector.cc34
-rw-r--r--sql/rpl_injector.h6
-rw-r--r--sql/rpl_mi.cc310
-rw-r--r--sql/rpl_mi.h37
-rw-r--r--sql/rpl_record.cc132
-rw-r--r--sql/rpl_record.h19
-rw-r--r--sql/rpl_record_old.cc7
-rw-r--r--sql/rpl_record_old.h6
-rw-r--r--sql/rpl_reporting.cc21
-rw-r--r--sql/rpl_reporting.h19
-rw-r--r--sql/rpl_rli.cc275
-rw-r--r--sql/rpl_rli.h114
-rw-r--r--sql/rpl_tblmap.cc29
-rw-r--r--sql/rpl_tblmap.h15
-rw-r--r--sql/rpl_utility.cc1020
-rw-r--r--sql/rpl_utility.h204
-rw-r--r--sql/scheduler.cc766
-rw-r--r--sql/scheduler.h103
-rw-r--r--sql/set_var.cc4432
-rw-r--r--sql/set_var.h1522
-rw-r--r--sql/sha2.cc68
-rw-r--r--sql/share/.cvsignore2
-rw-r--r--sql/share/CMakeLists.txt13
-rw-r--r--sql/share/Makefile.am60
-rw-r--r--sql/share/charsets/Index.xml1
-rw-r--r--sql/share/charsets/armscii8.xml1
-rw-r--r--sql/share/charsets/ascii.xml1
-rw-r--r--sql/share/charsets/cp1250.xml1
-rw-r--r--sql/share/charsets/cp1256.xml1
-rw-r--r--sql/share/charsets/cp1257.xml1
-rw-r--r--sql/share/charsets/cp850.xml1
-rw-r--r--sql/share/charsets/cp852.xml1
-rw-r--r--sql/share/charsets/cp866.xml1
-rw-r--r--sql/share/charsets/dec8.xml1
-rw-r--r--sql/share/charsets/geostd8.xml1
-rw-r--r--sql/share/charsets/greek.xml1
-rw-r--r--sql/share/charsets/hebrew.xml1
-rw-r--r--sql/share/charsets/hp8.xml1
-rw-r--r--sql/share/charsets/keybcs2.xml1
-rw-r--r--sql/share/charsets/koi8r.xml1
-rw-r--r--sql/share/charsets/koi8u.xml1
-rw-r--r--sql/share/charsets/languages.html17
-rw-r--r--sql/share/charsets/latin1.xml1
-rw-r--r--sql/share/charsets/latin2.xml1
-rw-r--r--sql/share/charsets/latin5.xml1
-rw-r--r--sql/share/charsets/latin7.xml1
-rw-r--r--sql/share/charsets/macce.xml1
-rw-r--r--sql/share/charsets/macroman.xml1
-rw-r--r--sql/share/charsets/swe7.xml1
-rw-r--r--sql/share/errmsg-utf8.txt6567
-rw-r--r--sql/share/errmsg.txt6299
-rw-r--r--sql/signal_handler.cc61
-rw-r--r--sql/slave.cc1727
-rw-r--r--sql/slave.h65
-rw-r--r--sql/sp.cc1070
-rw-r--r--sql/sp.h167
-rw-r--r--sql/sp_cache.cc139
-rw-r--r--sql/sp_cache.h12
-rw-r--r--sql/sp_head.cc861
-rw-r--r--sql/sp_head.h123
-rw-r--r--sql/sp_pcontext.cc57
-rw-r--r--sql/sp_pcontext.h20
-rw-r--r--sql/sp_rcontext.cc277
-rw-r--r--sql/sp_rcontext.h142
-rw-r--r--sql/spatial.cc50
-rw-r--r--sql/spatial.h8
-rw-r--r--sql/sql_acl.cc2552
-rw-r--r--sql/sql_acl.h176
-rw-r--r--sql/sql_admin.cc1144
-rw-r--r--sql/sql_admin.h134
-rw-r--r--sql/sql_alter.cc109
-rw-r--r--sql/sql_alter.h66
-rw-r--r--sql/sql_analyse.cc15
-rw-r--r--sql/sql_analyse.h13
-rw-r--r--sql/sql_array.h11
-rw-r--r--sql/sql_audit.cc584
-rw-r--r--sql/sql_audit.h280
-rw-r--r--sql/sql_base.cc7768
-rw-r--r--sql/sql_base.h648
-rw-r--r--sql/sql_binlog.cc44
-rw-r--r--sql/sql_binlog.h23
-rw-r--r--sql/sql_bitmap.h22
-rw-r--r--sql/sql_builtin.cc.in22
-rw-r--r--sql/sql_cache.cc692
-rw-r--r--sql/sql_cache.h161
-rw-r--r--sql/sql_callback.h42
-rw-r--r--sql/sql_class.cc2397
-rw-r--r--sql/sql_class.h1873
-rw-r--r--sql/sql_client.cc10
-rw-r--r--sql/sql_connect.cc306
-rw-r--r--sql/sql_connect.h73
-rw-r--r--sql/sql_const.h257
-rw-r--r--sql/sql_crypt.cc11
-rw-r--r--sql/sql_crypt.h15
-rw-r--r--sql/sql_cursor.cc480
-rw-r--r--sql/sql_cursor.h26
-rw-r--r--sql/sql_db.cc805
-rw-r--r--sql/sql_db.h48
-rw-r--r--sql/sql_delete.cc367
-rw-r--r--sql/sql_delete.h32
-rw-r--r--sql/sql_derived.cc53
-rw-r--r--sql/sql_derived.h39
-rw-r--r--sql/sql_do.cc16
-rw-r--r--sql/sql_do.h26
-rw-r--r--sql/sql_error.cc802
-rw-r--r--sql/sql_error.h596
-rw-r--r--sql/sql_expression_cache.cc12
-rw-r--r--sql/sql_expression_cache.h16
-rw-r--r--sql/sql_handler.cc549
-rw-r--r--sql/sql_handler.h27
-rw-r--r--sql/sql_help.cc69
-rw-r--r--sql/sql_help.h28
-rw-r--r--sql/sql_hset.h97
-rw-r--r--sql/sql_insert.cc1625
-rw-r--r--sql/sql_insert.h49
-rw-r--r--sql/sql_join_cache.cc3
-rw-r--r--sql/sql_join_cache.h20
-rw-r--r--sql/sql_lex.cc728
-rw-r--r--sql/sql_lex.h1088
-rw-r--r--sql/sql_lifo_buffer.h16
-rw-r--r--sql/sql_list.cc6
-rw-r--r--sql/sql_list.h65
-rw-r--r--sql/sql_load.cc737
-rw-r--r--sql/sql_load.h34
-rw-r--r--sql/sql_locale.cc899
-rw-r--r--sql/sql_locale.h78
-rw-r--r--sql/sql_manager.cc55
-rw-r--r--sql/sql_manager.h23
-rw-r--r--sql/sql_map.cc140
-rw-r--r--sql/sql_map.h62
-rw-r--r--sql/sql_olap.cc188
-rw-r--r--sql/sql_parse.cc4226
-rw-r--r--sql/sql_parse.h207
-rw-r--r--sql/sql_partition.cc3396
-rw-r--r--sql/sql_partition.h142
-rw-r--r--sql/sql_partition_admin.cc195
-rw-r--r--sql/sql_partition_admin.h236
-rw-r--r--sql/sql_plist.h290
-rw-r--r--sql/sql_plugin.cc1771
-rw-r--r--sql/sql_plugin.h62
-rw-r--r--sql/sql_plugin_compat.h65
-rw-r--r--sql/sql_plugin_services.h45
-rw-r--r--sql/sql_prepare.cc1185
-rw-r--r--sql/sql_prepare.h368
-rw-r--r--sql/sql_priv.h435
-rw-r--r--sql/sql_profile.cc62
-rw-r--r--sql/sql_profile.h25
-rw-r--r--sql/sql_reload.cc527
-rw-r--r--sql/sql_reload.h26
-rw-r--r--sql/sql_rename.cc61
-rw-r--r--sql/sql_rename.h27
-rw-r--r--sql/sql_repl.cc1049
-rw-r--r--sql/sql_repl.h15
-rw-r--r--sql/sql_select.cc2866
-rw-r--r--sql/sql_select.h164
-rw-r--r--sql/sql_servers.cc161
-rw-r--r--sql/sql_servers.h12
-rw-r--r--sql/sql_show.cc2770
-rw-r--r--sql/sql_show.h101
-rw-r--r--sql/sql_signal.cc513
-rw-r--r--sql/sql_signal.h153
-rw-r--r--sql/sql_sort.h22
-rw-r--r--sql/sql_state.c3
-rw-r--r--sql/sql_string.cc325
-rw-r--r--sql/sql_string.h77
-rw-r--r--sql/sql_table.cc3936
-rw-r--r--sql/sql_table.h226
-rw-r--r--sql/sql_tablespace.cc17
-rw-r--r--sql/sql_tablespace.h24
-rw-r--r--sql/sql_test.cc170
-rw-r--r--sql/sql_test.h39
-rw-r--r--sql/sql_time.cc (renamed from sql/time.cc)159
-rw-r--r--sql/sql_time.h132
-rw-r--r--sql/sql_trigger.cc271
-rw-r--r--sql/sql_trigger.h53
-rw-r--r--sql/sql_truncate.cc555
-rw-r--r--sql/sql_truncate.h70
-rw-r--r--sql/sql_udf.cc173
-rw-r--r--sql/sql_udf.h9
-rw-r--r--sql/sql_union.cc94
-rw-r--r--sql/sql_union.h31
-rw-r--r--sql/sql_update.cc556
-rw-r--r--sql/sql_update.h44
-rw-r--r--sql/sql_view.cc301
-rw-r--r--sql/sql_view.h21
-rw-r--r--sql/sql_yacc.yy3347
-rw-r--r--sql/strfunc.cc298
-rw-r--r--sql/strfunc.h49
-rw-r--r--sql/structs.h83
-rw-r--r--sql/sys_vars.cc3935
-rw-r--r--sql/sys_vars.h1883
-rw-r--r--sql/sys_vars_shared.h86
-rw-r--r--sql/table.cc1542
-rw-r--r--sql/table.h744
-rw-r--r--sql/thr_malloc.cc19
-rw-r--r--sql/thr_malloc.h34
-rw-r--r--sql/threadpool.h70
-rw-r--r--sql/threadpool_common.cc282
-rw-r--r--sql/threadpool_unix.cc1680
-rw-r--r--sql/threadpool_win.cc747
-rw-r--r--sql/transaction.cc835
-rw-r--r--sql/transaction.h47
-rw-r--r--sql/tzfile.h10
-rw-r--r--sql/tztime.cc355
-rw-r--r--sql/tztime.h20
-rw-r--r--sql/udf_example.c25
-rw-r--r--sql/uniques.cc26
-rw-r--r--sql/unireg.cc271
-rw-r--r--sql/unireg.h117
-rw-r--r--sql/winservice.c18
-rw-r--r--sql/winservice.h16
358 files changed, 92774 insertions, 56824 deletions
diff --git a/sql/.cvsignore b/sql/.cvsignore
deleted file mode 100644
index 7fcd82ab952..00000000000
--- a/sql/.cvsignore
+++ /dev/null
@@ -1,13 +0,0 @@
-.deps
-.libs
-Makefile
-Makefile.in
-deadlock_test.c
-gen_lex_hash
-lex_hash.h
-mysqlbinlog
-mysqlbinlog
-mysqld
-sql_yacc.cc
-sql_yacc.hh
-sql_yacc.h
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 9a620775c85..11951971c73 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2006, 2011, Oracle and/or its affiliates.
+# Copyright (c) 2006, 2013, Oracle and/or its affiliates.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -11,215 +11,275 @@
#
# 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("${PROJECT_SOURCE_DIR}/win/mysql_manifest.cmake")
-
-ADD_DEFINITIONS(-DUSE_SYMDIR)
-
-INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include
- ${CMAKE_SOURCE_DIR}/extra/yassl/include
- ${CMAKE_SOURCE_DIR}/sql
- ${CMAKE_SOURCE_DIR}/regex
- ${CMAKE_SOURCE_DIR}/zlib
- ${CMAKE_SOURCE_DIR}/extra/libevent
- ${CMAKE_BINARY_DIR}/extra/libevent
- ${CMAKE_CURRENT_BINARY_DIR}
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+INCLUDE_DIRECTORIES(
+${CMAKE_SOURCE_DIR}/include
+${CMAKE_SOURCE_DIR}/sql
+${CMAKE_SOURCE_DIR}/regex
+${ZLIB_INCLUDE_DIR}
+${SSL_INCLUDE_DIRS}
+${CMAKE_BINARY_DIR}/sql
+)
+
+SET(GEN_SOURCES
+${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.h
+${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.cc
+${CMAKE_CURRENT_BINARY_DIR}/lex_hash.h
)
-SET_SOURCE_FILES_PROPERTIES(${CMAKE_BINARY_DIR}/sql/sql_yacc.h
- ${CMAKE_BINARY_DIR}/sql/sql_yacc.cc
- ${CMAKE_BINARY_DIR}/include/mysql_version.h
- ${CMAKE_BINARY_DIR}/sql/sql_builtin.cc
- ${CMAKE_BINARY_DIR}/sql/lex_hash.h
- ${CMAKE_BINARY_DIR}/include/mysqld_error.h
- ${CMAKE_BINARY_DIR}/include/mysqld_ername.h
- ${CMAKE_BINARY_DIR}/include/sql_state.h
- PROPERTIES GENERATED 1)
-
-ADD_DEFINITIONS(-DMYSQL_SERVER -D_CONSOLE -DHAVE_EVENT_SCHEDULER)
-IF(WITH_FEEDBACK_STORAGE_ENGINE)
- ADD_DEFINITIONS(-DWITH_FEEDBACK_PLUGIN)
+SET_SOURCE_FILES_PROPERTIES(${GEN_SOURCES} PROPERTIES GENERATED 1)
+
+ADD_DEFINITIONS(-DMYSQL_SERVER -DHAVE_EVENT_SCHEDULER)
+
+IF(SSL_DEFINES)
+ ADD_DEFINITIONS(${SSL_DEFINES})
ENDIF()
SET (SQL_SOURCE
- ../sql-common/client.c derror.cc des_key_file.cc
+ ../sql-common/client.c derror.cc des_key_file.cc
discover.cc ../libmysql/errmsg.c field.cc field_conv.cc
- filesort.cc gstream.cc
- ha_partition.cc
- handler.cc hash_filo.cc hash_filo.h sql_plugin_services.h
+ filesort.cc gstream.cc sha2.cc
+ signal_handler.cc
+ handler.cc hash_filo.h sql_plugin_services.h
hostname.cc init.cc item.cc item_buff.cc item_cmpfunc.cc
item_create.cc item_func.cc item_geofunc.cc item_row.cc
item_strfunc.cc item_subselect.cc item_sum.cc item_timefunc.cc
- key.cc log.cc lock.cc
+ key.cc log.cc lock.cc
log_event.cc rpl_record.cc rpl_reporting.cc
log_event_old.cc rpl_record_old.cc
message.h mf_iocache.cc my_decimal.cc ../sql-common/my_time.c
- mysqld.cc net_serv.cc ../sql-common/client_plugin.c
- nt_servc.cc nt_servc.h opt_range.cc opt_range.h opt_sum.cc
+ mysqld.cc net_serv.cc keycaches.cc
+ ../sql-common/client_plugin.c
+ opt_range.cc opt_range.h opt_sum.cc
../sql-common/pack.c parse_file.cc password.c procedure.cc
protocol.cc records.cc repl_failsafe.cc rpl_filter.cc set_var.cc
slave.cc sp.cc sp_cache.cc sp_head.cc sp_pcontext.cc
sp_rcontext.cc spatial.cc sql_acl.cc sql_analyse.cc sql_base.cc
sql_cache.cc sql_class.cc sql_client.cc sql_crypt.cc sql_crypt.h
sql_cursor.cc sql_db.cc sql_delete.cc sql_derived.cc sql_do.cc
- sql_error.cc sql_handler.cc sql_help.cc sql_insert.cc
- sql_lifo_buffer.h
- sql_join_cache.h sql_join_cache.cc
- sql_lex.cc sql_list.cc sql_load.cc sql_manager.cc
- sql_map.cc sql_parse.cc sql_partition.cc sql_plugin.cc
- sql_prepare.cc sql_rename.cc
+ sql_error.cc sql_handler.cc sql_help.cc sql_insert.cc sql_lex.cc
+ sql_list.cc sql_load.cc sql_manager.cc sql_parse.cc
+ sql_partition.cc sql_plugin.cc sql_prepare.cc sql_rename.cc
debug_sync.cc debug_sync.h
- signal_handler.cc
- sql_repl.cc sql_select.cc sql_show.cc sql_state.c sql_string.cc
+ sql_repl.cc sql_select.cc sql_show.cc sql_state.c sql_string.cc
sql_table.cc sql_test.cc sql_trigger.cc sql_udf.cc sql_union.cc
sql_update.cc sql_view.cc strfunc.cc table.cc thr_malloc.cc
- time.cc tztime.cc uniques.cc unireg.cc item_xmlfunc.cc
+ sql_time.cc tztime.cc uniques.cc unireg.cc item_xmlfunc.cc
rpl_tblmap.cc sql_binlog.cc event_scheduler.cc event_data_objects.cc
event_queue.cc event_db_repository.cc
sql_tablespace.cc events.cc ../sql-common/my_user.c
partition_info.cc rpl_utility.cc rpl_injector.cc sql_locale.cc
- rpl_rli.cc rpl_mi.cc sql_servers.cc
- sql_connect.cc scheduler.cc
- sql_profile.cc event_parse_data.cc opt_table_elimination.cc
+ rpl_rli.cc rpl_mi.cc sql_servers.cc sql_audit.cc
+ sql_connect.cc scheduler.cc sql_partition_admin.cc
+ sql_profile.cc event_parse_data.cc sql_alter.cc
+ sql_signal.cc rpl_handler.cc mdl.cc sql_admin.cc
+ transaction.cc sys_vars.cc sql_truncate.cc datadict.cc
+ sql_reload.cc
+
+ # added in MariaDB:
+ sql_lifo_buffer.h sql_join_cache.h sql_join_cache.cc
+ create_options.cc multi_range_read.cc
+ opt_index_cond_pushdown.cc opt_subselect.cc
+ opt_table_elimination.cc sql_expression_cache.cc
gcalc_slicescan.cc gcalc_tools.cc
- multi_range_read.cc
- opt_subselect.cc
- opt_index_cond_pushdown.cc
- create_options.cc
- sql_expression_cache.cc
- ${CMAKE_BINARY_DIR}/sql/sql_yacc.cc
- ${CMAKE_BINARY_DIR}/sql/sql_yacc.h
- ${CMAKE_BINARY_DIR}/include/mysqld_error.h
- ${CMAKE_BINARY_DIR}/include/mysqld_ername.h
- ${CMAKE_BINARY_DIR}/include/sql_state.h
- ${CMAKE_BINARY_DIR}/include/mysql_version.h
- ${CMAKE_BINARY_DIR}/sql/sql_builtin.cc
- ${CMAKE_BINARY_DIR}/sql/lex_hash.h)
-
-ADD_LIBRARY(sql ${SQL_SOURCE})
-
-CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/win/cmake/dummy.in cmake_dummy.cc COPYONLY)
-MYSQL_ADD_EXECUTABLE(mysqld cmake_dummy.cc message.rc DESTINATION ${INSTALL_SBINDIR} COMPONENT Server)
-INSTALL_DEBUG_TARGET(mysqld
- DESTINATION ${INSTALL_SBINDIR}
- PDB_DESTINATION ${INSTALL_SBINDIR}/debug
- RENAME mysqld-debug)
+ threadpool_common.cc
+ ../sql-common/mysql_async.c
+ ${CMAKE_CURRENT_BINARY_DIR}/sql_builtin.cc
+ ${GEN_SOURCES}
+ ${MYSYS_LIBWRAP_SOURCE}
+ )
+
+IF (CMAKE_SYSTEM_NAME MATCHES "Linux" OR
+ CMAKE_SYSTEM_NAME MATCHES "Windows" OR
+ CMAKE_SYSTEM_NAME MATCHES "SunOS" OR
+ HAVE_KQUEUE)
+ ADD_DEFINITIONS(-DHAVE_POOL_OF_THREADS)
+ IF(WIN32)
+ SET(SQL_SOURCE ${SQL_SOURCE} threadpool_win.cc)
+ ELSE()
+ SET(SQL_SOURCE ${SQL_SOURCE} threadpool_unix.cc)
+ ENDIF()
+ENDIF()
+
+MYSQL_ADD_PLUGIN(partition ha_partition.cc STORAGE_ENGINE DEFAULT 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 dbug strings vio regex ${LIBJEMALLOC}
+ ${LIBWRAP} ${LIBCRYPT} ${LIBDL} ${CMAKE_THREAD_LIBS_INIT}
+ ${SSL_LIBRARIES})
+
+IF(WIN32)
+ SET(MYSQLD_SOURCE main.cc nt_servc.cc nt_servc.h message.rc)
+ TARGET_LINK_LIBRARIES(sql psapi)
+ELSE()
+ SET(MYSQLD_SOURCE main.cc ${DTRACE_PROBES_ALL})
+ENDIF()
-SET_TARGET_PROPERTIES(mysqld PROPERTIES OUTPUT_NAME mysqld${MYSQLD_EXE_SUFFIX})
-SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE)
+MYSQL_ADD_EXECUTABLE(mysqld ${MYSQLD_SOURCE} DESTINATION ${INSTALL_SBINDIR} COMPONENT Server)
+
+IF(APPLE)
+ # Add CoreServices framework since some dloadable plugins may need it
+ FIND_LIBRARY(CORESERVICES NAMES CoreServices)
+ IF(CORESERVICES)
+ TARGET_LINK_LIBRARIES(mysqld ${CORESERVICES})
+ ENDIF()
+ENDIF()
+
+IF(NOT WITHOUT_DYNAMIC_PLUGINS)
+ SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE)
+ GET_TARGET_PROPERTY(mysqld_link_flags mysqld LINK_FLAGS)
+ IF(NOT mysqld_link_flags)
+ SET(mysqld_link_flags)
+ ENDIF()
+ IF (MINGW OR CYGWIN)
+ SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_FLAGS "${mysqld_link_flags} -Wl,--export-all-symbols")
+ ENDIF()
+ IF(MSVC)
+ # Set module definition file. Also use non-incremental linker,
+ # incremental appears to crash from time to time,if used with /DEF option
+ SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_FLAGS "${mysqld_link_flags} /DEF:mysqld.def /INCREMENTAL:NO")
-SET (MYSQLD_CORE_LIBS mysys zlib dbug strings yassl taocrypt vio regex sql libevent)
-TARGET_LINK_LIBRARIES(mysqld ${MYSQLD_CORE_LIBS} ${MYSQLD_STATIC_ENGINE_LIBS})
-TARGET_LINK_LIBRARIES(mysqld ws2_32.lib psapi.lib)
+ FOREACH (CORELIB sql mysys dbug strings)
+ GET_TARGET_PROPERTY(LOC ${CORELIB} LOCATION)
+ FILE(TO_NATIVE_PATH ${LOC} LOC)
+ SET (LIB_LOCATIONS ${LIB_LOCATIONS} ${LOC})
+ ENDFOREACH (CORELIB ${MYSQLD_CORE_LIBS})
+ SET(_PLATFORM x86)
+ IF(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ SET(_PLATFORM x64)
+ ENDIF()
+ ADD_CUSTOM_COMMAND(TARGET mysqld PRE_LINK
+ COMMAND echo ${_PLATFORM} && cscript ARGS //nologo ${PROJECT_SOURCE_DIR}/win/create_def_file.js
+ ${_PLATFORM} ${LIB_LOCATIONS} > mysqld.def
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+ ADD_DEPENDENCIES(sql GenError)
+ ENDIF(MSVC)
+ENDIF(NOT WITHOUT_DYNAMIC_PLUGINS)
+SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE)
+TARGET_LINK_LIBRARIES(mysqld sql)
-IF(MSVC AND NOT WITHOUT_DYNAMIC_PLUGINS)
- # Set module definition file. Also use non-incremental linker,
- # incremental appears to crash from time to time,if used with /DEF option
- SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_FLAGS "/DEF:mysqld.def /INCREMENTAL:NO")
+# Provide plugins with minimal set of libraries
+SET(INTERFACE_LIBS ${LIBRT})
+IF(INTERFACE_LIBS)
+ SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_INTERFACE_LIBRARIES
+ "${INTERFACE_LIBS}")
+ENDIF()
- FOREACH (CORELIB ${MYSQLD_CORE_LIBS})
- GET_TARGET_PROPERTY(LOC ${CORELIB} LOCATION)
- FILE(TO_NATIVE_PATH ${LOC} LOC)
- SET (LIB_LOCATIONS ${LIB_LOCATIONS} ${LOC})
- ENDFOREACH (CORELIB ${MYSQLD_CORE_LIBS})
+# On Solaris, some extra effort is required in order to get dtrace probes
+# from static libraries
+DTRACE_INSTRUMENT_STATIC_LIBS(mysqld
+ "sql;mysys;${MYSQLD_STATIC_PLUGIN_LIBS}")
- ADD_CUSTOM_COMMAND(TARGET mysqld PRE_LINK
- COMMAND cscript ARGS //nologo ${PROJECT_SOURCE_DIR}/win/create_def_file.js
- ${PLATFORM} ${LIB_LOCATIONS} > mysqld.def
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
-ENDIF(MSVC AND NOT WITHOUT_DYNAMIC_PLUGINS)
-ADD_DEPENDENCIES(sql GenError)
+SET(WITH_MYSQLD_LDFLAGS "" CACHE STRING "Additional linker flags for mysqld")
+MARK_AS_ADVANCED(WITH_MYSQLD_LDFLAGS)
+IF(WITH_MYSQLD_LDFLAGS)
+ GET_TARGET_PROPERTY(mysqld LINK_FLAGS MYSQLD_LINK_FLAGS)
+ IF(NOT MYSQLD_LINK_FLAGS)
+ SET(MYSQLD_LINK_FLAGS)
+ ENDIF()
+ SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_FLAGS
+ "${MYSQLD_LINK_FLAGS} ${WITH_MYSQLD_LDFLAGS}")
+ENDIF()
+INSTALL_DEBUG_TARGET(mysqld
+ DESTINATION ${INSTALL_SBINDIR}
+ PDB_DESTINATION ${INSTALL_SBINDIR}/debug
+ RENAME mysqld-debug)
+
+INCLUDE(${CMAKE_SOURCE_DIR}/cmake/bison.cmake)
-# Sql Parser custom command
-ADD_CUSTOM_COMMAND(
- OUTPUT ${CMAKE_BINARY_DIR}/sql/sql_yacc.h
- ${CMAKE_BINARY_DIR}/sql/sql_yacc.cc
- COMMAND bison ARGS -y -p MYSQL --defines=sql_yacc.h
- --output=sql_yacc.cc "${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc.yy"
- DEPENDS ${PROJECT_SOURCE_DIR}/sql/sql_yacc.yy)
+# 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)
+ ENDIF()
+ 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
+)
# Gen_lex_hash
ADD_EXECUTABLE(gen_lex_hash gen_lex_hash.cc)
-TARGET_LINK_LIBRARIES(gen_lex_hash debug dbug mysqlclient strings wsock32)
-GET_TARGET_PROPERTY(GEN_LEX_HASH_EXE gen_lex_hash LOCATION)
+
ADD_CUSTOM_COMMAND(
- OUTPUT ${CMAKE_BINARY_DIR}/sql/lex_hash.h
- COMMAND ${GEN_LEX_HASH_EXE} ARGS > lex_hash.h
- DEPENDS ${GEN_LEX_HASH_EXE})
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lex_hash.h
+ COMMAND gen_lex_hash > lex_hash.h
+ DEPENDS gen_lex_hash
+)
-ADD_CUSTOM_TARGET(
- GenServerSource ALL
- DEPENDS ${CMAKE_BINARY_DIR}/sql/sql_yacc.h
- ${CMAKE_BINARY_DIR}/sql/sql_yacc.cc
- ${CMAKE_BINARY_DIR}/sql/lex_hash.h)
+MYSQL_ADD_EXECUTABLE(mysql_tzinfo_to_sql tztime.cc COMPONENT Server)
+SET_TARGET_PROPERTIES(mysql_tzinfo_to_sql PROPERTIES COMPILE_FLAGS "-DTZINFO2SQL")
+TARGET_LINK_LIBRARIES(mysql_tzinfo_to_sql mysys)
-ADD_DEPENDENCIES(sql GenServerSource)
+ADD_CUSTOM_TARGET(
+ GenServerSource
+ DEPENDS ${GEN_SOURCES}
+)
-# Remove the auto-generated files as part of 'Clean Solution'
-SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES
- "lex_hash.h;sql_yacc.h;sql_yacc.cc;mysqld.def")
+#Need this only for embedded
+SET_TARGET_PROPERTIES(GenServerSource PROPERTIES EXCLUDE_FROM_ALL TRUE)
-ADD_LIBRARY(udf_example MODULE udf_example.c udf_example.def)
-ADD_DEPENDENCIES(udf_example strings GenError)
-TARGET_LINK_LIBRARIES(udf_example strings wsock32)
+IF(WIN32 OR HAVE_DLOPEN AND NOT DISABLE_SHARED)
+ ADD_LIBRARY(udf_example MODULE udf_example.c)
+ SET_TARGET_PROPERTIES(udf_example PROPERTIES PREFIX "")
+ # udf_example depends on strings
+ IF(WIN32)
+ IF(MSVC)
+ SET_TARGET_PROPERTIES(udf_example PROPERTIES LINK_FLAGS "/DEF:${CMAKE_CURRENT_SOURCE_DIR}/udf_example.def")
+ ENDIF()
+ TARGET_LINK_LIBRARIES(udf_example strings)
+ ELSE()
+ # udf_example is using safemutex exported by mysqld
+ TARGET_LINK_LIBRARIES(udf_example mysqld)
+ ENDIF()
+ENDIF()
-ADD_SUBDIRECTORY(share)
+FOREACH(tool glibtoolize libtoolize aclocal autoconf autoheader automake gtar
+ tar bzr)
+ STRING(TOUPPER ${tool} TOOL)
+ FIND_PROGRAM(${TOOL}_EXECUTABLE ${tool} DOC "path to the executable")
+ MARK_AS_ADVANCED(${TOOL}_EXECUTABLE)
+ENDFOREACH()
-IF(WIN32)
- SET(my_bootstrap_sql ${CMAKE_CURRENT_BINARY_DIR}/my_bootstrap.sql)
- FILE(TO_NATIVE_PATH ${my_bootstrap_sql} native_outfile)
+CONFIGURE_FILE(
+ ${CMAKE_SOURCE_DIR}/cmake/make_dist.cmake.in ${CMAKE_BINARY_DIR}/make_dist.cmake @ONLY)
- # Create bootstrapper SQL script
- 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 ${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
- )
-
-ADD_CUSTOM_COMMAND(
- OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/mysql_bootstrap_sql.c
- COMMAND comp_sql
- mysql_bootstrap_sql
- ${CMAKE_CURRENT_BINARY_DIR}/my_bootstrap.sql
- mysql_bootstrap_sql.c
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
- DEPENDS comp_sql ${my_bootstrap_sql})
-
- MYSQL_ADD_EXECUTABLE(mysql_install_db
- mysql_install_db.cc
- ${CMAKE_CURRENT_BINARY_DIR}/mysql_bootstrap_sql.c
- COMPONENT Server)
- TARGET_LINK_LIBRARIES(mysql_install_db mysys strings dbug)
+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
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+)
- ADD_LIBRARY(winservice STATIC winservice.c)
-
- MYSQL_ADD_EXECUTABLE(mysql_upgrade_service
- mysql_upgrade_service.cc
- COMPONENT Server)
-
- TARGET_LINK_LIBRARIES(mysql_upgrade_service mysys strings dbug winservice)
+ADD_CUSTOM_TARGET(distclean
+ COMMAND ${CMAKE_COMMAND} -E echo WARNING: distclean target is not functional
+ COMMAND ${CMAKE_COMMAND} -E echo Use 'bzr clean-tree' with --unknown and/or
+ --ignored parameter instead
+ VERBATIM
+ )
- # mysql_install_db should be in the same directory as mysqld
- # to work correctly
- GET_TARGET_PROPERTY(MYSQLD_EXECUTABLE mysqld LOCATION)
- IF(NOT MYSQLD_EXECUTABLE)
- MESSAGE(FATAL_ERROR "Unexpected")
- ENDIF()
-ENDIF()
+IF(INSTALL_LAYOUT STREQUAL "STANDALONE")
-# We need to create empty directories (data/test) the installation.
-# This does not work with current CPack due to http://www.cmake.org/Bug/view.php?id=8767
-# Avoid completely empty directories and install dummy file instead.
-SET(DUMMY_FILE ${CMAKE_CURRENT_BINARY_DIR}/.empty )
-FILE(WRITE ${DUMMY_FILE} "")
-INSTALL(FILES ${DUMMY_FILE} DESTINATION data/test COMPONENT DataFiles)
+# Copy db.opt into data/test/
+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)
@@ -227,7 +287,7 @@ IF(NOT CMAKE_CROSSCOMPILING)
ENDIF()
IF(WIN32 AND MYSQLD_EXECUTABLE)
CONFIGURE_FILE(
- ${CMAKE_SOURCE_DIR}/win/cmake/create_initial_db.cmake.in
+ ${CMAKE_SOURCE_DIR}/cmake/create_initial_db.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/create_initial_db.cmake
@ONLY
)
@@ -241,7 +301,6 @@ IF(WIN32 AND MYSQLD_EXECUTABLE)
COMMAND ${CMAKE_COMMAND}
${CONFIG_PARAM} -P ${CMAKE_CURRENT_BINARY_DIR}/create_initial_db.cmake
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data
- COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/initdb.dep
DEPENDS mysqld
)
ADD_CUSTOM_TARGET(initial_database
@@ -249,8 +308,60 @@ IF(WIN32 AND MYSQLD_EXECUTABLE)
DEPENDS initdb.dep
)
INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data DESTINATION .
- COMPONENT DataFiles PATTERN "initdb.dep" EXCLUDE PATTERN "bootstrap.sql" EXCLUDE)
+ COMPONENT DataFiles
+ PATTERN "initdb.dep" EXCLUDE
+ PATTERN "bootstrap.sql" EXCLUDE
+ PATTERN "aria*" EXCLUDE
+ )
ELSE()
# Not windows or cross compiling, just install an empty directory
INSTALL(FILES ${DUMMY_FILE} DESTINATION data/mysql COMPONENT DataFiles)
-ENDIF()
+ENDIF(WIN32 AND MYSQLD_EXECUTABLE)
+ENDIF(INSTALL_LAYOUT STREQUAL "STANDALONE")
+
+IF(WIN32)
+ SET(my_bootstrap_sql ${CMAKE_CURRENT_BINARY_DIR}/my_bootstrap.sql)
+ FILE(TO_NATIVE_PATH ${my_bootstrap_sql} native_outfile)
+
+ # Create bootstrapper SQL script
+ 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}
+ 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
+ )
+
+ ADD_CUSTOM_COMMAND(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/mysql_bootstrap_sql.c
+ COMMAND comp_sql
+ mysql_bootstrap_sql
+ ${CMAKE_CURRENT_BINARY_DIR}/my_bootstrap.sql
+ mysql_bootstrap_sql.c
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS comp_sql ${my_bootstrap_sql}
+ )
+
+ MYSQL_ADD_EXECUTABLE(mysql_install_db
+ mysql_install_db.cc
+ ${CMAKE_CURRENT_BINARY_DIR}/mysql_bootstrap_sql.c
+ COMPONENT Server
+ )
+ TARGET_LINK_LIBRARIES(mysql_install_db mysys)
+
+ ADD_LIBRARY(winservice STATIC winservice.c)
+ TARGET_LINK_LIBRARIES(winservice shell32)
+ MYSQL_ADD_EXECUTABLE(mysql_upgrade_service
+ mysql_upgrade_service.cc
+ COMPONENT Server)
+ TARGET_LINK_LIBRARIES(mysql_upgrade_service mysys winservice)
+ENDIF(WIN32)
+
+INSTALL(DIRECTORY . DESTINATION ${INSTALL_INCLUDEDIR}/private COMPONENT Development
+ FILES_MATCHING PATTERN "*.h"
+ PATTERN examples EXCLUDE
+ PATTERN share EXCLUDE
+ PATTERN CMakeFiles EXCLUDE)
diff --git a/sql/Makefile.am b/sql/Makefile.am
deleted file mode 100644
index 3bcc40f3304..00000000000
--- a/sql/Makefile.am
+++ /dev/null
@@ -1,212 +0,0 @@
-# Copyright (c) 2000, 2012, Oracle and/or its affiliates.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# 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
-
-#called from the top level Makefile
-
-MYSQLDATAdir = $(localstatedir)
-MYSQLSHAREdir = $(pkgdatadir)
-MYSQLBASEdir= $(prefix)
-MYSQLLIBdir= $(pkglibdir)
-pkgplugindir = $(pkglibdir)/plugin
-INCLUDES = @ZLIB_INCLUDES@ \
- -I$(top_builddir)/include -I$(top_srcdir)/include \
- -I$(top_srcdir)/regex -I$(srcdir) $(openssl_includes) \
- $(libevent_includes)
-WRAPLIBS= @WRAPLIBS@
-SUBDIRS = share
-libexec_PROGRAMS = mysqld
-EXTRA_PROGRAMS = gen_lex_hash
-bin_PROGRAMS = mysql_tzinfo_to_sql
-
-noinst_LTLIBRARIES= libndb.la \
- udf_example.la
-
-SUPPORTING_LIBS = $(top_builddir)/vio/libvio.a \
- $(top_builddir)/mysys/libmysys.a \
- $(top_builddir)/dbug/libdbug.a \
- $(top_builddir)/regex/libregex.la \
- $(top_builddir)/strings/libmystrings.a
-mysqld_DEPENDENCIES= @mysql_plugin_libs@ $(SUPPORTING_LIBS) libndb.la
-LDADD = $(SUPPORTING_LIBS) @ZLIB_LIBS@ @NDB_SCI_LIBS@
-mysqld_LDADD = libndb.la \
- @MYSQLD_EXTRA_LDFLAGS@ \
- $(libevent_libs) \
- @mysql_plugin_libs@ \
- $(LDADD) $(CXXLDFLAGS) $(WRAPLIBS) @LIBDL@ \
- $(yassl_libs) $(openssl_libs) @MYSQLD_EXTRA_LIBS@
-
-internalincludedir = $(pkgincludedir)/private
-internalinclude_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
- item_strfunc.h item_timefunc.h \
- item_xmlfunc.h sql_plugin_services.h \
- item_create.h item_subselect.h item_row.h \
- mysql_priv.h item_geofunc.h sql_bitmap.h \
- procedure.h sql_class.h sql_lex.h sql_list.h \
- sql_map.h sql_string.h unireg.h \
- sql_error.h field.h handler.h mysqld_suffix.h \
- sql_profile.h \
- gcalc_slicescan.h gcalc_tools.h \
- ha_ndbcluster.h ha_ndbcluster_cond.h \
- ha_ndbcluster_binlog.h ha_ndbcluster_tables.h \
- ha_partition.h rpl_constants.h \
- debug_sync.h \
- opt_range.h protocol.h rpl_tblmap.h rpl_utility.h \
- opt_subselect.h \
- rpl_reporting.h \
- log.h log_slow.h sql_show.h rpl_rli.h rpl_mi.h \
- sql_select.h structs.h table.h sql_udf.h hash_filo.h \
- lex.h lex_symbol.h sql_acl.h sql_crypt.h \
- sql_lifo_buffer.h \
- sql_repl.h slave.h rpl_filter.h rpl_injector.h \
- log_event.h rpl_record.h \
- log_event_old.h rpl_record_old.h \
- sql_sort.h sql_cache.h set_var.h \
- spatial.h gstream.h client_settings.h tzfile.h \
- tztime.h my_decimal.h\
- sp_head.h sp_pcontext.h sp_rcontext.h sp.h sp_cache.h \
- parse_file.h sql_view.h sql_trigger.h \
- mem_root_array.h \
- sql_array.h sql_cursor.h events.h scheduler.h \
- event_db_repository.h event_queue.h \
- sql_plugin.h authors.h event_parse_data.h \
- event_data_objects.h event_scheduler.h \
- sql_partition.h partition_info.h partition_element.h \
- contributors.h sql_servers.h \
- multi_range_read.h sql_handler.h \
- sql_join_cache.h \
- create_options.h \
- sql_expression_cache.h \
- gcalc_slicescan.h gcalc_tools.h plistsort.c
-
-mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
- item.cc item_sum.cc item_buff.cc item_func.cc \
- item_cmpfunc.cc item_strfunc.cc item_timefunc.cc \
- thr_malloc.cc item_create.cc item_subselect.cc \
- item_row.cc item_geofunc.cc item_xmlfunc.cc \
- field.cc strfunc.cc key.cc sql_class.cc sql_list.cc \
- net_serv.cc protocol.cc sql_state.c \
- lock.cc my_lock.c \
- sql_string.cc sql_manager.cc sql_map.cc \
- mysqld.cc password.c hash_filo.cc hostname.cc \
- sql_connect.cc scheduler.cc sql_parse.cc \
- set_var.cc sql_yacc.yy \
- sql_join_cache.cc \
- sql_base.cc table.cc sql_select.cc sql_insert.cc \
- sql_profile.cc \
- gcalc_slicescan.cc gcalc_tools.cc \
- sql_prepare.cc sql_error.cc sql_locale.cc \
- sql_update.cc sql_delete.cc uniques.cc sql_do.cc \
- procedure.cc sql_test.cc \
- log.cc init.cc derror.cc sql_acl.cc \
- unireg.cc des_key_file.cc \
- log_event.cc rpl_record.cc \
- log_event_old.cc rpl_record_old.cc \
- discover.cc time.cc opt_range.cc opt_subselect.cc \
- opt_sum.cc \
- records.cc filesort.cc handler.cc \
- ha_partition.cc \
- debug_sync.cc \
- signal_handler.cc \
- sql_db.cc sql_table.cc sql_rename.cc sql_crypt.cc \
- sql_load.cc mf_iocache.cc field_conv.cc sql_show.cc \
- sql_udf.cc sql_analyse.cc sql_analyse.h sql_cache.cc \
- slave.cc sql_repl.cc rpl_filter.cc rpl_tblmap.cc \
- rpl_utility.cc rpl_injector.cc rpl_rli.cc rpl_mi.cc \
- rpl_reporting.cc \
- sql_union.cc sql_derived.cc \
- sql_client.cc \
- repl_failsafe.h repl_failsafe.cc \
- sql_olap.cc sql_view.cc \
- gstream.cc spatial.cc sql_help.cc sql_cursor.cc \
- tztime.cc my_decimal.cc\
- sp_head.cc sp_pcontext.cc sp_rcontext.cc sp.cc \
- sp_cache.cc parse_file.cc sql_trigger.cc \
- event_scheduler.cc event_data_objects.cc \
- event_queue.cc event_db_repository.cc events.cc \
- sql_plugin.cc sql_binlog.cc \
- sql_builtin.cc sql_tablespace.cc partition_info.cc \
- sql_servers.cc event_parse_data.cc \
- opt_table_elimination.cc create_options.cc \
- multi_range_read.cc \
- opt_index_cond_pushdown.cc sql_expression_cache.cc
-
-nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.c client_plugin.c
-
-libndb_la_CPPFLAGS= @ndbcluster_includes@
-libndb_la_SOURCES= ha_ndbcluster.cc \
- ha_ndbcluster_binlog.cc \
- ha_ndbcluster_cond.cc
-
-gen_lex_hash_SOURCES = gen_lex_hash.cc
-gen_lex_hash_LDFLAGS = @NOINST_LDFLAGS@
-
-mysql_tzinfo_to_sql_SOURCES = tztime.cc
-mysql_tzinfo_to_sql_CXXFLAGS= -DTZINFO2SQL
-
-DEFS = -DMYSQL_SERVER \
- -DDEFAULT_MYSQL_HOME='"$(MYSQLBASEdir)"' \
- -DMYSQL_DATADIR='"$(MYSQLDATAdir)"' \
- -DSHAREDIR='"$(MYSQLSHAREdir)"' \
- -DPLUGINDIR='"$(pkgplugindir)"' \
- -DHAVE_EVENT_SCHEDULER \
- @DEFS@
-
-BUILT_MAINT_SRC = sql_yacc.cc sql_yacc.$(YACC_HEXT)
-BUILT_SOURCES = $(BUILT_MAINT_SRC) lex_hash.h link_sources
-EXTRA_DIST = udf_example.c udf_example.def $(BUILT_MAINT_SRC) \
- nt_servc.cc nt_servc.h mysql_install_db.cc mysql_upgrade_service.cc \
- message.mc message.h message.rc MSG00001.bin \
- winservice.c winservice.h \
- CMakeLists.txt opt_range_mrr.cc
-
-CLEANFILES = lex_hash.h sql_yacc.output link_sources
-DISTCLEANFILES = $(EXTRA_PROGRAMS)
-MAINTAINERCLEANFILES = $(BUILT_MAINT_SRC)
-AM_YFLAGS = -d --verbose
-
-# These are listed in 'nodist_mysqld_SOURCES'
-link_sources:
- rm -f mini_client_errors.c
- @LN_CP_F@ $(top_srcdir)/libmysql/errmsg.c mini_client_errors.c
- rm -f pack.c
- @LN_CP_F@ $(top_srcdir)/sql-common/pack.c pack.c
- rm -f client.c
- @LN_CP_F@ $(top_srcdir)/sql-common/client.c client.c
- rm -f client_plugin.c
- @LN_CP_F@ $(top_srcdir)/sql-common/client_plugin.c client_plugin.c
- rm -f my_time.c
- @LN_CP_F@ $(top_srcdir)/sql-common/my_time.c my_time.c
- rm -f my_user.c
- @LN_CP_F@ $(top_srcdir)/sql-common/my_user.c my_user.c
- echo timestamp > link_sources
-
-# This generates lex_hash.h
-# NOTE Built sources should depend on their sources not the tool
-# this avoid the rebuild of the built files in a source dist
-lex_hash.h: gen_lex_hash.cc lex.h
- $(MAKE) $(AM_MAKEFLAGS) gen_lex_hash$(EXEEXT)
- ./gen_lex_hash$(EXEEXT) > $@-t
- $(MV) $@-t $@
-
-# For testing of udf_example.so
-udf_example_la_SOURCES= udf_example.c
-udf_example_la_LDFLAGS= -module -rpath $(pkglibdir)
-
-# We might have some stuff not built in this build, but that we want to install
-install-exec-hook:
- $(mkinstalldirs) $(DESTDIR)$(libexecdir) $(DESTDIR)$(pkglibdir)
- test ! -x mysqld-debug$(EXEEXT) || $(INSTALL_PROGRAM) mysqld-debug$(EXEEXT) $(DESTDIR)$(libexecdir)
- test ! -f mysqld-debug.sym.gz || $(INSTALL_DATA) mysqld-debug.sym.gz $(DESTDIR)$(pkglibdir)
- test ! -f mysqld.sym.gz || $(INSTALL_DATA) mysqld.sym.gz $(DESTDIR)$(pkglibdir)
diff --git a/sql/add_errmsg b/sql/add_errmsg
index cf54ede5dce..86226926d38 100755
--- a/sql/add_errmsg
+++ b/sql/add_errmsg
@@ -8,8 +8,8 @@ then
fi
FILE=/tmp/add.$$
-tail -$1 share/english/errmsg.txt > $FILE
-for i in `ls share/*/errmsg.txt | grep -v english`
+tail -$1 share/english/errmsg-utf8.txt > $FILE
+for i in `ls share/*/errmsg-utf8.txt | grep -v english`
do
cat $FILE >> $i
done
diff --git a/sql/authors.h b/sql/authors.h
index 3925696d640..84c29f8c127 100644
--- a/sql/authors.h
+++ b/sql/authors.h
@@ -1,5 +1,7 @@
-/*
- Copyright (c) 2005, 2010, Oracle and/or its affiliates.
+#ifndef AUTHORS_INCLUDED
+#define AUTHORS_INCLUDED
+
+/* Copyright (c) 2005, 2010, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +14,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* Structure of the name list */
@@ -48,12 +49,13 @@ struct show_table_authors_st show_table_authors[]= {
"Query Cache (4.0), Subqueries (4.1), Views (5.0)" },
{ "Brian (Krow) Aker", "Seattle, WA, USA",
"Architecture, archive, federated, bunch of little stuff :)" },
+ { "Marc Alff", "Denver, CO, USA", "Signal, Resignal, Performance schema" },
+ { "Venu Anuganti", "", "Client/server protocol (4.1)" },
{ "Kristian Nielsen", "Copenhagen, Denmark",
"General build stuff," },
{ "Alexander (Bar) Barkov", "Izhevsk, Russia",
"Unicode and character sets (4.1)" },
{ "Guilhem Bichot", "Bordeaux, France", "Replication (since 4.0)" },
- { "Venu Anuganti", "", "Client/server protocol (4.1)" },
{ "Konstantin Osipov", "Moscow, Russia",
"Prepared statements (4.1), Cursors (5.0)" },
{ "Dmitri Lenev", "Moscow, Russia",
@@ -99,6 +101,7 @@ struct show_table_authors_st show_table_authors[]= {
{ "Arjen Lentz", "Brisbane, Australia",
"Documentation (2001-2004), Dutch error messages, LOG2()" },
{ "Marc Liyanage", "", "Created Mac OS X packages" },
+ { "Kelly Long", "Denver, CO, USA", "Pool Of Threads" },
{ "Zarko Mocnik", "", "Sorting for Slovenian language" },
{ "Per-Erik Martin", "Uppsala, Sweden", "Stored Procedures (5.0)" },
{ "Alexis Mikhailov", "", "User-defined functions" },
@@ -157,3 +160,5 @@ struct show_table_authors_st show_table_authors[]= {
{"Percona", "CA, USA", "Microslow patches"},
{NULL, NULL, NULL}
};
+
+#endif /* AUTHORS_INCLUDED */
diff --git a/sql/client_settings.h b/sql/client_settings.h
index a76955cd0f7..54cb72f9412 100644
--- a/sql/client_settings.h
+++ b/sql/client_settings.h
@@ -1,6 +1,4 @@
-/*
- Copyright (c) 2003, 2006 MySQL AB, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2003, 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
@@ -13,25 +11,32 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef CLIENT_SETTINGS_INCLUDED
+#define CLIENT_SETTINGS_INCLUDED
+#else
+#error You have already included an client_settings.h and it should not be included twice
+#endif /* CLIENT_SETTINGS_INCLUDED */
#include <thr_alarm.h>
#include <sql_common.h>
-#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | \
- CLIENT_SECURE_CONNECTION | CLIENT_TRANSACTIONS | \
- CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION)
+/*
+ Note: CLIENT_CAPABILITIES is also defined in libmysql/client_settings.h.
+ When adding capabilities here, consider if they should be also added to
+ the libmysql version.
+*/
+#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | \
+ CLIENT_LONG_FLAG | \
+ CLIENT_SECURE_CONNECTION | \
+ CLIENT_TRANSACTIONS | \
+ CLIENT_PROTOCOL_41 | \
+ CLIENT_SECURE_CONNECTION | \
+ CLIENT_PLUGIN_AUTH)
-#define init_sigpipe_variables
-#define set_sigpipe(mysql)
-#define reset_sigpipe(mysql)
#define read_user_name(A) {}
-#define mysql_rpl_query_type(A,B) MYSQL_RPL_ADMIN
-#define mysql_master_send_query(A, B, C) 1
-#define mysql_slave_send_query(A, B, C) 1
-#define mysql_rpl_probe(mysql) 0
#undef _CUSTOMCONFIG_
#define mysql_server_init(a,b,c) mysql_client_plugin_init()
diff --git a/sql/contributors.h b/sql/contributors.h
index 87001e29d88..b1ba2c20997 100644
--- a/sql/contributors.h
+++ b/sql/contributors.h
@@ -1,4 +1,8 @@
-/* Copyright (C) 2006 MySQL AB
+#ifndef CONTRIBUTORS_INCLUDED
+#define CONTRIBUTORS_INCLUDED
+
+/* 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
@@ -37,3 +41,5 @@ struct show_table_contributors_st show_table_contributors[]= {
{"Mark Shuttleworth", "London, UK.", "EFF contribution for UC2006 Auction"},
{NULL, NULL, NULL}
};
+
+#endif /* CONTRIBUTORS_INCLUDED */
diff --git a/sql/create_options.cc b/sql/create_options.cc
index 25e35a1f218..5cedfa03a63 100644
--- a/sql/create_options.cc
+++ b/sql/create_options.cc
@@ -19,7 +19,6 @@
Engine defined options of tables/fields/keys in CREATE/ALTER TABLE.
*/
-#include "mysql_priv.h"
#include "create_options.h"
#include <my_getopt.h>
@@ -548,7 +547,7 @@ my_bool engine_table_options_frm_read(const uchar *buff, uint length,
TABLE_SHARE *share)
{
const uchar *buff_end= buff + length;
- engine_option_value *end;
+ engine_option_value *UNINIT_VAR(end);
MEM_ROOT *root= &share->mem_root;
uint count;
DBUG_ENTER("engine_table_options_frm_read");
diff --git a/sql/create_options.h b/sql/create_options.h
index 174abb1a59a..ae918f6cea1 100644
--- a/sql/create_options.h
+++ b/sql/create_options.h
@@ -22,7 +22,8 @@
#ifndef SQL_CREATE_OPTIONS_INCLUDED
#define SQL_CREATE_OPTIONS_INCLUDED
-#include "handler.h"
+#include "sql_class.h"
+//#include "handler.h"
class engine_option_value: public Sql_alloc
{
diff --git a/sql/custom_conf.h b/sql/custom_conf.h
index 51220a22167..62fdb619c27 100644
--- a/sql/custom_conf.h
+++ b/sql/custom_conf.h
@@ -1,4 +1,5 @@
/* Copyright (c) 2000, 2006 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
diff --git a/sql/datadict.cc b/sql/datadict.cc
new file mode 100644
index 00000000000..4e4fcafa31b
--- /dev/null
+++ b/sql/datadict.cc
@@ -0,0 +1,234 @@
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "datadict.h"
+#include "sql_priv.h"
+#include "sql_class.h"
+#include "sql_table.h"
+
+
+/**
+ Check type of .frm if we are not going to parse it.
+
+ @param[in] thd The current session.
+ @param[in] path path to FRM file.
+ @param[out] dbt db_type of the table if FRMTYPE_TABLE, otherwise undefined.
+
+ @retval FRMTYPE_ERROR error
+ @retval FRMTYPE_TABLE table
+ @retval FRMTYPE_VIEW view
+*/
+
+frm_type_enum dd_frm_type(THD *thd, char *path, enum legacy_db_type *dbt)
+{
+ File file;
+ uchar header[10]; //"TYPE=VIEW\n" it is 10 characters
+ size_t error;
+ frm_type_enum type= FRMTYPE_ERROR;
+ DBUG_ENTER("dd_frm_type");
+
+ *dbt= DB_TYPE_UNKNOWN;
+
+ 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));
+
+ if (error)
+ goto err;
+ if (!strncmp((char*) header, "TYPE=VIEW\n", sizeof(header)))
+ {
+ type= FRMTYPE_VIEW;
+ goto err;
+ }
+
+ type= FRMTYPE_TABLE;
+
+ /*
+ This is just a check for DB_TYPE. We'll return default unknown type
+ if the following test is true (arg #3). This should not have effect
+ on return value from this function (default FRMTYPE_TABLE)
+ */
+ if (header[0] != (uchar) 254 || header[1] != 1 ||
+ (header[2] != FRM_VER && header[2] != FRM_VER+1 &&
+ (header[2] < FRM_VER+3 || header[2] > FRM_VER+4)))
+ goto err;
+
+ *dbt= (enum legacy_db_type) (uint) *(header + 3);
+
+ if (*dbt >= DB_TYPE_FIRST_DYNAMIC) /* read the true engine name */
+ {
+ MY_STAT state;
+ uchar *frm_image= 0;
+ uint n_length;
+
+ if (mysql_file_fstat(file, &state, MYF(MY_WME)))
+ goto err;
+
+ if (mysql_file_seek(file, 0, SEEK_SET, MYF(MY_WME)))
+ goto err;
+
+ if (read_string(file, &frm_image, state.st_size))
+ goto err;
+
+ if ((n_length= uint4korr(frm_image+55)))
+ {
+ uint record_offset= (uint2korr(frm_image+6)+
+ ((uint2korr(frm_image+14) == 0xffff ?
+ uint4korr(frm_image+47) : uint2korr(frm_image+14))));
+ uint reclength= uint2korr(frm_image+16);
+
+ uchar *next_chunk= frm_image + record_offset + reclength;
+ uchar *buff_end= next_chunk + n_length;
+ uint connect_string_length= uint2korr(next_chunk);
+ next_chunk+= connect_string_length + 2;
+ if (next_chunk + 2 < buff_end)
+ {
+ uint str_db_type_length= uint2korr(next_chunk);
+ LEX_STRING name;
+ name.str= (char*) next_chunk + 2;
+ name.length= str_db_type_length;
+ plugin_ref tmp_plugin= ha_resolve_by_name(thd, &name);
+ if (tmp_plugin)
+ *dbt= plugin_data(tmp_plugin, handlerton *)->db_type;
+ else
+ *dbt= DB_TYPE_UNKNOWN;
+ }
+ }
+
+ my_free(frm_image);
+ }
+
+ /* Probably a table. */
+err:
+ mysql_file_close(file, MYF(MY_WME));
+ DBUG_RETURN(type);
+}
+
+
+/**
+ Given a table name, check type of .frm and legacy table type.
+
+ @param[in] thd The current session.
+ @param[in] db Table schema.
+ @param[in] table_name Table database.
+ @param[out] table_type handlerton of the table if FRMTYPE_TABLE,
+ otherwise undefined.
+
+ @return FALSE if FRMTYPE_TABLE and storage engine found. TRUE otherwise.
+*/
+
+bool dd_frm_storage_engine(THD *thd, const char *db, const char *table_name,
+ handlerton **table_type)
+{
+ char path[FN_REFLEN + 1];
+ enum legacy_db_type db_type;
+ LEX_STRING db_name = {(char *) db, strlen(db)};
+
+ /* There should be at least some lock on the table. */
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db,
+ table_name, MDL_SHARED));
+
+ if (check_db_name(&db_name))
+ {
+ my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str);
+ return TRUE;
+ }
+
+ if (check_table_name(table_name, strlen(table_name), FALSE))
+ {
+ my_error(ER_WRONG_TABLE_NAME, MYF(0), table_name);
+ return TRUE;
+ }
+
+ (void) build_table_filename(path, sizeof(path) - 1, db,
+ table_name, reg_ext, 0);
+
+ dd_frm_type(thd, path, &db_type);
+
+ /* Type is unknown if the object is not found or is not a table. */
+ if (db_type == DB_TYPE_UNKNOWN ||
+ !(*table_type= ha_resolve_by_legacy_type(thd, db_type)))
+ {
+ my_error(ER_NO_SUCH_TABLE, MYF(0), db, table_name);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Given a table name, check if the storage engine for the
+ table referred by this name supports an option 'flag'.
+ Return an error if the table does not exist or is not a
+ base table.
+
+ @pre Any metadata lock on the table.
+
+ @param[in] thd The current session.
+ @param[in] db Table schema.
+ @param[in] table_name Table database.
+ @param[in] flag The option to check.
+ @param[out] yes_no The result. Undefined if error.
+*/
+
+bool dd_check_storage_engine_flag(THD *thd,
+ const char *db, const char *table_name,
+ uint32 flag, bool *yes_no)
+{
+ handlerton *table_type;
+
+ if (dd_frm_storage_engine(thd, db, table_name, &table_type))
+ return TRUE;
+
+ *yes_no= ha_check_storage_engine_flag(table_type, flag);
+
+ return FALSE;
+}
+
+
+/*
+ Regenerate a metadata locked table.
+
+ @param thd Thread context.
+ @param db Name of the database to which the table belongs to.
+ @param name Table name.
+
+ @retval FALSE Success.
+ @retval TRUE Error.
+*/
+
+bool dd_recreate_table(THD *thd, const char *db, const char *table_name)
+{
+ bool error= TRUE;
+ HA_CREATE_INFO create_info;
+ char path[FN_REFLEN + 1];
+ DBUG_ENTER("dd_recreate_table");
+
+ /* There should be a exclusive metadata lock on the table. */
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
+ MDL_EXCLUSIVE));
+
+ memset(&create_info, 0, sizeof(create_info));
+
+ /* Create a path to the table, but without a extension. */
+ build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0);
+
+ /* Attempt to reconstruct the table. */
+ error= ha_create_table(thd, path, db, table_name, &create_info, TRUE);
+
+ DBUG_RETURN(error);
+}
+
diff --git a/sql/datadict.h b/sql/datadict.h
new file mode 100644
index 00000000000..f852b02f52c
--- /dev/null
+++ b/sql/datadict.h
@@ -0,0 +1,41 @@
+#ifndef DATADICT_INCLUDED
+#define DATADICT_INCLUDED
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "handler.h"
+
+/*
+ Data dictionary API.
+*/
+
+enum frm_type_enum
+{
+ FRMTYPE_ERROR= 0,
+ FRMTYPE_TABLE,
+ FRMTYPE_VIEW
+};
+
+frm_type_enum dd_frm_type(THD *thd, char *path, enum legacy_db_type *dbt);
+
+bool dd_frm_storage_engine(THD *thd, const char *db, const char *table_name,
+ handlerton **table_type);
+bool dd_check_storage_engine_flag(THD *thd,
+ const char *db, const char *table_name,
+ uint32 flag,
+ bool *yes_no);
+bool dd_recreate_table(THD *thd, const char *db, const char *table_name);
+
+#endif // DATADICT_INCLUDED
diff --git a/sql/debug_sync.cc b/sql/debug_sync.cc
index 585be68a7f6..4cff1c09ba7 100644
--- a/sql/debug_sync.cc
+++ b/sql/debug_sync.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, Oracle and/or its affiliates.
+/* Copyright (c) 2009, 2013, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,307 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-/**
- == Debug Sync Facility ==
-
- The Debug Sync Facility allows placement of synchronization points in
- the server code by using the DEBUG_SYNC macro:
-
- open_tables(...)
-
- DEBUG_SYNC(thd, "after_open_tables");
-
- lock_tables(...)
-
- When activated, a sync point can
-
- - Emit a signal and/or
- - Wait for a signal
-
- Nomenclature:
-
- - signal: A value of a global variable that persists
- until overwritten by a new signal. The global
- variable can also be seen as a "signal post"
- or "flag mast". Then the signal is what is
- attached to the "signal post" or "flag mast".
-
- - emit a signal: Assign the value (the signal) to the global
- variable ("set a flag") and broadcast a
- global condition to wake those waiting for
- a signal.
-
- - wait for a signal: Loop over waiting for the global condition until
- the global value matches the wait-for signal.
-
- By default, all sync points are inactive. They do nothing (except to
- burn a couple of CPU cycles for checking if they are active).
-
- A sync point becomes active when an action is requested for it.
- To do so, put a line like this in the test case file:
-
- SET DEBUG_SYNC= 'after_open_tables SIGNAL opened WAIT_FOR flushed';
-
- This activates the sync point 'after_open_tables'. It requests it to
- emit the signal 'opened' and wait for another thread to emit the signal
- 'flushed' when the thread's execution runs through the sync point.
-
- For every sync point there can be one action per thread only. Every
- thread can request multiple actions, but only one per sync point. In
- other words, a thread can activate multiple sync points.
-
- Here is an example how to activate and use the sync points:
-
- --connection conn1
- SET DEBUG_SYNC= 'after_open_tables SIGNAL opened WAIT_FOR flushed';
- send INSERT INTO t1 VALUES(1);
- --connection conn2
- SET DEBUG_SYNC= 'now WAIT_FOR opened';
- SET DEBUG_SYNC= 'after_abort_locks SIGNAL flushed';
- FLUSH TABLE t1;
-
- When conn1 runs through the INSERT statement, it hits the sync point
- 'after_open_tables'. It notices that it is active and executes its
- action. It emits the signal 'opened' and waits for another thread to
- emit the signal 'flushed'.
-
- conn2 waits immediately at the special sync point 'now' for another
- thread to emit the 'opened' signal.
-
- A signal remains in effect until it is overwritten. If conn1 signals
- 'opened' before conn2 reaches 'now', conn2 will still find the 'opened'
- signal. It does not wait in this case.
-
- When conn2 reaches 'after_abort_locks', it signals 'flushed', which lets
- conn1 awake.
-
- Normally the activation of a sync point is cleared when it has been
- executed. Sometimes it is necessary to keep the sync point active for
- another execution. You can add an execute count to the action:
-
- SET DEBUG_SYNC= 'name SIGNAL sig EXECUTE 3';
-
- This sets the signal point's activation counter to 3. Each execution
- decrements the counter. After the third execution the sync point
- becomes inactive.
-
- One of the primary goals of this facility is to eliminate sleeps from
- the test suite. In most cases it should be possible to rewrite test
- cases so that they do not need to sleep. (But this facility cannot
- synchronize multiple processes.) However, to support test development,
- and as a last resort, sync point waiting times out. There is a default
- timeout, but it can be overridden:
-
- SET DEBUG_SYNC= 'name WAIT_FOR sig TIMEOUT 10 EXECUTE 2';
-
- TIMEOUT 0 is special: If the signal is not present, the wait times out
- immediately.
-
- When a wait timed out (even on TIMEOUT 0), a warning is generated so
- that it shows up in the test result.
-
- You can throw an error message and kill the query when a synchronization
- point is hit a certain number of times:
-
- SET DEBUG_SYNC= 'name HIT_LIMIT 3';
-
- Or combine it with signal and/or wait:
-
- SET DEBUG_SYNC= 'name SIGNAL sig EXECUTE 2 HIT_LIMIT 3';
-
- Here the first two hits emit the signal, the third hit returns the error
- message and kills the query.
-
- For cases where you are not sure that an action is taken and thus
- cleared in any case, you can force to clear (deactivate) a sync point:
-
- SET DEBUG_SYNC= 'name CLEAR';
-
- If you want to clear all actions and clear the global signal, use:
-
- SET DEBUG_SYNC= 'RESET';
-
- This is the only way to reset the global signal to an empty string.
-
- For testing of the facility itself you can execute a sync point just
- as if it had been hit:
-
- SET DEBUG_SYNC= 'name TEST';
-
-
- === Formal Syntax ===
-
- The string to "assign" to the DEBUG_SYNC variable can contain:
-
- {RESET |
- <sync point name> TEST |
- <sync point name> CLEAR |
- <sync point name> {{SIGNAL <signal name> |
- WAIT_FOR <signal name> [TIMEOUT <seconds>]}
- [EXECUTE <count>] &| HIT_LIMIT <count>}
-
- Here '&|' means 'and/or'. This means that one of the sections
- separated by '&|' must be present or both of them.
-
-
- === Activation/Deactivation ===
-
- The facility is an optional part of the MySQL server.
- It is enabled in a debug server by default.
-
- ./configure --enable-debug-sync
-
- The Debug Sync Facility, when compiled in, is disabled by default. It
- can be enabled by a mysqld command line option:
-
- --debug-sync-timeout[=default_wait_timeout_value_in_seconds]
-
- 'default_wait_timeout_value_in_seconds' is the default timeout for the
- WAIT_FOR action. If set to zero, the facility stays disabled.
-
- The facility is enabled by default in the test suite, but can be
- disabled with:
-
- mysql-test-run.pl ... --debug-sync-timeout=0 ...
-
- Likewise the default wait timeout can be set:
-
- mysql-test-run.pl ... --debug-sync-timeout=10 ...
-
- The command line option influences the readable value of the system
- variable 'debug_sync'.
-
- * If the facility is not compiled in, the system variable does not exist.
-
- * If --debug-sync-timeout=0 the value of the variable reads as "OFF".
-
- * Otherwise the value reads as "ON - current signal: " followed by the
- current signal string, which can be empty.
-
- The readable variable value is the same, regardless if read as global
- or session value.
-
- Setting the 'debug-sync' system variable requires 'SUPER' privilege.
- You can never read back the string that you assigned to the variable,
- unless you assign the value that the variable does already have. But
- that would give a parse error. A syntactically correct string is
- parsed into a debug sync action and stored apart from the variable value.
-
-
- === Implementation ===
-
- Pseudo code for a sync point:
-
- #define DEBUG_SYNC(thd, sync_point_name)
- if (unlikely(opt_debug_sync_timeout))
- debug_sync(thd, STRING_WITH_LEN(sync_point_name))
-
- The sync point performs a binary search in a sorted array of actions
- for this thread.
-
- The SET DEBUG_SYNC statement adds a requested action to the array or
- overwrites an existing action for the same sync point. When it adds a
- new action, the array is sorted again.
-
-
- === A typical synchronization pattern ===
-
- There are quite a few places in MySQL, where we use a synchronization
- pattern like this:
-
- pthread_mutex_lock(&mutex);
- thd->enter_cond(&condition_variable, &mutex, new_message);
- #if defined(ENABLE_DEBUG_SYNC)
- if (!thd->killed && !end_of_wait_condition)
- DEBUG_SYNC(thd, "sync_point_name");
- #endif
- while (!thd->killed && !end_of_wait_condition)
- pthread_cond_wait(&condition_variable, &mutex);
- thd->exit_cond(old_message);
-
- Here some explanations:
-
- thd->enter_cond() is used to register the condition variable and the
- mutex in thd->mysys_var. This is done to allow the thread to be
- interrupted (killed) from its sleep. Another thread can find the
- condition variable to signal and mutex to use for synchronization in
- this thread's THD::mysys_var.
-
- thd->enter_cond() requires the mutex to be acquired in advance.
-
- thd->exit_cond() unregisters the condition variable and mutex and
- releases the mutex.
-
- If you want to have a Debug Sync point with the wait, please place it
- behind enter_cond(). Only then you can safely decide, if the wait will
- be taken. Also you will have THD::proc_info correct when the sync
- point emits a signal. DEBUG_SYNC sets its own proc_info, but restores
- the previous one before releasing its internal mutex. As soon as
- another thread sees the signal, it does also see the proc_info from
- before entering the sync point. In this case it will be "new_message",
- which is associated with the wait that is to be synchronized.
-
- In the example above, the wait condition is repeated before the sync
- point. This is done to skip the sync point, if no wait takes place.
- The sync point is before the loop (not inside the loop) to have it hit
- once only. It is possible that the condition variable is signaled
- multiple times without the wait condition to be true.
-
- A bit off-topic: At some places, the loop is taken around the whole
- synchronization pattern:
-
- while (!thd->killed && !end_of_wait_condition)
- {
- pthread_mutex_lock(&mutex);
- thd->enter_cond(&condition_variable, &mutex, new_message);
- if (!thd->killed [&& !end_of_wait_condition])
- {
- [DEBUG_SYNC(thd, "sync_point_name");]
- pthread_cond_wait(&condition_variable, &mutex);
- }
- thd->exit_cond(old_message);
- }
-
- Note that it is important to repeat the test for thd->killed after
- enter_cond(). Otherwise the killing thread may kill this thread after
- it tested thd->killed in the loop condition and before it registered
- the condition variable and mutex in enter_cond(). In this case, the
- killing thread does not know that this thread is going to wait on a
- condition variable. It would just set THD::killed. But if we would not
- test it again, we would go asleep though we are killed. If the killing
- thread would kill us when we are after the second test, but still
- before sleeping, we hold the mutex, which is registered in mysys_var.
- The killing thread would try to acquire the mutex before signaling
- the condition variable. Since the mutex is only released implicitly in
- pthread_cond_wait(), the signaling happens at the right place. We
- have a safe synchronization.
-
- === Co-work with the DBUG facility ===
-
- When running the MySQL test suite with the --debug command line
- option, the Debug Sync Facility writes trace messages to the DBUG
- trace. The following shell commands proved very useful in extracting
- relevant information:
-
- egrep 'query:|debug_sync_exec:' mysql-test/var/log/mysqld.1.trace
-
- It shows all executed SQL statements and all actions executed by
- synchronization points.
-
- Sometimes it is also useful to see, which synchronization points have
- been run through (hit) with or without executing actions. Then add
- "|debug_sync_point:" to the egrep pattern.
-
- === Further reading ===
-
- For a discussion of other methods to synchronize threads see
- http://forge.mysql.com/wiki/MySQL_Internals_Test_Synchronization
-
- For complete syntax tests, functional tests, and examples see the test
- case debug_sync.test.
-
- See also worklog entry WL#4259 - Test Synchronization Facility
-*/
+/* see include/mysql/service_debug_sync.h for debug sync documentation */
#include "debug_sync.h"
@@ -321,12 +21,13 @@
/*
Due to weaknesses in our include files, we need to include
- mysql_priv.h here. To have THD declared, we need to include
+ sql_priv.h here. To have THD declared, we need to include
sql_class.h. This includes log_event.h, which in turn requires
- declarations from mysql_priv.h (e.g. OPTION_AUTO_IS_NULL).
- mysql_priv.h includes almost everything, so is sufficient here.
+ declarations from sql_priv.h (e.g. OPTION_AUTO_IS_NULL).
+ sql_priv.h includes almost everything, so is sufficient here.
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "sql_parse.h"
/*
Action to perform at a synchronization point.
@@ -373,57 +74,54 @@ struct st_debug_sync_control
struct st_debug_sync_globals
{
String ds_signal; /* signal variable */
- pthread_cond_t ds_cond; /* condition variable */
- pthread_mutex_t ds_mutex; /* mutex variable */
+ mysql_cond_t ds_cond; /* condition variable */
+ mysql_mutex_t ds_mutex; /* mutex variable */
ulonglong dsp_hits; /* statistics */
ulonglong dsp_executed; /* statistics */
ulonglong dsp_max_active; /* statistics */
};
static st_debug_sync_globals debug_sync_global; /* All globals in one object */
+extern uint opt_debug_sync_timeout;
+
/**
- Callback pointer for C files.
+ Callbacks from C files.
*/
-extern "C" void (*debug_sync_C_callback_ptr)(const char *, size_t);
+C_MODE_START
+static void debug_sync(THD *thd, const char *sync_point_name, size_t name_len);
+static int debug_sync_qsort_cmp(const void *, const void *);
+C_MODE_END
+#ifdef HAVE_PSI_INTERFACE
+static PSI_mutex_key key_debug_sync_globals_ds_mutex;
-/**
- Callback for debug sync, to be used by C files. See thr_lock.c for example.
-
- @description
+static PSI_mutex_info all_debug_sync_mutexes[]=
+{
+ { &key_debug_sync_globals_ds_mutex, "DEBUG_SYNC::mutex", PSI_FLAG_GLOBAL}
+};
- We cannot place a sync point directly in C files (like those in mysys or
- certain storage engines written mostly in C like MyISAM or Maria). Because
- they are C code and do not include mysql_priv.h. So they do not know the
- macro DEBUG_SYNC(thd, sync_point_name). The macro needs a 'thd' argument.
- Hence it cannot be used in files outside of the sql/ directory.
-
- The workaround is to call back simple functions like this one from
- non-sql/ files.
-
- We want to allow modules like thr_lock to be used without sql/ and
- especially without Debug Sync. So we cannot just do a simple call
- of the callback function. Instead we provide a global pointer in
- the other file, which is to be set to the callback by Debug Sync.
- If the pointer is not set, no call back will be done. If Debug
- Sync sets the pointer to a callback function like this one, it will
- be called. That way thr_lock.c does not have an undefined reference
- to Debug Sync and can be used without it. Debug Sync, in contrast,
- has an undefined reference to that pointer and thus requires
- thr_lock to be linked too. But this is not a problem as it is part
- of the MySQL server anyway.
+static PSI_cond_key key_debug_sync_globals_ds_cond;
- @note
- The callback pointer in C files is set only if debug sync is
- initialized. And this is done only if opt_debug_sync_timeout is set.
-*/
+static PSI_cond_info all_debug_sync_conds[]=
+{
+ { &key_debug_sync_globals_ds_cond, "DEBUG_SYNC::cond", PSI_FLAG_GLOBAL}
+};
-static void debug_sync_C_callback(const char *sync_point_name,
- size_t name_len)
+static void init_debug_sync_psi_keys(void)
{
- if (unlikely(opt_debug_sync_timeout))
- debug_sync(current_thd, sync_point_name, name_len);
+ const char* category= "sql";
+ int count;
+
+ if (PSI_server == NULL)
+ return;
+
+ count= array_elements(all_debug_sync_mutexes);
+ PSI_server->register_mutex(category, all_debug_sync_mutexes, count);
+
+ count= array_elements(all_debug_sync_conds);
+ PSI_server->register_cond(category, all_debug_sync_conds, count);
}
+#endif /* HAVE_PSI_INTERFACE */
/**
@@ -438,19 +136,25 @@ int debug_sync_init(void)
{
DBUG_ENTER("debug_sync_init");
+#ifdef HAVE_PSI_INTERFACE
+ init_debug_sync_psi_keys();
+#endif
+
if (opt_debug_sync_timeout)
{
int rc;
/* Initialize the global variables. */
debug_sync_global.ds_signal.length(0);
- if ((rc= pthread_cond_init(&debug_sync_global.ds_cond, NULL)) ||
- (rc= pthread_mutex_init(&debug_sync_global.ds_mutex,
- MY_MUTEX_INIT_FAST)))
+ if ((rc= mysql_cond_init(key_debug_sync_globals_ds_cond,
+ &debug_sync_global.ds_cond, NULL)) ||
+ (rc= mysql_mutex_init(key_debug_sync_globals_ds_mutex,
+ &debug_sync_global.ds_mutex,
+ MY_MUTEX_INIT_FAST)))
DBUG_RETURN(rc); /* purecov: inspected */
/* Set the call back pointer in C files. */
- debug_sync_C_callback_ptr= debug_sync_C_callback;
+ debug_sync_C_callback_ptr= debug_sync;
}
DBUG_RETURN(0);
@@ -476,8 +180,8 @@ void debug_sync_end(void)
/* Destroy the global variables. */
debug_sync_global.ds_signal.free();
- (void) pthread_cond_destroy(&debug_sync_global.ds_cond);
- (void) pthread_mutex_destroy(&debug_sync_global.ds_mutex);
+ mysql_cond_destroy(&debug_sync_global.ds_cond);
+ mysql_mutex_destroy(&debug_sync_global.ds_mutex);
/* Print statistics. */
{
@@ -581,18 +285,18 @@ void debug_sync_end_thread(THD *thd)
action->wait_for.free();
action->sync_point.free();
}
- my_free(ds_control->ds_action, MYF(0));
+ my_free(ds_control->ds_action);
}
/* Statistics. */
- pthread_mutex_lock(&debug_sync_global.ds_mutex);
+ mysql_mutex_lock(&debug_sync_global.ds_mutex);
debug_sync_global.dsp_hits+= ds_control->dsp_hits;
debug_sync_global.dsp_executed+= ds_control->dsp_executed;
if (debug_sync_global.dsp_max_active < ds_control->dsp_max_active)
debug_sync_global.dsp_max_active= ds_control->dsp_max_active;
- pthread_mutex_unlock(&debug_sync_global.ds_mutex);
+ mysql_mutex_unlock(&debug_sync_global.ds_mutex);
- my_free(ds_control, MYF(0));
+ my_free(ds_control);
thd->debug_sync_control= NULL;
}
@@ -824,9 +528,9 @@ static void debug_sync_reset(THD *thd)
ds_control->ds_active= 0;
/* Clear the global signal. */
- pthread_mutex_lock(&debug_sync_global.ds_mutex);
+ mysql_mutex_lock(&debug_sync_global.ds_mutex);
debug_sync_global.ds_signal.length(0);
- pthread_mutex_unlock(&debug_sync_global.ds_mutex);
+ mysql_mutex_unlock(&debug_sync_global.ds_mutex);
DBUG_VOID_RETURN;
}
@@ -1524,56 +1228,6 @@ static bool debug_sync_eval_action(THD *thd, char *action_str)
DBUG_RETURN(FALSE);
}
-
-/**
- Check if the system variable 'debug_sync' can be set.
-
- @param[in] thd thread handle
- @param[in] var set variable request
-
- @return status
- @retval FALSE ok, variable can be set
- @retval TRUE error, variable cannot be set
-*/
-
-bool sys_var_debug_sync::check(THD *thd, set_var *var)
-{
- DBUG_ENTER("sys_var_debug_sync::check");
- DBUG_ASSERT(thd);
- DBUG_ASSERT(var);
-
- /*
- Variable can be set for the session only.
-
- This could be changed later. Then we need to have a global array of
- actions in addition to the thread local ones. SET GLOBAL would
- manage the global array, SET [SESSION] the local array. A sync point
- would need to look for a local and a global action. Setting and
- executing of global actions need to be protected by a mutex.
-
- The purpose of global actions could be to allow synchronizing with
- connectionless threads that cannot execute SET statements.
- */
- if (var->type == OPT_GLOBAL)
- {
- my_error(ER_LOCAL_VARIABLE, MYF(0), name);
- DBUG_RETURN(TRUE);
- }
-
- /*
- Do not check for disabled facility. Test result should not
- unnecessarily differ from enabled facility.
- */
-
- /*
- Facility requires SUPER privilege. Sync points could be inside
- global mutexes (e.g. LOCK_open). Waiting there forever would
- stall the whole server.
- */
- DBUG_RETURN(check_global_access(thd, SUPER_ACL));
-}
-
-
/**
Set the system variable 'debug_sync'.
@@ -1594,28 +1248,9 @@ bool sys_var_debug_sync::check(THD *thd, set_var *var)
terminators in the string. So we need to take a copy here.
*/
-bool sys_var_debug_sync::update(THD *thd, set_var *var)
+bool debug_sync_update(THD *thd, char *val_str)
{
- char *val_str;
- String *val_ptr;
- String val_buf;
- DBUG_ENTER("sys_var_debug_sync::update");
- DBUG_ASSERT(thd);
-
- /*
- Depending on the value type (string literal, user variable, ...)
- val_buf receives a copy of the value or not. But we always need
- a copy. So we take a copy, if it is not done by val_str().
- If val_str() puts a copy into val_buf, then it returns &val_buf,
- otherwise it returns a pointer to the string object that we need
- to copy.
- */
- val_ptr= var ? var->value->val_str(&val_buf) : &val_buf;
- if (val_ptr != &val_buf)
- {
- val_buf.copy(*val_ptr);
- }
- val_str= val_buf.c_ptr();
+ DBUG_ENTER("debug_sync_update");
DBUG_PRINT("debug_sync", ("set action: '%s'", val_str));
/*
@@ -1632,8 +1267,6 @@ bool sys_var_debug_sync::update(THD *thd, set_var *var)
Retrieve the value of the system variable 'debug_sync'.
@param[in] thd thread handle
- @param[in] type variable type, unused
- @param[in] base variable base, unused
@return string
@retval != NULL ok, string pointer
@@ -1646,20 +1279,17 @@ bool sys_var_debug_sync::update(THD *thd, set_var *var)
When "ON", the current signal is added.
*/
-uchar *sys_var_debug_sync::value_ptr(THD *thd,
- enum_var_type type __attribute__((unused)),
- LEX_STRING *base __attribute__((unused)))
+uchar *debug_sync_value_ptr(THD *thd)
{
char *value;
- DBUG_ENTER("sys_var_debug_sync::value_ptr");
- DBUG_ASSERT(thd);
+ DBUG_ENTER("debug_sync_value_ptr");
if (opt_debug_sync_timeout)
{
static char on[]= "ON - current signal: '";
// Ensure exclusive access to debug_sync_global.ds_signal
- pthread_mutex_lock(&debug_sync_global.ds_mutex);
+ mysql_mutex_lock(&debug_sync_global.ds_mutex);
size_t lgt= (sizeof(on) /* includes '\0' */ +
debug_sync_global.ds_signal.length() + 1 /* for '\'' */);
@@ -1676,12 +1306,12 @@ uchar *sys_var_debug_sync::value_ptr(THD *thd,
*(vptr++)= '\'';
*vptr= '\0'; /* We have one byte reserved for the worst case. */
}
- pthread_mutex_unlock(&debug_sync_global.ds_mutex);
+ mysql_mutex_unlock(&debug_sync_global.ds_mutex);
}
else
{
/* purecov: begin tested */
- value= strmake_root(thd->mem_root, STRING_WITH_LEN("OFF"));
+ value= const_cast<char*>("OFF");
/* purecov: end */
}
@@ -1701,9 +1331,11 @@ uchar *sys_var_debug_sync::value_ptr(THD *thd,
static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
{
- IF_DBUG(const char *dsp_name= action->sync_point.c_ptr());
- IF_DBUG(const char *sig_emit= action->signal.c_ptr());
- IF_DBUG(const char *sig_wait= action->wait_for.c_ptr());
+#ifndef DBUG_OFF
+ const char *dsp_name= action->sync_point.c_ptr();
+ const char *sig_emit= action->signal.c_ptr();
+ const char *sig_wait= action->wait_for.c_ptr();
+#endif
DBUG_ENTER("debug_sync_execute");
DBUG_ASSERT(thd);
DBUG_ASSERT(action);
@@ -1743,7 +1375,7 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
read access too, to create a memory barrier in order to avoid that
threads just reads an old cached version of the signal.
*/
- pthread_mutex_lock(&debug_sync_global.ds_mutex);
+ mysql_mutex_lock(&debug_sync_global.ds_mutex);
if (action->signal.length())
{
@@ -1757,15 +1389,15 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
debug_sync_emergency_disable(); /* purecov: tested */
}
/* Wake threads waiting in a sync point. */
- pthread_cond_broadcast(&debug_sync_global.ds_cond);
+ mysql_cond_broadcast(&debug_sync_global.ds_cond);
DBUG_PRINT("debug_sync_exec", ("signal '%s' at: '%s'",
sig_emit, dsp_name));
} /* end if (action->signal.length()) */
if (action->wait_for.length())
{
- pthread_mutex_t *old_mutex;
- pthread_cond_t *old_cond;
+ mysql_mutex_t *old_mutex;
+ mysql_cond_t *old_cond= NULL;
int error= 0;
struct timespec abstime;
@@ -1773,11 +1405,20 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
We don't use enter_cond()/exit_cond(). They do not save old
mutex and cond. This would prohibit the use of DEBUG_SYNC
between other places of enter_cond() and exit_cond().
+
+ We need to check for existence of thd->mysys_var to also make
+ it possible to use DEBUG_SYNC framework in scheduler when this
+ variable has been set to NULL.
*/
- old_mutex= thd->mysys_var->current_mutex;
- old_cond= thd->mysys_var->current_cond;
- thd->mysys_var->current_mutex= &debug_sync_global.ds_mutex;
- thd->mysys_var->current_cond= &debug_sync_global.ds_cond;
+ if (thd->mysys_var)
+ {
+ old_mutex= thd->mysys_var->current_mutex;
+ old_cond= thd->mysys_var->current_cond;
+ thd->mysys_var->current_mutex= &debug_sync_global.ds_mutex;
+ thd->mysys_var->current_cond= &debug_sync_global.ds_cond;
+ }
+ else
+ old_mutex= NULL;
set_timespec(abstime, action->timeout);
DBUG_EXECUTE("debug_sync_exec", {
@@ -1796,9 +1437,9 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
while (stringcmp(&debug_sync_global.ds_signal, &action->wait_for) &&
!thd->killed && opt_debug_sync_timeout)
{
- error= pthread_cond_timedwait(&debug_sync_global.ds_cond,
- &debug_sync_global.ds_mutex,
- &abstime);
+ error= mysql_cond_timedwait(&debug_sync_global.ds_cond,
+ &debug_sync_global.ds_mutex,
+ &abstime);
DBUG_EXECUTE("debug_sync", {
/* Functions as DBUG_PRINT args can change keyword and line nr. */
const char *sig_glob= debug_sync_global.ds_signal.c_ptr();
@@ -1831,18 +1472,22 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
protected mutex must always unlocked _before_ mysys_var->mutex
is locked. (See comment in THD::exit_cond().)
*/
- pthread_mutex_unlock(&debug_sync_global.ds_mutex);
- pthread_mutex_lock(&thd->mysys_var->mutex);
- thd->mysys_var->current_mutex= old_mutex;
- thd->mysys_var->current_cond= old_cond;
- thd_proc_info(thd, old_proc_info);
- pthread_mutex_unlock(&thd->mysys_var->mutex);
-
+ mysql_mutex_unlock(&debug_sync_global.ds_mutex);
+ if (old_mutex)
+ {
+ mysql_mutex_lock(&thd->mysys_var->mutex);
+ thd->mysys_var->current_mutex= old_mutex;
+ thd->mysys_var->current_cond= old_cond;
+ thd_proc_info(thd, old_proc_info);
+ mysql_mutex_unlock(&thd->mysys_var->mutex);
+ }
+ else
+ thd_proc_info(thd, old_proc_info);
}
else
{
/* In case we don't wait, we just release the mutex. */
- pthread_mutex_unlock(&debug_sync_global.ds_mutex);
+ mysql_mutex_unlock(&debug_sync_global.ds_mutex);
} /* end if (action->wait_for.length()) */
} /* end if (action->execute) */
@@ -1871,12 +1516,14 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
@param[in] name_len length of sync point name
*/
-void debug_sync(THD *thd, const char *sync_point_name, size_t name_len)
+static void debug_sync(THD *thd, const char *sync_point_name, size_t name_len)
{
+ if (!thd)
+ thd= current_thd;
+
st_debug_sync_control *ds_control= thd->debug_sync_control;
st_debug_sync_action *action;
DBUG_ENTER("debug_sync");
- DBUG_ASSERT(thd);
DBUG_ASSERT(sync_point_name);
DBUG_ASSERT(name_len);
DBUG_ASSERT(ds_control);
diff --git a/sql/debug_sync.h b/sql/debug_sync.h
index 5cd838ec359..4d29d6e7508 100644
--- a/sql/debug_sync.h
+++ b/sql/debug_sync.h
@@ -32,15 +32,6 @@ class THD;
#if defined(ENABLED_DEBUG_SYNC)
-/* Macro to be put in the code at synchronization points. */
-#define DEBUG_SYNC(_thd_, _sync_point_name_) \
- do { if (unlikely(opt_debug_sync_timeout)) \
- debug_sync(_thd_, STRING_WITH_LEN(_sync_point_name_)); \
- } while (0)
-
-/* Command line option --debug-sync-timeout. See mysqld.cc. */
-extern uint opt_debug_sync_timeout;
-
/* Default WAIT_FOR timeout if command line option is given without argument. */
#define DEBUG_SYNC_DEFAULT_WAIT_TIMEOUT 300
@@ -49,13 +40,8 @@ extern int debug_sync_init(void);
extern void debug_sync_end(void);
extern void debug_sync_init_thread(THD *thd);
extern void debug_sync_end_thread(THD *thd);
-extern void debug_sync(THD *thd, const char *sync_point_name, size_t name_len);
extern bool debug_sync_set_action(THD *thd, const char *action_str, size_t len);
-#else /* defined(ENABLED_DEBUG_SYNC) */
-
-#define DEBUG_SYNC(_thd_, _sync_point_name_) /* disabled DEBUG_SYNC */
-
#endif /* defined(ENABLED_DEBUG_SYNC) */
#endif /* DEBUG_SYNC_INCLUDED */
diff --git a/sql/derror.cc b/sql/derror.cc
index 2aaef0e46d9..33835992258 100644
--- a/sql/derror.cc
+++ b/sql/derror.cc
@@ -21,14 +21,27 @@
Read language depeneded messagefile
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "derror.h"
#include "mysys_err.h"
+#include "mysqld.h" // lc_messages_dir
+#include "derror.h" // read_texts
+#include "sql_class.h" // THD
static bool check_error_mesg(const char *file_name, const char **errmsg);
-static bool read_texts(const char *file_name,const char ***point,
- uint error_messages);
static void init_myfunc_errs(void);
+
+C_MODE_START
+static const char **get_server_errmsgs()
+{
+ if (!current_thd)
+ return DEFAULT_ERRMSGS;
+ return CURRENT_THD_ERRMSGS;
+}
+C_MODE_END
+
/**
Read messages from errorfile.
@@ -59,10 +72,11 @@ bool init_errmessage(void)
org_errmsgs= my_error_unregister(ER_ERROR_FIRST, ER_ERROR_LAST);
/* Read messages from file. */
- if (read_texts(ERRMSG_FILE, &errmsgs, ER_ERROR_LAST - ER_ERROR_FIRST + 1) ||
- check_error_mesg(ERRMSG_FILE, errmsgs))
+ if (read_texts(ERRMSG_FILE, my_default_lc_messages->errmsgs->language,
+ &errmsgs, ER_ERROR_LAST - ER_ERROR_FIRST + 1) &&
+ !errmsgs)
{
- x_free(errmsgs);
+ free(errmsgs);
if (org_errmsgs)
{
@@ -85,16 +99,16 @@ bool init_errmessage(void)
}
}
else
- x_free(org_errmsgs); // Free old language
+ free(org_errmsgs); // Free old language
/* Register messages for use with my_error(). */
- if (my_error_register(errmsgs, ER_ERROR_FIRST, ER_ERROR_LAST))
+ if (my_error_register(get_server_errmsgs, ER_ERROR_FIRST, ER_ERROR_LAST))
{
- x_free((uchar*) errmsgs);
+ my_free(errmsgs);
DBUG_RETURN(TRUE);
}
- errmesg= errmsgs; /* Init global variabel */
+ DEFAULT_ERRMSGS= errmsgs; /* Init global variable */
init_myfunc_errs(); /* Init myfunc messages */
DBUG_RETURN(error);
}
@@ -126,19 +140,17 @@ static bool check_error_mesg(const char *file_name, const char **errmsg)
Read text from packed textfile in language-directory.
If we can't read messagefile then it's panic- we can't continue.
-
- @todo
- Convert the character set to server system character set
*/
-static bool read_texts(const char *file_name,const char ***point,
- uint error_messages)
+bool read_texts(const char *file_name, const char *language,
+ const char ***point, uint error_messages)
{
register uint i;
uint count,funktpos,textcount;
size_t length;
File file;
char name[FN_REFLEN];
+ char lang_path[FN_REFLEN];
uchar *buff;
uchar head[32],*pos;
DBUG_ENTER("read_texts");
@@ -147,34 +159,38 @@ static bool read_texts(const char *file_name,const char ***point,
LINT_INIT(buff);
funktpos=0;
- if ((file=my_open(fn_format(name,file_name,language,"",4),
- O_RDONLY | O_SHARE | O_BINARY,
- MYF(0))) < 0)
- goto err; /* purecov: inspected */
+ convert_dirname(lang_path, language, NullS);
+ (void) my_load_path(lang_path, lang_path, lc_messages_dir);
+ if ((file= mysql_file_open(key_file_ERRMSG,
+ fn_format(name, file_name, lang_path, "", 4),
+ O_RDONLY | O_SHARE | O_BINARY,
+ MYF(0))) < 0)
+ {
+ /*
+ Trying pre-5.4 sematics of the --language parameter.
+ It included the language-specific part, e.g.:
+
+ --language=/path/to/english/
+ */
+ if ((file= mysql_file_open(key_file_ERRMSG,
+ fn_format(name, file_name, lc_messages_dir, "", 4),
+ 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.");
+ }
funktpos=1;
- if (my_read(file,(uchar*) head,32,MYF(MY_NABP))) goto err;
+ if (mysql_file_read(file, (uchar*) head, 32, MYF(MY_NABP)))
+ goto err;
funktpos=2;
if (head[0] != (uchar) 254 || head[1] != (uchar) 254 ||
head[2] != 2 || head[3] != 1)
goto err; /* purecov: inspected */
textcount=head[4];
- if (!head[30])
- {
- sql_print_error("Character set information not found in '%s'. \
-Please install the latest version of this file.",name);
- goto err1;
- }
-
- /* TODO: Convert the character set to server system character set */
- if (!get_charset(head[30],MYF(MY_WME)))
- {
- sql_print_error("Character set #%d is not supported for messagefile '%s'",
- (int)head[30],name);
- goto err1;
- }
-
+ error_message_charset_info= system_charset_info;
length=uint2korr(head+6); count=uint2korr(head+8);
if (count < error_messages)
@@ -182,7 +198,7 @@ Please install the latest version of this file.",name);
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!",
name,count,error_messages);
- VOID(my_close(file,MYF(MY_WME)));
+ (void) mysql_file_close(file, MYF(MY_WME));
DBUG_RETURN(1);
}
@@ -194,31 +210,32 @@ Error message file '%s' had only %d error messages, but it should contain at lea
}
buff= (uchar*) (*point + count);
- if (my_read(file, buff, (size_t) count*2,MYF(MY_NABP)))
+ if (mysql_file_read(file, buff, (size_t) count*2, MYF(MY_NABP)))
goto err;
for (i=0, pos= buff ; i< count ; i++)
{
(*point)[i]= (char*) buff+uint2korr(pos);
pos+=2;
}
- if (my_read(file, buff, length, MYF(MY_NABP)))
+ if (mysql_file_read(file, buff, length, MYF(MY_NABP)))
goto err;
for (i=1 ; i < textcount ; i++)
{
point[i]= *point +uint2korr(head+10+i+i);
}
- VOID(my_close(file,MYF(0)));
- DBUG_RETURN(0);
+ (void) mysql_file_close(file, MYF(0));
+
+ i= check_error_mesg(file_name, *point);
+ DBUG_RETURN(i);
err:
sql_print_error((funktpos == 3) ? "Not enough memory for messagefile '%s'" :
(funktpos == 2) ? "Incompatible header in messagefile '%s'. Probably from another version of MariaDB" :
((funktpos == 1) ? "Can't read from messagefile '%s'" :
"Can't find messagefile '%s'"), name);
-err1:
if (file != FERR)
- VOID(my_close(file,MYF(MY_WME)));
+ (void) mysql_file_close(file, MYF(MY_WME));
DBUG_RETURN(1);
} /* read_texts */
diff --git a/sql/derror.h b/sql/derror.h
new file mode 100644
index 00000000000..b2f6331e048
--- /dev/null
+++ b/sql/derror.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef DERROR_INCLUDED
+#define DERROR_INCLUDED
+
+#include "my_global.h" /* uint */
+
+bool init_errmessage(void);
+bool read_texts(const char *file_name, const char *language,
+ const char ***point, uint error_messages);
+
+#endif /* DERROR_INCLUDED */
diff --git a/sql/des_key_file.cc b/sql/des_key_file.cc
index 6e47a04020d..b6b6f4536bc 100644
--- a/sql/des_key_file.cc
+++ b/sql/des_key_file.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2001-2003, 2005-2007 MySQL AB
+/* Copyright (c) 2001, 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
@@ -11,9 +11,12 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
+#include "my_global.h" // HAVE_*
+#include "sql_priv.h"
+#include "des_key_file.h" // st_des_keyschedule, st_des_keyblock
+#include "log.h" // sql_print_error
#include <m_ctype.h>
#ifdef HAVE_OPENSSL
@@ -43,8 +46,9 @@ load_des_key_file(const char *file_name)
DBUG_ENTER("load_des_key_file");
DBUG_PRINT("enter",("name: %s",file_name));
- VOID(pthread_mutex_lock(&LOCK_des_key_file));
- if ((file=my_open(file_name,O_RDONLY | O_BINARY ,MYF(MY_WME))) < 0 ||
+ mysql_mutex_lock(&LOCK_des_key_file);
+ if ((file= mysql_file_open(key_file_des_key_file, file_name,
+ O_RDONLY | O_BINARY, MYF(MY_WME))) < 0 ||
init_io_cache(&io, file, IO_SIZE*2, READ_CACHE, 0, 0, MYF(MY_WME)))
goto error;
@@ -93,10 +97,10 @@ load_des_key_file(const char *file_name)
error:
if (file >= 0)
{
- my_close(file,MYF(0));
+ mysql_file_close(file, MYF(0));
end_io_cache(&io);
}
- VOID(pthread_mutex_unlock(&LOCK_des_key_file));
+ mysql_mutex_unlock(&LOCK_des_key_file);
DBUG_RETURN(result);
}
#endif /* HAVE_OPENSSL */
diff --git a/sql/des_key_file.h b/sql/des_key_file.h
new file mode 100644
index 00000000000..024a1715d47
--- /dev/null
+++ b/sql/des_key_file.h
@@ -0,0 +1,40 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef DES_KEY_FILE_INCLUDED
+#define DES_KEY_FILE_INCLUDED
+
+#ifdef HAVE_OPENSSL
+#include <openssl/des.h>
+
+#include "violite.h" /* DES_cblock, DES_key_schedule */
+
+struct st_des_keyblock
+{
+ DES_cblock key1, key2, key3;
+};
+
+struct st_des_keyschedule
+{
+ DES_key_schedule ks1, ks2, ks3;
+};
+
+extern struct st_des_keyschedule des_keyschedule[10];
+extern uint des_default_key;
+
+bool load_des_key_file(const char *file_name);
+#endif /* HAVE_OPENSSL */
+
+#endif /* DES_KEY_FILE_INCLUDED */
diff --git a/sql/discover.cc b/sql/discover.cc
index 9d1df1d2141..b9dba92a780 100644
--- a/sql/discover.cc
+++ b/sql/discover.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2004-2007 MySQL AB
+/* Copyright (c) 2004, 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
@@ -11,7 +11,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@@ -21,7 +21,9 @@
Functions for discover of frm file from handler
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "discover.h"
#include <my_dir.h>
/**
@@ -57,15 +59,16 @@ int readfrm(const char *name, uchar **frmdata, size_t *len)
*frmdata= NULL; // In case of errors
*len= 0;
error= 1;
- if ((file=my_open(fn_format(index_file,name,"",reg_ext,
- MY_UNPACK_FILENAME|MY_APPEND_EXT),
- O_RDONLY | O_SHARE,
- MYF(0))) < 0)
+ if ((file= mysql_file_open(key_file_frm,
+ fn_format(index_file, name, "", reg_ext,
+ MY_UNPACK_FILENAME|MY_APPEND_EXT),
+ O_RDONLY | O_SHARE,
+ MYF(0))) < 0)
goto err_end;
// Get length of file
error= 2;
- if (my_fstat(file, &state, MYF(0)))
+ if (mysql_file_fstat(file, &state, MYF(0)))
goto err;
read_len= (size_t)state.st_size;
@@ -82,7 +85,7 @@ int readfrm(const char *name, uchar **frmdata, size_t *len)
err:
if (file > 0)
- VOID(my_close(file,MYF(MY_WME)));
+ (void) mysql_file_close(file, MYF(MY_WME));
err_end: /* Here when no file */
DBUG_RETURN (error);
@@ -112,13 +115,15 @@ int writefrm(const char *name, const uchar *frmdata, size_t len)
DBUG_PRINT("enter",("name: '%s' len: %lu ",name, (ulong) len));
error= 0;
- if ((file=my_create(fn_format(index_file,name,"",reg_ext,
- MY_UNPACK_FILENAME|MY_APPEND_EXT),
- CREATE_MODE,O_RDWR | O_TRUNC,MYF(MY_WME))) >= 0)
+ if ((file= mysql_file_create(key_file_frm,
+ fn_format(index_file, name, "", reg_ext,
+ MY_UNPACK_FILENAME | MY_APPEND_EXT),
+ CREATE_MODE, O_RDWR | O_TRUNC,
+ MYF(MY_WME))) >= 0)
{
- if (my_write(file, frmdata, len,MYF(MY_WME | MY_NABP)))
+ if (mysql_file_write(file, frmdata, len, MYF(MY_WME | MY_NABP)))
error= 2;
- VOID(my_close(file,MYF(0)));
+ (void) mysql_file_close(file, MYF(0));
}
DBUG_RETURN(error);
} /* writefrm */
diff --git a/sql/discover.h b/sql/discover.h
new file mode 100644
index 00000000000..a663e44128d
--- /dev/null
+++ b/sql/discover.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef DISCOVER_INCLUDED
+#define DISCOVER_INCLUDED
+
+#include "my_global.h" /* uchar */
+
+int readfrm(const char *name, uchar **data, size_t *length);
+int writefrm(const char* name, const uchar* data, size_t len);
+
+#endif /* DISCOVER_INCLUDED */
diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc
index e90e6ec4bf9..dfd35583581 100644
--- a/sql/event_data_objects.cc
+++ b/sql/event_data_objects.cc
@@ -12,15 +12,26 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#define MYSQL_LEX 1
-#include "mysql_priv.h"
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_priv.h"
+#include "unireg.h"
+#include "sql_parse.h" // parse_sql
+#include "strfunc.h" // find_string_in_array
+#include "sql_db.h" // get_default_db_collation
+#include "sql_time.h" // interval_type_to_name,
+ // date_add_interval,
+ // calc_time_diff
+#include "tztime.h" // my_tz_find, my_tz_OFFSET0, struct Time_zone
+#include "sql_acl.h" // EVENT_ACL, SUPER_ACL
+#include "sp.h" // load_charset, load_collation
#include "events.h"
#include "event_data_objects.h"
#include "event_db_repository.h"
#include "sp_head.h"
+#include "sql_show.h" // append_definer, append_identifier
/**
@addtogroup Event_Scheduler
@@ -166,7 +177,7 @@ Event_queue_element_for_exec::init(LEX_STRING db, LEX_STRING n)
return TRUE;
if (!(name.str= my_strndup(n.str, name.length= n.length, MYF(MY_WME))))
{
- my_free((uchar*) dbname.str, MYF(0));
+ my_free(dbname.str);
return TRUE;
}
return FALSE;
@@ -182,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((uchar*) dbname.str, MYF(0));
- my_free((uchar*) name.str, MYF(0));
+ my_free(dbname.str);
+ my_free(name.str);
}
@@ -198,7 +209,7 @@ Event_basic::Event_basic()
{
DBUG_ENTER("Event_basic::Event_basic");
/* init memory root */
- init_alloc_root(&mem_root, 256, 512);
+ init_sql_alloc(&mem_root, 256, 512);
dbname.str= name.str= NULL;
dbname.length= name.length= 0;
time_zone= NULL;
@@ -280,7 +291,6 @@ Event_basic::load_time_zone(THD *thd, const LEX_STRING tz_name)
*/
Event_queue_element::Event_queue_element():
- status_changed(FALSE), last_executed_changed(FALSE),
on_completion(Event_parse_data::ON_COMPLETION_DROP),
status(Event_parse_data::ENABLED), expression(0), dropped(FALSE),
execution_count(0)
@@ -529,7 +539,6 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table)
TIME_NO_ZERO_DATE);
last_executed= my_tz_OFFSET0->TIME_to_gmt_sec(&time,&not_used);
}
- last_executed_changed= FALSE;
if ((ptr= get_field(&mem_root, table->field[ET_FIELD_STATUS])) == NullS)
DBUG_RETURN(TRUE);
@@ -924,7 +933,6 @@ Event_queue_element::compute_next_execution_time()
DBUG_PRINT("info",("One-time event will be dropped: %d.", dropped));
status= Event_parse_data::DISABLED;
- status_changed= TRUE;
}
goto ret;
}
@@ -944,7 +952,6 @@ Event_queue_element::compute_next_execution_time()
dropped= TRUE;
DBUG_PRINT("info", ("Dropped: %d", dropped));
status= Event_parse_data::DISABLED;
- status_changed= TRUE;
goto ret;
}
@@ -1007,7 +1014,6 @@ Event_queue_element::compute_next_execution_time()
if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
dropped= TRUE;
status= Event_parse_data::DISABLED;
- status_changed= TRUE;
}
else
{
@@ -1097,7 +1103,6 @@ Event_queue_element::compute_next_execution_time()
execute_at= 0;
execute_at_null= TRUE;
status= Event_parse_data::DISABLED;
- status_changed= TRUE;
if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
dropped= TRUE;
}
@@ -1133,50 +1138,11 @@ void
Event_queue_element::mark_last_executed(THD *thd)
{
last_executed= thd->query_start();
- last_executed_changed= TRUE;
execution_count++;
}
-/*
- Saves status and last_executed_at to the disk if changed.
-
- SYNOPSIS
- Event_queue_element::update_timing_fields()
- thd - thread context
-
- RETURN VALUE
- FALSE OK
- TRUE Error while opening mysql.event for writing or during
- write on disk
-*/
-
-bool
-Event_queue_element::update_timing_fields(THD *thd)
-{
- Event_db_repository *db_repository= Events::get_db_repository();
- int ret;
-
- DBUG_ENTER("Event_queue_element::update_timing_fields");
-
- DBUG_PRINT("enter", ("name: %*s", (int) name.length, name.str));
-
- /* No need to update if nothing has changed */
- if (!(status_changed || last_executed_changed))
- DBUG_RETURN(0);
-
- ret= db_repository->update_timing_fields_for_event(thd,
- dbname, name,
- last_executed_changed,
- last_executed,
- status_changed,
- (ulonglong) status);
- last_executed_changed= status_changed= FALSE;
- DBUG_RETURN(ret);
-}
-
-
static
void
append_datetime(String *buf, Time_zone *time_zone, my_time_t secs,
@@ -1227,7 +1193,9 @@ Event_timed::get_create_event(THD *thd, String *buf)
expression))
DBUG_RETURN(EVEX_MICROSECOND_UNSUP);
- buf->append(STRING_WITH_LEN("CREATE EVENT "));
+ 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);
if (expression)
@@ -1369,7 +1337,7 @@ Event_job_data::execute(THD *thd, bool drop)
DBUG_ENTER("Event_job_data::execute");
- mysql_reset_thd_for_next_command(thd, 0);
+ mysql_reset_thd_for_next_command(thd);
/*
MySQL parser currently assumes that current database is either
@@ -1389,6 +1357,8 @@ Event_job_data::execute(THD *thd, bool drop)
*/
thd->set_db(dbname.str, dbname.length);
+ lex_start(thd);
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (event_sctx.change_security_context(thd,
&definer_user, &definer_host,
@@ -1398,12 +1368,11 @@ Event_job_data::execute(THD *thd, bool drop)
"[%s].[%s.%s] execution failed, "
"failed to authenticate the user.",
definer.str, dbname.str, name.str);
- goto end_no_lex_start;
+ goto end;
}
#endif
- if (check_access(thd, EVENT_ACL, dbname.str,
- 0, 0, 0, is_schema_db(dbname.str, dbname.length)))
+ if (check_access(thd, EVENT_ACL, dbname.str, NULL, NULL, 0, 0))
{
/*
This aspect of behavior is defined in the worklog,
@@ -1415,11 +1384,11 @@ Event_job_data::execute(THD *thd, bool drop)
"[%s].[%s.%s] execution failed, "
"user no longer has EVENT privilege.",
definer.str, dbname.str, name.str);
- goto end_no_lex_start;
+ goto end;
}
if (construct_sp_sql(thd, &sp_sql))
- goto end_no_lex_start;
+ goto end;
/*
Set up global thread attributes to reflect the properties of
@@ -1439,8 +1408,6 @@ Event_job_data::execute(THD *thd, bool drop)
if (parser_state.init(thd, thd->query(), thd->query_length()))
goto end;
- lex_start(thd);
-
if (parse_sql(thd, & parser_state, creation_ctx))
{
sql_print_error("Event Scheduler: "
@@ -1471,13 +1438,6 @@ Event_job_data::execute(THD *thd, bool drop)
}
end:
- if (thd->lex->sphead) /* NULL only if a parse error */
- {
- delete thd->lex->sphead;
- thd->lex->sphead= NULL;
- }
-
-end_no_lex_start:
if (drop && !thd->is_fatal_error)
{
/*
@@ -1516,12 +1476,11 @@ end_no_lex_start:
if (save_sctx)
event_sctx.restore_security_context(thd, save_sctx);
#endif
- lex_end(thd->lex);
thd->lex->unit.cleanup();
thd->end_statement();
thd->cleanup_after_query();
/* Avoid races with SHOW PROCESSLIST */
- thd->set_query(NULL, 0);
+ thd->reset_query();
DBUG_PRINT("info", ("EXECUTED %s.%s ret: %d", dbname.str, name.str, ret));
diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h
index 97bc517911e..2483c564dff 100644
--- a/sql/event_data_objects.h
+++ b/sql/event_data_objects.h
@@ -1,6 +1,6 @@
#ifndef _EVENT_DATA_OBJECTS_H_
#define _EVENT_DATA_OBJECTS_H_
-/* Copyright (c) 2004-2008 MySQL AB
+/* Copyright (c) 2004, 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
@@ -13,7 +13,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@addtogroup Event_Scheduler
@@ -23,6 +23,12 @@
*/
#include "event_parse_data.h"
+#include "thr_lock.h" /* thr_lock_type */
+
+class Field;
+class THD;
+class Time_zone;
+struct TABLE;
class Event_queue_element_for_exec
{
@@ -76,10 +82,6 @@ protected:
class Event_queue_element : public Event_basic
{
-protected:
- bool status_changed;
- bool last_executed_changed;
-
public:
int on_completion;
int status;
@@ -111,9 +113,6 @@ public:
void
mark_last_executed(THD *thd);
-
- bool
- update_timing_fields(THD *thd);
};
diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc
index 73b8eaf473c..37dff0da714 100644
--- a/sql/event_db_repository.cc
+++ b/sql/event_db_repository.cc
@@ -12,15 +12,23 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "sql_base.h" // close_thread_tables
#include "event_db_repository.h"
+#include "key.h" // key_copy
+#include "sql_db.h" // get_default_db_collation
+#include "sql_time.h" // interval_type_to_name
+#include "tztime.h" // struct Time_zone
+#include "sql_acl.h" // SUPER_ACL, MYSQL_DB_FIELD_COUNT, mysql_db_table_fields
+#include "records.h" // init_read_record, end_read_record
#include "sp_head.h"
#include "event_data_objects.h"
#include "events.h"
#include "sql_show.h"
+#include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT
/**
@addtogroup Event_Scheduler
@@ -47,7 +55,7 @@ const TABLE_FIELD_TYPE event_table_fields[ET_FIELD_COUNT] =
},
{
{ C_STRING_WITH_LEN("definer") },
- { C_STRING_WITH_LEN("char(77)") },
+ { C_STRING_WITH_LEN("char(") },
{ C_STRING_WITH_LEN("utf8") }
},
{
@@ -193,7 +201,7 @@ mysql_event_fill_row(THD *thd,
TABLE *table,
Event_parse_data *et,
sp_head *sp,
- ulong sql_mode,
+ ulonglong sql_mode,
my_bool is_update)
{
CHARSET_INFO *scs= system_charset_info;
@@ -401,7 +409,6 @@ Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table,
TABLE *event_table,
const char *db)
{
- int ret=0;
CHARSET_INFO *scs= system_charset_info;
KEY *key_info;
uint key_len;
@@ -411,7 +418,14 @@ Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table,
DBUG_ENTER("Event_db_repository::index_read_for_db_for_i_s");
DBUG_PRINT("info", ("Using prefix scanning on PK"));
- event_table->file->ha_index_init(0, 1);
+
+ int ret= event_table->file->ha_index_init(0, 1);
+ if (ret)
+ {
+ event_table->file->print_error(ret, MYF(0));
+ DBUG_RETURN(true);
+ }
+
key_info= event_table->key_info;
if (key_info->key_parts == 0 ||
@@ -436,7 +450,7 @@ Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table,
key_copy(key_buf, event_table->record[0], key_info, key_len);
if (!(ret= event_table->file->ha_index_read_map(event_table->record[0],
key_buf,
- (key_part_map) 1,
+ (key_part_map)1,
HA_READ_KEY_EXACT)))
{
DBUG_PRINT("info",("Found rows. Let's retrieve them. ret=%d", ret));
@@ -523,18 +537,28 @@ Event_db_repository::table_scan_all_for_i_s(THD *thd, TABLE *schema_table,
*/
bool
-Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *tables,
+Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *i_s_table,
const char *db)
{
- TABLE *schema_table= tables->table;
- TABLE *event_table= NULL;
+ TABLE *schema_table= i_s_table->table;
+ Open_tables_backup open_tables_backup;
+ TABLE_LIST event_table;
int ret= 0;
DBUG_ENTER("Event_db_repository::fill_schema_events");
DBUG_PRINT("info",("db=%s", db? db:"(null)"));
- if (open_event_table(thd, TL_READ, &event_table))
+ event_table.init_one_table("mysql", 5, "event", 5, "event", TL_READ);
+
+ if (open_system_tables_for_read(thd, &event_table, &open_tables_backup))
+ DBUG_RETURN(TRUE);
+
+ if (table_intact.check(event_table.table, &event_table_def))
+ {
+ close_system_tables(thd, &open_tables_backup);
+ my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
DBUG_RETURN(TRUE);
+ }
/*
1. SELECT I_S => use table scan. I_S.EVENTS does not guarantee order
@@ -546,11 +570,11 @@ Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *tables,
every single row's `db` with the schema which we show.
*/
if (db)
- ret= index_read_for_db_for_i_s(thd, schema_table, event_table, db);
+ ret= index_read_for_db_for_i_s(thd, schema_table, event_table.table, db);
else
- ret= table_scan_all_for_i_s(thd, schema_table, event_table);
+ ret= table_scan_all_for_i_s(thd, schema_table, event_table.table);
- close_thread_tables(thd);
+ close_system_tables(thd, &open_tables_backup);
DBUG_PRINT("info", ("Return code=%d", ret));
DBUG_RETURN(ret);
@@ -586,13 +610,10 @@ 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", "event", lock_type);
+ tables.init_one_table("mysql", 5, "event", 5, "event", lock_type);
- if (simple_open_n_lock_tables(thd, &tables))
- {
- close_thread_tables(thd);
+ if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
DBUG_RETURN(TRUE);
- }
*table= tables.table;
tables.table->use_all_columns();
@@ -637,7 +658,13 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
int ret= 1;
TABLE *table= NULL;
sp_head *sp= thd->lex->sphead;
- ulong saved_mode= thd->variables.sql_mode;
+ ulonglong saved_mode= thd->variables.sql_mode;
+ /*
+ Take a savepoint to release only the lock on mysql.event
+ table at the end but keep the global read lock and
+ possible other locks taken by the caller.
+ */
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
DBUG_ENTER("Event_db_repository::create_event");
@@ -714,8 +741,9 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
ret= 0;
end:
- if (table)
- close_thread_tables(thd);
+ close_thread_tables(thd);
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+
thd->variables.sql_mode= saved_mode;
DBUG_RETURN(test(ret));
}
@@ -747,7 +775,13 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
CHARSET_INFO *scs= system_charset_info;
TABLE *table= NULL;
sp_head *sp= thd->lex->sphead;
- ulong saved_mode= thd->variables.sql_mode;
+ ulonglong saved_mode= thd->variables.sql_mode;
+ /*
+ Take a savepoint to release only the lock on mysql.event
+ table at the end but keep the global read lock and
+ possible other locks taken by the caller.
+ */
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
int ret= 1;
DBUG_ENTER("Event_db_repository::update_event");
@@ -825,8 +859,9 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
ret= 0;
end:
- if (table)
- close_thread_tables(thd);
+ close_thread_tables(thd);
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+
thd->variables.sql_mode= saved_mode;
DBUG_RETURN(test(ret));
}
@@ -851,6 +886,12 @@ Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name,
bool drop_if_exists)
{
TABLE *table= NULL;
+ /*
+ Take a savepoint to release only the lock on mysql.event
+ table at the end but keep the global read lock and
+ possible other locks taken by the caller.
+ */
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
int ret= 1;
DBUG_ENTER("Event_db_repository::drop_event");
@@ -879,8 +920,8 @@ Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name,
ret= 0;
end:
- if (table)
- close_thread_tables(thd);
+ close_thread_tables(thd);
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
DBUG_RETURN(test(ret));
}
@@ -954,33 +995,13 @@ Event_db_repository::find_named_event(LEX_STRING db, LEX_STRING name,
void
Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema)
{
- DBUG_ENTER("Event_db_repository::drop_schema_events");
- drop_events_by_field(thd, ET_FIELD_DB, schema);
- DBUG_VOID_RETURN;
-}
-
-
-/**
- Drops all events which have a specific value of a field.
-
- @pre The thread handle has no open tables.
-
- @param[in,out] thd Thread
- @param[in,out] table mysql.event TABLE
- @param[in] field Which field of the row to use for matching
- @param[in] field_value The value that should match
-*/
-
-void
-Event_db_repository::drop_events_by_field(THD *thd,
- enum enum_events_table_field field,
- LEX_STRING field_value)
-{
int ret= 0;
TABLE *table= NULL;
READ_RECORD read_record_info;
- DBUG_ENTER("Event_db_repository::drop_events_by_field");
- DBUG_PRINT("enter", ("field=%d field_value=%s", field, field_value.str));
+ 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));
if (open_event_table(thd, TL_WRITE, &table))
DBUG_VOID_RETURN;
@@ -1001,7 +1022,7 @@ Event_db_repository::drop_events_by_field(THD *thd,
get_field(thd->mem_root,
table->field[ET_FIELD_NAME])));
- if (!sortcmp_lex_string(et_field_lex, field_value, 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])))
@@ -1013,6 +1034,12 @@ Event_db_repository::drop_events_by_field(THD *thd,
end:
close_thread_tables(thd);
+ /*
+ Make sure to only release the MDL lock on mysql.event, not other
+ metadata locks DROP DATABASE might have acquired.
+ */
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+
DBUG_VOID_RETURN;
}
@@ -1032,24 +1059,40 @@ Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname,
LEX_STRING name, Event_basic *etn)
{
bool ret;
- TABLE *table= NULL;
- ulong saved_mode= thd->variables.sql_mode;
+ ulonglong saved_mode= thd->variables.sql_mode;
+ Open_tables_backup open_tables_backup;
+ TABLE_LIST event_table;
DBUG_ENTER("Event_db_repository::load_named_event");
DBUG_PRINT("enter",("thd: 0x%lx name: %*s", (long) thd,
(int) name.length, name.str));
+ event_table.init_one_table("mysql", 5, "event", 5, "event", TL_READ);
+
/* Reset sql_mode during data dictionary operations. */
thd->variables.sql_mode= 0;
- if (!(ret= open_event_table(thd, TL_READ, &table)))
+ /*
+ We don't use open_event_table() here to make sure that SHOW
+ CREATE EVENT works properly in transactional context, and
+ does not release transactional metadata locks when the
+ event table is closed.
+ */
+ if (!(ret= open_system_tables_for_read(thd, &event_table, &open_tables_backup)))
{
- if ((ret= find_named_event(dbname, name, table)))
+ if (table_intact.check(event_table.table, &event_table_def))
+ {
+ close_system_tables(thd, &open_tables_backup);
+ my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
+ if ((ret= find_named_event(dbname, name, event_table.table)))
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
- else if ((ret= etn->load_from_row(thd, table)))
+ else if ((ret= etn->load_from_row(thd, event_table.table)))
my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "event");
- close_thread_tables(thd);
+ close_system_tables(thd, &open_tables_backup);
}
thd->variables.sql_mode= saved_mode;
@@ -1069,15 +1112,14 @@ Event_db_repository::
update_timing_fields_for_event(THD *thd,
LEX_STRING event_db_name,
LEX_STRING event_name,
- bool update_last_executed,
my_time_t last_executed,
- bool update_status,
ulonglong status)
{
TABLE *table= NULL;
Field **fields;
int ret= 1;
bool save_binlog_row_based;
+ MYSQL_TIME time;
DBUG_ENTER("Event_db_repository::update_timing_fields_for_event");
@@ -1085,8 +1127,8 @@ update_timing_fields_for_event(THD *thd,
Turn off row binlogging of event timing updates. These are not used
for RBR of events replicated to the slave.
*/
- save_binlog_row_based= thd->current_stmt_binlog_row_based;
- thd->clear_current_stmt_binlog_row_based();
+ if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
+ thd->clear_current_stmt_binlog_format_row();
DBUG_ASSERT(thd->security_ctx->master_access & SUPER_ACL);
@@ -1102,19 +1144,12 @@ update_timing_fields_for_event(THD *thd,
/* Don't update create on row update. */
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
- if (update_last_executed)
- {
- MYSQL_TIME time;
- my_tz_OFFSET0->gmt_sec_to_TIME(&time, last_executed);
+ my_tz_OFFSET0->gmt_sec_to_TIME(&time, last_executed);
+ fields[ET_FIELD_LAST_EXECUTED]->set_notnull();
+ fields[ET_FIELD_LAST_EXECUTED]->store_time(&time);
- fields[ET_FIELD_LAST_EXECUTED]->set_notnull();
- fields[ET_FIELD_LAST_EXECUTED]->store_time(&time);
- }
- if (update_status)
- {
- fields[ET_FIELD_STATUS]->set_notnull();
- fields[ET_FIELD_STATUS]->store(status, TRUE);
- }
+ fields[ET_FIELD_STATUS]->set_notnull();
+ fields[ET_FIELD_STATUS]->store(status, TRUE);
if ((ret= table->file->ha_update_row(table->record[1], table->record[0])))
{
@@ -1126,9 +1161,12 @@ update_timing_fields_for_event(THD *thd,
end:
if (table)
- close_thread_tables(thd);
+ close_mysql_tables(thd);
+
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(test(ret));
}
@@ -1158,11 +1196,10 @@ Event_db_repository::check_system_tables(THD *thd)
DBUG_ENTER("Event_db_repository::check_system_tables");
DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
-
/* Check mysql.db */
- tables.init_one_table("mysql", "db", TL_READ);
+ tables.init_one_table("mysql", 5, "db", 2, "db", TL_READ);
- if (simple_open_n_lock_tables(thd, &tables))
+ if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{
ret= 1;
sql_print_error("Cannot open mysql.db");
@@ -1172,12 +1209,12 @@ Event_db_repository::check_system_tables(THD *thd)
if (table_intact.check(tables.table, &mysql_db_table_def))
ret= 1;
- close_thread_tables(thd);
+ close_mysql_tables(thd);
}
/* Check mysql.user */
- tables.init_one_table("mysql", "user", TL_READ);
+ tables.init_one_table("mysql", 5, "user", 4, "user", TL_READ);
- if (simple_open_n_lock_tables(thd, &tables))
+ if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{
ret= 1;
sql_print_error("Cannot open mysql.user");
@@ -1192,12 +1229,12 @@ Event_db_repository::check_system_tables(THD *thd)
event_priv_column_position);
ret= 1;
}
- close_thread_tables(thd);
+ close_mysql_tables(thd);
}
/* Check mysql.event */
- tables.init_one_table("mysql", "event", TL_READ);
+ tables.init_one_table("mysql", 5, "event", 5, "event", TL_READ);
- if (simple_open_n_lock_tables(thd, &tables))
+ if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{
ret= 1;
sql_print_error("Cannot open mysql.event");
@@ -1206,7 +1243,7 @@ Event_db_repository::check_system_tables(THD *thd)
{
if (table_intact.check(tables.table, &event_table_def))
ret= 1;
- close_thread_tables(thd);
+ close_mysql_tables(thd);
}
DBUG_RETURN(test(ret));
diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h
index 09cbea737ba..a2862790be1 100644
--- a/sql/event_db_repository.h
+++ b/sql/event_db_repository.h
@@ -13,7 +13,8 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
/**
@addtogroup Event_Scheduler
@@ -91,7 +92,7 @@ public:
bool
load_named_event(THD *thd, LEX_STRING dbname, LEX_STRING name, Event_basic *et);
- bool
+ static bool
open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table);
bool
@@ -101,17 +102,12 @@ public:
update_timing_fields_for_event(THD *thd,
LEX_STRING event_db_name,
LEX_STRING event_name,
- bool update_last_executed,
my_time_t last_executed,
- bool update_status,
ulonglong status);
public:
static bool
check_system_tables(THD *thd);
private:
- void
- drop_events_by_field(THD *thd, enum enum_events_table_field field,
- LEX_STRING field_value);
bool
index_read_for_db_for_i_s(THD *thd, TABLE *schema_table, TABLE *event_table,
const char *db);
diff --git a/sql/event_parse_data.cc b/sql/event_parse_data.cc
index 5ae6db4ee13..ad812a6aa5d 100644
--- a/sql/event_parse_data.cc
+++ b/sql/event_parse_data.cc
@@ -12,12 +12,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-1301 USA
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
#include "sp_head.h"
#include "event_parse_data.h"
+#include "sql_time.h" // TIME_to_timestamp
/*
Returns a new instance
@@ -255,7 +256,6 @@ wrong_value:
int
Event_parse_data::init_interval(THD *thd)
{
- String value;
INTERVAL interval_tmp;
DBUG_ENTER("Event_parse_data::init_interval");
@@ -277,8 +277,7 @@ Event_parse_data::init_interval(THD *thd)
if (item_expression->fix_fields(thd, &item_expression))
goto wrong_value;
- value.alloc(MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN);
- if (get_interval_value(item_expression, interval, &value, &interval_tmp))
+ if (get_interval_value(item_expression, interval, &interval_tmp))
goto wrong_value;
expression= 0;
diff --git a/sql/event_parse_data.h b/sql/event_parse_data.h
index ae834cf9a9f..faf42db623a 100644
--- a/sql/event_parse_data.h
+++ b/sql/event_parse_data.h
@@ -12,12 +12,17 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef _EVENT_PARSE_DATA_H_
#define _EVENT_PARSE_DATA_H_
+#include "sql_list.h" /* Sql_alloc */
+
+class Item;
+class THD;
+class sp_name;
+
#define EVEX_GET_FIELD_FAILED -2
#define EVEX_BAD_PARAMS -5
#define EVEX_MICROSECOND_UNSUP -6
diff --git a/sql/event_queue.cc b/sql/event_queue.cc
index e9df99e7de1..40fddff094c 100644
--- a/sql/event_queue.cc
+++ b/sql/event_queue.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2004-2008 MySQL AB
+/* Copyright (c) 2004, 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
@@ -11,11 +11,18 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
#include "event_queue.h"
#include "event_data_objects.h"
+#include "event_db_repository.h"
+#include "events.h"
+#include "sql_audit.h"
+#include "tztime.h" // my_tz_find, my_tz_OFFSET0, struct Time_zone
+#include "log.h" // sql_print_error
+#include "sql_class.h" // struct THD
/**
@addtogroup Event_Scheduler
@@ -94,21 +101,16 @@ Event_queue::Event_queue()
mutex_queue_data_attempting_lock(FALSE),
waiting_on_cond(FALSE)
{
- /*
- Inconsisent usage between LOCK_event_queue and LOCK_scheduler_state and
- LOCK_open
- */
- my_pthread_mutex_init(&LOCK_event_queue, MY_MUTEX_INIT_FAST,
- "LOCK_event_queue", MYF_NO_DEADLOCK_DETECTION);
- pthread_cond_init(&COND_queue_state, NULL);
+ mysql_mutex_init(key_LOCK_event_queue, &LOCK_event_queue, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_COND_queue_state, &COND_queue_state, NULL);
}
Event_queue::~Event_queue()
{
deinit_queue();
- pthread_mutex_destroy(&LOCK_event_queue);
- pthread_cond_destroy(&COND_queue_state);
+ mysql_mutex_destroy(&LOCK_event_queue);
+ mysql_cond_destroy(&COND_queue_state);
}
@@ -215,7 +217,7 @@ Event_queue::create_event(THD *thd, Event_queue_element *new_element,
LOCK_QUEUE_DATA();
*created= (queue_insert_safe(&queue, (uchar *) new_element) == FALSE);
dbug_dump_queue(thd->query_start());
- pthread_cond_broadcast(&COND_queue_state);
+ mysql_cond_broadcast(&COND_queue_state);
UNLOCK_QUEUE_DATA();
DBUG_RETURN(!*created);
@@ -263,7 +265,7 @@ Event_queue::update_event(THD *thd, LEX_STRING dbname, LEX_STRING name,
{
DBUG_PRINT("info", ("new event in the queue: 0x%lx", (long) new_element));
queue_insert_safe(&queue, (uchar *) new_element);
- pthread_cond_broadcast(&COND_queue_state);
+ mysql_cond_broadcast(&COND_queue_state);
}
dbug_dump_queue(thd->query_start());
@@ -351,7 +353,7 @@ Event_queue::drop_matching_events(THD *thd, LEX_STRING pattern,
i++;
}
/*
- We don't call pthread_cond_broadcast(&COND_queue_state);
+ We don't call mysql_cond_broadcast(&COND_queue_state);
If we remove the top event:
1. The queue is empty. The scheduler will wake up at some time and
realize that the queue is empty. If create_event() comes inbetween
@@ -451,7 +453,6 @@ Event_queue::recalculate_activation_times(THD *thd)
i++)
{
((Event_queue_element*)queue_element(&queue, i))->compute_next_execution_time();
- ((Event_queue_element*)queue_element(&queue, i))->update_timing_fields(thd);
}
queue_fix(&queue);
/*
@@ -582,6 +583,8 @@ Event_queue::get_top_for_execution_if_time(THD *thd,
{
bool ret= FALSE;
*event_name= NULL;
+ my_time_t UNINIT_VAR(last_executed);
+ int UNINIT_VAR(status);
DBUG_ENTER("Event_queue::get_top_for_execution_if_time");
LOCK_QUEUE_DATA();
@@ -601,6 +604,9 @@ Event_queue::get_top_for_execution_if_time(THD *thd,
/* There are no events in the queue */
next_activation_at= 0;
+ /* Release any held audit resources before waiting */
+ mysql_audit_release(thd);
+
/* Wait on condition until signaled. Release LOCK_queue while waiting. */
cond_wait(thd, NULL, queue_empty_msg, SCHED_FUNC, __LINE__);
@@ -618,8 +624,10 @@ Event_queue::get_top_for_execution_if_time(THD *thd,
Not yet time for top event, wait on condition with
time or until signaled. Release LOCK_queue while waiting.
*/
- struct timespec top_time;
- set_timespec_time_nsec(top_time, next_activation_at*1000000000ULL);
+ struct timespec top_time= { next_activation_at, 0 };
+
+ /* Release any held audit resources before waiting */
+ mysql_audit_release(thd);
cond_wait(thd, &top_time, queue_wait_msg, SCHED_FUNC, __LINE__);
continue;
@@ -640,8 +648,14 @@ Event_queue::get_top_for_execution_if_time(THD *thd,
top->execution_count++;
(*event_name)->dropped= top->dropped;
+ /*
+ Save new values of last_executed timestamp and event status on stack
+ in order to be able to update event description in system table once
+ QUEUE_DATA lock is released.
+ */
+ last_executed= top->last_executed;
+ status= top->status;
- top->update_timing_fields(thd);
if (top->status == Event_parse_data::DISABLED)
{
DBUG_PRINT("info", ("removing from the queue"));
@@ -664,9 +678,16 @@ end:
ret, (long) *event_name));
if (*event_name)
+ {
DBUG_PRINT("info", ("db: %s name: %s",
(*event_name)->dbname.str, (*event_name)->name.str));
+ Event_db_repository *db_repository= Events::get_db_repository();
+ (void) db_repository->update_timing_fields_for_event(thd,
+ (*event_name)->dbname, (*event_name)->name,
+ last_executed, (ulonglong) status);
+ }
+
DBUG_RETURN(ret);
}
@@ -689,7 +710,7 @@ Event_queue::lock_data(const char *func, uint line)
mutex_last_attempted_lock_in_func= func;
mutex_last_attempted_lock_at_line= line;
mutex_queue_data_attempting_lock= TRUE;
- pthread_mutex_lock(&LOCK_event_queue);
+ mysql_mutex_lock(&LOCK_event_queue);
mutex_last_attempted_lock_in_func= "";
mutex_last_attempted_lock_at_line= 0;
mutex_queue_data_attempting_lock= FALSE;
@@ -720,19 +741,19 @@ Event_queue::unlock_data(const char *func, uint line)
mutex_last_unlocked_at_line= line;
mutex_queue_data_locked= FALSE;
mutex_last_unlocked_in_func= func;
- pthread_mutex_unlock(&LOCK_event_queue);
+ mysql_mutex_unlock(&LOCK_event_queue);
DBUG_VOID_RETURN;
}
/*
- Wrapper for pthread_cond_wait/timedwait
+ Wrapper for mysql_cond_wait/timedwait
SYNOPSIS
Event_queue::cond_wait()
thd Thread (Could be NULL during shutdown procedure)
msg Message for thd->proc_info
- abstime If not null then call pthread_cond_timedwait()
+ abstime If not null then call mysql_cond_timedwait()
func Which function is requesting cond_wait
line On which line cond_wait is requested
*/
@@ -753,10 +774,11 @@ Event_queue::cond_wait(THD *thd, struct timespec *abstime, const char* msg,
{
DBUG_PRINT("info", ("pthread_cond_%swait", abstime ? "timed" : ""));
if (!abstime)
- pthread_cond_wait(&COND_queue_state, &LOCK_event_queue);
+ mysql_cond_wait(&COND_queue_state, &LOCK_event_queue);
else
- pthread_cond_timedwait(&COND_queue_state, &LOCK_event_queue, abstime);
+ mysql_cond_timedwait(&COND_queue_state, &LOCK_event_queue, abstime);
}
+
mutex_last_locked_in_func= func;
mutex_last_locked_at_line= line;
mutex_queue_data_locked= TRUE;
diff --git a/sql/event_queue.h b/sql/event_queue.h
index 7d9b13215b6..affa306b259 100644
--- a/sql/event_queue.h
+++ b/sql/event_queue.h
@@ -1,6 +1,6 @@
#ifndef _EVENT_QUEUE_H_
#define _EVENT_QUEUE_H_
-/* Copyright (c) 2004-2007 MySQL AB
+/* Copyright (c) 2004, 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
@@ -13,7 +13,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@@ -25,6 +25,15 @@
Queue of events awaiting execution.
*/
+#ifdef HAVE_PSI_INTERFACE
+extern PSI_mutex_key key_LOCK_event_queue;
+extern PSI_cond_key key_COND_queue_state;
+#endif /* HAVE_PSI_INTERFACE */
+
+#include "queues.h" // QUEUE
+#include "sql_string.h" /* LEX_STRING */
+#include "my_time.h" /* my_time_t, interval_type */
+
class Event_basic;
class Event_queue_element;
class Event_queue_element_for_exec;
@@ -101,8 +110,8 @@ private:
dbug_dump_queue(my_time_t now);
/* LOCK_event_queue is the mutex which protects the access to the queue. */
- pthread_mutex_t LOCK_event_queue;
- pthread_cond_t COND_queue_state;
+ mysql_mutex_t LOCK_event_queue;
+ mysql_cond_t COND_queue_state;
/* The sorted queue with the Event_queue_element objects */
QUEUE queue;
diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc
index 07c9cf4f625..beb3c864662 100644
--- a/sql/event_scheduler.cc
+++ b/sql/event_scheduler.cc
@@ -1,5 +1,4 @@
-/*
- Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,15 +11,17 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "event_scheduler.h"
#include "events.h"
#include "event_data_objects.h"
-#include "event_scheduler.h"
#include "event_queue.h"
#include "event_db_repository.h"
+#include "sql_connect.h" // init_new_connection_handler_thread
+#include "sql_acl.h" // SUPER_ACL
/**
@addtogroup Event_Scheduler
@@ -41,7 +42,7 @@
cond_wait(mythd, abstime, msg, SCHED_FUNC, __LINE__)
extern pthread_attr_t connection_attrib;
-
+extern ulong event_executed;
Event_db_repository *Event_worker_thread::db_repository;
@@ -76,7 +77,7 @@ Event_worker_thread::print_warnings(THD *thd, Event_job_data *et)
{
MYSQL_ERROR *err;
DBUG_ENTER("evex_print_warnings");
- if (!thd->warn_list.elements)
+ if (thd->warning_info->is_empty())
DBUG_VOID_RETURN;
char msg_buf[10 * STRING_BUFFER_USUAL_SIZE];
@@ -92,17 +93,18 @@ Event_worker_thread::print_warnings(THD *thd, Event_job_data *et)
prefix.append(et->name.str, et->name.length, system_charset_info);
prefix.append("] ", 2);
- List_iterator_fast<MYSQL_ERROR> it(thd->warn_list);
+ List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
while ((err= it++))
{
String err_msg(msg_buf, sizeof(msg_buf), system_charset_info);
/* set it to 0 or we start adding at the end. That's the trick ;) */
err_msg.length(0);
err_msg.append(prefix);
- err_msg.append(err->msg, strlen(err->msg), system_charset_info);
- DBUG_ASSERT(err->level < 3);
- (sql_print_message_handlers[err->level])("%*s", err_msg.length(),
- err_msg.c_ptr_safe());
+ err_msg.append(err->get_message_text(),
+ err->get_message_octet_length(), system_charset_info);
+ DBUG_ASSERT(err->get_level() < 3);
+ (sql_print_message_handlers[err->get_level()])("%*s", err_msg.length(),
+ err_msg.c_ptr_safe());
}
DBUG_VOID_RETURN;
}
@@ -129,14 +131,12 @@ post_init_event_thread(THD *thd)
thd->cleanup();
return TRUE;
}
- lex_start(thd);
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
threads.append(thd);
thread_count++;
- thread_running++;
- pthread_mutex_unlock(&LOCK_thread_count);
-
+ inc_thread_running();
+ mysql_mutex_unlock(&LOCK_thread_count);
return FALSE;
}
@@ -156,17 +156,17 @@ deinit_event_thread(THD *thd)
DBUG_ASSERT(thd->net.buff != 0);
net_end(&thd->net);
DBUG_PRINT("exit", ("Event thread finishing"));
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
thread_count--;
- thread_running--;
+ dec_thread_running();
delete thd;
- pthread_cond_broadcast(&COND_thread_count);
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_cond_broadcast(&COND_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
}
/*
- Performs pre- pthread_create() initialisation of THD. Do this
+ Performs pre- mysql_thread_create() initialisation of THD. Do this
in the thread that will pass THD to the child thread. In the
child thread call post_init_event_thread().
@@ -191,11 +191,11 @@ pre_init_event_thread(THD* thd)
thd->security_ctx->set_user((char*)"event_scheduler");
thd->net.read_timeout= slave_net_timeout;
thd->slave_thread= 0;
- thd->options|= OPTION_AUTO_IS_NULL;
+ thd->variables.option_bits|= OPTION_AUTO_IS_NULL;
thd->client_capabilities|= CLIENT_MULTI_RESULTS;
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
/*
Guarantees that we will see the thread in SHOW PROCESSLIST though its
@@ -203,9 +203,11 @@ pre_init_event_thread(THD* thd)
*/
thd->proc_info= "Initialized";
- thd->version= refresh_version;
thd->set_time();
+ /* Do not use user-supplied timeout value for system threads. */
+ thd->variables.lock_wait_timeout= LONG_TIMEOUT;
+
DBUG_VOID_RETURN;
}
@@ -230,12 +232,21 @@ event_scheduler_thread(void *arg)
bool res;
thd->thread_stack= (char *)&thd; // remember where our stack is
+
+ mysql_thread_set_psi_id(thd->thread_id);
+
res= post_init_event_thread(thd);
DBUG_ENTER("event_scheduler_thread");
- my_free((char*)arg, MYF(0));
+ my_free(arg);
if (!res)
scheduler->run(thd);
+ else
+ {
+ thd->proc_info= "Clearing";
+ net_end(&thd->net);
+ delete thd;
+ }
DBUG_LEAVE; // Against gcc warnings
my_thread_end();
@@ -263,6 +274,8 @@ event_worker_thread(void *arg)
thd= event->thd;
+ mysql_thread_set_psi_id(thd->thread_id);
+
Event_worker_thread worker_thread;
worker_thread.run(thd, event);
@@ -339,47 +352,41 @@ Event_scheduler::Event_scheduler(Event_queue *queue_arg)
waiting_on_cond(FALSE),
started_events(0)
{
- pthread_mutex_init(&LOCK_scheduler_state, MY_MUTEX_INIT_FAST);
- pthread_cond_init(&COND_state, NULL);
-
-#ifdef SAFE_MUTEX
- /* Ensure right mutex order */
- pthread_mutex_lock(&LOCK_scheduler_state);
- pthread_mutex_lock(&LOCK_global_system_variables);
- pthread_mutex_unlock(&LOCK_global_system_variables);
- pthread_mutex_unlock(&LOCK_scheduler_state);
-#endif
+ mysql_mutex_init(key_event_scheduler_LOCK_scheduler_state,
+ &LOCK_scheduler_state, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_event_scheduler_COND_state, &COND_state, NULL);
+ mysql_mutex_record_order(&LOCK_scheduler_state, &LOCK_global_system_variables);
}
Event_scheduler::~Event_scheduler()
{
stop(); /* does nothing if not running */
- pthread_mutex_destroy(&LOCK_scheduler_state);
- pthread_cond_destroy(&COND_state);
+ mysql_mutex_destroy(&LOCK_scheduler_state);
+ mysql_cond_destroy(&COND_state);
}
-/*
+/**
Starts the scheduler (again). Creates a new THD and passes it to
a forked thread. Does not wait for acknowledgement from the new
thread that it has started. Asynchronous starting. Most of the
needed initializations are done in the current thread to minimize
the chance of failure in the spawned thread.
- SYNOPSIS
- Event_scheduler::start()
+ @param[out] err_no - errno indicating type of error which caused
+ failure to start scheduler thread.
- RETURN VALUE
- FALSE OK
- TRUE Error (not reported)
+ @return
+ @retval false Success.
+ @retval true Error.
*/
bool
-Event_scheduler::start()
+Event_scheduler::start(int *err_no)
{
THD *new_thd= NULL;
- bool ret= FALSE;
+ bool ret= false;
pthread_t th;
struct scheduler_param *scheduler_param_value;
DBUG_ENTER("Event_scheduler::start");
@@ -392,7 +399,7 @@ Event_scheduler::start()
if (!(new_thd= new THD))
{
sql_print_error("Event Scheduler: Cannot initialize the scheduler thread");
- ret= TRUE;
+ ret= true;
goto end;
}
pre_init_event_thread(new_thd);
@@ -415,27 +422,30 @@ Event_scheduler::start()
DBUG_PRINT("info", ("Setting state go RUNNING"));
state= RUNNING;
DBUG_PRINT("info", ("Forking new thread for scheduler. THD: 0x%lx", (long) new_thd));
- if (pthread_create(&th, &connection_attrib, event_scheduler_thread,
- (void*)scheduler_param_value))
+ if ((*err_no= mysql_thread_create(key_thread_event_scheduler,
+ &th, &connection_attrib,
+ event_scheduler_thread,
+ (void*)scheduler_param_value)))
{
DBUG_PRINT("error", ("cannot create a new thread"));
- state= INITIALIZED;
- scheduler_thd= NULL;
- ret= TRUE;
+ sql_print_error("Event scheduler: Failed to start scheduler,"
+ " Can not create thread for event scheduler (errno=%d)",
+ *err_no);
new_thd->proc_info= "Clearing";
DBUG_ASSERT(new_thd->net.buff != 0);
net_end(&new_thd->net);
- pthread_mutex_lock(&LOCK_thread_count);
- thread_count--;
- thread_running--;
+
+ state= INITIALIZED;
+ scheduler_thd= NULL;
delete new_thd;
- pthread_cond_broadcast(&COND_thread_count);
- pthread_mutex_unlock(&LOCK_thread_count);
+
+ delete scheduler_param_value;
+ ret= true;
}
+
end:
UNLOCK_DATA();
-
DBUG_RETURN(ret);
}
@@ -498,8 +508,8 @@ Event_scheduler::run(THD *thd)
deinit_event_thread(thd);
scheduler_thd= NULL;
state= INITIALIZED;
- DBUG_PRINT("info", ("Signalling back to the stopper COND_state"));
- pthread_cond_signal(&COND_state);
+ DBUG_PRINT("info", ("Broadcasting COND_state back to the stoppers"));
+ mysql_cond_broadcast(&COND_state);
UNLOCK_DATA();
DBUG_RETURN(res);
@@ -543,11 +553,26 @@ Event_scheduler::execute_top(Event_queue_element_for_exec *event_name)
reasonable level.
*/
/* Major failure */
- if ((res= pthread_create(&th, &connection_attrib, event_worker_thread,
- event_name)))
+ if ((res= mysql_thread_create(key_thread_event_worker,
+ &th, &connection_attrib, event_worker_thread,
+ event_name)))
+ {
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ Events::opt_event_scheduler= Events::EVENTS_OFF;
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+
+ sql_print_error("Event_scheduler::execute_top: Can not create event worker"
+ " thread (errno=%d). Stopping event scheduler", res);
+
+ new_thd->proc_info= "Clearing";
+ DBUG_ASSERT(new_thd->net.buff != 0);
+ net_end(&new_thd->net);
+
goto error;
+ }
- ++started_events;
+ started_events++;
+ executed_events++; // For SHOW STATUS
DBUG_PRINT("info", ("Event is in THD: 0x%lx", (long) new_thd));
DBUG_RETURN(FALSE);
@@ -555,17 +580,8 @@ Event_scheduler::execute_top(Event_queue_element_for_exec *event_name)
error:
DBUG_PRINT("error", ("Event_scheduler::execute_top() res: %d", res));
if (new_thd)
- {
- new_thd->proc_info= "Clearing";
- DBUG_ASSERT(new_thd->net.buff != 0);
- net_end(&new_thd->net);
- pthread_mutex_lock(&LOCK_thread_count);
- thread_count--;
- thread_running--;
delete new_thd;
- pthread_cond_broadcast(&COND_thread_count);
- pthread_mutex_unlock(&LOCK_thread_count);
- }
+
delete event_name;
DBUG_RETURN(TRUE);
}
@@ -617,7 +633,12 @@ Event_scheduler::stop()
LOCK_DATA();
DBUG_PRINT("info", ("state before action %s", scheduler_states_names[state].str));
if (state != RUNNING)
+ {
+ /* Synchronously wait until the scheduler stops. */
+ while (state != INITIALIZED)
+ COND_STATE_WAIT(thd, NULL, "Waiting for the scheduler to stop");
goto end;
+ }
/* Guarantee we don't catch spurious signals */
do {
@@ -639,13 +660,13 @@ Event_scheduler::stop()
DBUG_PRINT("info", ("Scheduler thread has id %lu",
scheduler_thd->thread_id));
/* Lock from delete */
- pthread_mutex_lock(&scheduler_thd->LOCK_thd_kill);
+ 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",
scheduler_thd->thread_id);
scheduler_thd->awake(KILL_CONNECTION);
- pthread_mutex_unlock(&scheduler_thd->LOCK_thd_kill);
+ mysql_mutex_unlock(&scheduler_thd->LOCK_thd_data);
/* thd could be 0x0, when shutting down */
sql_print_information("Event Scheduler: "
@@ -681,12 +702,12 @@ Event_scheduler::workers_count()
uint count= 0;
DBUG_ENTER("Event_scheduler::workers_count");
- pthread_mutex_lock(&LOCK_thread_count); // For unlink from list
+ mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
I_List_iterator<THD> it(threads);
while ((tmp=it++))
if (tmp->system_thread == SYSTEM_THREAD_EVENT_WORKER)
++count;
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
DBUG_PRINT("exit", ("%d", count));
DBUG_RETURN(count);
}
@@ -707,7 +728,7 @@ Event_scheduler::lock_data(const char *func, uint line)
{
DBUG_ENTER("Event_scheduler::lock_data");
DBUG_PRINT("enter", ("func=%s line=%u", func, line));
- pthread_mutex_lock(&LOCK_scheduler_state);
+ mysql_mutex_lock(&LOCK_scheduler_state);
mutex_last_locked_in_func= func;
mutex_last_locked_at_line= line;
mutex_scheduler_data_locked= TRUE;
@@ -733,18 +754,18 @@ Event_scheduler::unlock_data(const char *func, uint line)
mutex_last_unlocked_at_line= line;
mutex_scheduler_data_locked= FALSE;
mutex_last_unlocked_in_func= func;
- pthread_mutex_unlock(&LOCK_scheduler_state);
+ mysql_mutex_unlock(&LOCK_scheduler_state);
DBUG_VOID_RETURN;
}
/*
- Wrapper for pthread_cond_wait/timedwait
+ Wrapper for mysql_cond_wait/timedwait
SYNOPSIS
Event_scheduler::cond_wait()
thd Thread (Could be NULL during shutdown procedure)
- abstime If not null then call pthread_cond_timedwait()
+ abstime If not null then call mysql_cond_timedwait()
msg Message for thd->proc_info
func Which function is requesting cond_wait
line On which line cond_wait is requested
@@ -762,11 +783,11 @@ Event_scheduler::cond_wait(THD *thd, struct timespec *abstime, const char* msg,
if (thd)
thd->enter_cond(&COND_state, &LOCK_scheduler_state, msg);
- DBUG_PRINT("info", ("pthread_cond_%swait", abstime? "timed":""));
+ DBUG_PRINT("info", ("mysql_cond_%swait", abstime? "timed":""));
if (!abstime)
- pthread_cond_wait(&COND_state, &LOCK_scheduler_state);
+ mysql_cond_wait(&COND_state, &LOCK_scheduler_state);
else
- pthread_cond_timedwait(&COND_state, &LOCK_scheduler_state, abstime);
+ mysql_cond_timedwait(&COND_state, &LOCK_scheduler_state, abstime);
if (thd)
{
/*
diff --git a/sql/event_scheduler.h b/sql/event_scheduler.h
index cd52c30e05e..538514b8b12 100644
--- a/sql/event_scheduler.h
+++ b/sql/event_scheduler.h
@@ -1,6 +1,6 @@
#ifndef _EVENT_SCHEDULER_H_
#define _EVENT_SCHEDULER_H_
-/* Copyright (c) 2004-2007 MySQL AB
+/* Copyright (c) 2004, 2013, 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
@@ -13,7 +13,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@addtogroup Event_Scheduler
@@ -34,7 +34,9 @@
class Event_queue;
class Event_job_data;
class Event_db_repository;
+class Event_queue_element_for_exec;
class Events;
+class THD;
void
pre_init_event_thread(THD* thd);
@@ -76,7 +78,7 @@ public:
/* State changing methods follow */
bool
- start();
+ start(int *err_no);
bool
stop();
@@ -115,7 +117,7 @@ private:
cond_wait(THD *thd, struct timespec *abstime, const char* msg,
const char *func, uint line);
- pthread_mutex_t LOCK_scheduler_state;
+ mysql_mutex_t LOCK_scheduler_state;
enum enum_state
{
@@ -129,7 +131,7 @@ private:
THD *scheduler_thd;
- pthread_cond_t COND_state;
+ mysql_cond_t COND_state;
Event_queue *queue;
diff --git a/sql/events.cc b/sql/events.cc
index f0188bbb0ae..763c75e77b0 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2005, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2005, 2013, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,16 +12,26 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "sql_parse.h" // check_access
+#include "sql_base.h" // close_mysql_tables
+#include "sql_show.h" // append_definer
#include "events.h"
+#include "sql_db.h" // check_db_dir_existence
+#include "sql_table.h" // write_bin_log
+#include "tztime.h" // struct Time_zone
+#include "sql_acl.h" // EVENT_ACL
+#include "records.h" // init_read_record, end_read_record
#include "event_data_objects.h"
#include "event_db_repository.h"
#include "event_queue.h"
#include "event_scheduler.h"
#include "sp_head.h" // for Stored_program_creation_ctx
+#include "set_var.h"
+#include "lock.h" // lock_object_name
/**
@addtogroup Event_Scheduler
@@ -65,44 +75,10 @@
eligible for execution.
*/
-/*
- Keep the order of the first to as in var_typelib
- sys_var_event_scheduler::value_ptr() references this array. Keep in
- mind!
-*/
-static const char *opt_event_scheduler_state_names[]=
- { "OFF", "ON", "0", "1", "DISABLED", NullS };
-
-const TYPELIB Events::opt_typelib=
-{
- array_elements(opt_event_scheduler_state_names)-1,
- "",
- opt_event_scheduler_state_names,
- NULL
-};
-
-
-/*
- The order should not be changed. We consider OFF to be equivalent of INT 0
- And ON of 1. If OFF & ON are interchanged the logic in
- sys_var_event_scheduler::update() will be broken!
-*/
-static const char *var_event_scheduler_state_names[]= { "OFF", "ON", NullS };
-
-const TYPELIB Events::var_typelib=
-{
- array_elements(var_event_scheduler_state_names)-1,
- "",
- var_event_scheduler_state_names,
- NULL
-};
-
Event_queue *Events::event_queue;
Event_scheduler *Events::scheduler;
Event_db_repository *Events::db_repository;
-enum Events::enum_opt_event_scheduler
-Events::opt_event_scheduler= Events::EVENTS_OFF;
-pthread_mutex_t Events::LOCK_event_metadata;
+ulong Events::opt_event_scheduler= Events::EVENTS_OFF;
bool Events::check_system_tables_error= FALSE;
@@ -129,69 +105,6 @@ int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs)
/**
- @brief Initialize the start up option of the Events scheduler.
-
- Do not initialize the scheduler subsystem yet - the initialization
- is split into steps as it has to fit into the common MySQL
- initialization framework.
- No locking as this is called only at start up.
-
- @param[in,out] argument The value of the argument. If this value
- is found in the typelib, the argument is
- updated.
-
- @retval TRUE unknown option value
- @retval FALSE success
-*/
-
-bool
-Events::set_opt_event_scheduler(char *argument)
-{
- if (argument == NULL)
- opt_event_scheduler= Events::EVENTS_ON;
- else
- {
- int type;
- /*
- type= 1 2 3 4 5
- (OFF | ON) - (0 | 1) (DISABLE )
- */
- const static enum enum_opt_event_scheduler type2state[]=
- { EVENTS_OFF, EVENTS_ON, EVENTS_OFF, EVENTS_ON, EVENTS_DISABLED };
-
- type= find_type(argument, &opt_typelib, 1);
-
- DBUG_ASSERT(type >= 0 && type <= 5); /* guaranteed by find_type */
-
- if (type == 0)
- {
- fprintf(stderr, "Unknown option to event-scheduler: %s\n", argument);
- return TRUE;
- }
- opt_event_scheduler= type2state[type-1];
- }
- return FALSE;
-}
-
-
-/**
- Return a string representation of the current scheduler mode.
-*/
-
-const char *
-Events::get_opt_event_scheduler_str()
-{
- const char *str;
-
- pthread_mutex_lock(&LOCK_event_metadata);
- str= opt_typelib.type_names[(int) opt_event_scheduler];
- pthread_mutex_unlock(&LOCK_event_metadata);
-
- return str;
-}
-
-
-/**
Push an error into the error stack if the system tables are
not up to date.
*/
@@ -396,15 +309,6 @@ Events::create_event(THD *thd, Event_parse_data *parse_data,
bool save_binlog_row_based, event_already_exists;
DBUG_ENTER("Events::create_event");
- /*
- Let's commit the transaction first - MySQL manual specifies
- that a DDL issues an implicit commit, and it doesn't say "successful
- DDL", so that an implicit commit is a property of any successfully
- parsed DDL statement.
- */
- if (end_active_trans(thd))
- DBUG_RETURN(TRUE);
-
if (check_if_system_tables_error())
DBUG_RETURN(TRUE);
@@ -419,9 +323,7 @@ Events::create_event(THD *thd, Event_parse_data *parse_data,
/* At create, one of them must be set */
DBUG_ASSERT(parse_data->expression || parse_data->execute_at);
- if (check_access(thd, EVENT_ACL, parse_data->dbname.str, 0, 0, 0,
- is_schema_db(parse_data->dbname.str,
- parse_data->dbname.length)))
+ if (check_access(thd, EVENT_ACL, parse_data->dbname.str, NULL, NULL, 0, 0))
DBUG_RETURN(TRUE);
if (check_db_dir_existence(parse_data->dbname.str))
@@ -436,10 +338,12 @@ Events::create_event(THD *thd, Event_parse_data *parse_data,
Turn off row binlogging of this statement and use statement-based
so that all supporting tables are updated for CREATE EVENT command.
*/
- save_binlog_row_based= thd->current_stmt_binlog_row_based;
- thd->clear_current_stmt_binlog_row_based();
+ if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
+ thd->clear_current_stmt_binlog_format_row();
- pthread_mutex_lock(&LOCK_event_metadata);
+ if (lock_object_name(thd, MDL_key::EVENT,
+ parse_data->dbname.str, parse_data->name.str))
+ DBUG_RETURN(TRUE);
/* On error conditions my_error() is called so no need to handle here */
if (!(ret= db_repository->create_event(thd, parse_data, if_not_exists,
@@ -494,9 +398,10 @@ Events::create_event(THD *thd, Event_parse_data *parse_data,
}
}
}
- pthread_mutex_unlock(&LOCK_event_metadata);
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(ret);
}
@@ -531,22 +436,13 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
DBUG_ENTER("Events::update_event");
- /*
- For consistency, implicit COMMIT should be the first thing in the
- execution chain.
- */
- if (end_active_trans(thd))
- DBUG_RETURN(TRUE);
-
if (check_if_system_tables_error())
DBUG_RETURN(TRUE);
if (parse_data->check_parse_data(thd) || parse_data->do_not_create)
DBUG_RETURN(TRUE);
- if (check_access(thd, EVENT_ACL, parse_data->dbname.str, 0, 0, 0,
- is_schema_db(parse_data->dbname.str,
- parse_data->dbname.length)))
+ if (check_access(thd, EVENT_ACL, parse_data->dbname.str, NULL, NULL, 0, 0))
DBUG_RETURN(TRUE);
if (new_dbname) /* It's a rename */
@@ -567,8 +463,7 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
to tell the user that a database doesn't exist if they can not
access it.
*/
- if (check_access(thd, EVENT_ACL, new_dbname->str, 0, 0, 0,
- is_schema_db(new_dbname->str, new_dbname->length)))
+ if (check_access(thd, EVENT_ACL, new_dbname->str, NULL, NULL, 0, 0))
DBUG_RETURN(TRUE);
/* Check that the target database exists */
@@ -583,10 +478,12 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
Turn off row binlogging of this statement and use statement-based
so that all supporting tables are updated for UPDATE EVENT command.
*/
- save_binlog_row_based= thd->current_stmt_binlog_row_based;
- thd->clear_current_stmt_binlog_row_based();
+ if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
+ thd->clear_current_stmt_binlog_format_row();
- pthread_mutex_lock(&LOCK_event_metadata);
+ if (lock_object_name(thd, MDL_key::EVENT,
+ parse_data->dbname.str, parse_data->name.str))
+ DBUG_RETURN(TRUE);
/* On error conditions my_error() is called so no need to handle here */
if (!(ret= db_repository->update_event(thd, parse_data,
@@ -599,10 +496,7 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
ret= TRUE; // OOM
else if ((ret= db_repository->load_named_event(thd, dbname, name,
new_element)))
- {
- DBUG_ASSERT(ret == OP_LOAD_ERROR);
delete new_element;
- }
else
{
/*
@@ -619,9 +513,10 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
ret= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
}
}
- pthread_mutex_unlock(&LOCK_event_metadata);
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(ret);
}
@@ -658,35 +553,22 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists)
bool save_binlog_row_based;
DBUG_ENTER("Events::drop_event");
- /*
- In MySQL, DDL must always commit: since mysql.* tables are
- non-transactional, we must modify them outside a transaction
- to not break atomicity.
- But the second and more important reason to commit here
- regardless whether we're actually changing mysql.event table
- or not is replication: end_active_trans syncs the binary log,
- and unless we run DDL in it's own transaction it may simply
- never appear on the slave in case the outside transaction
- rolls back.
- */
- if (end_active_trans(thd))
- DBUG_RETURN(TRUE);
-
if (check_if_system_tables_error())
DBUG_RETURN(TRUE);
- if (check_access(thd, EVENT_ACL, dbname.str, 0, 0, 0,
- is_schema_db(dbname.str, dbname.length)))
+ if (check_access(thd, EVENT_ACL, dbname.str, NULL, NULL, 0, 0))
DBUG_RETURN(TRUE);
/*
Turn off row binlogging of this statement and use statement-based so
that all supporting tables are updated for DROP EVENT command.
*/
- save_binlog_row_based= thd->current_stmt_binlog_row_based;
- thd->clear_current_stmt_binlog_row_based();
+ if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
+ thd->clear_current_stmt_binlog_format_row();
- pthread_mutex_lock(&LOCK_event_metadata);
+ if (lock_object_name(thd, MDL_key::EVENT,
+ 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)))
{
@@ -696,9 +578,10 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists)
DBUG_ASSERT(thd->query() && thd->query_length());
ret= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
}
- pthread_mutex_unlock(&LOCK_event_metadata);
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(ret);
}
@@ -723,15 +606,12 @@ Events::drop_schema_events(THD *thd, char *db)
DBUG_PRINT("enter", ("dropping events from %s", db));
/*
- sic: no check if the scheduler is disabled or system tables
+ Sic: no check if the scheduler is disabled or system tables
are damaged, as intended.
*/
-
- pthread_mutex_lock(&LOCK_event_metadata);
if (event_queue)
event_queue->drop_schema_events(thd, db_lex);
db_repository->drop_schema_events(thd, db_lex);
- pthread_mutex_unlock(&LOCK_event_metadata);
DBUG_VOID_RETURN;
}
@@ -759,7 +639,7 @@ send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol)
field_list.push_back(new Item_empty_string("Event", NAME_CHAR_LEN));
- if (sys_var::make_set(thd, et->sql_mode, &sql_mode_typelib, &sql_mode))
+ if (sql_mode_string_representation(thd, et->sql_mode, &sql_mode))
DBUG_RETURN(TRUE);
field_list.push_back(new Item_empty_string("sql_mode", (uint) sql_mode.length));
@@ -781,7 +661,7 @@ send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol)
field_list.push_back(
new Item_empty_string("Database Collation", MY_CS_NAME_SIZE));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
@@ -824,7 +704,6 @@ send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol)
bool
Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
{
- Open_tables_state open_tables_backup;
Event_timed et;
bool ret;
@@ -834,8 +713,7 @@ Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
if (check_if_system_tables_error())
DBUG_RETURN(TRUE);
- if (check_access(thd, EVENT_ACL, dbname.str, 0, 0, 0,
- is_schema_db(dbname.str, dbname.length)))
+ if (check_access(thd, EVENT_ACL, dbname.str, NULL, NULL, 0, 0))
DBUG_RETURN(TRUE);
/*
@@ -848,9 +726,7 @@ Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name)
deadlock can occur please refer to the description of 'system table'
flag.
*/
- thd->reset_n_backup_open_tables_state(&open_tables_backup);
ret= db_repository->load_named_event(thd, dbname, name, &et);
- thd->restore_backup_open_tables_state(&open_tables_backup);
if (!ret)
ret= send_show_create_event(thd, &et, thd->protocol);
@@ -880,7 +756,6 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
{
char *db= NULL;
int ret;
- Open_tables_state open_tables_backup;
DBUG_ENTER("Events::fill_schema_events");
if (check_if_system_tables_error())
@@ -893,20 +768,13 @@ 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_schema_db(thd->lex->select_lex.db) && // There is no events in I_S
- check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0, 0))
+ 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,
+ NULL, NULL, 0, 0))
DBUG_RETURN(1);
db= thd->lex->select_lex.db;
}
- /*
- Reset and backup of the currently open tables in this thread
- is a way to allow SELECTs from INFORMATION_SCHEMA.events under
- LOCK TABLES and in pre-locked mode. See also
- Events::show_create_event for additional comments.
- */
- thd->reset_n_backup_open_tables_state(&open_tables_backup);
ret= db_repository->fill_schema_events(thd, tables, db);
- thd->restore_backup_open_tables_state(&open_tables_backup);
DBUG_RETURN(ret);
}
@@ -932,6 +800,7 @@ Events::init(bool opt_noacl_or_bootstrap)
{
THD *thd;
+ int err_no;
bool res= FALSE;
DBUG_ENTER("Events::init");
@@ -949,7 +818,6 @@ Events::init(bool opt_noacl_or_bootstrap)
*/
thd->thread_stack= (char*) &thd;
thd->store_globals();
- lex_start(thd);
/*
We will need Event_db_repository anyway, even if the scheduler is
@@ -1004,7 +872,7 @@ Events::init(bool opt_noacl_or_bootstrap)
}
if (event_queue->init_queue(thd) || load_events_from_db(thd) ||
- (opt_event_scheduler == EVENTS_ON && scheduler->start()))
+ (opt_event_scheduler == EVENTS_ON && scheduler->start(&err_no)))
{
sql_print_error("Event Scheduler: Error while loading from disk.");
res= TRUE; /* fatal error: request unireg_abort */
@@ -1055,38 +923,65 @@ Events::deinit()
DBUG_VOID_RETURN;
}
+#ifdef HAVE_PSI_INTERFACE
+PSI_mutex_key key_LOCK_event_queue,
+ key_event_scheduler_LOCK_scheduler_state;
-/**
- Inits Events mutexes
+static PSI_mutex_info all_events_mutexes[]=
+{
+ { &key_LOCK_event_queue, "LOCK_event_queue", PSI_FLAG_GLOBAL},
+ { &key_event_scheduler_LOCK_scheduler_state, "Event_scheduler::LOCK_scheduler_state", PSI_FLAG_GLOBAL}
+};
- SYNOPSIS
- Events::init_mutexes()
- thd Thread
-*/
+PSI_cond_key key_event_scheduler_COND_state, key_COND_queue_state;
-void
-Events::init_mutexes()
+static PSI_cond_info all_events_conds[]=
{
- /*
- Inconsisent usage between LOCK_event_metadata and LOCK_scheduler_state
- and LOCK_open
- */
- my_pthread_mutex_init(&LOCK_event_metadata, MY_MUTEX_INIT_FAST,
- "LOCK_event_metadata", MYF_NO_DEADLOCK_DETECTION);
-}
+ { &key_event_scheduler_COND_state, "Event_scheduler::COND_state", PSI_FLAG_GLOBAL},
+ { &key_COND_queue_state, "COND_queue_state", PSI_FLAG_GLOBAL},
+};
+PSI_thread_key key_thread_event_scheduler, key_thread_event_worker;
-/*
- Destroys Events mutexes
+static PSI_thread_info all_events_threads[]=
+{
+ { &key_thread_event_scheduler, "event_scheduler", PSI_FLAG_GLOBAL},
+ { &key_thread_event_worker, "event_worker", 0}
+};
+
+static void init_events_psi_keys(void)
+{
+ const char* category= "sql";
+ int count;
+
+ if (PSI_server == NULL)
+ return;
+
+ count= array_elements(all_events_mutexes);
+ PSI_server->register_mutex(category, all_events_mutexes, count);
+
+ count= array_elements(all_events_conds);
+ PSI_server->register_cond(category, all_events_conds, count);
+
+ count= array_elements(all_events_threads);
+ PSI_server->register_thread(category, all_events_threads, count);
+}
+#endif /* HAVE_PSI_INTERFACE */
+
+/**
+ Inits Events mutexes
SYNOPSIS
- Events::destroy_mutexes()
+ Events::init_mutexes()
+ thd Thread
*/
void
-Events::destroy_mutexes()
+Events::init_mutexes()
{
- pthread_mutex_destroy(&LOCK_event_metadata);
+#ifdef HAVE_PSI_INTERFACE
+ init_events_psi_keys();
+#endif
}
@@ -1108,7 +1003,11 @@ Events::dump_internal_status()
puts("LLA = Last Locked At LUA = Last Unlocked At");
puts("WOC = Waiting On Condition DL = Data Locked");
- pthread_mutex_lock(&LOCK_event_metadata);
+ /*
+ opt_event_scheduler should only be accessed while
+ holding LOCK_global_system_variables.
+ */
+ mysql_mutex_lock(&LOCK_global_system_variables);
if (opt_event_scheduler == EVENTS_DISABLED)
puts("The Event Scheduler is disabled");
else
@@ -1117,64 +1016,19 @@ Events::dump_internal_status()
event_queue->dump_internal_status();
}
- pthread_mutex_unlock(&LOCK_event_metadata);
+ mysql_mutex_unlock(&LOCK_global_system_variables);
DBUG_VOID_RETURN;
}
-
-/**
- Starts or stops the event scheduler thread.
-
- @retval FALSE success
- @retval TRUE error
-*/
-
-bool
-Events::switch_event_scheduler_state(enum_opt_event_scheduler new_state)
+bool Events::start(int *err_no)
{
- bool ret= FALSE;
-
- DBUG_ENTER("Events::switch_event_scheduler_state");
-
- DBUG_ASSERT(new_state == Events::EVENTS_ON ||
- new_state == Events::EVENTS_OFF);
-
- /*
- If the scheduler was disabled because there are no/bad
- system tables, produce a more meaningful error message
- than ER_OPTION_PREVENTS_STATEMENT
- */
- if (check_if_system_tables_error())
- DBUG_RETURN(TRUE);
-
- pthread_mutex_lock(&LOCK_event_metadata);
-
- if (opt_event_scheduler == EVENTS_DISABLED)
- {
- my_error(ER_OPTION_PREVENTS_STATEMENT,
- MYF(0), "--event-scheduler=DISABLED or --skip-grant-tables");
- ret= TRUE;
- goto end;
- }
-
- if (new_state == EVENTS_ON)
- ret= scheduler->start();
- else
- ret= scheduler->stop();
-
- if (ret)
- {
- my_error(ER_EVENT_SET_VAR_ERROR, MYF(0), 0);
- goto end;
- }
-
- opt_event_scheduler= new_state;
-
-end:
- pthread_mutex_unlock(&LOCK_event_metadata);
- DBUG_RETURN(ret);
+ return scheduler->start(err_no);
}
+bool Events::stop()
+{
+ return scheduler->stop();
+}
/**
Loads all ENABLED events from mysql.event into a prioritized
@@ -1289,8 +1143,7 @@ Events::load_events_from_db(THD *thd)
end:
end_read_record(&read_record_info);
- close_thread_tables(thd);
-
+ close_mysql_tables(thd);
DBUG_RETURN(ret);
}
diff --git a/sql/events.h b/sql/events.h
index 3f15d10f8ed..223f68c8370 100644
--- a/sql/events.h
+++ b/sql/events.h
@@ -1,6 +1,6 @@
#ifndef _EVENT_H_
#define _EVENT_H_
-/* Copyright (c) 2004-2007 MySQL AB
+/* Copyright (c) 2004, 2013, 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
@@ -13,7 +13,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@defgroup Event_Scheduler Event Scheduler
@@ -25,23 +25,22 @@
A public interface of Events_Scheduler module.
*/
-class Event_parse_data;
+#ifdef HAVE_PSI_INTERFACE
+extern PSI_mutex_key key_event_scheduler_LOCK_scheduler_state;
+extern PSI_cond_key key_event_scheduler_COND_state;
+extern PSI_thread_key key_thread_event_scheduler, key_thread_event_worker;
+#endif /* HAVE_PSI_INTERFACE */
+
+#include "sql_string.h" /* LEX_STRING */
+#include "my_time.h" /* interval_type */
+
class Event_db_repository;
+class Event_parse_data;
class Event_queue;
class Event_scheduler;
-
-/* Return codes */
-enum enum_events_error_code
-{
- OP_OK= 0,
- OP_NOT_RUNNING,
- OP_CANT_KILL,
- OP_CANT_INIT,
- OP_DISABLED_EVENT,
- OP_LOAD_ERROR,
- OP_ALREADY_EXISTS
-};
-
+struct TABLE_LIST;
+class THD;
+typedef class Item COND;
int
sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs);
@@ -56,7 +55,7 @@ sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs);
The life cycle of the Events module is the following:
At server start up:
- set_opt_event_scheduler() -> init_mutexes() -> init()
+ init_mutexes() -> init()
When the server is running:
create_event(), drop_event(), start_or_stop_event_scheduler(), etc
At shutdown:
@@ -70,23 +69,19 @@ sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs);
class Events
{
public:
- /* The order should match the order in opt_typelib */
- enum enum_opt_event_scheduler
- {
- EVENTS_OFF= 0,
- EVENTS_ON= 1,
- EVENTS_DISABLED= 4
- };
-
- /* Possible values of @@event_scheduler variable */
- static const TYPELIB var_typelib;
-
- static bool
- set_opt_event_scheduler(char *argument);
-
- static const char *
- get_opt_event_scheduler_str();
+ /*
+ the following block is to support --event-scheduler command line option
+ and the @@global.event_scheduler SQL variable.
+ See sys_var.cc
+ */
+ enum enum_opt_event_scheduler { EVENTS_OFF, EVENTS_ON, EVENTS_DISABLED };
+ /* Protected using LOCK_global_system_variables only. */
+ static ulong opt_event_scheduler;
+ static bool check_if_system_tables_error();
+ static bool start(int *err_no);
+ static bool stop();
+public:
/* A hack needed for Event_queue_element */
static Event_db_repository *
get_db_repository() { return db_repository; }
@@ -104,9 +99,6 @@ public:
destroy_mutexes();
static bool
- switch_event_scheduler_state(enum enum_opt_event_scheduler new_state);
-
- static bool
create_event(THD *thd, Event_parse_data *parse_data, bool if_exists);
static bool
@@ -134,20 +126,14 @@ public:
dump_internal_status();
private:
- static bool check_if_system_tables_error();
static bool
load_events_from_db(THD *thd);
private:
- /* Command line option names */
- static const TYPELIB opt_typelib;
- static pthread_mutex_t LOCK_event_metadata;
static Event_queue *event_queue;
static Event_scheduler *scheduler;
static Event_db_repository *db_repository;
- /* Current state of Event Scheduler */
- static enum enum_opt_event_scheduler opt_event_scheduler;
/* Set to TRUE if an error at start up */
static bool check_system_tables_error;
diff --git a/sql/examples/CMakeLists.txt b/sql/examples/CMakeLists.txt
index 47a64c88c68..c4ea4c25679 100755..100644
--- a/sql/examples/CMakeLists.txt
+++ b/sql/examples/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (C) 2006 MySQL AB
+# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -11,7 +11,7 @@
#
# 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
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/sql
${CMAKE_SOURCE_DIR}/extra/yassl/include
diff --git a/sql/field.cc b/sql/field.cc
index 4452f881fb7..52a490921b8 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -1,6 +1,6 @@
/*
- Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 2008, 2014, Monty Program Ab
+ Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2013, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -27,15 +27,21 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
#include "sql_select.h"
#include "rpl_rli.h" // Pull in Relay_log_info
#include "slave.h" // Pull in rpl_master_has_bug()
+#include "strfunc.h" // find_type2, find_set
+#include "sql_time.h" // str_to_datetime_with_warn,
+ // str_to_time_with_warn,
+ // TIME_to_timestamp,
+ // make_time, make_date,
+ // make_truncated_value_warning
+#include "tztime.h" // struct Time_zone
+#include "filesort.h" // change_double_for_sort
+#include "log_event.h" // class Table_map_log_event
#include <m_ctype.h>
#include <errno.h>
-#ifdef HAVE_FCONVERT
-#include <floatingpoint.h>
-#endif
// Maximum allowed exponent value for converting string to decimal
#define MAX_EXPONENT 1024
@@ -63,7 +69,7 @@ static uint time_hires_bytes[MAX_DATETIME_PRECISION+1]= { 3, 4, 4, 5, 5, 5, 6 };
uchar Field_null::null[1]={1};
const char field_separator=',';
-#define DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE 320
+#define DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE FLOATING_POINT_BUFFER
#define LONGLONG_TO_STRING_CONVERSION_BUFFER_SIZE 128
#define DECIMAL_TO_STRING_CONVERSION_BUFFER_SIZE 128
#define BLOB_PACK_LENGTH_TO_MAX_LENGH(arg) \
@@ -72,6 +78,8 @@ const char field_separator=',';
#define ASSERT_COLUMN_MARKED_FOR_READ DBUG_ASSERT(!table || (!table->read_set || bitmap_is_set(table->read_set, field_index)))
#define ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED DBUG_ASSERT(!table || (!table->write_set || bitmap_is_set(table->write_set, field_index) || bitmap_is_set(table->vcol_set, field_index)))
+#define FLAGSTR(S,F) ((S) & (F) ? #F " " : "")
+
/*
Rules for merging different types of fields in UNION
@@ -1010,6 +1018,25 @@ test_if_important_data(CHARSET_INFO *cs, const char *str, const char *strend)
/**
+ Function to compare two unsigned integers for their relative order.
+ Used below. In an anonymous namespace to not clash with definitions
+ in other files.
+ */
+
+CPP_UNNAMED_NS_START
+
+int compare(unsigned int a, unsigned int b)
+{
+ if (a < b)
+ return -1;
+ if (b < a)
+ return 1;
+ return 0;
+}
+
+CPP_UNNAMED_NS_END
+
+/**
Detect Item_result by given field type of UNION merge result.
@param field_type given field type
@@ -1029,6 +1056,36 @@ Item_result Field::result_merge_type(enum_field_types field_type)
Static help functions
*****************************************************************************/
+/**
+ Output a warning for erroneous conversion of strings to numerical
+ values. For use with ER_TRUNCATED_WRONG_VALUE[_FOR_FIELD]
+
+ @param thd THD object
+ @param str pointer to string that failed to be converted
+ @param length length of string
+ @param cs charset for string
+ @param typestr string describing type converted to
+ @param error error value to output
+ @param field_name (for *_FOR_FIELD) name of field
+ @param row_num (for *_FOR_FIELD) row number
+ */
+static void push_numerical_conversion_warning(THD* thd, const char* str,
+ uint length, CHARSET_INFO* cs,
+ const char* typestr, int error,
+ const char* field_name="UNKNOWN",
+ ulong row_num=0)
+{
+ char buf[max(max(DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE,
+ LONGLONG_TO_STRING_CONVERSION_BUFFER_SIZE),
+ DECIMAL_TO_STRING_CONVERSION_BUFFER_SIZE)];
+
+ String tmp(buf, sizeof(buf), cs);
+ tmp.copy(str, length, cs);
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ error, ER(error), typestr, tmp.c_ptr(),
+ field_name, row_num);
+}
+
/**
Check whether a field type can be partially indexed by a key.
@@ -1137,14 +1194,12 @@ int Field_num::check_int(CHARSET_INFO *cs, const char *str, int length,
/* Test if we get an empty string or wrong integer */
if (str == int_end || error == MY_ERRNO_EDOM)
{
- char buff[128];
- String tmp(buff, (uint32) sizeof(buff), system_charset_info);
- tmp.copy(str, length, system_charset_info);
+ ErrConvString err(str, length, cs);
push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
- "integer", tmp.c_ptr(), field_name,
- (ulong) table->in_use->row_count);
+ "integer", err.ptr(), field_name,
+ (ulong) table->in_use->warning_info->current_row_for_warning());
return 1;
}
/* Test if we have garbage at the end of the given string. */
@@ -1251,61 +1306,6 @@ int Field::warn_if_overflow(int op_result)
}
-#ifdef NOT_USED
-static bool test_if_real(const char *str,int length, CHARSET_INFO *cs)
-{
- cs= system_charset_info; // QQ move test_if_real into CHARSET_INFO struct
-
- while (length && my_isspace(cs,*str))
- { // Allow start space
- length--; str++;
- }
- if (!length)
- return 0;
- if (*str == '+' || *str == '-')
- {
- length--; str++;
- if (!length || !(my_isdigit(cs,*str) || *str == '.'))
- return 0;
- }
- while (length && my_isdigit(cs,*str))
- {
- length--; str++;
- }
- if (!length)
- return 1;
- if (*str == '.')
- {
- length--; str++;
- while (length && my_isdigit(cs,*str))
- {
- length--; str++;
- }
- }
- if (!length)
- return 1;
- if (*str == 'E' || *str == 'e')
- {
- if (length < 3 || (str[1] != '+' && str[1] != '-') ||
- !my_isdigit(cs,str[2]))
- return 0;
- length-=3;
- str+=3;
- while (length && my_isdigit(cs,*str))
- {
- length--; str++;
- }
- }
- for (; length ; length--, str++)
- { // Allow end space
- if (!my_isspace(cs,*str))
- return 0;
- }
- return 1;
-}
-#endif
-
-
/**
Interpret field value as an integer but return the result as a string.
@@ -1358,7 +1358,7 @@ void Field::hash(ulong *nr, ulong *nr2)
else
{
uint len= pack_length();
- CHARSET_INFO *cs= charset();
+ CHARSET_INFO *cs= sort_charset();
cs->coll->hash_sort(cs, ptr, len, nr, nr2);
}
}
@@ -1396,24 +1396,48 @@ bool Field::send_binary(Protocol *protocol)
/**
Check to see if field size is compatible with destination.
- This method is used in row-based replication to verify that the slave's
- field size is less than or equal to the master's field size. The
- encoded field metadata (from the master or source) is decoded and compared
- to the size of this field (the slave or destination).
+ This method is used in row-based replication to verify that the
+ slave's field size is less than or equal to the master's field
+ size. The encoded field metadata (from the master or source) is
+ decoded and compared to the size of this field (the slave or
+ destination).
+
+ @note
+
+ The comparison is made so that if the source data (from the master)
+ is less than the target data (on the slave), -1 is returned in @c
+ <code>*order_var</code>. This implies that a conversion is
+ necessary, but that it is lossy and can result in truncation of the
+ value.
+
+ If the source data is strictly greater than the target data, 1 is
+ returned in <code>*order_var</code>. This implies that the source
+ type can is contained in the target type and that a conversion is
+ necessary but is non-lossy.
+
+ If no conversion is required to fit the source type in the target
+ type, 0 is returned in <code>*order_var</code>.
@param field_metadata Encoded size in field metadata
@param mflags Flags from the table map event for the table.
+ @param order_var Pointer to variable where the order
+ between the source field and this field
+ will be returned.
- @retval 0 if this field's size is < the source field's size
- @retval 1 if this field's size is >= the source field's size
+ @return @c true if this field's size is compatible with the
+ master's field size, @c false otherwise.
*/
-int Field::compatible_field_size(uint field_metadata,
- const Relay_log_info *rli_arg __attribute__((unused)),
- uint16 mflags __attribute__((unused)))
+bool Field::compatible_field_size(uint field_metadata,
+ Relay_log_info *rli_arg __attribute__((unused)),
+ uint16 mflags __attribute__((unused)),
+ int *order_var)
{
uint const source_size= pack_length_from_metadata(field_metadata);
uint const destination_size= row_pack_length();
- return (source_size <= destination_size);
+ DBUG_PRINT("debug", ("real_type: %d, source_size: %u, destination_size: %u",
+ real_type(), source_size, destination_size));
+ *order_var = compare(source_size, destination_size);
+ return true;
}
@@ -1548,7 +1572,12 @@ void Field::make_field(Send_field *field)
if (orig_table && orig_table->s->db.str && *orig_table->s->db.str)
{
field->db_name= orig_table->s->db.str;
- field->org_table_name= orig_table->s->table_name.str;
+ 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);
+ else
+ field->org_table_name= orig_table->s->table_name.str;
}
else
field->org_table_name= field->db_name= "";
@@ -1755,12 +1784,12 @@ uint Field::fill_cache_field(CACHE_FIELD *copy)
}
-bool Field::get_date(MYSQL_TIME *ltime,uint fuzzydate)
+bool Field::get_date(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->ptr(), res->length(),
+ str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR)
return 1;
return 0;
@@ -1778,7 +1807,9 @@ int Field::store_time_dec(MYSQL_TIME *ltime, uint dec)
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
char buff[MAX_DATE_STRING_REP_LENGTH];
uint length= (uint) my_TIME_to_str(ltime, buff, dec);
- return store(buff, length, &my_charset_bin);
+ /* Avoid conversion when field character set is ASCII compatible */
+ return store(buff, length, (charset()->state & MY_CS_NONASCII) ?
+ &my_charset_latin1 : charset());
}
@@ -1788,7 +1819,7 @@ bool Field::optimize_range(uint idx, uint part)
}
-Field *Field::new_field(MEM_ROOT *root, struct st_table *new_table,
+Field *Field::new_field(MEM_ROOT *root, TABLE *new_table,
bool keep_type __attribute__((unused)))
{
Field *tmp;
@@ -1809,7 +1840,7 @@ Field *Field::new_field(MEM_ROOT *root, struct st_table *new_table,
}
-Field *Field::new_key_field(MEM_ROOT *root, struct st_table *new_table,
+Field *Field::new_key_field(MEM_ROOT *root, TABLE *new_table,
uchar *new_ptr, uchar *new_null_ptr,
uint new_null_bit)
{
@@ -1826,7 +1857,7 @@ Field *Field::new_key_field(MEM_ROOT *root, struct st_table *new_table,
/* This is used to generate a field in TABLE from TABLE_SHARE */
-Field *Field::clone(MEM_ROOT *root, struct st_table *new_table)
+Field *Field::clone(MEM_ROOT *root, TABLE *new_table)
{
Field *tmp;
if ((tmp= (Field*) memdup_root(root,(char*) this,size_of())))
@@ -2287,13 +2318,7 @@ int Field_decimal::store(double nr)
char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
fyllchar = zerofill ? (char) '0' : (char) ' ';
-#ifdef HAVE_SNPRINTF
- buff[sizeof(buff)-1]=0; // Safety
- snprintf(buff,sizeof(buff)-1, "%.*f",(int) dec,nr);
- length= strlen(buff);
-#else
- length= my_sprintf(buff,(buff,"%.*f",dec,nr));
-#endif
+ length= my_fcvt(nr, dec, buff, NULL);
if (length > field_length)
{
@@ -2376,7 +2401,7 @@ String *Field_decimal::val_str(String *val_buffer __attribute__((unused)),
size_t tmp_length;
for (str=ptr ; *str == ' ' ; str++) ;
- val_ptr->set_charset(&my_charset_bin);
+ val_ptr->set_charset(&my_charset_numeric);
tmp_length= (size_t) (str-ptr);
if (field_length < tmp_length) // Error in data
val_ptr->length(0);
@@ -2506,7 +2531,7 @@ Field *Field_new_decimal::create_from_item (Item *item)
{
uint8 dec= item->decimals;
uint8 intg= item->decimal_precision() - dec;
- uint32 len= item->max_length;
+ uint32 len= item->max_char_length();
DBUG_ASSERT (item->result_type() == DECIMAL_RESULT);
@@ -2649,15 +2674,12 @@ int Field_new_decimal::store(const char *from, uint length,
&decimal_value)) &&
table->in_use->abort_on_warning)
{
- /* Because "from" is not NUL-terminated and we use %s in the ER() */
- String from_as_str;
- from_as_str.copy(from, length, &my_charset_bin);
-
- push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ErrConvString errmsg(from, length, &my_charset_bin);
+ push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
- "decimal", from_as_str.c_ptr(), field_name,
- (ulong) table->in_use->row_count);
+ "decimal", errmsg.ptr(), field_name,
+ (ulong) table->in_use->warning_info->current_row_for_warning());
DBUG_RETURN(err);
}
@@ -2672,18 +2694,15 @@ int Field_new_decimal::store(const char *from, uint length,
break;
case E_DEC_BAD_NUM:
{
- /* Because "from" is not NUL-terminated and we use %s in the ER() */
- String from_as_str;
- from_as_str.copy(from, length, &my_charset_bin);
-
- push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
- ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
- "decimal", from_as_str.c_ptr(), field_name,
- (ulong) table->in_use->row_count);
- my_decimal_set_zero(&decimal_value);
-
- break;
+ ErrConvString errmsg(from, length, &my_charset_bin);
+ push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
+ ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
+ "decimal", errmsg.ptr(), field_name,
+ (ulong) table->in_use->warning_info->
+ current_row_for_warning());
+ my_decimal_set_zero(&decimal_value);
+ break;
}
}
@@ -2712,17 +2731,6 @@ int Field_new_decimal::store(double nr)
err= double2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, nr,
&decimal_value);
- /*
- TODO: fix following when double2my_decimal when double2decimal
- will return E_DEC_TRUNCATED always correctly
- */
- if (!err)
- {
- double nr2;
- my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &nr2);
- if (nr2 != nr)
- err= E_DEC_TRUNCATED;
- }
if (err)
{
if (check_overflow(err))
@@ -2815,6 +2823,7 @@ String *Field_new_decimal::val_str(String *val_buffer,
uint fixed_precision= zerofill ? precision : 0;
my_decimal2string(E_DEC_FATAL_ERROR, val_decimal(&decimal_value),
fixed_precision, dec, '0', val_buffer);
+ val_buffer->set_charset(&my_charset_numeric);
return val_buffer;
}
@@ -2882,34 +2891,16 @@ uint Field_new_decimal::pack_length_from_metadata(uint field_metadata)
}
-/**
- Check to see if field size is compatible with destination.
-
- This method is used in row-based replication to verify that the slave's
- field size is less than or equal to the master's field size. The
- encoded field metadata (from the master or source) is decoded and compared
- to the size of this field (the slave or destination).
-
- @param field_metadata Encoded size in field metadata
-
- @retval 0 if this field's size is < the source field's size
- @retval 1 if this field's size is >= the source field's size
-*/
-int Field_new_decimal::compatible_field_size(uint field_metadata,
- const Relay_log_info * __attribute__((unused)),
- uint16 mflags __attribute__((unused)))
+bool Field_new_decimal::compatible_field_size(uint field_metadata,
+ Relay_log_info * __attribute__((unused)),
+ uint16 mflags __attribute__((unused)),
+ int *order_var)
{
- int compatible= 0;
uint const source_precision= (field_metadata >> 8U) & 0x00ff;
uint const source_decimal= field_metadata & 0x00ff;
- uint const source_size= my_decimal_get_binary_size(source_precision,
- source_decimal);
- uint const destination_size= row_pack_length();
- compatible= (source_size <= destination_size);
- if (compatible)
- compatible= (source_precision <= precision) &&
- (source_decimal <= decimals());
- return (compatible);
+ int order= compare(source_precision, precision);
+ *order_var= order != 0 ? order : compare(source_decimal, dec);
+ return true;
}
@@ -3115,7 +3106,7 @@ String *Field_tiny::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
ASSERT_COLUMN_MARKED_FOR_READ;
- CHARSET_INFO *cs= &my_charset_bin;
+ CHARSET_INFO *cs= &my_charset_numeric;
uint length;
uint mlength=max(field_length+1,5*cs->mbmaxlen);
val_buffer->alloc(mlength);
@@ -3131,6 +3122,7 @@ String *Field_tiny::val_str(String *val_buffer,
val_buffer->length(length);
if (zerofill)
prepend_zeros(val_buffer);
+ val_buffer->set_charset(cs);
return val_buffer;
}
@@ -3296,7 +3288,7 @@ String *Field_short::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
ASSERT_COLUMN_MARKED_FOR_READ;
- CHARSET_INFO *cs= &my_charset_bin;
+ CHARSET_INFO *cs= &my_charset_numeric;
uint length;
uint mlength=max(field_length+1,7*cs->mbmaxlen);
val_buffer->alloc(mlength);
@@ -3312,6 +3304,7 @@ String *Field_short::val_str(String *val_buffer,
val_buffer->length(length);
if (zerofill)
prepend_zeros(val_buffer);
+ val_buffer->set_charset(cs);
return val_buffer;
}
@@ -3485,7 +3478,7 @@ String *Field_medium::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
ASSERT_COLUMN_MARKED_FOR_READ;
- CHARSET_INFO *cs= &my_charset_bin;
+ CHARSET_INFO *cs= &my_charset_numeric;
uint length;
uint mlength=max(field_length+1,10*cs->mbmaxlen);
val_buffer->alloc(mlength);
@@ -3496,6 +3489,7 @@ String *Field_medium::val_str(String *val_buffer,
val_buffer->length(length);
if (zerofill)
prepend_zeros(val_buffer); /* purecov: inspected */
+ val_buffer->set_charset(cs);
return val_buffer;
}
@@ -3673,7 +3667,7 @@ String *Field_long::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
ASSERT_COLUMN_MARKED_FOR_READ;
- CHARSET_INFO *cs= &my_charset_bin;
+ CHARSET_INFO *cs= &my_charset_numeric;
uint length;
uint mlength=max(field_length+1,12*cs->mbmaxlen);
val_buffer->alloc(mlength);
@@ -3688,6 +3682,7 @@ String *Field_long::val_str(String *val_buffer,
val_buffer->length(length);
if (zerofill)
prepend_zeros(val_buffer);
+ val_buffer->set_charset(cs);
return val_buffer;
}
@@ -3822,7 +3817,7 @@ longlong Field_longlong::val_int(void)
String *Field_longlong::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
- CHARSET_INFO *cs= &my_charset_bin;
+ CHARSET_INFO *cs= &my_charset_numeric;
uint length;
uint mlength=max(field_length+1,22*cs->mbmaxlen);
val_buffer->alloc(mlength);
@@ -3835,6 +3830,7 @@ String *Field_longlong::val_str(String *val_buffer,
val_buffer->length(length);
if (zerofill)
prepend_zeros(val_buffer);
+ val_buffer->set_charset(cs);
return val_buffer;
}
@@ -3956,75 +3952,35 @@ String *Field_float::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(!zerofill || field_length <= MAX_FIELD_CHARLENGTH);
float nr;
float4get(nr,ptr);
- uint to_length=max(field_length,70);
- val_buffer->alloc(to_length);
+ uint to_length= 70;
+ if (val_buffer->alloc(to_length))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return val_buffer;
+ }
+
char *to=(char*) val_buffer->ptr();
+ size_t len;
if (dec >= NOT_FIXED_DEC)
- {
- sprintf(to,"%-*.*g",(int) field_length,FLT_DIG,nr);
- to=strcend(to,' ');
- *to=0;
- }
+ len= my_gcvt(nr, MY_GCVT_ARG_FLOAT, to_length - 1, to, NULL);
else
{
-#ifdef HAVE_FCONVERT
- char buff[70],*pos=buff;
- int decpt,sign,tmp_dec=dec;
-
- VOID(sfconvert(&nr,tmp_dec,&decpt,&sign,buff));
- if (sign)
- {
- *to++='-';
- }
- if (decpt < 0)
- { /* val_buffer is < 0 */
- *to++='0';
- if (!tmp_dec)
- goto end;
- *to++='.';
- if (-decpt > tmp_dec)
- decpt= - (int) tmp_dec;
- tmp_dec=(uint) ((int) tmp_dec+decpt);
- while (decpt++ < 0)
- *to++='0';
- }
- else if (decpt == 0)
- {
- *to++= '0';
- if (!tmp_dec)
- goto end;
- *to++='.';
- }
- else
- {
- while (decpt-- > 0)
- *to++= *pos++;
- if (!tmp_dec)
- goto end;
- *to++='.';
- }
- while (tmp_dec--)
- *to++= *pos++;
-#else
-#ifdef HAVE_SNPRINTF
- to[to_length-1]=0; // Safety
- snprintf(to,to_length-1,"%.*f",dec,nr);
- to=strend(to);
-#else
- to+= my_sprintf(to,(to,"%.*f",dec,nr));
-#endif
-#endif
+ /*
+ We are safe here because the buffer length is 70, and
+ fabs(float) < 10^39, dec < NOT_FIXED_DEC. So the resulting string
+ will be not longer than 69 chars + terminating '\0'.
+ */
+ len= my_fcvt(nr, dec, to, NULL);
}
-#ifdef HAVE_FCONVERT
- end:
-#endif
- val_buffer->length((uint) (to-val_buffer->ptr()));
+ val_buffer->length((uint) len);
if (zerofill)
prepend_zeros(val_buffer);
+ val_buffer->set_charset(&my_charset_numeric);
return val_buffer;
}
@@ -4053,7 +4009,7 @@ void Field_float::sort_string(uchar *to,uint length __attribute__((unused)))
else
{
#ifdef WORDS_BIGENDIAN
- memcpy_fixed(tmp,&nr,sizeof(nr));
+ memcpy(tmp, &nr, sizeof(nr));
#else
tmp[0]= ptr[3]; tmp[1]=ptr[2]; tmp[2]= ptr[1]; tmp[3]=ptr[0];
#endif
@@ -4200,8 +4156,12 @@ int truncate_double(double *nr, uint field_length, uint dec,
max_value*= log_10[order];
max_value-= 1.0 / log_10[dec];
- double tmp= rint((res - floor(res)) * log_10[dec]) / log_10[dec];
- res= floor(res) + tmp;
+ /* Check for infinity so we don't get NaN in calculations */
+ if (!my_isinf(res))
+ {
+ double tmp= rint((res - floor(res)) * log_10[dec]) / log_10[dec];
+ res= floor(res) + tmp;
+ }
}
if (res < -max_value)
@@ -4303,13 +4263,11 @@ longlong Field_double::val_int(void)
res= double_to_longlong(j, 0, &error);
if (error)
{
- char buf[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
- String tmp(buf, sizeof(buf), &my_charset_latin1), *str;
- str= val_str(&tmp, 0);
+ ErrConvDouble err(j);
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER",
- str->c_ptr());
+ err.ptr());
}
return res;
}
@@ -4323,7 +4281,7 @@ my_decimal *Field_real::val_decimal(my_decimal *decimal_value)
}
-bool Field_real::get_date(MYSQL_TIME *ltime,uint fuzzydate)
+bool Field_real::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
{
ASSERT_COLUMN_MARKED_FOR_READ;
double nr= val_real();
@@ -4335,76 +4293,29 @@ String *Field_double::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
ASSERT_COLUMN_MARKED_FOR_READ;
+ DBUG_ASSERT(!zerofill || field_length <= MAX_FIELD_CHARLENGTH);
double nr;
float8get(nr,ptr);
uint to_length= DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE;
- val_buffer->alloc(to_length);
+ if (val_buffer->alloc(to_length))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return val_buffer;
+ }
+
char *to=(char*) val_buffer->ptr();
+ size_t len;
if (dec >= NOT_FIXED_DEC)
- {
- sprintf(to,"%-*.*g",(int) field_length,DBL_DIG,nr);
- to=strcend(to,' ');
- }
+ len= my_gcvt(nr, MY_GCVT_ARG_DOUBLE, to_length - 1, to, NULL);
else
- {
-#ifdef HAVE_FCONVERT
- char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
- char *pos= buff;
- int decpt,sign,tmp_dec=dec;
-
- VOID(fconvert(nr,tmp_dec,&decpt,&sign,buff));
- if (sign)
- {
- *to++='-';
- }
- if (decpt < 0)
- { /* val_buffer is < 0 */
- *to++='0';
- if (!tmp_dec)
- goto end;
- *to++='.';
- if (-decpt > tmp_dec)
- decpt= - (int) tmp_dec;
- tmp_dec=(uint) ((int) tmp_dec+decpt);
- while (decpt++ < 0)
- *to++='0';
- }
- else if (decpt == 0)
- {
- *to++= '0';
- if (!tmp_dec)
- goto end;
- *to++='.';
- }
- else
- {
- while (decpt-- > 0)
- *to++= *pos++;
- if (!tmp_dec)
- goto end;
- *to++='.';
- }
- while (tmp_dec--)
- *to++= *pos++;
-#else
-#ifdef HAVE_SNPRINTF
- to[to_length-1]=0; // Safety
- snprintf(to,to_length-1,"%.*f",dec,nr);
- to=strend(to);
-#else
- to+= my_sprintf(to,(to,"%.*f",dec,nr));
-#endif
-#endif
- }
-#ifdef HAVE_FCONVERT
- end:
-#endif
+ len= my_fcvt(nr, dec, to, NULL);
- val_buffer->length((uint) (to-val_buffer->ptr()));
+ val_buffer->length((uint) len);
if (zerofill)
prepend_zeros(val_buffer);
+ val_buffer->set_charset(&my_charset_numeric);
return val_buffer;
}
@@ -4522,7 +4433,7 @@ Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg,
unireg_check_arg, field_name_arg, cs)
{
/* For 4.0 MYD and 4.0 InnoDB compatibility */
- flags|= UNSIGNED_FLAG;
+ flags|= UNSIGNED_FLAG | BINARY_FLAG;
if (unireg_check != NONE && !share->timestamp_field)
{
/* This timestamp has auto-update */
@@ -4575,8 +4486,9 @@ my_time_t Field_timestamp::get_timestamp(ulong *sec_part) const
return sint4korr(ptr);
}
+
int Field_timestamp::store_TIME_with_warning(THD *thd, MYSQL_TIME *l_time,
- const Lazy_string *str,
+ const ErrConv *str,
bool was_cut,
bool have_smth_to_conv)
{
@@ -4619,7 +4531,7 @@ int Field_timestamp::store_time_dec(MYSQL_TIME *ltime, uint dec)
THD *thd= table->in_use;
int unused;
MYSQL_TIME l_time= *ltime;
- Lazy_string_time str(ltime);
+ ErrConvTime str(ltime);
bool valid= !check_date(&l_time, pack_time(&l_time) != 0,
(thd->variables.sql_mode & MODE_NO_ZERO_DATE) |
MODE_NO_ZERO_IN_DATE, &unused);
@@ -4633,11 +4545,11 @@ int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs)
MYSQL_TIME l_time;
int error;
int have_smth_to_conv;
- Lazy_string_str str(from, len);
+ ErrConvString str(from, len, cs);
THD *thd= table->in_use;
/* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */
- have_smth_to_conv= (str_to_datetime(from, len, &l_time,
+ have_smth_to_conv= (str_to_datetime(cs, from, len, &l_time,
(thd->variables.sql_mode &
MODE_NO_ZERO_DATE) |
MODE_NO_ZERO_IN_DATE, &error) >
@@ -4650,7 +4562,7 @@ int Field_timestamp::store(double nr)
{
MYSQL_TIME l_time;
int error;
- Lazy_string_double str(nr);
+ ErrConvDouble str(nr);
THD *thd= table->in_use;
longlong tmp= double_to_datetime(nr, &l_time, (thd->variables.sql_mode &
@@ -4664,7 +4576,7 @@ int Field_timestamp::store(longlong nr, bool unsigned_val)
{
MYSQL_TIME l_time;
int error;
- Lazy_string_num str(nr, unsigned_val);
+ ErrConvInteger str(nr, unsigned_val);
THD *thd= table->in_use;
/* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */
@@ -4683,54 +4595,34 @@ double Field_timestamp::val_real(void)
longlong Field_timestamp::val_int(void)
{
- MYSQL_TIME time_tmp;
- THD *thd= table->in_use;
-
- thd->time_zone_used= 1;
- ulong sec_part;
- my_time_t temp= get_timestamp(&sec_part);
-
- /*
- Field_timestamp() and Field_timestamp_hres() shares this code.
- This is why are also testing sec_part below.
- */
+ MYSQL_TIME ltime;
+ if (get_date(&ltime, TIME_NO_ZERO_DATE))
+ return 0;
- if (temp == 0 && sec_part == 0)
- return(0);
-
- thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, (my_time_t)temp);
-
- return time_tmp.year * LL(10000000000) + time_tmp.month * LL(100000000) +
- time_tmp.day * 1000000L + time_tmp.hour * 10000L +
- time_tmp.minute * 100 + time_tmp.second;
+ return ltime.year * 10000000000LL + ltime.month * 100000000LL +
+ ltime.day * 1000000L + ltime.hour * 10000L +
+ ltime.minute * 100 + ltime.second;
}
String *Field_timestamp::val_str(String *val_buffer, String *val_ptr)
{
- uint32 temp2;
- THD *thd= table->in_use;
- MYSQL_TIME time_tmp;
+ MYSQL_TIME ltime;
+ uint32 temp, temp2;
char *to;
val_buffer->alloc(field_length+1);
to= (char*) val_buffer->ptr();
val_buffer->length(field_length);
- thd->time_zone_used= 1;
- ulong sec_part;
- my_time_t temp= get_timestamp(&sec_part);
-
- if (temp == 0 && sec_part == 0)
+ if (get_date(&ltime, TIME_NO_ZERO_DATE))
{ /* Zero time is "000000" */
- val_ptr->set(zero_timestamp, field_length, &my_charset_bin);
+ val_ptr->set(zero_timestamp, field_length, &my_charset_numeric);
return val_ptr;
}
- val_buffer->set_charset(&my_charset_bin); // Safety
-
- thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,(my_time_t)temp);
-
- temp= time_tmp.year % 100;
+ val_buffer->set_charset(&my_charset_numeric); // Safety
+
+ temp= ltime.year % 100;
if (temp < YY_PART_YEAR - 1)
{
*to++= '2';
@@ -4745,36 +4637,37 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr)
*to++= (char) ('0'+(char) (temp2));
*to++= (char) ('0'+(char) (temp));
*to++= '-';
- temp=time_tmp.month;
+ temp=ltime.month;
temp2=temp/10; temp=temp-temp2*10;
*to++= (char) ('0'+(char) (temp2));
*to++= (char) ('0'+(char) (temp));
*to++= '-';
- temp=time_tmp.day;
+ temp=ltime.day;
temp2=temp/10; temp=temp-temp2*10;
*to++= (char) ('0'+(char) (temp2));
*to++= (char) ('0'+(char) (temp));
*to++= ' ';
- temp=time_tmp.hour;
+ temp=ltime.hour;
temp2=temp/10; temp=temp-temp2*10;
*to++= (char) ('0'+(char) (temp2));
*to++= (char) ('0'+(char) (temp));
*to++= ':';
- temp=time_tmp.minute;
+ temp=ltime.minute;
temp2=temp/10; temp=temp-temp2*10;
*to++= (char) ('0'+(char) (temp2));
*to++= (char) ('0'+(char) (temp));
*to++= ':';
- temp=time_tmp.second;
+ temp=ltime.second;
temp2=temp/10; temp=temp-temp2*10;
*to++= (char) ('0'+(char) (temp2));
*to++= (char) ('0'+(char) (temp));
*to= 0;
+ val_buffer->set_charset(&my_charset_numeric);
return val_buffer;
}
-bool Field_timestamp::get_date(MYSQL_TIME *ltime, uint fuzzydate)
+bool Field_timestamp::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
THD *thd= table->in_use;
thd->time_zone_used= 1;
@@ -4937,21 +4830,13 @@ my_time_t Field_timestamp_hires::get_timestamp(ulong *sec_part) const
double Field_timestamp_hires::val_real(void)
{
- MYSQL_TIME time_tmp;
- THD *thd= table->in_use;
-
- thd->time_zone_used= 1;
- ulong sec_part;
- my_time_t temp= get_timestamp(&sec_part);
-
- if (temp == 0 && sec_part == 0)
- return(0);
-
- thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, (my_time_t)temp);
+ MYSQL_TIME ltime;
+ if (get_date(&ltime, TIME_NO_ZERO_DATE))
+ return 0;
- return time_tmp.year * 1e10 + time_tmp.month * 1e8 +
- time_tmp.day * 1e6 + time_tmp.hour * 1e4 +
- time_tmp.minute * 1e2 + time_tmp.second + sec_part*1e-6;
+ return ltime.year * 1e10 + ltime.month * 1e8 +
+ ltime.day * 1e6 + ltime.hour * 1e4 +
+ ltime.minute * 1e2 + ltime.second + ltime.second_part*1e-6;
}
String *Field_timestamp_hires::val_str(String *val_buffer, String *val_ptr)
@@ -4987,7 +4872,7 @@ int Field_timestamp_hires::store_decimal(const my_decimal *d)
MYSQL_TIME ltime;
longlong tmp;
THD *thd= table->in_use;
- Lazy_string_decimal str(d);
+ ErrConvDecimal str(d);
if (my_decimal2seconds(d, &nr, &sec_part))
{
@@ -5059,7 +4944,7 @@ void Field_timestamp_hires::make_field(Send_field *field)
This is used by opt_range.cc:get_mm_leaf().
*/
int Field_temporal::store_TIME_with_warning(MYSQL_TIME *ltime,
- const Lazy_string *str,
+ const ErrConv *str,
int was_cut, int have_smth_to_conv)
{
MYSQL_ERROR::enum_warning_level trunc_level= MYSQL_ERROR::WARN_LEVEL_WARN;
@@ -5080,7 +4965,7 @@ int Field_temporal::store_TIME_with_warning(MYSQL_TIME *ltime,
if (was_cut == 0 &&
have_smth_to_conv == 0 &&
- temporal_type() != MYSQL_TIMESTAMP_TIME) // special case: zero date
+ mysql_type_to_time_type(type()) != MYSQL_TIMESTAMP_TIME) // special case: zero date
was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE;
else
if (!have_smth_to_conv)
@@ -5090,7 +4975,7 @@ int Field_temporal::store_TIME_with_warning(MYSQL_TIME *ltime,
ret= 1;
}
else if (!(was_cut & MYSQL_TIME_WARN_TRUNCATED) &&
- temporal_type() == MYSQL_TIMESTAMP_DATE &&
+ mysql_type_to_time_type(type()) == MYSQL_TIMESTAMP_DATE &&
(ltime->hour || ltime->minute || ltime->second || ltime->second_part))
{
trunc_level= MYSQL_ERROR::WARN_LEVEL_NOTE;
@@ -5098,7 +4983,7 @@ int Field_temporal::store_TIME_with_warning(MYSQL_TIME *ltime,
ret= 3;
}
else if (!(was_cut & MYSQL_TIME_WARN_TRUNCATED) &&
- temporal_type() == MYSQL_TIMESTAMP_TIME &&
+ mysql_type_to_time_type(type()) == MYSQL_TIMESTAMP_TIME &&
(ltime->year || ltime->month))
{
ltime->year= ltime->month= ltime->day= 0;
@@ -5119,10 +5004,10 @@ int Field_temporal::store_TIME_with_warning(MYSQL_TIME *ltime,
*/
if (was_cut & MYSQL_TIME_WARN_TRUNCATED)
set_datetime_warning(trunc_level, WARN_DATA_TRUNCATED,
- str, temporal_type(), 1);
+ str, mysql_type_to_time_type(type()), 1);
if (was_cut & MYSQL_TIME_WARN_OUT_OF_RANGE)
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE,
- str, temporal_type(), 1);
+ str, mysql_type_to_time_type(type()), 1);
store_TIME(ltime);
return was_cut ? ret : 0;
@@ -5135,10 +5020,10 @@ int Field_temporal::store(const char *from,uint len,CHARSET_INFO *cs)
int error;
enum enum_mysql_timestamp_type func_res;
THD *thd= table->in_use;
- Lazy_string_str str(from, len);
+ ErrConvString str(from, len, cs);
- func_res= str_to_datetime(from, len, &ltime,
- (thd->variables.sql_mode &
+ func_res= str_to_datetime(cs, from, len, &ltime,
+ (thd->variables.sql_mode &
(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE |
MODE_INVALID_DATES)),
&error);
@@ -5151,7 +5036,7 @@ int Field_temporal::store(double nr)
int error= 0;
MYSQL_TIME ltime;
THD *thd= table->in_use;
- Lazy_string_double str(nr);
+ ErrConvDouble str(nr);
longlong tmp= double_to_datetime(nr, &ltime,
(thd->variables.sql_mode &
@@ -5168,7 +5053,7 @@ int Field_temporal::store(longlong nr, bool unsigned_val)
MYSQL_TIME ltime;
longlong tmp;
THD *thd= table->in_use;
- Lazy_string_num str(nr, unsigned_val);
+ ErrConvInteger str(nr, unsigned_val);
tmp= number_to_datetime(nr, 0, &ltime, (thd->variables.sql_mode &
(MODE_NO_ZERO_IN_DATE |
@@ -5183,8 +5068,8 @@ int Field_temporal::store_time_dec(MYSQL_TIME *ltime, uint dec)
{
int error = 0, have_smth_to_conv= 1;
MYSQL_TIME l_time= *ltime;
- Lazy_string_time str(ltime);
-
+ ErrConvTime str(ltime);
+
if (l_time.time_type == MYSQL_TIMESTAMP_TIME && time_to_datetime(&l_time))
{
have_smth_to_conv= 0;
@@ -5234,15 +5119,15 @@ void Field_time::store_TIME(MYSQL_TIME *ltime)
int Field_time::store(const char *from,uint len,CHARSET_INFO *cs)
{
MYSQL_TIME ltime;
- Lazy_string_str str(from, len);
+ ErrConvString str(from, len, cs);
int was_cut;
int have_smth_to_conv=
- str_to_time(from, len, &ltime,
+ str_to_time(cs, from, len, &ltime,
table->in_use->variables.sql_mode &
(MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE |
MODE_INVALID_DATES),
&was_cut) > MYSQL_TIMESTAMP_ERROR;
-
+
return store_TIME_with_warning(&ltime, &str, was_cut, have_smth_to_conv);
}
@@ -5250,7 +5135,7 @@ int Field_time::store(const char *from,uint len,CHARSET_INFO *cs)
int Field_time::store_time_dec(MYSQL_TIME *ltime, uint dec)
{
MYSQL_TIME l_time= *ltime;
- Lazy_string_time str(ltime);
+ ErrConvTime str(ltime);
int was_cut= 0;
int have_smth_to_conv= !check_time_range(&l_time, decimals(), &was_cut);
@@ -5261,7 +5146,7 @@ int Field_time::store_time_dec(MYSQL_TIME *ltime, uint dec)
int Field_time::store(double nr)
{
MYSQL_TIME ltime;
- Lazy_string_double str(nr);
+ ErrConvDouble str(nr);
int was_cut;
bool neg= nr < 0;
if (neg)
@@ -5277,7 +5162,7 @@ int Field_time::store(double nr)
int Field_time::store(longlong nr, bool unsigned_val)
{
MYSQL_TIME ltime;
- Lazy_string_num str(nr, unsigned_val);
+ ErrConvInteger str(nr, unsigned_val);
int was_cut;
if (nr < 0 && unsigned_val)
nr= 99991231235959LL + 1;
@@ -5332,7 +5217,7 @@ String *Field_time::val_str(String *val_buffer,
uint length= (uint) my_time_to_str(&ltime,
const_cast<char*>(val_buffer->ptr()), 0);
val_buffer->length(length);
- val_buffer->set_charset(&my_charset_bin);
+ val_buffer->set_charset(&my_charset_numeric);
return val_buffer;
}
@@ -5345,7 +5230,7 @@ String *Field_time::val_str(String *val_buffer,
DATE_FORMAT(time, "%l.%i %p")
*/
-bool Field_time::get_date(MYSQL_TIME *ltime, uint fuzzydate)
+bool Field_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
THD *thd= table->in_use;
if (!(fuzzydate & TIME_TIME_ONLY) &&
@@ -5354,7 +5239,7 @@ bool Field_time::get_date(MYSQL_TIME *ltime, uint fuzzydate)
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_DATA_OUT_OF_RANGE,
ER(ER_WARN_DATA_OUT_OF_RANGE), field_name,
- thd->row_count);
+ thd->warning_info->current_row_for_warning());
return 1;
}
long tmp=(long) sint3korr(ptr);
@@ -5409,6 +5294,7 @@ int Field_time_hires::reset()
return 0;
}
+
void Field_time_hires::store_TIME(MYSQL_TIME *ltime)
{
ulonglong packed= sec_part_shift(pack_time(ltime), dec) + zero_point;
@@ -5419,7 +5305,7 @@ int Field_time_hires::store_decimal(const my_decimal *d)
{
ulonglong nr;
ulong sec_part;
- Lazy_string_decimal str(d);
+ ErrConvDecimal str(d);
MYSQL_TIME ltime;
int was_cut;
bool neg= my_decimal2seconds(d, &nr, &sec_part);
@@ -5463,7 +5349,7 @@ String *Field_time_hires::val_str(String *str,
return str;
}
-bool Field_time_hires::get_date(MYSQL_TIME *ltime, uint fuzzydate)
+bool Field_time_hires::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
uint32 len= pack_length();
longlong packed= read_bigendian(ptr, len);
@@ -5473,7 +5359,7 @@ bool Field_time_hires::get_date(MYSQL_TIME *ltime, uint fuzzydate)
unpack_time(packed, ltime);
/*
unpack_time() returns MYSQL_TIMESTAMP_DATETIME.
- To get MYSQL_TIMESTAMP_TIME we few adjustments
+ To get MYSQL_TIMESTAMP_TIME we need few adjustments
*/
ltime->time_type= MYSQL_TIMESTAMP_TIME;
ltime->hour+= (ltime->month*32+ltime->day)*24;
@@ -5594,7 +5480,7 @@ int Field_year::store(longlong nr, bool unsigned_val)
int Field_year::store_time_dec(MYSQL_TIME *ltime, uint dec)
{
- Lazy_string_time str(ltime);
+ ErrConvTime str(ltime);
if (Field_year::store(ltime->year, 0))
return 1;
@@ -5638,11 +5524,12 @@ String *Field_year::val_str(String *val_buffer,
val_buffer->length(field_length);
char *to=(char*) val_buffer->ptr();
sprintf(to,field_length == 2 ? "%02d" : "%04d",(int) Field_year::val_int());
+ val_buffer->set_charset(&my_charset_numeric);
return val_buffer;
}
-bool Field_year::get_date(MYSQL_TIME *ltime,uint fuzzydate)
+bool Field_year::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
{
int tmp= (int) ptr[0];
if (tmp || field_length != 4)
@@ -5718,7 +5605,7 @@ String *Field_date::val_str(String *val_buffer,
uint length= (uint) my_date_to_str(&ltime,
const_cast<char*>(val_buffer->ptr()));
val_buffer->length(length);
- val_buffer->set_charset(&my_charset_bin);
+ val_buffer->set_charset(&my_charset_numeric);
return val_buffer;
}
@@ -5809,11 +5696,12 @@ String *Field_newdate::val_str(String *val_buffer,
*pos--= (char) ('0'+part%10); part/=10;
*pos--= (char) ('0'+part%10); part/=10;
*pos= (char) ('0'+part);
+ val_buffer->set_charset(&my_charset_numeric);
return val_buffer;
}
-bool Field_newdate::get_date(MYSQL_TIME *ltime,uint fuzzydate)
+bool Field_newdate::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
{
uint32 tmp=(uint32) uint3korr(ptr);
ltime->day= tmp & 31;
@@ -5929,10 +5817,11 @@ String *Field_datetime::val_str(String *val_buffer,
*pos--= (char) ('0'+(char) (part3%10)); part3/=10;
*pos--= (char) ('0'+(char) (part3%10)); part3/=10;
*pos=(char) ('0'+(char) part3);
+ val_buffer->set_charset(&my_charset_numeric);
return val_buffer;
}
-bool Field_datetime::get_date(MYSQL_TIME *ltime, uint fuzzydate)
+bool Field_datetime::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
longlong tmp=Field_datetime::val_int();
uint32 part1,part2;
@@ -5949,7 +5838,7 @@ bool Field_datetime::get_date(MYSQL_TIME *ltime, uint fuzzydate)
ltime->month= (int) (part1/100%100);
ltime->year= (int) (part1/10000);
if (!tmp)
- return (fuzzydate & TIME_NO_ZERO_DATE) != 0;
+ return fuzzydate & TIME_NO_ZERO_DATE;
if (!ltime->month || !ltime->day)
return fuzzydate & TIME_NO_ZERO_IN_DATE;
return 0;
@@ -5985,7 +5874,7 @@ void Field_datetime::sql_type(String &res) const
void Field_datetime_hires::store_TIME(MYSQL_TIME *ltime)
{
ulonglong packed= sec_part_shift(pack_time(ltime), dec);
- store_bigendian(packed, ptr, pack_length());
+ store_bigendian(packed, ptr, Field_datetime_hires::pack_length());
}
int Field_datetime_hires::store_decimal(const my_decimal *d)
@@ -5996,7 +5885,7 @@ int Field_datetime_hires::store_decimal(const my_decimal *d)
MYSQL_TIME ltime;
longlong tmp;
THD *thd= table->in_use;
- Lazy_string_decimal str(d);
+ ErrConvDecimal str(d);
if (my_decimal2seconds(d, &nr, &sec_part))
{
@@ -6047,9 +5936,9 @@ String *Field_datetime_hires::val_str(String *str,
return str;
}
-bool Field_datetime_hires::get_date(MYSQL_TIME *ltime, uint fuzzydate)
+bool Field_datetime_hires::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
- ulonglong packed= read_bigendian(ptr, pack_length());
+ ulonglong packed= read_bigendian(ptr, Field_datetime_hires::pack_length());
unpack_time(sec_part_unshift(packed, dec), ltime);
if (!packed)
return fuzzydate & TIME_NO_ZERO_DATE;
@@ -6065,15 +5954,15 @@ uint32 Field_datetime_hires::pack_length() const
int Field_datetime_hires::cmp(const uchar *a_ptr, const uchar *b_ptr)
{
- ulonglong a=read_bigendian(a_ptr, pack_length());
- ulonglong b=read_bigendian(b_ptr, pack_length());
+ ulonglong a=read_bigendian(a_ptr, Field_datetime_hires::pack_length());
+ ulonglong b=read_bigendian(b_ptr, Field_datetime_hires::pack_length());
return a < b ? -1 : a > b ? 1 : 0;
}
void Field_datetime_hires::sort_string(uchar *to,
uint length __attribute__((unused)))
{
- DBUG_ASSERT(length == pack_length());
+ DBUG_ASSERT(length == Field_datetime_hires::pack_length());
memcpy(to, ptr, length);
}
@@ -6131,21 +6020,20 @@ check_string_copy_error(Field_str *field,
{
const char *pos;
char tmp[32];
-
+ THD *thd= field->table->in_use;
+
if (!(pos= well_formed_error_pos) &&
!(pos= cannot_convert_error_pos))
return FALSE;
convert_to_printable(tmp, sizeof(tmp), pos, (end - pos), cs, 6);
- push_warning_printf(field->table->in_use,
- field->table->in_use->abort_on_warning ?
- MYSQL_ERROR::WARN_LEVEL_ERROR :
+ push_warning_printf(thd,
MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
+ ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
"string", tmp, field->field_name,
- (ulong) field->table->in_use->row_count);
+ thd->warning_info->current_row_for_warning());
return TRUE;
}
@@ -6179,7 +6067,7 @@ Field_longstr::report_if_important_data(const char *pstr, const char *end,
if (test_if_important_data(field_charset, pstr, end))
{
if (table->in_use->abort_on_warning)
- set_warning(MYSQL_ERROR::WARN_LEVEL_ERROR, ER_DATA_TOO_LONG, 1);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1);
else
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
return 2;
@@ -6241,91 +6129,22 @@ int Field_str::store(double nr)
{
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
- uint length;
uint local_char_length= field_length / charset()->mbmaxlen;
- double anr= fabs(nr);
- bool fractional= (anr != floor(anr));
- int neg= (nr < 0.0) ? 1 : 0;
- uint max_length;
- int exp;
- uint digits;
- uint i;
-
- /* Calculate the exponent from the 'e'-format conversion */
- if (anr < 1.0 && anr > 0)
- {
- for (exp= 0; anr < 1e-100; exp-= 100, anr*= 1e100)
- ;
- for (; anr < 1e-10; exp-= 10, anr*= 1e10)
- ;
- for (i= 1; anr < 1 / log_10[i]; exp--, i++)
- ;
- exp--;
- }
- else
- {
- for (exp= 0; anr > 1e100; exp+= 100, anr/= 1e100)
- ;
- for (; anr > 1e10; exp+= 10, anr/= 1e10)
- ;
- for (i= 1; anr > log_10[i]; exp++, i++)
- ;
- }
+ size_t length= 0;
+ my_bool error= (local_char_length == 0);
- max_length= local_char_length - neg;
+ // my_gcvt() requires width > 0, and we may have a CHAR(0) column.
+ if (!error)
+ length= my_gcvt(nr, MY_GCVT_ARG_DOUBLE, local_char_length, buff, &error);
- /*
- Since in sprintf("%g") precision means the number of significant digits,
- calculate the maximum number of significant digits if the 'f'-format
- would be used (+1 for decimal point if the number has a fractional part).
- */
- digits= max(1, (int) max_length - fractional);
- /*
- If the exponent is negative, decrease digits by the number of leading zeros
- after the decimal point that do not count as significant digits.
- */
- if (exp < 0)
- digits= max(1, (int) digits + exp);
- /*
- 'e'-format is used only if the exponent is less than -4 or greater than or
- equal to the precision. In this case we need to adjust the number of
- significant digits to take "e+NN" + decimal point into account (hence -5).
- We also have to reserve one additional character if abs(exp) >= 100.
- */
- if (exp >= (int) digits || exp < -4)
- digits= max(1, (int) (max_length - 5 - (exp >= 100 || exp <= -100)));
-
- /* Limit precision to DBL_DIG to avoid garbage past significant digits */
- set_if_smaller(digits, DBL_DIG);
-
- length= (uint) my_sprintf(buff, (buff, "%-.*g", digits, nr));
-
-#ifdef __WIN__
- /*
- Windows always zero-pads the exponent to 3 digits, we want to remove the
- leading 0 to match the sprintf() output on other platforms.
- */
- if ((exp >= (int) digits || exp < -4) && exp > -100 && exp < 100)
+ if (error)
{
- DBUG_ASSERT(length >= 6); /* 1e+NNN */
- uint tmp= length - 3;
- buff[tmp]= buff[tmp + 1];
- tmp++;
- buff[tmp]= buff[tmp + 1];
- length--;
+ if (table->in_use->abort_on_warning)
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1);
+ else
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
}
-#endif
-
- /*
- +1 below is because "precision" in %g above means the
- max. number of significant digits, not the output width.
- Thus the width can be larger than number of significant digits by 1
- (for decimal point)
- the test for local_char_length < 5 is for extreme cases,
- like inserting 500.0 in char(1)
- */
- DBUG_ASSERT(local_char_length < 5 || length <= local_char_length+1);
- return store(buff, length, charset());
+ return store(buff, length, &my_charset_numeric);
}
@@ -6360,7 +6179,7 @@ int Field_string::store(longlong nr, bool unsigned_val)
int Field_longstr::store_decimal(const my_decimal *d)
{
char buff[DECIMAL_MAX_STR_LENGTH+1];
- String str(buff, sizeof(buff), &my_charset_bin);
+ String str(buff, sizeof(buff), &my_charset_numeric);
my_decimal2string(E_DEC_FATAL_ERROR, d, 0, 0, 0, &str);
return store(str.ptr(), str.length(), str.charset());
}
@@ -6385,13 +6204,11 @@ double Field_string::val_real(void)
!check_if_only_end_space(cs, end,
(char*) ptr + field_length))))
{
- char buf[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
- String tmp(buf, sizeof(buf), cs);
- tmp.copy((char*) ptr, field_length, cs);
+ ErrConvString err((char*) ptr, field_length, cs);
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_TRUNCATED_WRONG_VALUE,
- ER(ER_TRUNCATED_WRONG_VALUE),
- "DOUBLE", tmp.c_ptr());
+ ER_TRUNCATED_WRONG_VALUE,
+ ER(ER_TRUNCATED_WRONG_VALUE), "DOUBLE",
+ err.ptr());
}
return result;
}
@@ -6411,13 +6228,11 @@ longlong Field_string::val_int(void)
!check_if_only_end_space(cs, end,
(char*) ptr + field_length))))
{
- char buf[LONGLONG_TO_STRING_CONVERSION_BUFFER_SIZE];
- String tmp(buf, sizeof(buf), cs);
- tmp.copy((char*) ptr, field_length, cs);
+ ErrConvString err((char*) ptr, field_length, cs);
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE),
- "INTEGER", tmp.c_ptr());
+ "INTEGER", err.ptr());
}
return result;
}
@@ -6449,14 +6264,11 @@ my_decimal *Field_string::val_decimal(my_decimal *decimal_value)
charset(), decimal_value);
if (!table->in_use->no_errors && err)
{
- char buf[DECIMAL_TO_STRING_CONVERSION_BUFFER_SIZE];
- CHARSET_INFO *cs= charset();
- String tmp(buf, sizeof(buf), cs);
- tmp.copy((char*) ptr, field_length, cs);
+ ErrConvString errmsg((char*) ptr, field_length, charset());
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE),
- "DECIMAL", tmp.c_ptr());
+ "DECIMAL", errmsg.ptr());
}
return decimal_value;
@@ -6481,9 +6293,11 @@ check_field_for_37426(const void *param_arg)
}
#endif
-int Field_string::compatible_field_size(uint field_metadata,
- const Relay_log_info *rli_arg,
- uint16 mflags __attribute__((unused)))
+bool
+Field_string::compatible_field_size(uint field_metadata,
+ Relay_log_info *rli_arg,
+ uint16 mflags __attribute__((unused)),
+ int *order_var)
{
#ifdef HAVE_REPLICATION
const Check_field_param check_param = { this };
@@ -6491,7 +6305,7 @@ int Field_string::compatible_field_size(uint field_metadata,
check_field_for_37426, &check_param))
return FALSE; // Not compatible field sizes
#endif
- return Field::compatible_field_size(field_metadata, rli_arg, mflags);
+ return Field::compatible_field_size(field_metadata, rli_arg, mflags, order_var);
}
@@ -6520,9 +6334,8 @@ int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr)
void Field_string::sort_string(uchar *to,uint length)
{
- IF_DBUG(uint tmp=) my_strnxfrm(field_charset,
- to, length,
- ptr, field_length);
+ uint tmp __attribute__((unused))=
+ my_strnxfrm(field_charset, to, length, ptr, field_length);
DBUG_ASSERT(tmp == length);
}
@@ -6550,12 +6363,26 @@ uchar *Field_string::pack(uchar *to, const uchar *from, uint max_length)
{
uint length= 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));
+
if (length > local_char_length)
local_char_length= my_charpos(field_charset, from, from+length,
local_char_length);
set_if_smaller(length, local_char_length);
- while (length && from[length-1] == field_charset->pad_char)
- length--;
+
+ /*
+ TODO: change charset interface to add a new function that does
+ the following or add a flag to lengthsp to do it itself
+ (this is for not packing padding adding bytes in BINARY
+ fields).
+ */
+ if (field_charset->mbmaxlen == 1)
+ {
+ while (length && from[length-1] == field_charset->pad_char)
+ length --;
+ }
+ else
+ length= field_charset->cset->lengthsp(field_charset, (const char*) from, length);
// Length always stored little-endian
*to++= (uchar) length;
@@ -6627,7 +6454,7 @@ Field_string::unpack(uchar *to, const uchar *from, const uchar *from_end,
memcpy(to, from, length);
// Pad the string with the pad character of the fields charset
- bfill(to + length, field_length - length, field_charset->pad_char);
+ field_charset->cset->fill(field_charset, (char*) to + length, field_length - length, field_charset->pad_char);
return from+length;
}
@@ -6674,86 +6501,6 @@ int Field_string::do_save_field_metadata(uchar *metadata_ptr)
}
-/*
- Compare two packed keys
-
- SYNOPSIS
- pack_cmp()
- a New key
- b Original key
- length Key length
- insert_or_update 1 if this is an insert or update
-
- RETURN
- < 0 a < b
- 0 a = b
- > 0 a > b
-*/
-
-int Field_string::pack_cmp(const uchar *a, const uchar *b, uint length,
- bool insert_or_update)
-{
- uint a_length, b_length;
- if (length > 255)
- {
- a_length= uint2korr(a);
- b_length= uint2korr(b);
- a+= 2;
- b+= 2;
- }
- else
- {
- a_length= (uint) *a++;
- b_length= (uint) *b++;
- }
- return field_charset->coll->strnncollsp(field_charset,
- a, a_length,
- b, b_length,
- insert_or_update);
-}
-
-
-/**
- Compare a packed key against row.
-
- @param key Original key
- @param length Key length. (May be less than field length)
- @param insert_or_update 1 if this is an insert or update
-
- @return
- < 0 row < key
- @return
- 0 row = key
- @return
- > 0 row > key
-*/
-
-int Field_string::pack_cmp(const uchar *key, uint length,
- bool insert_or_update)
-{
- uint row_length, local_key_length;
- uchar *end;
- if (length > 255)
- {
- local_key_length= uint2korr(key);
- key+= 2;
- }
- else
- local_key_length= (uint) *key++;
-
- /* Only use 'length' of key, not field_length */
- end= ptr + length;
- while (end > ptr && end[-1] == ' ')
- end--;
- row_length= (uint) (end - ptr);
-
- return field_charset->coll->strnncollsp(field_charset,
- ptr, row_length,
- key, local_key_length,
- insert_or_update);
-}
-
-
uint Field_string::packed_col_length(const uchar *data_ptr, uint length)
{
if (length > 255)
@@ -6781,7 +6528,7 @@ uint Field_string::get_key_image(uchar *buff, uint length, imagetype type_arg)
}
-Field *Field_string::new_field(MEM_ROOT *root, struct st_table *new_table,
+Field *Field_string::new_field(MEM_ROOT *root, TABLE *new_table,
bool keep_type)
{
Field *field;
@@ -6891,22 +6638,46 @@ int Field_varstring::store(longlong nr, bool unsigned_val)
double Field_varstring::val_real(void)
{
ASSERT_COLUMN_MARKED_FOR_READ;
- int not_used;
- char *end_not_used;
+ int error;
+ char *end;
+ double result;
+ CHARSET_INFO* cs= charset();
+
uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
- return my_strntod(field_charset, (char*) ptr+length_bytes, length,
- &end_not_used, &not_used);
+ result= my_strntod(cs, (char*)ptr+length_bytes, length, &end, &error);
+
+ if (!table->in_use->no_errors &&
+ (error || (length != (uint)(end - (char*)ptr+length_bytes) &&
+ !check_if_only_end_space(cs, end, (char*)ptr+length_bytes+length))))
+ {
+ push_numerical_conversion_warning(current_thd, (char*)ptr+length_bytes,
+ length, cs,"DOUBLE",
+ ER_TRUNCATED_WRONG_VALUE);
+ }
+ return result;
}
longlong Field_varstring::val_int(void)
{
ASSERT_COLUMN_MARKED_FOR_READ;
- int not_used;
- char *end_not_used;
+ int error;
+ char *end;
+ CHARSET_INFO *cs= charset();
+
uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
- return my_strntoll(field_charset, (char*) ptr+length_bytes, length, 10,
- &end_not_used, &not_used);
+ longlong result= my_strntoll(cs, (char*) ptr+length_bytes, length, 10,
+ &end, &error);
+
+ if (!table->in_use->no_errors &&
+ (error || (length != (uint)(end - (char*)ptr+length_bytes) &&
+ !check_if_only_end_space(cs, end, (char*)ptr+length_bytes+length))))
+ {
+ push_numerical_conversion_warning(current_thd, (char*)ptr+length_bytes,
+ length, cs, "INTEGER",
+ ER_TRUNCATED_WRONG_VALUE);
+ }
+ return result;
}
String *Field_varstring::val_str(String *val_buffer __attribute__((unused)),
@@ -6922,9 +6693,17 @@ String *Field_varstring::val_str(String *val_buffer __attribute__((unused)),
my_decimal *Field_varstring::val_decimal(my_decimal *decimal_value)
{
ASSERT_COLUMN_MARKED_FOR_READ;
+ CHARSET_INFO *cs= charset();
uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
- str2my_decimal(E_DEC_FATAL_ERROR, (char*) ptr+length_bytes, length,
- charset(), decimal_value);
+ int error= str2my_decimal(E_DEC_FATAL_ERROR, (char*) ptr+length_bytes, length,
+ cs, decimal_value);
+
+ if (!table->in_use->no_errors && error)
+ {
+ push_numerical_conversion_warning(current_thd, (char*)ptr+length_bytes,
+ length, cs, "DECIMAL",
+ ER_TRUNCATED_WRONG_VALUE);
+ }
return decimal_value;
}
@@ -7078,89 +6857,6 @@ uchar *Field_varstring::pack(uchar *to, const uchar *from, uint max_length)
}
-uchar *
-Field_varstring::pack_key(uchar *to, const uchar *key, uint max_length)
-{
- uint length= length_bytes == 1 ? (uint) *key : uint2korr(key);
- uint local_char_length= ((field_charset->mbmaxlen > 1) ?
- max_length/field_charset->mbmaxlen : max_length);
- key+= length_bytes;
- if (length > local_char_length)
- {
- local_char_length= my_charpos(field_charset, key, key+length,
- local_char_length);
- set_if_smaller(length, local_char_length);
- }
- *to++= (char) (length & 255);
- if (max_length > 255)
- *to++= (char) (length >> 8);
- if (length)
- memcpy(to, key, length);
- return to+length;
-}
-
-
-/**
- Unpack a key into a record buffer.
-
- A VARCHAR key has a maximum size of 64K-1.
- In its packed form, the length field is one or two bytes long,
- depending on 'max_length'.
-
- @param to Pointer into the record buffer.
- @param key Pointer to the packed key.
- @param max_length Key length limit from key description.
-
- @return
- Pointer to end of 'key' (To the next key part if multi-segment key)
-*/
-
-#ifdef NOT_USED
-const uchar *
-Field_varstring::unpack_key(uchar *to, const uchar *key, uint max_length)
-{
- /* get length of the blob key */
- uint32 length= *key++;
- if (max_length > 255)
- length+= (*key++) << 8;
-
- /* put the length into the record buffer */
- if (length_bytes == 1)
- *ptr= (uchar) length;
- else
- int2store(ptr, length);
- memcpy(ptr + length_bytes, key, length);
- return key + length;
-}
-#endif
-
-/**
- Create a packed key that will be used for storage in the index tree.
-
- @param to Store packed key segment here
- @param from Key segment (as given to index_read())
- @param max_length Max length of key
-
- @return
- end of key storage
-*/
-
-uchar * Field_varstring::pack_key_from_key_image(uchar *to, const uchar *from,
- uint max_length)
-{
- /* Key length is always stored as 2 bytes */
- uint length= uint2korr(from);
- if (length > max_length)
- length= max_length;
- *to++= (char) (length & 255);
- if (max_length > 255)
- *to++= (char) (length >> 8);
- if (length)
- memcpy(to, from+HA_KEY_BLOB_LENGTH, length);
- return to+length;
-}
-
-
/**
Unpack a varstring field from row data.
@@ -7210,59 +6906,6 @@ Field_varstring::unpack(uchar *to, const uchar *from, const uchar *from_end,
}
-int Field_varstring::pack_cmp(const uchar *a, const uchar *b,
- uint key_length_arg,
- bool insert_or_update)
-{
- uint a_length, b_length;
- if (key_length_arg > 255)
- {
- a_length=uint2korr(a); a+= 2;
- b_length=uint2korr(b); b+= 2;
- }
- else
- {
- a_length= (uint) *a++;
- b_length= (uint) *b++;
- }
- return field_charset->coll->strnncollsp(field_charset,
- a, a_length,
- b, b_length,
- insert_or_update);
-}
-
-
-int Field_varstring::pack_cmp(const uchar *b, uint key_length_arg,
- bool insert_or_update)
-{
- uchar *a= ptr+ length_bytes;
- uint a_length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
- uint b_length;
- uint local_char_length= ((field_charset->mbmaxlen > 1) ?
- key_length_arg / field_charset->mbmaxlen :
- key_length_arg);
-
- if (key_length_arg > 255)
- {
- b_length=uint2korr(b); b+= HA_KEY_BLOB_LENGTH;
- }
- else
- b_length= (uint) *b++;
-
- if (a_length > local_char_length)
- {
- local_char_length= my_charpos(field_charset, a, a+a_length,
- local_char_length);
- set_if_smaller(a_length, local_char_length);
- }
-
- return field_charset->coll->strnncollsp(field_charset,
- a, a_length,
- b, b_length,
- insert_or_update);
-}
-
-
uint Field_varstring::packed_col_length(const uchar *data_ptr, uint length)
{
if (length > 255)
@@ -7330,7 +6973,7 @@ int Field_varstring::cmp_binary(const uchar *a_ptr, const uchar *b_ptr,
}
-Field *Field_varstring::new_field(MEM_ROOT *root, struct st_table *new_table,
+Field *Field_varstring::new_field(MEM_ROOT *root, TABLE *new_table,
bool keep_type)
{
Field_varstring *res= (Field_varstring*) Field::new_field(root, new_table,
@@ -7342,7 +6985,7 @@ Field *Field_varstring::new_field(MEM_ROOT *root, struct st_table *new_table,
Field *Field_varstring::new_key_field(MEM_ROOT *root,
- struct st_table *new_table,
+ TABLE *new_table,
uchar *new_ptr, uchar *new_null_ptr,
uint new_null_bit)
{
@@ -7406,6 +7049,7 @@ Field_blob::Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
cs),
packlength(blob_pack_length)
{
+ DBUG_ASSERT(blob_pack_length <= 4); // Only pack lengths 1-4 supported currently
flags|= BLOB_FLAG;
share->blob_fields++;
/* TODO: why do not fill table->s->blob_field array here? */
@@ -7452,8 +7096,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
If content of the 'from'-address is cached in the 'value'-object
it is possible that the content needs a character conversion.
*/
- uint32 dummy_offset;
- if (!String::needs_conversion(length, cs, field_charset, &dummy_offset))
+ if (!String::needs_conversion_on_storage(length, cs, field_charset))
{
Field_blob::store_length(length);
bmove(ptr + packlength, &from, sizeof(char*));
@@ -7512,7 +7155,7 @@ oom_error:
int Field_blob::store(double nr)
{
CHARSET_INFO *cs=charset();
- value.set_real(nr, 2, cs);
+ value.set_real(nr, NOT_FIXED_DEC, cs);
return Field_blob::store(value.ptr(),(uint) value.length(), cs);
}
@@ -7533,7 +7176,7 @@ double Field_blob::val_real(void)
uint32 length;
CHARSET_INFO *cs;
- memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
+ memcpy(&blob, ptr+packlength, sizeof(char*));
if (!blob)
return 0.0;
length= get_length(ptr);
@@ -7547,7 +7190,7 @@ longlong Field_blob::val_int(void)
ASSERT_COLUMN_MARKED_FOR_READ;
int not_used;
char *blob;
- memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
+ memcpy(&blob, ptr+packlength, sizeof(char*));
if (!blob)
return 0;
uint32 length=get_length(ptr);
@@ -7559,7 +7202,7 @@ String *Field_blob::val_str(String *val_buffer __attribute__((unused)),
{
ASSERT_COLUMN_MARKED_FOR_READ;
char *blob;
- memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
+ memcpy(&blob, ptr+packlength, sizeof(char*));
if (!blob)
val_ptr->set("",0,charset()); // A bit safer than ->length(0)
else
@@ -7573,7 +7216,7 @@ my_decimal *Field_blob::val_decimal(my_decimal *decimal_value)
ASSERT_COLUMN_MARKED_FOR_READ;
const char *blob;
size_t length;
- memcpy_fixed(&blob, ptr+packlength, sizeof(const uchar*));
+ memcpy(&blob, ptr+packlength, sizeof(const uchar*));
if (!blob)
{
blob= "";
@@ -7601,8 +7244,8 @@ int Field_blob::cmp_max(const uchar *a_ptr, const uchar *b_ptr,
uint max_length)
{
uchar *blob1,*blob2;
- memcpy_fixed(&blob1,a_ptr+packlength,sizeof(char*));
- memcpy_fixed(&blob2,b_ptr+packlength,sizeof(char*));
+ memcpy(&blob1, a_ptr+packlength, sizeof(char*));
+ memcpy(&blob2, b_ptr+packlength, sizeof(char*));
uint a_len= get_length(a_ptr), b_len= get_length(b_ptr);
set_if_smaller(a_len, max_length);
set_if_smaller(b_len, max_length);
@@ -7616,8 +7259,8 @@ int Field_blob::cmp_binary(const uchar *a_ptr, const uchar *b_ptr,
char *a,*b;
uint diff;
uint32 a_length,b_length;
- memcpy_fixed(&a,a_ptr+packlength,sizeof(char*));
- memcpy_fixed(&b,b_ptr+packlength,sizeof(char*));
+ memcpy(&a, a_ptr+packlength, sizeof(char*));
+ memcpy(&b, b_ptr+packlength, sizeof(char*));
a_length=get_length(a_ptr);
if (a_length > max_length)
a_length=max_length;
@@ -7698,7 +7341,7 @@ int Field_blob::key_cmp(const uchar *key_ptr, uint max_key_length)
{
uchar *blob1;
uint blob_length=get_length(ptr);
- memcpy_fixed(&blob1,ptr+packlength,sizeof(char*));
+ memcpy(&blob1, ptr+packlength, sizeof(char*));
CHARSET_INFO *cs= charset();
uint local_char_length= max_key_length / cs->mbmaxlen;
local_char_length= my_charpos(cs, blob1, blob1+blob_length,
@@ -7728,8 +7371,10 @@ int Field_blob::key_cmp(const uchar *a,const uchar *b)
*/
int Field_blob::do_save_field_metadata(uchar *metadata_ptr)
{
+ DBUG_ENTER("Field_blob::do_save_field_metadata");
*metadata_ptr= pack_length_no_ptr();
- return 1;
+ DBUG_PRINT("debug", ("metadata: %u (pack_length_no_ptr)", *metadata_ptr));
+ DBUG_RETURN(1);
}
@@ -7761,7 +7406,7 @@ void Field_blob::sort_string(uchar *to,uint length)
store_bigendian(blob_length, pos, packlength);
}
- memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
+ memcpy(&blob, ptr+packlength, sizeof(char*));
blob_length=my_strnxfrm(field_charset,
to, length, blob, blob_length);
@@ -7791,10 +7436,6 @@ void Field_blob::sql_type(String &res) const
uchar *Field_blob::pack(uchar *to, const uchar *from, uint max_length)
{
- DBUG_ENTER("Field_blob::pack");
- DBUG_PRINT("enter", ("to: 0x%lx; from: 0x%lx; max_length: %u",
- (ulong) to, (ulong) from, max_length));
- DBUG_DUMP("record", from, table->s->reclength);
uchar *save= ptr;
ptr= (uchar*) from;
uint32 length=get_length(); // Length of from string
@@ -7815,8 +7456,7 @@ uchar *Field_blob::pack(uchar *to, const uchar *from, uint max_length)
memcpy(to+packlength, from,length);
}
ptr=save; // Restore org row pointer
- DBUG_DUMP("packed", to, packlength + length);
- DBUG_RETURN(to+packlength+length);
+ return to+packlength+length;
}
@@ -7858,136 +7498,6 @@ const uchar *Field_blob::unpack(uchar *to, const uchar *from,
DBUG_RETURN(from + master_packlength + length);
}
-/* Keys for blobs are like keys on varchars */
-
-int Field_blob::pack_cmp(const uchar *a, const uchar *b, uint key_length_arg,
- bool insert_or_update)
-{
- uint a_length, b_length;
- if (key_length_arg > 255)
- {
- a_length=uint2korr(a); a+=2;
- b_length=uint2korr(b); b+=2;
- }
- else
- {
- a_length= (uint) *a++;
- b_length= (uint) *b++;
- }
- return field_charset->coll->strnncollsp(field_charset,
- a, a_length,
- b, b_length,
- insert_or_update);
-}
-
-
-int Field_blob::pack_cmp(const uchar *b, uint key_length_arg,
- bool insert_or_update)
-{
- uchar *a;
- uint a_length, b_length;
- memcpy_fixed(&a,ptr+packlength,sizeof(char*));
- if (!a)
- return key_length_arg > 0 ? -1 : 0;
-
- a_length= get_length(ptr);
- if (key_length_arg > 255)
- {
- b_length= uint2korr(b); b+=2;
- }
- else
- b_length= (uint) *b++;
- return field_charset->coll->strnncollsp(field_charset,
- a, a_length,
- b, b_length,
- insert_or_update);
-}
-
-/** Create a packed key that will be used for storage from a MySQL row. */
-
-uchar *
-Field_blob::pack_key(uchar *to, const uchar *from, uint max_length)
-{
- uchar *save= ptr;
- ptr= (uchar*) from;
- uint32 length=get_length(); // Length of from string
- uint local_char_length= ((field_charset->mbmaxlen > 1) ?
- max_length/field_charset->mbmaxlen : max_length);
- if (length)
- get_ptr((uchar**) &from);
- if (length > local_char_length)
- local_char_length= my_charpos(field_charset, from, from+length,
- local_char_length);
- set_if_smaller(length, local_char_length);
- *to++= (uchar) length;
- if (max_length > 255) // 2 byte length
- *to++= (uchar) (length >> 8);
- memcpy(to, from, length);
- ptr=save; // Restore org row pointer
- return to+length;
-}
-
-
-/**
- Unpack a blob key into a record buffer.
-
- A blob key has a maximum size of 64K-1.
- In its packed form, the length field is one or two bytes long,
- depending on 'max_length'.
- Depending on the maximum length of a blob, its length field is
- put into 1 to 4 bytes. This is a property of the blob object,
- described by 'packlength'.
- Blobs are internally stored apart from the record buffer, which
- contains a pointer to the blob buffer.
-
-
- @param to Pointer into the record buffer.
- @param from Pointer to the packed key.
- @param max_length Key length limit from key description.
-
- @return
- Pointer into 'from' past the last byte copied from packed key.
-*/
-
-#ifdef NOT_USED
-const uchar *
-Field_blob::unpack_key(uchar *to, const uchar *from, uint max_length)
-{
- /* get length of the blob key */
- uint32 length= *from++;
- if (max_length > 255)
- length+= *from++ << 8;
-
- /* put the length into the record buffer */
- store_length(to, packlength, length);
-
- /* put the address of the blob buffer or NULL */
- if (length)
- memcpy_fixed(to + packlength, &from, sizeof(from));
- else
- bzero(to + packlength, sizeof(from));
-
- /* point to first byte of next field in 'from' */
- return from + length;
-}
-#endif
-
-/** Create a packed key that will be used for storage from a MySQL key. */
-
-uchar *Field_blob::pack_key_from_key_image(uchar *to, const uchar *from,
- uint max_length)
-{
- uint length=uint2korr(from);
- if (length > max_length)
- length=max_length;
- *to++= (char) (length & 255);
- if (max_length > 255)
- *to++= (char) (length >> 8);
- if (length)
- memcpy(to, from+HA_KEY_BLOB_LENGTH, length);
- return to+length;
-}
-
uint Field_blob::packed_col_length(const uchar *data_ptr, uint length)
{
@@ -8088,6 +7598,19 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs)
if (wkb_type < (uint32) Geometry::wkb_point ||
wkb_type > (uint32) Geometry::wkb_last)
goto err;
+
+ if (geom_type != Field::GEOM_GEOMETRY &&
+ geom_type != Field::GEOM_GEOMETRYCOLLECTION &&
+ (uint32) geom_type != wkb_type)
+ {
+ my_printf_error(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
+ ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), MYF(0),
+ Geometry::ci_collection[geom_type]->m_name.str,
+ Geometry::ci_collection[wkb_type]->m_name.str, field_name,
+ (ulong) table->in_use->warning_info->current_row_for_warning());
+ goto err_exit;
+ }
+
Field_blob::store_length(length);
if (table->copy_blobs || length <= MAX_FIELD_WIDTH)
{ // Must make a copy
@@ -8099,9 +7622,10 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs)
return 0;
err:
- bzero(ptr, Field_blob::pack_length());
my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
ER(ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
+err_exit:
+ bzero(ptr, Field_blob::pack_length());
return -1;
}
@@ -8140,12 +7664,11 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs)
{
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int err= 0;
- uint32 not_used;
char buff[STRING_BUFFER_USUAL_SIZE];
String tmpstr(buff,sizeof(buff), &my_charset_bin);
/* Convert character set if necessary */
- if (String::needs_conversion(length, cs, field_charset, &not_used))
+ if (String::needs_conversion_on_storage(length, cs, field_charset))
{
uint dummy_errors;
tmpstr.copy(from, length, cs, field_charset, &dummy_errors);
@@ -8295,7 +7818,7 @@ void Field_enum::sql_type(String &res) const
}
-Field *Field_enum::new_field(MEM_ROOT *root, struct st_table *new_table,
+Field *Field_enum::new_field(MEM_ROOT *root, TABLE *new_table,
bool keep_type)
{
Field_enum *res= (Field_enum*) Field::new_field(root, new_table, keep_type);
@@ -8322,12 +7845,11 @@ int Field_set::store(const char *from,uint length,CHARSET_INFO *cs)
int err= 0;
char *not_used;
uint not_used2;
- uint32 not_used_offset;
char buff[STRING_BUFFER_USUAL_SIZE];
String tmpstr(buff,sizeof(buff), &my_charset_bin);
/* Convert character set if necessary */
- if (String::needs_conversion(length, cs, field_charset, &not_used_offset))
+ if (String::needs_conversion_on_storage(length, cs, field_charset))
{
uint dummy_errors;
tmpstr.copy(from, length, cs, field_charset, &dummy_errors);
@@ -8383,8 +7905,19 @@ String *Field_set::val_str(String *val_buffer,
ulonglong tmp=(ulonglong) Field_enum::val_int();
uint bitnr=0;
- val_buffer->length(0);
+ if (tmp == 0)
+ {
+ /*
+ Some callers expect *val_buffer to contain the result,
+ so we assign to it, rather than doing 'return &empty_set_string.
+ */
+ *val_buffer= empty_set_string;
+ return val_buffer;
+ }
+
val_buffer->set_charset(field_charset);
+ val_buffer->length(0);
+
while (tmp && bitnr < (uint) typelib->count)
{
if (tmp & 1)
@@ -8521,6 +8054,24 @@ uint Field_enum::is_equal(Create_field *new_field)
}
+uchar *Field_enum::pack(uchar *to, const uchar *from, uint max_length)
+{
+ DBUG_ENTER("Field_enum::pack");
+ DBUG_PRINT("debug", ("packlength: %d", packlength));
+ DBUG_DUMP("from", from, packlength);
+ DBUG_RETURN(pack_int(to, from, packlength));
+}
+
+const uchar *Field_enum::unpack(uchar *to, const uchar *from,
+ const uchar *from_end, uint param_data)
+{
+ DBUG_ENTER("Field_enum::unpack");
+ DBUG_PRINT("debug", ("packlength: %d", packlength));
+ DBUG_DUMP("from", from, packlength);
+ DBUG_RETURN(unpack_int(to, from, from_end, packlength));
+}
+
+
/**
@return
returns 1 if the fields are equally defined
@@ -8593,6 +8144,9 @@ Field_bit::Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
bit_ptr(bit_ptr_arg), bit_ofs(bit_ofs_arg), bit_len(len_arg & 7),
bytes_in_rec(len_arg / 8)
{
+ DBUG_ENTER("Field_bit::Field_bit");
+ DBUG_PRINT("enter", ("ptr_arg: %p, null_ptr_arg: %p, len_arg: %u, bit_len: %u, bytes_in_rec: %u",
+ ptr_arg, null_ptr_arg, len_arg, bit_len, bytes_in_rec));
flags|= UNSIGNED_FLAG;
/*
Ensure that Field::eq() can distinguish between two different bit fields.
@@ -8600,6 +8154,7 @@ Field_bit::Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
*/
if (!null_ptr_arg)
null_bit= bit_ofs_arg;
+ DBUG_VOID_RETURN;
}
@@ -8648,7 +8203,7 @@ Field_bit::do_last_null_byte() const
Field *Field_bit::new_key_field(MEM_ROOT *root,
- struct st_table *new_table,
+ TABLE *new_table,
uchar *new_ptr, uchar *new_null_ptr,
uint new_null_bit)
{
@@ -8690,7 +8245,7 @@ int Field_bit::store(const char *from, uint length, CHARSET_INFO *cs)
set_rec_bits((1 << bit_len) - 1, bit_ptr, bit_ofs, bit_len);
memset(ptr, 0xff, bytes_in_rec);
if (table->in_use->really_abort_on_warning())
- set_warning(MYSQL_ERROR::WARN_LEVEL_ERROR, ER_DATA_TOO_LONG, 1);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1);
else
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
return 1;
@@ -8785,7 +8340,7 @@ String *Field_bit::val_str(String *val_buffer,
mi_int8store(buff,bits);
val_buffer->alloc(length);
- memcpy_fixed((char*) val_buffer->ptr(), buff+8-length, length);
+ memcpy((char *) val_buffer->ptr(), buff+8-length, length);
val_buffer->length(length);
val_buffer->set_charset(&my_charset_bin);
return val_buffer;
@@ -8885,6 +8440,9 @@ uint Field_bit::get_key_image(uchar *buff, uint length, imagetype type_arg)
*/
int Field_bit::do_save_field_metadata(uchar *metadata_ptr)
{
+ DBUG_ENTER("Field_bit::do_save_field_metadata");
+ DBUG_PRINT("debug", ("bit_len: %d, bytes_in_rec: %d",
+ bit_len, bytes_in_rec));
/*
Since this class and Field_bit_as_char have different ideas of
what should be stored here, we compute the values of the metadata
@@ -8892,7 +8450,7 @@ int Field_bit::do_save_field_metadata(uchar *metadata_ptr)
*/
metadata_ptr[0]= field_length % 8;
metadata_ptr[1]= field_length / 8;
- return 2;
+ DBUG_RETURN(2);
}
@@ -8917,26 +8475,19 @@ uint Field_bit::pack_length_from_metadata(uint field_metadata)
}
-/**
- Check to see if field size is compatible with destination.
-
- This method is used in row-based replication to verify that the slave's
- field size is less than or equal to the master's field size. The
- encoded field metadata (from the master or source) is decoded and compared
- to the size of this field (the slave or destination).
-
- @param field_metadata Encoded size in field metadata
-
- @retval 0 if this field's size is < the source field's size
- @retval 1 if this field's size is >= the source field's size
-*/
-int Field_bit::compatible_field_size(uint field_metadata,
- const Relay_log_info * __attribute__((unused)),
- uint16 mflags)
+bool
+Field_bit::compatible_field_size(uint field_metadata,
+ Relay_log_info * __attribute__((unused)),
+ uint16 mflags,
+ int *order_var)
{
- uint from_bit_len= 8 * (field_metadata >> 8) + (field_metadata & 0xff);
+ DBUG_ENTER("Field_bit::compatible_field_size");
+ DBUG_ASSERT((field_metadata >> 16) == 0);
+ uint from_bit_len=
+ 8 * (field_metadata >> 8) + (field_metadata & 0xff);
uint to_bit_len= max_display_length();
-
+ DBUG_PRINT("debug", ("from_bit_len: %u, to_bit_len: %u",
+ from_bit_len, to_bit_len));
/*
If the bit length exact flag is clear, we are dealing with an old
master, so we allow some less strict behaviour if replicating by
@@ -8950,7 +8501,8 @@ int Field_bit::compatible_field_size(uint field_metadata,
to_bit_len= (to_bit_len + 7) / 8;
}
- return from_bit_len <= to_bit_len;
+ *order_var= compare(from_bit_len, to_bit_len);
+ DBUG_RETURN(TRUE);
}
@@ -9015,8 +8567,15 @@ const uchar *
Field_bit::unpack(uchar *to, const uchar *from, const uchar *from_end,
uint param_data)
{
+ DBUG_ENTER("Field_bit::unpack");
+ DBUG_PRINT("enter", ("to: %p, from: %p, param_data: 0x%x",
+ to, from, param_data));
+ DBUG_PRINT("debug", ("bit_ptr: %p, bit_len: %u, bit_ofs: %u",
+ bit_ptr, bit_len, bit_ofs));
uint const from_len= (param_data >> 8U) & 0x00ff;
uint const from_bit_len= param_data & 0x00ff;
+ DBUG_PRINT("debug", ("from_len: %u, from_bit_len: %u",
+ from_len, from_bit_len));
/*
If the parameter data is zero (i.e., undefined), or if the master
and slave have the same sizes, then use the old unpack() method.
@@ -9040,7 +8599,7 @@ Field_bit::unpack(uchar *to, const uchar *from, const uchar *from_end,
from++;
}
memcpy(to, from, bytes_in_rec);
- return from + bytes_in_rec;
+ DBUG_RETURN(from + bytes_in_rec);
}
/*
@@ -9072,7 +8631,7 @@ Field_bit::unpack(uchar *to, const uchar *from, const uchar *from_end,
bitmap_set_bit(table->write_set,field_index);
store(value, new_len, system_charset_info);
my_afree(value);
- return from + len;
+ DBUG_RETURN(from + len);
}
@@ -9121,7 +8680,7 @@ int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs)
if (bits)
*ptr&= ((1 << bits) - 1); /* set first uchar */
if (table->in_use->really_abort_on_warning())
- set_warning(MYSQL_ERROR::WARN_LEVEL_ERROR, ER_DATA_TOO_LONG, 1);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1);
else
set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
return 1;
@@ -9202,8 +8761,11 @@ void Create_field::create_length_to_internal_length(void)
*/
void Create_field::init_for_tmp_table(enum_field_types sql_type_arg,
uint32 length_arg, uint32 decimals_arg,
- bool maybe_null, bool is_unsigned)
+ bool maybe_null, bool is_unsigned,
+ uint pack_length_arg)
{
+ DBUG_ENTER("Create_field::init_for_tmp_table");
+
field_name= "";
sql_type= sql_type_arg;
char_length= length= length_arg;;
@@ -9211,12 +8773,97 @@ void Create_field::init_for_tmp_table(enum_field_types sql_type_arg,
interval= 0;
charset= &my_charset_bin;
geom_type= Field::GEOM_GEOMETRY;
- pack_flag= (FIELDFLAG_NUMBER |
- ((decimals_arg & FIELDFLAG_MAX_DEC) << FIELDFLAG_DEC_SHIFT) |
- (maybe_null ? FIELDFLAG_MAYBE_NULL : 0) |
- (is_unsigned ? 0 : FIELDFLAG_DECIMAL));
+
+ DBUG_PRINT("enter", ("sql_type: %d, length: %u, pack_length: %u",
+ sql_type_arg, length_arg, pack_length_arg));
+
+ /*
+ These pack flags are crafted to get it correctly through the
+ branches of make_field().
+ */
+ switch (sql_type_arg)
+ {
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_SET:
+ pack_flag= 0;
+ break;
+
+ case MYSQL_TYPE_GEOMETRY:
+ pack_flag= FIELDFLAG_GEOM;
+ break;
+
+ case MYSQL_TYPE_ENUM:
+ pack_flag= FIELDFLAG_INTERVAL;
+ break;
+
+ case MYSQL_TYPE_NEWDECIMAL:
+ DBUG_ASSERT(decimals_arg <= DECIMAL_MAX_SCALE);
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ pack_flag= FIELDFLAG_NUMBER |
+ (decimals_arg & FIELDFLAG_MAX_DEC) << FIELDFLAG_DEC_SHIFT;
+ break;
+
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ pack_flag= FIELDFLAG_BLOB;
+ break;
+
+ case MYSQL_TYPE_BIT:
+ pack_flag= FIELDFLAG_NUMBER | FIELDFLAG_TREAT_BIT_AS_CHAR;
+ break;
+
+ default:
+ pack_flag= FIELDFLAG_NUMBER;
+ break;
+ }
+
+ /*
+ Set the pack flag correctly for the blob-like types. This sets the
+ packtype to something that make_field can use. If the pack type is
+ not set correctly, the packlength will be reeeeally wierd (like
+ 129 or so).
+ */
+ switch (sql_type_arg)
+ {
+ 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:
+ // If you are going to use the above types, you have to pass a
+ // pack_length as parameter. Assert that is really done.
+ DBUG_ASSERT(pack_length_arg != ~0U);
+ pack_flag|= pack_length_to_packflag(pack_length_arg);
+ break;
+ default:
+ /* Nothing */
+ break;
+ }
+
+ pack_flag|=
+ (maybe_null ? FIELDFLAG_MAYBE_NULL : 0) |
+ (is_unsigned ? 0 : FIELDFLAG_DECIMAL);
+
+ DBUG_PRINT("debug", ("pack_flag: %s%s%s%s%s%s, pack_type: %d",
+ FLAGSTR(pack_flag, FIELDFLAG_BINARY),
+ FLAGSTR(pack_flag, FIELDFLAG_NUMBER),
+ FLAGSTR(pack_flag, FIELDFLAG_INTERVAL),
+ FLAGSTR(pack_flag, FIELDFLAG_GEOM),
+ FLAGSTR(pack_flag, FIELDFLAG_BLOB),
+ FLAGSTR(pack_flag, FIELDFLAG_DECIMAL),
+ f_packtype(pack_flag)));
vcol_info= 0;
stored_in_db= TRUE;
+
+ DBUG_VOID_RETURN;
}
@@ -9630,8 +9277,8 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
case MYSQL_TYPE_TIME:
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP:
- charset= &my_charset_bin;
- flags|= BINCMP_FLAG;
+ charset= &my_charset_numeric;
+ flags|= BINARY_FLAG;
default: break;
}
@@ -9715,6 +9362,7 @@ uint pack_length_to_packflag(uint type)
return 0; // This shouldn't happen
}
+
Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length,
uchar *null_pos, uchar null_bit,
uint pack_flag,
@@ -9754,10 +9402,18 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length,
case MYSQL_TYPE_TIME:
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP:
- field_charset= &my_charset_bin;
+ field_charset= &my_charset_numeric;
default: break;
}
+ DBUG_PRINT("debug", ("field_type: %d, field_length: %u, interval: %p, pack_flag: %s%s%s%s%s",
+ field_type, field_length, interval,
+ FLAGSTR(pack_flag, FIELDFLAG_BINARY),
+ 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))
@@ -9784,9 +9440,12 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length,
#ifdef HAVE_SPATIAL
if (f_is_geom(pack_flag))
+ {
+ status_var_increment(current_thd->status_var.feature_gis);
return new Field_geom(ptr,null_pos,null_bit,
unireg_check, field_name, share,
pack_length, geom_type);
+ }
#endif
if (f_is_blob(pack_flag))
return new Field_blob(ptr,null_pos,null_bit,
@@ -10006,6 +9665,39 @@ Create_field::Create_field(Field *old_field,Field *orig_field)
/**
+ maximum possible character length for blob.
+
+ This method is used in Item_field::set_field to calculate
+ max_length for Item.
+
+ For example:
+ CREATE TABLE t2 SELECT CONCAT(tinyblob_utf8_column) FROM t1;
+ must create a "VARCHAR(255) CHARACTER SET utf8" column.
+
+ @return
+ length
+*/
+
+uint32 Field_blob::char_length()
+{
+ switch (packlength)
+ {
+ case 1:
+ return 255;
+ case 2:
+ return 65535;
+ case 3:
+ return 16777215;
+ case 4:
+ return (uint32) 4294967295U;
+ default:
+ DBUG_ASSERT(0); // we should never go here
+ return 0;
+ }
+}
+
+
+/**
Makes a clone of this object for ALTER/CREATE TABLE
@param mem_root MEM_ROOT where to clone the field
@@ -10061,11 +9753,6 @@ uint32 Field_blob::max_display_length()
if count_cuted_fields == CHECK_FIELD_IGNORE then we ignore notes.
This allows us to avoid notes in optimisation, like convert_constant_item().
-
- @retval
- 1 if count_cuted_fields == CHECK_FIELD_IGNORE and error level is not NOTE
- @retval
- 0 otherwise
*/
void
@@ -10081,7 +9768,7 @@ Field::set_warning(MYSQL_ERROR::enum_warning_level level, uint code,
{
thd->cuted_fields+= cuted_increment;
push_warning_printf(thd, level, code, ER(code), field_name,
- thd->row_count);
+ thd->warning_info->current_row_for_warning());
}
}
@@ -10101,11 +9788,11 @@ Field::set_warning(MYSQL_ERROR::enum_warning_level level, uint code,
thread.
See also bug#2336
-*/
+*/
void Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level,
- uint code, const Lazy_string *str,
+ uint code, const ErrConv *str,
timestamp_type ts_type, int cuted_increment)
{
THD *thd= table->in_use;
diff --git a/sql/field.h b/sql/field.h
index e7e7545aacf..c2a26f2c47d 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -1,5 +1,7 @@
+#ifndef FIELD_INCLUDED
+#define FIELD_INCLUDED
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2008, 2014, Monty Program Ab
+ Copyright (c) 2008, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,10 +14,7 @@
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_compare.h" /* for clr_rec_bits */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
Because of the function new_field() all field classes that have static
@@ -26,13 +25,47 @@
#pragma interface /* gcc class implementation */
#endif
-#define NOT_FIXED_DEC 31
-const uint32 max_field_size= (uint32) 4294967295U;
+#include "mysqld.h" /* system_charset_info */
+#include "table.h" /* TABLE */
+#include "sql_string.h" /* String */
+#include "my_decimal.h" /* my_decimal */
+#include "sql_error.h" /* MYSQL_ERROR */
class Send_field;
class Protocol;
class Create_field;
class Relay_log_info;
+class Field;
+
+enum enum_check_fields
+{
+ CHECK_FIELD_IGNORE,
+ CHECK_FIELD_WARN,
+ CHECK_FIELD_ERROR_FOR_NULL
+};
+
+
+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_HEADER_SIZE(b) (3 + test(b))
+
struct ha_field_option_struct;
struct st_cache_field;
@@ -52,6 +85,21 @@ inline uint get_set_pack_length(int elements)
return len > 4 ? 8 : len;
}
+
+static inline enum enum_mysql_timestamp_type
+mysql_type_to_time_type(enum enum_field_types mysql_type)
+{
+ switch(mysql_type) {
+ case MYSQL_TYPE_TIME: return MYSQL_TIMESTAMP_TIME;
+ case MYSQL_TYPE_TIMESTAMP:
+ 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.
@@ -172,8 +220,8 @@ public:
Note that you can use table->in_use as replacement for current_thd member
only inside of val_*() and store() members (e.g. you can't use it in cons)
*/
- struct st_table *table; // Pointer for table
- struct st_table *orig_table; // Pointer to original table
+ TABLE *table; // Pointer for table
+ TABLE *orig_table; // Pointer to original table
const char * const *table_name;
const char *field_name;
/** reference to the list of options or NULL */
@@ -294,22 +342,13 @@ public:
table, which is located on disk).
*/
virtual uint32 pack_length_in_rec() const { return pack_length(); }
- virtual int compatible_field_size(uint field_metadata,
- const Relay_log_info *, uint16 mflags);
+ virtual bool compatible_field_size(uint metadata, Relay_log_info *rli,
+ uint16 mflags, int *order);
virtual uint pack_length_from_metadata(uint field_metadata)
- { return field_metadata; }
- /*
- This method is used to return the size of the data in a row-based
- replication row record. The default implementation of returning 0 is
- designed to allow fields that do not use metadata to return TRUE (1)
- from compatible_field_size() which uses this function in the comparison.
- The default value for field metadata for fields that do not have
- metadata is 0. Thus, 0 == 0 means the fields are compatible in size.
-
- Note: While most classes that override this method return pack_length(),
- the classes Field_string, Field_varstring, and Field_blob return
- field_length + 1, field_length, and pack_length_no_ptr() respectfully.
- */
+ {
+ DBUG_ENTER("Field::pack_length_from_metadata");
+ DBUG_RETURN(field_metadata);
+ }
virtual uint row_pack_length() { return 0; }
virtual int save_field_metadata(uchar *first_byte)
{ return do_save_field_metadata(first_byte); }
@@ -427,12 +466,12 @@ public:
virtual void sort_string(uchar *buff,uint length)=0;
virtual bool optimize_range(uint idx, uint part);
virtual void free() {}
- virtual Field *new_field(MEM_ROOT *root, struct st_table *new_table,
+ virtual Field *new_field(MEM_ROOT *root, TABLE *new_table,
bool keep_type);
- virtual Field *new_key_field(MEM_ROOT *root, struct st_table *new_table,
+ virtual Field *new_key_field(MEM_ROOT *root, TABLE *new_table,
uchar *new_ptr, uchar *new_null_ptr,
uint new_null_bit);
- Field *clone(MEM_ROOT *mem_root, struct st_table *new_table);
+ Field *clone(MEM_ROOT *mem_root, TABLE *new_table);
inline void move_field(uchar *ptr_arg,uchar *null_ptr_arg,uchar null_bit_arg)
{
ptr=ptr_arg; null_ptr=null_ptr_arg; null_bit=null_bit_arg;
@@ -522,51 +561,35 @@ public:
virtual const uchar *unpack(uchar* to, const uchar *from,
const uchar *from_end, uint param_data=0);
- virtual uchar *pack_key(uchar* to, const uchar *from, uint max_length)
- {
- return pack(to, from, max_length);
- }
- virtual uchar *pack_key_from_key_image(uchar* to, const uchar *from, uint max_length)
- {
- return pack(to, from, max_length);
- }
-#ifdef NOT_USED
- virtual const uchar *unpack_key(uchar* to, const uchar *from, uint max_length)
- {
- return unpack(to, from, from + max_length+2, max_length);
- }
-#endif
+
virtual uint packed_col_length(const uchar *to, uint length)
{ return length;}
virtual uint max_packed_col_length(uint max_length)
{ return max_length;}
- virtual int pack_cmp(const uchar *a,const uchar *b, uint key_length_arg,
- bool insert_or_update)
- { return cmp(a,b); }
- virtual int pack_cmp(const uchar *b, uint key_length_arg,
- bool insert_or_update)
- { return cmp(ptr,b); }
uint offset(uchar *record)
{
return (uint) (ptr - record);
}
void copy_from_tmp(int offset);
uint fill_cache_field(struct st_cache_field *copy);
- virtual bool get_date(MYSQL_TIME *ltime,uint fuzzydate);
+ virtual bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
bool get_time(MYSQL_TIME *ltime) { return get_date(ltime, TIME_TIME_ONLY); }
virtual CHARSET_INFO *charset(void) const { return &my_charset_bin; }
+ virtual CHARSET_INFO *charset_for_protocol(void) const
+ { return binary() ? &my_charset_bin : charset(); }
virtual CHARSET_INFO *sort_charset(void) const { return charset(); }
virtual bool has_charset(void) const { return FALSE; }
virtual void set_charset(CHARSET_INFO *charset_arg) { }
virtual enum Derivation derivation(void) const
{ return DERIVATION_IMPLICIT; }
+ virtual uint repertoire(void) const { return MY_REPERTOIRE_UNICODE30; }
virtual void set_derivation(enum Derivation derivation_arg) { }
virtual int set_time() { return 1; }
void set_warning(MYSQL_ERROR::enum_warning_level, unsigned int code,
int cuted_increment);
void set_datetime_warning(MYSQL_ERROR::enum_warning_level, uint code,
- const Lazy_string *str, timestamp_type ts_type,
+ const ErrConv *str, timestamp_type ts_type,
int cuted_increment);
inline bool check_overflow(int op_result)
{
@@ -598,7 +621,7 @@ public:
longlong convert_decimal2longlong(const my_decimal *val, bool unsigned_flag,
int *err);
/* The max. number of characters */
- inline uint32 char_length() const
+ virtual uint32 char_length()
{
return field_length / charset()->mbmaxlen;
}
@@ -619,7 +642,6 @@ public:
virtual bool hash_join_is_possible() { return TRUE; }
virtual bool eq_cmp_as_binary() { return TRUE; }
- friend bool reopen_table(THD *,struct st_table *,bool);
friend int cre_myisam(char * name, register TABLE *form, uint options,
ulonglong auto_increment_value);
friend class Copy_field;
@@ -669,20 +691,31 @@ protected:
return to + size;
}
- const uchar *unpack_int(uchar* to, const uchar *from, size_t size)
+ const uchar *unpack_int(uchar* to, const uchar *from,
+ const uchar *from_end, size_t size)
{
+ if (from + size > from_end)
+ return 0;
memcpy(to, from, size);
return from + size;
}
+ uchar *pack_int16(uchar *to, const uchar *from)
+ { return pack_int(to, from, 2); }
+ const uchar *unpack_int16(uchar* to, const uchar *from, const uchar *from_end)
+ { return unpack_int(to, from, from_end, 2); }
+ uchar *pack_int24(uchar *to, const uchar *from)
+ { return pack_int(to, from, 3); }
+ const uchar *unpack_int24(uchar* to, const uchar *from, const uchar *from_end)
+ { return unpack_int(to, from, from_end, 3); }
uchar *pack_int32(uchar *to, const uchar *from)
{ return pack_int(to, from, 4); }
- const uchar *unpack_int32(uchar* to, const uchar *from)
- { return unpack_int(to, from, 4); }
+ const uchar *unpack_int32(uchar* to, const uchar *from, const uchar *from_end)
+ { return unpack_int(to, from, from_end, 4); }
uchar *pack_int64(uchar* to, const uchar *from)
{ return pack_int(to, from, 8); }
- const uchar *unpack_int64(uchar* to, const uchar *from)
- { return unpack_int(to, from, 8); }
+ const uchar *unpack_int64(uchar* to, const uchar *from, const uchar *from_end)
+ { return unpack_int(to, from, from_end, 8); }
bool field_flags_are_binary()
{
@@ -701,6 +734,9 @@ public:
const char *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; }
void prepend_zeros(String *value);
void add_zerofill_and_unsigned(String &res) const;
friend class Create_field;
@@ -711,6 +747,13 @@ public:
int store_decimal(const my_decimal *);
my_decimal *val_decimal(my_decimal *);
uint is_equal(Create_field *new_field);
+ uint row_pack_length() { return pack_length(); }
+ uint32 pack_length_from_metadata(uint field_metadata) {
+ uint32 length= pack_length();
+ DBUG_PRINT("result", ("pack_length_from_metadata(%d): %u",
+ field_metadata, length));
+ return length;
+ }
int store_time_dec(MYSQL_TIME *ltime, uint dec);
int check_int(CHARSET_INFO *cs, const char *str, int length,
const char *int_end, int error);
@@ -729,12 +772,27 @@ public:
uchar null_bit_arg, utype unireg_check_arg,
const char *field_name_arg, CHARSET_INFO *charset);
Item_result result_type () const { return STRING_RESULT; }
+ /*
+ match_collation_to_optimize_range() is to distinguish in
+ range optimizer (see opt_range.cc) between real string types:
+ CHAR, VARCHAR, TEXT
+ and the other string-alike types with result_type() == STRING_RESULT:
+ DATE, TIME, DATETIME, TIMESTAMP
+ We need it to decide whether to test if collation of the operation
+ matches collation of the field (needed only for real string types).
+ QQ: shouldn't DATE/TIME types have their own XXX_RESULT types eventually?
+ */
+ virtual bool match_collation_to_optimize_range() const=0;
uint decimals() const { return NOT_FIXED_DEC; }
int store(double nr);
int store(longlong nr, bool unsigned_val)=0;
int store_decimal(const my_decimal *);
int store(const char *to,uint length,CHARSET_INFO *cs)=0;
uint size_of() const { return sizeof(*this); }
+ uint repertoire(void) const
+ {
+ return my_charset_repertoire(field_charset);
+ }
CHARSET_INFO *charset(void) const { return field_charset; }
void set_charset(CHARSET_INFO *charset_arg) { field_charset= charset_arg; }
enum Derivation derivation(void) const { return field_derivation; }
@@ -784,7 +842,7 @@ public:
Item_result result_type () const { return REAL_RESULT; }
int store_decimal(const my_decimal *);
int store_time_dec(MYSQL_TIME *ltime, uint dec);
- bool get_date(MYSQL_TIME *ltime,uint fuzzydate);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
my_decimal *val_decimal(my_decimal *);
uint32 max_display_length() { return field_length; }
uint size_of() const { return sizeof(*this); }
@@ -868,8 +926,8 @@ public:
uint32 pack_length() const { return (uint32) bin_size; }
uint pack_length_from_metadata(uint field_metadata);
uint row_pack_length() { return pack_length(); }
- int compatible_field_size(uint field_metadata,
- const Relay_log_info *rli, uint16 mflags);
+ bool compatible_field_size(uint field_metadata, Relay_log_info *rli,
+ 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 (Item *);
@@ -953,24 +1011,11 @@ public:
uint32 max_display_length() { return 6; }
virtual uchar *pack(uchar* to, const uchar *from, uint max_length)
- {
- int16 val;
- val = sint2korr(from);
- int2store(to, val);
- return to + 2;
- }
+ { return pack_int16(to, from); }
virtual const uchar *unpack(uchar* to, const uchar *from,
const uchar *from_end, uint param_data)
- {
- int16 val;
- if (from +2 > from_end)
- return 0;
-
- val = sint2korr(from);
- int2store(to, val);
- return from + 2;
- }
+ { return unpack_int16(to, from, from_end); }
};
class Field_medium :public Field_num {
@@ -1047,9 +1092,7 @@ public:
const uchar *from_end,
uint param_data __attribute__((unused)))
{
- if (from + 4 > from_end)
- return 0;
- return unpack_int32(to, from);
+ return unpack_int32(to, from, from_end);
}
};
@@ -1098,9 +1141,7 @@ public:
const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end,
uint param_data __attribute__((unused)))
{
- if (from + 8 > from_end)
- return 0;
- return unpack_int64(to, from);
+ return unpack_int64(to, from, from_end);
}
};
@@ -1192,6 +1233,7 @@ public:
unireg_check_arg, field_name_arg, cs)
{}
enum_field_types type() const { return MYSQL_TYPE_NULL;}
+ bool match_collation_to_optimize_range() const { return FALSE; }
int store(const char *to, uint length, CHARSET_INFO *cs)
{ null[0]=1; return 0; }
int store(double nr) { null[0]=1; return 0; }
@@ -1215,7 +1257,7 @@ public:
class Field_timestamp :public Field_str {
protected:
- int store_TIME_with_warning(THD *, MYSQL_TIME *, const Lazy_string *,
+ int store_TIME_with_warning(THD *, MYSQL_TIME *, const ErrConv *,
bool, bool);
public:
Field_timestamp(uchar *ptr_arg, uint32 len_arg,
@@ -1225,8 +1267,13 @@ public:
Field_timestamp(bool maybe_null_arg, const char *field_name_arg,
CHARSET_INFO *cs);
enum_field_types type() const { return MYSQL_TYPE_TIMESTAMP;}
+ bool match_collation_to_optimize_range() const { return FALSE; }
enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; }
enum Item_result cmp_type () const { return TIME_RESULT; }
+ enum Derivation derivation(void) const { return DERIVATION_NUMERIC; }
+ uint repertoire(void) const { return MY_REPERTOIRE_NUMERIC; }
+ CHARSET_INFO *charset(void) const { return &my_charset_numeric; }
+ bool binary() const { return 1; }
int store(const char *to,uint length,CHARSET_INFO *charset);
int store(double nr);
int store(longlong nr, bool unsigned_val);
@@ -1256,7 +1303,7 @@ public:
{
int4store(ptr,timestamp);
}
- bool get_date(MYSQL_TIME *ltime,uint fuzzydate);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
timestamp_auto_set_type get_auto_set_type() const;
uchar *pack(uchar *to, const uchar *from,
uint max_length __attribute__((unused)))
@@ -1266,9 +1313,7 @@ public:
const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end,
uint param_data __attribute__((unused)))
{
- if (from + 4 > from_end)
- return 0;
- return unpack_int32(to, from);
+ return unpack_int32(to, from, from_end);
}
};
@@ -1291,9 +1336,9 @@ public:
my_time_t get_timestamp(ulong *sec_part) const;
void store_TIME(my_time_t timestamp, ulong sec_part);
int store_decimal(const my_decimal *d);
- my_decimal* val_decimal(my_decimal*);
double val_real(void);
String *val_str(String*,String *);
+ my_decimal* val_decimal(my_decimal*);
bool send_binary(Protocol *protocol);
int cmp(const uchar *,const uchar *);
void sort_string(uchar *buff,uint length);
@@ -1329,7 +1374,7 @@ public:
double val_real(void);
longlong val_int(void);
String *val_str(String*,String *);
- bool get_date(MYSQL_TIME *ltime,uint fuzzydate);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
bool send_binary(Protocol *protocol);
uint32 max_display_length() { return field_length; }
void sql_type(String &str) const;
@@ -1338,16 +1383,21 @@ public:
class Field_temporal: public Field_str {
protected:
- int store_TIME_with_warning(MYSQL_TIME *ltime, const Lazy_string *str,
+ int store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str,
int was_cut, int have_smth_to_conv);
virtual void store_TIME(MYSQL_TIME *ltime) = 0;
- virtual timestamp_type temporal_type() = 0;
public:
Field_temporal(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
const char *field_name_arg, CHARSET_INFO *charset_arg)
:Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
- field_name_arg, charset_arg) { }
+ field_name_arg, charset_arg)
+ { flags|= BINARY_FLAG; }
+ enum Derivation derivation(void) const { return DERIVATION_NUMERIC; }
+ uint repertoire(void) const { return MY_REPERTOIRE_NUMERIC; }
+ CHARSET_INFO *charset(void) const { return &my_charset_numeric; }
+ bool binary() const { return 1; }
+ bool match_collation_to_optimize_range() const { return FALSE; }
enum Item_result cmp_type () const { return TIME_RESULT; }
int store(const char *to,uint length,CHARSET_INFO *charset);
int store(double nr);
@@ -1362,15 +1412,12 @@ public:
class Field_date :public Field_temporal {
void store_TIME(MYSQL_TIME *ltime);
- timestamp_type temporal_type()
- { return MYSQL_TIMESTAMP_DATE; }
public:
Field_date(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
CHARSET_INFO *cs)
:Field_temporal(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, cs)
- {}
+ unireg_check_arg, field_name_arg, cs) {}
enum_field_types type() const { return MYSQL_TYPE_DATE;}
enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; }
int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; }
@@ -1392,16 +1439,13 @@ public:
const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end,
uint param_data __attribute__((unused)))
{
- if (from + 4 > from_end)
- return 0;
- return unpack_int32(to, from);
+ return unpack_int32(to, from, from_end);
}
};
class Field_newdate :public Field_temporal {
void store_TIME(MYSQL_TIME *ltime);
- timestamp_type temporal_type() { return MYSQL_TIMESTAMP_DATE; }
public:
Field_newdate(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
@@ -1423,14 +1467,12 @@ public:
uint32 pack_length() const { return 3; }
void sql_type(String &str) const;
bool zero_pack() const { return 1; }
- bool get_date(MYSQL_TIME *ltime,uint fuzzydate);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
};
class Field_time :public Field_temporal {
void store_TIME(MYSQL_TIME *ltime);
- timestamp_type temporal_type()
- { return MYSQL_TIMESTAMP_TIME; }
public:
Field_time(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg,
uchar null_bit_arg, enum utype unireg_check_arg,
@@ -1448,7 +1490,7 @@ public:
double val_real(void);
longlong val_int(void);
String *val_str(String*,String *);
- bool get_date(MYSQL_TIME *ltime, uint fuzzydate);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
bool send_binary(Protocol *protocol);
int cmp(const uchar *,const uchar *);
void sort_string(uchar *buff,uint length);
@@ -1481,7 +1523,7 @@ public:
double val_real(void);
String *val_str(String*,String *);
int reset(void);
- bool get_date(MYSQL_TIME *ltime, uint fuzzydate);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
bool send_binary(Protocol *protocol);
int cmp(const uchar *,const uchar *);
void sort_string(uchar *buff,uint length);
@@ -1493,8 +1535,6 @@ public:
class Field_datetime :public Field_temporal {
void store_TIME(MYSQL_TIME *ltime);
- timestamp_type temporal_type()
- { return MYSQL_TIMESTAMP_DATETIME; }
public:
Field_datetime(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg,
uchar null_bit_arg, enum utype unireg_check_arg,
@@ -1514,7 +1554,7 @@ public:
uint32 pack_length() const { return 8; }
void sql_type(String &str) const;
bool zero_pack() const { return 1; }
- bool get_date(MYSQL_TIME *ltime,uint fuzzydate);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
uchar *pack(uchar* to, const uchar *from,
uint max_length __attribute__((unused)))
{
@@ -1523,9 +1563,7 @@ public:
const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end,
uint param_data __attribute__((unused)))
{
- if (from + 8 > from_end)
- return 0;
- return unpack_int64(to, from);
+ return unpack_int64(to, from, from_end);
}
};
@@ -1557,7 +1595,7 @@ public:
void sort_string(uchar *buff,uint length);
uint32 pack_length() const;
void sql_type(String &str) const;
- bool get_date(MYSQL_TIME *ltime,uint fuzzydate);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
uchar *pack(uchar *to, const uchar *from, uint max_length)
{ return Field::pack(to, from, max_length); }
const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end,
@@ -1632,6 +1670,7 @@ public:
orig_table->s->frm_version < FRM_VER_TRUE_VARCHAR ?
MYSQL_TYPE_VAR_STRING : MYSQL_TYPE_STRING);
}
+ bool match_collation_to_optimize_range() const { return TRUE; }
enum ha_base_keytype key_type() const
{ return binary() ? HA_KEYTYPE_BINARY : HA_KEYTYPE_TEXT; }
bool zero_pack() const { return 0; }
@@ -1662,9 +1701,9 @@ public:
return row_pack_length();
return (((field_metadata >> 4) & 0x300) ^ 0x300) + (field_metadata & 0x00ff);
}
- int compatible_field_size(uint field_metadata,
- const Relay_log_info *rli, uint16 mflags);
- uint row_pack_length() { return (field_length + 1); }
+ bool compatible_field_size(uint field_metadata, Relay_log_info *rli,
+ uint16 mflags, int *order_var);
+ uint row_pack_length() { return field_length; }
int pack_cmp(const uchar *a,const uchar *b,uint key_length,
bool insert_or_update);
int pack_cmp(const uchar *b,uint key_length,bool insert_or_update);
@@ -1674,7 +1713,7 @@ public:
enum_field_types real_type() const { return MYSQL_TYPE_STRING; }
bool has_charset(void) const
{ return charset() == &my_charset_bin ? FALSE : TRUE; }
- Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type);
+ Field *new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type);
virtual uint get_key_image(uchar *buff,uint length, imagetype type);
private:
int do_save_field_metadata(uchar *first_byte);
@@ -1712,6 +1751,7 @@ public:
}
enum_field_types type() const { return MYSQL_TYPE_VARCHAR; }
+ bool match_collation_to_optimize_range() const { return TRUE; }
enum ha_base_keytype key_type() const;
uint row_pack_length() { return field_length; }
bool zero_pack() const { return 0; }
@@ -1740,16 +1780,8 @@ public:
void set_key_image(const uchar *buff,uint length);
void sql_type(String &str) const;
virtual uchar *pack(uchar *to, const uchar *from, uint max_length);
- uchar *pack_key(uchar *to, const uchar *from, uint max_length);
- uchar *pack_key_from_key_image(uchar* to, const uchar *from, uint max_length);
virtual const uchar *unpack(uchar* to, const uchar *from,
const uchar *from_end, uint param_data);
-#ifdef NOT_USED
- const uchar *unpack_key(uchar* to, const uchar *from, uint max_length);
-#endif
- int pack_cmp(const uchar *a, const uchar *b, uint key_length,
- bool insert_or_update);
- int pack_cmp(const uchar *b, uint key_length,bool insert_or_update);
int cmp_binary(const uchar *a,const uchar *b, uint32 max_length=~0L);
int key_cmp(const uchar *,const uchar*);
int key_cmp(const uchar *str, uint length);
@@ -1760,8 +1792,8 @@ public:
enum_field_types real_type() const { return MYSQL_TYPE_VARCHAR; }
bool has_charset(void) const
{ return charset() == &my_charset_bin ? FALSE : TRUE; }
- Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type);
- Field *new_key_field(MEM_ROOT *root, struct st_table *new_table,
+ Field *new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type);
+ Field *new_key_field(MEM_ROOT *root, TABLE *new_table,
uchar *new_ptr, uchar *new_null_ptr,
uint new_null_bit);
uint is_equal(Create_field *new_field);
@@ -1814,6 +1846,7 @@ public:
:Field_longstr((uchar*) 0, 0, (uchar*) "", 0, NONE, "temp", system_charset_info),
packlength(packlength_arg) {}
enum_field_types type() const { return MYSQL_TYPE_BLOB;}
+ bool match_collation_to_optimize_range() const { return TRUE; }
enum ha_base_keytype key_type() const
{ return binary() ? HA_KEYTYPE_VARBINARY2 : HA_KEYTYPE_VARTEXT2; }
int store(const char *to,uint length,CHARSET_INFO *charset);
@@ -1859,42 +1892,30 @@ public:
{
store_length(ptr, packlength, number);
}
-
- /**
- Return the packed length plus the length of the data.
-
- This is used to determine the size of the data plus the
- packed length portion in the row data.
-
- @returns The length in the row plus the size of the data.
- */
- uint32 get_packed_size(const uchar *ptr_arg)
- {return packlength + get_length(ptr_arg, packlength);}
-
inline uint32 get_length(uint row_offset= 0)
{ return get_length(ptr+row_offset, this->packlength); }
uint32 get_length(const uchar *ptr, uint packlength);
uint32 get_length(const uchar *ptr_arg)
{ return get_length(ptr_arg, this->packlength); }
inline void get_ptr(uchar **str)
- {
- memcpy_fixed((uchar*) str,ptr+packlength,sizeof(uchar*));
- }
+ {
+ memcpy(str, ptr+packlength, sizeof(uchar*));
+ }
inline void get_ptr(uchar **str, uint row_offset)
- {
- memcpy_fixed((uchar*) str,ptr+packlength+row_offset,sizeof(char*));
- }
+ {
+ memcpy(str, ptr+packlength+row_offset, sizeof(char*));
+ }
inline void set_ptr(uchar *length, uchar *data)
- {
- memcpy(ptr,length,packlength);
- memcpy_fixed(ptr+packlength,&data,sizeof(char*));
- }
+ {
+ memcpy(ptr,length,packlength);
+ memcpy(ptr+packlength, &data,sizeof(char*));
+ }
void set_ptr_offset(my_ptrdiff_t ptr_diff, uint32 length, uchar *data)
- {
- uchar *ptr_ofs= ADD_TO_PTR(ptr,ptr_diff,uchar*);
- store_length(ptr_ofs, packlength, length);
- memcpy_fixed(ptr_ofs+packlength,&data,sizeof(char*));
- }
+ {
+ uchar *ptr_ofs= ADD_TO_PTR(ptr,ptr_diff,uchar*);
+ store_length(ptr_ofs, packlength, length);
+ memcpy(ptr_ofs+packlength, &data, sizeof(char*));
+ }
inline void set_ptr(uint32 length, uchar *data)
{
set_ptr_offset(0, length, data);
@@ -1912,23 +1933,14 @@ public:
return 1;
}
tmp=(uchar*) value.ptr();
- memcpy_fixed(ptr+packlength,&tmp,sizeof(char*));
+ memcpy(ptr+packlength, &tmp, sizeof(char*));
return 0;
}
virtual uchar *pack(uchar *to, const uchar *from, uint max_length);
- uchar *pack_key(uchar *to, const uchar *from, uint max_length);
- uchar *pack_key_from_key_image(uchar* to, const uchar *from, uint max_length);
virtual const uchar *unpack(uchar *to, const uchar *from,
const uchar *from_end, uint param_data);
-#ifdef NOT_USED
- const uchar *unpack_key(uchar* to, const uchar *from, uint max_length);
-#endif
- int pack_cmp(const uchar *a, const uchar *b, uint key_length,
- bool insert_or_update);
- int pack_cmp(const uchar *b, uint key_length,bool insert_or_update);
uint packed_col_length(const uchar *col_ptr, uint length);
uint max_packed_col_length(uint max_length);
- /* Set if we should convert constant item's with convert_const_item */
void free() { value.free(); }
inline void clear_temporary() { bzero((uchar*) &value,sizeof(value)); }
friend int field_conv(Field *to,Field *from);
@@ -1936,6 +1948,7 @@ public:
bool has_charset(void) const
{ return charset() == &my_charset_bin ? FALSE : TRUE; }
uint32 max_display_length();
+ uint32 char_length();
uint is_equal(Create_field *new_field);
inline bool in_read_set() { return bitmap_is_set(table->read_set, field_index); }
inline bool in_write_set() { return bitmap_is_set(table->write_set, field_index); }
@@ -1962,6 +1975,7 @@ public:
{ geom_type= geom_type_arg; }
enum ha_base_keytype key_type() const { return HA_KEYTYPE_VARBINARY2; }
enum_field_types type() const { return MYSQL_TYPE_GEOMETRY; }
+ bool match_collation_to_optimize_range() const { return FALSE; }
void sql_type(String &str) const;
int store(const char *to, uint length, CHARSET_INFO *charset);
int store(double nr);
@@ -1976,7 +1990,6 @@ public:
int reset(void) { return Field_blob::reset() || !maybe_null(); }
geometry_type get_geometry_type() { return geom_type; };
- bool hash_join_is_possible() { return FALSE; }
};
#endif /*HAVE_SPATIAL*/
@@ -1998,8 +2011,9 @@ public:
{
flags|=ENUM_FLAG;
}
- Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type);
+ Field *new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type);
enum_field_types type() const { return MYSQL_TYPE_STRING; }
+ bool match_collation_to_optimize_range() const { return FALSE; }
enum Item_result cmp_type () const { return INT_RESULT; }
enum ha_base_keytype key_type() const;
int store(const char *to,uint length,CHARSET_INFO *charset);
@@ -2025,6 +2039,11 @@ public:
/* enum and set are sorted as integers */
CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; }
uint decimals() const { return 0; }
+
+ virtual uchar *pack(uchar *to, const uchar *from, uint max_length);
+ virtual const uchar *unpack(uchar *to, const uchar *from,
+ const uchar *from_end, uint param_data);
+
private:
int do_save_field_metadata(uchar *first_byte);
uint is_equal(Create_field *new_field);
@@ -2041,7 +2060,8 @@ public:
:Field_enum(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg,
packlength_arg,
- typelib_arg,charset_arg)
+ typelib_arg,charset_arg),
+ empty_set_string("", 0, charset_arg)
{
flags=(flags & ~ENUM_FLAG) | SET_FLAG;
}
@@ -2052,8 +2072,11 @@ public:
virtual bool zero_pack() const { return 1; }
String *val_str(String*,String *);
void sql_type(String &str) const;
+ uint size_of() const { return sizeof(*this); }
enum_field_types real_type() const { return MYSQL_TYPE_SET; }
bool has_charset(void) const { return TRUE; }
+private:
+ const String empty_set_string;
};
@@ -2103,9 +2126,12 @@ public:
virtual bool str_needs_quotes() { return TRUE; }
my_decimal *val_decimal(my_decimal *);
int cmp(const uchar *a, const uchar *b)
- {
- DBUG_ASSERT(ptr == a);
- return Field_bit::key_cmp(b, bytes_in_rec+test(bit_len));
+ {
+ DBUG_ASSERT(ptr == a || ptr == b);
+ if (ptr == a)
+ return Field_bit::key_cmp(b, bytes_in_rec+test(bit_len));
+ else
+ return Field_bit::key_cmp(a, bytes_in_rec+test(bit_len)) * -1;
}
int cmp_binary_offset(uint row_offset)
{ return cmp_offset(row_offset); }
@@ -2128,15 +2154,15 @@ public:
uint pack_length_from_metadata(uint field_metadata);
uint row_pack_length()
{ return (bytes_in_rec + ((bit_len > 0) ? 1 : 0)); }
- int compatible_field_size(uint field_metadata,
- const Relay_log_info *rli, uint16 mflags);
+ bool compatible_field_size(uint metadata, Relay_log_info *rli,
+ uint16 mflags, int *order_var);
void sql_type(String &str) const;
virtual uchar *pack(uchar *to, const uchar *from, uint max_length);
virtual const uchar *unpack(uchar *to, const uchar *from,
const uchar *from_end, uint param_data);
virtual void set_default();
- Field *new_key_field(MEM_ROOT *root, struct st_table *new_table,
+ Field *new_key_field(MEM_ROOT *root, TABLE *new_table,
uchar *new_ptr, uchar *new_null_ptr,
uint new_null_bit);
void set_bit_ptr(uchar *bit_ptr_arg, uchar bit_ofs_arg)
@@ -2248,7 +2274,8 @@ public:
/* Init for a tmp table field. To be extended if need be. */
void init_for_tmp_table(enum_field_types sql_type_arg,
uint32 max_length, uint32 decimals,
- bool maybe_null, bool is_unsigned);
+ bool maybe_null, bool is_unsigned,
+ uint pack_length = ~0U);
bool init(THD *thd, char *field_name, enum_field_types type, char *length,
char *decimals, uint type_modifier, Item *default_value,
@@ -2265,6 +2292,8 @@ public:
{
return 255 - FRM_VCOL_HEADER_SIZE(interval != NULL);
}
+private:
+ const String empty_set_string;
};
@@ -2272,7 +2301,7 @@ public:
A class for sending info to the client
*/
-class Send_field {
+class Send_field :public Sql_alloc {
public:
const char *db_name;
const char *table_name,*org_table_name;
@@ -2392,3 +2421,5 @@ int set_field_to_null_with_conversions(Field *field, bool no_conversions);
#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)
+
+#endif /* FIELD_INCLUDED */
diff --git a/sql/field_conv.cc b/sql/field_conv.cc
index e3d23b1f4c0..6c3fcc0d355 100644
--- a/sql/field_conv.cc
+++ b/sql/field_conv.cc
@@ -1,5 +1,7 @@
/*
- Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2012, Monty Program Ab
+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +14,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@@ -26,7 +27,8 @@
gives much more speed.
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "sql_class.h" // THD
#include <m_ctype.h>
static void do_field_eq(Copy_field *copy)
@@ -333,7 +335,7 @@ static void do_copy_blob(Copy_field *copy)
{
ulong length=((Field_blob*) copy->from_field)->get_length();
((Field_blob*) copy->to_field)->store_length(length);
- memcpy_fixed(copy->to_ptr,copy->from_ptr,sizeof(char*));
+ memcpy(copy->to_ptr, copy->from_ptr, sizeof(char*));
}
static void do_conv_blob(Copy_field *copy)
@@ -361,10 +363,11 @@ static void do_save_blob(Copy_field *copy)
static void do_field_string(Copy_field *copy)
{
char buff[MAX_FIELD_WIDTH];
- copy->tmp.set_quick(buff,sizeof(buff),copy->tmp.charset());
- copy->from_field->val_str(&copy->tmp);
- copy->to_field->store(copy->tmp.c_ptr_quick(),copy->tmp.length(),
- copy->tmp.charset());
+ String res(buff, sizeof(buff), copy->from_field->charset());
+ res.length(0U);
+
+ copy->from_field->val_str(&res);
+ copy->to_field->store(res.c_ptr_quick(), res.length(), res.charset());
}
diff --git a/sql/filesort.cc b/sql/filesort.cc
index 8ddf36b6d92..d6cb22ebe2c 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -1,5 +1,5 @@
-/*
- Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2014, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +12,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
/**
@@ -23,16 +22,21 @@
Sorts a database
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "filesort.h"
+#include "unireg.h" // REQUIRED by other includes
#ifdef HAVE_STDDEF_H
#include <stddef.h> /* for macro offsetof */
#endif
#include <m_ctype.h>
#include "sql_sort.h"
-
-#ifndef THREAD
-#define SKIP_DBUG_IN_FILESORT
-#endif
+#include "probes_mysql.h"
+#include "sql_base.h" // update_virtual_fields
+#include "sql_test.h" // TEST_filesort
+#include "opt_range.h" // SQL_SELECT
+#include "log_slow.h"
+#include "debug_sync.h"
+#include "sql_base.h"
/// How to write record_ref.
#define WRITE_REF(file,from) \
@@ -45,10 +49,9 @@ static uchar *read_buffpek_from_file(IO_CACHE *buffer_file, uint count,
uchar *buf);
static ha_rows find_all_keys(SORTPARAM *param,SQL_SELECT *select,
uchar * *sort_keys, uchar *sort_keys_buf,
- IO_CACHE *buffer_file,
- IO_CACHE *tempfile,IO_CACHE *indexfile);
-static int write_keys(SORTPARAM *param,uchar * *sort_keys,
- uint count, IO_CACHE *buffer_file, IO_CACHE *tempfile);
+ IO_CACHE *buffer_file, IO_CACHE *tempfile);
+static bool write_keys(SORTPARAM *param,uchar * *sort_keys,
+ uint count, IO_CACHE *buffer_file, IO_CACHE *tempfile);
static void make_sortkey(SORTPARAM *param,uchar *to, uchar *ref_pos);
static void register_used_fields(SORTPARAM *param);
static bool save_index(SORTPARAM *param,uchar **sort_keys, uint count,
@@ -82,8 +85,6 @@ static void unpack_addon_fields(struct st_sort_addon_field *addon_field,
(Needed by UPDATE/INSERT or ALTER TABLE)
@param examined_rows Store number of examined rows here
- @todo
- check why we do this (param.keys--)
@note
If we sort by position (like if sort_positions is 1) filesort() will
call table->prepare_for_position().
@@ -101,13 +102,14 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
bool sort_positions, ha_rows *examined_rows)
{
int error;
- ulong memavl, min_sort_memory;
- ulong sort_buff_sz;
+ size_t memory_available= thd->variables.sortbuff_size;
+ size_t min_sort_memory;
+ size_t sort_buff_sz;
uint maxbuffer;
BUFFPEK *buffpek;
- ha_rows records= HA_POS_ERROR;
+ ha_rows num_rows= HA_POS_ERROR;
uchar **sort_keys= 0;
- IO_CACHE tempfile, buffpek_pointers, *selected_records_file, *outfile;
+ IO_CACHE tempfile, buffpek_pointers, *outfile;
SORTPARAM param;
bool multi_byte_charset;
DBUG_ENTER("filesort");
@@ -119,6 +121,9 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
TABLE_LIST *tab= table->pos_in_table_list;
Item_subselect *subselect= tab ? tab->containing_subselect() : 0;
+ MYSQL_FILESORT_START(table->s->db.str, table->s->table_name.str);
+ DEBUG_SYNC(thd, "filesort_start");
+
/*
Release InnoDB's adaptive hash index latch (if holding) before
running a sort.
@@ -132,6 +137,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
*/
memcpy(&table_sort, &table->sort, sizeof(FILESORT_INFO));
table->sort.io_cache= NULL;
+ DBUG_ASSERT(table_sort.record_pointers == NULL);
outfile= table_sort.io_cache;
my_b_clear(&tempfile);
@@ -177,65 +183,48 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
param.max_rows= max_rows;
if (select && select->quick)
- {
status_var_increment(thd->status_var.filesort_range_count);
- }
else
- {
status_var_increment(thd->status_var.filesort_scan_count);
- }
thd->query_plan_flags|= QPLAN_FILESORT;
-#ifdef CAN_TRUST_RANGE
- if (select && select->quick && select->quick->records > 0L)
- {
- records=min((ha_rows) (select->quick->records*2+EXTRA_RECORDS*2),
- table->file->stats.records)+EXTRA_RECORDS;
- selected_records_file=0;
- }
- else
-#endif
- {
- records= table->file->estimate_rows_upper_bound();
- /*
- If number of records is not known, use as much of sort buffer
- as possible.
- */
- if (records == HA_POS_ERROR)
- records--; // we use 'records+1' below.
- selected_records_file= 0;
- }
+
+ // If number of rows is not known, use as much of sort buffer as possible.
+ num_rows= table->file->estimate_rows_upper_bound();
if (multi_byte_charset &&
!(param.tmp_buffer= (char*) my_malloc(param.sort_length,MYF(MY_WME))))
goto err;
- memavl= thd->variables.sortbuff_size;
- min_sort_memory= max(MIN_SORT_MEMORY, param.sort_length*MERGEBUFF2);
+ min_sort_memory= max(MIN_SORT_MEMORY, param.sort_length * MERGEBUFF2);
set_if_bigger(min_sort_memory, sizeof(BUFFPEK*)*MERGEBUFF2);
if (!table_sort.sort_keys)
{
- while (memavl >= min_sort_memory)
+ while (memory_available >= min_sort_memory)
{
- ulong old_memavl;
- ulong keys= memavl/(param.rec_length+sizeof(char*));
- table_sort.keys= (uint) min(records+1, keys);
+ ulonglong keys= memory_available / (param.rec_length + sizeof(char*));
+ table_sort.keys= (uint) min(num_rows, keys);
sort_buff_sz= table_sort.keys*(param.rec_length+sizeof(char*));
set_if_bigger(sort_buff_sz, param.rec_length * MERGEBUFF2);
+
+ DBUG_EXECUTE_IF("make_sort_keys_alloc_fail",
+ DBUG_SET("+d,simulate_out_of_memory"););
+
if ((table_sort.sort_keys=
(uchar**) my_malloc(sort_buff_sz, MYF(0))))
break;
- old_memavl=memavl;
- if ((memavl=memavl/4*3) < min_sort_memory &&
- old_memavl > min_sort_memory)
- memavl= min_sort_memory;
+ size_t old_memory_available= memory_available;
+ memory_available= memory_available/4*3;
+ if (memory_available < min_sort_memory &&
+ old_memory_available > min_sort_memory)
+ memory_available= min_sort_memory;
}
}
sort_keys= table_sort.sort_keys;
- param.keys= table_sort.keys - 1; /* TODO: check why we do this " - 1" */
- if (memavl < min_sort_memory)
+ param.keys= table_sort.keys;
+ if (memory_available < min_sort_memory)
{
- my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR+ME_WAITTANG));
+ my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR + ME_FATALERROR));
goto err;
}
if (open_cached_file(&buffpek_pointers,mysql_tmpdir,TEMP_PREFIX,
@@ -244,26 +233,31 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
param.sort_form= table;
param.end=(param.local_sortorder=sortorder)+s_length;
- if ((records=find_all_keys(&param,select,sort_keys,
- (uchar *)(sort_keys+param.keys), &buffpek_pointers,
- &tempfile, selected_records_file)) ==
- HA_POS_ERROR)
+ num_rows= find_all_keys(&param,
+ select,
+ sort_keys,
+ (uchar *)(sort_keys+param.keys),
+ &buffpek_pointers,
+ &tempfile);
+ if (num_rows == HA_POS_ERROR)
goto err;
maxbuffer= (uint) (my_b_tell(&buffpek_pointers)/sizeof(*buffpek));
if (maxbuffer == 0) // The whole set is in memory
{
- if (save_index(&param,sort_keys,(uint) records, &table_sort))
+ if (save_index(&param,sort_keys,(uint) num_rows, &table_sort))
goto err;
}
else
{
thd->query_plan_flags|= QPLAN_FILESORT_DISK;
+
/* filesort cannot handle zero-length records during merge. */
DBUG_ASSERT(param.sort_length != 0);
+
if (table_sort.buffpek && table_sort.buffpek_len < maxbuffer)
{
- x_free(table_sort.buffpek);
+ my_free(table_sort.buffpek);
table_sort.buffpek= 0;
}
if (!(table_sort.buffpek=
@@ -285,8 +279,9 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
Use also the space previously used by string pointers in sort_buffer
for temporary key storage.
*/
- param.keys=((param.keys*(param.rec_length+sizeof(char*))) /
- param.rec_length-1);
+ param.keys=((param.keys *
+ (param.rec_length+sizeof(char*))) /
+ param.rec_length - 1);
maxbuffer--; // Offset from 0
if (merge_many_buff(&param,(uchar*) sort_keys,buffpek,&maxbuffer,
&tempfile))
@@ -294,22 +289,29 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
if (flush_io_cache(&tempfile) ||
reinit_io_cache(&tempfile,READ_CACHE,0L,0,0))
goto err;
- if (merge_index(&param,(uchar*) sort_keys,buffpek,maxbuffer,&tempfile,
+ if (merge_index(&param,
+ (uchar*) sort_keys,
+ buffpek,
+ maxbuffer,
+ &tempfile,
outfile))
goto err;
}
- if (records > param.max_rows)
- records=param.max_rows;
- error =0;
+
+ if (num_rows > param.max_rows)
+ {
+ // If find_all_keys() produced more results than the query LIMIT.
+ num_rows= param.max_rows;
+ }
+ error= 0;
err:
- if (param.tmp_buffer)
- x_free(param.tmp_buffer);
+ my_free(param.tmp_buffer);
if (!subselect || !subselect->is_uncacheable())
{
- x_free((uchar*) sort_keys);
+ my_free(sort_keys);
table_sort.sort_keys= 0;
- x_free((uchar*) buffpek);
+ my_free(buffpek);
table_sort.buffpek= 0;
table_sort.buffpek_len= 0;
}
@@ -328,48 +330,72 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
}
}
if (error)
- my_message(ER_FILSORT_ABORT, ER(ER_FILSORT_ABORT), MYF(0));
+ {
+ int kill_errno= thd->killed_errno();
+ DBUG_ASSERT(thd->is_error() || kill_errno || thd->killed == ABORT_QUERY);
+
+ /*
+ We replace the table->sort at the end.
+ Hence calling free_io_cache to make sure table->sort.io_cache
+ used for QUICK_INDEX_MERGE_SELECT is free.
+ */
+ free_io_cache(table);
+
+ my_printf_error(ER_FILSORT_ABORT,
+ "%s: %s",
+ MYF(0),
+ ER_THD(thd, ER_FILSORT_ABORT),
+ kill_errno ? ER(kill_errno) :
+ thd->killed == ABORT_QUERY ? "" : thd->stmt_da->message());
+
+ if (global_system_variables.log_warnings > 1)
+ {
+ sql_print_warning("%s, host: %s, user: %s, thread: %lu, query: %-.4096s",
+ ER_THD(thd, ER_FILSORT_ABORT),
+ thd->security_ctx->host_or_ip,
+ &thd->security_ctx->priv_user[0],
+ (ulong) thd->thread_id,
+ thd->query());
+ }
+ }
else
statistic_add(thd->status_var.filesort_rows,
- (ulong) records, &LOCK_status);
+ (ulong) num_rows, &LOCK_status);
*examined_rows= param.examined_rows;
#ifdef SKIP_DBUG_IN_FILESORT
DBUG_POP(); /* Ok to DBUG */
#endif
+
+ /* table->sort.io_cache should be free by this time */
+ DBUG_ASSERT(NULL == table->sort.io_cache);
+
memcpy(&table->sort, &table_sort, sizeof(FILESORT_INFO));
- DBUG_PRINT("exit",("records: %ld", (long) records));
- DBUG_RETURN(error ? HA_POS_ERROR : records);
+ DBUG_PRINT("exit",("num_rows: %ld", (long) num_rows));
+ MYSQL_FILESORT_DONE(error, num_rows);
+ DBUG_RETURN(error ? HA_POS_ERROR : num_rows);
} /* filesort */
void filesort_free_buffers(TABLE *table, bool full)
{
- if (table->sort.record_pointers)
- {
- my_free((uchar*) table->sort.record_pointers,MYF(0));
- table->sort.record_pointers=0;
- }
+ DBUG_ENTER("filesort_free_buffers");
+ my_free(table->sort.record_pointers);
+ table->sort.record_pointers= NULL;
+
if (full)
{
- if (table->sort.sort_keys )
- {
- x_free((uchar*) table->sort.sort_keys);
- table->sort.sort_keys= 0;
- }
- if (table->sort.buffpek)
- {
- x_free((uchar*) table->sort.buffpek);
- table->sort.buffpek= 0;
- table->sort.buffpek_len= 0;
- }
- }
- if (table->sort.addon_buf)
- {
- my_free((char *) table->sort.addon_buf, MYF(0));
- my_free((char *) table->sort.addon_field, MYF(MY_ALLOW_ZERO_PTR));
- table->sort.addon_buf=0;
- table->sort.addon_field=0;
+ my_free(table->sort.sort_keys);
+ table->sort.sort_keys= NULL;
+ my_free(table->sort.buffpek);
+ table->sort.buffpek= NULL;
+ table->sort.buffpek_len= 0;
}
+
+ my_free(table->sort.addon_buf);
+ my_free(table->sort.addon_field);
+ table->sort.addon_buf= NULL;
+ table->sort.addon_field= NULL;
+ DBUG_VOID_RETURN;
}
@@ -378,7 +404,7 @@ void filesort_free_buffers(TABLE *table, bool full)
static uchar *read_buffpek_from_file(IO_CACHE *buffpek_pointers, uint count,
uchar *buf)
{
- ulong length= sizeof(BUFFPEK)*count;
+ size_t length= sizeof(BUFFPEK)*count;
uchar *tmp= buf;
DBUG_ENTER("read_buffpek_from_file");
if (count > UINT_MAX/sizeof(BUFFPEK))
@@ -390,7 +416,7 @@ static uchar *read_buffpek_from_file(IO_CACHE *buffpek_pointers, uint count,
if (reinit_io_cache(buffpek_pointers,READ_CACHE,0L,0,0) ||
my_b_read(buffpek_pointers, (uchar*) tmp, length))
{
- my_free((char*) tmp, MYF(0));
+ my_free(tmp);
tmp=0;
}
}
@@ -537,7 +563,6 @@ static void dbug_print_record(TABLE *table, bool print_rowid)
@param buffpek_pointers File to write BUFFPEKs describing sorted segments
in tempfile.
@param tempfile File to write sorted sequences of sortkeys to.
- @param indexfile If !NULL, use it for source data (contains rowids)
@note
Basic idea:
@@ -567,7 +592,7 @@ static void dbug_print_record(TABLE *table, bool print_rowid)
static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
uchar **sort_keys, uchar *sort_keys_buf,
IO_CACHE *buffpek_pointers,
- IO_CACHE *tempfile, IO_CACHE *indexfile)
+ IO_CACHE *tempfile)
{
int error,flag,quick_select;
uint idx,indexpos,ref_length;
@@ -592,14 +617,15 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
ref_pos= ref_buff;
quick_select=select && select->quick;
record=0;
- flag= ((!indexfile && (file->ha_table_flags() & HA_REC_NOT_IN_SEQ))
- || quick_select);
- if (indexfile || flag)
+ flag= ((file->ha_table_flags() & HA_REC_NOT_IN_SEQ) || quick_select);
+ if (flag)
ref_pos= &file->ref[0];
next_pos=ref_pos;
- if (! indexfile && ! quick_select)
+ if (!quick_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))
DBUG_RETURN(HA_POS_ERROR);
file->extra_opt(HA_EXTRA_CACHE,
@@ -616,6 +642,9 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
/* Temporary set for register_used_fields and register_field_in_read_map */
sort_form->read_set= &sort_form->tmp_set;
register_used_fields(param);
+ if (quick_select)
+ select->quick->add_used_key_part_to_set(sort_form->read_set);
+
Item *sort_cond= !select ?
0 : !select->pre_idx_push_select_cond ?
select->cond : select->pre_idx_push_select_cond;
@@ -631,6 +660,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
DBUG_RETURN(HA_POS_ERROR);
}
+ DEBUG_SYNC(thd, "after_index_merge_phase1");
for (;;)
{
if (quick_select)
@@ -644,18 +674,8 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
}
else /* Not quick-select */
{
- if (indexfile)
- {
- if (my_b_read(indexfile,(uchar*) ref_pos,ref_length)) /* purecov: deadcode */
- {
- error= my_errno ? my_errno : -1; /* Abort */
- break;
- }
- error=file->ha_rnd_pos(sort_form->record[0],next_pos);
- }
- else
{
- error=file->ha_rnd_next(sort_form->record[0]);
+ error= file->ha_rnd_next(sort_form->record[0]);
if (!error && sort_form->vfield)
update_virtual_fields(thd, sort_form);
if (!flag)
@@ -673,7 +693,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
if (*killed)
{
DBUG_PRINT("info",("Sort killed by user"));
- if (!indexfile && !quick_select)
+ if (!quick_select)
{
(void) file->extra(HA_EXTRA_NO_CACHE);
file->ha_rnd_end();
@@ -717,9 +737,10 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
{
if (idx == param->keys)
{
- if (write_keys(param,sort_keys,idx,buffpek_pointers,tempfile))
+ if (write_keys(param, sort_keys,
+ idx, buffpek_pointers, tempfile))
DBUG_RETURN(HA_POS_ERROR);
- idx=0;
+ idx= 0;
next_sort_key= sort_keys_buf;
indexpos++;
}
@@ -727,12 +748,17 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
make_sortkey(param, next_sort_key, ref_pos);
next_sort_key+= param->rec_length;
}
- else
- file->unlock_row();
/* It does not make sense to read more keys in case of a fatal error */
if (thd->is_error())
break;
+
+ /*
+ We need to this after checking the error as the transaction may have
+ rolled back in case of a deadlock
+ */
+ if (!write_record)
+ file->unlock_row();
}
if (!quick_select)
{
@@ -750,15 +776,17 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
DBUG_PRINT("test",("error: %d indexpos: %d",error,indexpos));
if (error != HA_ERR_END_OF_FILE)
{
- file->print_error(error,MYF(ME_ERROR | ME_WAITTANG)); /* purecov: inspected */
+ file->print_error(error,MYF(ME_ERROR | ME_WAITTANG)); // purecov: inspected
DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
}
if (indexpos && idx &&
- write_keys(param,sort_keys,idx,buffpek_pointers,tempfile))
+ write_keys(param, sort_keys,
+ idx, buffpek_pointers, tempfile))
DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
- DBUG_RETURN(my_b_inited(tempfile) ?
- (ha_rows) (my_b_tell(tempfile)/param->rec_length) :
- idx);
+ const ha_rows retval=
+ my_b_inited(tempfile) ?
+ (ha_rows) (my_b_tell(tempfile)/param->rec_length) : idx;
+ DBUG_RETURN(retval);
} /* find_all_keys */
@@ -784,7 +812,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
1 Error
*/
-static int
+static bool
write_keys(SORTPARAM *param, register uchar **sort_keys, uint count,
IO_CACHE *buffpek_pointers, IO_CACHE *tempfile)
{
@@ -875,13 +903,11 @@ static void make_sortkey(register SORTPARAM *param,
{
CHARSET_INFO *cs=item->collation.collation;
char fill_char= ((cs->state & MY_CS_BINSORT) ? (char) 0 : ' ');
- int diff;
- uint sort_field_length;
if (maybe_null)
*to++=1;
- /* All item->str() to use some extra byte for end null.. */
- String tmp((char*) to,sort_field->length+4,cs);
+ char *tmp_buffer= param->tmp_buffer ? param->tmp_buffer : (char*)to;
+ String tmp(tmp_buffer, param->sort_length, cs);
String *res= item->str_result(&tmp);
if (!res)
{
@@ -904,35 +930,32 @@ static void make_sortkey(register SORTPARAM *param,
break;
}
length= res->length();
- sort_field_length= sort_field->length - sort_field->suffix_length;
- diff=(int) (sort_field_length - length);
- if (diff < 0)
- {
- diff=0;
- length= sort_field_length;
- }
- if (sort_field->suffix_length)
- {
- /* Store length last in result_string */
- store_length(to + sort_field_length, length,
- sort_field->suffix_length);
- }
if (sort_field->need_strxnfrm)
{
- char *from=(char*) res->ptr();
uint tmp_length __attribute__((unused));
- if ((uchar*) from == to)
- {
- set_if_smaller(length,sort_field->length);
- memcpy(param->tmp_buffer,from,length);
- from=param->tmp_buffer;
- }
- tmp_length= my_strnxfrm(cs,to,sort_field->length,
- (uchar*) from, length);
+ tmp_length= my_strnxfrm(cs, to ,sort_field->length,
+ (uchar*) res->ptr(), length);
DBUG_ASSERT(tmp_length == sort_field->length);
}
else
{
+ uint diff;
+ uint sort_field_length= sort_field->length -
+ sort_field->suffix_length;
+ if (sort_field_length < length)
+ {
+ diff= 0;
+ length= sort_field_length;
+ }
+ else
+ diff= sort_field_length - length;
+ if (sort_field->suffix_length)
+ {
+ /* Store length last in result_string */
+ store_length(to + sort_field_length, length,
+ sort_field->suffix_length);
+ }
+ /* apply cs->sort_order for case-insensitive comparison if needed */
my_strnxfrm(cs,(uchar*)to,length,(const uchar*)res->ptr(),length);
cs->cset->fill(cs, (char *)to+length,diff,fill_char);
}
@@ -1222,8 +1245,9 @@ uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek,
if ((count=(uint) min((ha_rows) buffpek->max_keys,buffpek->count)))
{
- if (my_pread(fromfile->file,(uchar*) buffpek->base,
- (length= rec_length*count),buffpek->file_pos,MYF_RW))
+ if (mysql_file_pread(fromfile->file, (uchar*) buffpek->base,
+ (length= rec_length*count),
+ buffpek->file_pos, MYF_RW))
return((uint) -1); /* purecov: inspected */
buffpek->key=buffpek->base;
buffpek->file_pos+= length; /* New filepos */
@@ -1353,8 +1377,9 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
{
buffpek->base= strpos;
buffpek->max_keys= maxcount;
- strpos+= (uint) (error= (int) read_to_buffer(from_file, buffpek,
- rec_length));
+ strpos+=
+ (uint) (error= (int) read_to_buffer(from_file, buffpek, rec_length));
+
if (error == -1)
goto err; /* purecov: inspected */
buffpek->max_keys= buffpek->mem_count; // If less data in buffers than expected
@@ -1379,7 +1404,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
if (!(error= (int) read_to_buffer(from_file, buffpek,
rec_length)))
{
- VOID(queue_remove(&queue,0));
+ queue_remove(&queue,0);
reuse_freed_buff(&queue, buffpek, rec_length);
}
else if (error == -1)
@@ -1456,7 +1481,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
if (!(error= (int) read_to_buffer(from_file, buffpek,
rec_length)))
{
- VOID(queue_remove_top(&queue));
+ (void) queue_remove_top(&queue);
reuse_freed_buff(&queue, buffpek, rec_length);
break; /* One buffer have been removed */
}
@@ -1832,7 +1857,7 @@ void change_double_for_sort(double nr,uchar *to)
else
{
#ifdef WORDS_BIGENDIAN
- memcpy_fixed(tmp,&nr,sizeof(nr));
+ memcpy(tmp, &nr, sizeof(nr));
#else
{
uchar *ptr= (uchar*) &nr;
diff --git a/sql/filesort.h b/sql/filesort.h
new file mode 100644
index 00000000000..8ee8999d055
--- /dev/null
+++ b/sql/filesort.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef FILESORT_INCLUDED
+#define FILESORT_INCLUDED
+
+class SQL_SELECT;
+
+#include "my_global.h" /* uint, uchar */
+#include "my_base.h" /* ha_rows */
+
+class SQL_SELECT;
+class THD;
+struct TABLE;
+typedef struct st_sort_field SORT_FIELD;
+
+ha_rows filesort(THD *thd, TABLE *table, st_sort_field *sortorder,
+ uint s_length, SQL_SELECT *select,
+ ha_rows max_rows, bool sort_positions,
+ ha_rows *examined_rows);
+void filesort_free_buffers(TABLE *table, bool full);
+double get_merge_many_buffs_cost(uint *buffer, uint last_n_elems,
+ int elem_size);
+void change_double_for_sort(double nr,uchar *to);
+
+#endif /* FILESORT_INCLUDED */
diff --git a/sql/frm_crypt.cc b/sql/frm_crypt.cc
index 621103e5b37..5612908aea5 100644
--- a/sql/frm_crypt.cc
+++ b/sql/frm_crypt.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2006 MySQL AB
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,7 +11,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
@@ -21,7 +21,8 @@
** mysql binary.
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "frm_crypt.h"
#ifdef HAVE_CRYPTED_FRM
diff --git a/sql/frm_crypt.h b/sql/frm_crypt.h
new file mode 100644
index 00000000000..0605644b3e0
--- /dev/null
+++ b/sql/frm_crypt.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef FRM_CRYPT_INCLUDED
+#define FRM_CRYPT_INCLUDED
+
+class SQL_CRYPT;
+
+SQL_CRYPT *get_crypt_for_frm(void);
+
+#endif /* FRM_CRYPT_INCLUDED */
diff --git a/sql/gcalc_slicescan.cc b/sql/gcalc_slicescan.cc
index d05ef7ed826..251869cad03 100644
--- a/sql/gcalc_slicescan.cc
+++ b/sql/gcalc_slicescan.cc
@@ -15,7 +15,9 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#include "mysql_priv.h"
+#include <my_global.h>
+#include <my_sys.h>
+#include <m_string.h>
#ifdef HAVE_SPATIAL
@@ -207,7 +209,7 @@ static void free_blk_list(void *list)
while (list)
{
next_blk= *((void **)list);
- my_free(list, MYF(0));
+ my_free(list);
list= next_blk;
}
}
diff --git a/sql/gcalc_tools.cc b/sql/gcalc_tools.cc
index 729b322769c..864437401b7 100644
--- a/sql/gcalc_tools.cc
+++ b/sql/gcalc_tools.cc
@@ -15,7 +15,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#include "mysql_priv.h"
+#include <my_global.h>
#ifdef HAVE_SPATIAL
diff --git a/sql/gcalc_tools.h b/sql/gcalc_tools.h
index eed9be0803f..12ee56732a2 100644
--- a/sql/gcalc_tools.h
+++ b/sql/gcalc_tools.h
@@ -19,6 +19,7 @@
#define GCALC_TOOLS_INCLUDED
#include "gcalc_slicescan.h"
+#include "sql_string.h"
/*
diff --git a/sql/gen_lex_hash.cc b/sql/gen_lex_hash.cc
index c34647c8916..c37f4f145cf 100644
--- a/sql/gen_lex_hash.cc
+++ b/sql/gen_lex_hash.cc
@@ -12,8 +12,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@file
@@ -79,36 +78,15 @@ So, we can read full search-structure as 32-bit word
*/
#define NO_YACC_SYMBOLS
-#include "my_global.h"
-#include "my_sys.h"
-#include "m_string.h"
-#ifndef __GNU_LIBRARY__
-#define __GNU_LIBRARY__ // Skip warnings in getopt.h
-#endif
-#include <my_getopt.h>
+#include <my_global.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 */
-const char *default_dbug_option="d:t:o,/tmp/gen_lex_hash.trace";
-
-struct my_option my_long_options[] =
-{
-#ifdef DBUG_OFF
- {"debug", '#', "This is a non-debug version. Catch this and exit",
- 0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
-#else
- {"debug", '#', "Output debug log", (uchar**) &default_dbug_option,
- (uchar**) &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
-#endif
- {"help", '?', "Display help and exit",
- 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"version", 'V', "Output version information and exit",
- 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
- {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
-};
-
struct hash_lex_struct
{
int first_char;
@@ -344,55 +322,6 @@ void print_find_structs()
}
-static void usage(int version)
-{
- printf("%s Ver 3.6 Distrib %s, for %s (%s)\n",
- my_progname, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE);
- if (version)
- return;
- puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"));
- puts("This program generates a perfect hashing function for the sql_lex.cc");
- printf("Usage: %s [OPTIONS]\n\n", my_progname);
- my_print_help(my_long_options);
-}
-
-
-extern "C" my_bool
-get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
- char *argument __attribute__((unused)))
-{
- switch(optid) {
- case 'V':
- usage(1);
- exit(0);
- case 'I':
- case '?':
- usage(0);
- exit(0);
- case '#':
- DBUG_PUSH(argument ? argument : default_dbug_option);
- break;
- }
- return 0;
-}
-
-
-static int get_options(int argc, char **argv)
-{
- int ho_error;
-
- if ((ho_error= handle_options(&argc, &argv, my_long_options, get_one_option)))
- exit(ho_error);
-
- if (argc >= 1)
- {
- usage(0);
- exit(1);
- }
- return(0);
-}
-
-
int check_dup_symbols(SYMBOL *s1, SYMBOL *s2)
{
if (s1->length!=s2->length || strncmp(s1->name,s2->name,s1->length))
@@ -443,11 +372,7 @@ int check_duplicates()
int main(int argc,char **argv)
{
- MY_INIT(argv[0]);
- DBUG_PROCESS(argv[0]);
- if (get_options(argc,(char **) argv))
- exit(1);
/* Broken up to indicate that it's not advice to you, gentle reader. */
printf("/*\n\n Do " "not " "edit " "this " "file " "directly!\n\n*/\n");
@@ -549,7 +474,6 @@ static SYMBOL *get_hash_symbol(const char *s,\n\
}\n\
}\n"
);
- my_end(0);
exit(0);
}
diff --git a/sql/gstream.cc b/sql/gstream.cc
index 4f3934386e9..3a9e478c376 100644
--- a/sql/gstream.cc
+++ b/sql/gstream.cc
@@ -1,6 +1,4 @@
-/*
- Copyright (c) 2002, 2004, 2005, 2007 MySQL AB, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2002, 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
@@ -13,15 +11,16 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
Functions to read and parse geometrical data.
NOTE: These functions assumes that the string is end \0 terminated!
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "gstream.h"
+#include "m_string.h" // LEX_STRING
enum Gis_read_stream::enum_tok_types Gis_read_stream::get_next_toc_type()
{
diff --git a/sql/gstream.h b/sql/gstream.h
index b6f277a3fa9..f10b7e9b830 100644
--- a/sql/gstream.h
+++ b/sql/gstream.h
@@ -1,4 +1,7 @@
-/* Copyright (c) 2000-2004, 2006, 2007 MySQL AB
+#ifndef GSTREAM_INCLUDED
+#define GSTREAM_INCLUDED
+
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,8 +14,12 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "my_global.h" /* NULL, NullS */
+#include "my_sys.h" /* MY_ALLOW_ZERO_PTR */
+#include "m_ctype.h" /* my_charset_latin1, my_charset_bin */
class Gis_read_stream
{
@@ -35,7 +42,7 @@ public:
{}
~Gis_read_stream()
{
- my_free((uchar*) m_err_msg, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(m_err_msg);
}
enum enum_tok_types get_next_toc_type();
@@ -82,3 +89,5 @@ protected:
char *m_err_msg;
CHARSET_INFO *m_charset;
};
+
+#endif /* GSTREAM_INCLUDED */
diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc
index fa27a13bcd0..2878f25ed14 100644
--- a/sql/ha_ndbcluster.cc
+++ b/sql/ha_ndbcluster.cc
@@ -25,9 +25,26 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h" // REQUIRED: for other includes
+#include "sql_table.h" // build_table_filename,
+ // tablename_to_filename,
+ // filename_to_tablename
+#include "sql_partition.h" // HA_CAN_*, partition_info, part_id_range
+#include "sql_base.h" // close_cached_tables
+#include "discover.h" // readfrm
+#include "sql_acl.h" // wild_case_compare
#include "rpl_mi.h"
+#include "transaction.h"
+/*
+ There is an incompatibility between GNU ar and the Solaris linker
+ which makes the Solaris linker return an elf error when compiling
+ without NDB support (which makes libndb.a an empty library).
+ To avoid this we add a dummy declaration of a static variable
+ which makes us avoid this bug.
+*/
+int ha_ndb_dummy;
#include <my_dir.h>
#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
#include "ha_ndbcluster.h"
@@ -39,33 +56,136 @@
#include "ha_ndbcluster_binlog.h"
#include "ha_ndbcluster_tables.h"
-#include <mysql/plugin.h>
+#include "sql_plugin.h"
+#include "probes_mysql.h"
+#include "sql_show.h" // init_fill_schema_files_row,
+ // schema_table_store_record
+#include "sql_test.h" // print_where
#ifdef ndb_dynamite
#undef assert
#define assert(x) do { if(x) break; ::printf("%s %d: assert failed: %s\n", __FILE__, __LINE__, #x); ::fflush(stdout); ::signal(SIGABRT,SIG_DFL); ::abort(); ::kill(::getpid(),6); ::kill(::getpid(),9); } while (0)
#endif
-// options from from mysqld.cc
-extern my_bool opt_ndb_optimized_node_selection;
-extern const char *opt_ndbcluster_connectstring;
-extern ulong opt_ndb_cache_check_time;
-
-// ndb interface initialization/cleanup
-#ifdef __cplusplus
-extern "C" {
-#endif
-extern void ndb_init_internal();
-extern void ndb_end_internal();
-#ifdef __cplusplus
-}
-#endif
-
-const char *ndb_distribution_names[]= {"KEYHASH", "LINHASH", NullS};
-TYPELIB ndb_distribution_typelib= { array_elements(ndb_distribution_names)-1,
- "", ndb_distribution_names, NULL };
-const char *opt_ndb_distribution= ndb_distribution_names[ND_KEYHASH];
-enum ndb_distribution opt_ndb_distribution_id= ND_KEYHASH;
+// ndb interface initialization/cleanup functions
+extern "C" void ndb_init_internal();
+extern "C" void ndb_end_internal();
+
+static const int DEFAULT_PARALLELISM= 0;
+static const ha_rows DEFAULT_AUTO_PREFETCH= 32;
+static const ulong ONE_YEAR_IN_SECONDS= (ulong) 3600L*24L*365L;
+
+ulong opt_ndb_extra_logging;
+static ulong opt_ndb_cache_check_time;
+static char* opt_ndb_connectstring;
+static char* opt_ndb_mgmd_host;
+static uint opt_ndb_nodeid;
+
+
+static MYSQL_THDVAR_UINT(
+ autoincrement_prefetch_sz, /* name */
+ PLUGIN_VAR_RQCMDARG,
+ "Specify number of autoincrement values that are prefetched.",
+ NULL, /* check func. */
+ NULL, /* update func. */
+ 1, /* default */
+ 1, /* min */
+ 256, /* max */
+ 0 /* block */
+);
+
+
+static MYSQL_THDVAR_BOOL(
+ force_send, /* name */
+ PLUGIN_VAR_OPCMDARG,
+ "Force send of buffers to ndb immediately without waiting for "
+ "other threads.",
+ NULL, /* check func. */
+ NULL, /* update func. */
+ 1 /* default */
+);
+
+
+static MYSQL_THDVAR_BOOL(
+ use_exact_count, /* name */
+ PLUGIN_VAR_OPCMDARG,
+ "Use exact records count during query planning and for fast "
+ "select count(*), disable for faster queries.",
+ NULL, /* check func. */
+ NULL, /* update func. */
+ 1 /* default */
+);
+
+
+static MYSQL_THDVAR_BOOL(
+ use_transactions, /* name */
+ PLUGIN_VAR_OPCMDARG,
+ "Use transactions for large inserts, if enabled then large "
+ "inserts will be split into several smaller transactions",
+ NULL, /* check func. */
+ NULL, /* update func. */
+ 1 /* default */
+);
+
+
+static MYSQL_THDVAR_BOOL(
+ use_copying_alter_table, /* name */
+ PLUGIN_VAR_OPCMDARG,
+ "Force ndbcluster to always copy tables at alter table (should "
+ "only be used if on-line alter table fails).",
+ NULL, /* check func. */
+ NULL, /* update func. */
+ 0 /* default */
+);
+
+
+static MYSQL_THDVAR_UINT(
+ optimized_node_selection, /* name */
+ PLUGIN_VAR_OPCMDARG,
+ "Select nodes for transactions in a more optimal way.",
+ NULL, /* check func. */
+ NULL, /* update func. */
+ 3, /* default */
+ 0, /* min */
+ 3, /* max */
+ 0 /* block */
+);
+
+
+static MYSQL_THDVAR_BOOL(
+ index_stat_enable, /* name */
+ PLUGIN_VAR_OPCMDARG,
+ "Use ndb index statistics in query optimization.",
+ NULL, /* check func. */
+ NULL, /* update func. */
+ FALSE /* default */
+);
+
+
+static MYSQL_THDVAR_ULONG(
+ index_stat_cache_entries, /* name */
+ PLUGIN_VAR_NOCMDARG,
+ "",
+ NULL, /* check func. */
+ NULL, /* update func. */
+ 32, /* default */
+ 0, /* min */
+ ULONG_MAX, /* max */
+ 0 /* block */
+);
+
+
+static MYSQL_THDVAR_ULONG(
+ index_stat_update_freq, /* name */
+ PLUGIN_VAR_NOCMDARG,
+ "",
+ NULL, /* check func. */
+ NULL, /* update func. */
+ 20, /* default */
+ 0, /* min */
+ ULONG_MAX, /* max */
+ 0 /* block */
+);
// Default value for parallelism
static const int parallelism= 0;
@@ -84,6 +204,11 @@ static bool ndbcluster_show_status(handlerton *hton, THD*,
static int ndbcluster_alter_tablespace(handlerton *hton,
THD* thd,
st_alter_tablespace *info);
+static int ndbcluster_fill_is_table(handlerton *hton,
+ THD *thd,
+ TABLE_LIST *tables,
+ COND *cond,
+ enum enum_schema_tables);
static int ndbcluster_fill_files_table(handlerton *hton,
THD *thd,
TABLE_LIST *tables,
@@ -127,6 +252,13 @@ static uint ndbcluster_alter_table_flags(uint flags)
DBUG_RETURN(ndb_to_mysql_error(&tmp)); \
}
+#define ERR_RETURN_PREPARE(rc, err) \
+{ \
+ const NdbError& tmp= err; \
+ set_ndb_err(current_thd, tmp); \
+ rc= ndb_to_mysql_error(&tmp); \
+}
+
#define ERR_BREAK(err, code) \
{ \
const NdbError& tmp= err; \
@@ -143,7 +275,7 @@ Ndb_cluster_connection* g_ndb_cluster_connection= NULL;
uchar g_node_id_map[max_ndb_nodes];
/// Handler synchronization
-pthread_mutex_t ndbcluster_mutex;
+mysql_mutex_t ndbcluster_mutex;
/// Table lock handling
HASH ndbcluster_open_tables;
@@ -160,11 +292,10 @@ static int ndb_get_table_statistics(ha_ndbcluster*, bool, Ndb*, const NDBTAB *,
// Util thread variables
pthread_t ndb_util_thread;
int ndb_util_thread_running= 0;
-pthread_mutex_t LOCK_ndb_util_thread;
-pthread_cond_t COND_ndb_util_thread;
-pthread_cond_t COND_ndb_util_ready;
+mysql_mutex_t LOCK_ndb_util_thread;
+mysql_cond_t COND_ndb_util_thread;
+mysql_cond_t COND_ndb_util_ready;
pthread_handler_t ndb_util_thread_func(void *arg);
-ulong ndb_cache_check_time;
/**
Dummy buffer to read zero pack_length fields
@@ -248,11 +379,11 @@ static int ndb_to_mysql_error(const NdbError *ndberr)
- Used by replication to see if the error was temporary
*/
if (ndberr->status == NdbError::TemporaryError)
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_GET_TEMPORARY_ERRMSG, ER(ER_GET_TEMPORARY_ERRMSG),
ndberr->code, ndberr->message, "NDB");
else
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
ndberr->code, ndberr->message, "NDB");
return error;
@@ -299,7 +430,7 @@ int execute_commit(THD *thd, NdbTransaction *trans)
{
return trans->execute(NdbTransaction::Commit,
NdbOperation::AbortOnError,
- thd->variables.ndb_force_send);
+ THDVAR(thd, force_send));
}
inline
@@ -338,8 +469,8 @@ Thd_ndb::Thd_ndb()
m_error_code= 0;
query_state&= NDB_QUERY_NORMAL;
options= 0;
- (void) hash_init(&open_tables, &my_charset_bin, 5, 0, 0,
- (hash_get_key)thd_ndb_share_get_key, 0, 0);
+ (void) my_hash_init(&open_tables, &my_charset_bin, 5, 0, 0,
+ (my_hash_get_key)thd_ndb_share_get_key, 0, 0);
}
Thd_ndb::~Thd_ndb()
@@ -363,7 +494,7 @@ Thd_ndb::~Thd_ndb()
ndb= NULL;
}
changed_tables.empty();
- hash_free(&open_tables);
+ my_hash_free(&open_tables);
}
void
@@ -519,7 +650,7 @@ static void set_ndb_err(THD *thd, const NdbError &err)
{
char buf[FN_REFLEN];
ndb_error_string(thd_ndb->m_error_code, buf, sizeof(buf));
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
thd_ndb->m_error_code, buf, "NDB");
}
@@ -548,7 +679,7 @@ int ha_ndbcluster::ndb_err(NdbTransaction *trans)
bzero((char*) &table_list,sizeof(table_list));
table_list.db= m_dbname;
table_list.alias= table_list.table_name= m_tabname;
- close_cached_tables(thd, &table_list, FALSE, FALSE, FALSE);
+ close_cached_tables(thd, &table_list, FALSE, LONG_TIMEOUT);
break;
}
default:
@@ -904,7 +1035,7 @@ int get_ndb_blobs_value(TABLE* table, NdbValue* value_array,
}
if (loop == 0 && offset > buffer_size)
{
- my_free(buffer, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(buffer);
buffer_size= 0;
DBUG_PRINT("info", ("allocate blobs buffer size %u", offset));
buffer= (uchar*) my_malloc(offset, MYF(MY_WME));
@@ -1057,8 +1188,8 @@ int ha_ndbcluster::get_metadata(const char *path)
if (readfrm(path, &data, &length) ||
packfrm(data, length, &pack_data, &pack_length))
{
- my_free(data, MYF(MY_ALLOW_ZERO_PTR));
- my_free(pack_data, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(data);
+ my_free(pack_data);
DBUG_RETURN(1);
}
@@ -1077,8 +1208,8 @@ int ha_ndbcluster::get_metadata(const char *path)
DBUG_DUMP("frm", (uchar*) tab->getFrmData(), tab->getFrmLength());
error= HA_ERR_TABLE_DEF_CHANGED;
}
- my_free((char*)data, MYF(0));
- my_free((char*)pack_data, MYF(0));
+ my_free(data);
+ my_free(pack_data);
if (error)
goto err;
@@ -1104,7 +1235,7 @@ static int fix_unique_index_attr_order(NDB_INDEX_DATA &data,
unsigned sz= index->getNoOfIndexColumns();
if (data.unique_index_attrid_map)
- my_free((char*)data.unique_index_attrid_map, MYF(0));
+ my_free(data.unique_index_attrid_map);
data.unique_index_attrid_map= (uchar*)my_malloc(sz,MYF(MY_WME));
if (data.unique_index_attrid_map == 0)
{
@@ -1182,7 +1313,7 @@ static void ndb_clear_index(NDB_INDEX_DATA &data)
{
if (data.unique_index_attrid_map)
{
- my_free((char*)data.unique_index_attrid_map, MYF(0));
+ my_free(data.unique_index_attrid_map);
}
if (data.index_stat)
{
@@ -1228,11 +1359,11 @@ int ha_ndbcluster::add_index_handle(THD *thd, NDBDICT *dict, KEY *key_info,
NDB_INDEX_DATA& d=m_index[index_no];
delete d.index_stat;
d.index_stat=NULL;
- if (thd->variables.ndb_index_stat_enable)
+ if (THDVAR(thd, index_stat_enable))
{
d.index_stat=new NdbIndexStat(index);
- d.index_stat_cache_entries=thd->variables.ndb_index_stat_cache_entries;
- d.index_stat_update_freq=thd->variables.ndb_index_stat_update_freq;
+ d.index_stat_cache_entries=THDVAR(thd, index_stat_cache_entries);
+ d.index_stat_update_freq=THDVAR(thd, index_stat_update_freq);
d.index_stat_query_count=0;
d.index_stat->alloc_cache(d.index_stat_cache_entries);
DBUG_PRINT("info", ("index %s stat=on cache_entries=%u update_freq=%u",
@@ -3602,9 +3733,11 @@ int ha_ndbcluster::index_read(uchar *buf,
{
key_range start_key;
bool descending= FALSE;
+ int rc;
DBUG_ENTER("ha_ndbcluster::index_read");
DBUG_PRINT("enter", ("active_index: %u, key_len: %u, find_flag: %d",
active_index, key_len, find_flag));
+ MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
start_key.key= key;
start_key.length= key_len;
@@ -3620,43 +3753,61 @@ int ha_ndbcluster::index_read(uchar *buf,
default:
break;
}
- DBUG_RETURN(read_range_first_to_buf(&start_key, 0, descending,
- m_sorted, buf));
+ rc= read_range_first_to_buf(&start_key, 0, descending,
+ m_sorted, buf);
+ MYSQL_INDEX_READ_ROW_DONE(rc);
+ DBUG_RETURN(rc);
}
int ha_ndbcluster::index_next(uchar *buf)
{
+ int rc;
DBUG_ENTER("ha_ndbcluster::index_next");
+ MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
ha_statistic_increment(&SSV::ha_read_next_count);
- DBUG_RETURN(next_result(buf));
+ rc= next_result(buf);
+ MYSQL_INDEX_READ_ROW_DONE(rc);
+ DBUG_RETURN(rc);
}
int ha_ndbcluster::index_prev(uchar *buf)
{
+ int rc;
DBUG_ENTER("ha_ndbcluster::index_prev");
+ MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
ha_statistic_increment(&SSV::ha_read_prev_count);
- DBUG_RETURN(next_result(buf));
+ rc= next_result(buf);
+ MYSQL_INDEX_READ_ROW_DONE(rc);
+ DBUG_RETURN(rc);
}
int ha_ndbcluster::index_first(uchar *buf)
{
+ int rc;
DBUG_ENTER("ha_ndbcluster::index_first");
+ MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
ha_statistic_increment(&SSV::ha_read_first_count);
// Start the ordered index scan and fetch the first row
// Only HA_READ_ORDER indexes get called by index_first
- DBUG_RETURN(ordered_index_scan(0, 0, TRUE, FALSE, buf, NULL));
+ rc= ordered_index_scan(0, 0, TRUE, FALSE, buf, NULL);
+ MYSQL_INDEX_READ_ROW_DONE(rc);
+ DBUG_RETURN(rc);
}
int ha_ndbcluster::index_last(uchar *buf)
{
+ int rc;
DBUG_ENTER("ha_ndbcluster::index_last");
+ MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
ha_statistic_increment(&SSV::ha_read_last_count);
- DBUG_RETURN(ordered_index_scan(0, 0, TRUE, TRUE, buf, NULL));
+ rc= ordered_index_scan(0, 0, TRUE, TRUE, buf, NULL);
+ MYSQL_INDEX_READ_ROW_DONE(rc);
+ DBUG_RETURN(rc);
}
int ha_ndbcluster::index_read_last(uchar * buf, const uchar * key, uint key_len)
@@ -3748,16 +3899,24 @@ int ha_ndbcluster::read_range_first(const key_range *start_key,
const key_range *end_key,
bool eq_r, bool sorted)
{
+ int rc;
uchar* buf= table->record[0];
DBUG_ENTER("ha_ndbcluster::read_range_first");
- DBUG_RETURN(read_range_first_to_buf(start_key, end_key, FALSE,
- sorted, buf));
+ MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
+ rc= read_range_first_to_buf(start_key, end_key, FALSE,
+ sorted, buf);
+ MYSQL_INDEX_READ_ROW_DONE(rc);
+ DBUG_RETURN(rc);
}
int ha_ndbcluster::read_range_next()
{
+ int rc;
DBUG_ENTER("ha_ndbcluster::read_range_next");
- DBUG_RETURN(next_result(table->record[0]));
+ MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
+ rc= next_result(table->record[0]);
+ MYSQL_INDEX_READ_ROW_DONE(rc);
+ DBUG_RETURN(rc);
}
@@ -3840,12 +3999,18 @@ int ha_ndbcluster::rnd_end()
int ha_ndbcluster::rnd_next(uchar *buf)
{
+ int rc;
DBUG_ENTER("rnd_next");
+ MYSQL_READ_ROW_START(table_share->db.str, table_share->table_name.str,
+ TRUE);
ha_statistic_increment(&SSV::ha_read_rnd_next_count);
if (!m_active_cursor)
- DBUG_RETURN(full_table_scan(buf));
- DBUG_RETURN(next_result(buf));
+ rc= full_table_scan(buf);
+ else
+ rc= next_result(buf);
+ MYSQL_READ_ROW_DONE(rc);
+ DBUG_RETURN(rc);
}
@@ -3857,7 +4022,10 @@ int ha_ndbcluster::rnd_next(uchar *buf)
int ha_ndbcluster::rnd_pos(uchar *buf, uchar *pos)
{
+ int rc;
DBUG_ENTER("rnd_pos");
+ MYSQL_READ_ROW_START(table_share->db.str, table_share->table_name.str,
+ FALSE);
ha_statistic_increment(&SSV::ha_read_rnd_count);
// The primary key for the record is stored in pos
// Perform a pk_read using primary key "index"
@@ -3890,7 +4058,9 @@ int ha_ndbcluster::rnd_pos(uchar *buf, uchar *pos)
DBUG_PRINT("info", ("partition id %u", part_spec.start_part));
}
DBUG_DUMP("key", pos, key_length);
- DBUG_RETURN(pk_read(pos, key_length, buf, part_spec.start_part));
+ rc= pk_read(pos, key_length, buf, part_spec.start_part);
+ MYSQL_READ_ROW_DONE(rc);
+ DBUG_RETURN(rc);
}
}
@@ -4023,7 +4193,7 @@ int ha_ndbcluster::info(uint flag)
{
DBUG_RETURN(my_errno= HA_ERR_OUT_OF_MEM);
}
- if (current_thd->variables.ndb_use_exact_count &&
+ if (THDVAR(current_thd, use_exact_count) &&
(result= ndb_get_table_statistics(this, TRUE, ndb, m_table, &stat))
== 0)
{
@@ -4079,7 +4249,7 @@ int ha_ndbcluster::info(uint flag)
}
-void ha_ndbcluster::get_dynamic_partition_info(PARTITION_INFO *stat_info,
+void ha_ndbcluster::get_dynamic_partition_info(PARTITION_STATS *stat_info,
uint part_id)
{
/*
@@ -4087,7 +4257,7 @@ void ha_ndbcluster::get_dynamic_partition_info(PARTITION_INFO *stat_info,
implement ndb function which retrives the statistics
about ndb partitions.
*/
- bzero((char*) stat_info, sizeof(PARTITION_INFO));
+ bzero((char*) stat_info, sizeof(PARTITION_STATS));
return;
}
@@ -4262,7 +4432,7 @@ int ha_ndbcluster::end_bulk_insert()
}
else
{
- IF_DBUG(int res=) trans->restart();
+ int res __attribute__((unused))= trans->restart();
DBUG_ASSERT(res == 0);
}
}
@@ -4353,12 +4523,12 @@ THR_LOCK_DATA **ha_ndbcluster::store_lock(THD *thd,
#ifndef DBUG_OFF
#define PRINT_OPTION_FLAGS(t) { \
- if (t->options & OPTION_NOT_AUTOCOMMIT) \
- DBUG_PRINT("thd->options", ("OPTION_NOT_AUTOCOMMIT")); \
- if (t->options & OPTION_BEGIN) \
- DBUG_PRINT("thd->options", ("OPTION_BEGIN")); \
- if (t->options & OPTION_TABLE_LOCK) \
- DBUG_PRINT("thd->options", ("OPTION_TABLE_LOCK")); \
+ if (t->variables.option_bits & OPTION_NOT_AUTOCOMMIT) \
+ DBUG_PRINT("thd->variables.option_bits", ("OPTION_NOT_AUTOCOMMIT")); \
+ if (t->variables.option_bits & OPTION_BEGIN) \
+ DBUG_PRINT("thd->variables.option_bits", ("OPTION_BEGIN")); \
+ if (t->variables.option_bits & OPTION_TABLE_LOCK) \
+ DBUG_PRINT("thd->variables.option_bits", ("OPTION_TABLE_LOCK")); \
}
#else
#define PRINT_OPTION_FLAGS(t)
@@ -4445,7 +4615,7 @@ void ha_ndbcluster::transaction_checks(THD *thd)
else if (!thd->transaction.on)
m_transaction_on= FALSE;
else
- m_transaction_on= thd->variables.ndb_use_transactions;
+ m_transaction_on= THDVAR(thd, use_transactions);
}
int ha_ndbcluster::start_statement(THD *thd,
@@ -4458,7 +4628,7 @@ int ha_ndbcluster::start_statement(THD *thd,
trans_register_ha(thd, FALSE, ndbcluster_hton);
if (!thd_ndb->trans)
{
- if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
+ if (thd->in_multi_stmt_transaction_mode())
trans_register_ha(thd, TRUE, ndbcluster_hton);
DBUG_PRINT("trans",("Starting transaction"));
thd_ndb->trans= ndb->startTransaction();
@@ -4468,7 +4638,7 @@ int ha_ndbcluster::start_statement(THD *thd,
thd_ndb->query_state&= NDB_QUERY_NORMAL;
thd_ndb->trans_options= 0;
thd_ndb->m_slow_path= FALSE;
- if (!(thd->options & OPTION_BIN_LOG) ||
+ if (!(thd->variables.option_bits & OPTION_BIN_LOG) ||
thd->variables.binlog_format == BINLOG_FORMAT_STMT)
{
thd_ndb->trans_options|= TNTO_NO_LOGGING;
@@ -4483,7 +4653,7 @@ int ha_ndbcluster::start_statement(THD *thd,
Check if it should be read or write lock
*/
- if (thd->options & (OPTION_TABLE_LOCK))
+ if (thd->variables.option_bits & OPTION_TABLE_LOCK)
{
//lockThisTable();
DBUG_PRINT("info", ("Locking the table..." ));
@@ -4507,13 +4677,13 @@ int ha_ndbcluster::init_handler_for_statement(THD *thd, Thd_ndb *thd_ndb)
DBUG_ENTER("ha_ndbcluster::init_handler_for_statement");
// store thread specific data first to set the right context
- m_force_send= thd->variables.ndb_force_send;
- m_ha_not_exact_count= !thd->variables.ndb_use_exact_count;
+ m_force_send= THDVAR(thd, force_send);
+ m_ha_not_exact_count= !THDVAR(thd, use_exact_count);
m_autoincrement_prefetch=
- (thd->variables.ndb_autoincrement_prefetch_sz >
- NDB_DEFAULT_AUTO_PREFETCH) ?
- (ha_rows) thd->variables.ndb_autoincrement_prefetch_sz
- : (ha_rows) NDB_DEFAULT_AUTO_PREFETCH;
+ (THDVAR(thd, autoincrement_prefetch_sz) >
+ DEFAULT_AUTO_PREFETCH) ?
+ (ha_rows) THDVAR(thd, autoincrement_prefetch_sz)
+ : (ha_rows) DEFAULT_AUTO_PREFETCH;
m_active_trans= thd_ndb->trans;
DBUG_ASSERT(m_active_trans);
// Start of transaction
@@ -4528,14 +4698,14 @@ int ha_ndbcluster::init_handler_for_statement(THD *thd, Thd_ndb *thd_ndb)
}
#endif
- if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
+ if (thd->in_multi_stmt_transaction_mode())
{
const void *key= m_table;
HASH_SEARCH_STATE state;
THD_NDB_SHARE *thd_ndb_share=
- (THD_NDB_SHARE*)hash_first(&thd_ndb->open_tables, (uchar *)&key, sizeof(key), &state);
+ (THD_NDB_SHARE*)my_hash_first(&thd_ndb->open_tables, (uchar *)&key, sizeof(key), &state);
while (thd_ndb_share && thd_ndb_share->key != key)
- thd_ndb_share= (THD_NDB_SHARE*)hash_next(&thd_ndb->open_tables, (uchar *)&key, sizeof(key), &state);
+ thd_ndb_share= (THD_NDB_SHARE*)my_hash_next(&thd_ndb->open_tables, (uchar *)&key, sizeof(key), &state);
if (thd_ndb_share == 0)
{
thd_ndb_share= (THD_NDB_SHARE *) alloc_root(&thd->transaction.mem_root,
@@ -4609,21 +4779,21 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type)
{
DBUG_PRINT("info", ("lock_type == F_UNLCK"));
- if (ndb_cache_check_time && m_rows_changed)
+ if (opt_ndb_cache_check_time && m_rows_changed)
{
DBUG_PRINT("info", ("Rows has changed and util thread is running"));
- if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
+ if (thd->in_multi_stmt_transaction_mode())
{
DBUG_PRINT("info", ("Add share to list of tables to be invalidated"));
/* NOTE push_back allocates memory using transactions mem_root! */
thd_ndb->changed_tables.push_back(m_share, &thd->transaction.mem_root);
}
- pthread_mutex_lock(&m_share->mutex);
+ mysql_mutex_lock(&m_share->mutex);
DBUG_PRINT("info", ("Invalidating commit_count"));
m_share->commit_count= 0;
m_share->commit_count_lock++;
- pthread_mutex_unlock(&m_share->mutex);
+ mysql_mutex_unlock(&m_share->mutex);
}
if (!--thd_ndb->lock_count)
@@ -4631,7 +4801,7 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type)
DBUG_PRINT("trans", ("Last external_lock"));
PRINT_OPTION_FLAGS(thd);
- if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
+ if (!thd->in_multi_stmt_transaction_mode())
{
if (thd_ndb->trans)
{
@@ -4741,8 +4911,7 @@ static int ndbcluster_commit(handlerton *hton, THD *thd, bool all)
PRINT_OPTION_FLAGS(thd);
DBUG_PRINT("enter", ("Commit %s", (all ? "all" : "stmt")));
thd_ndb->start_stmt_count= 0;
- if (trans == NULL || (!all &&
- thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
+ if (trans == NULL || (!all && thd->in_multi_stmt_transaction_mode()))
{
/*
An odditity in the handler interface is that commit on handlerton
@@ -4784,12 +4953,12 @@ static int ndbcluster_commit(handlerton *hton, THD *thd, bool all)
List_iterator_fast<NDB_SHARE> it(thd_ndb->changed_tables);
while ((share= it++))
{
- pthread_mutex_lock(&share->mutex);
+ mysql_mutex_lock(&share->mutex);
DBUG_PRINT("info", ("Invalidate commit_count for %s, share->commit_count: %lu",
share->table_name, (ulong) share->commit_count));
share->commit_count= 0;
share->commit_count_lock++;
- pthread_mutex_unlock(&share->mutex);
+ mysql_mutex_unlock(&share->mutex);
}
thd_ndb->changed_tables.empty();
@@ -4812,7 +4981,7 @@ static int ndbcluster_rollback(handlerton *hton, THD *thd, bool all)
DBUG_ASSERT(ndb);
thd_ndb->start_stmt_count= 0;
if (trans == NULL || (!all &&
- thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
+ thd->in_multi_stmt_transaction_mode()))
{
/* Ignore end-of-statement until real rollback or commit is called */
DBUG_PRINT("info", ("Rollback before start or end-of-statement only"));
@@ -5230,15 +5399,15 @@ int ha_ndbcluster::create(const char *name,
DBUG_RETURN(1);
if (packfrm(data, length, &pack_data, &pack_length))
{
- my_free((char*)data, MYF(0));
+ my_free(data);
DBUG_RETURN(2);
}
DBUG_PRINT("info",
("setFrm data: 0x%lx len: %lu", (long) pack_data,
(ulong) pack_length));
tab.setFrm(pack_data, pack_length);
- my_free((char*)data, MYF(0));
- my_free((char*)pack_data, MYF(0));
+ my_free(data);
+ my_free(pack_data);
/*
Check for disk options
@@ -5254,7 +5423,7 @@ int ha_ndbcluster::create(const char *name,
{
if (create_info->storage_media == HA_SM_MEMORY)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_ILLEGAL_HA_CREATE_OPTION,
ER(ER_ILLEGAL_HA_CREATE_OPTION),
ndbcluster_hton_name,
@@ -5309,7 +5478,7 @@ int ha_ndbcluster::create(const char *name,
case ROW_TYPE_FIXED:
if (field_type_forces_var_part(field->type()))
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_ILLEGAL_HA_CREATE_OPTION,
ER(ER_ILLEGAL_HA_CREATE_OPTION),
ndbcluster_hton_name,
@@ -5463,14 +5632,14 @@ int ha_ndbcluster::create(const char *name,
if (!my_errno)
{
NDB_SHARE *share= 0;
- pthread_mutex_lock(&ndbcluster_mutex);
+ mysql_mutex_lock(&ndbcluster_mutex);
/*
First make sure we get a "fresh" share here, not an old trailing one...
*/
{
uint length= (uint) strlen(name);
- if ((share= (NDB_SHARE*) hash_search(&ndbcluster_open_tables,
- (uchar*) name, length)))
+ if ((share= (NDB_SHARE*) my_hash_search(&ndbcluster_open_tables,
+ (uchar*) name, length)))
handle_trailing_share(share);
}
/*
@@ -5488,7 +5657,7 @@ int ha_ndbcluster::create(const char *name,
DBUG_PRINT("NDB_SHARE", ("%s binlog create use_count: %u",
share->key, share->use_count));
}
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
while (!IS_TMP_PREFIX(m_tabname))
{
@@ -5508,7 +5677,7 @@ int ha_ndbcluster::create(const char *name,
if (!ndbcluster_create_event(ndb, m_table, event_name.c_ptr(), share,
share && do_event_op ? 2 : 1/* push warning */))
{
- if (ndb_extra_logging)
+ if (opt_ndb_extra_logging)
sql_print_information("NDB Binlog: CREATE TABLE Event: %s",
event_name.c_ptr());
if (share &&
@@ -5532,7 +5701,7 @@ int ha_ndbcluster::create(const char *name,
m_table->getObjectVersion(),
(is_truncate) ?
SOT_TRUNCATE_TABLE : SOT_CREATE_TABLE,
- 0, 0, 1);
+ 0, 0);
break;
}
}
@@ -5582,8 +5751,8 @@ int ha_ndbcluster::create_handler_files(const char *file,
packfrm(data, length, &pack_data, &pack_length))
{
DBUG_PRINT("info", ("Missing frm for %s", m_tabname));
- my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
- my_free((char*)pack_data, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(data);
+ my_free(pack_data);
error= 1;
}
else
@@ -5597,8 +5766,8 @@ int ha_ndbcluster::create_handler_files(const char *file,
set_ndb_err(current_thd, dict->getNdbError());
error= ndb_to_mysql_error(&dict->getNdbError());
}
- my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
- my_free((char*)pack_data, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(data);
+ my_free(pack_data);
}
set_ndb_share_state(m_share, NSS_INITIAL);
@@ -5649,7 +5818,7 @@ int ha_ndbcluster::create_index(const char *name, KEY *key_info,
case ORDERED_INDEX:
if (key_info->algorithm == HA_KEY_ALG_HASH)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_ILLEGAL_HA_CREATE_OPTION,
ER(ER_ILLEGAL_HA_CREATE_OPTION),
ndbcluster_hton_name,
@@ -5874,7 +6043,7 @@ int ha_ndbcluster::rename_table(const char *from, const char *to)
{
DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u",
share->key, share->use_count));
- IF_DBUG(int r=) rename_share(share, to);
+ int r __attribute__((unused))= rename_share(share, to);
DBUG_ASSERT(r == 0);
}
#endif
@@ -5898,7 +6067,7 @@ int ha_ndbcluster::rename_table(const char *from, const char *to)
#ifdef HAVE_NDB_BINLOG
if (share)
{
- IF_DBUG(int ret=) rename_share(share, from);
+ int ret __attribute__((unused))= rename_share(share, from);
DBUG_ASSERT(ret == 0);
/* ndb_share reference temporary free */
DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
@@ -5951,7 +6120,7 @@ int ha_ndbcluster::rename_table(const char *from, const char *to)
if (!ndbcluster_create_event(ndb, ndbtab, event_name.c_ptr(), share,
share && ndb_binlog_running ? 2 : 1/* push warning */))
{
- if (ndb_extra_logging)
+ if (opt_ndb_extra_logging)
sql_print_information("NDB Binlog: RENAME Event: %s",
event_name.c_ptr());
if (share &&
@@ -5973,7 +6142,7 @@ int ha_ndbcluster::rename_table(const char *from, const char *to)
old_dbname, m_tabname,
ndb_table_id, ndb_table_version,
SOT_RENAME_TABLE,
- m_dbname, new_tabname, 1);
+ m_dbname, new_tabname);
}
// If we are moving tables between databases, we need to recreate
@@ -6127,7 +6296,7 @@ retry_temporary_error1:
/* the drop table failed for some reason, drop the share anyways */
if (share)
{
- pthread_mutex_lock(&ndbcluster_mutex);
+ mysql_mutex_lock(&ndbcluster_mutex);
if (share->state != NSS_DROPPED)
{
/*
@@ -6143,7 +6312,7 @@ retry_temporary_error1:
DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
share->key, share->use_count));
free_share(&share, TRUE);
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
}
#endif
DBUG_RETURN(res);
@@ -6167,7 +6336,7 @@ retry_temporary_error1:
thd->query(), thd->query_length(),
share->db, share->table_name,
ndb_table_id, ndb_table_version,
- SOT_DROP_TABLE, 0, 0, 1);
+ SOT_DROP_TABLE, 0, 0);
}
else if (table_dropped && share && share->op) /* ndbcluster_log_schema_op
will do a force GCP */
@@ -6184,7 +6353,7 @@ retry_temporary_error1:
if (share)
{
- pthread_mutex_lock(&ndbcluster_mutex);
+ mysql_mutex_lock(&ndbcluster_mutex);
if (share->state != NSS_DROPPED)
{
/*
@@ -6200,7 +6369,7 @@ retry_temporary_error1:
DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
share->key, share->use_count));
free_share(&share, TRUE);
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
}
#endif
DBUG_RETURN(0);
@@ -6254,10 +6423,9 @@ void ha_ndbcluster::get_auto_increment(ulonglong offset, ulonglong increment,
m_rows_to_insert+= m_autoincrement_prefetch;
}
uint remaining= m_rows_to_insert - m_rows_inserted;
+ ha_rows prefetch= THDVAR(thd, autoincrement_prefetch_sz);
uint min_prefetch=
- (remaining < thd->variables.ndb_autoincrement_prefetch_sz) ?
- thd->variables.ndb_autoincrement_prefetch_sz
- : remaining;
+ (remaining < prefetch) ? prefetch : remaining;
cache_size= ((remaining < m_autoincrement_prefetch) ?
min_prefetch
: remaining);
@@ -6267,7 +6435,7 @@ void ha_ndbcluster::get_auto_increment(ulonglong offset, ulonglong increment,
{
Ndb_tuple_id_range_guard g(m_share);
if ((m_skip_auto_increment &&
- ndb->readAutoIncrementValue(m_table, g.range, auto_value)) ||
+ ndb->readAutoIncrementValue(m_table, g.range, auto_value)) ||
ndb->getAutoIncrementValue(m_table, g.range, auto_value, cache_size, increment, offset))
{
if (--retries &&
@@ -6347,7 +6515,7 @@ ha_ndbcluster::ha_ndbcluster(handlerton *hton, TABLE_SHARE *table_arg):
m_dupkey((uint) -1),
m_ha_not_exact_count(FALSE),
m_force_send(TRUE),
- m_autoincrement_prefetch((ha_rows) NDB_DEFAULT_AUTO_PREFETCH),
+ m_autoincrement_prefetch(DEFAULT_AUTO_PREFETCH),
m_transaction_on(TRUE),
m_cond(NULL),
m_multi_cursor(NULL)
@@ -6397,7 +6565,7 @@ ha_ndbcluster::~ha_ndbcluster()
free_share(&m_share);
}
release_metadata(thd, ndb);
- my_free(m_blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(m_blobs_buffer);
m_blobs_buffer= 0;
// Check for open cursor/transaction
@@ -6743,7 +6911,7 @@ int ndbcluster_discover(handlerton *hton, THD* thd, const char *db,
DBUG_RETURN(0);
err:
- my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(data);
if (share)
{
/* ndb_share reference temporary free */
@@ -6850,7 +7018,6 @@ int ndbcluster_drop_database_impl(const char *path)
while ((tabname=it++))
{
tablename_to_filename(tabname, tmp, FN_REFLEN - (tmp - full_path)-1);
- VOID(pthread_mutex_lock(&LOCK_open));
if (ha_ndbcluster::delete_table(0, ndb, full_path, dbname, tabname))
{
const NdbError err= dict->getNdbError();
@@ -6860,7 +7027,6 @@ int ndbcluster_drop_database_impl(const char *path)
ret= ndb_to_mysql_error(&err);
}
}
- VOID(pthread_mutex_unlock(&LOCK_open));
}
DBUG_RETURN(ret);
}
@@ -6887,7 +7053,7 @@ static void ndbcluster_drop_database(handlerton *hton, char *path)
ha_ndbcluster::set_dbname(path, db);
ndbcluster_log_schema_op(thd, 0,
thd->query(), thd->query_length(),
- db, "", 0, 0, SOT_DROP_DB, 0, 0, 0);
+ db, "", 0, 0, SOT_DROP_DB, 0, 0);
#endif
DBUG_VOID_RETURN;
}
@@ -6898,7 +7064,6 @@ int ndb_create_table_from_engine(THD *thd, const char *db,
LEX *old_lex= thd->lex, newlex;
thd->lex= &newlex;
newlex.current_select= NULL;
- lex_start(thd);
int res= ha_create_table_from_engine(thd, db, table_name);
thd->lex= old_lex;
return res;
@@ -7010,10 +7175,9 @@ int ndbcluster_find_all_files(THD *thd)
free_share(&share);
}
}
- my_free((char*) data, MYF(MY_ALLOW_ZERO_PTR));
- my_free((char*) pack_data, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(data);
+ my_free(pack_data);
- pthread_mutex_lock(&LOCK_open);
if (discover)
{
/* ToDo 4.1 database needs to be created if missing */
@@ -7031,7 +7195,6 @@ int ndbcluster_find_all_files(THD *thd)
TRUE);
}
#endif
- pthread_mutex_unlock(&LOCK_open);
}
}
while (unhandled && retries);
@@ -7065,18 +7228,18 @@ int ndbcluster_find_files(handlerton *hton, THD *thd,
NdbDictionary::Object::UserTable) != 0)
ERR_RETURN(dict->getNdbError());
- if (hash_init(&ndb_tables, system_charset_info,list.count,0,0,
- (hash_get_key)tables_get_key,0,0))
+ if (my_hash_init(&ndb_tables, system_charset_info,list.count,0,0,
+ (my_hash_get_key)tables_get_key,0,0))
{
DBUG_PRINT("error", ("Failed to init HASH ndb_tables"));
DBUG_RETURN(-1);
}
- if (hash_init(&ok_tables, system_charset_info,32,0,0,
- (hash_get_key)tables_get_key,0,0))
+ if (my_hash_init(&ok_tables, system_charset_info,32,0,0,
+ (my_hash_get_key)tables_get_key,0,0))
{
DBUG_PRINT("error", ("Failed to init HASH ok_tables"));
- hash_free(&ndb_tables);
+ my_hash_free(&ndb_tables);
DBUG_RETURN(-1);
}
@@ -7117,25 +7280,23 @@ int ndbcluster_find_files(handlerton *hton, THD *thd,
{
bool file_on_disk= FALSE;
DBUG_PRINT("info", ("%s", file_name->str));
- if (hash_search(&ndb_tables, (uchar*) file_name->str, file_name->length))
+ if (my_hash_search(&ndb_tables, (uchar*) file_name->str,
+ file_name->length))
{
build_table_filename(name, sizeof(name) - 1, db,
file_name->str, reg_ext, 0);
if (my_access(name, F_OK))
{
- pthread_mutex_lock(&LOCK_open);
DBUG_PRINT("info", ("Table %s listed and need discovery",
file_name->str));
if (ndb_create_table_from_engine(thd, db, file_name->str))
{
- pthread_mutex_unlock(&LOCK_open);
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TABLE_EXISTS_ERROR,
"Discover of table %s.%s failed",
db, file_name->str);
continue;
}
- pthread_mutex_unlock(&LOCK_open);
}
DBUG_PRINT("info", ("%s existed in NDB _and_ on disk ", file_name->str));
file_on_disk= TRUE;
@@ -7152,10 +7313,10 @@ int ndbcluster_find_files(handlerton *hton, THD *thd,
if (file_on_disk)
{
// Ignore this ndb table
- uchar *record= hash_search(&ndb_tables, (uchar*) file_name->str,
- file_name->length);
+ uchar *record= my_hash_search(&ndb_tables, (uchar*) file_name->str,
+ file_name->length);
DBUG_ASSERT(record);
- hash_delete(&ndb_tables, record);
+ my_hash_delete(&ndb_tables, record);
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TABLE_EXISTS_ERROR,
"Local table %s.%s shadows ndb table",
@@ -7189,13 +7350,11 @@ int ndbcluster_find_files(handlerton *hton, THD *thd,
build_table_filename(name, sizeof(name) - 1, db, "", "", 0);
for (i= 0; i < ok_tables.records; i++)
{
- file_name_str= (char*)hash_element(&ok_tables, i);
+ file_name_str= (char*)my_hash_element(&ok_tables, i);
end= end1 +
tablename_to_filename(file_name_str, end1, sizeof(name) - (end1 - name));
- pthread_mutex_lock(&LOCK_open);
ndbcluster_create_binlog_setup(ndb, name, end-name,
db, file_name_str, TRUE);
- pthread_mutex_unlock(&LOCK_open);
}
}
#endif
@@ -7205,8 +7364,9 @@ int ndbcluster_find_files(handlerton *hton, THD *thd,
List<char> create_list;
for (i= 0 ; i < ndb_tables.records ; i++)
{
- file_name_str= (char*) hash_element(&ndb_tables, i);
- if (!hash_search(&ok_tables, (uchar*) file_name_str, strlen(file_name_str)))
+ file_name_str= (char*) my_hash_element(&ndb_tables, i);
+ if (!my_hash_search(&ok_tables, (uchar*) file_name_str,
+ strlen(file_name_str)))
{
build_table_filename(name, sizeof(name) - 1,
db, file_name_str, reg_ext, 0);
@@ -7220,31 +7380,40 @@ int ndbcluster_find_files(handlerton *hton, THD *thd,
}
}
- if (!global_read_lock)
- {
- // Delete old files
- List_iterator_fast<char> it3(delete_list);
- while ((file_name_str= it3++))
- {
- DBUG_PRINT("info", ("Remove table %s/%s", db, file_name_str));
- // Delete the table and all related files
- TABLE_LIST table_list;
- bzero((char*) &table_list,sizeof(table_list));
- table_list.db= (char*) db;
- table_list.alias= table_list.table_name= (char*)file_name_str;
- (void)mysql_rm_table_part2(thd, &table_list,
- FALSE, /* if_exists */
- FALSE, /* drop_temporary */
- FALSE, /* drop_view */
- TRUE /* dont_log_query*/);
+ /*
+ Delete old files.
- /* Clear error message that is returned when table is deleted */
- thd->clear_error();
- }
- }
+ ndbcluster_find_files() may be called from I_S code and ndbcluster_binlog
+ thread in situations when some tables are already open. This means that
+ code below will try to obtain exclusive metadata lock on some table
+ while holding shared meta-data lock on other tables. This might lead to a
+ deadlock but such a deadlock should be detected by MDL deadlock detector.
- pthread_mutex_lock(&LOCK_open);
- // Create new files
+ XXX: the scenario described above is not covered with any test.
+ */
+ List_iterator_fast<char> it3(delete_list);
+ while ((file_name_str= it3++))
+ {
+ DBUG_PRINT("info", ("Remove table %s/%s", db, file_name_str));
+ /* Delete the table and all related files. */
+ TABLE_LIST table_list;
+ table_list.init_one_table(db, strlen(db), file_name_str,
+ strlen(file_name_str), file_name_str,
+ TL_WRITE);
+ table_list.mdl_request.set_type(MDL_EXCLUSIVE);
+ (void)mysql_rm_table_part2(thd, &table_list,
+ FALSE, /* if_exists */
+ FALSE, /* drop_temporary */
+ FALSE, /* drop_view */
+ TRUE /* dont_log_query*/);
+ trans_commit_implicit(thd); /* Safety, should be unnecessary. */
+ thd->mdl_context.release_transactional_locks();
+ /* Clear error message that is returned when table is deleted */
+ thd->clear_error();
+ }
+
+ /* Lock mutex before creating .FRM files. */
+ /* Create new files. */
List_iterator_fast<char> it2(create_list);
while ((file_name_str=it2++))
{
@@ -7258,10 +7427,8 @@ int ndbcluster_find_files(handlerton *hton, THD *thd,
}
}
- pthread_mutex_unlock(&LOCK_open);
-
- hash_free(&ok_tables);
- hash_free(&ndb_tables);
+ my_hash_free(&ok_tables);
+ my_hash_free(&ndb_tables);
// Delete schema file from files
if (!strcmp(db, NDB_REP_DB))
@@ -7292,7 +7459,7 @@ int ndbcluster_find_files(handlerton *hton, THD *thd,
/* Call back after cluster connect */
static int connect_callback()
{
- pthread_mutex_lock(&LOCK_ndb_util_thread);
+ mysql_mutex_lock(&LOCK_ndb_util_thread);
update_status_variables(g_ndb_cluster_connection);
uint node_id, i= 0;
@@ -7301,13 +7468,91 @@ static int connect_callback()
while ((node_id= g_ndb_cluster_connection->get_next_node(node_iter)))
g_node_id_map[node_id]= i++;
- pthread_cond_signal(&COND_ndb_util_thread);
- pthread_mutex_unlock(&LOCK_ndb_util_thread);
+ mysql_cond_signal(&COND_ndb_util_thread);
+ mysql_mutex_unlock(&LOCK_ndb_util_thread);
return 0;
}
extern int ndb_dictionary_is_mysqld;
-extern pthread_mutex_t LOCK_plugin;
+
+#ifdef HAVE_PSI_INTERFACE
+
+#ifdef HAVE_NDB_BINLOG
+PSI_mutex_key key_injector_mutex, key_ndb_schema_share_mutex,
+ key_ndb_schema_object_mutex;
+#endif /* HAVE_NDB_BINLOG */
+
+PSI_mutex_key key_NDB_SHARE_mutex, key_ndbcluster_mutex,
+ key_LOCK_ndb_util_thread;
+
+static PSI_mutex_info all_ndbcluster_mutexes[]=
+{
+#ifdef HAVE_NDB_BINLOG
+ {& key_injector_mutex, "injector_mutex", PSI_FLAG_GLOBAL},
+ {& key_ndb_schema_share_mutex, "ndb_schema_share_mutex", PSI_FLAG_GLOBAL},
+ {& key_ndb_schema_object_mutex, "ndb_schema_object_mutex", PSI_FLAG_GLOBAL},
+#endif /* HAVE_NDB_BINLOG */
+ {& key_NDB_SHARE_mutex, "NDB_SHARE::mutex", PSI_FLAG_GLOBAL},
+ {& key_ndbcluster_mutex, "ndbcluster_mutex", PSI_FLAG_GLOBAL},
+ {& key_LOCK_ndb_util_thread, "LOCK_ndb_util_thread", PSI_FLAG_GLOBAL}
+};
+
+#ifdef HAVE_NDB_BINLOG
+PSI_cond_key key_injector_cond;
+#endif /* HAVE_NDB_BINLOG */
+
+PSI_cond_key key_COND_ndb_util_thread, key_COND_ndb_util_ready;
+
+static PSI_cond_info all_ndbcluster_conds[]=
+{
+#ifdef HAVE_NDB_BINLOG
+ {& key_injector_cond, "injector_cond", PSI_FLAG_GLOBAL},
+#endif /* HAVE_NDB_BINLOG */
+ {& key_COND_ndb_util_thread, "COND_ndb_util_thread", PSI_FLAG_GLOBAL},
+ {& key_COND_ndb_util_ready, "COND_ndb_util_ready", PSI_FLAG_GLOBAL}
+};
+
+#ifdef HAVE_NDB_BINLOG
+PSI_thread_key key_thread_ndb_binlog;
+#endif /* HAVE_NDB_BINLOG */
+PSI_thread_key key_thread_ndb_util;
+
+static PSI_thread_info all_ndbcluster_threads[]=
+{
+#ifdef HAVE_NDB_BINLOG
+ { &key_thread_ndb_binlog, "ndb_binlog", PSI_FLAG_GLOBAL},
+#endif /* HAVE_NDB_BINLOG */
+ { &key_thread_ndb_util, "ndb_util", PSI_FLAG_GLOBAL}
+};
+
+PSI_file_key key_file_ndb;
+
+static PSI_file_info all_ndbcluster_files[]=
+{
+ { &key_file_ndb, "ndb", 0}
+};
+
+void init_ndbcluster_psi_keys()
+{
+ const char* category= "ndbcluster";
+ int count;
+
+ if (PSI_server == NULL)
+ return;
+
+ count= array_elements(all_ndbcluster_mutexes);
+ PSI_server->register_mutex(category, all_ndbcluster_mutexes, count);
+
+ count= array_elements(all_ndbcluster_conds);
+ PSI_server->register_cond(category, all_ndbcluster_conds, count);
+
+ count= array_elements(all_ndbcluster_threads);
+ PSI_server->register_thread(category, all_ndbcluster_threads, count);
+
+ count= array_elements(all_ndbcluster_files);
+ PSI_server->register_file(category, all_ndbcluster_files, count);
+}
+#endif /* HAVE_PSI_INTERFACE */
static int ndbcluster_init(void *p)
{
@@ -7317,10 +7562,16 @@ static int ndbcluster_init(void *p)
if (ndbcluster_inited)
DBUG_RETURN(FALSE);
- pthread_mutex_init(&ndbcluster_mutex,MY_MUTEX_INIT_FAST);
- pthread_mutex_init(&LOCK_ndb_util_thread, MY_MUTEX_INIT_FAST);
- pthread_cond_init(&COND_ndb_util_thread, NULL);
- pthread_cond_init(&COND_ndb_util_ready, NULL);
+#ifdef HAVE_PSI_INTERFACE
+ init_ndbcluster_psi_keys();
+#endif
+
+ mysql_mutex_init(key_ndbcluster_mutex,
+ &ndbcluster_mutex, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_ndb_util_thread,
+ &LOCK_ndb_util_thread, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_COND_ndb_util_thread, &COND_ndb_util_thread, NULL);
+ mysql_cond_init(key_COND_ndb_util_ready, &COND_ndb_util_ready, NULL);
ndb_util_thread_running= -1;
ndbcluster_terminating= 0;
ndb_dictionary_is_mysqld= 1;
@@ -7340,7 +7591,7 @@ static int ndbcluster_init(void *p)
h->alter_tablespace= ndbcluster_alter_tablespace; /* Show status */
h->partition_flags= ndbcluster_partition_flags; /* Partition flags */
h->alter_table_flags=ndbcluster_alter_table_flags; /* Alter table flags */
- h->fill_files_table= ndbcluster_fill_files_table;
+ h->fill_is_table= ndbcluster_fill_is_table;
#ifdef HAVE_NDB_BINLOG
ndbcluster_binlog_init_handlerton();
#endif
@@ -7350,17 +7601,31 @@ static int ndbcluster_init(void *p)
h->table_exists_in_engine= ndbcluster_table_exists_in_engine;
}
+ // Format the connect string to be used for connecting to the cluster
+ int pos= 0;
+ char connectstring_buf[1024] = {0};
+ if (opt_ndb_nodeid != 0)
+ pos+= my_snprintf(connectstring_buf, sizeof(connectstring_buf),
+ "nodeid=%u", opt_ndb_nodeid);
+ if (opt_ndb_mgmd_host)
+ pos+= my_snprintf(connectstring_buf+pos, sizeof(connectstring_buf)-pos,
+ "%s%s", pos ? "," : "", opt_ndb_mgmd_host);
+ if (opt_ndb_connectstring)
+ pos+= my_snprintf(connectstring_buf+pos, sizeof(connectstring_buf)-pos,
+ "%s%s", pos ? "," : "", opt_ndb_connectstring);
+
+
// Initialize ndb interface
ndb_init_internal();
// Set connectstring if specified
- if (opt_ndbcluster_connectstring != 0)
- DBUG_PRINT("connectstring", ("%s", opt_ndbcluster_connectstring));
+ if (opt_ndb_connectstring != 0)
+ DBUG_PRINT("connectstring", ("%s", opt_ndb_connectstring));
if ((g_ndb_cluster_connection=
- new Ndb_cluster_connection(opt_ndbcluster_connectstring)) == 0)
+ new Ndb_cluster_connection(opt_ndb_connectstring)) == 0)
{
DBUG_PRINT("error",("Ndb_cluster_connection(%s)",
- opt_ndbcluster_connectstring));
+ opt_ndb_connectstring));
my_errno= HA_ERR_OUT_OF_MEM;
goto ndbcluster_init_error;
}
@@ -7370,7 +7635,7 @@ static int ndbcluster_init(void *p)
g_ndb_cluster_connection->set_name(buf);
}
g_ndb_cluster_connection->set_optimized_node_selection
- (opt_ndb_optimized_node_selection);
+ (THDVAR(0, optimized_node_selection));
// Create a Ndb object to open the connection to NDB
if ( (g_ndb= new Ndb(g_ndb_cluster_connection, "sys")) == 0 )
@@ -7418,42 +7683,42 @@ static int ndbcluster_init(void *p)
goto ndbcluster_init_error;
}
- (void) hash_init(&ndbcluster_open_tables,system_charset_info,32,0,0,
- (hash_get_key) ndbcluster_get_key,0,0);
+ (void) my_hash_init(&ndbcluster_open_tables,system_charset_info,32,0,0,
+ (my_hash_get_key) ndbcluster_get_key,0,0);
#ifdef HAVE_NDB_BINLOG
/* start the ndb injector thread */
if (ndbcluster_binlog_start())
goto ndbcluster_init_error;
#endif /* HAVE_NDB_BINLOG */
- ndb_cache_check_time = opt_ndb_cache_check_time;
// Create utility thread
pthread_t tmp;
- if (pthread_create(&tmp, &connection_attrib, ndb_util_thread_func, 0))
+ if (mysql_thread_create(key_thread_ndb_util,
+ &tmp, &connection_attrib, ndb_util_thread_func, 0))
{
DBUG_PRINT("error", ("Could not create ndb utility thread"));
- hash_free(&ndbcluster_open_tables);
- pthread_mutex_destroy(&ndbcluster_mutex);
- pthread_mutex_destroy(&LOCK_ndb_util_thread);
- pthread_cond_destroy(&COND_ndb_util_thread);
- pthread_cond_destroy(&COND_ndb_util_ready);
+ my_hash_free(&ndbcluster_open_tables);
+ mysql_mutex_destroy(&ndbcluster_mutex);
+ mysql_mutex_destroy(&LOCK_ndb_util_thread);
+ mysql_cond_destroy(&COND_ndb_util_thread);
+ mysql_cond_destroy(&COND_ndb_util_ready);
goto ndbcluster_init_error;
}
/* Wait for the util thread to start */
- pthread_mutex_lock(&LOCK_ndb_util_thread);
+ mysql_mutex_lock(&LOCK_ndb_util_thread);
while (ndb_util_thread_running < 0)
- pthread_cond_wait(&COND_ndb_util_ready, &LOCK_ndb_util_thread);
- pthread_mutex_unlock(&LOCK_ndb_util_thread);
+ mysql_cond_wait(&COND_ndb_util_ready, &LOCK_ndb_util_thread);
+ mysql_mutex_unlock(&LOCK_ndb_util_thread);
if (!ndb_util_thread_running)
{
DBUG_PRINT("error", ("ndb utility thread exited prematurely"));
- hash_free(&ndbcluster_open_tables);
- pthread_mutex_destroy(&ndbcluster_mutex);
- pthread_mutex_destroy(&LOCK_ndb_util_thread);
- pthread_cond_destroy(&COND_ndb_util_thread);
- pthread_cond_destroy(&COND_ndb_util_ready);
+ my_hash_free(&ndbcluster_open_tables);
+ mysql_mutex_destroy(&ndbcluster_mutex);
+ mysql_mutex_destroy(&LOCK_ndb_util_thread);
+ mysql_cond_destroy(&COND_ndb_util_thread);
+ mysql_cond_destroy(&COND_ndb_util_ready);
goto ndbcluster_init_error;
}
@@ -7472,6 +7737,34 @@ ndbcluster_init_error:
DBUG_RETURN(TRUE);
}
+/**
+ Used to fill in INFORMATION_SCHEMA* tables.
+
+ @param hton handle to the handlerton structure
+ @param thd the thread/connection descriptor
+ @param[in,out] tables the information schema table that is filled up
+ @param cond used for conditional pushdown to storage engine
+ @param schema_table_idx the table id that distinguishes the type of table
+
+ @return Operation status
+ */
+static int ndbcluster_fill_is_table(handlerton *hton,
+ THD *thd,
+ TABLE_LIST *tables,
+ COND *cond,
+ enum enum_schema_tables schema_table_idx)
+{
+ int ret= 0;
+
+ if (schema_table_idx == SCH_FILES)
+ {
+ ret= ndbcluster_fill_files_table(hton, thd, tables, cond);
+ }
+
+ return ret;
+}
+
+
static int ndbcluster_end(handlerton *hton, ha_panic_function type)
{
DBUG_ENTER("ndbcluster_end");
@@ -7482,31 +7775,31 @@ static int ndbcluster_end(handlerton *hton, ha_panic_function type)
/* wait for util thread to finish */
sql_print_information("Stopping Cluster Utility thread");
- pthread_mutex_lock(&LOCK_ndb_util_thread);
+ mysql_mutex_lock(&LOCK_ndb_util_thread);
ndbcluster_terminating= 1;
- pthread_cond_signal(&COND_ndb_util_thread);
+ mysql_cond_signal(&COND_ndb_util_thread);
while (ndb_util_thread_running > 0)
- pthread_cond_wait(&COND_ndb_util_ready, &LOCK_ndb_util_thread);
- pthread_mutex_unlock(&LOCK_ndb_util_thread);
+ mysql_cond_wait(&COND_ndb_util_ready, &LOCK_ndb_util_thread);
+ mysql_mutex_unlock(&LOCK_ndb_util_thread);
#ifdef HAVE_NDB_BINLOG
{
- pthread_mutex_lock(&ndbcluster_mutex);
+ mysql_mutex_lock(&ndbcluster_mutex);
while (ndbcluster_open_tables.records)
{
NDB_SHARE *share=
- (NDB_SHARE*) hash_element(&ndbcluster_open_tables, 0);
+ (NDB_SHARE*) my_hash_element(&ndbcluster_open_tables, 0);
#ifndef DBUG_OFF
fprintf(stderr, "NDB: table share %s with use_count %d not freed\n",
share->key, share->use_count);
#endif
ndbcluster_real_free_share(&share);
}
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
}
#endif
- hash_free(&ndbcluster_open_tables);
+ my_hash_free(&ndbcluster_open_tables);
if (g_ndb)
{
@@ -7532,10 +7825,10 @@ static int ndbcluster_end(handlerton *hton, ha_panic_function type)
// cleanup ndb interface
ndb_end_internal();
- pthread_mutex_destroy(&ndbcluster_mutex);
- pthread_mutex_destroy(&LOCK_ndb_util_thread);
- pthread_cond_destroy(&COND_ndb_util_thread);
- pthread_cond_destroy(&COND_ndb_util_ready);
+ mysql_mutex_destroy(&ndbcluster_mutex);
+ mysql_mutex_destroy(&LOCK_ndb_util_thread);
+ mysql_cond_destroy(&COND_ndb_util_thread);
+ mysql_cond_destroy(&COND_ndb_util_ready);
DBUG_RETURN(0);
}
@@ -7838,12 +8131,12 @@ uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname,
build_table_filename(name, sizeof(name) - 1,
dbname, tabname, "", 0);
DBUG_PRINT("enter", ("name: %s", name));
- pthread_mutex_lock(&ndbcluster_mutex);
- if (!(share=(NDB_SHARE*) hash_search(&ndbcluster_open_tables,
- (uchar*) name,
- strlen(name))))
+ mysql_mutex_lock(&ndbcluster_mutex);
+ if (!(share=(NDB_SHARE*) my_hash_search(&ndbcluster_open_tables,
+ (uchar*) name,
+ strlen(name))))
{
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
DBUG_PRINT("info", ("Table %s not found in ndbcluster_open_tables", name));
DBUG_RETURN(1);
}
@@ -7851,10 +8144,10 @@ uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname,
share->use_count++;
DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u",
share->key, share->use_count));
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
- pthread_mutex_lock(&share->mutex);
- if (ndb_cache_check_time > 0)
+ mysql_mutex_lock(&share->mutex);
+ if (opt_ndb_cache_check_time > 0)
{
if (share->commit_count != 0)
{
@@ -7864,7 +8157,7 @@ uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname,
#endif
DBUG_PRINT("info", ("Getting commit_count: %s from share",
llstr(share->commit_count, buff)));
- pthread_mutex_unlock(&share->mutex);
+ mysql_mutex_unlock(&share->mutex);
/* ndb_share reference temporary free */
DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
share->key, share->use_count));
@@ -7881,7 +8174,7 @@ uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname,
ERR_RETURN(ndb->getNdbError());
}
uint lock= share->commit_count_lock;
- pthread_mutex_unlock(&share->mutex);
+ mysql_mutex_unlock(&share->mutex);
struct Ndb_statistics stat;
{
@@ -7897,7 +8190,7 @@ uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname,
}
}
- pthread_mutex_lock(&share->mutex);
+ mysql_mutex_lock(&share->mutex);
if (share->commit_count_lock == lock)
{
#ifndef DBUG_OFF
@@ -7913,7 +8206,7 @@ uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname,
DBUG_PRINT("info", ("Discarding commit_count, comit_count_lock changed"));
*commit_count= 0;
}
- pthread_mutex_unlock(&share->mutex);
+ mysql_mutex_unlock(&share->mutex);
/* ndb_share reference temporary free */
DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
share->key, share->use_count));
@@ -7957,17 +8250,15 @@ ndbcluster_cache_retrieval_allowed(THD *thd,
ulonglong *engine_data)
{
Uint64 commit_count;
- bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
char *dbname= full_name;
char *tabname= dbname+strlen(dbname)+1;
#ifndef DBUG_OFF
char buff[22], buff2[22];
#endif
DBUG_ENTER("ndbcluster_cache_retrieval_allowed");
- DBUG_PRINT("enter", ("dbname: %s, tabname: %s, is_autocommit: %d",
- dbname, tabname, is_autocommit));
+ DBUG_PRINT("enter", ("dbname: %s, tabname: %s", dbname, tabname));
- if (!is_autocommit)
+ if (thd->in_multi_stmt_transaction_mode())
{
DBUG_PRINT("exit", ("No, don't use cache in transaction"));
DBUG_RETURN(FALSE);
@@ -8032,12 +8323,10 @@ ha_ndbcluster::register_query_cache_table(THD *thd,
#ifndef DBUG_OFF
char buff[22];
#endif
- bool is_autocommit= !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
DBUG_ENTER("ha_ndbcluster::register_query_cache_table");
- DBUG_PRINT("enter",("dbname: %s, tabname: %s, is_autocommit: %d",
- m_dbname, m_tabname, is_autocommit));
+ DBUG_PRINT("enter",("dbname: %s, tabname: %s", m_dbname, m_tabname));
- if (!is_autocommit)
+ if (thd->in_multi_stmt_transaction_mode())
{
DBUG_PRINT("exit", ("Can't register table during transaction"));
DBUG_RETURN(FALSE);
@@ -8101,7 +8390,7 @@ static void print_ndbcluster_open_tables()
fprintf(DBUG_FILE, ">ndbcluster_open_tables\n");
for (uint i= 0; i < ndbcluster_open_tables.records; i++)
print_share("",
- (NDB_SHARE*)hash_element(&ndbcluster_open_tables, i));
+ (NDB_SHARE*)my_hash_element(&ndbcluster_open_tables, i));
fprintf(DBUG_FILE, "<ndbcluster_open_tables\n");
DBUG_UNLOCK_FILE;
}
@@ -8129,7 +8418,7 @@ static void print_ndbcluster_open_tables()
to avoid segmentation faults. There is a risk that the memory for
this trailing share leaks.
- Must be called with previous pthread_mutex_lock(&ndbcluster_mutex)
+ Must be called with previous mysql_mutex_lock(&ndbcluster_mutex)
*/
int handle_trailing_share(NDB_SHARE *share)
{
@@ -8141,22 +8430,21 @@ int handle_trailing_share(NDB_SHARE *share)
++share->use_count;
DBUG_PRINT("NDB_SHARE", ("%s temporary use_count: %u",
share->key, share->use_count));
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
TABLE_LIST table_list;
bzero((char*) &table_list,sizeof(table_list));
table_list.db= share->db;
table_list.alias= table_list.table_name= share->table_name;
- safe_mutex_assert_owner(&LOCK_open);
- close_cached_tables(thd, &table_list, TRUE, FALSE, FALSE);
+ close_cached_tables(thd, &table_list, FALSE, LONG_TIMEOUT);
- pthread_mutex_lock(&ndbcluster_mutex);
+ mysql_mutex_lock(&ndbcluster_mutex);
/* ndb_share reference temporary free */
DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
share->key, share->use_count));
if (!--share->use_count)
{
- if (ndb_extra_logging)
+ if (opt_ndb_extra_logging)
sql_print_information("NDB_SHARE: trailing share "
"%s(connect_count: %u) "
"released by close_cached_tables at "
@@ -8182,7 +8470,7 @@ int handle_trailing_share(NDB_SHARE *share)
if (share->use_count == 0)
{
- if (ndb_extra_logging)
+ if (opt_ndb_extra_logging)
sql_print_information("NDB_SHARE: trailing share "
"%s(connect_count: %u) "
"released after NSS_DROPPED check "
@@ -8212,7 +8500,7 @@ int handle_trailing_share(NDB_SHARE *share)
at the cost of a possible mem leak, by "renaming" the share
- First remove from hash
*/
- hash_delete(&ndbcluster_open_tables, (uchar*) share);
+ my_hash_delete(&ndbcluster_open_tables, (uchar*) share);
/*
now give it a new name, just a running number
@@ -8241,16 +8529,16 @@ int handle_trailing_share(NDB_SHARE *share)
static int rename_share(NDB_SHARE *share, const char *new_key)
{
NDB_SHARE *tmp;
- pthread_mutex_lock(&ndbcluster_mutex);
+ mysql_mutex_lock(&ndbcluster_mutex);
uint new_length= (uint) strlen(new_key);
DBUG_PRINT("rename_share", ("old_key: %s old__length: %d",
share->key, share->key_length));
- if ((tmp= (NDB_SHARE*) hash_search(&ndbcluster_open_tables,
- (uchar*) new_key, new_length)))
+ if ((tmp= (NDB_SHARE*) my_hash_search(&ndbcluster_open_tables,
+ (uchar*) new_key, new_length)))
handle_trailing_share(tmp);
/* remove the share from hash */
- hash_delete(&ndbcluster_open_tables, (uchar*) share);
+ my_hash_delete(&ndbcluster_open_tables, (uchar*) share);
dbug_print_open_tables();
/* save old stuff if insert should fail */
@@ -8279,7 +8567,7 @@ static int rename_share(NDB_SHARE *share, const char *new_key)
share->key));
}
dbug_print_open_tables();
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
return -1;
}
dbug_print_open_tables();
@@ -8304,7 +8592,7 @@ static int rename_share(NDB_SHARE *share, const char *new_key)
share->old_names= old_key;
// ToDo free old_names after ALTER EVENT
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
return 0;
}
#endif
@@ -8315,12 +8603,12 @@ static int rename_share(NDB_SHARE *share, const char *new_key)
*/
NDB_SHARE *ndbcluster_get_share(NDB_SHARE *share)
{
- pthread_mutex_lock(&ndbcluster_mutex);
+ mysql_mutex_lock(&ndbcluster_mutex);
share->use_count++;
dbug_print_open_tables();
dbug_print_share("ndbcluster_get_share:", share);
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
return share;
}
@@ -8337,7 +8625,7 @@ NDB_SHARE *ndbcluster_get_share(NDB_SHARE *share)
create_if_not_exists == FALSE:
returns 0 if share does not exist
- have_lock == TRUE, pthread_mutex_lock(&ndbcluster_mutex) already taken
+ have_lock == TRUE, mysql_mutex_lock(&ndbcluster_mutex) already taken
*/
NDB_SHARE *ndbcluster_get_share(const char *key, TABLE *table,
@@ -8350,16 +8638,16 @@ NDB_SHARE *ndbcluster_get_share(const char *key, TABLE *table,
DBUG_PRINT("enter", ("key: '%s'", key));
if (!have_lock)
- pthread_mutex_lock(&ndbcluster_mutex);
- if (!(share= (NDB_SHARE*) hash_search(&ndbcluster_open_tables,
- (uchar*) key,
- length)))
+ mysql_mutex_lock(&ndbcluster_mutex);
+ if (!(share= (NDB_SHARE*) my_hash_search(&ndbcluster_open_tables,
+ (uchar*) key,
+ length)))
{
if (!create_if_not_exists)
{
DBUG_PRINT("error", ("get_share: %s does not exist", key));
if (!have_lock)
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
DBUG_RETURN(0);
}
if ((share= (NDB_SHARE*) my_malloc(sizeof(*share),
@@ -8378,14 +8666,14 @@ NDB_SHARE *ndbcluster_get_share(const char *key, TABLE *table,
if (my_hash_insert(&ndbcluster_open_tables, (uchar*) share))
{
free_root(&share->mem_root, MYF(0));
- my_free((uchar*) share, 0);
+ my_free(share);
*root_ptr= old_root;
if (!have_lock)
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
DBUG_RETURN(0);
}
thr_lock_init(&share->lock);
- pthread_mutex_init(&share->mutex, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_NDB_SHARE_mutex, &share->mutex, MY_MUTEX_INIT_FAST);
share->commit_count= 0;
share->commit_count_lock= 0;
share->db= share->key + length + 1;
@@ -8399,7 +8687,7 @@ NDB_SHARE *ndbcluster_get_share(const char *key, TABLE *table,
ndbcluster_real_free_share(&share);
*root_ptr= old_root;
if (!have_lock)
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
DBUG_RETURN(0);
}
#endif
@@ -8409,7 +8697,7 @@ NDB_SHARE *ndbcluster_get_share(const char *key, TABLE *table,
{
DBUG_PRINT("error", ("get_share: failed to alloc share"));
if (!have_lock)
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
my_error(ER_OUTOFMEMORY, MYF(0), static_cast<int>(sizeof(*share)));
DBUG_RETURN(0);
}
@@ -8419,7 +8707,7 @@ NDB_SHARE *ndbcluster_get_share(const char *key, TABLE *table,
dbug_print_open_tables();
dbug_print_share("ndbcluster_get_share:", share);
if (!have_lock)
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
DBUG_RETURN(share);
}
@@ -8429,9 +8717,9 @@ void ndbcluster_real_free_share(NDB_SHARE **share)
DBUG_ENTER("ndbcluster_real_free_share");
dbug_print_share("ndbcluster_real_free_share:", *share);
- hash_delete(&ndbcluster_open_tables, (uchar*) *share);
+ my_hash_delete(&ndbcluster_open_tables, (uchar*) *share);
thr_lock_delete(&(*share)->lock);
- pthread_mutex_destroy(&(*share)->mutex);
+ mysql_mutex_destroy(&(*share)->mutex);
#ifdef HAVE_NDB_BINLOG
if ((*share)->table)
@@ -8449,7 +8737,7 @@ void ndbcluster_real_free_share(NDB_SHARE **share)
}
#endif
free_root(&(*share)->mem_root, MYF(0));
- my_free((uchar*) *share, MYF(0));
+ my_free(*share);
*share= 0;
dbug_print_open_tables();
@@ -8460,7 +8748,7 @@ void ndbcluster_real_free_share(NDB_SHARE **share)
void ndbcluster_free_share(NDB_SHARE **share, bool have_lock)
{
if (!have_lock)
- pthread_mutex_lock(&ndbcluster_mutex);
+ mysql_mutex_lock(&ndbcluster_mutex);
if ((*share)->util_lock == current_thd)
(*share)->util_lock= 0;
if (!--(*share)->use_count)
@@ -8473,7 +8761,7 @@ void ndbcluster_free_share(NDB_SHARE **share, bool have_lock)
dbug_print_share("ndbcluster_free_share:", *share);
}
if (!have_lock)
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
}
@@ -8633,11 +8921,12 @@ int ha_ndbcluster::write_ndb_file(const char *name)
(void)strxnmov(path, FN_REFLEN-1,
mysql_data_home,"/",name,ha_ndb_ext,NullS);
- if ((file=my_create(path, CREATE_MODE,O_RDWR | O_TRUNC,MYF(MY_WME))) >= 0)
+ if ((file= mysql_file_create(key_file_ndb, path, CREATE_MODE,
+ O_RDWR | O_TRUNC, MYF(MY_WME))) >= 0)
{
// It's an empty file
error=0;
- my_close(file,MYF(0));
+ mysql_file_close(file, MYF(0));
}
DBUG_RETURN(error);
}
@@ -8724,6 +9013,7 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
sorted,
buffer));
}
+ MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
thd_ndb->query_state|= NDB_QUERY_MULTI_READ_RANGE;
m_disable_multi_read= FALSE;
@@ -8805,7 +9095,13 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
(op->setPartitionId(part_spec.start_part), TRUE)))
curr += reclength;
else
- ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
+ {
+ ERR_RETURN_PREPARE(res,
+ op ? op->getNdbError() :
+ m_active_trans->getNdbError())
+ MYSQL_INDEX_READ_ROW_DONE(res);
+ DBUG_RETURN(res);
+ }
break;
}
break;
@@ -8825,7 +9121,13 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
!define_read_attrs(curr, op))
curr += reclength;
else
- ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError());
+ {
+ ERR_RETURN_PREPARE(res,
+ op ? op->getNdbError() :
+ m_active_trans->getNdbError());
+ MYSQL_INDEX_READ_ROW_DONE(res);
+ DBUG_RETURN(res);
+ }
break;
}
case ORDERED_INDEX: {
@@ -8840,7 +9142,11 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
DBUG_ASSERT(scanOp->getLockMode() ==
(NdbOperation::LockMode)get_ndb_lock_type(m_lock.type));
if (scanOp->reset_bounds(m_force_send))
- DBUG_RETURN(ndb_err(m_active_trans));
+ {
+ res= ndb_err(m_active_trans);
+ MYSQL_INDEX_READ_ROW_DONE(res);
+ DBUG_RETURN(res);
+ }
end_of_buffer -= reclength;
}
@@ -8855,8 +9161,11 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
}
else
{
- ERR_RETURN(scanOp ? scanOp->getNdbError() :
- m_active_trans->getNdbError());
+ ERR_RETURN_PREPARE(res,
+ scanOp ? scanOp->getNdbError() :
+ m_active_trans->getNdbError());
+ MYSQL_INDEX_READ_ROW_DONE(res);
+ DBUG_RETURN(res);
}
}
@@ -8864,11 +9173,15 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
&multi_range_curr->end_key };
if ((res= set_bounds(scanOp, active_index, FALSE, keys,
multi_range_curr-ranges)))
+ {
+ MYSQL_INDEX_READ_ROW_DONE(res);
DBUG_RETURN(res);
+ }
break;
}
case UNDEFINED_INDEX:
DBUG_ASSERT(FALSE);
+ MYSQL_INDEX_READ_ROW_DONE(1);
DBUG_RETURN(1);
break;
}
@@ -8899,9 +9212,13 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
m_multi_range_defined= multi_range_curr;
multi_range_curr= ranges;
m_multi_range_result_ptr= (uchar*)buffer->buffer;
- DBUG_RETURN(read_multi_range_next(found_range_p));
+ res= loc_read_multi_range_next(found_range_p);
+ MYSQL_INDEX_READ_ROW_DONE(res);
+ DBUG_RETURN(res);
}
- ERR_RETURN(m_active_trans->getNdbError());
+ ERR_RETURN_PREPARE(res, m_active_trans->getNdbError());
+ MYSQL_INDEX_READ_ROW_DONE(res);
+ DBUG_RETURN(res);
}
#if 0
@@ -8913,17 +9230,28 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
int
ha_ndbcluster::read_multi_range_next(KEY_MULTI_RANGE ** multi_range_found_p)
{
+ int rc;
DBUG_ENTER("ha_ndbcluster::read_multi_range_next");
if (m_disable_multi_read)
{
DBUG_MULTI_RANGE(11);
DBUG_RETURN(handler::read_multi_range_next(multi_range_found_p));
}
-
+ MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
+ rc= loc_read_multi_range_next(multi_range_found_p);
+ MYSQL_INDEX_READ_ROW_DONE(rc);
+ DBUG_RETURN(rc);
+}
+
+int ha_ndbcluster::loc_read_multi_range_next(
+ KEY_MULTI_RANGE **multi_range_found_p)
+{
int res;
int range_no;
ulong reclength= table_share->reclength;
const NdbOperation* op= m_current_multi_operation;
+ DBUG_ENTER("ha_ndbcluster::loc_read_multi_range_next");
+
for (;multi_range_curr < m_multi_range_defined; multi_range_curr++)
{
DBUG_MULTI_RANGE(12);
@@ -9030,6 +9358,7 @@ close_scan:
/*
* Read remaining ranges
*/
+ MYSQL_INDEX_READ_ROW_DONE(1);
DBUG_RETURN(read_multi_range_first(multi_range_found_p,
multi_range_curr,
multi_range_end - multi_range_curr,
@@ -9151,9 +9480,9 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
my_thread_init();
DBUG_ENTER("ndb_util_thread");
- DBUG_PRINT("enter", ("ndb_cache_check_time: %lu", ndb_cache_check_time));
+ DBUG_PRINT("enter", ("cache_check_time: %lu", opt_ndb_cache_check_time));
- pthread_mutex_lock(&LOCK_ndb_util_thread);
+ mysql_mutex_lock(&LOCK_ndb_util_thread);
thd= new THD; /* note that contructor of THD uses DBUG_ */
if (thd == NULL)
@@ -9168,14 +9497,14 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
thd->thread_stack= (char*)&thd; /* remember where our stack is */
if (thd->store_globals())
goto ndb_util_thread_fail;
- lex_start(thd);
thd->init_for_queries();
- thd->version=refresh_version;
thd->main_security_ctx.host_or_ip= "";
thd->client_capabilities = 0;
my_net_init(&thd->net, 0);
thd->main_security_ctx.master_access= ~0;
thd->main_security_ctx.priv_user[0] = 0;
+ /* Do not use user-supplied timeout value for system threads. */
+ thd->variables.lock_wait_timeout= LONG_TIMEOUT;
CHARSET_INFO *charset_connection;
charset_connection= get_charset_by_csname("utf8",
@@ -9187,52 +9516,52 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
/* Signal successful initialization */
ndb_util_thread_running= 1;
- pthread_cond_signal(&COND_ndb_util_ready);
- pthread_mutex_unlock(&LOCK_ndb_util_thread);
+ mysql_cond_signal(&COND_ndb_util_ready);
+ mysql_mutex_unlock(&LOCK_ndb_util_thread);
/*
wait for mysql server to start
*/
- pthread_mutex_lock(&LOCK_server_started);
+ mysql_mutex_lock(&LOCK_server_started);
while (!mysqld_server_started)
{
set_timespec(abstime, 1);
- pthread_cond_timedwait(&COND_server_started, &LOCK_server_started,
- &abstime);
+ mysql_cond_timedwait(&COND_server_started, &LOCK_server_started,
+ &abstime);
if (ndbcluster_terminating)
{
- pthread_mutex_unlock(&LOCK_server_started);
- pthread_mutex_lock(&LOCK_ndb_util_thread);
+ mysql_mutex_unlock(&LOCK_server_started);
+ mysql_mutex_lock(&LOCK_ndb_util_thread);
goto ndb_util_thread_end;
}
}
- pthread_mutex_unlock(&LOCK_server_started);
+ mysql_mutex_unlock(&LOCK_server_started);
/*
Wait for cluster to start
*/
- pthread_mutex_lock(&LOCK_ndb_util_thread);
+ mysql_mutex_lock(&LOCK_ndb_util_thread);
while (!ndb_cluster_node_id && (ndbcluster_hton->slot != ~(uint)0))
{
/* ndb not connected yet */
- pthread_cond_wait(&COND_ndb_util_thread, &LOCK_ndb_util_thread);
+ mysql_cond_wait(&COND_ndb_util_thread, &LOCK_ndb_util_thread);
if (ndbcluster_terminating)
goto ndb_util_thread_end;
}
- pthread_mutex_unlock(&LOCK_ndb_util_thread);
+ mysql_mutex_unlock(&LOCK_ndb_util_thread);
/* Get thd_ndb for this thread */
if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb()))
{
sql_print_error("Could not allocate Thd_ndb object");
- pthread_mutex_lock(&LOCK_ndb_util_thread);
+ mysql_mutex_lock(&LOCK_ndb_util_thread);
goto ndb_util_thread_end;
}
set_thd_ndb(thd, thd_ndb);
thd_ndb->options|= TNO_NO_LOG_SCHEMA_OP;
#ifdef HAVE_NDB_BINLOG
- if (ndb_extra_logging && ndb_binlog_running)
+ if (opt_ndb_extra_logging && ndb_binlog_running)
sql_print_information("NDB Binlog: Ndb tables initially read only.");
/* create tables needed by the replication */
ndbcluster_setup_binlog_table_shares(thd);
@@ -9246,17 +9575,17 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
set_timespec(abstime, 0);
for (;;)
{
- pthread_mutex_lock(&LOCK_ndb_util_thread);
+ mysql_mutex_lock(&LOCK_ndb_util_thread);
if (!ndbcluster_terminating)
- pthread_cond_timedwait(&COND_ndb_util_thread,
- &LOCK_ndb_util_thread,
- &abstime);
+ mysql_cond_timedwait(&COND_ndb_util_thread,
+ &LOCK_ndb_util_thread,
+ &abstime);
if (ndbcluster_terminating) /* Shutting down server */
goto ndb_util_thread_end;
- pthread_mutex_unlock(&LOCK_ndb_util_thread);
+ mysql_mutex_unlock(&LOCK_ndb_util_thread);
#ifdef NDB_EXTRA_DEBUG_UTIL_THREAD
- DBUG_PRINT("ndb_util_thread", ("Started, ndb_cache_check_time: %lu",
- ndb_cache_check_time));
+ DBUG_PRINT("ndb_util_thread", ("Started, opt_ndb_cache_check_time: %lu",
+ opt_ndb_cache_check_time));
#endif
#ifdef HAVE_NDB_BINLOG
@@ -9269,7 +9598,7 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
ndbcluster_setup_binlog_table_shares(thd);
#endif
- if (ndb_cache_check_time == 0)
+ if (opt_ndb_cache_check_time == 0)
{
/* Wake up in 1 second to check if value has changed */
set_timespec(abstime, 1);
@@ -9278,7 +9607,7 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
/* Lock mutex and fill list with pointers to all open tables */
NDB_SHARE *share;
- pthread_mutex_lock(&ndbcluster_mutex);
+ mysql_mutex_lock(&ndbcluster_mutex);
uint i, open_count, record_count= ndbcluster_open_tables.records;
if (share_list_size < record_count)
{
@@ -9287,7 +9616,7 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
{
sql_print_warning("ndb util thread: malloc failure, "
"query cache not maintained properly");
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
goto next; // At least do not crash
}
delete [] share_list;
@@ -9296,7 +9625,7 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
}
for (i= 0, open_count= 0; i < record_count; i++)
{
- share= (NDB_SHARE *)hash_element(&ndbcluster_open_tables, i);
+ share= (NDB_SHARE *)my_hash_element(&ndbcluster_open_tables, i);
#ifdef HAVE_NDB_BINLOG
if ((share->use_count - (int) (share->op != 0) - (int) (share->op != 0))
<= 0)
@@ -9314,7 +9643,7 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
/* Store pointer to table */
share_list[open_count++]= share;
}
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
/* Iterate through the open files list */
for (i= 0; i < open_count; i++)
@@ -9339,9 +9668,9 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
struct Ndb_statistics stat;
uint lock;
- pthread_mutex_lock(&share->mutex);
+ mysql_mutex_lock(&share->mutex);
lock= share->commit_count_lock;
- pthread_mutex_unlock(&share->mutex);
+ mysql_mutex_unlock(&share->mutex);
{
/* Contact NDB to get commit count for table */
Ndb* ndb= thd_ndb->ndb;
@@ -9372,10 +9701,10 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
}
}
loop_next:
- pthread_mutex_lock(&share->mutex);
+ mysql_mutex_lock(&share->mutex);
if (share->commit_count_lock == lock)
share->commit_count= stat.commit_count;
- pthread_mutex_unlock(&share->mutex);
+ mysql_mutex_unlock(&share->mutex);
/* ndb_share reference temporary free */
DBUG_PRINT("NDB_SHARE", ("%s temporary free use_count: %u",
@@ -9385,7 +9714,7 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
next:
/* Calculate new time to wake up */
int secs= 0;
- int msecs= ndb_cache_check_time;
+ int msecs= opt_ndb_cache_check_time;
struct timeval tick_time;
gettimeofday(&tick_time, 0);
@@ -9405,7 +9734,7 @@ next:
}
}
- pthread_mutex_lock(&LOCK_ndb_util_thread);
+ mysql_mutex_lock(&LOCK_ndb_util_thread);
ndb_util_thread_end:
net_end(&thd->net);
@@ -9417,8 +9746,8 @@ ndb_util_thread_fail:
/* signal termination */
ndb_util_thread_running= 0;
- pthread_cond_signal(&COND_ndb_util_ready);
- pthread_mutex_unlock(&LOCK_ndb_util_thread);
+ mysql_cond_signal(&COND_ndb_util_ready);
+ mysql_mutex_unlock(&LOCK_ndb_util_thread);
DBUG_PRINT("exit", ("ndb_util_thread"));
DBUG_LEAVE; // Must match DBUG_ENTER()
@@ -9507,11 +9836,11 @@ char* ha_ndbcluster::get_tablespace_name(THD *thd, char* name, uint name_len)
}
err:
if (ndberr.status == NdbError::TemporaryError)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_GET_TEMPORARY_ERRMSG, ER(ER_GET_TEMPORARY_ERRMSG),
ndberr.code, ndberr.message, "NDB");
else
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
ndberr.code, ndberr.message, "NDB");
return 0;
@@ -9661,12 +9990,33 @@ int ha_ndbcluster::get_default_no_partitions(HA_CREATE_INFO *create_info)
and partition by hidden key otherwise.
*/
+
+enum ndb_distribution_enum { ND_KEYHASH= 0, ND_LINHASH= 1 };
+static const char* distribution_names[]= { "KEYHASH", "LINHASH", NullS };
+static ulong default_ndb_distribution= ND_KEYHASH;
+static TYPELIB distribution_typelib= {
+ array_elements(distribution_names) - 1,
+ "",
+ distribution_names,
+ NULL
+};
+static MYSQL_SYSVAR_ENUM(
+ distribution, /* name */
+ default_ndb_distribution, /* var */
+ PLUGIN_VAR_RQCMDARG,
+ "Default distribution for new tables in ndb",
+ NULL, /* check func. */
+ NULL, /* update func. */
+ ND_KEYHASH, /* default */
+ &distribution_typelib /* typelib */
+);
+
void ha_ndbcluster::set_auto_partitions(partition_info *part_info)
{
DBUG_ENTER("ha_ndbcluster::set_auto_partitions");
part_info->list_of_part_fields= TRUE;
part_info->part_type= HASH_PARTITION;
- switch (opt_ndb_distribution_id)
+ switch (default_ndb_distribution)
{
case ND_KEYHASH:
part_info->linear_hash_ind= FALSE;
@@ -9682,7 +10032,7 @@ void ha_ndbcluster::set_auto_partitions(partition_info *part_info)
int ha_ndbcluster::set_range_data(void *tab_ref, partition_info *part_info)
{
NDBTAB *tab= (NDBTAB*)tab_ref;
- int32 *range_data= (int32*)my_malloc(part_info->no_parts*sizeof(int32),
+ int32 *range_data= (int32*)my_malloc(part_info->num_parts*sizeof(int32),
MYF(0));
uint i;
int error= 0;
@@ -9691,17 +10041,17 @@ int ha_ndbcluster::set_range_data(void *tab_ref, partition_info *part_info)
if (!range_data)
{
- mem_alloc_error(part_info->no_parts*sizeof(int32));
+ mem_alloc_error(part_info->num_parts*sizeof(int32));
DBUG_RETURN(1);
}
- for (i= 0; i < part_info->no_parts; i++)
+ for (i= 0; i < part_info->num_parts; i++)
{
longlong range_val= part_info->range_int_array[i];
if (unsigned_flag)
range_val-= 0x8000000000000000ULL;
if (range_val < INT_MIN32 || range_val >= INT_MAX32)
{
- if ((i != part_info->no_parts - 1) ||
+ if ((i != part_info->num_parts - 1) ||
(range_val != LONGLONG_MAX))
{
my_error(ER_LIMITED_PART_RANGE, MYF(0), "NDB");
@@ -9712,16 +10062,16 @@ int ha_ndbcluster::set_range_data(void *tab_ref, partition_info *part_info)
}
range_data[i]= (int32)range_val;
}
- tab->setRangeListData(range_data, sizeof(int32)*part_info->no_parts);
+ tab->setRangeListData(range_data, sizeof(int32)*part_info->num_parts);
error:
- my_free((char*)range_data, MYF(0));
+ my_free(range_data);
DBUG_RETURN(error);
}
int ha_ndbcluster::set_list_data(void *tab_ref, partition_info *part_info)
{
NDBTAB *tab= (NDBTAB*)tab_ref;
- int32 *list_data= (int32*)my_malloc(part_info->no_list_values * 2
+ int32 *list_data= (int32*)my_malloc(part_info->num_list_values * 2
* sizeof(int32), MYF(0));
uint32 *part_id, i;
int error= 0;
@@ -9730,10 +10080,10 @@ int ha_ndbcluster::set_list_data(void *tab_ref, partition_info *part_info)
if (!list_data)
{
- mem_alloc_error(part_info->no_list_values*2*sizeof(int32));
+ mem_alloc_error(part_info->num_list_values*2*sizeof(int32));
DBUG_RETURN(1);
}
- for (i= 0; i < part_info->no_list_values; i++)
+ for (i= 0; i < part_info->num_list_values; i++)
{
LIST_PART_ENTRY *list_entry= &part_info->list_array[i];
longlong list_val= list_entry->list_value;
@@ -9749,9 +10099,9 @@ int ha_ndbcluster::set_list_data(void *tab_ref, partition_info *part_info)
part_id= (uint32*)&list_data[2*i+1];
*part_id= list_entry->partition_id;
}
- tab->setRangeListData(list_data, 2*sizeof(int32)*part_info->no_list_values);
+ tab->setRangeListData(list_data, 2*sizeof(int32)*part_info->num_list_values);
error:
- my_free((char*)list_data, MYF(0));
+ my_free(list_data);
DBUG_RETURN(error);
}
@@ -9805,7 +10155,7 @@ uint ha_ndbcluster::set_up_partition_info(partition_info *part_info,
{
if (!current_thd->variables.new_mode)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_ILLEGAL_HA_CREATE_OPTION,
ER(ER_ILLEGAL_HA_CREATE_OPTION),
ndbcluster_hton_name,
@@ -9871,11 +10221,11 @@ uint ha_ndbcluster::set_up_partition_info(partition_info *part_info,
ng= 0;
ts_names[fd_index]= part_elem->tablespace_name;
frag_data[fd_index++]= ng;
- } while (++j < part_info->no_subparts);
+ } while (++j < part_info->num_subparts);
}
first= FALSE;
- } while (++i < part_info->no_parts);
- tab->setDefaultNoPartitionsFlag(part_info->use_default_no_partitions);
+ } while (++i < part_info->num_parts);
+ tab->setDefaultNoPartitionsFlag(part_info->use_default_num_partitions);
tab->setLinearFlag(part_info->linear_hash_ind);
{
ha_rows max_rows= table_share->max_rows;
@@ -9902,7 +10252,7 @@ bool ha_ndbcluster::check_if_incompatible_data(HA_CREATE_INFO *create_info,
uint i;
const NDBTAB *tab= (const NDBTAB *) m_table;
- if (current_thd->variables.ndb_use_copying_alter_table)
+ if (THDVAR(current_thd, use_copying_alter_table))
{
DBUG_PRINT("info", ("On-line alter table disabled"));
DBUG_RETURN(COMPATIBLE_DATA_NO);
@@ -10248,13 +10598,13 @@ int ndbcluster_alter_tablespace(handlerton *hton,
thd->query(), thd->query_length(),
"", alter_info->tablespace_name,
0, 0,
- SOT_TABLESPACE, 0, 0, 0);
+ SOT_TABLESPACE, 0, 0);
else
ndbcluster_log_schema_op(thd, 0,
thd->query(), thd->query_length(),
"", alter_info->logfile_group_name,
0, 0,
- SOT_LOGFILE_GROUP, 0, 0, 0);
+ SOT_LOGFILE_GROUP, 0, 0);
#endif
DBUG_RETURN(FALSE);
@@ -10269,7 +10619,7 @@ ndberror2:
}
-bool ha_ndbcluster::get_no_parts(const char *name, uint *no_parts)
+bool ha_ndbcluster::get_no_parts(const char *name, uint *num_parts)
{
Ndb *ndb;
NDBDICT *dict;
@@ -10291,7 +10641,7 @@ bool ha_ndbcluster::get_no_parts(const char *name, uint *no_parts)
Ndb_table_guard ndbtab_g(dict= ndb->getDictionary(), m_tabname);
if (!ndbtab_g.get_table())
ERR_BREAK(dict->getNdbError(), err);
- *no_parts= ndbtab_g.get_table()->getFragmentCount();
+ *num_parts= ndbtab_g.get_table()->getFragmentCount();
DBUG_RETURN(FALSE);
}
@@ -10348,7 +10698,8 @@ static int ndbcluster_fill_files_table(handlerton *hton,
continue;
ERR_RETURN(ndberr);
}
-
+ table->field[IS_FILES_TABLE_CATALOG]->store(STRING_WITH_LEN("def"),
+ system_charset_info);
table->field[IS_FILES_FILE_NAME]->set_notnull();
table->field[IS_FILES_FILE_NAME]->store(elt.name, strlen(elt.name),
system_charset_info);
@@ -10533,6 +10884,132 @@ SHOW_VAR ndb_status_variables_export[]= {
{NullS, NullS, SHOW_LONG}
};
+static MYSQL_SYSVAR_ULONG(
+ cache_check_time, /* name */
+ opt_ndb_cache_check_time, /* var */
+ PLUGIN_VAR_RQCMDARG,
+ "A dedicated thread is created to, at the given "
+ "millisecond interval, invalidate the query cache "
+ "if another MySQL server in the cluster has changed "
+ "the data in the database.",
+ NULL, /* check func. */
+ NULL, /* update func. */
+ 0, /* default */
+ 0, /* min */
+ ONE_YEAR_IN_SECONDS, /* max */
+ 0 /* block */
+);
+
+
+static MYSQL_SYSVAR_ULONG(
+ extra_logging, /* name */
+ opt_ndb_extra_logging, /* var */
+ PLUGIN_VAR_OPCMDARG,
+ "Turn on more logging in the error log.",
+ NULL, /* check func. */
+ NULL, /* update func. */
+ 1, /* default */
+ 0, /* min */
+ 0, /* max */
+ 0 /* block */
+);
+
+
+ulong opt_ndb_report_thresh_binlog_epoch_slip;
+static MYSQL_SYSVAR_ULONG(
+ report_thresh_binlog_epoch_slip, /* name */
+ opt_ndb_report_thresh_binlog_epoch_slip,/* var */
+ PLUGIN_VAR_RQCMDARG,
+ "Threshold on number of epochs to be behind before reporting binlog "
+ "status. E.g. 3 means that if the difference between what epoch has "
+ "been received from the storage nodes and what has been applied to "
+ "the binlog is 3 or more, a status message will be sent to the cluster "
+ "log.",
+ NULL, /* check func. */
+ NULL, /* update func. */
+ 3, /* default */
+ 0, /* min */
+ 256, /* max */
+ 0 /* block */
+);
+
+
+ulong opt_ndb_report_thresh_binlog_mem_usage;
+static MYSQL_SYSVAR_ULONG(
+ report_thresh_binlog_mem_usage, /* name */
+ opt_ndb_report_thresh_binlog_mem_usage,/* var */
+ PLUGIN_VAR_RQCMDARG,
+ "Threshold on percentage of free memory before reporting binlog "
+ "status. E.g. 10 means that if amount of available memory for "
+ "receiving binlog data from the storage nodes goes below 10%, "
+ "a status message will be sent to the cluster log.",
+ NULL, /* check func. */
+ NULL, /* update func. */
+ 10, /* default */
+ 0, /* min */
+ 100, /* max */
+ 0 /* block */
+);
+
+
+static MYSQL_SYSVAR_STR(
+ connectstring, /* name */
+ opt_ndb_connectstring, /* var */
+ PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
+ "Connect string for ndbcluster.",
+ NULL, /* check func. */
+ NULL, /* update func. */
+ NULL /* default */
+);
+
+
+static MYSQL_SYSVAR_STR(
+ mgmd_host, /* name */
+ opt_ndb_mgmd_host, /* var */
+ PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
+ "Set host and port for ndb_mgmd. Syntax: hostname[:port]",
+ NULL, /* check func. */
+ NULL, /* update func. */
+ NULL /* default */
+);
+
+
+static MYSQL_SYSVAR_UINT(
+ nodeid, /* name */
+ opt_ndb_nodeid, /* var */
+ PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
+ "Nodeid for this mysqld in the cluster.",
+ NULL, /* check func. */
+ NULL, /* update func. */
+ 0, /* default */
+ 0, /* min */
+ 255, /* max */
+ 0 /* block */
+);
+
+static struct st_mysql_sys_var* system_variables[]= {
+ MYSQL_SYSVAR(cache_check_time),
+ MYSQL_SYSVAR(extra_logging),
+ MYSQL_SYSVAR(report_thresh_binlog_mem_usage),
+ MYSQL_SYSVAR(report_thresh_binlog_epoch_slip),
+ MYSQL_SYSVAR(distribution),
+ MYSQL_SYSVAR(autoincrement_prefetch_sz),
+ MYSQL_SYSVAR(force_send),
+ MYSQL_SYSVAR(use_exact_count),
+ MYSQL_SYSVAR(use_transactions),
+ MYSQL_SYSVAR(use_copying_alter_table),
+ MYSQL_SYSVAR(optimized_node_selection),
+ MYSQL_SYSVAR(index_stat_enable),
+ MYSQL_SYSVAR(index_stat_cache_entries),
+ MYSQL_SYSVAR(index_stat_update_freq),
+ MYSQL_SYSVAR(connectstring),
+ MYSQL_SYSVAR(mgmd_host),
+ MYSQL_SYSVAR(nodeid),
+
+ NULL
+};
+
+
struct st_mysql_storage_engine ndbcluster_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
@@ -10548,8 +11025,9 @@ mysql_declare_plugin(ndbcluster)
NULL, /* Plugin Deinit */
0x0100 /* 1.0 */,
ndb_status_variables_export,/* status variables */
- NULL, /* system variables */
- NULL /* config options */
+ system_variables, /* system variables */
+ NULL, /* config options */
+ 0, /* flags */
}
mysql_declare_plugin_end;
maria_declare_plugin(ndbcluster)
diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h
index d0e1dd1f59f..70e1e9dc7cf 100644
--- a/sql/ha_ndbcluster.h
+++ b/sql/ha_ndbcluster.h
@@ -1,4 +1,7 @@
-/* Copyright (c) 2000-2008 MySQL AB
+#ifndef HA_NDBCLUSTER_INCLUDED
+#define HA_NDBCLUSTER_INCLUDED
+
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,7 +14,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
This file defines the NDB Cluster handler: the interface between MySQL and
@@ -31,7 +34,10 @@
#include <ndbapi_limits.h>
#define NDB_HIDDEN_PRIMARY_KEY_LENGTH 8
-#define NDB_DEFAULT_AUTO_PREFETCH 32
+
+#ifdef HAVE_PSI_INTERFACE
+extern PSI_file_key key_file_ndb;
+#endif /* HAVE_PSI_INTERFACE */
class Ndb; // Forward declaration
@@ -45,13 +51,10 @@ class NdbIndexStat;
class NdbEventOperation;
class ha_ndbcluster_cond;
+#include "sql_partition.h" /* part_id_range */
+
// connectstring to cluster if given by mysqld
extern const char *ndbcluster_connectstring;
-extern ulong ndb_cache_check_time;
-#ifdef HAVE_NDB_BINLOG
-extern ulong ndb_report_thresh_binlog_epoch_slip;
-extern ulong ndb_report_thresh_binlog_mem_usage;
-#endif
typedef enum ndb_index_type {
UNDEFINED_INDEX = 0,
@@ -105,7 +108,7 @@ typedef struct st_ndbcluster_share {
NDB_SHARE_STATE state;
MEM_ROOT mem_root;
THR_LOCK lock;
- pthread_mutex_t mutex;
+ mysql_mutex_t mutex;
char *key;
uint key_length;
THD *util_lock;
@@ -134,9 +137,9 @@ NDB_SHARE_STATE
get_ndb_share_state(NDB_SHARE *share)
{
NDB_SHARE_STATE state;
- pthread_mutex_lock(&share->mutex);
+ mysql_mutex_lock(&share->mutex);
state= share->state;
- pthread_mutex_unlock(&share->mutex);
+ mysql_mutex_unlock(&share->mutex);
return state;
}
@@ -144,19 +147,19 @@ inline
void
set_ndb_share_state(NDB_SHARE *share, NDB_SHARE_STATE state)
{
- pthread_mutex_lock(&share->mutex);
+ mysql_mutex_lock(&share->mutex);
share->state= state;
- pthread_mutex_unlock(&share->mutex);
+ mysql_mutex_unlock(&share->mutex);
}
struct Ndb_tuple_id_range_guard {
Ndb_tuple_id_range_guard(NDB_SHARE* _share) :
share(_share),
range(share->tuple_id_range) {
- pthread_mutex_lock(&share->mutex);
+ mysql_mutex_lock(&share->mutex);
}
~Ndb_tuple_id_range_guard() {
- pthread_mutex_unlock(&share->mutex);
+ mysql_mutex_unlock(&share->mutex);
}
NDB_SHARE* share;
Ndb::TupleIdRange& range;
@@ -282,7 +285,7 @@ class ha_ndbcluster: public handler
ha_rows estimate_rows_upper_bound()
{ return HA_POS_ERROR; }
int info(uint);
- void get_dynamic_partition_info(PARTITION_INFO *stat_info, uint part_id);
+ void get_dynamic_partition_info(PARTITION_STATS *stat_info, uint part_id);
int extra(enum ha_extra_function operation);
int extra_opt(enum ha_extra_function operation, ulong cache_size);
int reset();
@@ -395,6 +398,7 @@ static void set_tabname(const char *pathname, char *tabname);
uint table_changes);
private:
+ int loc_read_multi_range_next(KEY_MULTI_RANGE **found_range_p);
friend int ndbcluster_drop_database_impl(const char *path);
friend int ndb_handle_schema_change(THD *thd,
Ndb *ndb, NdbEventOperation *pOp,
@@ -590,4 +594,6 @@ static const char ndbcluster_hton_name[]= "ndbcluster";
static const int ndbcluster_hton_name_length=sizeof(ndbcluster_hton_name)-1;
extern int ndbcluster_terminating;
extern int ndb_util_thread_running;
-extern pthread_cond_t COND_ndb_util_ready;
+extern mysql_cond_t COND_ndb_util_ready;
+
+#endif /* HA_NDBCLUSTER_INCLUDED */
diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc
index cb7e73a605e..013929c24e0 100644
--- a/sql/ha_ndbcluster_binlog.cc
+++ b/sql/ha_ndbcluster_binlog.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2006, 2012, Oracle and/or its affiliates.
- Copyright (c) 2012, 2014, Monty Proram Ab.
+/* Copyright (c) 2006, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2012, 2013, Monty Proram 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
@@ -12,9 +12,11 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h" // REQUIRED: for other includes
#include "sql_show.h"
#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
#include "ha_ndbcluster.h"
@@ -28,11 +30,22 @@
#include "ndb_cluster_connection.hpp"
#include <util/NdbAutoPtr.hpp>
+#include "sql_base.h" // close_thread_tables
+#include "sql_table.h" // build_table_filename
+#include "table.h" // open_table_from_share
+#include "discover.h" // readfrm, writefrm
+#include "lock.h" // MYSQL_LOCK_IGNORE_FLUSH,
+ // mysql_unlock_tables
+#include "sql_parse.h" // mysql_parse
+#include "transaction.h"
+
#ifdef ndb_dynamite
#undef assert
#define assert(x) do { if(x) break; ::printf("%s %d: assert failed: %s\n", __FILE__, __LINE__, #x); ::fflush(stdout); ::signal(SIGABRT,SIG_DFL); ::abort(); ::kill(::getpid(),6); ::kill(::getpid(),9); } while (0)
#endif
+extern my_bool opt_ndb_log_binlog_index;
+extern ulong opt_ndb_extra_logging;
/*
defines for cluster replication table names
*/
@@ -44,14 +57,16 @@
Timeout for syncing schema events between
mysql servers, and between mysql server and the binlog
*/
-const int opt_ndb_sync_timeout= 120;
+static const int DEFAULT_SYNC_TIMEOUT= 120;
+
/*
Flag showing if the ndb injector thread is running, if so == 1
-1 if it was started but later stopped for some reason
0 if never started
*/
-int ndb_binlog_thread_running= 0;
+static int ndb_binlog_thread_running= 0;
+
/*
Flag showing if the ndb binlog should be created, if so == TRUE
FALSE if not
@@ -75,7 +90,7 @@ THD *injector_thd= 0;
to enable ndb injector thread receiving events.
Must therefore always be used with a surrounding
- pthread_mutex_lock(&injector_mutex), when doing create/dropEventOperation
+ mysql_mutex_lock(&injector_mutex), when doing create/dropEventOperation
*/
static Ndb *injector_ndb= 0;
static Ndb *schema_ndb= 0;
@@ -102,8 +117,8 @@ static int ndbcluster_binlog_terminating= 0;
and injector thread
*/
pthread_t ndb_binlog_thread;
-pthread_mutex_t injector_mutex;
-pthread_cond_t injector_cond;
+mysql_mutex_t injector_mutex;
+mysql_cond_t injector_cond;
/* NDB Injector thread (used for binlog creation) */
static ulonglong ndb_latest_applied_binlog_epoch= 0;
@@ -112,7 +127,7 @@ static ulonglong ndb_latest_received_binlog_epoch= 0;
NDB_SHARE *ndb_apply_status_share= 0;
NDB_SHARE *ndb_schema_share= 0;
-pthread_mutex_t ndb_schema_share_mutex;
+mysql_mutex_t ndb_schema_share_mutex;
extern my_bool opt_log_slave_updates;
static my_bool g_ndb_log_slave_updates;
@@ -120,7 +135,7 @@ static my_bool g_ndb_log_slave_updates;
/* Schema object distribution handling */
HASH ndb_schema_objects;
typedef struct st_ndb_schema_object {
- pthread_mutex_t mutex;
+ mysql_mutex_t mutex;
char *key;
uint key_length;
uint use_count;
@@ -247,24 +262,27 @@ static void run_query(THD *thd, char *buf, char *end,
struct system_status_var save_thd_status_var= thd->status_var;
THD_TRANS save_thd_transaction_all= thd->transaction.all;
THD_TRANS save_thd_transaction_stmt= thd->transaction.stmt;
- ulonglong save_thd_options= thd->options;
- DBUG_ASSERT(sizeof(save_thd_options) == sizeof(thd->options));
+ ulonglong save_thd_options= thd->variables.option_bits;
+ DBUG_ASSERT(sizeof(save_thd_options) == sizeof(thd->variables.option_bits));
NET save_thd_net= thd->net;
- const char* found_semicolon= NULL;
bzero((char*) &thd->net, sizeof(NET));
thd->set_query(buf, (uint) (end - buf));
thd->variables.pseudo_thread_id= thread_id;
thd->transaction.stmt.modified_non_trans_table= FALSE;
if (disable_binlog)
- thd->options&= ~OPTION_BIN_LOG;
+ thd->variables.option_bits&= ~OPTION_BIN_LOG;
DBUG_PRINT("query", ("%s", thd->query()));
DBUG_ASSERT(!thd->in_sub_stmt);
- DBUG_ASSERT(!thd->prelocked_mode);
+ DBUG_ASSERT(!thd->locked_tables_mode);
- mysql_parse(thd, thd->query(), thd->query_length(), &found_semicolon);
+ {
+ Parser_state parser_state;
+ if (!parser_state.init(thd, thd->query(), thd->query_length()))
+ mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);
+ }
if (no_print_error && thd->is_slave_error)
{
@@ -272,17 +290,16 @@ static void run_query(THD *thd, char *buf, char *end,
Thd_ndb *thd_ndb= get_thd_ndb(thd);
for (i= 0; no_print_error[i]; i++)
if ((thd_ndb->m_error_code == no_print_error[i]) ||
- (thd->main_da.sql_errno() == (unsigned) no_print_error[i]))
+ (thd->stmt_da->sql_errno() == (unsigned) no_print_error[i]))
break;
if (!no_print_error[i])
sql_print_error("NDB: %s: error %s %d(ndb: %d) %d %d",
buf,
- thd->main_da.message(),
- thd->main_da.sql_errno(),
+ thd->stmt_da->message(),
+ thd->stmt_da->sql_errno(),
thd_ndb->m_error_code,
(int) thd->is_error(), thd->is_slave_error);
}
- close_thread_tables(thd);
/*
XXX: this code is broken. mysql_parse()/mysql_reset_thd_for_next_command()
can not be called from within a statement, and
@@ -293,15 +310,16 @@ static void run_query(THD *thd, char *buf, char *end,
is called from ndbcluster_reset_logs(), which is called from
mysql_flush().
*/
- thd->main_da.reset_diagnostics_area();
+ thd->stmt_da->reset_diagnostics_area();
- thd->options= save_thd_options;
+ thd->variables.option_bits= save_thd_options;
thd->set_query(save_thd_query, save_thd_query_length);
thd->variables.pseudo_thread_id= save_thread_id;
thd->status_var= save_thd_status_var;
thd->transaction.all= save_thd_transaction_all;
thd->transaction.stmt= save_thd_transaction_stmt;
thd->net= save_thd_net;
+ thd->set_current_stmt_binlog_format_row();
if (thd == injector_thd)
{
@@ -343,7 +361,6 @@ ndbcluster_binlog_open_table(THD *thd, NDB_SHARE *share,
int error;
DBUG_ENTER("ndbcluster_binlog_open_table");
- safe_mutex_assert_owner(&LOCK_open);
init_tmp_table_share(thd, table_share, share->db, 0, share->table_name,
share->key);
if ((error= open_table_def(thd, table_share, 0)))
@@ -359,7 +376,9 @@ ndbcluster_binlog_open_table(THD *thd, NDB_SHARE *share,
free_table_share(table_share);
DBUG_RETURN(error);
}
+ mysql_mutex_lock(&LOCK_open);
assign_new_table_id(table_share);
+ mysql_mutex_unlock(&LOCK_open);
if (!reopen)
{
@@ -608,7 +627,7 @@ ndbcluster_binlog_log_query(handlerton *hton, THD *thd, enum_binlog_command binl
{
ndbcluster_log_schema_op(thd, 0, query, query_length,
db, table_name, 0, 0, type,
- 0, 0, 0);
+ 0, 0);
}
DBUG_VOID_RETURN;
}
@@ -637,28 +656,28 @@ static int ndbcluster_binlog_end(THD *thd)
however be a likely case as the ndbcluster_binlog_end is supposed to
be called before ndb_cluster_end().
*/
- pthread_mutex_lock(&LOCK_ndb_util_thread);
+ mysql_mutex_lock(&LOCK_ndb_util_thread);
/* Ensure mutex are not freed if ndb_cluster_end is running at same time */
ndb_util_thread_running++;
ndbcluster_terminating= 1;
- pthread_cond_signal(&COND_ndb_util_thread);
+ mysql_cond_signal(&COND_ndb_util_thread);
while (ndb_util_thread_running > 1)
- pthread_cond_wait(&COND_ndb_util_ready, &LOCK_ndb_util_thread);
+ mysql_cond_wait(&COND_ndb_util_ready, &LOCK_ndb_util_thread);
ndb_util_thread_running--;
- pthread_mutex_unlock(&LOCK_ndb_util_thread);
+ mysql_mutex_unlock(&LOCK_ndb_util_thread);
}
/* wait for injector thread to finish */
ndbcluster_binlog_terminating= 1;
- pthread_mutex_lock(&injector_mutex);
- pthread_cond_signal(&injector_cond);
+ mysql_mutex_lock(&injector_mutex);
+ mysql_cond_signal(&injector_cond);
while (ndb_binlog_thread_running > 0)
- pthread_cond_wait(&injector_cond, &injector_mutex);
- pthread_mutex_unlock(&injector_mutex);
+ mysql_cond_wait(&injector_cond, &injector_mutex);
+ mysql_mutex_unlock(&injector_mutex);
- pthread_mutex_destroy(&injector_mutex);
- pthread_cond_destroy(&injector_cond);
- pthread_mutex_destroy(&ndb_schema_share_mutex);
+ mysql_mutex_destroy(&injector_mutex);
+ mysql_cond_destroy(&injector_cond);
+ mysql_mutex_destroy(&ndb_schema_share_mutex);
#endif
DBUG_RETURN(0);
@@ -738,14 +757,14 @@ void ndbcluster_binlog_init_handlerton()
*/
static NDB_SHARE *ndbcluster_check_ndb_apply_status_share()
{
- pthread_mutex_lock(&ndbcluster_mutex);
+ mysql_mutex_lock(&ndbcluster_mutex);
- void *share= hash_search(&ndbcluster_open_tables,
- (uchar*) NDB_APPLY_TABLE_FILE,
- sizeof(NDB_APPLY_TABLE_FILE) - 1);
+ void *share= my_hash_search(&ndbcluster_open_tables,
+ (uchar*) NDB_APPLY_TABLE_FILE,
+ sizeof(NDB_APPLY_TABLE_FILE) - 1);
DBUG_PRINT("info",("ndbcluster_check_ndb_apply_status_share %s 0x%lx",
NDB_APPLY_TABLE_FILE, (long) share));
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
return (NDB_SHARE*) share;
}
@@ -756,14 +775,14 @@ static NDB_SHARE *ndbcluster_check_ndb_apply_status_share()
*/
static NDB_SHARE *ndbcluster_check_ndb_schema_share()
{
- pthread_mutex_lock(&ndbcluster_mutex);
+ mysql_mutex_lock(&ndbcluster_mutex);
- void *share= hash_search(&ndbcluster_open_tables,
- (uchar*) NDB_SCHEMA_TABLE_FILE,
- sizeof(NDB_SCHEMA_TABLE_FILE) - 1);
+ void *share= my_hash_search(&ndbcluster_open_tables,
+ (uchar*) NDB_SCHEMA_TABLE_FILE,
+ sizeof(NDB_SCHEMA_TABLE_FILE) - 1);
DBUG_PRINT("info",("ndbcluster_check_ndb_schema_share %s 0x%lx",
NDB_SCHEMA_TABLE_FILE, (long) share));
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
return (NDB_SHARE*) share;
}
@@ -788,7 +807,7 @@ static int ndbcluster_create_ndb_apply_status_table(THD *thd)
char buf[1024 + 1], *end;
- if (ndb_extra_logging)
+ if (opt_ndb_extra_logging)
sql_print_information("NDB: Creating " NDB_REP_DB "." NDB_APPLY_TABLE);
/*
@@ -798,7 +817,7 @@ static int ndbcluster_create_ndb_apply_status_table(THD *thd)
{
build_table_filename(buf, sizeof(buf) - 1,
NDB_REP_DB, NDB_APPLY_TABLE, reg_ext, 0);
- my_delete(buf, MYF(0));
+ mysql_file_delete(key_file_frm, buf, MYF(0));
}
/*
@@ -846,7 +865,7 @@ static int ndbcluster_create_schema_table(THD *thd)
char buf[1024 + 1], *end;
- if (ndb_extra_logging)
+ if (opt_ndb_extra_logging)
sql_print_information("NDB: Creating " NDB_REP_DB "." NDB_SCHEMA_TABLE);
/*
@@ -856,7 +875,7 @@ static int ndbcluster_create_schema_table(THD *thd)
{
build_table_filename(buf, sizeof(buf) - 1,
NDB_REP_DB, NDB_SCHEMA_TABLE, reg_ext, 0);
- my_delete(buf, MYF(0));
+ mysql_file_delete(key_file_frm, buf, MYF(0));
}
/*
@@ -891,9 +910,7 @@ int ndbcluster_setup_binlog_table_shares(THD *thd)
if (!ndb_schema_share &&
ndbcluster_check_ndb_schema_share() == 0)
{
- pthread_mutex_lock(&LOCK_open);
ndb_create_table_from_engine(thd, NDB_REP_DB, NDB_SCHEMA_TABLE);
- pthread_mutex_unlock(&LOCK_open);
if (!ndb_schema_share)
{
ndbcluster_create_schema_table(thd);
@@ -905,9 +922,7 @@ int ndbcluster_setup_binlog_table_shares(THD *thd)
if (!ndb_apply_status_share &&
ndbcluster_check_ndb_apply_status_share() == 0)
{
- pthread_mutex_lock(&LOCK_open);
ndb_create_table_from_engine(thd, NDB_REP_DB, NDB_APPLY_TABLE);
- pthread_mutex_unlock(&LOCK_open);
if (!ndb_apply_status_share)
{
ndbcluster_create_ndb_apply_status_table(thd);
@@ -917,14 +932,12 @@ int ndbcluster_setup_binlog_table_shares(THD *thd)
}
if (!ndbcluster_find_all_files(thd))
{
- pthread_mutex_lock(&LOCK_open);
ndb_binlog_tables_inited= TRUE;
- if (ndb_extra_logging)
+ if (opt_ndb_extra_logging)
sql_print_information("NDB Binlog: ndb tables writable");
- close_cached_tables(NULL, NULL, TRUE, FALSE, FALSE);
- pthread_mutex_unlock(&LOCK_open);
+ close_cached_tables(NULL, NULL, FALSE, LONG_TIMEOUT);
/* Signal injector thread that all is setup */
- pthread_cond_signal(&injector_cond);
+ mysql_cond_signal(&injector_cond);
}
return 0;
}
@@ -963,6 +976,21 @@ struct Cluster_schema
uint32 any_value;
};
+static void print_could_not_discover_error(THD *thd,
+ const Cluster_schema *schema)
+{
+ sql_print_error("NDB Binlog: Could not discover table '%s.%s' from "
+ "binlog schema event '%s' from node %d. "
+ "my_errno: %d",
+ schema->db, schema->name, schema->query,
+ schema->node_id, my_errno);
+ List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
+ MYSQL_ERROR *err;
+ while ((err= it++))
+ sql_print_warning("NDB Binlog: (%d)%s", err->get_sql_errno(),
+ err->get_message_text());
+}
+
/*
Transfer schema table data into corresponding struct
*/
@@ -982,7 +1010,7 @@ static void ndbcluster_get_schema(NDB_SHARE *share,
ptrdiff);
if (ret != 0)
{
- my_free(blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(blobs_buffer);
DBUG_PRINT("info", ("blob read error"));
DBUG_ASSERT(FALSE);
}
@@ -1033,7 +1061,7 @@ static void ndbcluster_get_schema(NDB_SHARE *share,
field++;
s->type= ((Field_long *)*field)->val_int();
/* free blobs buffer */
- my_free(blobs_buffer, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(blobs_buffer);
dbug_tmp_restore_column_map(table->read_set, old_map);
}
@@ -1202,7 +1230,7 @@ ndbcluster_update_slock(THD *thd,
char buf[1024];
my_snprintf(buf, sizeof(buf), "Could not release lock on '%s.%s'",
db, table_name);
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
ndb_error->code, ndb_error->message, buf);
}
@@ -1222,12 +1250,12 @@ static void ndb_report_waiting(const char *key,
{
ulonglong ndb_latest_epoch= 0;
const char *proc_info= "<no info>";
- pthread_mutex_lock(&injector_mutex);
+ mysql_mutex_lock(&injector_mutex);
if (injector_ndb)
ndb_latest_epoch= injector_ndb->getLatestGCI();
if (injector_thd)
proc_info= injector_thd->proc_info;
- pthread_mutex_unlock(&injector_mutex);
+ mysql_mutex_unlock(&injector_mutex);
sql_print_information("NDB %s:"
" waiting max %u sec for %s %s."
" epochs: (%u,%u,%u)"
@@ -1246,8 +1274,7 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share,
uint32 ndb_table_id,
uint32 ndb_table_version,
enum SCHEMA_OP_TYPE type,
- const char *new_db, const char *new_table_name,
- int have_lock_open)
+ const char *new_db, const char *new_table_name)
{
DBUG_ENTER("ndbcluster_log_schema_op");
Thd_ndb *thd_ndb= get_thd_ndb(thd);
@@ -1349,15 +1376,15 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share,
bitmap_set_all(&schema_subscribers);
/* begin protect ndb_schema_share */
- pthread_mutex_lock(&ndb_schema_share_mutex);
+ mysql_mutex_lock(&ndb_schema_share_mutex);
if (ndb_schema_share == 0)
{
- pthread_mutex_unlock(&ndb_schema_share_mutex);
+ mysql_mutex_unlock(&ndb_schema_share_mutex);
if (ndb_schema_object)
ndb_free_schema_object(&ndb_schema_object, FALSE);
DBUG_RETURN(0);
}
- (void) pthread_mutex_lock(&ndb_schema_share->mutex);
+ mysql_mutex_lock(&ndb_schema_share->mutex);
for (i= 0; i < no_storage_nodes; i++)
{
MY_BITMAP *table_subscribers= &ndb_schema_share->subscriber_bitmap[i];
@@ -1368,8 +1395,8 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share,
updated= 1;
}
}
- (void) pthread_mutex_unlock(&ndb_schema_share->mutex);
- pthread_mutex_unlock(&ndb_schema_share_mutex);
+ mysql_mutex_unlock(&ndb_schema_share->mutex);
+ mysql_mutex_unlock(&ndb_schema_share_mutex);
/* end protect ndb_schema_share */
if (updated)
@@ -1389,10 +1416,10 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share,
if (ndb_schema_object)
{
- (void) pthread_mutex_lock(&ndb_schema_object->mutex);
+ mysql_mutex_lock(&ndb_schema_object->mutex);
memcpy(ndb_schema_object->slock, schema_subscribers.bitmap,
sizeof(ndb_schema_object->slock));
- (void) pthread_mutex_unlock(&ndb_schema_object->mutex);
+ mysql_mutex_unlock(&ndb_schema_object->mutex);
}
DBUG_DUMP("schema_subscribers", (uchar*)schema_subscribers.bitmap,
@@ -1494,7 +1521,7 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share,
r|= op->setValue(SCHEMA_TYPE_I, log_type);
DBUG_ASSERT(r == 0);
/* any value */
- if (!(thd->options & OPTION_BIN_LOG))
+ if (!(thd->variables.option_bits & OPTION_BIN_LOG))
r|= op->setAnyValue(NDB_ANYVALUE_FOR_NOLOGGING);
else
r|= op->setAnyValue(thd->server_id);
@@ -1532,7 +1559,7 @@ err:
}
end:
if (ndb_error)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
ndb_error->code,
ndb_error->message,
@@ -1557,35 +1584,28 @@ end:
else
dict->forceGCPWait();
- int max_timeout= opt_ndb_sync_timeout;
- /* Inconsistent usage of ndb_schema_object->mutex and LOCK_open */
- (void) my_pthread_mutex_lock(&ndb_schema_object->mutex,
- MYF_NO_DEADLOCK_DETECTION);
- if (have_lock_open)
- {
- safe_mutex_assert_owner(&LOCK_open);
- (void) pthread_mutex_unlock(&LOCK_open);
- }
+ int max_timeout= DEFAULT_SYNC_TIMEOUT;
+ mysql_mutex_lock(&ndb_schema_object->mutex);
while (1)
{
struct timespec abstime;
int i;
int no_storage_nodes= g_ndb_cluster_connection->no_db_nodes();
set_timespec(abstime, 1);
- int ret= pthread_cond_timedwait(&injector_cond,
- &ndb_schema_object->mutex,
- &abstime);
+ int ret= mysql_cond_timedwait(&injector_cond,
+ &ndb_schema_object->mutex,
+ &abstime);
if (thd->killed)
break;
/* begin protect ndb_schema_share */
- pthread_mutex_lock(&ndb_schema_share_mutex);
+ mysql_mutex_lock(&ndb_schema_share_mutex);
if (ndb_schema_share == 0)
{
- pthread_mutex_unlock(&ndb_schema_share_mutex);
+ mysql_mutex_unlock(&ndb_schema_share_mutex);
break;
}
- (void) pthread_mutex_lock(&ndb_schema_share->mutex);
+ mysql_mutex_lock(&ndb_schema_share->mutex);
for (i= 0; i < no_storage_nodes; i++)
{
/* remove any unsubscribed from schema_subscribers */
@@ -1593,8 +1613,8 @@ end:
if (!bitmap_is_clear_all(tmp))
bitmap_intersect(&schema_subscribers, tmp);
}
- (void) pthread_mutex_unlock(&ndb_schema_share->mutex);
- pthread_mutex_unlock(&ndb_schema_share_mutex);
+ mysql_mutex_unlock(&ndb_schema_share->mutex);
+ mysql_mutex_unlock(&ndb_schema_share_mutex);
/* end protect ndb_schema_share */
/* remove any unsubscribed from ndb_schema_object->slock */
@@ -1616,16 +1636,12 @@ end:
type_str, ndb_schema_object->key);
break;
}
- if (ndb_extra_logging)
+ if (opt_ndb_extra_logging)
ndb_report_waiting(type_str, max_timeout,
"distributing", ndb_schema_object->key);
}
}
- if (have_lock_open)
- {
- (void) pthread_mutex_lock(&LOCK_open);
- }
- (void) pthread_mutex_unlock(&ndb_schema_object->mutex);
+ mysql_mutex_unlock(&ndb_schema_object->mutex);
}
if (ndb_schema_object)
@@ -1707,14 +1723,13 @@ ndb_handle_schema_change(THD *thd, Ndb *ndb, NdbEventOperation *pOp,
{
DBUG_DUMP("frm", (uchar*) altered_table->getFrmData(),
altered_table->getFrmLength());
- pthread_mutex_lock(&LOCK_open);
Ndb_table_guard ndbtab_g(dict, tabname);
const NDBTAB *old= ndbtab_g.get_table();
if (!old &&
old->getObjectVersion() != altered_table->getObjectVersion())
dict->putTable(altered_table);
- my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(data);
data= NULL;
if ((error= unpackfrm(&data, &length,
(const uchar*) altered_table->getFrmData())) ||
@@ -1733,7 +1748,7 @@ ndb_handle_schema_change(THD *thd, Ndb *ndb, NdbEventOperation *pOp,
bzero((char*) &table_list,sizeof(table_list));
table_list.db= (char *)dbname;
table_list.alias= table_list.table_name= (char *)tabname;
- close_cached_tables(thd, &table_list, TRUE, FALSE, FALSE);
+ close_cached_tables(thd, &table_list, FALSE, LONG_TIMEOUT);
if ((error= ndbcluster_binlog_open_table(thd, share,
table_share, table, 1)))
@@ -1744,28 +1759,26 @@ ndb_handle_schema_change(THD *thd, Ndb *ndb, NdbEventOperation *pOp,
table_share= share->table_share;
dbname= table_share->db.str;
tabname= table_share->table_name.str;
-
- pthread_mutex_unlock(&LOCK_open);
}
- my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR));
- my_free((char*)pack_data, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(data);
+ my_free(pack_data);
}
// If only frm was changed continue replicating
if (is_online_alter_table)
{
/* Signal ha_ndbcluster::alter_table that drop is done */
- (void) pthread_cond_signal(&injector_cond);
+ mysql_cond_signal(&injector_cond);
DBUG_RETURN(0);
}
- (void) pthread_mutex_lock(&share->mutex);
+ mysql_mutex_lock(&share->mutex);
if (is_rename_table && !is_remote_change)
{
DBUG_PRINT("info", ("Detected name change of table %s.%s",
share->db, share->table_name));
/* ToDo: remove printout */
- if (ndb_extra_logging)
+ if (opt_ndb_extra_logging)
sql_print_information("NDB Binlog: rename table %s%s/%s -> %s.",
share_prefix, share->table->s->db.str,
share->table->s->table_name.str,
@@ -1795,10 +1808,10 @@ ndb_handle_schema_change(THD *thd, Ndb *ndb, NdbEventOperation *pOp,
// either just us or drop table handling as well
/* Signal ha_ndbcluster::delete/rename_table that drop is done */
- (void) pthread_mutex_unlock(&share->mutex);
- (void) pthread_cond_signal(&injector_cond);
+ mysql_mutex_unlock(&share->mutex);
+ mysql_cond_signal(&injector_cond);
- pthread_mutex_lock(&ndbcluster_mutex);
+ mysql_mutex_lock(&ndbcluster_mutex);
/* ndb_share reference binlog free */
DBUG_PRINT("NDB_SHARE", ("%s binlog free use_count: %u",
share->key, share->use_count));
@@ -1824,14 +1837,14 @@ ndb_handle_schema_change(THD *thd, Ndb *ndb, NdbEventOperation *pOp,
}
else
share= 0;
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
pOp->setCustomData(0);
- pthread_mutex_lock(&injector_mutex);
+ mysql_mutex_lock(&injector_mutex);
ndb->dropEventOperation(pOp);
pOp= 0;
- pthread_mutex_unlock(&injector_mutex);
+ mysql_mutex_unlock(&injector_mutex);
if (do_close_cached_tables)
{
@@ -1839,7 +1852,7 @@ ndb_handle_schema_change(THD *thd, Ndb *ndb, NdbEventOperation *pOp,
bzero((char*) &table_list,sizeof(table_list));
table_list.db= (char *)dbname;
table_list.alias= table_list.table_name= (char *)tabname;
- close_cached_tables(thd, &table_list, FALSE, FALSE, FALSE);
+ close_cached_tables(thd, &table_list, FALSE, LONG_TIMEOUT);
/* ndb_share reference create free */
DBUG_PRINT("NDB_SHARE", ("%s create free use_count: %u",
share->key, share->use_count));
@@ -1868,7 +1881,7 @@ static void ndb_binlog_query(THD *thd, Cluster_schema *schema)
thd->db= schema->db;
int errcode = query_error_code(thd, thd->killed == NOT_KILLED);
thd->binlog_query(THD::STMT_QUERY_TYPE, schema->query,
- schema->query_length, FALSE,
+ schema->query_length, FALSE, TRUE,
schema->name[0] == 0 || thd->db[0] == 0,
errcode);
thd->server_id= thd_server_id_save;
@@ -1960,7 +1973,7 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb,
bzero((char*) &table_list,sizeof(table_list));
table_list.db= schema->db;
table_list.alias= table_list.table_name= schema->name;
- close_cached_tables(thd, &table_list, FALSE, FALSE, FALSE);
+ close_cached_tables(thd, &table_list, FALSE, LONG_TIMEOUT);
}
/* ndb_share reference temporary free */
if (share)
@@ -1972,7 +1985,6 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb,
}
// fall through
case SOT_CREATE_TABLE:
- pthread_mutex_lock(&LOCK_open);
if (ndbcluster_check_if_local_table(schema->db, schema->name))
{
DBUG_PRINT("info", ("NDB Binlog: Skipping locally defined table '%s.%s'",
@@ -1984,17 +1996,8 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb,
}
else if (ndb_create_table_from_engine(thd, schema->db, schema->name))
{
- sql_print_error("NDB Binlog: Could not discover table '%s.%s' from "
- "binlog schema event '%s' from node %d. "
- "my_errno: %d",
- schema->db, schema->name, schema->query,
- schema->node_id, my_errno);
- List_iterator_fast<MYSQL_ERROR> it(thd->warn_list);
- MYSQL_ERROR *err;
- while ((err= it++))
- sql_print_warning("NDB Binlog: (%d)%s", err->code, err->msg);
+ print_could_not_discover_error(thd, schema);
}
- pthread_mutex_unlock(&LOCK_open);
log_query= 1;
break;
case SOT_DROP_DB:
@@ -2063,18 +2066,18 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb,
// skip
break;
case NDBEVENT::TE_CLUSTER_FAILURE:
- if (ndb_extra_logging)
+ if (opt_ndb_extra_logging)
sql_print_information("NDB Binlog: cluster failure for %s at epoch %u.",
ndb_schema_share->key, (unsigned) pOp->getGCI());
// fall through
case NDBEVENT::TE_DROP:
- if (ndb_extra_logging &&
+ if (opt_ndb_extra_logging &&
ndb_binlog_tables_inited && ndb_binlog_running)
sql_print_information("NDB Binlog: ndb tables initially "
"read only on reconnect.");
/* begin protect ndb_schema_share */
- pthread_mutex_lock(&ndb_schema_share_mutex);
+ mysql_mutex_lock(&ndb_schema_share_mutex);
/* ndb_share reference binlog extra free */
DBUG_PRINT("NDB_SHARE", ("%s binlog extra free use_count: %u",
ndb_schema_share->key,
@@ -2082,10 +2085,10 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb,
free_share(&ndb_schema_share);
ndb_schema_share= 0;
ndb_binlog_tables_inited= 0;
- pthread_mutex_unlock(&ndb_schema_share_mutex);
+ mysql_mutex_unlock(&ndb_schema_share_mutex);
/* end protect ndb_schema_share */
- close_cached_tables(NULL, NULL, FALSE, FALSE, FALSE);
+ close_cached_tables(NULL, NULL, FALSE, LONG_TIMEOUT);
// fall through
case NDBEVENT::TE_ALTER:
ndb_handle_schema_change(thd, ndb, pOp, tmp_share);
@@ -2094,10 +2097,10 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb,
{
uint8 node_id= g_node_id_map[pOp->getNdbdNodeId()];
DBUG_ASSERT(node_id != 0xFF);
- (void) pthread_mutex_lock(&tmp_share->mutex);
+ mysql_mutex_lock(&tmp_share->mutex);
bitmap_clear_all(&tmp_share->subscriber_bitmap[node_id]);
DBUG_PRINT("info",("NODE_FAILURE UNSUBSCRIBE[%d]", node_id));
- if (ndb_extra_logging)
+ if (opt_ndb_extra_logging)
{
sql_print_information("NDB Binlog: Node: %d, down,"
" Subscriber bitmask %x%x",
@@ -2105,8 +2108,8 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb,
tmp_share->subscriber_bitmap[node_id].bitmap[1],
tmp_share->subscriber_bitmap[node_id].bitmap[0]);
}
- (void) pthread_mutex_unlock(&tmp_share->mutex);
- (void) pthread_cond_signal(&injector_cond);
+ mysql_mutex_unlock(&tmp_share->mutex);
+ mysql_cond_signal(&injector_cond);
break;
}
case NDBEVENT::TE_SUBSCRIBE:
@@ -2114,10 +2117,10 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb,
uint8 node_id= g_node_id_map[pOp->getNdbdNodeId()];
uint8 req_id= pOp->getReqNodeId();
DBUG_ASSERT(req_id != 0 && node_id != 0xFF);
- (void) pthread_mutex_lock(&tmp_share->mutex);
+ mysql_mutex_lock(&tmp_share->mutex);
bitmap_set_bit(&tmp_share->subscriber_bitmap[node_id], req_id);
DBUG_PRINT("info",("SUBSCRIBE[%d] %d", node_id, req_id));
- if (ndb_extra_logging)
+ if (opt_ndb_extra_logging)
{
sql_print_information("NDB Binlog: Node: %d, subscribe from node %d,"
" Subscriber bitmask %x%x",
@@ -2126,8 +2129,8 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb,
tmp_share->subscriber_bitmap[node_id].bitmap[1],
tmp_share->subscriber_bitmap[node_id].bitmap[0]);
}
- (void) pthread_mutex_unlock(&tmp_share->mutex);
- (void) pthread_cond_signal(&injector_cond);
+ mysql_mutex_unlock(&tmp_share->mutex);
+ mysql_cond_signal(&injector_cond);
break;
}
case NDBEVENT::TE_UNSUBSCRIBE:
@@ -2135,10 +2138,10 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb,
uint8 node_id= g_node_id_map[pOp->getNdbdNodeId()];
uint8 req_id= pOp->getReqNodeId();
DBUG_ASSERT(req_id != 0 && node_id != 0xFF);
- (void) pthread_mutex_lock(&tmp_share->mutex);
+ mysql_mutex_lock(&tmp_share->mutex);
bitmap_clear_bit(&tmp_share->subscriber_bitmap[node_id], req_id);
DBUG_PRINT("info",("UNSUBSCRIBE[%d] %d", node_id, req_id));
- if (ndb_extra_logging)
+ if (opt_ndb_extra_logging)
{
sql_print_information("NDB Binlog: Node: %d, unsubscribe from node %d,"
" Subscriber bitmask %x%x",
@@ -2147,8 +2150,8 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb,
tmp_share->subscriber_bitmap[node_id].bitmap[1],
tmp_share->subscriber_bitmap[node_id].bitmap[0]);
}
- (void) pthread_mutex_unlock(&tmp_share->mutex);
- (void) pthread_cond_signal(&injector_cond);
+ mysql_mutex_unlock(&tmp_share->mutex);
+ mysql_cond_signal(&injector_cond);
break;
}
default:
@@ -2188,22 +2191,22 @@ ndb_binlog_thread_handle_schema_event_post_epoch(THD *thd,
build_table_filename(key, sizeof(key) - 1, schema->db, schema->name, "", 0);
if (schema_type == SOT_CLEAR_SLOCK)
{
- pthread_mutex_lock(&ndbcluster_mutex);
+ mysql_mutex_lock(&ndbcluster_mutex);
NDB_SCHEMA_OBJECT *ndb_schema_object=
- (NDB_SCHEMA_OBJECT*) hash_search(&ndb_schema_objects,
- (uchar*) key, strlen(key));
+ (NDB_SCHEMA_OBJECT*) my_hash_search(&ndb_schema_objects,
+ (uchar*) key, strlen(key));
if (ndb_schema_object)
{
- pthread_mutex_lock(&ndb_schema_object->mutex);
+ mysql_mutex_lock(&ndb_schema_object->mutex);
memcpy(ndb_schema_object->slock, schema->slock,
sizeof(ndb_schema_object->slock));
DBUG_DUMP("ndb_schema_object->slock_bitmap.bitmap",
(uchar*)ndb_schema_object->slock_bitmap.bitmap,
no_bytes_in_map(&ndb_schema_object->slock_bitmap));
- pthread_mutex_unlock(&ndb_schema_object->mutex);
- pthread_cond_signal(&injector_cond);
+ mysql_mutex_unlock(&ndb_schema_object->mutex);
+ mysql_cond_signal(&injector_cond);
}
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
continue;
}
/* ndb_share reference temporary, free below */
@@ -2242,7 +2245,7 @@ ndb_binlog_thread_handle_schema_event_post_epoch(THD *thd,
bzero((char*) &table_list,sizeof(table_list));
table_list.db= schema->db;
table_list.alias= table_list.table_name= schema->name;
- close_cached_tables(thd, &table_list, FALSE, FALSE, FALSE);
+ close_cached_tables(thd, &table_list, FALSE, LONG_TIMEOUT);
}
if (schema_type != SOT_ALTER_TABLE)
break;
@@ -2263,7 +2266,6 @@ ndb_binlog_thread_handle_schema_event_post_epoch(THD *thd,
free_share(&share);
share= 0;
}
- pthread_mutex_lock(&LOCK_open);
if (ndbcluster_check_if_local_table(schema->db, schema->name))
{
DBUG_PRINT("info", ("NDB Binlog: Skipping locally defined table '%s.%s'",
@@ -2275,16 +2277,8 @@ ndb_binlog_thread_handle_schema_event_post_epoch(THD *thd,
}
else if (ndb_create_table_from_engine(thd, schema->db, schema->name))
{
- sql_print_error("NDB Binlog: Could not discover table '%s.%s' from "
- "binlog schema event '%s' from node %d. my_errno: %d",
- schema->db, schema->name, schema->query,
- schema->node_id, my_errno);
- List_iterator_fast<MYSQL_ERROR> it(thd->warn_list);
- MYSQL_ERROR *err;
- while ((err= it++))
- sql_print_warning("NDB Binlog: (%d)%s", err->code, err->msg);
+ print_could_not_discover_error(thd, schema);
}
- pthread_mutex_unlock(&LOCK_open);
}
break;
default:
@@ -2336,29 +2330,27 @@ struct ndb_binlog_index_row {
/*
Open the ndb_binlog_index table
*/
-static int open_ndb_binlog_index(THD *thd, TABLE_LIST *tables,
- TABLE **ndb_binlog_index)
+static int open_ndb_binlog_index(THD *thd, TABLE **ndb_binlog_index)
{
static char repdb[]= NDB_REP_DB;
static char reptable[]= NDB_REP_TABLE;
const char *save_proc_info= thd->proc_info;
+ TABLE_LIST *tables= &binlog_tables;
- bzero((char*) tables, sizeof(*tables));
- tables->db= repdb;
- tables->alias= tables->table_name= reptable;
- tables->lock_type= TL_WRITE;
+ tables->init_one_table(repdb, strlen(repdb), reptable, strlen(reptable),
+ reptable, TL_WRITE);
thd->proc_info= "Opening " NDB_REP_DB "." NDB_REP_TABLE;
+
tables->required_type= FRMTYPE_TABLE;
- uint counter;
thd->clear_error();
- if (open_tables(thd, &tables, &counter, MYSQL_LOCK_IGNORE_FLUSH))
+ if (open_and_lock_tables(thd, tables, FALSE, 0))
{
if (thd->killed)
sql_print_error("NDB Binlog: Opening ndb_binlog_index: killed");
else
sql_print_error("NDB Binlog: Opening ndb_binlog_index: %d, '%s'",
- thd->main_da.sql_errno(),
- thd->main_da.message());
+ thd->stmt_da->sql_errno(),
+ thd->stmt_da->message());
thd->proc_info= save_proc_info;
return -1;
}
@@ -2377,36 +2369,18 @@ int ndb_add_ndb_binlog_index(THD *thd, void *_row)
{
ndb_binlog_index_row &row= *(ndb_binlog_index_row *) _row;
int error= 0;
- bool need_reopen;
/*
Turn of binlogging to prevent the table changes to be written to
the binary log.
*/
- ulong saved_options= thd->options;
- thd->options&= ~(OPTION_BIN_LOG);
+ ulong saved_options= thd->variables.option_bits;
+ thd->variables.option_bits&= ~OPTION_BIN_LOG;
- for ( ; ; ) /* loop for need_reopen */
+ if (!ndb_binlog_index && open_ndb_binlog_index(thd, &ndb_binlog_index))
{
- if (!ndb_binlog_index && open_ndb_binlog_index(thd, &binlog_tables, &ndb_binlog_index))
- {
- error= -1;
- goto add_ndb_binlog_index_err;
- }
-
- if (lock_tables(thd, &binlog_tables, 1, &need_reopen))
- {
- if (need_reopen)
- {
- TABLE_LIST *p_binlog_tables= &binlog_tables;
- close_tables_for_reopen(thd, &p_binlog_tables);
- ndb_binlog_index= 0;
- continue;
- }
- sql_print_error("NDB Binlog: Unable to lock table ndb_binlog_index");
- error= -1;
- goto add_ndb_binlog_index_err;
- }
- break;
+ sql_print_error("NDB Binlog: Unable to lock table ndb_binlog_index");
+ error= -1;
+ goto add_ndb_binlog_index_err;
}
/*
@@ -2431,14 +2405,20 @@ int ndb_add_ndb_binlog_index(THD *thd, void *_row)
goto add_ndb_binlog_index_err;
}
- mysql_unlock_tables(thd, thd->lock);
- thd->lock= 0;
- thd->options= saved_options;
- return 0;
add_ndb_binlog_index_err:
+ thd->stmt_da->can_overwrite_status= TRUE;
+ thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
+ thd->stmt_da->can_overwrite_status= FALSE;
close_thread_tables(thd);
+ /*
+ There should be no need for rolling back transaction due to deadlock
+ (since ndb_binlog_index is non transactional).
+ */
+ DBUG_ASSERT(! thd->transaction_rollback_request);
+
+ thd->mdl_context.release_transactional_locks();
ndb_binlog_index= 0;
- thd->options= saved_options;
+ thd->variables.option_bits= saved_options;
return error;
}
@@ -2471,27 +2451,29 @@ int ndbcluster_binlog_start()
DBUG_RETURN(-1);
}
- pthread_mutex_init(&injector_mutex, MY_MUTEX_INIT_FAST);
- pthread_cond_init(&injector_cond, NULL);
- pthread_mutex_init(&ndb_schema_share_mutex, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_injector_mutex, &injector_mutex, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_injector_cond, &injector_cond, NULL);
+ mysql_mutex_init(key_ndb_schema_share_mutex,
+ &ndb_schema_share_mutex, MY_MUTEX_INIT_FAST);
/* Create injector thread */
- if (pthread_create(&ndb_binlog_thread, &connection_attrib,
- ndb_binlog_thread_func, 0))
+ if (mysql_thread_create(key_thread_ndb_binlog,
+ &ndb_binlog_thread, &connection_attrib,
+ ndb_binlog_thread_func, 0))
{
DBUG_PRINT("error", ("Could not create ndb injector thread"));
- pthread_cond_destroy(&injector_cond);
- pthread_mutex_destroy(&injector_mutex);
+ mysql_cond_destroy(&injector_cond);
+ mysql_mutex_destroy(&injector_mutex);
DBUG_RETURN(-1);
}
ndbcluster_binlog_inited= 1;
/* Wait for the injector thread to start */
- pthread_mutex_lock(&injector_mutex);
+ mysql_mutex_lock(&injector_mutex);
while (!ndb_binlog_thread_running)
- pthread_cond_wait(&injector_cond, &injector_mutex);
- pthread_mutex_unlock(&injector_mutex);
+ mysql_cond_wait(&injector_cond, &injector_mutex);
+ mysql_mutex_unlock(&injector_mutex);
if (ndb_binlog_thread_running < 0)
DBUG_RETURN(-1);
@@ -2581,11 +2563,11 @@ int ndbcluster_create_binlog_setup(Ndb *ndb, const char *key,
DBUG_ASSERT(! IS_NDB_BLOB_PREFIX(table_name));
DBUG_ASSERT(strlen(key) == key_len);
- pthread_mutex_lock(&ndbcluster_mutex);
+ mysql_mutex_lock(&ndbcluster_mutex);
/* Handle any trailing share */
- NDB_SHARE *share= (NDB_SHARE*) hash_search(&ndbcluster_open_tables,
- (uchar*) key, key_len);
+ NDB_SHARE *share= (NDB_SHARE*) my_hash_search(&ndbcluster_open_tables,
+ (uchar*) key, key_len);
if (share && share_may_exist)
{
@@ -2593,7 +2575,7 @@ int ndbcluster_create_binlog_setup(Ndb *ndb, const char *key,
share->op != 0 ||
share->op_old != 0)
{
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
DBUG_RETURN(0); // replication already setup, or should not
}
}
@@ -2603,7 +2585,7 @@ int ndbcluster_create_binlog_setup(Ndb *ndb, const char *key,
if (share->op || share->op_old)
{
my_errno= HA_ERR_TABLE_EXIST;
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
DBUG_RETURN(1);
}
if (!share_may_exist || share->connect_count !=
@@ -2646,10 +2628,10 @@ int ndbcluster_create_binlog_setup(Ndb *ndb, const char *key,
if (!do_event_op)
{
share->flags|= NSF_NO_BINLOG;
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
DBUG_RETURN(0);
}
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
while (share && !IS_TMP_PREFIX(table_name))
{
@@ -2668,7 +2650,7 @@ int ndbcluster_create_binlog_setup(Ndb *ndb, const char *key,
const NDBTAB *ndbtab= ndbtab_g.get_table();
if (ndbtab == 0)
{
- if (ndb_extra_logging)
+ if (opt_ndb_extra_logging)
sql_print_information("NDB Binlog: Failed to get table %s from ndb: "
"%s, %d", key, dict->getNdbError().message,
dict->getNdbError().code);
@@ -2690,7 +2672,7 @@ int ndbcluster_create_binlog_setup(Ndb *ndb, const char *key,
event_name.c_ptr());
break; // error
}
- if (ndb_extra_logging)
+ if (opt_ndb_extra_logging)
sql_print_information("NDB Binlog: "
"CREATE (DISCOVER) TABLE Event: %s",
event_name.c_ptr());
@@ -2698,7 +2680,7 @@ int ndbcluster_create_binlog_setup(Ndb *ndb, const char *key,
else
{
delete ev;
- if (ndb_extra_logging)
+ if (opt_ndb_extra_logging)
sql_print_information("NDB Binlog: DISCOVER TABLE Event: %s",
event_name.c_ptr());
}
@@ -2754,7 +2736,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab,
"with BLOB attribute and no PK is not supported",
share->key);
if (push_warning)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_ILLEGAL_HA_CREATE_OPTION,
ER(ER_ILLEGAL_HA_CREATE_OPTION),
ndbcluster_hton_name,
@@ -2798,7 +2780,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab,
failed, print a warning
*/
if (push_warning > 1)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
dict->getNdbError().code,
dict->getNdbError().message, "NDB");
@@ -2826,7 +2808,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab,
dict->dropEvent(my_event.getName()))
{
if (push_warning > 1)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
dict->getNdbError().code,
dict->getNdbError().message, "NDB");
@@ -2845,7 +2827,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab,
if (dict->createEvent(my_event))
{
if (push_warning > 1)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
dict->getNdbError().code,
dict->getNdbError().message, "NDB");
@@ -2858,7 +2840,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab,
DBUG_RETURN(-1);
}
#ifdef NDB_BINLOG_EXTRA_WARNINGS
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
0, "NDB Binlog: Removed trailing event",
"NDB");
@@ -2942,14 +2924,14 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab,
int retry_sleep= 100;
while (1)
{
- pthread_mutex_lock(&injector_mutex);
+ mysql_mutex_lock(&injector_mutex);
Ndb *ndb= injector_ndb;
if (do_ndb_schema_share)
ndb= schema_ndb;
if (ndb == 0)
{
- pthread_mutex_unlock(&injector_mutex);
+ mysql_mutex_unlock(&injector_mutex);
DBUG_RETURN(-1);
}
@@ -2969,12 +2951,12 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab,
{
sql_print_error("NDB Binlog: Creating NdbEventOperation failed for"
" %s",event_name);
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
ndb->getNdbError().code,
ndb->getNdbError().message,
"NDB");
- pthread_mutex_unlock(&injector_mutex);
+ mysql_mutex_unlock(&injector_mutex);
DBUG_RETURN(-1);
}
@@ -3018,13 +3000,13 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab,
sql_print_error("NDB Binlog: Creating NdbEventOperation"
" blob field %u handles failed (code=%d) for %s",
j, op->getNdbError().code, event_name);
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
op->getNdbError().code,
op->getNdbError().message,
"NDB");
ndb->dropEventOperation(op);
- pthread_mutex_unlock(&injector_mutex);
+ mysql_mutex_unlock(&injector_mutex);
DBUG_RETURN(-1);
}
}
@@ -3057,7 +3039,7 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab,
retries= 0;
if (retries == 0)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
op->getNdbError().code, op->getNdbError().message,
"NDB");
@@ -3066,7 +3048,7 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab,
op->getNdbError().code, op->getNdbError().message);
}
ndb->dropEventOperation(op);
- pthread_mutex_unlock(&injector_mutex);
+ mysql_mutex_unlock(&injector_mutex);
if (retries)
{
my_sleep(retry_sleep);
@@ -3074,7 +3056,7 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab,
}
DBUG_RETURN(-1);
}
- pthread_mutex_unlock(&injector_mutex);
+ mysql_mutex_unlock(&injector_mutex);
break;
}
@@ -3088,7 +3070,7 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab,
ndb_apply_status_share= get_share(share);
DBUG_PRINT("NDB_SHARE", ("%s binlog extra use_count: %u",
share->key, share->use_count));
- (void) pthread_cond_signal(&injector_cond);
+ mysql_cond_signal(&injector_cond);
}
else if (do_ndb_schema_share)
{
@@ -3096,13 +3078,13 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab,
ndb_schema_share= get_share(share);
DBUG_PRINT("NDB_SHARE", ("%s binlog extra use_count: %u",
share->key, share->use_count));
- (void) pthread_cond_signal(&injector_cond);
+ mysql_cond_signal(&injector_cond);
}
DBUG_PRINT("info",("%s share->op: 0x%lx share->use_count: %u",
share->key, (long) share->op, share->use_count));
- if (ndb_extra_logging)
+ if (opt_ndb_extra_logging)
sql_print_information("NDB Binlog: logging %s", share->key);
DBUG_RETURN(0);
}
@@ -3125,7 +3107,7 @@ ndbcluster_handle_drop_table(Ndb *ndb, const char *event_name,
if (dict->getNdbError().code != 4710)
{
/* drop event failed for some reason, issue a warning */
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
dict->getNdbError().code,
dict->getNdbError().message, "NDB");
@@ -3167,17 +3149,15 @@ ndbcluster_handle_drop_table(Ndb *ndb, const char *event_name,
#define SYNC_DROP_
#ifdef SYNC_DROP_
thd->proc_info= "Syncing ndb table schema operation and binlog";
- (void) pthread_mutex_lock(&share->mutex);
- safe_mutex_assert_owner(&LOCK_open);
- (void) pthread_mutex_unlock(&LOCK_open);
- int max_timeout= opt_ndb_sync_timeout;
+ mysql_mutex_lock(&share->mutex);
+ int max_timeout= DEFAULT_SYNC_TIMEOUT;
while (share->op)
{
struct timespec abstime;
set_timespec(abstime, 1);
- int ret= pthread_cond_timedwait(&injector_cond,
- &share->mutex,
- &abstime);
+ int ret= mysql_cond_timedwait(&injector_cond,
+ &share->mutex,
+ &abstime);
if (thd->killed ||
share->op == 0)
break;
@@ -3190,18 +3170,17 @@ ndbcluster_handle_drop_table(Ndb *ndb, const char *event_name,
type_str, share->key);
break;
}
- if (ndb_extra_logging)
+ if (opt_ndb_extra_logging)
ndb_report_waiting(type_str, max_timeout,
type_str, share->key);
}
}
- (void) pthread_mutex_lock(&LOCK_open);
- (void) pthread_mutex_unlock(&share->mutex);
+ mysql_mutex_unlock(&share->mutex);
#else
- (void) pthread_mutex_lock(&share->mutex);
+ mysql_mutex_lock(&share->mutex);
share->op_old= share->op;
share->op= 0;
- (void) pthread_mutex_unlock(&share->mutex);
+ mysql_mutex_unlock(&share->mutex);
#endif
thd->proc_info= save_proc_info;
@@ -3268,12 +3247,12 @@ ndb_binlog_thread_handle_non_data_event(THD *thd, Ndb *ndb,
switch (type)
{
case NDBEVENT::TE_CLUSTER_FAILURE:
- if (ndb_extra_logging)
+ if (opt_ndb_extra_logging)
sql_print_information("NDB Binlog: cluster failure for %s at epoch %u.",
share->key, (unsigned) pOp->getGCI());
if (ndb_apply_status_share == share)
{
- if (ndb_extra_logging &&
+ if (opt_ndb_extra_logging &&
ndb_binlog_tables_inited && ndb_binlog_running)
sql_print_information("NDB Binlog: ndb tables initially "
"read only on reconnect.");
@@ -3293,7 +3272,7 @@ ndb_binlog_thread_handle_non_data_event(THD *thd, Ndb *ndb,
case NDBEVENT::TE_DROP:
if (ndb_apply_status_share == share)
{
- if (ndb_extra_logging &&
+ if (opt_ndb_extra_logging &&
ndb_binlog_tables_inited && ndb_binlog_running)
sql_print_information("NDB Binlog: ndb tables initially "
"read only on reconnect.");
@@ -3305,7 +3284,7 @@ ndb_binlog_thread_handle_non_data_event(THD *thd, Ndb *ndb,
ndb_binlog_tables_inited= 0;
}
/* ToDo: remove printout */
- if (ndb_extra_logging)
+ if (opt_ndb_extra_logging)
sql_print_information("NDB Binlog: drop table %s.", share->key);
// fall through
case NDBEVENT::TE_ALTER:
@@ -3403,14 +3382,14 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp,
if (share->flags & NSF_BLOB_FLAG)
{
my_ptrdiff_t ptrdiff= 0;
- IF_DBUG(int ret =) get_ndb_blobs_value(table, share->ndb_value[0],
+ int ret __attribute__((unused))= get_ndb_blobs_value(table, share->ndb_value[0],
blobs_buffer[0],
blobs_buffer_size[0],
ptrdiff);
DBUG_ASSERT(ret == 0);
}
ndb_unpack_record(table, share->ndb_value[0], &b, table->record[0]);
- IF_DBUG(int ret=) trans.write_row(originating_server_id,
+ int ret __attribute__((unused))= trans.write_row(originating_server_id,
injector::transaction::table(table,
TRUE),
&b, n_fields, table->record[0]);
@@ -3442,7 +3421,7 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp,
if (share->flags & NSF_BLOB_FLAG)
{
my_ptrdiff_t ptrdiff= table->record[n] - table->record[0];
- IF_DBUG(int ret =) get_ndb_blobs_value(table, share->ndb_value[n],
+ int ret __attribute__((unused))= get_ndb_blobs_value(table, share->ndb_value[n],
blobs_buffer[n],
blobs_buffer_size[n],
ptrdiff);
@@ -3450,7 +3429,7 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp,
}
ndb_unpack_record(table, share->ndb_value[n], &b, table->record[n]);
DBUG_EXECUTE("info", print_records(table, table->record[n]););
- IF_DBUG(int ret =) trans.delete_row(originating_server_id,
+ int ret __attribute__((unused))= trans.delete_row(originating_server_id,
injector::transaction::table(table,
TRUE),
&b, n_fields, table->record[n]);
@@ -3465,7 +3444,7 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp,
if (share->flags & NSF_BLOB_FLAG)
{
my_ptrdiff_t ptrdiff= 0;
- IF_DBUG(int ret =) get_ndb_blobs_value(table, share->ndb_value[0],
+ int ret __attribute__((unused))= get_ndb_blobs_value(table, share->ndb_value[0],
blobs_buffer[0],
blobs_buffer_size[0],
ptrdiff);
@@ -3493,7 +3472,7 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp,
if (share->flags & NSF_BLOB_FLAG)
{
my_ptrdiff_t ptrdiff= table->record[1] - table->record[0];
- IF_DBUG(int ret =) get_ndb_blobs_value(table, share->ndb_value[1],
+ int ret __attribute__((unused))= get_ndb_blobs_value(table, share->ndb_value[1],
blobs_buffer[1],
blobs_buffer_size[1],
ptrdiff);
@@ -3501,7 +3480,7 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp,
}
ndb_unpack_record(table, share->ndb_value[1], &b, table->record[1]);
DBUG_EXECUTE("info", print_records(table, table->record[1]););
- IF_DBUG(int ret =) trans.update_row(originating_server_id,
+ int ret __attribute__((unused))= trans.update_row(originating_server_id,
injector::transaction::table(table,
TRUE),
&b, n_fields,
@@ -3519,8 +3498,8 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp,
if (share->flags & NSF_BLOB_FLAG)
{
- my_free(blobs_buffer[0], MYF(MY_ALLOW_ZERO_PTR));
- my_free(blobs_buffer[1], MYF(MY_ALLOW_ZERO_PTR));
+ my_free(blobs_buffer[0]);
+ my_free(blobs_buffer[1]);
}
return 0;
@@ -3569,11 +3548,11 @@ static NDB_SCHEMA_OBJECT *ndb_get_schema_object(const char *key,
DBUG_PRINT("enter", ("key: '%s'", key));
if (!have_lock)
- pthread_mutex_lock(&ndbcluster_mutex);
+ mysql_mutex_lock(&ndbcluster_mutex);
while (!(ndb_schema_object=
- (NDB_SCHEMA_OBJECT*) hash_search(&ndb_schema_objects,
- (uchar*) key,
- length)))
+ (NDB_SCHEMA_OBJECT*) my_hash_search(&ndb_schema_objects,
+ (uchar*) key,
+ length)))
{
if (!create_if_not_exists)
{
@@ -3592,10 +3571,10 @@ static NDB_SCHEMA_OBJECT *ndb_get_schema_object(const char *key,
ndb_schema_object->key_length= length;
if (my_hash_insert(&ndb_schema_objects, (uchar*) ndb_schema_object))
{
- my_free((uchar*) ndb_schema_object, 0);
+ my_free(ndb_schema_object);
break;
}
- pthread_mutex_init(&ndb_schema_object->mutex, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_ndb_schema_object_mutex, &ndb_schema_object->mutex, MY_MUTEX_INIT_FAST);
bitmap_init(&ndb_schema_object->slock_bitmap, ndb_schema_object->slock,
sizeof(ndb_schema_object->slock)*8, FALSE);
bitmap_clear_all(&ndb_schema_object->slock_bitmap);
@@ -3607,7 +3586,7 @@ static NDB_SCHEMA_OBJECT *ndb_get_schema_object(const char *key,
DBUG_PRINT("info", ("use_count: %d", ndb_schema_object->use_count));
}
if (!have_lock)
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
DBUG_RETURN(ndb_schema_object);
}
@@ -3618,13 +3597,13 @@ static void ndb_free_schema_object(NDB_SCHEMA_OBJECT **ndb_schema_object,
DBUG_ENTER("ndb_free_schema_object");
DBUG_PRINT("enter", ("key: '%s'", (*ndb_schema_object)->key));
if (!have_lock)
- pthread_mutex_lock(&ndbcluster_mutex);
+ mysql_mutex_lock(&ndbcluster_mutex);
if (!--(*ndb_schema_object)->use_count)
{
DBUG_PRINT("info", ("use_count: %d", (*ndb_schema_object)->use_count));
- hash_delete(&ndb_schema_objects, (uchar*) *ndb_schema_object);
- pthread_mutex_destroy(&(*ndb_schema_object)->mutex);
- my_free((uchar*) *ndb_schema_object, MYF(0));
+ my_hash_delete(&ndb_schema_objects, (uchar*) *ndb_schema_object);
+ mysql_mutex_destroy(&(*ndb_schema_object)->mutex);
+ my_free(*ndb_schema_object);
*ndb_schema_object= 0;
}
else
@@ -3632,10 +3611,12 @@ static void ndb_free_schema_object(NDB_SCHEMA_OBJECT **ndb_schema_object,
DBUG_PRINT("info", ("use_count: %d", (*ndb_schema_object)->use_count));
}
if (!have_lock)
- pthread_mutex_unlock(&ndbcluster_mutex);
+ mysql_mutex_unlock(&ndbcluster_mutex);
DBUG_VOID_RETURN;
}
+extern ulong opt_ndb_report_thresh_binlog_epoch_slip;
+extern ulong opt_ndb_report_thresh_binlog_mem_usage;
pthread_handler_t ndb_binlog_thread_func(void *arg)
{
@@ -3651,7 +3632,7 @@ pthread_handler_t ndb_binlog_thread_func(void *arg)
Timer main_timer;
#endif
- pthread_mutex_lock(&injector_mutex);
+ mysql_mutex_lock(&injector_mutex);
/*
Set up the Thread
*/
@@ -3660,13 +3641,16 @@ pthread_handler_t ndb_binlog_thread_func(void *arg)
thd= new THD; /* note that contructor of THD uses DBUG_ */
THD_CHECK_SENTRY(thd);
+ thd->set_current_stmt_binlog_format_row();
/* We need to set thd->thread_id before thd->store_globals, or it will
set an invalid value for thd->variables.pseudo_thread_id.
*/
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
thd->thread_id= thread_id++;
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
+
+ mysql_thread_set_psi_id(thd->thread_id);
thd->thread_stack= (char*) &thd; /* remember where our stack is */
if (thd->store_globals())
@@ -3674,25 +3658,25 @@ pthread_handler_t ndb_binlog_thread_func(void *arg)
thd->cleanup();
delete thd;
ndb_binlog_thread_running= -1;
- pthread_mutex_unlock(&injector_mutex);
- pthread_cond_signal(&injector_cond);
+ mysql_mutex_unlock(&injector_mutex);
+ mysql_cond_signal(&injector_cond);
DBUG_LEAVE; // Must match DBUG_ENTER()
my_thread_end();
pthread_exit(0);
return NULL; // Avoid compiler warnings
}
- lex_start(thd);
thd->init_for_queries();
thd->command= COM_DAEMON;
thd->system_thread= SYSTEM_THREAD_NDBCLUSTER_BINLOG;
- thd->version= refresh_version;
thd->main_security_ctx.host_or_ip= "";
thd->client_capabilities= 0;
my_net_init(&thd->net, 0);
thd->main_security_ctx.master_access= ~0;
thd->main_security_ctx.priv_user[0]= 0;
+ /* Do not use user-supplied timeout value for system threads. */
+ thd->variables.lock_wait_timeout= LONG_TIMEOUT;
/*
Set up ndb binlog
@@ -3701,9 +3685,9 @@ pthread_handler_t ndb_binlog_thread_func(void *arg)
pthread_detach_this_thread();
thd->real_id= pthread_self();
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
threads.append(thd);
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
thd->lex->start_transaction_opt= 0;
if (!(s_ndb= new Ndb(g_ndb_cluster_connection, "")) ||
@@ -3711,8 +3695,8 @@ pthread_handler_t ndb_binlog_thread_func(void *arg)
{
sql_print_error("NDB Binlog: Getting Schema Ndb object failed");
ndb_binlog_thread_running= -1;
- pthread_mutex_unlock(&injector_mutex);
- pthread_cond_signal(&injector_cond);
+ mysql_mutex_unlock(&injector_mutex);
+ mysql_cond_signal(&injector_cond);
goto err;
}
@@ -3722,21 +3706,21 @@ pthread_handler_t ndb_binlog_thread_func(void *arg)
{
sql_print_error("NDB Binlog: Getting Ndb object failed");
ndb_binlog_thread_running= -1;
- pthread_mutex_unlock(&injector_mutex);
- pthread_cond_signal(&injector_cond);
+ mysql_mutex_unlock(&injector_mutex);
+ mysql_cond_signal(&injector_cond);
goto err;
}
/* init hash for schema object distribution */
- (void) hash_init(&ndb_schema_objects, system_charset_info, 32, 0, 0,
- (hash_get_key)ndb_schema_objects_get_key, 0, 0);
+ (void) my_hash_init(&ndb_schema_objects, system_charset_info, 32, 0, 0,
+ (my_hash_get_key)ndb_schema_objects_get_key, 0, 0);
/*
Expose global reference to our ndb object.
Used by both sql client thread and binlog thread to interact
with the storage
- pthread_mutex_lock(&injector_mutex);
+ mysql_mutex_lock(&injector_mutex);
*/
injector_thd= thd;
injector_ndb= i_ndb;
@@ -3751,27 +3735,27 @@ pthread_handler_t ndb_binlog_thread_func(void *arg)
/* Thread start up completed */
ndb_binlog_thread_running= 1;
- pthread_mutex_unlock(&injector_mutex);
- pthread_cond_signal(&injector_cond);
+ mysql_mutex_unlock(&injector_mutex);
+ mysql_cond_signal(&injector_cond);
/*
wait for mysql server to start (so that the binlog is started
and thus can receive the first GAP event)
*/
- pthread_mutex_lock(&LOCK_server_started);
+ mysql_mutex_lock(&LOCK_server_started);
while (!mysqld_server_started)
{
struct timespec abstime;
set_timespec(abstime, 1);
- pthread_cond_timedwait(&COND_server_started, &LOCK_server_started,
- &abstime);
+ mysql_cond_timedwait(&COND_server_started, &LOCK_server_started,
+ &abstime);
if (ndbcluster_terminating)
{
- pthread_mutex_unlock(&LOCK_server_started);
+ mysql_mutex_unlock(&LOCK_server_started);
goto err;
}
}
- pthread_mutex_unlock(&LOCK_server_started);
+ mysql_mutex_unlock(&LOCK_server_started);
restart:
/*
Main NDB Injector loop
@@ -3805,7 +3789,7 @@ restart:
{ C_STRING_WITH_LEN("mysqld startup") },
{ C_STRING_WITH_LEN("cluster disconnect")}
};
- IF_DBUG(int error=)
+ int error __attribute__((unused))=
inj->record_incident(thd, INCIDENT_LOST_EVENTS, msg[incident_id]);
DBUG_ASSERT(!error);
break;
@@ -3814,21 +3798,21 @@ restart:
{
thd->proc_info= "Waiting for ndbcluster to start";
- pthread_mutex_lock(&injector_mutex);
+ mysql_mutex_lock(&injector_mutex);
while (!ndb_schema_share ||
(ndb_binlog_running && !ndb_apply_status_share))
{
/* ndb not connected yet */
struct timespec abstime;
set_timespec(abstime, 1);
- pthread_cond_timedwait(&injector_cond, &injector_mutex, &abstime);
+ mysql_cond_timedwait(&injector_cond, &injector_mutex, &abstime);
if (ndbcluster_binlog_terminating)
{
- pthread_mutex_unlock(&injector_mutex);
+ mysql_mutex_unlock(&injector_mutex);
goto err;
}
}
- pthread_mutex_unlock(&injector_mutex);
+ mysql_mutex_unlock(&injector_mutex);
if (thd_ndb == NULL)
{
@@ -3895,7 +3879,7 @@ restart:
"Changes to the database that occured while "
"disconnected will not be in the binlog");
}
- if (ndb_extra_logging)
+ if (opt_ndb_extra_logging)
{
sql_print_information("NDB Binlog: starting log at epoch %u",
(unsigned)schema_gci);
@@ -3905,9 +3889,6 @@ restart:
{
static char db[]= "";
thd->db= db;
- if (ndb_binlog_running)
- open_ndb_binlog_index(thd, &binlog_tables, &ndb_binlog_index);
- thd->db= db;
}
do_ndbcluster_binlog_close_connection= BCCC_running;
for ( ; !((ndbcluster_binlog_terminating ||
@@ -3975,11 +3956,13 @@ restart:
!ndb_binlog_running))
break; /* Shutting down server */
- if (ndb_binlog_index && ndb_binlog_index->s->version < refresh_version)
+ if (ndb_binlog_index && ndb_binlog_index->s->has_old_version())
{
- if (ndb_binlog_index->s->version < refresh_version)
+ if (ndb_binlog_index->s->has_old_version())
{
+ trans_commit_stmt(thd);
close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
ndb_binlog_index= 0;
}
}
@@ -3997,9 +3980,9 @@ restart:
{
thd->proc_info= "Processing events from schema table";
s_ndb->
- setReportThreshEventGCISlip(ndb_report_thresh_binlog_epoch_slip);
+ setReportThreshEventGCISlip(opt_ndb_report_thresh_binlog_epoch_slip);
s_ndb->
- setReportThreshEventFreeMem(ndb_report_thresh_binlog_mem_usage);
+ setReportThreshEventFreeMem(opt_ndb_report_thresh_binlog_mem_usage);
NdbEventOperation *pOp= s_ndb->nextEvent();
while (pOp != NULL)
{
@@ -4062,8 +4045,8 @@ restart:
/* initialize some variables for this epoch */
g_ndb_log_slave_updates= opt_log_slave_updates;
i_ndb->
- setReportThreshEventGCISlip(ndb_report_thresh_binlog_epoch_slip);
- i_ndb->setReportThreshEventFreeMem(ndb_report_thresh_binlog_mem_usage);
+ setReportThreshEventGCISlip(opt_ndb_report_thresh_binlog_epoch_slip);
+ i_ndb->setReportThreshEventFreeMem(opt_ndb_report_thresh_binlog_mem_usage);
bzero((char*) &row, sizeof(row));
thd->variables.character_set_client= &my_charset_latin1;
@@ -4120,7 +4103,7 @@ restart:
DBUG_PRINT("info", ("use_table: %.*s",
(int) name.length, name.str));
injector::transaction::table tbl(table, TRUE);
- IF_DBUG(int ret=) trans.use_table(::server_id, tbl);
+ int ret __attribute__((unused))= trans.use_table(::server_id, tbl);
DBUG_ASSERT(ret == 0);
}
}
@@ -4136,7 +4119,7 @@ restart:
(int) name.length, name.str));
#endif
injector::transaction::table tbl(table, TRUE);
- IF_DBUG(int ret=) trans.use_table(::server_id, tbl);
+ int ret __attribute__((unused))= trans.use_table(::server_id, tbl);
DBUG_ASSERT(ret == 0);
/*
@@ -4206,7 +4189,7 @@ restart:
else
{
// set injector_ndb database/schema from table internal name
- IF_DBUG(int ret=)
+ int ret __attribute__((unused))=
i_ndb->setDatabaseAndSchemaName(pOp->getEvent()->getTable());
DBUG_ASSERT(ret == 0);
ndb_binlog_thread_handle_non_data_event(thd, i_ndb, pOp, row);
@@ -4290,7 +4273,9 @@ restart:
if (do_ndbcluster_binlog_close_connection == BCCC_restart)
{
ndb_binlog_tables_inited= FALSE;
+ trans_commit_stmt(thd);
close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
ndb_binlog_index= 0;
goto restart;
}
@@ -4298,14 +4283,18 @@ err:
sql_print_information("Stopping Cluster Binlog");
DBUG_PRINT("info",("Shutting down cluster binlog thread"));
thd->proc_info= "Shutting down";
+ thd->stmt_da->can_overwrite_status= TRUE;
+ thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
+ thd->stmt_da->can_overwrite_status= FALSE;
close_thread_tables(thd);
- pthread_mutex_lock(&injector_mutex);
+ thd->mdl_context.release_transactional_locks();
+ mysql_mutex_lock(&injector_mutex);
/* don't mess with the injector_ndb anymore from other threads */
injector_thd= 0;
injector_ndb= 0;
p_latest_trans_gci= 0;
schema_ndb= 0;
- pthread_mutex_unlock(&injector_mutex);
+ mysql_mutex_unlock(&injector_mutex);
thd->db= 0; // as not to try to free memory
if (ndb_apply_status_share)
@@ -4320,7 +4309,7 @@ err:
if (ndb_schema_share)
{
/* begin protect ndb_schema_share */
- pthread_mutex_lock(&ndb_schema_share_mutex);
+ mysql_mutex_lock(&ndb_schema_share_mutex);
/* ndb_share reference binlog extra free */
DBUG_PRINT("NDB_SHARE", ("%s binlog extra free use_count: %u",
ndb_schema_share->key,
@@ -4328,7 +4317,7 @@ err:
free_share(&ndb_schema_share);
ndb_schema_share= 0;
ndb_binlog_tables_inited= 0;
- pthread_mutex_unlock(&ndb_schema_share_mutex);
+ mysql_mutex_unlock(&ndb_schema_share_mutex);
/* end protect ndb_schema_share */
}
@@ -4380,7 +4369,7 @@ err:
i_ndb= 0;
}
- hash_free(&ndb_schema_objects);
+ my_hash_free(&ndb_schema_objects);
net_end(&thd->net);
thd->cleanup();
@@ -4388,7 +4377,7 @@ err:
ndb_binlog_thread_running= -1;
ndb_binlog_running= FALSE;
- (void) pthread_cond_signal(&injector_cond);
+ mysql_cond_signal(&injector_cond);
DBUG_PRINT("exit", ("ndb_binlog_thread"));
@@ -4407,12 +4396,12 @@ ndbcluster_show_status_binlog(THD* thd, stat_print_fn *stat_print,
ulonglong ndb_latest_epoch= 0;
DBUG_ENTER("ndbcluster_show_status_binlog");
- pthread_mutex_lock(&injector_mutex);
+ mysql_mutex_lock(&injector_mutex);
if (injector_ndb)
{
char buff1[22],buff2[22],buff3[22],buff4[22],buff5[22];
ndb_latest_epoch= injector_ndb->getLatestGCI();
- pthread_mutex_unlock(&injector_mutex);
+ mysql_mutex_unlock(&injector_mutex);
buflen=
snprintf(buf, sizeof(buf),
@@ -4432,7 +4421,7 @@ ndbcluster_show_status_binlog(THD* thd, stat_print_fn *stat_print,
DBUG_RETURN(TRUE);
}
else
- pthread_mutex_unlock(&injector_mutex);
+ mysql_mutex_unlock(&injector_mutex);
DBUG_RETURN(FALSE);
}
diff --git a/sql/ha_ndbcluster_binlog.h b/sql/ha_ndbcluster_binlog.h
index 927b0e76748..a02f687d76f 100644
--- a/sql/ha_ndbcluster_binlog.h
+++ b/sql/ha_ndbcluster_binlog.h
@@ -1,4 +1,7 @@
-/* Copyright (c) 2000-2003, 2006, 2007 MySQL AB
+#ifndef HA_NDBCLUSTER_BINLOG_INCLUDED
+#define HA_NDBCLUSTER_BINLOG_INCLUDED
+
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,9 +14,11 @@
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
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "sql_class.h" /* THD */
+
// Typedefs for long names
typedef NdbDictionary::Object NDBOBJ;
typedef NdbDictionary::Column NDBCOL;
@@ -24,8 +29,6 @@ typedef NdbDictionary::Event NDBEVENT;
#define IS_TMP_PREFIX(A) (is_prefix(A, tmp_file_prefix))
-extern ulong ndb_extra_logging;
-
#define INJECTOR_EVENT_LEN 200
#define NDB_INVALID_SCHEMA_OBJECT 241
@@ -103,16 +106,24 @@ private:
};
#ifdef HAVE_NDB_BINLOG
+
+#ifdef HAVE_PSI_INTERFACE
+extern PSI_mutex_key key_injector_mutex, key_ndb_schema_share_mutex,
+ key_ndb_schema_object_mutex;
+extern PSI_cond_key key_injector_cond;
+extern PSI_thread_key key_thread_ndb_binlog;
+#endif /* HAVE_PSI_INTERFACE */
+
extern pthread_t ndb_binlog_thread;
-extern pthread_mutex_t injector_mutex;
-extern pthread_cond_t injector_cond;
+extern mysql_mutex_t injector_mutex;
+extern mysql_cond_t injector_cond;
extern unsigned char g_node_id_map[max_ndb_nodes];
extern pthread_t ndb_util_thread;
-extern pthread_mutex_t LOCK_ndb_util_thread;
-extern pthread_cond_t COND_ndb_util_thread;
+extern mysql_mutex_t LOCK_ndb_util_thread;
+extern mysql_cond_t COND_ndb_util_thread;
extern int ndbcluster_util_inited;
-extern pthread_mutex_t ndbcluster_mutex;
+extern mysql_mutex_t ndbcluster_mutex;
extern HASH ndbcluster_open_tables;
extern Ndb_cluster_connection* g_ndb_cluster_connection;
extern long ndb_number_of_storage_nodes;
@@ -147,8 +158,7 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share,
uint32 ndb_table_version,
enum SCHEMA_OP_TYPE type,
const char *new_db,
- const char *new_table_name,
- int have_lock_open);
+ const char *new_table_name);
int ndbcluster_handle_drop_table(Ndb *ndb, const char *event_name,
NDB_SHARE *share,
const char *type_str);
@@ -225,3 +235,5 @@ set_thd_ndb(THD *thd, Thd_ndb *thd_ndb)
{ thd_set_ha_data(thd, ndbcluster_hton, thd_ndb); }
Ndb* check_ndb_in_thd(THD* thd);
+
+#endif /* HA_NDBCLUSTER_BINLOG_INCLUDED */
diff --git a/sql/ha_ndbcluster_cond.cc b/sql/ha_ndbcluster_cond.cc
index af076c1caf7..e1bd6271866 100644
--- a/sql/ha_ndbcluster_cond.cc
+++ b/sql/ha_ndbcluster_cond.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000-2003, 2007, 2008 MySQL AB
+/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,7 +22,10 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "sql_class.h" // set_var.h: THD
+#include "my_global.h" // WITH_*
+#include "log.h" // sql_print_error
#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
#include <ndbapi/NdbApi.hpp>
@@ -32,6 +35,110 @@
typedef NdbDictionary::Column NDBCOL;
typedef NdbDictionary::Table NDBTAB;
+
+/**
+ Serialize a constant item into a Ndb_cond node.
+
+ @param const_type item's result type
+ @param item item to be serialized
+ @param curr_cond Ndb_cond node the item to be serialized into
+ @param context Traverse context
+*/
+
+static void ndb_serialize_const(Item_result const_type, const Item *item,
+ Ndb_cond *curr_cond,
+ Ndb_cond_traverse_context *context)
+{
+ DBUG_ASSERT(item->const_item());
+ switch (const_type) {
+ case STRING_RESULT:
+ {
+ NDB_ITEM_QUALIFICATION q;
+ q.value_type= Item::STRING_ITEM;
+ curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
+ if (! context->expecting_no_field_result())
+ {
+ // We have not seen the field argument yet
+ context->expect_only(Item::FIELD_ITEM);
+ context->expect_only_field_result(STRING_RESULT);
+ context->expect_collation(item->collation.collation);
+ }
+ else
+ {
+ // Expect another logical expression
+ context->expect_only(Item::FUNC_ITEM);
+ context->expect(Item::COND_ITEM);
+ // Check that string result have correct collation
+ if (!context->expecting_collation(item->collation.collation))
+ {
+ DBUG_PRINT("info", ("Found non-matching collation %s",
+ item->collation.collation->name));
+ context->supported= FALSE;
+ }
+ }
+ break;
+ }
+ case REAL_RESULT:
+ {
+ NDB_ITEM_QUALIFICATION q;
+ q.value_type= Item::REAL_ITEM;
+ curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
+ if (! context->expecting_no_field_result())
+ {
+ // We have not seen the field argument yet
+ context->expect_only(Item::FIELD_ITEM);
+ context->expect_only_field_result(REAL_RESULT);
+ }
+ else
+ {
+ // Expect another logical expression
+ context->expect_only(Item::FUNC_ITEM);
+ context->expect(Item::COND_ITEM);
+ }
+ break;
+ }
+ case INT_RESULT:
+ {
+ NDB_ITEM_QUALIFICATION q;
+ q.value_type= Item::INT_ITEM;
+ curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
+ if (! context->expecting_no_field_result())
+ {
+ // We have not seen the field argument yet
+ context->expect_only(Item::FIELD_ITEM);
+ context->expect_only_field_result(INT_RESULT);
+ }
+ else
+ {
+ // Expect another logical expression
+ context->expect_only(Item::FUNC_ITEM);
+ context->expect(Item::COND_ITEM);
+ }
+ break;
+ }
+ case DECIMAL_RESULT:
+ {
+ NDB_ITEM_QUALIFICATION q;
+ q.value_type= Item::DECIMAL_ITEM;
+ curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
+ if (! context->expecting_no_field_result())
+ {
+ // We have not seen the field argument yet
+ context->expect_only(Item::FIELD_ITEM);
+ context->expect_only_field_result(DECIMAL_RESULT);
+ }
+ else
+ {
+ // Expect another logical expression
+ context->expect_only(Item::FUNC_ITEM);
+ context->expect(Item::COND_ITEM);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
/*
Serialize the item tree into a linked list represented by Ndb_cond
for fast generation of NbdScanFilter. Adds information such as
@@ -110,7 +217,7 @@ void ndb_serialize_cond(const Item *item, void *arg)
to ndb_serialize_cond and end of rewrite statement
is wrapped in end of ndb_serialize_cond
*/
- if (context->expecting(item->type()))
+ if (context->expecting(item->type()) || item->const_item())
{
// This is the <field>|<const> item, save it in the rewrite context
rewrite_context2->left_hand_item= item;
@@ -594,108 +701,12 @@ void ndb_serialize_cond(const Item *item, void *arg)
DBUG_PRINT("info", ("result type %d", func_item->result_type()));
if (func_item->const_item())
{
- switch (func_item->result_type()) {
- case STRING_RESULT:
- {
- NDB_ITEM_QUALIFICATION q;
- q.value_type= Item::STRING_ITEM;
- curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
- if (! context->expecting_no_field_result())
- {
- // We have not seen the field argument yet
- context->expect_only(Item::FIELD_ITEM);
- context->expect_only_field_result(STRING_RESULT);
- context->expect_collation(func_item->collation.collation);
- }
- else
- {
- // Expect another logical expression
- context->expect_only(Item::FUNC_ITEM);
- context->expect(Item::COND_ITEM);
- // Check that string result have correct collation
- if (!context->expecting_collation(item->collation.collation))
- {
- DBUG_PRINT("info", ("Found non-matching collation %s",
- item->collation.collation->name));
- context->supported= FALSE;
- }
- }
- // Skip any arguments since we will evaluate function instead
- DBUG_PRINT("info", ("Skip until end of arguments marker"));
- context->skip= func_item->argument_count();
- break;
- }
- case REAL_RESULT:
- {
- NDB_ITEM_QUALIFICATION q;
- q.value_type= Item::REAL_ITEM;
- curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
- if (! context->expecting_no_field_result())
- {
- // We have not seen the field argument yet
- context->expect_only(Item::FIELD_ITEM);
- context->expect_only_field_result(REAL_RESULT);
- }
- else
- {
- // Expect another logical expression
- context->expect_only(Item::FUNC_ITEM);
- context->expect(Item::COND_ITEM);
- }
-
- // Skip any arguments since we will evaluate function instead
- DBUG_PRINT("info", ("Skip until end of arguments marker"));
- context->skip= func_item->argument_count();
- break;
- }
- case INT_RESULT:
- {
- NDB_ITEM_QUALIFICATION q;
- q.value_type= Item::INT_ITEM;
- curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
- if (! context->expecting_no_field_result())
- {
- // We have not seen the field argument yet
- context->expect_only(Item::FIELD_ITEM);
- context->expect_only_field_result(INT_RESULT);
- }
- else
- {
- // Expect another logical expression
- context->expect_only(Item::FUNC_ITEM);
- context->expect(Item::COND_ITEM);
- }
-
- // Skip any arguments since we will evaluate function instead
- DBUG_PRINT("info", ("Skip until end of arguments marker"));
- context->skip= func_item->argument_count();
- break;
- }
- case DECIMAL_RESULT:
- {
- NDB_ITEM_QUALIFICATION q;
- q.value_type= Item::DECIMAL_ITEM;
- curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
- if (! context->expecting_no_field_result())
- {
- // We have not seen the field argument yet
- context->expect_only(Item::FIELD_ITEM);
- context->expect_only_field_result(DECIMAL_RESULT);
- }
- else
- {
- // Expect another logical expression
- context->expect_only(Item::FUNC_ITEM);
- context->expect(Item::COND_ITEM);
- }
- // Skip any arguments since we will evaluate function instead
- DBUG_PRINT("info", ("Skip until end of arguments marker"));
- context->skip= func_item->argument_count();
- break;
- }
- default:
- break;
- }
+ ndb_serialize_const(func_item->result_type(), item, curr_cond,
+ context);
+
+ // Skip any arguments since we will evaluate function instead
+ DBUG_PRINT("info", ("Skip until end of arguments marker"));
+ context->skip= func_item->argument_count();
}
else
// Function does not return constant expression
@@ -880,6 +891,19 @@ void ndb_serialize_cond(const Item *item, void *arg)
}
break;
}
+ case Item::CACHE_ITEM:
+ {
+ DBUG_PRINT("info", ("CACHE_ITEM"));
+ if (item->const_item())
+ {
+ ndb_serialize_const(((Item_cache*)item)->result_type(), item,
+ curr_cond, context);
+ }
+ else
+ context->supported= FALSE;
+
+ break;
+ }
default:
{
DBUG_PRINT("info", ("Found item of type %d", item->type()));
@@ -1448,4 +1472,4 @@ int ha_ndbcluster_cond::generate_scan_filter_from_key(NdbScanOperation *op,
DBUG_RETURN(0);
}
-#endif /* HAVE_NDBCLUSTER_DB */
+#endif
diff --git a/sql/ha_ndbcluster_cond.h b/sql/ha_ndbcluster_cond.h
index 2f10bf498af..2387dcaf5ba 100644
--- a/sql/ha_ndbcluster_cond.h
+++ b/sql/ha_ndbcluster_cond.h
@@ -1,4 +1,7 @@
-/* Copyright (c) 2000-2008 MySQL AB
+#ifndef HA_NDBCLUSTER_COND_INCLUDED
+#define HA_NDBCLUSTER_COND_INCLUDED
+
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,6 +25,13 @@
#pragma interface /* gcc class implementation */
#endif
+/*
+ It is necessary to include set_var.h instead of item.h because there
+ are dependencies on include order for set_var.h and item.h. This
+ will be resolved later.
+*/
+#include "set_var.h" /* Item, Item_field */
+
typedef enum ndb_item_type {
NDB_VALUE = 0, // Qualified more with Item::Type
NDB_FIELD = 1, // Qualified from table definition
@@ -486,3 +496,5 @@ private:
Ndb_cond_stack *m_cond_stack;
};
+
+#endif /* HA_NDBCLUSTER_COND_INCLUDED */
diff --git a/sql/ha_ndbcluster_tables.h b/sql/ha_ndbcluster_tables.h
index 5aa4683ffb4..4d97ca2c254 100644
--- a/sql/ha_ndbcluster_tables.h
+++ b/sql/ha_ndbcluster_tables.h
@@ -1,4 +1,8 @@
-/* Copyright (c) 2000-2003, 2006, 2007 MySQL AB
+#ifndef HA_NDBCLUSTER_TABLES_INCLUDED
+#define HA_NDBCLUSTER_TABLES_INCLUDED
+
+/* Copyright (c) 2000-2003, 2006, 2007 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
@@ -21,3 +25,5 @@
#define OLD_NDB_APPLY_TABLE "apply_status"
#define NDB_SCHEMA_TABLE "ndb_schema"
#define OLD_NDB_SCHEMA_TABLE "schema"
+
+#endif /* HA_NDBCLUSTER_TABLES_INCLUDED */
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index db858e9737f..15fa7d12b16 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2005, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2005, 2013, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -53,22 +53,23 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "sql_parse.h" // append_file_to_dir
#include "create_options.h"
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
-
-#include <mysql/plugin.h>
+#include "sql_table.h" // tablename_to_filename
+#include "key.h"
+#include "sql_plugin.h"
+#include "table.h" /* HA_DATA_PARTITION */
+#include "sql_show.h" // append_identifier
+#include "sql_admin.h" // SQL_ADMIN_MSG_TEXT_SIZE
#include "debug_sync.h"
static const char *ha_par_ext= ".par";
-#ifdef NOT_USED
-static int free_share(PARTITION_SHARE * share);
-static PARTITION_SHARE *get_share(const char *table_name, TABLE * table);
-#endif
-
+#define MI_MAX_MSG_BUF MYSQL_ERRMSG_SIZE
/****************************************************************************
MODULE create/delete handler object
****************************************************************************/
@@ -79,6 +80,8 @@ static handler *partition_create_handler(handlerton *hton,
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 int partition_initialize(void *p)
{
@@ -225,6 +228,7 @@ ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share,
m_is_sub_partitioned= m_part_info->is_sub_partitioned();
m_is_clone_of= clone_arg;
m_clone_mem_root= clone_mem_root_arg;
+ m_pkey_is_clustered= clone_arg->primary_key_is_clustered();
DBUG_VOID_RETURN;
}
@@ -276,6 +280,7 @@ void ha_partition::init_handler_variables()
m_rec_length= 0;
m_last_part= 0;
m_rec0= 0;
+ m_err_rec= NULL;
m_curr_key_info[0]= NULL;
m_curr_key_info[1]= NULL;
m_part_func_monotonicity_info= NON_MONOTONIC;
@@ -284,12 +289,13 @@ void ha_partition::init_handler_variables()
/*
this allows blackhole to work properly
*/
- m_no_locks= 0;
+ m_num_locks= 0;
m_part_info= NULL;
m_create_handler= FALSE;
m_is_sub_partitioned= 0;
m_is_clone_of= NULL;
m_clone_mem_root= NULL;
+ m_part_ids_sorted_by_num_of_records= NULL;
#ifdef DONT_HAVE_TO_BE_INITALIZED
m_start_key.flag= 0;
@@ -301,7 +307,7 @@ void ha_partition::init_handler_variables()
const char *ha_partition::table_type() const
{
// we can do this since we only support a single engine type
- return m_file[0]->table_type();
+ return m_file && m_file[0] ? m_file[0]->table_type() : "Unknown";
}
@@ -325,6 +331,7 @@ ha_partition::~ha_partition()
delete m_file[i];
}
destroy_record_priority_queue();
+ my_free(m_part_ids_sorted_by_num_of_records);
clear_handler_file();
@@ -374,7 +381,7 @@ ha_partition::~ha_partition()
The flag HA_READ_ORDER will be reset for the time being to indicate no
ordered output is available from partition handler indexes. Later a merge
sort will be performed using the underlying handlers.
- 5) primary_key_is_clustered, has_transactions and low_byte_first is
+ 5) primary_key_is_clustered and has_transactions are
calculated here.
*/
@@ -403,14 +410,14 @@ bool ha_partition::initialize_partition(MEM_ROOT *mem_root)
else if (get_from_handler_file(table_share->normalized_path.str,
mem_root, false))
{
- my_message(ER_UNKNOWN_ERROR, "Failed to read from the .par file", MYF(0));
+ my_error(ER_FAILED_READ_FROM_PAR_FILE, MYF(0));
DBUG_RETURN(1);
}
/*
We create all underlying table handlers here. We do it in this special
method to be able to report allocation errors.
- Set up low_byte_first, primary_key_is_clustered and
+ Set up primary_key_is_clustered and
has_transactions since they are called often in all kinds of places,
other parameters are calculated on demand.
Verify that all partitions have the same table_flags.
@@ -541,9 +548,9 @@ int ha_partition::create_handler_files(const char *path,
strxmov(name, path, ha_par_ext, NullS);
strxmov(old_name, old_path, ha_par_ext, NullS);
if ((action_flag == CHF_DELETE_FLAG &&
- my_delete(name, MYF(MY_WME))) ||
+ mysql_file_delete(key_file_partition, name, MYF(MY_WME))) ||
(action_flag == CHF_RENAME_FLAG &&
- my_rename(old_name, name, MYF(MY_WME))))
+ mysql_file_rename(key_file_partition, old_name, name, MYF(MY_WME))))
{
DBUG_RETURN(TRUE);
}
@@ -628,8 +635,8 @@ int ha_partition::drop_partitions(const char *path)
{
List_iterator<partition_element> part_it(m_part_info->partitions);
char part_name_buff[FN_REFLEN];
- uint no_parts= m_part_info->partitions.elements;
- uint no_subparts= m_part_info->no_subparts;
+ uint num_parts= m_part_info->partitions.elements;
+ uint num_subparts= m_part_info->num_subparts;
uint i= 0;
uint name_variant;
int ret_error;
@@ -659,7 +666,7 @@ int ha_partition::drop_partitions(const char *path)
do
{
partition_element *sub_elem= sub_it++;
- part= i * no_subparts + j;
+ part= i * num_subparts + j;
create_subpartition_name(part_name_buff, path,
part_elem->partition_name,
sub_elem->partition_name, name_variant);
@@ -669,7 +676,7 @@ int ha_partition::drop_partitions(const char *path)
error= ret_error;
if (deactivate_ddl_log_entry(sub_elem->log_entry->entry_pos))
error= 1;
- } while (++j < no_subparts);
+ } while (++j < num_subparts);
}
else
{
@@ -688,8 +695,8 @@ int ha_partition::drop_partitions(const char *path)
else
part_elem->part_state= PART_IS_DROPPED;
}
- } while (++i < no_parts);
- VOID(sync_ddl_log());
+ } while (++i < num_parts);
+ (void) sync_ddl_log();
DBUG_RETURN(error);
}
@@ -719,9 +726,9 @@ int ha_partition::rename_partitions(const char *path)
List_iterator<partition_element> temp_it(m_part_info->temp_partitions);
char part_name_buff[FN_REFLEN];
char norm_name_buff[FN_REFLEN];
- uint no_parts= m_part_info->partitions.elements;
+ uint num_parts= m_part_info->partitions.elements;
uint part_count= 0;
- uint no_subparts= m_part_info->no_subparts;
+ uint num_subparts= m_part_info->num_subparts;
uint i= 0;
uint j= 0;
int error= 0;
@@ -771,7 +778,7 @@ int ha_partition::rename_partitions(const char *path)
error= 1;
else
sub_elem->log_entry= NULL; /* Indicate success */
- } while (++j < no_subparts);
+ } while (++j < num_subparts);
}
else
{
@@ -788,7 +795,7 @@ int ha_partition::rename_partitions(const char *path)
part_elem->log_entry= NULL; /* Indicate success */
}
} while (++i < temp_partitions);
- VOID(sync_ddl_log());
+ (void) sync_ddl_log();
}
i= 0;
do
@@ -827,7 +834,7 @@ int ha_partition::rename_partitions(const char *path)
do
{
sub_elem= sub_it++;
- part= i * no_subparts + j;
+ part= i * num_subparts + j;
create_subpartition_name(norm_name_buff, path,
part_elem->partition_name,
sub_elem->partition_name,
@@ -840,7 +847,7 @@ int ha_partition::rename_partitions(const char *path)
error= ret_error;
else if (deactivate_ddl_log_entry(sub_elem->log_entry->entry_pos))
error= 1;
- VOID(sync_ddl_log());
+ (void) sync_ddl_log();
}
file= m_new_file[part];
create_subpartition_name(part_name_buff, path,
@@ -856,7 +863,7 @@ int ha_partition::rename_partitions(const char *path)
error= 1;
else
sub_elem->log_entry= NULL;
- } while (++j < no_subparts);
+ } while (++j < num_subparts);
}
else
{
@@ -871,7 +878,7 @@ int ha_partition::rename_partitions(const char *path)
error= ret_error;
else if (deactivate_ddl_log_entry(part_elem->log_entry->entry_pos))
error= 1;
- VOID(sync_ddl_log());
+ (void) sync_ddl_log();
}
file= m_new_file[i];
create_partition_name(part_name_buff, path,
@@ -888,8 +895,8 @@ int ha_partition::rename_partitions(const char *path)
part_elem->log_entry= NULL;
}
}
- } while (++i < no_parts);
- VOID(sync_ddl_log());
+ } while (++i < num_parts);
+ (void) sync_ddl_log();
DBUG_RETURN(error);
}
@@ -898,9 +905,12 @@ int ha_partition::rename_partitions(const char *path)
#define ANALYZE_PARTS 2
#define CHECK_PARTS 3
#define REPAIR_PARTS 4
+#define ASSIGN_KEYCACHE_PARTS 5
+#define PRELOAD_KEYS_PARTS 6
static const char *opt_op_name[]= {NULL,
- "optimize", "analyze", "check", "repair" };
+ "optimize", "analyze", "check", "repair",
+ "assign_to_keycache", "preload_keys"};
/*
Optimize table
@@ -985,7 +995,44 @@ int ha_partition::repair(THD *thd, HA_CHECK_OPT *check_opt)
DBUG_RETURN(handle_opt_partitions(thd, check_opt, REPAIR_PARTS));
}
+/**
+ Assign to keycache
+
+ @param thd Thread object
+ @param check_opt Check/analyze/repair/optimize options
+
+ @return
+ @retval >0 Error
+ @retval 0 Success
+*/
+
+int ha_partition::assign_to_keycache(THD *thd, HA_CHECK_OPT *check_opt)
+{
+ DBUG_ENTER("ha_partition::assign_to_keycache");
+
+ DBUG_RETURN(handle_opt_partitions(thd, check_opt, ASSIGN_KEYCACHE_PARTS));
+}
+
+
+/**
+ Preload to keycache
+
+ @param thd Thread object
+ @param check_opt Check/analyze/repair/optimize options
+ @return
+ @retval >0 Error
+ @retval 0 Success
+*/
+
+int ha_partition::preload_keys(THD *thd, HA_CHECK_OPT *check_opt)
+{
+ DBUG_ENTER("ha_partition::preload_keys");
+
+ DBUG_RETURN(handle_opt_partitions(thd, check_opt, PRELOAD_KEYS_PARTS));
+}
+
+
/*
Handle optimize/analyze/check/repair of one partition
@@ -1001,10 +1048,11 @@ int ha_partition::repair(THD *thd, HA_CHECK_OPT *check_opt)
0 Success
*/
-static int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt,
- handler *file, uint flag)
+int ha_partition::handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt,
+ uint part_id, uint flag)
{
int error;
+ handler *file= m_file[part_id];
DBUG_ENTER("handle_opt_part");
DBUG_PRINT("enter", ("flag = %u", flag));
@@ -1013,9 +1061,31 @@ static int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt,
else if (flag == ANALYZE_PARTS)
error= file->ha_analyze(thd, check_opt);
else if (flag == CHECK_PARTS)
+ {
error= file->ha_check(thd, check_opt);
+ if (!error ||
+ error == HA_ADMIN_ALREADY_DONE ||
+ error == HA_ADMIN_NOT_IMPLEMENTED)
+ {
+ if (check_opt->flags & (T_MEDIUM | T_EXTEND))
+ error= check_misplaced_rows(part_id, false);
+ }
+ }
else if (flag == REPAIR_PARTS)
+ {
error= file->ha_repair(thd, check_opt);
+ if (!error ||
+ error == HA_ADMIN_ALREADY_DONE ||
+ error == HA_ADMIN_NOT_IMPLEMENTED)
+ {
+ if (check_opt->flags & (T_MEDIUM | T_EXTEND))
+ error= check_misplaced_rows(part_id, true);
+ }
+ }
+ else if (flag == ASSIGN_KEYCACHE_PARTS)
+ error= file->assign_to_keycache(thd, check_opt);
+ else if (flag == PRELOAD_KEYS_PARTS)
+ error= file->preload_keys(thd, check_opt);
else
{
DBUG_ASSERT(FALSE);
@@ -1028,37 +1098,45 @@ static int 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, const char* msg_type,
- const char* db_name, const char* table_name,
+static 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, 6, 7);
-static bool print_admin_msg(THD* thd, const char* msg_type,
- const char* db_name, const char* table_name,
+ ATTRIBUTE_FORMAT(printf, 7, 8);
+static 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, msg_length;
- char msgbuf[HA_MAX_MSG_BUF];
+ uint length;
+ uint msg_length;
char name[SAFE_NAME_LEN*2+2];
+ char *msgbuf;
+ bool error= true;
+ if (!(msgbuf= (char*) my_malloc(len, MYF(0))))
+ return true;
va_start(args, fmt);
- msg_length= my_vsnprintf(msgbuf, sizeof(msgbuf), fmt, args);
+ msg_length= my_vsnprintf(msgbuf, len, fmt, args);
va_end(args);
- msgbuf[sizeof(msgbuf) - 1] = 0; // healthy paranoia
+ if (msg_length >= (len - 1))
+ goto err;
+ msgbuf[len - 1] = 0; // healthy paranoia
if (!thd->vio_ok())
{
- sql_print_error(fmt, args);
- return TRUE;
+ sql_print_error("%s", msgbuf);
+ goto err;
}
- length=(uint) (strxmov(name, db_name, ".", table_name,NullS) - name);
+ length=(uint) (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. Due to following trace:
@@ -1078,9 +1156,12 @@ static bool print_admin_msg(THD* thd, const char* msg_type,
{
sql_print_error("Failed on my_net_write, writing to stderr instead: %s\n",
msgbuf);
- return TRUE;
+ goto err;
}
- return FALSE;
+ error= false;
+err:
+ my_free(msgbuf);
+ return error;
}
@@ -1102,8 +1183,8 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt,
uint flag)
{
List_iterator<partition_element> part_it(m_part_info->partitions);
- uint no_parts= m_part_info->no_parts;
- uint no_subparts= m_part_info->no_subparts;
+ uint num_parts= m_part_info->num_parts;
+ uint num_subparts= m_part_info->num_subparts;
uint i= 0;
int error;
DBUG_ENTER("ha_partition::handle_opt_partitions");
@@ -1117,7 +1198,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt,
it should only do named partitions, otherwise all partitions
*/
if (!(thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION) ||
- part_elem->part_state == PART_CHANGED)
+ part_elem->part_state == PART_ADMIN)
{
if (m_is_sub_partitioned)
{
@@ -1127,61 +1208,60 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt,
do
{
sub_elem= subpart_it++;
- part= i * no_subparts + j;
+ part= i * num_subparts + j;
DBUG_PRINT("info", ("Optimize subpartition %u (%s)",
part, sub_elem->partition_name));
-#ifdef NOT_USED
- if (print_admin_msg(thd, "note", table_share->db.str, table->alias,
- opt_op_name[flag],
- "Start to operate on subpartition %s",
- sub_elem->partition_name))
- DBUG_RETURN(HA_ADMIN_INTERNAL_ERROR);
-#endif
- if ((error= handle_opt_part(thd, check_opt, m_file[part], flag)))
+ if ((error= handle_opt_part(thd, check_opt, part, flag)))
{
/* print a line which partition the error belongs to */
if (error != HA_ADMIN_NOT_IMPLEMENTED &&
error != HA_ADMIN_ALREADY_DONE &&
error != HA_ADMIN_TRY_ALTER)
{
- print_admin_msg(thd, "error", table_share->db.str,
- table->alias.c_ptr(),
+ print_admin_msg(thd, MI_MAX_MSG_BUF, "error",
+ table_share->db.str, table->alias,
opt_op_name[flag],
"Subpartition %s returned error",
sub_elem->partition_name);
}
+ /* reset part_state for the remaining partitions */
+ do
+ {
+ if (part_elem->part_state == PART_ADMIN)
+ part_elem->part_state= PART_NORMAL;
+ } while ((part_elem= part_it++));
DBUG_RETURN(error);
}
- } while (++j < no_subparts);
+ } while (++j < num_subparts);
}
else
{
DBUG_PRINT("info", ("Optimize partition %u (%s)", i,
part_elem->partition_name));
-#ifdef NOT_USED
- if (print_admin_msg(thd, "note", table_share->db.str, table->alias,
- opt_op_name[flag],
- "Start to operate on partition %s",
- part_elem->partition_name))
- DBUG_RETURN(HA_ADMIN_INTERNAL_ERROR);
-#endif
- if ((error= handle_opt_part(thd, check_opt, m_file[i], flag)))
+ if ((error= handle_opt_part(thd, check_opt, i, flag)))
{
/* print a line which partition the error belongs to */
if (error != HA_ADMIN_NOT_IMPLEMENTED &&
error != HA_ADMIN_ALREADY_DONE &&
error != HA_ADMIN_TRY_ALTER)
{
- print_admin_msg(thd, "error", table_share->db.str,
- table->alias.c_ptr(),
- opt_op_name[flag], "Partition %s returned error",
+ print_admin_msg(thd, MI_MAX_MSG_BUF, "error",
+ table_share->db.str, table->alias,
+ opt_op_name[flag], "Partition %s returned error",
part_elem->partition_name);
}
+ /* reset part_state for the remaining partitions */
+ do
+ {
+ if (part_elem->part_state == PART_ADMIN)
+ part_elem->part_state= PART_NORMAL;
+ } while ((part_elem= part_it++));
DBUG_RETURN(error);
}
}
+ part_elem->part_state= PART_NORMAL;
}
- } while (++i < no_parts);
+ } while (++i < num_parts);
DBUG_RETURN(FALSE);
}
@@ -1267,7 +1347,8 @@ bool ha_partition::is_crashed() const
int ha_partition::prepare_new_partition(TABLE *tbl,
HA_CREATE_INFO *create_info,
handler *file, const char *part_name,
- partition_element *p_elem)
+ partition_element *p_elem,
+ uint disable_non_uniq_indexes)
{
int error;
DBUG_ENTER("prepare_new_partition");
@@ -1293,21 +1374,25 @@ int ha_partition::prepare_new_partition(TABLE *tbl,
if ((error= file->ha_open(tbl, part_name, m_mode, m_open_test_lock)))
goto error_open;
DBUG_PRINT("info", ("partition %s opened", part_name));
+
/*
Note: if you plan to add another call that may return failure,
better to do it before external_lock() as cleanup_new_partition()
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(), m_lock_type)))
+ if ((error= file->ha_external_lock(ha_thd(), F_WRLCK)))
goto error_external_lock;
DBUG_PRINT("info", ("partition %s external locked", part_name));
+ if (disable_non_uniq_indexes)
+ file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
+
DBUG_RETURN(0);
error_external_lock:
- VOID(file->ha_close());
+ (void) file->ha_close();
error_open:
- VOID(file->ha_delete_table(part_name));
+ (void) file->ha_delete_table(part_name);
error_create:
DBUG_RETURN(error);
}
@@ -1404,10 +1489,10 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
List_iterator<partition_element> part_it(m_part_info->partitions);
List_iterator <partition_element> t_it(m_part_info->temp_partitions);
char part_name_buff[FN_REFLEN];
- uint no_parts= m_part_info->partitions.elements;
- uint no_subparts= m_part_info->no_subparts;
+ uint num_parts= m_part_info->partitions.elements;
+ uint num_subparts= m_part_info->num_subparts;
uint i= 0;
- uint no_remain_partitions, part_count, orig_count;
+ uint num_remain_partitions, part_count, orig_count;
handler **new_file_array;
int error= 1;
bool first;
@@ -1423,7 +1508,7 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
part_name_buff)));
m_reorged_parts= 0;
if (!m_part_info->is_sub_partitioned())
- no_subparts= 1;
+ num_subparts= 1;
/*
Step 1:
@@ -1432,7 +1517,7 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
*/
if (temp_partitions)
{
- m_reorged_parts= temp_partitions * no_subparts;
+ m_reorged_parts= temp_partitions * num_subparts;
}
else
{
@@ -1442,9 +1527,9 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
if (part_elem->part_state == PART_CHANGED ||
part_elem->part_state == PART_REORGED_DROPPED)
{
- m_reorged_parts+= no_subparts;
+ m_reorged_parts+= num_subparts;
}
- } while (++i < no_parts);
+ } while (++i < num_parts);
}
if (m_reorged_parts &&
!(m_reorged_file= (handler**)sql_calloc(sizeof(handler*)*
@@ -1459,10 +1544,10 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
Calculate number of partitions after change and allocate space for
their handler references.
*/
- no_remain_partitions= 0;
+ num_remain_partitions= 0;
if (temp_partitions)
{
- no_remain_partitions= no_parts * no_subparts;
+ num_remain_partitions= num_parts * num_subparts;
}
else
{
@@ -1475,17 +1560,17 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
part_elem->part_state == PART_TO_BE_ADDED ||
part_elem->part_state == PART_CHANGED)
{
- no_remain_partitions+= no_subparts;
+ num_remain_partitions+= num_subparts;
}
- } while (++i < no_parts);
+ } while (++i < num_parts);
}
if (!(new_file_array= (handler**)sql_calloc(sizeof(handler*)*
- (2*(no_remain_partitions + 1)))))
+ (2*(num_remain_partitions + 1)))))
{
- mem_alloc_error(sizeof(handler*)*2*(no_remain_partitions+1));
+ mem_alloc_error(sizeof(handler*)*2*(num_remain_partitions+1));
DBUG_RETURN(ER_OUTOFMEMORY);
}
- m_added_file= &new_file_array[no_remain_partitions + 1];
+ m_added_file= &new_file_array[num_remain_partitions + 1];
/*
Step 3:
@@ -1504,9 +1589,9 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
part_elem->part_state == PART_REORGED_DROPPED)
{
memcpy((void*)&m_reorged_file[part_count],
- (void*)&m_file[i*no_subparts],
- sizeof(handler*)*no_subparts);
- part_count+= no_subparts;
+ (void*)&m_file[i*num_subparts],
+ sizeof(handler*)*num_subparts);
+ part_count+= num_subparts;
}
else if (first && temp_partitions &&
part_elem->part_state == PART_TO_BE_ADDED)
@@ -1521,11 +1606,11 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
ones used to be.
*/
first= FALSE;
- DBUG_ASSERT(((i*no_subparts) + m_reorged_parts) <= m_file_tot_parts);
- memcpy((void*)m_reorged_file, &m_file[i*no_subparts],
+ DBUG_ASSERT(((i*num_subparts) + m_reorged_parts) <= m_file_tot_parts);
+ memcpy((void*)m_reorged_file, &m_file[i*num_subparts],
sizeof(handler*)*m_reorged_parts);
}
- } while (++i < no_parts);
+ } while (++i < num_parts);
}
/*
@@ -1543,11 +1628,11 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
partition_element *part_elem= part_it++;
if (part_elem->part_state == PART_NORMAL)
{
- DBUG_ASSERT(orig_count + no_subparts <= m_file_tot_parts);
+ DBUG_ASSERT(orig_count + num_subparts <= m_file_tot_parts);
memcpy((void*)&new_file_array[part_count], (void*)&m_file[orig_count],
- sizeof(handler*)*no_subparts);
- part_count+= no_subparts;
- orig_count+= no_subparts;
+ sizeof(handler*)*num_subparts);
+ part_count+= num_subparts;
+ orig_count+= num_subparts;
}
else if (part_elem->part_state == PART_CHANGED ||
part_elem->part_state == PART_TO_BE_ADDED)
@@ -1563,16 +1648,16 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
mem_alloc_error(sizeof(handler));
DBUG_RETURN(ER_OUTOFMEMORY);
}
- } while (++j < no_subparts);
+ } while (++j < num_subparts);
if (part_elem->part_state == PART_CHANGED)
- orig_count+= no_subparts;
+ orig_count+= num_subparts;
else if (temp_partitions && first)
{
- orig_count+= (no_subparts * temp_partitions);
+ orig_count+= (num_subparts * temp_partitions);
first= FALSE;
}
}
- } while (++i < no_parts);
+ } while (++i < num_parts);
first= FALSE;
/*
Step 5:
@@ -1580,6 +1665,14 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
on them to prepare them for copy phase and also for later close
calls
*/
+
+ /*
+ Before creating new partitions check whether indexes are disabled
+ in the partitions.
+ */
+
+ uint disable_non_uniq_indexes = indexes_are_disabled();
+
i= 0;
part_count= 0;
part_it.rewind();
@@ -1609,18 +1702,20 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
part_elem->partition_name,
sub_elem->partition_name,
name_variant);
- part= i * no_subparts + j;
+ 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)))
+ sub_elem,
+ disable_non_uniq_indexes)))
{
cleanup_new_partition(part_count);
DBUG_RETURN(error);
}
+
m_added_file[part_count++]= new_file_array[part];
- } while (++j < no_subparts);
+ } while (++j < num_subparts);
}
else
{
@@ -1631,15 +1726,17 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
if ((error= prepare_new_partition(table, create_info,
new_file_array[i],
(const char *)part_name_buff,
- part_elem)))
+ part_elem,
+ disable_non_uniq_indexes)))
{
cleanup_new_partition(part_count);
DBUG_RETURN(error);
}
+
m_added_file[part_count++]= new_file_array[i];
}
}
- } while (++i < no_parts);
+ } while (++i < num_parts);
/*
Step 6:
@@ -1656,7 +1753,7 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
part_elem->part_state= PART_IS_CHANGED;
else if (part_elem->part_state == PART_REORGED_DROPPED)
part_elem->part_state= PART_TO_BE_DROPPED;
- } while (++i < no_parts);
+ } while (++i < num_parts);
for (i= 0; i < temp_partitions; i++)
{
partition_element *part_elem= t_it++;
@@ -1705,9 +1802,9 @@ int ha_partition::copy_partitions(ulonglong * const copied,
if (m_part_info->linear_hash_ind)
{
if (m_part_info->part_type == HASH_PARTITION)
- set_linear_hash_mask(m_part_info, m_part_info->no_parts);
+ set_linear_hash_mask(m_part_info, m_part_info->num_parts);
else
- set_linear_hash_mask(m_part_info, m_part_info->no_subparts);
+ set_linear_hash_mask(m_part_info, m_part_info->num_subparts);
}
while (reorg_part < m_reorged_parts)
@@ -1915,7 +2012,7 @@ int ha_partition::del_ren_cre_table(const char *from,
if (get_from_handler_file(from, ha_thd()->mem_root, false))
DBUG_RETURN(error);
DBUG_ASSERT(m_file_buffer);
- DBUG_PRINT("enter", ("from: (%s) to: (%s)", from, to));
+ DBUG_PRINT("enter", ("from: (%s) to: (%s)", from, to ? to : "(nil)"));
name_buffer_ptr= m_name_buffer_ptr;
file= m_file;
/*
@@ -2024,7 +2121,7 @@ partition_element *ha_partition::find_partition_element(uint part_id)
uint curr_part_id= 0;
List_iterator_fast <partition_element> part_it(m_part_info->partitions);
- for (i= 0; i < m_part_info->no_parts; i++)
+ for (i= 0; i < m_part_info->num_parts; i++)
{
partition_element *part_elem;
part_elem= part_it++;
@@ -2032,7 +2129,7 @@ partition_element *ha_partition::find_partition_element(uint part_id)
{
uint j;
List_iterator_fast <partition_element> sub_it(part_elem->subpartitions);
- for (j= 0; j < m_part_info->no_subparts; j++)
+ for (j= 0; j < m_part_info->num_subparts; j++)
{
part_elem= sub_it++;
if (part_id == curr_part_id++)
@@ -2043,11 +2140,137 @@ partition_element *ha_partition::find_partition_element(uint part_id)
return part_elem;
}
DBUG_ASSERT(0);
- my_error(ER_OUT_OF_RESOURCES, MYF(0));
- current_thd->fatal_error(); // Abort
+ my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
return NULL;
}
+uint ha_partition::count_query_cache_dependant_tables(uint8 *tables_type)
+{
+ DBUG_ENTER("ha_partition::count_query_cache_dependant_tables");
+ /* 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));
+ /*
+ We need save underlying tables only for HA_CACHE_TBL_ASKTRANSACT:
+ HA_CACHE_TBL_NONTRANSACT - because all changes goes through partition table
+ HA_CACHE_TBL_NOCACHE - because will not be cached
+ HA_CACHE_TBL_TRANSACT - QC need to know that such type present
+ */
+ DBUG_RETURN(type == HA_CACHE_TBL_ASKTRANSACT ? m_tot_parts : 0);
+}
+
+my_bool ha_partition::reg_query_cache_dependant_table(THD *thd,
+ char *key, uint key_len,
+ uint8 type,
+ Query_cache *cache,
+ Query_cache_block_table **block_table,
+ handler *file,
+ uint *n)
+{
+ DBUG_ENTER("ha_partition::reg_query_cache_dependant_table");
+ qc_engine_callback engine_callback;
+ ulonglong engine_data;
+ /* ask undelying engine */
+ if (!file->register_query_cache_table(thd, key,
+ key_len,
+ &engine_callback,
+ &engine_data))
+ {
+ DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s",
+ key,
+ key + table_share->db.length + 1));
+ /*
+ As this can change from call to call, don't reset set
+ thd->lex->safe_to_cache_query
+ */
+ thd->query_cache_is_applicable= 0; // Query can't be cached
+ DBUG_RETURN(TRUE);
+ }
+ (++(*block_table))->n= ++(*n);
+ if (!cache->insert_table(key_len,
+ key, (*block_table),
+ table_share->db.length,
+ type,
+ engine_callback, engine_data,
+ FALSE))
+ DBUG_RETURN(TRUE);
+ DBUG_RETURN(FALSE);
+}
+
+
+my_bool ha_partition::register_query_cache_dependant_tables(THD *thd,
+ Query_cache *cache,
+ Query_cache_block_table **block_table,
+ uint *n)
+{
+ char *name;
+ uint prefix_length= table_share->table_cache_key.length + 3;
+ uint num_parts= m_part_info->num_parts;
+ uint num_subparts= m_part_info->num_subparts;
+ uint i= 0;
+ List_iterator<partition_element> part_it(m_part_info->partitions);
+ char key[FN_REFLEN];
+
+ DBUG_ENTER("ha_partition::register_query_cache_dependant_tables");
+
+ /* see ha_partition::count_query_cache_dependant_tables */
+ if (m_file[0]->table_cache_type() != HA_CACHE_TBL_ASKTRANSACT)
+ DBUG_RETURN(FALSE); // nothing to register
+
+ /* prepare static part of the key */
+ memmove(key, table_share->table_cache_key.str,
+ table_share->table_cache_key.length);
+
+ name= key + table_share->table_cache_key.length - 1;
+ name[0]= name[2]= '#';
+ name[1]= 'P';
+ name+= 3;
+
+ do
+ {
+ partition_element *part_elem= part_it++;
+ uint part_len= strmov(name, part_elem->partition_name) - name;
+ if (m_is_sub_partitioned)
+ {
+ List_iterator<partition_element> subpart_it(part_elem->subpartitions);
+ partition_element *sub_elem;
+ char *sname= name + part_len;
+ uint j= 0, part;
+ sname[0]= sname[3]= '#';
+ sname[1]= 'S';
+ sname[2]= 'P';
+ sname += 4;
+ do
+ {
+ sub_elem= subpart_it++;
+ part= i * num_subparts + j;
+ uint spart_len= strmov(sname, sub_elem->partition_name) - name + 1;
+ if (reg_query_cache_dependant_table(thd, key,
+ prefix_length + part_len + 4 +
+ spart_len,
+ m_file[part]->table_cache_type(),
+ cache,
+ block_table, m_file[part],
+ n))
+ DBUG_RETURN(TRUE);
+ } while (++j < num_subparts);
+ }
+ else
+ {
+ if (reg_query_cache_dependant_table(thd, key,
+ prefix_length + part_len + 1,
+ m_file[i]->table_cache_type(),
+ cache,
+ block_table, m_file[i],
+ n))
+ DBUG_RETURN(TRUE);
+ }
+ } while (++i < num_parts);
+ DBUG_PRINT("info", ("cnt: %u", (uint)m_tot_parts));
+ DBUG_RETURN(FALSE);
+}
+
/*
Set up table share object before calling create on underlying handler
@@ -2155,7 +2378,7 @@ 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, no_parts;
+ uint tot_partition_words, tot_name_len, num_parts;
uint tot_parts= 0;
uint tot_len_words, tot_len_byte, chksum, tot_name_words;
char *name_buffer_ptr;
@@ -2168,11 +2391,11 @@ bool ha_partition::create_handler_file(const char *name)
List_iterator_fast <partition_element> part_it(m_part_info->partitions);
DBUG_ENTER("create_handler_file");
- no_parts= m_part_info->partitions.elements;
- DBUG_PRINT("info", ("table name = %s, no_parts = %u", name,
- no_parts));
+ num_parts= m_part_info->partitions.elements;
+ DBUG_PRINT("info", ("table name = %s, num_parts = %u", name,
+ num_parts));
tot_name_len= 0;
- for (i= 0; i < no_parts; i++)
+ for (i= 0; i < num_parts; i++)
{
part_elem= part_it++;
if (part_elem->part_state != PART_NORMAL &&
@@ -2190,7 +2413,7 @@ bool ha_partition::create_handler_file(const char *name)
else
{
List_iterator_fast <partition_element> sub_it(part_elem->subpartitions);
- for (j= 0; j < m_part_info->no_subparts; j++)
+ for (j= 0; j < m_part_info->num_subparts; j++)
{
subpart_elem= sub_it++;
tablename_to_filename(subpart_elem->partition_name,
@@ -2229,7 +2452,7 @@ bool ha_partition::create_handler_file(const char *name)
name_buffer_ptr= (char*) (engine_array + tot_partition_words * PAR_WORD_SIZE
+ PAR_WORD_SIZE);
part_it.rewind();
- for (i= 0; i < no_parts; i++)
+ for (i= 0; i < num_parts; i++)
{
part_elem= part_it++;
if (part_elem->part_state != PART_NORMAL &&
@@ -2247,7 +2470,7 @@ bool ha_partition::create_handler_file(const char *name)
else
{
List_iterator_fast <partition_element> sub_it(part_elem->subpartitions);
- for (j= 0; j < m_part_info->no_subparts; j++)
+ for (j= 0; j < m_part_info->num_subparts; j++)
{
subpart_elem= sub_it++;
tablename_to_filename(part_elem->partition_name, part_name,
@@ -2278,15 +2501,16 @@ bool ha_partition::create_handler_file(const char *name)
to be used at open, delete_table and rename_table
*/
fn_format(file_name, name, "", ha_par_ext, MY_APPEND_EXT);
- if ((file= my_create(file_name, CREATE_MODE, O_RDWR | O_TRUNC,
- MYF(MY_WME))) >= 0)
+ if ((file= mysql_file_create(key_file_partition,
+ file_name, CREATE_MODE, O_RDWR | O_TRUNC,
+ MYF(MY_WME))) >= 0)
{
- result= my_write(file, (uchar *) file_buffer, tot_len_byte,
- MYF(MY_WME | MY_NABP)) != 0;
+ result= mysql_file_write(file, (uchar *) file_buffer, tot_len_byte,
+ MYF(MY_WME | MY_NABP)) != 0;
/* Write connection information (for federatedx engine) */
part_it.rewind();
- for (i= 0; i < no_parts && !result; i++)
+ for (i= 0; i < num_parts && !result; i++)
{
uchar buffer[4];
part_elem= part_it++;
@@ -2300,7 +2524,7 @@ bool ha_partition::create_handler_file(const char *name)
break;
}
}
- VOID(my_close(file, MYF(0)));
+ (void) mysql_file_close(file, MYF(0));
}
else
result= TRUE;
@@ -2397,7 +2621,7 @@ bool ha_partition::new_handlers_from_part_info(MEM_ROOT *mem_root)
}
m_file_tot_parts= m_tot_parts;
bzero((char*) m_file, alloc_len);
- DBUG_ASSERT(m_part_info->no_parts > 0);
+ DBUG_ASSERT(m_part_info->num_parts > 0);
i= 0;
part_count= 0;
@@ -2410,7 +2634,7 @@ bool ha_partition::new_handlers_from_part_info(MEM_ROOT *mem_root)
part_elem= part_it++;
if (m_is_sub_partitioned)
{
- for (j= 0; j < m_part_info->no_subparts; j++)
+ for (j= 0; j < m_part_info->num_subparts; j++)
{
if (!(m_file[part_count++]= get_new_handler(table_share, mem_root,
part_elem->engine_type)))
@@ -2427,7 +2651,7 @@ bool ha_partition::new_handlers_from_part_info(MEM_ROOT *mem_root)
DBUG_PRINT("info", ("engine_type: %u",
(uint) ha_legacy_type(part_elem->engine_type)));
}
- } while (++i < m_part_info->no_parts);
+ } while (++i < m_part_info->num_parts);
if (part_elem->engine_type == myisam_hton)
{
DBUG_PRINT("info", ("MyISAM"));
@@ -2467,18 +2691,19 @@ bool ha_partition::read_par_file(const char *name)
DBUG_RETURN(false);
fn_format(buff, name, "", ha_par_ext, MY_APPEND_EXT);
- /* Following could be done with my_stat to read in whole file */
- if ((file= my_open(buff, O_RDONLY | O_SHARE, MYF(MY_WME))) < 0)
- DBUG_RETURN(true);
- if (my_read(file, (uchar *) & buff[0], PAR_WORD_SIZE, MYF(MY_NABP)))
+ /* Following could be done with mysql_file_stat to read in whole file */
+ if ((file= mysql_file_open(key_file_partition,
+ buff, O_RDONLY | O_SHARE, MYF(0))) < 0)
+ DBUG_RETURN(TRUE);
+ if (mysql_file_read(file, (uchar *) &buff[0], PAR_WORD_SIZE, MYF(MY_NABP)))
goto err1;
len_words= uint4korr(buff);
len_bytes= PAR_WORD_SIZE * len_words;
- if (my_seek(file, 0, MY_SEEK_SET, MYF(0)) == MY_FILEPOS_ERROR)
+ if (mysql_file_seek(file, 0, MY_SEEK_SET, MYF(0)) == MY_FILEPOS_ERROR)
goto err1;
if (!(file_buffer= (char*) alloc_root(&m_mem_root, len_bytes)))
goto err1;
- if (my_read(file, (uchar *) file_buffer, len_bytes, MYF(MY_NABP)))
+ if (mysql_file_read(file, (uchar *) file_buffer, len_bytes, MYF(MY_NABP)))
goto err2;
chksum= 0;
@@ -2527,12 +2752,12 @@ bool ha_partition::read_par_file(const char *name)
m_connect_string[i]= connect_string;
}
- VOID(my_close(file, MYF(0)));
+ (void) mysql_file_close(file, MYF(0));
DBUG_RETURN(false);
err2:
err1:
- VOID(my_close(file, MYF(0)));
+ (void) mysql_file_close(file, MYF(0));
DBUG_RETURN(true);
}
@@ -2634,12 +2859,11 @@ bool ha_partition::get_from_handler_file(const char *name, MEM_ROOT *mem_root,
A destructor for partition-specific TABLE_SHARE data.
*/
-void ha_data_partition_destroy(void *ha_data)
+void ha_data_partition_destroy(HA_DATA_PARTITION* ha_part_data)
{
- if (ha_data)
+ if (ha_part_data)
{
- HA_DATA_PARTITION *ha_part_data= (HA_DATA_PARTITION*) ha_data;
- pthread_mutex_destroy(&ha_part_data->LOCK_auto_inc);
+ mysql_mutex_destroy(&ha_part_data->LOCK_auto_inc);
}
}
@@ -2686,12 +2910,33 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
name_buffer_ptr= m_name_buffer_ptr;
m_start_key.length= 0;
m_rec0= table->record[0];
- m_rec_length= table_share->reclength;
+ m_rec_length= table_share->stored_rec_length;
+ if (!m_part_ids_sorted_by_num_of_records)
+ {
+ if (!(m_part_ids_sorted_by_num_of_records=
+ (uint32*) my_malloc(m_tot_parts * sizeof(uint32), MYF(MY_WME))))
+ DBUG_RETURN(error);
+ uint32 i;
+ /* Initialize it with all partition ids. */
+ for (i= 0; i < m_tot_parts; i++)
+ m_part_ids_sorted_by_num_of_records[i]= i;
+ }
/* Initialize the bitmap we use to minimize ha_start_bulk_insert calls */
if (bitmap_init(&m_bulk_insert_started, NULL, m_tot_parts + 1, FALSE))
DBUG_RETURN(error);
bitmap_clear_all(&m_bulk_insert_started);
+ /*
+ Initialize the bitmap we use to keep track of partitions which returned
+ HA_ERR_KEY_NOT_FOUND from index_read_map.
+ */
+ if (bitmap_init(&m_key_not_found_partitions, NULL, m_tot_parts, FALSE))
+ {
+ bitmap_free(&m_bulk_insert_started);
+ DBUG_RETURN(error);
+ }
+ bitmap_clear_all(&m_key_not_found_partitions);
+ m_key_not_found= false;
/* Initialize the bitmap we use to determine what partitions are used */
if (!m_is_clone_of)
{
@@ -2742,7 +2987,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
if ((error= (*file)->ha_open(table, name_buff, mode, test_if_locked)))
goto err_handler;
bzero(&table->s->connect_string, sizeof(LEX_STRING));
- m_no_locks+= (*file)->lock_count();
+ m_num_locks+= (*file)->lock_count();
name_buffer_ptr+= strlen(name_buffer_ptr) + 1;
} while (*(++file));
}
@@ -2786,31 +3031,33 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
clear_handler_file();
/*
- Use table_share->ha_data to share auto_increment_value among all handlers
- for the same table.
+ Use table_share->ha_part_data to share auto_increment_value among
+ all handlers for the same table.
*/
if (is_not_tmp_table)
- pthread_mutex_lock(&table_share->mutex);
- if (!table_share->ha_data)
+ mysql_mutex_lock(&table_share->LOCK_ha_data);
+ if (!table_share->ha_part_data)
{
- HA_DATA_PARTITION *ha_data;
/* currently only needed for auto_increment */
- table_share->ha_data= ha_data= (HA_DATA_PARTITION*)
+ table_share->ha_part_data= (HA_DATA_PARTITION*)
alloc_root(&table_share->mem_root,
sizeof(HA_DATA_PARTITION));
- if (!ha_data)
+ if (!table_share->ha_part_data)
{
if (is_not_tmp_table)
- pthread_mutex_unlock(&table_share->mutex);
+ mysql_mutex_unlock(&table_share->LOCK_ha_data);
goto err_handler;
}
- DBUG_PRINT("info", ("table_share->ha_data 0x%p", ha_data));
- bzero(ha_data, sizeof(HA_DATA_PARTITION));
- table_share->ha_data_destroy= ha_data_partition_destroy;
- VOID(pthread_mutex_init(&ha_data->LOCK_auto_inc, MY_MUTEX_INIT_FAST));
+ DBUG_PRINT("info", ("table_share->ha_part_data 0x%p",
+ table_share->ha_part_data));
+ bzero(table_share->ha_part_data, sizeof(HA_DATA_PARTITION));
+ table_share->ha_part_data_destroy= ha_data_partition_destroy;
+ mysql_mutex_init(key_PARTITION_LOCK_auto_inc,
+ &table_share->ha_part_data->LOCK_auto_inc,
+ MY_MUTEX_INIT_FAST);
}
if (is_not_tmp_table)
- pthread_mutex_unlock(&table_share->mutex);
+ mysql_mutex_unlock(&table_share->LOCK_ha_data);
/*
Some handlers update statistics as part of the open call. This will in
some cases corrupt the statistics of the partition handler and thus
@@ -2832,6 +3079,7 @@ err_handler:
(*file)->ha_close();
err_alloc:
bitmap_free(&m_bulk_insert_started);
+ bitmap_free(&m_key_not_found_partitions);
if (!m_is_clone_of)
bitmap_free(&(m_part_info->used_partitions));
@@ -2908,6 +3156,7 @@ int ha_partition::close(void)
DBUG_ASSERT(table->s == table_share);
destroy_record_priority_queue();
bitmap_free(&m_bulk_insert_started);
+ bitmap_free(&m_key_not_found_partitions);
if (!m_is_clone_of)
bitmap_free(&(m_part_info->used_partitions));
file= m_file;
@@ -3123,8 +3372,8 @@ int ha_partition::start_stmt(THD *thd, thr_lock_type lock_type)
uint ha_partition::lock_count() const
{
DBUG_ENTER("ha_partition::lock_count");
- DBUG_PRINT("info", ("m_no_locks %d", m_no_locks));
- DBUG_RETURN(m_no_locks);
+ DBUG_PRINT("info", ("m_num_locks %d", m_num_locks));
+ DBUG_RETURN(m_num_locks);
}
@@ -3261,10 +3510,9 @@ int ha_partition::write_row(uchar * buf)
longlong func_value;
bool have_auto_increment= table->next_number_field && buf == table->record[0];
my_bitmap_map *old_map;
- HA_DATA_PARTITION *ha_data= (HA_DATA_PARTITION*) table_share->ha_data;
THD *thd= ha_thd();
timestamp_auto_set_type saved_timestamp_type= table->timestamp_field_type;
- ulong saved_sql_mode= thd->variables.sql_mode;
+ ulonglong saved_sql_mode= thd->variables.sql_mode;
bool saved_auto_inc_field_not_null= table->auto_increment_field_not_null;
#ifdef NOT_NEEDED
uchar *rec0= m_rec0;
@@ -3283,8 +3531,8 @@ int ha_partition::write_row(uchar * buf)
*/
if (have_auto_increment)
{
- if (!ha_data->auto_inc_initialized &&
- !table->s->next_number_keypart)
+ if (!table_share->ha_part_data->auto_inc_initialized &&
+ !table_share->next_number_keypart)
{
/*
If auto_increment in table_share is not initialized, start by
@@ -3378,7 +3626,7 @@ exit:
Called from sql_select.cc, sql_acl.cc, sql_update.cc, and sql_insert.cc.
new_data is always record[0]
- old_data is normally record[1] but may be anything
+ old_data is always record[1]
*/
int ha_partition::update_row(const uchar *old_data, uchar *new_data)
@@ -3389,6 +3637,7 @@ int ha_partition::update_row(const uchar *old_data, uchar *new_data)
longlong func_value;
timestamp_auto_set_type orig_timestamp_type= table->timestamp_field_type;
DBUG_ENTER("ha_partition::update_row");
+ m_err_rec= NULL;
/*
We need to set timestamp field once before we calculate
@@ -3406,6 +3655,25 @@ int ha_partition::update_row(const uchar *old_data, uchar *new_data)
m_part_info->err_value= func_value;
goto exit;
}
+ /*
+ The protocol for updating a row is:
+ 1) position the handler (cursor) on the row to be updated,
+ either through the last read row (rnd or index) or by rnd_pos.
+ 2) call update_row with both old and new full records as arguments.
+
+ This means that m_last_part should already be set to actual partition
+ where the row was read from. And if that is not the same as the
+ calculated part_id we found a misplaced row, we return an error to
+ notify the user that something is broken in the row distribution
+ between partitions! Since we don't check all rows on read, we return an
+ error instead of correcting m_last_part, to make the user aware of the
+ problem!
+ */
+ if (old_part_id != m_last_part)
+ {
+ m_err_rec= old_data;
+ DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
+ }
m_last_part= new_part_id;
start_part_bulk_insert(thd, new_part_id);
@@ -3454,8 +3722,8 @@ int ha_partition::update_row(const uchar *old_data, uchar *new_data)
exit:
/*
- If updating an auto_increment column, update
- table_share->ha_data->next_auto_inc_val if needed.
+ if updating an auto_increment column, update
+ table_share->ha_part_data->next_auto_inc_val if needed.
(not to be used if auto_increment on secondary field in a multi-column
index)
mysql_update does not set table->next_number_field, so we use
@@ -3468,8 +3736,7 @@ exit:
bitmap_is_set(table->write_set,
table->found_next_number_field->field_index))
{
- HA_DATA_PARTITION *ha_data= (HA_DATA_PARTITION*) table_share->ha_data;
- if (!ha_data->auto_inc_initialized)
+ if (!table_share->ha_part_data->auto_inc_initialized)
info(HA_STATUS_AUTO);
set_auto_increment_if_higher(table->found_next_number_field);
}
@@ -3512,12 +3779,34 @@ int ha_partition::delete_row(const uchar *buf)
int error;
THD *thd= ha_thd();
DBUG_ENTER("ha_partition::delete_row");
+ m_err_rec= NULL;
if ((error= get_part_for_delete(buf, m_rec0, m_part_info, &part_id)))
{
DBUG_RETURN(error);
}
- m_last_part= part_id;
+ /*
+ The protocol for deleting a row is:
+ 1) position the handler (cursor) on the row to be deleted,
+ either through the last read row (rnd or index) or by rnd_pos.
+ 2) call delete_row with the full record as argument.
+
+ This means that m_last_part should already be set to actual partition
+ where the row was read from. And if that is not the same as the
+ calculated part_id we found a misplaced row, we return an error to
+ notify the user that something is broken in the row distribution
+ between partitions! Since we don't check all rows on read, we return an
+ error instead of forwarding the delete to the correct (m_last_part)
+ partition!
+ TODO: change the assert in InnoDB into an error instead and make this one
+ an assert instead and remove the get_part_for_delete()!
+ */
+ if (part_id != m_last_part)
+ {
+ m_err_rec= buf;
+ DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
+ }
+
tmp_disable_binlog(thd);
error= m_file[part_id]->ha_delete_row(buf);
reenable_binlog(thd);
@@ -3550,33 +3839,120 @@ int ha_partition::delete_row(const uchar *buf)
int ha_partition::delete_all_rows()
{
int error;
- bool truncate= FALSE;
handler **file;
- THD *thd= ha_thd();
DBUG_ENTER("ha_partition::delete_all_rows");
- if (thd->lex->sql_command == SQLCOM_TRUNCATE)
- {
- HA_DATA_PARTITION *ha_data= (HA_DATA_PARTITION*) table_share->ha_data;
- lock_auto_increment();
- ha_data->next_auto_inc_val= 0;
- ha_data->auto_inc_initialized= FALSE;
- unlock_auto_increment();
- truncate= TRUE;
- }
file= m_file;
do
{
if ((error= (*file)->ha_delete_all_rows()))
DBUG_RETURN(error);
- /* Ignore the error */
- if (truncate)
- (void) (*file)->ha_reset_auto_increment(0);
} while (*(++file));
DBUG_RETURN(0);
}
+/**
+ Manually truncate the table.
+
+ @retval 0 Success.
+ @retval > 0 Error code.
+*/
+
+int ha_partition::truncate()
+{
+ int error;
+ handler **file;
+ DBUG_ENTER("ha_partition::truncate");
+
+ /*
+ TRUNCATE also means resetting auto_increment. Hence, reset
+ it so that it will be initialized again at the next use.
+ */
+ lock_auto_increment();
+ table_share->ha_part_data->next_auto_inc_val= 0;
+ table_share->ha_part_data->auto_inc_initialized= FALSE;
+ unlock_auto_increment();
+
+ file= m_file;
+ do
+ {
+ if ((error= (*file)->ha_truncate()))
+ DBUG_RETURN(error);
+ } while (*(++file));
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Truncate a set of specific partitions.
+
+ @remark Auto increment value will be truncated in that partition as well!
+
+ ALTER TABLE t TRUNCATE PARTITION ...
+*/
+
+int ha_partition::truncate_partition(Alter_info *alter_info, bool *binlog_stmt)
+{
+ int error= 0;
+ List_iterator<partition_element> part_it(m_part_info->partitions);
+ uint num_parts= m_part_info->num_parts;
+ uint num_subparts= m_part_info->num_subparts;
+ uint i= 0;
+ DBUG_ENTER("ha_partition::truncate_partition");
+
+ /* Only binlog when it starts any call to the partitions handlers */
+ *binlog_stmt= false;
+
+ if (set_part_state(alter_info, m_part_info, PART_ADMIN))
+ DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
+
+ /*
+ TRUNCATE also means resetting auto_increment. Hence, reset
+ it so that it will be initialized again at the next use.
+ */
+ lock_auto_increment();
+ table_share->ha_part_data->next_auto_inc_val= 0;
+ table_share->ha_part_data->auto_inc_initialized= FALSE;
+ unlock_auto_increment();
+
+ *binlog_stmt= true;
+
+ do
+ {
+ partition_element *part_elem= part_it++;
+ if (part_elem->part_state == PART_ADMIN)
+ {
+ if (m_is_sub_partitioned)
+ {
+ List_iterator<partition_element>
+ subpart_it(part_elem->subpartitions);
+ partition_element *sub_elem __attribute__((unused));
+ uint j= 0, part;
+ do
+ {
+ sub_elem= subpart_it++;
+ part= i * num_subparts + j;
+ DBUG_PRINT("info", ("truncate subpartition %u (%s)",
+ part, sub_elem->partition_name));
+ if ((error= m_file[part]->ha_truncate()))
+ break;
+ sub_elem->part_state= PART_NORMAL;
+ } while (++j < num_subparts);
+ }
+ else
+ {
+ DBUG_PRINT("info", ("truncate partition %u (%s)", i,
+ part_elem->partition_name));
+ error= m_file[i]->ha_truncate();
+ }
+ part_elem->part_state= PART_NORMAL;
+ }
+ } while (!error && (++i < num_parts));
+ DBUG_RETURN(error);
+}
+
+
/*
Start a large batch of insert rows
@@ -4025,12 +4401,6 @@ void ha_partition::position(const uchar *record)
if (pad_length)
memset((ref + PARTITION_BYTES_IN_POS + file->ref_length), 0, pad_length);
-#ifdef SUPPORTING_PARTITION_OVER_DIFFERENT_ENGINES
-#ifdef HAVE_valgrind
- bzero(ref + PARTITION_BYTES_IN_POS + ref_length,
- max_ref_length-ref_length);
-#endif /* HAVE_valgrind */
-#endif
DBUG_VOID_RETURN;
}
@@ -4145,7 +4515,10 @@ bool ha_partition::init_record_priority_queue()
uint alloc_len;
uint used_parts= bitmap_bits_set(&m_part_info->used_partitions);
/* Allocate record buffer for each used partition. */
- alloc_len= used_parts * (m_rec_length + PARTITION_BYTES_IN_POS);
+ 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;
+ 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;
@@ -4167,15 +4540,27 @@ bool ha_partition::init_record_priority_queue()
{
DBUG_PRINT("info", ("init rec-buf for part %u", i));
int2store(ptr, i);
- ptr+= m_rec_length + PARTITION_BYTES_IN_POS;
+ ptr+= m_priority_queue_rec_len;
}
} while (++i < m_tot_parts);
m_start_key.key= (const uchar*)ptr;
+
/* Initialize priority queue, initialized to reading forward. */
- if (init_queue(&m_queue, used_parts, (uint) PARTITION_BYTES_IN_POS,
- 0, key_rec_cmp, (void*)m_curr_key_info, 0, 0))
+ int (*cmp_func)(void *, uchar *, uchar *);
+ void *cmp_arg;
+ if (!m_using_extended_keys)
+ {
+ cmp_func= cmp_key_rowid_part_id;
+ cmp_arg= (void*)this;
+ }
+ else
{
- my_free(m_ordered_rec_buffer, MYF(0));
+ cmp_func= cmp_key_part_id;
+ cmp_arg= (void*)m_curr_key_info;
+ }
+ if (init_queue(&m_queue, used_parts, 0, 0, cmp_func, cmp_arg, 0, 0))
+ {
+ my_free(m_ordered_rec_buffer);
m_ordered_rec_buffer= NULL;
DBUG_RETURN(true);
}
@@ -4194,7 +4579,7 @@ void ha_partition::destroy_record_priority_queue()
if (m_ordered_rec_buffer)
{
delete_queue(&m_queue);
- my_free(m_ordered_rec_buffer, MYF(0));
+ my_free(m_ordered_rec_buffer);
m_ordered_rec_buffer= NULL;
}
DBUG_VOID_RETURN;
@@ -4240,9 +4625,13 @@ int ha_partition::index_init(uint inx, bool sorted)
DBUG_PRINT("info", ("Clustered pk, using pk as secondary cmp"));
m_curr_key_info[1]= table->key_info+table->s->primary_key;
m_curr_key_info[2]= NULL;
+ m_using_extended_keys= TRUE;
}
else
+ {
m_curr_key_info[1]= NULL;
+ m_using_extended_keys= FALSE;
+ }
if (init_record_priority_queue())
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
@@ -4282,7 +4671,6 @@ int ha_partition::index_init(uint inx, bool sorted)
file= m_file;
do
{
- /* TODO RONM: Change to index_init() when code is stable */
if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file)))
if ((error= (*file)->ha_index_init(inx, sorted)))
{
@@ -4370,21 +4758,85 @@ 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);
+}
+
+
+/*
+ @brief
+ Provide ordering by (key_value, part_no).
+*/
+
+extern "C" int cmp_key_part_id(void *key_p, uchar *ref1, uchar *ref2)
+{
+ int res;
+ if ((res= key_rec_cmp(key_p, ref1 + PARTITION_BYTES_IN_POS,
+ ref2 + PARTITION_BYTES_IN_POS)))
+ {
+ return res;
+ }
+ return cmp_part_ids(ref1, ref2);
+}
+
/*
+ @brief
+ Provide ordering by (key_value, underying_table_rowid, part_no).
+*/
+extern "C" int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2)
+{
+ ha_partition *file= (ha_partition*)ptr;
+ int res;
+
+ if ((res= key_rec_cmp(file->m_curr_key_info, ref1 + PARTITION_BYTES_IN_POS,
+ ref2 + PARTITION_BYTES_IN_POS)))
+ {
+ 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)))
+ {
+ return res;
+ }
+ return cmp_part_ids(ref1, ref2);
+}
+
+
+/**
Common routine for a number of index_read variants
- SYNOPSIS
- ha_partition::common_index_read()
- buf Buffer where the record should be returned
- 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)
+ @param buf Buffer where the record should be returned.
+ @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).
- DESCRIPTION
+ @return Operation status
+ @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
an index lookup (when invoked from index_read_XXX):
- If possible, perform partition selection
@@ -4394,28 +4846,25 @@ int ha_partition::index_read_map(uchar *buf, const uchar *key,
handle_unordered_scan_next_partition)
YES: Fill the priority queue and get the record that is the first in
the ordering
-
- RETURN
- 0 OK
- other HA_ERR_END_OF_FILE or other error code.
*/
int ha_partition::common_index_read(uchar *buf, bool have_start_key)
{
int error;
- uint key_len;
+ uint UNINIT_VAR(key_len); /* used if have_start_key==TRUE */
bool reverse_order= FALSE;
DBUG_ENTER("ha_partition::common_index_read");
- LINT_INIT(key_len); /* used if have_start_key==TRUE */
- DBUG_PRINT("info", ("m_ordered: %u have_start_key: %u",
- m_ordered, have_start_key));
+ DBUG_PRINT("info", ("m_ordered %u m_ordered_scan_ong %u",
+ m_ordered, m_ordered_scan_ongoing));
if (have_start_key)
{
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)))
@@ -4433,24 +4882,16 @@ int ha_partition::common_index_read(uchar *buf, bool have_start_key)
}
DBUG_PRINT("info", ("m_ordered %u m_o_scan_ong %u have_start_key %u",
m_ordered, m_ordered_scan_ongoing, have_start_key));
- if (!m_ordered_scan_ongoing ||
- (have_start_key && m_start_key.flag == HA_READ_KEY_EXACT &&
- !m_pkey_is_clustered &&
- key_len >= m_curr_key_info[0]->key_length))
+ if (!m_ordered_scan_ongoing)
{
/*
- We use unordered index scan either when read_range is used and flag
- is set to not use ordered or when an exact key is used and in this
- case all records will be sorted equal and thus the sort order of the
- resulting records doesn't matter.
+ We use unordered index scan when read_range is used and flag
+ is set to not use ordered.
We also use an unordered index scan when the number of partitions to
scan is only one.
The unordered index scan will use the partition set created.
- Need to set unordered scan ongoing since we can come here even when
- it isn't set.
*/
DBUG_PRINT("info", ("doing unordered scan"));
- m_ordered_scan_ongoing= FALSE;
error= handle_unordered_scan_next_partition(buf);
}
else
@@ -4631,6 +5072,8 @@ int ha_partition::index_next(uchar * buf)
TODO(low priority):
If we want partition to work with the HANDLER commands, we
must be able to do index_last() -> index_prev() -> index_next()
+ 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_ordered_scan_ongoing)
@@ -4884,10 +5327,18 @@ int ha_partition::partition_scan_set_up(uchar * buf, bool idx_read_flag)
int ha_partition::handle_unordered_next(uchar *buf, bool is_next_same)
{
- handler *file= m_file[m_part_spec.start_part];
+ handler *file;
int error;
DBUG_ENTER("ha_partition::handle_unordered_next");
+ if (m_part_spec.start_part >= m_tot_parts)
+ {
+ /* Should never happen! */
+ DBUG_ASSERT(0);
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ }
+ file= m_file[m_part_spec.start_part];
+
/*
We should consider if this should be split into three functions as
partition_read_range is_next_same are always local constants
@@ -4948,6 +5399,7 @@ int ha_partition::handle_unordered_next(uchar *buf, bool is_next_same)
int ha_partition::handle_unordered_scan_next_partition(uchar * buf)
{
uint i;
+ int saved_error= HA_ERR_END_OF_FILE;
DBUG_ENTER("ha_partition::handle_unordered_scan_next_partition");
for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++)
@@ -4998,26 +5450,33 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf)
}
if ((error != HA_ERR_END_OF_FILE) && (error != HA_ERR_KEY_NOT_FOUND))
DBUG_RETURN(error);
- DBUG_PRINT("info", ("HA_ERR_END_OF_FILE on partition %d", i));
+
+ /*
+ 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));
}
- m_part_spec.start_part= NO_CURRENT_PART_ID;
- DBUG_RETURN(HA_ERR_END_OF_FILE);
+ if (saved_error == HA_ERR_END_OF_FILE)
+ m_part_spec.start_part= NO_CURRENT_PART_ID;
+ DBUG_RETURN(saved_error);
}
-/*
- Common routine to start index scan with ordered results
+/**
+ Common routine to start index scan with ordered results.
- SYNOPSIS
- handle_ordered_index_scan()
- out:buf Read row in MySQL Row Format
+ @param[out] buf Read row in MySQL Row Format
- RETURN VALUE
- HA_ERR_END_OF_FILE End of scan
- 0 Success
- other Error code
+ @return Operation status
+ @retval HA_ERR_END_OF_FILE End of scan
+ @retval HA_ERR_KEY_NOT_FOUNE End of scan
+ @retval 0 Success
+ @retval other Error code
- DESCRIPTION
+ @details
This part contains the logic to handle index scans that require ordered
output. This includes all except those started by read_range_first with
the flag ordered set to FALSE. Thus most direct index_read and all
@@ -5039,8 +5498,14 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
uint j= queue_first_element(&m_queue);
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");
+ if (m_key_not_found)
+ {
+ m_key_not_found= false;
+ bitmap_clear_all(&m_key_not_found_partitions);
+ }
m_top_entry= NO_CURRENT_PART_ID;
queue_remove_all(&m_queue);
@@ -5054,7 +5519,7 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
for (; first_used_part < m_part_spec.start_part; first_used_part++)
{
if (bitmap_is_set(&(m_part_info->used_partitions), first_used_part))
- part_rec_buf_ptr+= m_rec_length + PARTITION_BYTES_IN_POS;
+ part_rec_buf_ptr+= m_priority_queue_rec_len;
}
DBUG_PRINT("info", ("m_part_spec.start_part %u first_used_part %u",
m_part_spec.start_part, first_used_part));
@@ -5109,6 +5574,11 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
if (!error)
{
found= TRUE;
+ if (!m_using_extended_keys)
+ {
+ file->position(rec_buf_ptr);
+ memcpy(rec_buf_ptr + m_rec_length, file->ref, file->ref_length);
+ }
/*
Initialize queue without order first, simply insert
*/
@@ -5118,7 +5588,14 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
{
DBUG_RETURN(error);
}
- part_rec_buf_ptr+= m_rec_length + PARTITION_BYTES_IN_POS;
+ else if (error == HA_ERR_KEY_NOT_FOUND)
+ {
+ DBUG_PRINT("info", ("HA_ERR_KEY_NOT_FOUND from partition %u", i));
+ bitmap_set_bit(&m_key_not_found_partitions, i);
+ m_key_not_found= true;
+ saved_error= error;
+ }
+ part_rec_buf_ptr+= m_priority_queue_rec_len;
}
if (found)
{
@@ -5127,7 +5604,7 @@ 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, (void*)m_curr_key_info);
+ queue_set_cmp_arg(&m_queue, m_using_extended_keys? m_curr_key_info : (void*)this);
m_queue.elements= j - queue_first_element(&m_queue);
queue_fix(&m_queue);
return_top_record(buf);
@@ -5135,7 +5612,7 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
DBUG_PRINT("info", ("Record returned from partition %d", m_top_entry));
DBUG_RETURN(0);
}
- DBUG_RETURN(HA_ERR_END_OF_FILE);
+ DBUG_RETURN(saved_error);
}
@@ -5163,6 +5640,62 @@ void ha_partition::return_top_record(uchar *buf)
}
+/**
+ Add index_next/prev from partitions without exact match.
+
+ If there where any partitions that returned HA_ERR_KEY_NOT_FOUND when
+ ha_index_read_map was done, those partitions must be included in the
+ following index_next/prev call.
+*/
+
+int ha_partition::handle_ordered_index_scan_key_not_found()
+{
+ int error;
+ uint i, old_elements= m_queue.elements;
+ uchar *part_buf= m_ordered_rec_buffer;
+ uchar *curr_rec_buf= NULL;
+ DBUG_ENTER("ha_partition::handle_ordered_index_scan_key_not_found");
+ DBUG_ASSERT(m_key_not_found);
+ /*
+ Loop over all used partitions to get the correct offset
+ into m_ordered_rec_buffer.
+ */
+ for (i= 0; i < m_tot_parts; i++)
+ {
+ if (!bitmap_is_set(&m_part_info->used_partitions, i))
+ continue;
+
+ if (bitmap_is_set(&m_key_not_found_partitions, i))
+ {
+ /*
+ This partition is used and did return HA_ERR_KEY_NOT_FOUND
+ in index_read_map.
+ */
+ curr_rec_buf= part_buf + PARTITION_BYTES_IN_POS;
+ error= m_file[i]->index_next(curr_rec_buf);
+ /* HA_ERR_KEY_NOT_FOUND is not allowed from index_next! */
+ DBUG_ASSERT(error != HA_ERR_KEY_NOT_FOUND);
+ if (!error)
+ queue_insert(&m_queue, part_buf);
+ else if (error != HA_ERR_END_OF_FILE && error != HA_ERR_KEY_NOT_FOUND)
+ DBUG_RETURN(error);
+ }
+ part_buf += m_priority_queue_rec_len;
+ }
+ DBUG_ASSERT(curr_rec_buf);
+ bitmap_clear_all(&m_key_not_found_partitions);
+ m_key_not_found= false;
+
+ if (m_queue.elements > old_elements)
+ {
+ /* Update m_top_entry, which may have changed. */
+ uchar *key_buffer= queue_top(&m_queue);
+ m_top_entry= uint2korr(key_buffer);
+ }
+ DBUG_RETURN(0);
+}
+
+
/*
Common routine to handle index_next with ordered results
@@ -5182,9 +5715,45 @@ int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same)
int error;
uint part_id= m_top_entry;
uchar *rec_buf= queue_top(&m_queue) + PARTITION_BYTES_IN_POS;
- handler *file= m_file[part_id];
+ handler *file;
DBUG_ENTER("ha_partition::handle_ordered_next");
+ if (m_key_not_found)
+ {
+ if (is_next_same)
+ {
+ /* Only rows which match the key. */
+ m_key_not_found= false;
+ bitmap_clear_all(&m_key_not_found_partitions);
+ }
+ else
+ {
+ /* 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()))
+ DBUG_RETURN(error);
+ /*
+ If the queue top changed, i.e. one of the partitions that gave
+ HA_ERR_KEY_NOT_FOUND in index_read_map found the next record,
+ return it.
+ Otherwise replace the old with a call to index_next (fall through).
+ */
+ if (old_elements != m_queue.elements && part_id != m_top_entry)
+ {
+ return_top_record(buf);
+ DBUG_RETURN(0);
+ }
+ }
+ }
+ if (part_id >= m_tot_parts)
+ {
+ /* This should never happen! */
+ DBUG_ASSERT(0);
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ }
+
+ file= m_file[part_id];
+
if (m_index_scan_type == partition_read_range)
{
error= file->read_range_next();
@@ -5195,6 +5764,7 @@ int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same)
else
error= file->ha_index_next_same(rec_buf, m_start_key.key,
m_start_key.length);
+
if (error)
{
if (error == HA_ERR_END_OF_FILE)
@@ -5212,6 +5782,13 @@ int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same)
}
DBUG_RETURN(error);
}
+
+ if (!m_using_extended_keys)
+ {
+ file->position(rec_buf);
+ memcpy(rec_buf + m_rec_length, file->ref, file->ref_length);
+ }
+
queue_replace_top(&m_queue);
return_top_record(buf);
DBUG_PRINT("info", ("Record returned from partition %u", m_top_entry));
@@ -5272,6 +5849,24 @@ int ha_partition::handle_ordered_prev(uchar *buf)
and read_time calls
*/
+/**
+ Helper function for sorting according to number of rows in descending order.
+*/
+
+int ha_partition::compare_number_of_records(ha_partition *me,
+ const uint32 *a,
+ const uint32 *b)
+{
+ handler **file= me->m_file;
+ /* Note: sorting in descending order! */
+ if (file[*a]->stats.records > file[*b]->stats.records)
+ return -1;
+ if (file[*a]->stats.records < file[*b]->stats.records)
+ return 1;
+ return 0;
+}
+
+
/*
General method to gather info from handler
@@ -5339,27 +5934,29 @@ int ha_partition::handle_ordered_prev(uchar *buf)
int ha_partition::info(uint flag)
{
+ uint no_lock_flag= flag & HA_STATUS_NO_LOCK;
+ uint extra_var_flag= flag & HA_STATUS_VARIABLE_EXTRA;
DBUG_ENTER("ha_partition::info");
if (flag & HA_STATUS_AUTO)
{
bool auto_inc_is_first_in_idx= (table_share->next_number_keypart == 0);
- HA_DATA_PARTITION *ha_data= (HA_DATA_PARTITION*) table_share->ha_data;
DBUG_PRINT("info", ("HA_STATUS_AUTO"));
if (!table->found_next_number_field)
stats.auto_increment_value= 0;
- else if (ha_data->auto_inc_initialized)
+ else if (table_share->ha_part_data->auto_inc_initialized)
{
lock_auto_increment();
- stats.auto_increment_value= ha_data->next_auto_inc_val;
+ stats.auto_increment_value= table_share->ha_part_data->next_auto_inc_val;
unlock_auto_increment();
}
else
{
lock_auto_increment();
/* to avoid two concurrent initializations, check again when locked */
- if (ha_data->auto_inc_initialized)
- stats.auto_increment_value= ha_data->next_auto_inc_val;
+ if (table_share->ha_part_data->auto_inc_initialized)
+ stats.auto_increment_value=
+ table_share->ha_part_data->next_auto_inc_val;
else
{
handler *file, **file_array;
@@ -5370,7 +5967,7 @@ int ha_partition::info(uint flag)
do
{
file= *file_array;
- file->info(HA_STATUS_AUTO);
+ file->info(HA_STATUS_AUTO | no_lock_flag);
set_if_bigger(auto_increment_value,
file->stats.auto_increment_value);
} while (*(++file_array));
@@ -5379,10 +5976,11 @@ int ha_partition::info(uint flag)
stats.auto_increment_value= auto_increment_value;
if (auto_inc_is_first_in_idx)
{
- set_if_bigger(ha_data->next_auto_inc_val, auto_increment_value);
- ha_data->auto_inc_initialized= TRUE;
+ set_if_bigger(table_share->ha_part_data->next_auto_inc_val,
+ auto_increment_value);
+ table_share->ha_part_data->auto_inc_initialized= TRUE;
DBUG_PRINT("info", ("initializing next_auto_inc_val to %lu",
- (ulong) ha_data->next_auto_inc_val));
+ (ulong) table_share->ha_part_data->next_auto_inc_val));
}
}
unlock_auto_increment();
@@ -5423,7 +6021,7 @@ int ha_partition::info(uint flag)
if (bitmap_is_set(&(m_part_info->used_partitions), (file_array - m_file)))
{
file= *file_array;
- file->info(HA_STATUS_VARIABLE);
+ file->info(HA_STATUS_VARIABLE | no_lock_flag | extra_var_flag);
stats.records+= file->stats.records;
stats.deleted+= file->stats.deleted;
stats.data_file_length+= file->stats.data_file_length;
@@ -5505,7 +6103,7 @@ int ha_partition::info(uint flag)
if (!(flag & HA_STATUS_VARIABLE) ||
!bitmap_is_set(&(m_part_info->used_partitions),
(file_array - m_file)))
- file->info(HA_STATUS_VARIABLE);
+ file->info(HA_STATUS_VARIABLE | no_lock_flag | extra_var_flag);
if (file->stats.records > max_records)
{
max_records= file->stats.records;
@@ -5513,9 +6111,18 @@ int ha_partition::info(uint flag)
}
i++;
} while (*(++file_array));
+ /*
+ Sort the array of part_ids by number of records in
+ in descending order.
+ */
+ my_qsort2((void*) m_part_ids_sorted_by_num_of_records,
+ m_tot_parts,
+ sizeof(uint32),
+ (qsort2_cmp) compare_number_of_records,
+ this);
file= m_file[handler_instance];
- file->info(HA_STATUS_CONST);
+ file->info(HA_STATUS_CONST | no_lock_flag);
stats.block_size= file->stats.block_size;
stats.create_time= file->stats.create_time;
ref_length= m_ref_length;
@@ -5531,7 +6138,7 @@ int ha_partition::info(uint flag)
Note: all engines does not support HA_STATUS_ERRKEY, so set errkey.
*/
file->errkey= errkey;
- file->info(HA_STATUS_ERRKEY);
+ file->info(HA_STATUS_ERRKEY | no_lock_flag);
errkey= file->errkey;
}
if (flag & HA_STATUS_TIME)
@@ -5548,7 +6155,7 @@ int ha_partition::info(uint flag)
do
{
file= *file_array;
- file->info(HA_STATUS_TIME);
+ file->info(HA_STATUS_TIME | no_lock_flag);
if (file->stats.update_time > stats.update_time)
stats.update_time= file->stats.update_time;
} while (*(++file_array));
@@ -5557,12 +6164,12 @@ int ha_partition::info(uint flag)
}
-void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info,
+void ha_partition::get_dynamic_partition_info(PARTITION_STATS *stat_info,
uint part_id)
{
handler *file= m_file[part_id];
- file->info(HA_STATUS_CONST | HA_STATUS_TIME | HA_STATUS_VARIABLE |
- HA_STATUS_NO_LOCK);
+ file->info(HA_STATUS_TIME | HA_STATUS_VARIABLE |
+ HA_STATUS_VARIABLE_EXTRA | HA_STATUS_NO_LOCK);
stat_info->records= file->stats.records;
stat_info->mean_rec_length= file->stats.mean_rec_length;
@@ -5580,34 +6187,35 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info,
}
-/*
- General function to prepare handler for certain behavior
+/**
+ General function to prepare handler for certain behavior.
- SYNOPSIS
- extra()
+ @param[in] operation operation to execute
operation Operation type for extra call
- RETURN VALUE
- >0 Error code
- 0 Success
+ @return status
+ @retval 0 success
+ @retval >0 error code
+
+ @detail
- DESCRIPTION
extra() is called whenever the server wishes to send a hint to
the storage engine. The MyISAM engine implements the most hints.
We divide the parameters into the following categories:
- 1) Parameters used by most handlers
- 2) Parameters used by some non-MyISAM handlers
- 3) Parameters used only by MyISAM
- 4) Parameters only used by temporary tables for query processing
- 5) Parameters only used by MyISAM internally
- 6) Parameters not used at all
- 7) Parameters only used by federated tables for query processing
- 8) Parameters only used by NDB
+ 1) Operations used by most handlers
+ 2) Operations used by some non-MyISAM handlers
+ 3) Operations used only by MyISAM
+ 4) Operations only used by temporary tables for query processing
+ 5) Operations only used by MyISAM internally
+ 6) Operations not used at all
+ 7) Operations only used by federated tables for query processing
+ 8) Operations only used by NDB
+ 9) Operations only used by MERGE
The partition handler need to handle category 1), 2) and 3).
- 1) Parameters used by most handlers
+ 1) Operations used by most handlers
-----------------------------------
HA_EXTRA_RESET:
This option is used by most handlers and it resets the handler state
@@ -5646,7 +6254,7 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info,
ensure disk based tables are flushed at end of query execution.
Currently is never used.
- 2) Parameters used by some non-MyISAM handlers
+ 2) Operations used by some non-MyISAM handlers
----------------------------------------------
HA_EXTRA_KEYREAD_PRESERVE_FIELDS:
This is a strictly InnoDB feature that is more or less undocumented.
@@ -5665,7 +6273,7 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info,
SQL constructs.
Not used by MyISAM.
- 3) Parameters used only by MyISAM
+ 3) Operations used only by MyISAM
---------------------------------
HA_EXTRA_NORMAL:
Only used in MyISAM to reset quick mode, not implemented by any other
@@ -5794,7 +6402,7 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info,
Only used by MyISAM, called when altering table, closing tables to
enforce a reopen of the table files.
- 4) Parameters only used by temporary tables for query processing
+ 4) Operations only used by temporary tables for query processing
----------------------------------------------------------------
HA_EXTRA_RESET_STATE:
Same as reset() except that buffers are not released. If there is
@@ -5825,7 +6433,7 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info,
tables used in query processing.
Not handled by partition handler.
- 5) Parameters only used by MyISAM internally
+ 5) Operations only used by MyISAM internally
--------------------------------------------
HA_EXTRA_REINIT_CACHE:
This call reinitializes the READ CACHE described above if there is one
@@ -5860,19 +6468,19 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info,
HA_EXTRA_CHANGE_KEY_TO_UNIQUE:
Only used by MyISAM, never called.
- 6) Parameters not used at all
+ 6) Operations not used at all
-----------------------------
HA_EXTRA_KEY_CACHE:
HA_EXTRA_NO_KEY_CACHE:
This parameters are no longer used and could be removed.
- 7) Parameters only used by federated tables for query processing
+ 7) Operations only used by federated tables for query processing
----------------------------------------------------------------
HA_EXTRA_INSERT_WITH_UPDATE:
Inform handler that an "INSERT...ON DUPLICATE KEY UPDATE" will be
executed. This condition is unset by HA_EXTRA_NO_IGNORE_DUP_KEY.
- 8) Parameters only used by NDB
+ 8) Operations only used by NDB
------------------------------
HA_EXTRA_DELETE_CANNOT_BATCH:
HA_EXTRA_UPDATE_CANNOT_BATCH:
@@ -5880,6 +6488,14 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info,
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.
+
+ 9) Operations only used by MERGE
+ ------------------------------
+ HA_EXTRA_ADD_CHILDREN_LIST:
+ HA_EXTRA_ATTACH_CHILDREN:
+ HA_EXTRA_IS_ATTACHED_CHILDREN:
+ HA_EXTRA_DETACH_CHILDREN:
+ Special actions for MERGE tables. Ignore.
*/
int ha_partition::extra(enum ha_extra_function operation)
@@ -5920,7 +6536,7 @@ int ha_partition::extra(enum ha_extra_function operation)
if (!m_extra_cache)
m_extra_cache_part_id= m_part_spec.start_part;
DBUG_ASSERT(m_extra_cache_part_id == m_part_spec.start_part);
- VOID(m_file[m_part_spec.start_part]->extra(HA_EXTRA_PREPARE_FOR_UPDATE));
+ (void) m_file[m_part_spec.start_part]->extra(HA_EXTRA_PREPARE_FOR_UPDATE);
}
break;
case HA_EXTRA_NORMAL:
@@ -5996,13 +6612,22 @@ int ha_partition::extra(enum ha_extra_function operation)
/* Category 7), used by federated handlers */
case HA_EXTRA_INSERT_WITH_UPDATE:
DBUG_RETURN(loop_extra(operation));
- /* Category 8) Parameters only used by NDB */
+ /* Category 8) Operations only used by NDB */
case HA_EXTRA_DELETE_CANNOT_BATCH:
case HA_EXTRA_UPDATE_CANNOT_BATCH:
{
/* Currently only NDB use the *_CANNOT_BATCH */
break;
}
+ /* Category 9) Operations only used by MERGE */
+ case HA_EXTRA_ADD_CHILDREN_LIST:
+ case HA_EXTRA_ATTACH_CHILDREN:
+ case HA_EXTRA_IS_ATTACHED_CHILDREN:
+ case HA_EXTRA_DETACH_CHILDREN:
+ {
+ /* Special actions for MERGE tables. Ignore. */
+ break;
+ }
/*
http://dev.mysql.com/doc/refman/5.1/en/partitioning-limitations.html
says we no longer support logging to partitioned tables, so we fail
@@ -6032,7 +6657,7 @@ int ha_partition::extra(enum ha_extra_function operation)
0 Success
DESCRIPTION
- Called at end of each statement to reste buffers
+ Called at end of each statement to reset buffers
*/
int ha_partition::reset(void)
@@ -6189,13 +6814,14 @@ void ha_partition::late_extra_cache(uint partition_id)
if (m_extra_cache)
{
if (m_extra_cache_size == 0)
- VOID(file->extra(HA_EXTRA_CACHE));
+ (void) file->extra(HA_EXTRA_CACHE);
else
- VOID(file->extra_opt(HA_EXTRA_CACHE, m_extra_cache_size));
+ (void) file->extra_opt(HA_EXTRA_CACHE, m_extra_cache_size);
}
if (m_extra_prepare_for_update)
{
- VOID(file->extra(HA_EXTRA_PREPARE_FOR_UPDATE));
+ DBUG_ASSERT(m_extra_cache);
+ (void) file->extra(HA_EXTRA_PREPARE_FOR_UPDATE);
}
m_extra_cache_part_id= partition_id;
DBUG_VOID_RETURN;
@@ -6221,7 +6847,7 @@ void ha_partition::late_extra_no_cache(uint partition_id)
if (!m_extra_cache && !m_extra_prepare_for_update)
DBUG_VOID_RETURN;
file= m_file[partition_id];
- VOID(file->extra(HA_EXTRA_NO_CACHE));
+ (void) file->extra(HA_EXTRA_NO_CACHE);
DBUG_ASSERT(partition_id == m_extra_cache_part_id);
m_extra_cache_part_id= NO_CURRENT_PART_ID;
DBUG_VOID_RETURN;
@@ -6249,21 +6875,83 @@ const key_map *ha_partition::keys_to_use_for_scanning()
DBUG_RETURN(m_file[0]->keys_to_use_for_scanning());
}
-#define MAX_PARTS_FOR_OPTIMIZER_CALLS 10
-/*
- Prepare start variables for estimating optimizer costs.
- @param[out] num_used_parts Number of partitions after pruning.
- @param[out] check_min_num Number of partitions to call.
- @param[out] first first used partition.
+/**
+ Minimum number of rows to base optimizer estimate on.
*/
-void ha_partition::partitions_optimizer_call_preparations(uint *first,
- uint *num_used_parts,
- uint *check_min_num)
+
+ha_rows ha_partition::min_rows_for_estimate()
{
- *first= bitmap_get_first_set(&(m_part_info->used_partitions));
- *num_used_parts= bitmap_bits_set(&(m_part_info->used_partitions));
- *check_min_num= min(MAX_PARTS_FOR_OPTIMIZER_CALLS, *num_used_parts);
+ uint i, max_used_partitions, tot_used_partitions;
+ DBUG_ENTER("ha_partition::min_rows_for_estimate");
+
+ tot_used_partitions= bitmap_bits_set(&m_part_info->used_partitions);
+
+ /*
+ All partitions might have been left as unused during partition pruning
+ due to, for example, an impossible WHERE condition. Nonetheless, the
+ optimizer might still attempt to perform (e.g. range) analysis where an
+ estimate of the the number of rows is calculated using records_in_range.
+ Hence, to handle this and other possible cases, use zero as the minimum
+ number of rows to base the estimate on if no partition is being used.
+ */
+ if (!tot_used_partitions)
+ DBUG_RETURN(0);
+
+ /*
+ Allow O(log2(tot_partitions)) increase in number of used partitions.
+ This gives O(tot_rows/log2(tot_partitions)) rows to base the estimate on.
+ I.e when the total number of partitions doubles, allow one more
+ partition to be checked.
+ */
+ i= 2;
+ max_used_partitions= 1;
+ while (i < m_tot_parts)
+ {
+ max_used_partitions++;
+ i= i << 1;
+ }
+ if (max_used_partitions > tot_used_partitions)
+ max_used_partitions= tot_used_partitions;
+
+ /* stats.records is already updated by the info(HA_STATUS_VARIABLE) call. */
+ DBUG_PRINT("info", ("max_used_partitions: %u tot_rows: %lu",
+ max_used_partitions,
+ (ulong) stats.records));
+ DBUG_PRINT("info", ("tot_used_partitions: %u min_rows_to_check: %lu",
+ tot_used_partitions,
+ (ulong) stats.records * max_used_partitions
+ / tot_used_partitions));
+ DBUG_RETURN(stats.records * max_used_partitions / tot_used_partitions);
+}
+
+
+/**
+ Get the biggest used partition.
+
+ Starting at the N:th biggest partition and skips all non used
+ partitions, returning the biggest used partition found
+
+ @param[in,out] part_index Skip the *part_index biggest partitions
+
+ @return The biggest used partition with index not lower than *part_index.
+ @retval NO_CURRENT_PART_ID No more partition used.
+ @retval != NO_CURRENT_PART_ID partition id of biggest used partition with
+ index >= *part_index supplied. Note that
+ *part_index will be updated to the next
+ partition index to use.
+*/
+
+uint ha_partition::get_biggest_used_partition(uint *part_index)
+{
+ uint part_id;
+ while ((*part_index) < m_tot_parts)
+ {
+ part_id= m_part_ids_sorted_by_num_of_records[(*part_index)++];
+ if (bitmap_is_set(&m_part_info->used_partitions, part_id))
+ return part_id;
+ }
+ return NO_CURRENT_PART_ID;
}
@@ -6279,115 +6967,107 @@ void ha_partition::partitions_optimizer_call_preparations(uint *first,
double ha_partition::scan_time()
{
- double scan_time= 0.0;
- uint first, part_id, num_used_parts, check_min_num, partitions_called= 0;
+ double scan_time= 0;
+ handler **file;
DBUG_ENTER("ha_partition::scan_time");
- partitions_optimizer_call_preparations(&first, &num_used_parts, &check_min_num);
- for (part_id= first; partitions_called < num_used_parts ; part_id++)
- {
- if (!bitmap_is_set(&(m_part_info->used_partitions), part_id))
- continue;
- scan_time+= m_file[part_id]->scan_time();
- partitions_called++;
- if (partitions_called >= check_min_num && scan_time != 0.0)
- {
- DBUG_RETURN(scan_time *
- (double) num_used_parts / (double) partitions_called);
- }
- }
+ for (file= m_file; *file; file++)
+ if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file)))
+ scan_time+= (*file)->scan_time();
DBUG_RETURN(scan_time);
}
-/*
- Estimate rows for records_in_range or estimate_rows_upper_bound.
+/**
+ Find number of records in a range.
+ @param inx Index number
+ @param min_key Start of range
+ @param max_key End of range
- @param is_records_in_range call records_in_range instead of
- estimate_rows_upper_bound.
- @param inx (only for records_in_range) index to use.
- @param min_key (only for records_in_range) start of range.
- @param max_key (only for records_in_range) end of range.
+ @return Number of rows in range.
- @return Number of rows or HA_POS_ERROR.
+ Given a starting key, and an ending key estimate the number of rows that
+ will exist between the two. max_key may be empty which in case determine
+ if start_key matches any rows.
*/
-ha_rows ha_partition::estimate_rows(bool is_records_in_range, uint inx,
- key_range *min_key, key_range *max_key)
+
+ha_rows ha_partition::records_in_range(uint inx, key_range *min_key,
+ key_range *max_key)
{
- ha_rows rows, estimated_rows= 0;
- uint first, part_id, num_used_parts, check_min_num, partitions_called= 0;
+ ha_rows min_rows_to_check, rows, estimated_rows=0, checked_rows= 0;
+ uint partition_index= 0, part_id;
DBUG_ENTER("ha_partition::records_in_range");
- partitions_optimizer_call_preparations(&first, &num_used_parts, &check_min_num);
- for (part_id= first; partitions_called < num_used_parts ; part_id++)
+ min_rows_to_check= min_rows_for_estimate();
+
+ while ((part_id= get_biggest_used_partition(&partition_index))
+ != NO_CURRENT_PART_ID)
{
- if (!bitmap_is_set(&(m_part_info->used_partitions), part_id))
- continue;
- if (is_records_in_range)
- rows= m_file[part_id]->records_in_range(inx, min_key, max_key);
- else
- rows= m_file[part_id]->estimate_rows_upper_bound();
+ 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));
+
if (rows == HA_POS_ERROR)
DBUG_RETURN(HA_POS_ERROR);
estimated_rows+= rows;
- partitions_called++;
- if (partitions_called >= check_min_num && estimated_rows)
+ checked_rows+= m_file[part_id]->stats.records;
+ /*
+ Returning 0 means no rows can be found, so we must continue
+ this loop as long as we have estimated_rows == 0.
+ Also many engines return 1 to indicate that there may exist
+ a matching row, we do not normalize this by dividing by number of
+ used partitions, but leave it to be returned as a sum, which will
+ reflect that we will need to scan each partition's index.
+
+ Note that this statistics may not always be correct, so we must
+ continue even if the current partition has 0 rows, since we might have
+ deleted rows from the current partition, or inserted to the next
+ partition.
+ */
+ if (estimated_rows && checked_rows &&
+ checked_rows >= min_rows_to_check)
{
- DBUG_RETURN(estimated_rows * num_used_parts / partitions_called);
+ DBUG_PRINT("info",
+ ("records_in_range(inx %u): %lu (%lu * %lu / %lu)",
+ inx,
+ (ulong) (estimated_rows * stats.records / checked_rows),
+ (ulong) estimated_rows,
+ (ulong) stats.records,
+ (ulong) checked_rows));
+ DBUG_RETURN(estimated_rows * stats.records / checked_rows);
}
}
+ DBUG_PRINT("info", ("records_in_range(inx %u): %lu",
+ inx,
+ (ulong) estimated_rows));
DBUG_RETURN(estimated_rows);
}
-/*
- Find number of records in a range
-
- SYNOPSIS
- records_in_range()
- inx Index number
- min_key Start of range
- max_key End of range
-
- RETURN VALUE
- Number of rows in range
-
- DESCRIPTION
- Given a starting key, and an ending key estimate the number of rows that
- will exist between the two. end_key may be empty which in case determine
- if start_key matches any rows.
-
- Called from opt_range.cc by check_quick_keys().
-
- monty: MUST be called for each range and added.
- Note that MySQL will assume that if this returns 0 there is no
- matching rows for the range!
-*/
-
-ha_rows ha_partition::records_in_range(uint inx, key_range *min_key,
- key_range *max_key)
-{
- DBUG_ENTER("ha_partition::records_in_range");
-
- DBUG_RETURN(estimate_rows(TRUE, inx, min_key, max_key));
-}
-
-
-/*
- Estimate upper bound of number of rows
-
- SYNOPSIS
- estimate_rows_upper_bound()
+/**
+ Estimate upper bound of number of rows.
- RETURN VALUE
- Number of rows
+ @return Number of rows.
*/
ha_rows ha_partition::estimate_rows_upper_bound()
{
+ ha_rows rows, tot_rows= 0;
+ handler **file= m_file;
DBUG_ENTER("ha_partition::estimate_rows_upper_bound");
- DBUG_RETURN(estimate_rows(FALSE, 0, NULL, NULL));
+ do
+ {
+ if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file)))
+ {
+ rows= (*file)->estimate_rows_upper_bound();
+ if (rows == HA_POS_ERROR)
+ DBUG_RETURN(HA_POS_ERROR);
+ tot_rows+= rows;
+ }
+ } while (*(++file));
+ DBUG_RETURN(tot_rows);
}
@@ -6520,30 +7200,131 @@ enum row_type ha_partition::get_row_type() const
}
+void ha_partition::append_row_to_str(String &str)
+{
+ Field **field_ptr;
+ const uchar *rec;
+ bool is_rec0= !m_err_rec || m_err_rec == table->record[0];
+ if (is_rec0)
+ rec= table->record[0];
+ else
+ rec= m_err_rec;
+ // If PK, use full PK instead of full part field array!
+ if (table->s->primary_key != MAX_KEY)
+ {
+ KEY *key= table->key_info + table->s->primary_key;
+ KEY_PART_INFO *key_part= key->key_part;
+ KEY_PART_INFO *key_part_end= key_part + key->key_parts;
+ if (!is_rec0)
+ set_key_field_ptr(key, rec, table->record[0]);
+ for (; key_part != key_part_end; key_part++)
+ {
+ Field *field= key_part->field;
+ str.append(" ");
+ str.append(field->field_name);
+ str.append(":");
+ field_unpack(&str, field, rec, 0, false);
+ }
+ if (!is_rec0)
+ set_key_field_ptr(key, table->record[0], rec);
+ }
+ else
+ {
+ if (!is_rec0)
+ set_field_ptr(m_part_info->full_part_field_array, rec,
+ table->record[0]);
+ /* No primary key, use full partition field array. */
+ for (field_ptr= m_part_info->full_part_field_array;
+ *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(m_part_info->full_part_field_array, table->record[0],
+ rec);
+ }
+}
+
+
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));
if (error == HA_ERR_NO_PARTITION_FOUND)
- m_part_info->print_no_partition_found(table);
- else
{
- /* In case m_file has not been initialized, like in bug#42438 */
- if (m_file)
+ switch(thd_sql_command(thd))
{
- if (m_last_part >= m_tot_parts)
+ case SQLCOM_DELETE:
+ case SQLCOM_DELETE_MULTI:
+ case SQLCOM_UPDATE:
+ case SQLCOM_UPDATE_MULTI:
+ if (m_err_rec)
{
- DBUG_ASSERT(0);
- m_last_part= 0;
+ uint max_length;
+ char buf[MAX_KEY_LENGTH];
+ const char *msg= "Found a row in wrong partition (";
+ String str(buf,sizeof(buf),system_charset_info);
+ uint32 part_id;
+ /* Should only happen on DELETE or UPDATE! */
+ str.length(0);
+ str.append_ulonglong(m_last_part);
+ str.append(" != ");
+ if (!get_part_for_delete(m_err_rec, m_rec0, m_part_info, &part_id))
+ {
+ str.append_ulonglong(part_id);
+ }
+ str.append(")");
+ append_row_to_str(str);
+ /* Log this error, so the DBA can notice it and fix it! */
+ sql_print_error("Table '%-192s' corrupted: %s%s\n"
+ "Please CHECK and REPAIR the table!",
+ table->s->table_name.str, msg, str.c_ptr_safe());
+
+ max_length= (MYSQL_ERRMSG_SIZE-
+ (uint) strlen(msg));
+ if (str.length() >= max_length)
+ {
+ str.length(max_length-4);
+ str.append(STRING_WITH_LEN("..."));
+ }
+ my_printf_error(ER_NO_PARTITION_FOR_GIVEN_VALUE, "%s%s", MYF(0),
+ msg, str.c_ptr_safe());
+ m_err_rec= NULL;
+ DBUG_VOID_RETURN;
}
- m_file[m_last_part]->print_error(error, errflag);
+ default:
+ {
+ if (!(thd->lex->alter_info.flags & ALTER_TRUNCATE_PARTITION))
+ {
+ m_part_info->print_no_partition_found(table, errflag);
+ DBUG_VOID_RETURN;
+ }
+ }
+ /* fall through to generic error handling. */
}
- else
- handler::print_error(error, errflag);
}
+
+ /* In case m_file has not been initialized, like in bug#42438 */
+ if (m_file)
+ {
+ if (m_last_part >= m_tot_parts)
+ {
+ DBUG_ASSERT(0);
+ m_last_part= 0;
+ }
+ m_file[m_last_part]->print_error(error, errflag);
+ }
+ else
+ handler::print_error(error, errflag);
DBUG_VOID_RETURN;
}
@@ -6571,9 +7352,42 @@ bool ha_partition::get_error_message(int error, String *buf)
*/
uint ha_partition::alter_table_flags(uint flags)
{
+ uint flags_to_return, flags_to_check;
DBUG_ENTER("ha_partition::alter_table_flags");
- DBUG_RETURN(ht->alter_table_flags(flags) |
- m_file[0]->alter_table_flags(flags));
+
+ flags_to_return= ht->alter_table_flags(flags);
+ flags_to_return|= m_file[0]->alter_table_flags(flags);
+
+ /*
+ If one partition fails we must be able to revert the change for the other,
+ already altered, partitions. So both ADD and DROP can only be supported in
+ pairs.
+ */
+ flags_to_check= HA_INPLACE_ADD_INDEX_NO_READ_WRITE;
+ flags_to_check|= HA_INPLACE_DROP_INDEX_NO_READ_WRITE;
+ if ((flags_to_return & flags_to_check) != flags_to_check)
+ flags_to_return&= ~flags_to_check;
+ flags_to_check= HA_INPLACE_ADD_UNIQUE_INDEX_NO_READ_WRITE;
+ flags_to_check|= HA_INPLACE_DROP_UNIQUE_INDEX_NO_READ_WRITE;
+ if ((flags_to_return & flags_to_check) != flags_to_check)
+ flags_to_return&= ~flags_to_check;
+ flags_to_check= HA_INPLACE_ADD_PK_INDEX_NO_READ_WRITE;
+ flags_to_check|= HA_INPLACE_DROP_PK_INDEX_NO_READ_WRITE;
+ if ((flags_to_return & flags_to_check) != flags_to_check)
+ flags_to_return&= ~flags_to_check;
+ flags_to_check= HA_INPLACE_ADD_INDEX_NO_WRITE;
+ flags_to_check|= HA_INPLACE_DROP_INDEX_NO_WRITE;
+ if ((flags_to_return & flags_to_check) != flags_to_check)
+ flags_to_return&= ~flags_to_check;
+ flags_to_check= HA_INPLACE_ADD_UNIQUE_INDEX_NO_WRITE;
+ flags_to_check|= HA_INPLACE_DROP_UNIQUE_INDEX_NO_WRITE;
+ if ((flags_to_return & flags_to_check) != flags_to_check)
+ flags_to_return&= ~flags_to_check;
+ flags_to_check= HA_INPLACE_ADD_PK_INDEX_NO_WRITE;
+ flags_to_check|= HA_INPLACE_DROP_PK_INDEX_NO_WRITE;
+ if ((flags_to_return & flags_to_check) != flags_to_check)
+ flags_to_return&= ~flags_to_check;
+ DBUG_RETURN(flags_to_return);
}
@@ -6601,42 +7415,202 @@ bool ha_partition::check_if_incompatible_data(HA_CREATE_INFO *create_info,
/**
- Support of fast or online add/drop index
+ Helper class for [final_]add_index, see handler.h
*/
-int ha_partition::add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys)
+
+class ha_partition_add_index : public handler_add_index
{
- handler **file;
+public:
+ handler_add_index **add_array;
+ ha_partition_add_index(TABLE* table_arg, KEY* key_info_arg,
+ uint num_of_keys_arg)
+ : handler_add_index(table_arg, key_info_arg, num_of_keys_arg)
+ {}
+ ~ha_partition_add_index() {}
+};
+
+
+/**
+ Support of in-place add/drop index
+
+ @param table_arg Table to add index to
+ @param key_info Struct over the new keys to add
+ @param num_of_keys Number of keys to add
+ @param[out] add Data to be submitted with final_add_index
+
+ @return Operation status
+ @retval 0 Success
+ @retval != 0 Failure (error code returned, and all operations rollbacked)
+*/
+
+int ha_partition::add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys,
+ handler_add_index **add)
+{
+ uint i;
int ret= 0;
+ THD *thd= ha_thd();
+ ha_partition_add_index *part_add_index;
+ DBUG_ENTER("ha_partition::add_index");
/*
There has already been a check in fix_partition_func in mysql_alter_table
before this call, which checks for unique/primary key violations of the
partitioning function. So no need for extra check here.
*/
- for (file= m_file; *file; file++)
- if ((ret= (*file)->add_index(table_arg, key_info, num_of_keys)))
+
+ /*
+ This will be freed at the end of the statement.
+ And destroyed at final_add_index. (Sql_alloc does not free in delete).
+ */
+ part_add_index= new (thd->mem_root)
+ ha_partition_add_index(table_arg, key_info, num_of_keys);
+ if (!part_add_index)
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ part_add_index->add_array= (handler_add_index **)
+ thd->alloc(sizeof(void *) * m_tot_parts);
+ if (!part_add_index->add_array)
+ {
+ delete part_add_index;
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ }
+
+ for (i= 0; i < m_tot_parts; i++)
+ {
+ if ((ret= m_file[i]->add_index(table_arg, key_info, num_of_keys,
+ &part_add_index->add_array[i])))
goto err;
- return ret;
+ }
+ *add= part_add_index;
+ DBUG_RETURN(ret);
err:
- if (file > m_file)
+ /* Rollback all prepared partitions. i - 1 .. 0 */
+ while (i)
{
- uint *key_numbers= (uint*) ha_thd()->alloc(sizeof(uint) * num_of_keys);
- KEY *old_key_info= table_arg->key_info;
- uint i;
- /* Use the newly added key_info as table->key_info to remove them. */
- for (i= 0; i < num_of_keys; i++)
- key_numbers[i]= i;
- table_arg->key_info= key_info;
- while (--file >= m_file)
+ i--;
+ (void) m_file[i]->final_add_index(part_add_index->add_array[i], false);
+ }
+ delete part_add_index;
+ DBUG_RETURN(ret);
+}
+
+
+/**
+ Second phase of in-place add index.
+
+ @param add Info from add_index
+ @param commit Should we commit or rollback the add_index operation
+
+ @return Operation status
+ @retval 0 Success
+ @retval != 0 Failure (error code returned)
+
+ @note If commit is false, index changes are rolled back by dropping the
+ added indexes. If commit is true, nothing is done as the indexes
+ were already made active in ::add_index()
+*/
+
+int ha_partition::final_add_index(handler_add_index *add, bool commit)
+{
+ ha_partition_add_index *part_add_index;
+ uint i;
+ int ret= 0;
+
+ DBUG_ENTER("ha_partition::final_add_index");
+
+ if (!add)
+ {
+ DBUG_ASSERT(!commit);
+ DBUG_RETURN(0);
+ }
+ part_add_index= static_cast<class ha_partition_add_index*>(add);
+
+ for (i= 0; i < m_tot_parts; i++)
+ {
+ if ((ret= m_file[i]->final_add_index(part_add_index->add_array[i], commit)))
+ goto err;
+ DBUG_EXECUTE_IF("ha_partition_fail_final_add_index", {
+ /* Simulate a failure by rollback the second partition */
+ if (m_tot_parts > 1)
+ {
+ i++;
+ m_file[i]->final_add_index(part_add_index->add_array[i], false);
+ /* Set an error that is specific to ha_partition. */
+ ret= HA_ERR_NO_PARTITION_FOUND;
+ goto err;
+ }
+ });
+ }
+ delete part_add_index;
+ DBUG_RETURN(ret);
+err:
+ uint j;
+ uint *key_numbers= NULL;
+ KEY *old_key_info= NULL;
+ uint num_of_keys= 0;
+ int error;
+
+ /* How could this happen? Needed to create a covering test case :) */
+ DBUG_ASSERT(ret == HA_ERR_NO_PARTITION_FOUND);
+
+ if (i > 0)
+ {
+ num_of_keys= part_add_index->num_of_keys;
+ key_numbers= (uint*) ha_thd()->alloc(sizeof(uint) * num_of_keys);
+ if (!key_numbers)
{
- (void) (*file)->prepare_drop_index(table_arg, key_numbers, num_of_keys);
- (void) (*file)->final_drop_index(table_arg);
+ sql_print_error("Failed with error handling of adding index:\n"
+ "committing index failed, and when trying to revert "
+ "already committed partitions we failed allocating\n"
+ "memory for the index for table '%s'",
+ table_share->table_name.str);
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
}
- table_arg->key_info= old_key_info;
+ old_key_info= table->key_info;
+ /*
+ Use the newly added key_info as table->key_info to remove them.
+ Note that this requires the subhandlers to use name lookup of the
+ index. They must use given table->key_info[key_number], they cannot
+ use their local view of the keys, since table->key_info only include
+ the indexes to be removed here.
+ */
+ for (j= 0; j < num_of_keys; j++)
+ key_numbers[j]= j;
+ table->key_info= part_add_index->key_info;
}
- return ret;
-}
+ for (j= 0; j < m_tot_parts; j++)
+ {
+ if (j < i)
+ {
+ /* Remove the newly added index */
+ error= m_file[j]->prepare_drop_index(table, key_numbers, num_of_keys);
+ if (error || m_file[j]->final_drop_index(table))
+ {
+ sql_print_error("Failed with error handling of adding index:\n"
+ "committing index failed, and when trying to revert "
+ "already committed partitions we failed removing\n"
+ "the index for table '%s' partition nr %d",
+ table_share->table_name.str, j);
+ }
+ }
+ else if (j > i)
+ {
+ /* Rollback non finished partitions */
+ if (m_file[j]->final_add_index(part_add_index->add_array[j], false))
+ {
+ /* How could this happen? */
+ sql_print_error("Failed with error handling of adding index:\n"
+ "Rollback of add_index failed for table\n"
+ "'%s' partition nr %d",
+ table_share->table_name.str, j);
+ }
+ }
+ }
+ if (i > 0)
+ table->key_info= old_key_info;
+ delete part_add_index;
+ DBUG_RETURN(ret);
+}
int ha_partition::prepare_drop_index(TABLE *table_arg, uint *key_num,
uint num_of_keys)
@@ -6771,23 +7745,43 @@ uint ha_partition::min_record_length(uint options) const
If they belong to different partitions we decide that they are not
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:
+ 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.
+ If they belong to different partitions we decide that they are not
+ the same record. Otherwise we use the particular handler to decide if
+ they are the same. Sort in partition id order if not equal.
+
+ It is incorrect, MariaDB has an alternative fix.
*/
int ha_partition::cmp_ref(const uchar *ref1, const uchar *ref2)
{
- uint part_id;
+ int cmp;
my_ptrdiff_t diff1, diff2;
- handler *file;
DBUG_ENTER("ha_partition::cmp_ref");
+ cmp = m_file[0]->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]))
{
- part_id= uint2korr(ref1);
- file= m_file[part_id];
- DBUG_ASSERT(part_id < m_tot_parts);
- DBUG_RETURN(file->cmp_ref((ref1 + PARTITION_BYTES_IN_POS),
- (ref2 + PARTITION_BYTES_IN_POS)));
+ /* This means that the references are same and are in same partition.*/
+ DBUG_RETURN(0);
}
+
+ /*
+ In Innodb we compare with either primary key value or global DB_ROW_ID so
+ it is not possible that the two references are equal and are in different
+ partitions, but in myisam it is possible since we are comparing offsets.
+ 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)
@@ -6815,11 +7809,10 @@ int ha_partition::reset_auto_increment(ulonglong value)
{
handler **file= m_file;
int res;
- HA_DATA_PARTITION *ha_data= (HA_DATA_PARTITION*) table_share->ha_data;
DBUG_ENTER("ha_partition::reset_auto_increment");
lock_auto_increment();
- ha_data->auto_inc_initialized= FALSE;
- ha_data->next_auto_inc_val= 0;
+ table_share->ha_part_data->auto_inc_initialized= FALSE;
+ table_share->ha_part_data->next_auto_inc_val= 0;
do
{
if ((res= (*file)->ha_reset_auto_increment(value)) != 0)
@@ -6833,7 +7826,7 @@ int ha_partition::reset_auto_increment(ulonglong value)
/**
This method is called by update_auto_increment which in turn is called
by the individual handlers as part of write_row. We use the
- table_share->ha_data->next_auto_inc_val, or search all
+ table_share->ha_part_data->next_auto_inc_val, or search all
partitions for the highest auto_increment_value if not initialized or
if auto_increment field is a secondary part of a key, we must search
every partition when holding a mutex to be sure of correctness.
@@ -6861,8 +7854,7 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong first_value_part, max_first_value;
handler **file= m_file;
first_value_part= max_first_value= *first_value;
- /* Must lock and find highest value among all partitions. */
- lock_auto_increment();
+ /* Must find highest value among all partitions. */
do
{
/* Only nb_desired_values = 1 makes sense */
@@ -6873,7 +7865,6 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment,
*first_value= first_value_part;
/* log that the error was between table/partition handler */
sql_print_error("Partition failed to reserve auto_increment value");
- unlock_auto_increment();
DBUG_VOID_RETURN;
}
DBUG_PRINT("info", ("first_value_part: %lu", (ulong) first_value_part));
@@ -6881,18 +7872,16 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment,
} while (*(++file));
*first_value= max_first_value;
*nb_reserved_values= 1;
- unlock_auto_increment();
}
else
{
THD *thd= ha_thd();
- HA_DATA_PARTITION *ha_data= (HA_DATA_PARTITION*) table_share->ha_data;
/*
This is initialized in the beginning of the first write_row call.
*/
- DBUG_ASSERT(ha_data->auto_inc_initialized);
+ DBUG_ASSERT(table_share->ha_part_data->auto_inc_initialized);
/*
- Get a lock for handling the auto_increment in table_share->ha_data
+ Get a lock for handling the auto_increment in table_share->ha_part_data
for avoiding two concurrent statements getting the same number.
*/
@@ -6911,16 +7900,17 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment,
if (!auto_increment_safe_stmt_log_lock &&
thd->lex->sql_command != SQLCOM_INSERT &&
mysql_bin_log.is_open() &&
- !thd->current_stmt_binlog_row_based &&
- (thd->options & OPTION_BIN_LOG))
+ !thd->is_current_stmt_binlog_format_row() &&
+ (thd->variables.option_bits & OPTION_BIN_LOG))
{
DBUG_PRINT("info", ("locking auto_increment_safe_stmt_log_lock"));
auto_increment_safe_stmt_log_lock= TRUE;
}
/* this gets corrected (for offset/increment) in update_auto_increment */
- *first_value= ha_data->next_auto_inc_val;
- ha_data->next_auto_inc_val+= nb_desired_values * increment;
+ *first_value= table_share->ha_part_data->next_auto_inc_val;
+ table_share->ha_part_data->next_auto_inc_val+=
+ nb_desired_values * increment;
unlock_auto_increment();
DBUG_PRINT("info", ("*first_value: %lu", (ulong) *first_value));
@@ -6940,10 +7930,9 @@ void ha_partition::release_auto_increment()
}
else if (next_insert_id)
{
- HA_DATA_PARTITION *ha_data= (HA_DATA_PARTITION*) table_share->ha_data;
ulonglong next_auto_inc_val;
lock_auto_increment();
- next_auto_inc_val= ha_data->next_auto_inc_val;
+ next_auto_inc_val= table_share->ha_part_data->next_auto_inc_val;
/*
If the current auto_increment values is lower than the reserved
value, and the reserved value was reserved by this thread,
@@ -6958,10 +7947,10 @@ void ha_partition::release_auto_increment()
with SET INSERT_ID, i.e. forced/non generated values.
*/
if (thd->auto_inc_intervals_forced.maximum() < next_insert_id)
- ha_data->next_auto_inc_val= next_insert_id;
+ table_share->ha_part_data->next_auto_inc_val= next_insert_id;
}
- DBUG_PRINT("info", ("ha_data->next_auto_inc_val: %lu",
- (ulong) ha_data->next_auto_inc_val));
+ DBUG_PRINT("info", ("table_share->ha_part_data->next_auto_inc_val: %lu",
+ (ulong) table_share->ha_part_data->next_auto_inc_val));
/* Unlock the multi row statement lock taken in get_auto_increment */
if (auto_increment_safe_stmt_log_lock)
@@ -7061,146 +8050,292 @@ int ha_partition::indexes_are_disabled(void)
}
-/****************************************************************************
- MODULE Partition Share
-****************************************************************************/
-/*
- Service routines for ... methods.
--------------------------------------------------------------------------
- Variables for partition share methods. A hash used to track open tables.
- A mutex for the hash table and an init variable to check if hash table
- is initialized.
- There is also a constant ending of the partition handler file name.
-*/
-
-#ifdef NOT_USED
-static HASH partition_open_tables;
-static pthread_mutex_t partition_mutex;
-static int partition_init= 0;
+/**
+ Check/fix misplaced rows.
+ @param read_part_id Partition to check/fix.
+ @param repair If true, move misplaced rows to correct partition.
-/*
- Function we use in the creation of our hash to get key.
+ @return Operation status.
+ @retval 0 Success
+ @retval != 0 Error
*/
-static uchar *partition_get_key(PARTITION_SHARE *share, size_t *length,
- my_bool not_used __attribute__ ((unused)))
+int ha_partition::check_misplaced_rows(uint read_part_id, bool repair)
{
- *length= share->table_name_length;
- return (uchar *) share->table_name;
-}
+ int result= 0;
+ uint32 correct_part_id;
+ longlong func_value;
+ longlong num_misplaced_rows= 0;
-/*
- Example of simple lock controls. The "share" it creates is structure we
- will pass to each partition handler. Do you have to have one of these?
- Well, you have pieces that are used for locking, and they are needed to
- function.
-*/
+ DBUG_ENTER("ha_partition::check_misplaced_rows");
-static PARTITION_SHARE *get_share(const char *table_name, TABLE *table)
-{
- PARTITION_SHARE *share;
- uint length;
- char *tmp_name;
+ DBUG_ASSERT(m_file);
- /*
- So why does this exist? There is no way currently to init a storage
- engine.
- Innodb and BDB both have modifications to the server to allow them to
- do this. Since you will not want to do this, this is probably the next
- best method.
- */
- if (!partition_init)
+ if (repair)
{
- /* Hijack a mutex for init'ing the storage engine */
- pthread_mutex_lock(&LOCK_mysql_create_db);
- if (!partition_init)
- {
- partition_init++;
- VOID(pthread_mutex_init(&partition_mutex, MY_MUTEX_INIT_FAST));
- (void) hash_init(&partition_open_tables, system_charset_info, 32, 0, 0,
- (hash_get_key) partition_get_key, 0, 0);
- }
- pthread_mutex_unlock(&LOCK_mysql_create_db);
+ /* We must read the full row, if we need to move it! */
+ bitmap_set_all(table->read_set);
+ bitmap_set_all(table->write_set);
+ }
+ else
+ {
+ /* Only need to read the partitioning fields. */
+ bitmap_union(table->read_set, &m_part_info->full_part_field_set);
}
- pthread_mutex_lock(&partition_mutex);
- length= (uint) strlen(table_name);
- if (!(share= (PARTITION_SHARE *) hash_search(&partition_open_tables,
- (uchar *) table_name, length)))
+ if ((result= m_file[read_part_id]->ha_rnd_init(1)))
+ DBUG_RETURN(result);
+
+ while (true)
{
- if (!(share= (PARTITION_SHARE *)
- my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
- &share, (uint) sizeof(*share),
- &tmp_name, (uint) length + 1, NullS)))
+ if ((result= m_file[read_part_id]->rnd_next(m_rec0)))
{
- pthread_mutex_unlock(&partition_mutex);
- return NULL;
+ if (result == HA_ERR_RECORD_DELETED)
+ continue;
+ if (result != HA_ERR_END_OF_FILE)
+ break;
+
+ if (num_misplaced_rows > 0)
+ {
+ print_admin_msg(ha_thd(), MI_MAX_MSG_BUF, "warning",
+ table_share->db.str, table->alias,
+ opt_op_name[REPAIR_PARTS],
+ "Moved %lld misplaced rows",
+ num_misplaced_rows);
+ }
+ /* End-of-file reached, all rows are now OK, reset result and break. */
+ result= 0;
+ break;
}
- share->use_count= 0;
- share->table_name_length= length;
- share->table_name= tmp_name;
- strmov(share->table_name, table_name);
- if (my_hash_insert(&partition_open_tables, (uchar *) share))
- goto error;
- thr_lock_init(&share->lock);
- pthread_mutex_init(&share->mutex, MY_MUTEX_INIT_FAST);
- }
- share->use_count++;
- pthread_mutex_unlock(&partition_mutex);
+ result= m_part_info->get_partition_id(m_part_info, &correct_part_id,
+ &func_value);
+ if (result)
+ break;
- return share;
+ if (correct_part_id != read_part_id)
+ {
+ num_misplaced_rows++;
+ if (!repair)
+ {
+ /* Check. */
+ print_admin_msg(ha_thd(), MI_MAX_MSG_BUF, "error",
+ table_share->db.str, table->alias,
+ opt_op_name[CHECK_PARTS],
+ "Found a misplaced row");
+ /* Break on first misplaced row! */
+ result= HA_ADMIN_NEEDS_UPGRADE;
+ break;
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Moving row from partition %d to %d",
+ read_part_id, correct_part_id));
-error:
- pthread_mutex_unlock(&partition_mutex);
- my_free((uchar*) share, MYF(0));
+ /*
+ Insert row into correct partition. Notice that there are no commit
+ for every N row, so the repair will be one large transaction!
+ */
+ if ((result= m_file[correct_part_id]->ha_write_row(m_rec0)))
+ {
+ /*
+ We have failed to insert a row, it might have been a duplicate!
+ */
+ char buf[MAX_KEY_LENGTH];
+ String str(buf,sizeof(buf),system_charset_info);
+ str.length(0);
+ if (result == HA_ERR_FOUND_DUPP_KEY)
+ {
+ str.append("Duplicate key found, "
+ "please update or delete the record:\n");
+ result= HA_ADMIN_CORRUPT;
+ }
+ m_err_rec= NULL;
+ append_row_to_str(str);
+
+ /*
+ If the engine supports transactions, the failure will be
+ rollbacked.
+ */
+ if (!m_file[correct_part_id]->has_transactions())
+ {
+ /* 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",
+ table->s->table_name.str,
+ read_part_id,
+ correct_part_id,
+ str.c_ptr_safe());
+ }
+ print_admin_msg(ha_thd(), MI_MAX_MSG_BUF, "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,
+ str.c_ptr_safe());
+ break;
+ }
- return NULL;
+ /* Delete row from wrong partition. */
+ if ((result= m_file[read_part_id]->ha_delete_row(m_rec0)))
+ {
+ if (m_file[correct_part_id]->has_transactions())
+ break;
+ /*
+ We have introduced a duplicate, since we failed to remove it
+ from the wrong partition.
+ */
+ char buf[MAX_KEY_LENGTH];
+ String str(buf,sizeof(buf),system_charset_info);
+ str.length(0);
+ m_err_rec= NULL;
+ 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"
+ " error %d. But it was already inserted into"
+ " part %d, when moving the misplaced row!"
+ "\nPlease manually fix the duplicate row:\n%s",
+ table->s->table_name.str,
+ read_part_id,
+ result,
+ correct_part_id,
+ str.c_ptr_safe());
+ break;
+ }
+ }
+ }
+ }
+
+ int tmp_result= m_file[read_part_id]->ha_rnd_end();
+ DBUG_RETURN(result ? result : tmp_result);
}
-/*
- Free lock controls. We call this whenever we close a table. If the table
- had the last reference to the share then we free memory associated with
- it.
-*/
+#define KEY_PARTITIONING_CHANGED_STR \
+ "KEY () partitioning changed, please run:\nALTER TABLE %s.%s %s"
-static int free_share(PARTITION_SHARE *share)
+int ha_partition::check_for_upgrade(HA_CHECK_OPT *check_opt)
{
- pthread_mutex_lock(&partition_mutex);
- if (!--share->use_count)
+ int error= HA_ADMIN_NEEDS_CHECK;
+ DBUG_ENTER("ha_partition::check_for_upgrade");
+
+ /*
+ This is called even without FOR UPGRADE,
+ if the .frm version is lower than the current version.
+ In that case return that it needs checking!
+ */
+ if (!(check_opt->sql_flags & TT_FOR_UPGRADE))
+ DBUG_RETURN(error);
+
+ /*
+ Partitions will be checked for during their ha_check!
+
+ Check if KEY (sub)partitioning was used and any field's hash calculation
+ differs from 5.1, see bug#14521864.
+ */
+ if (table->s->mysql_version < 50503 && // 5.1 table (<5.5.3)
+ ((m_part_info->part_type == HASH_PARTITION && // KEY partitioned
+ m_part_info->list_of_part_fields) ||
+ (m_is_sub_partitioned && // KEY subpartitioned
+ m_part_info->list_of_subpart_fields)))
{
- hash_delete(&partition_open_tables, (uchar *) share);
- thr_lock_delete(&share->lock);
- pthread_mutex_destroy(&share->mutex);
- my_free((uchar*) share, MYF(0));
+ Field **field;
+ if (m_is_sub_partitioned)
+ {
+ field= m_part_info->subpart_field_array;
+ }
+ else
+ {
+ field= m_part_info->part_field_array;
+ }
+ for (; *field; field++)
+ {
+ switch ((*field)->real_type()) {
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_NEWDATE:
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ {
+ THD *thd= ha_thd();
+ char *part_buf;
+ String db_name, table_name;
+ uint part_buf_len;
+ bool skip_generation= false;
+ 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);
+ if (m_part_info->key_algorithm != partition_info::KEY_ALGORITHM_NONE)
+ {
+ /*
+ Only possible when someone tampered with .frm files,
+ like during tests :)
+ */
+ skip_generation= true;
+ }
+ m_part_info->key_algorithm= partition_info::KEY_ALGORITHM_51;
+ if (skip_generation ||
+ !(part_buf= generate_partition_syntax(m_part_info,
+ &part_buf_len,
+ true,
+ true,
+ NULL,
+ NULL,
+ NULL)) ||
+ print_admin_msg(thd, SQL_ADMIN_MSG_TEXT_SIZE + 1, "error",
+ table_share->db.str,
+ table->alias,
+ opt_op_name[CHECK_PARTS],
+ KEY_PARTITIONING_CHANGED_STR,
+ db_name.c_ptr_safe(),
+ table_name.c_ptr_safe(),
+ part_buf))
+ {
+ /* Error creating admin message (too long string?). */
+ print_admin_msg(thd, MI_MAX_MSG_BUF, "error",
+ table_share->db.str, table->alias,
+ opt_op_name[CHECK_PARTS],
+ KEY_PARTITIONING_CHANGED_STR,
+ db_name.c_ptr_safe(), table_name.c_ptr_safe(),
+ "<old partition clause>, but add ALGORITHM = 1"
+ " between 'KEY' and '(' to change the metadata"
+ " without the need of a full table rebuild.");
+ }
+ m_part_info->key_algorithm= old_algorithm;
+ DBUG_RETURN(error);
+ }
+ default:
+ /* Not affected! */
+ ;
+ }
+ }
}
- pthread_mutex_unlock(&partition_mutex);
- return 0;
+ DBUG_RETURN(error);
}
-#endif /* NOT_USED */
+
struct st_mysql_storage_engine partition_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
-mysql_declare_plugin(partition)
-{
- MYSQL_STORAGE_ENGINE_PLUGIN,
- &partition_storage_engine,
- "partition",
- "Mikael Ronstrom, MySQL AB",
- "Partition Storage Engine Helper",
- PLUGIN_LICENSE_GPL,
- partition_initialize, /* Plugin Init */
- NULL, /* Plugin Deinit */
- 0x0100, /* 1.0 */
- NULL, /* status variables */
- NULL, /* system variables */
- NULL /* config options */
-}
-mysql_declare_plugin_end;
maria_declare_plugin(partition)
{
MYSQL_STORAGE_ENGINE_PLUGIN,
diff --git a/sql/ha_partition.h b/sql/ha_partition.h
index a25c5cec5b2..e8e3858d076 100644
--- a/sql/ha_partition.h
+++ b/sql/ha_partition.h
@@ -1,5 +1,8 @@
+#ifndef HA_PARTITION_INCLUDED
+#define HA_PARTITION_INCLUDED
+
/*
- Copyright (c) 2005, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2005, 2013, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,46 +15,25 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifdef __GNUC__
#pragma interface /* gcc class implementation */
#endif
-enum partition_keywords
-{
- PKW_HASH= 0, PKW_RANGE, PKW_LIST, PKW_KEY, PKW_MAXVALUE, PKW_LINEAR
-};
-
-/*
- PARTITION_SHARE is a structure that will be shared amoung all open handlers
- The partition implements the minimum of what you will probably need.
-*/
+#include "sql_partition.h" /* part_id_range, partition_element */
+#include "queues.h" /* QUEUE */
-#ifdef NOT_USED
-typedef struct st_partition_share
-{
- char *table_name;
- uint table_name_length, use_count;
- pthread_mutex_t mutex;
- THR_LOCK lock;
-} PARTITION_SHARE;
-#endif
-
-/**
- Partition specific ha_data struct.
- @todo: move all partition specific data from TABLE_SHARE here.
-*/
-typedef struct st_ha_data_partition
+enum partition_keywords
{
- ulonglong next_auto_inc_val; /**< first non reserved value */
- pthread_mutex_t LOCK_auto_inc;
- bool auto_inc_initialized;
-} HA_DATA_PARTITION;
+ PKW_HASH= 0, PKW_RANGE, PKW_LIST, PKW_KEY, PKW_MAXVALUE, PKW_LINEAR,
+ PKW_COLUMNS, PKW_ALGORITHM
+};
#define PARTITION_BYTES_IN_POS 2
-#define PARTITION_ENABLED_TABLE_FLAGS (HA_FILE_BASED | HA_REC_NOT_IN_SEQ)
+#define PARTITION_ENABLED_TABLE_FLAGS (HA_FILE_BASED | \
+ HA_REC_NOT_IN_SEQ | \
+ HA_CAN_REPAIR)
#define PARTITION_DISABLED_TABLE_FLAGS (HA_CAN_GEOMETRY | \
HA_CAN_FULLTEXT | \
HA_DUPLICATE_POS | \
@@ -67,6 +49,8 @@ typedef struct st_ha_data_partition
/* offset to the engines array */
#define PAR_ENGINES_OFFSET 12
+extern "C" int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2);
+
class ha_partition :public handler
{
private:
@@ -104,7 +88,24 @@ private:
*/
KEY *m_curr_key_info[3]; // Current index
uchar *m_rec0; // table->record[0]
+ const uchar *m_err_rec; // record which gave error
QUEUE m_queue; // Prio queue used by sorted read
+
+ /*
+ 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;
+
+ /*
+ If true, then sorting records by key value also sorts them by their
+ underlying_table_rowid.
+ */
+ bool m_using_extended_keys;
+
/*
Since the partition handler is a handler on top of other handlers, it
is necessary to keep information about what the underlying handler
@@ -123,7 +124,7 @@ private:
uint m_reorged_parts; // Number of reorganised parts
uint m_tot_parts; // Total number of partitions;
- uint m_no_locks; // For engines like ha_blackhole, which needs no locks
+ uint m_num_locks; // For engines like ha_blackhole, which needs no locks
uint m_last_part; // Last file that we update,write,read
int m_lock_type; // Remembers type of last
// external_lock
@@ -184,9 +185,6 @@ private:
Variables for lock structures.
*/
THR_LOCK_DATA lock; /* MySQL lock */
-#ifdef NOT_USED
- PARTITION_SHARE *share; /* Shared lock info */
-#endif
bool auto_increment_lock; /**< lock reading/updating auto_inc */
/**
@@ -199,6 +197,15 @@ private:
ha_rows m_bulk_inserted_rows;
/** used for prediction of start_bulk_insert rows */
enum_monotonicity_info m_part_func_monotonicity_info;
+ /** Sorted array of partition ids in descending order of number of rows. */
+ uint32 *m_part_ids_sorted_by_num_of_records;
+ /* Compare function for my_qsort2, for reversed order. */
+ static int compare_number_of_records(ha_partition *me,
+ const uint32 *a,
+ const uint32 *b);
+ /** partitions that returned HA_ERR_KEY_NOT_FOUND. */
+ MY_BITMAP m_key_not_found_partitions;
+ bool m_key_not_found;
public:
handler *clone(const char *name, MEM_ROOT *mem_root);
virtual void set_part_info(partition_info *part_info)
@@ -265,10 +272,10 @@ public:
size_t pack_frm_len);
virtual int drop_partitions(const char *path);
virtual int rename_partitions(const char *path);
- bool get_no_parts(const char *name, uint *no_parts)
+ bool get_no_parts(const char *name, uint *num_parts)
{
DBUG_ENTER("ha_partition::get_no_parts");
- *no_parts= m_tot_parts;
+ *num_parts= m_tot_parts;
DBUG_RETURN(0);
}
virtual void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share);
@@ -280,7 +287,8 @@ private:
void cleanup_new_partition(uint part_count);
int prepare_new_partition(TABLE *table, HA_CREATE_INFO *create_info,
handler *file, const char *part_name,
- partition_element *p_elem);
+ partition_element *p_elem,
+ uint disable_non_uniq_indexes);
/*
delete_table, rename_table and create uses very similar logic which
is packed into this routine.
@@ -388,6 +396,7 @@ public:
virtual int update_row(const uchar * old_data, uchar * new_data);
virtual int delete_row(const uchar * buf);
virtual int delete_all_rows(void);
+ virtual int truncate();
virtual void start_bulk_insert(ha_rows rows);
virtual int end_bulk_insert();
private:
@@ -396,6 +405,15 @@ private:
long estimate_read_buffer_size(long original_size);
public:
+ /*
+ Method for truncating a specific partition.
+ (i.e. ALTER TABLE t1 TRUNCATE PARTITION p).
+
+ @remark This method is a partitioning-specific hook
+ and thus not a member of the general SE API.
+ */
+ int truncate_partition(Alter_info *, bool *binlog_stmt);
+
virtual bool is_fatal_error(int error, uint flags)
{
if (!handler::is_fatal_error(error, flags) ||
@@ -523,6 +541,7 @@ private:
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);
+ int handle_ordered_index_scan_key_not_found();
int handle_ordered_next(uchar * buf, bool next_same);
int handle_ordered_prev(uchar * buf);
void return_top_record(uchar * buf);
@@ -538,27 +557,25 @@ public:
-------------------------------------------------------------------------
*/
virtual int info(uint);
- void get_dynamic_partition_info(PARTITION_INFO *stat_info,
+ void get_dynamic_partition_info(PARTITION_STATS *stat_info,
uint part_id);
virtual int extra(enum ha_extra_function operation);
virtual int extra_opt(enum ha_extra_function operation, ulong cachesize);
virtual int reset(void);
- /*
- Do not allow caching of partitioned tables, since we cannot return
- a callback or engine_data that would work for a generic engine.
- */
- virtual my_bool register_query_cache_table(THD *thd, char *table_key,
- uint key_length,
- qc_engine_callback
- *engine_callback,
- ulonglong *engine_data)
- {
- *engine_callback= NULL;
- *engine_data= 0;
- return FALSE;
- }
+ virtual uint count_query_cache_dependant_tables(uint8 *tables_type);
+ virtual my_bool
+ register_query_cache_dependant_tables(THD *thd,
+ Query_cache *cache,
+ Query_cache_block_table **block,
+ uint *n);
private:
+ my_bool reg_query_cache_dependant_table(THD *thd,
+ char *key, uint key_len, uint8 type,
+ Query_cache *cache,
+ Query_cache_block_table
+ **block_table,
+ handler *file, uint *n);
static const uint NO_CURRENT_PART_ID;
int loop_extra(enum ha_extra_function operation);
void late_extra_cache(uint partition_id);
@@ -586,15 +603,9 @@ public:
*/
private:
- /*
- Helper function to get the minimum number of partitions to use for
- the optimizer hints/cost calls.
- */
- void partitions_optimizer_call_preparations(uint *num_used_parts,
- uint *check_min_num,
- uint *first);
- ha_rows estimate_rows(bool is_records_in_range, uint inx,
- key_range *min_key, key_range *max_key);
+ /* Helper functions for optimizer hints. */
+ ha_rows min_rows_for_estimate();
+ uint get_biggest_used_partition(uint *part_index);
public:
/*
@@ -969,17 +980,16 @@ private:
/* lock already taken */
if (auto_increment_safe_stmt_log_lock)
return;
- DBUG_ASSERT(table_share->ha_data && !auto_increment_lock);
+ DBUG_ASSERT(table_share->ha_part_data && !auto_increment_lock);
if(table_share->tmp_table == NO_TMP_TABLE)
{
- HA_DATA_PARTITION *ha_data= (HA_DATA_PARTITION*) table_share->ha_data;
auto_increment_lock= TRUE;
- pthread_mutex_lock(&ha_data->LOCK_auto_inc);
+ mysql_mutex_lock(&table_share->ha_part_data->LOCK_auto_inc);
}
}
virtual void unlock_auto_increment()
{
- DBUG_ASSERT(table_share->ha_data);
+ DBUG_ASSERT(table_share->ha_part_data);
/*
If auto_increment_safe_stmt_log_lock is true, we have to keep the lock.
It will be set to false and thus unlocked at the end of the statement by
@@ -987,21 +997,19 @@ private:
*/
if(auto_increment_lock && !auto_increment_safe_stmt_log_lock)
{
- HA_DATA_PARTITION *ha_data= (HA_DATA_PARTITION*) table_share->ha_data;
- pthread_mutex_unlock(&ha_data->LOCK_auto_inc);
+ mysql_mutex_unlock(&table_share->ha_part_data->LOCK_auto_inc);
auto_increment_lock= FALSE;
}
}
virtual void set_auto_increment_if_higher(Field *field)
{
- HA_DATA_PARTITION *ha_data= (HA_DATA_PARTITION*) table_share->ha_data;
ulonglong nr= (((Field_num*) field)->unsigned_flag ||
field->val_int() > 0) ? field->val_int() : 0;
lock_auto_increment();
- DBUG_ASSERT(ha_data->auto_inc_initialized == TRUE);
+ DBUG_ASSERT(table_share->ha_part_data->auto_inc_initialized == TRUE);
/* must check when the mutex is taken */
- if (nr >= ha_data->next_auto_inc_val)
- ha_data->next_auto_inc_val= nr + 1;
+ if (nr >= table_share->ha_part_data->next_auto_inc_val)
+ table_share->ha_part_data->next_auto_inc_val= nr + 1;
unlock_auto_increment();
}
@@ -1073,7 +1081,9 @@ public:
They are used for on-line/fast alter table add/drop index:
-------------------------------------------------------------------------
*/
- virtual int add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys);
+ virtual int add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys,
+ handler_add_index **add);
+ virtual int final_add_index(handler_add_index *add, bool commit);
virtual int prepare_drop_index(TABLE *table_arg, uint *key_num,
uint num_of_keys);
virtual int final_drop_index(TABLE *table_arg);
@@ -1107,9 +1117,18 @@ public:
virtual bool check_and_repair(THD *thd);
virtual bool auto_repair(int error) const;
virtual bool is_crashed() const;
+ virtual int check_for_upgrade(HA_CHECK_OPT *check_opt);
private:
int handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt, uint flags);
+ int handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt, uint part_id,
+ uint flag);
+ /**
+ Check if the rows are placed in the correct partition. If the given
+ argument is true, then move the rows to the correct partition.
+ */
+ int check_misplaced_rows(uint read_part_id, bool repair);
+ void append_row_to_str(String &str);
public:
/*
-------------------------------------------------------------------------
@@ -1119,12 +1138,13 @@ public:
virtual int backup(TD* thd, HA_CHECK_OPT *check_opt);
virtual int restore(THD* thd, HA_CHECK_OPT *check_opt);
- virtual int assign_to_keycache(THD* thd, HA_CHECK_OPT *check_opt);
- virtual int preload_keys(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);
/*
-------------------------------------------------------------------------
@@ -1170,4 +1190,9 @@ public:
DBUG_ASSERT(h == m_file[i]->ht);
return h;
}
+
+
+ friend int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2);
};
+
+#endif /* HA_PARTITION_INCLUDED */
diff --git a/sql/handler.cc b/sql/handler.cc
index bcf051deb62..086a6534120 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 2009, 2014, Monty Program Ab
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,8 +11,8 @@
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
/** @file handler.cc
@@ -24,16 +24,36 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "rpl_handler.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
+#include "sql_table.h" // build_table_filename
+#include "sql_parse.h" // check_stack_overrun
+#include "sql_acl.h" // SUPER_ACL
+#include "sql_base.h" // free_io_cache
+#include "discover.h" // writefrm
+#include "log_event.h" // *_rows_log_event
#include "create_options.h"
#include "rpl_filter.h"
#include <myisampack.h>
+#include "transaction.h"
#include "myisam.h"
+#include "probes_mysql.h"
+#include "debug_sync.h" // DEBUG_SYNC
+#include "sql_audit.h"
+#include "../mysys/my_handler_errors.h"
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
#endif
+#ifdef WITH_ARIA_STORAGE_ENGINE
+#include "../storage/maria/ha_maria.h"
+#endif
+
/*
While we have legacy_db_type, we have this array to
check for dups and to find handlerton from legacy_db_type.
@@ -45,7 +65,8 @@ 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} };
+KEY_CREATE_INFO default_key_create_info=
+ { HA_KEY_ALG_UNDEF, 0, {NullS, 0}, {NullS, 0} };
/* number of entries in handlertons[] */
ulong total_ha= 0;
@@ -82,7 +103,6 @@ uint known_extensions_id= 0;
static int commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans,
bool is_real_trans);
-
static plugin_ref ha_default_plugin(THD *thd)
{
if (thd->variables.table_plugin)
@@ -137,7 +157,7 @@ redo:
if ((plugin= my_plugin_lock_by_name(thd, name, MYSQL_STORAGE_ENGINE_PLUGIN)))
{
handlerton *hton= plugin_data(plugin, handlerton *);
- if (!(hton->flags & HTON_NOT_USER_SELECTABLE))
+ if (hton && !(hton->flags & HTON_NOT_USER_SELECTABLE))
return plugin;
/*
@@ -175,15 +195,6 @@ plugin_ref ha_lock_engine(THD *thd, const handlerton *hton)
}
-#ifdef NOT_USED
-static handler *create_default(TABLE_SHARE *table, MEM_ROOT *mem_root)
-{
- handlerton *hton= ha_default_handlerton(current_thd);
- return (hton && hton->create) ? hton->create(hton, table, mem_root) : NULL;
-}
-#endif
-
-
handlerton *ha_resolve_by_legacy_type(THD *thd, enum legacy_db_type db_type)
{
plugin_ref plugin;
@@ -221,11 +232,9 @@ handlerton *ha_checktype(THD *thd, enum legacy_db_type database_type,
return NULL;
}
+ RUN_HOOK(transaction, after_rollback, (thd, FALSE));
+
switch (database_type) {
-#ifndef NO_HASH
- case DB_TYPE_HASH:
- return ha_resolve_by_legacy_type(thd, DB_TYPE_HASH);
-#endif
case DB_TYPE_MRG_ISAM:
return ha_resolve_by_legacy_type(thd, DB_TYPE_MRG_MYISAM);
default:
@@ -275,13 +284,24 @@ handler *get_ha_partition(partition_info *part_info)
}
else
{
- my_error(ER_OUTOFMEMORY, MYF(0), static_cast<int>(sizeof(ha_partition)));
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR),
+ static_cast<int>(sizeof(ha_partition)));
}
DBUG_RETURN(((handler*) partition));
}
#endif
+static const char **handler_errmsgs;
+
+C_MODE_START
+static const char **get_handler_errmsgs()
+{
+ return handler_errmsgs;
+}
+C_MODE_END
+
+
/**
Register handler error messages for use with my_error().
@@ -293,63 +313,64 @@ handler *get_ha_partition(partition_info *part_info)
int ha_init_errors(void)
{
-#define SETMSG(nr, msg) errmsgs[(nr) - HA_ERR_FIRST]= (msg)
- const char **errmsgs;
+#define SETMSG(nr, msg) handler_errmsgs[(nr) - HA_ERR_FIRST]= (msg)
/* Allocate a pointer array for the error message strings. */
/* Zerofill it to avoid uninitialized gaps. */
- if (! (errmsgs= (const char**) my_malloc(HA_ERR_ERRORS * sizeof(char*),
- MYF(MY_WME | MY_ZEROFILL))))
+ if (! (handler_errmsgs= (const char**) my_malloc(HA_ERR_ERRORS * sizeof(char*),
+ MYF(MY_WME | MY_ZEROFILL))))
return 1;
/* Set the dedicated error messages. */
- SETMSG(HA_ERR_KEY_NOT_FOUND, ER(ER_KEY_NOT_FOUND));
- SETMSG(HA_ERR_FOUND_DUPP_KEY, ER(ER_DUP_KEY));
+ SETMSG(HA_ERR_KEY_NOT_FOUND, ER_DEFAULT(ER_KEY_NOT_FOUND));
+ SETMSG(HA_ERR_FOUND_DUPP_KEY, ER_DEFAULT(ER_DUP_KEY));
SETMSG(HA_ERR_RECORD_CHANGED, "Update wich is recoverable");
SETMSG(HA_ERR_WRONG_INDEX, "Wrong index given to function");
- SETMSG(HA_ERR_CRASHED, ER(ER_NOT_KEYFILE));
- SETMSG(HA_ERR_WRONG_IN_RECORD, ER(ER_CRASHED_ON_USAGE));
+ SETMSG(HA_ERR_CRASHED, ER_DEFAULT(ER_NOT_KEYFILE));
+ SETMSG(HA_ERR_WRONG_IN_RECORD, ER_DEFAULT(ER_CRASHED_ON_USAGE));
SETMSG(HA_ERR_OUT_OF_MEM, "Table handler out of memory");
SETMSG(HA_ERR_NOT_A_TABLE, "Incorrect file format '%.64s'");
SETMSG(HA_ERR_WRONG_COMMAND, "Command not supported");
- SETMSG(HA_ERR_OLD_FILE, ER(ER_OLD_KEYFILE));
+ SETMSG(HA_ERR_OLD_FILE, ER_DEFAULT(ER_OLD_KEYFILE));
SETMSG(HA_ERR_NO_ACTIVE_RECORD, "No record read in update");
SETMSG(HA_ERR_RECORD_DELETED, "Intern record deleted");
- SETMSG(HA_ERR_RECORD_FILE_FULL, ER(ER_RECORD_FILE_FULL));
+ SETMSG(HA_ERR_RECORD_FILE_FULL, ER_DEFAULT(ER_RECORD_FILE_FULL));
SETMSG(HA_ERR_INDEX_FILE_FULL, "No more room in index file '%.64s'");
SETMSG(HA_ERR_END_OF_FILE, "End in next/prev/first/last");
- SETMSG(HA_ERR_UNSUPPORTED, ER(ER_ILLEGAL_HA));
+ SETMSG(HA_ERR_UNSUPPORTED, ER_DEFAULT(ER_ILLEGAL_HA));
SETMSG(HA_ERR_TO_BIG_ROW, "Too big row");
SETMSG(HA_WRONG_CREATE_OPTION, "Wrong create option");
- SETMSG(HA_ERR_FOUND_DUPP_UNIQUE, ER(ER_DUP_UNIQUE));
+ SETMSG(HA_ERR_FOUND_DUPP_UNIQUE, ER_DEFAULT(ER_DUP_UNIQUE));
SETMSG(HA_ERR_UNKNOWN_CHARSET, "Can't open charset");
- SETMSG(HA_ERR_WRONG_MRG_TABLE_DEF, ER(ER_WRONG_MRG_TABLE));
- SETMSG(HA_ERR_CRASHED_ON_REPAIR, ER(ER_CRASHED_ON_REPAIR));
- SETMSG(HA_ERR_CRASHED_ON_USAGE, ER(ER_CRASHED_ON_USAGE));
- SETMSG(HA_ERR_LOCK_WAIT_TIMEOUT, ER(ER_LOCK_WAIT_TIMEOUT));
- SETMSG(HA_ERR_LOCK_TABLE_FULL, ER(ER_LOCK_TABLE_FULL));
- SETMSG(HA_ERR_READ_ONLY_TRANSACTION, ER(ER_READ_ONLY_TRANSACTION));
- SETMSG(HA_ERR_LOCK_DEADLOCK, ER(ER_LOCK_DEADLOCK));
- SETMSG(HA_ERR_CANNOT_ADD_FOREIGN, ER(ER_CANNOT_ADD_FOREIGN));
- SETMSG(HA_ERR_NO_REFERENCED_ROW, ER(ER_NO_REFERENCED_ROW_2));
- SETMSG(HA_ERR_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED_2));
+ SETMSG(HA_ERR_WRONG_MRG_TABLE_DEF, ER_DEFAULT(ER_WRONG_MRG_TABLE));
+ SETMSG(HA_ERR_CRASHED_ON_REPAIR, ER_DEFAULT(ER_CRASHED_ON_REPAIR));
+ SETMSG(HA_ERR_CRASHED_ON_USAGE, ER_DEFAULT(ER_CRASHED_ON_USAGE));
+ SETMSG(HA_ERR_LOCK_WAIT_TIMEOUT, ER_DEFAULT(ER_LOCK_WAIT_TIMEOUT));
+ SETMSG(HA_ERR_LOCK_TABLE_FULL, ER_DEFAULT(ER_LOCK_TABLE_FULL));
+ SETMSG(HA_ERR_READ_ONLY_TRANSACTION, ER_DEFAULT(ER_READ_ONLY_TRANSACTION));
+ SETMSG(HA_ERR_LOCK_DEADLOCK, ER_DEFAULT(ER_LOCK_DEADLOCK));
+ SETMSG(HA_ERR_CANNOT_ADD_FOREIGN, ER_DEFAULT(ER_CANNOT_ADD_FOREIGN));
+ SETMSG(HA_ERR_NO_REFERENCED_ROW, ER_DEFAULT(ER_NO_REFERENCED_ROW_2));
+ SETMSG(HA_ERR_ROW_IS_REFERENCED, ER_DEFAULT(ER_ROW_IS_REFERENCED_2));
SETMSG(HA_ERR_NO_SAVEPOINT, "No savepoint with that name");
SETMSG(HA_ERR_NON_UNIQUE_BLOCK_SIZE, "Non unique key block size");
SETMSG(HA_ERR_NO_SUCH_TABLE, "No such table: '%.64s'");
- SETMSG(HA_ERR_TABLE_EXIST, ER(ER_TABLE_EXISTS_ERROR));
+ SETMSG(HA_ERR_TABLE_EXIST, ER_DEFAULT(ER_TABLE_EXISTS_ERROR));
SETMSG(HA_ERR_NO_CONNECTION, "Could not connect to storage engine");
- SETMSG(HA_ERR_TABLE_DEF_CHANGED, ER(ER_TABLE_DEF_CHANGED));
+ SETMSG(HA_ERR_TABLE_DEF_CHANGED, ER_DEFAULT(ER_TABLE_DEF_CHANGED));
SETMSG(HA_ERR_FOREIGN_DUPLICATE_KEY, "FK constraint would lead to duplicate key");
- SETMSG(HA_ERR_TABLE_NEEDS_UPGRADE, ER(ER_TABLE_NEEDS_UPGRADE));
- SETMSG(HA_ERR_TABLE_READONLY, ER(ER_OPEN_AS_READONLY));
- SETMSG(HA_ERR_AUTOINC_READ_FAILED, ER(ER_AUTOINC_READ_FAILED));
- SETMSG(HA_ERR_AUTOINC_ERANGE, ER(ER_WARN_DATA_OUT_OF_RANGE));
- SETMSG(HA_ERR_TOO_MANY_CONCURRENT_TRXS, ER(ER_TOO_MANY_CONCURRENT_TRXS));
- SETMSG(HA_ERR_DISK_FULL, ER(ER_DISK_FULL));
- SETMSG(HA_ERR_TABLE_IN_FK_CHECK, "Table being used in foreign key check");
+ SETMSG(HA_ERR_TABLE_NEEDS_UPGRADE, ER_DEFAULT(ER_TABLE_NEEDS_UPGRADE));
+ SETMSG(HA_ERR_TABLE_READONLY, ER_DEFAULT(ER_OPEN_AS_READONLY));
+ SETMSG(HA_ERR_AUTOINC_READ_FAILED, ER_DEFAULT(ER_AUTOINC_READ_FAILED));
+ SETMSG(HA_ERR_AUTOINC_ERANGE, ER_DEFAULT(ER_WARN_DATA_OUT_OF_RANGE));
+ SETMSG(HA_ERR_TOO_MANY_CONCURRENT_TRXS, ER_DEFAULT(ER_TOO_MANY_CONCURRENT_TRXS));
+ SETMSG(HA_ERR_INDEX_COL_TOO_LONG, ER_DEFAULT(ER_INDEX_COLUMN_TOO_LONG));
+ SETMSG(HA_ERR_INDEX_CORRUPT, ER_DEFAULT(ER_INDEX_CORRUPT));
+ SETMSG(HA_ERR_TABLE_IN_FK_CHECK, ER_DEFAULT(ER_TABLE_IN_FK_CHECK));
+ SETMSG(HA_ERR_DISK_FULL, ER_DEFAULT(ER_DISK_FULL));
/* Register the error messages for use with my_error(). */
- return my_error_register(errmsgs, HA_ERR_FIRST, HA_ERR_LAST);
+ return my_error_register(get_handler_errmsgs, HA_ERR_FIRST, HA_ERR_LAST);
}
@@ -368,7 +389,7 @@ static int ha_finish_errors(void)
/* Allocate a pointer array for the error message strings. */
if (! (errmsgs= my_error_unregister(HA_ERR_FIRST, HA_ERR_LAST)))
return 1;
- my_free((uchar*) errmsgs, MYF(0));
+ my_free(errmsgs);
return 0;
}
@@ -414,9 +435,15 @@ int ha_finalize_handlerton(st_plugin_int *plugin)
reuse an array slot. Otherwise the number of uninstall/install
cycles would be limited.
*/
- hton2plugin[hton->slot]= NULL;
+ if (hton->slot != HA_SLOT_UNDEF)
+ {
+ /* Make sure we are not unpluging another plugin */
+ DBUG_ASSERT(hton2plugin[hton->slot] == plugin);
+ DBUG_ASSERT(hton->slot < MAX_HA);
+ hton2plugin[hton->slot]= NULL;
+ }
- my_free((uchar*)hton, MYF(0));
+ my_free(hton);
end:
DBUG_RETURN(0);
@@ -431,6 +458,15 @@ int ha_initialize_handlerton(st_plugin_int *plugin)
hton= (handlerton *)my_malloc(sizeof(handlerton),
MYF(MY_WME | MY_ZEROFILL));
+
+ if (hton == NULL)
+ {
+ sql_print_error("Unable to allocate memory for plugin '%s' handlerton.",
+ plugin->name.str);
+ goto err_no_hton_memory;
+ }
+
+ hton->slot= HA_SLOT_UNDEF;
/* Historical Requirement */
plugin->data= hton; // shortcut for the future
if (plugin->plugin->init && plugin->plugin->init(hton))
@@ -451,6 +487,12 @@ int ha_initialize_handlerton(st_plugin_int *plugin)
{
uint tmp;
ulong fslot;
+
+ DBUG_EXECUTE_IF("unstable_db_type", {
+ static int i= (int) DB_TYPE_FIRST_DYNAMIC;
+ hton->db_type= (enum legacy_db_type)++i;
+ });
+
/* now check the db_type for conflict */
if (hton->db_type <= DB_TYPE_UNKNOWN ||
hton->db_type >= DB_TYPE_DEFAULT ||
@@ -464,8 +506,6 @@ int ha_initialize_handlerton(st_plugin_int *plugin)
if (idx == (int) DB_TYPE_DEFAULT)
{
sql_print_warning("Too many storage engines!");
- my_free(hton, MYF(0));
- plugin->data= 0;
goto err_deinit;
}
if (hton->db_type != DB_TYPE_UNKNOWN)
@@ -473,10 +513,6 @@ int ha_initialize_handlerton(st_plugin_int *plugin)
"Assigning value %d.", plugin->plugin->name, idx);
hton->db_type= (enum legacy_db_type) idx;
}
- installed_htons[hton->db_type]= hton;
- tmp= hton->savepoint_offset;
- hton->savepoint_offset= savepoint_alloc_size;
- savepoint_alloc_size+= tmp;
/*
In case a plugin is uninstalled and re-installed later, it should
@@ -507,7 +543,19 @@ int ha_initialize_handlerton(st_plugin_int *plugin)
savepoint_alloc_size+= tmp;
hton2plugin[hton->slot]=plugin;
if (hton->prepare)
+ {
total_ha_2pc++;
+ if (tc_log && tc_log != get_tc_log_implementation())
+ {
+ total_ha_2pc--;
+ hton->prepare= 0;
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_UNKNOWN_ERROR,
+ "Cannot enable tc-log at run-time. "
+ "XA features of %s are disabled",
+ plugin->name.str);
+ }
+ }
break;
}
/* fall through */
@@ -546,7 +594,8 @@ err_deinit:
(void) plugin->plugin->deinit(NULL);
err:
- my_free((uchar*) hton, MYF(0));
+ my_free(hton);
+err_no_hton_memory:
plugin->data= NULL;
DBUG_RETURN(1);
}
@@ -635,7 +684,6 @@ static my_bool closecon_handlerton(THD *thd, plugin_ref plugin,
return FALSE;
}
-
/**
@note
don't bother to rollback here, it's done already
@@ -645,6 +693,25 @@ void ha_close_connection(THD* thd)
plugin_foreach(thd, closecon_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, 0);
}
+static my_bool kill_handlerton(THD *thd, plugin_ref plugin,
+ void *level)
+{
+ handlerton *hton= plugin_data(plugin, handlerton *);
+
+ if (hton->state == SHOW_OPTION_YES && hton->kill_query &&
+ thd_get_ha_data(thd, hton))
+ hton->kill_query(hton, thd, *(enum thd_kill_levels *) level);
+ return FALSE;
+}
+
+void ha_kill_query(THD* thd, enum thd_kill_levels level)
+{
+ DBUG_ENTER("ha_kill_query");
+ plugin_foreach(thd, kill_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, &level);
+ DBUG_VOID_RETURN;
+}
+
+
/* ========================================================================
======================= TRANSACTIONS ===================================*/
@@ -887,16 +954,16 @@ void ha_close_connection(THD* thd)
a transaction in a given engine is read-write and will not
involve the two-phase commit protocol!
- At the end of a statement, server call
- ha_autocommit_or_rollback() is invoked. This call in turn
- invokes handlerton::prepare() for every involved engine.
- Prepare is followed by a call to handlerton::commit_one_phase()
- If a one-phase commit will suffice, handlerton::prepare() is not
- invoked and the server only calls handlerton::commit_one_phase().
- At statement commit, the statement-related read-write engine
- flag is propagated to the corresponding flag in the normal
- transaction. When the commit is complete, the list of registered
- engines is cleared.
+ At the end of a statement, server call trans_commit_stmt is
+ invoked. This call in turn invokes handlerton::prepare()
+ for every involved engine. Prepare is followed by a call
+ to handlerton::commit_one_phase() If a one-phase commit
+ will suffice, handlerton::prepare() is not invoked and
+ the server only calls handlerton::commit_one_phase().
+ At statement commit, the statement-related read-write
+ engine flag is propagated to the corresponding flag in the
+ normal transaction. When the commit is complete, the list
+ of registered engines is cleared.
Rollback is handled in a similar fashion.
@@ -907,7 +974,7 @@ void ha_close_connection(THD* thd)
do not "register" in thd->transaction lists, and thus do not
modify the transaction state. Besides, each DDL in
MySQL is prefixed with an implicit normal transaction commit
- (a call to end_active_trans()), and thus leaves nothing
+ (a call to trans_commit_implicit()), and thus leaves nothing
to modify.
However, as it has been pointed out with CREATE TABLE .. SELECT,
some DDL statements can start a *new* transaction.
@@ -991,7 +1058,7 @@ int ha_prepare(THD *thd)
THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
Ha_trx_info *ha_info= trans->ha_list;
DBUG_ENTER("ha_prepare");
-#ifdef USING_TRANSACTIONS
+
if (ha_info)
{
for (; ha_info; ha_info= ha_info->next())
@@ -1017,7 +1084,7 @@ int ha_prepare(THD *thd)
}
}
}
-#endif /* USING_TRANSACTIONS */
+
DBUG_RETURN(error);
}
@@ -1134,6 +1201,7 @@ int ha_commit_trans(THD *thd, bool all)
if (thd->in_sub_stmt)
{
+ DBUG_ASSERT(0);
/*
Since we don't support nested statement transactions in 5.0,
we can't commit or rollback stmt transactions while we are inside
@@ -1148,11 +1216,14 @@ int ha_commit_trans(THD *thd, bool all)
bail out with error even before ha_commit_trans() call. To be 100% safe
let us throw error in non-debug builds.
*/
- DBUG_ASSERT(0);
my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
DBUG_RETURN(2);
}
-#ifdef USING_TRANSACTIONS
+
+#ifdef WITH_ARIA_STORAGE_ENGINE
+ ha_maria::implicit_commit(thd, TRUE);
+#endif
+
if (!ha_info)
{
/* Free resources and perform other cleanup even for 'empty' transactions. */
@@ -1170,12 +1241,29 @@ int ha_commit_trans(THD *thd, bool all)
uint rw_ha_count= ha_check_and_coalesce_trx_read_only(thd, ha_info, all);
/* rw_trans is TRUE when we in a transaction changing data */
bool rw_trans= is_real_trans && (rw_ha_count > 0);
+ MDL_request mdl_request;
- if (rw_trans &&
- wait_if_global_read_lock(thd, 0, 0))
+ if (rw_trans)
{
- ha_rollback_trans(thd, all);
- DBUG_RETURN(1);
+ /*
+ Acquire a metadata lock which will ensure that COMMIT is blocked
+ by an active FLUSH TABLES WITH READ LOCK (and vice versa:
+ COMMIT in progress blocks FTWRL).
+
+ We allow the owner of FTWRL to COMMIT; we assume that it knows
+ what it does.
+ */
+ mdl_request.init(MDL_key::COMMIT, "", "", MDL_INTENTION_EXCLUSIVE,
+ MDL_EXPLICIT);
+
+ if (thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout))
+ {
+ ha_rollback_trans(thd, all);
+ DBUG_RETURN(1);
+ }
+
+ DEBUG_SYNC(thd, "ha_commit_trans_after_acquire_commit_lock");
}
if (rw_trans &&
@@ -1190,8 +1278,7 @@ int ha_commit_trans(THD *thd, bool all)
if (trans->no_2pc || (rw_ha_count <= 1))
{
error= ha_commit_one_phase(thd, all);
- DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
- goto end;
+ goto done;
}
need_prepare_ordered= FALSE;
@@ -1224,13 +1311,13 @@ int ha_commit_trans(THD *thd, bool all)
need_prepare_ordered|= (ht->prepare_ordered != NULL);
need_commit_ordered|= (ht->commit_ordered != NULL);
}
+ DEBUG_SYNC(thd, "ha_commit_trans_after_prepare");
DBUG_EXECUTE_IF("crash_commit_after_prepare", DBUG_SUICIDE(););
if (!is_real_trans)
{
error= commit_one_phase_2(thd, all, trans, is_real_trans);
- DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
- goto end;
+ goto done;
}
cookie= tc_log->log_and_order(thd, xid, all, need_prepare_ordered,
@@ -1241,7 +1328,6 @@ int ha_commit_trans(THD *thd, bool all)
DBUG_EXECUTE_IF("crash_commit_after_log", DBUG_SUICIDE(););
error= commit_one_phase_2(thd, all, trans, is_real_trans) ? 2 : 0;
- DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
DBUG_EXECUTE_IF("crash_commit_before_unlog", DBUG_SUICIDE(););
if (tc_log->unlog(cookie, xid))
@@ -1250,7 +1336,9 @@ int ha_commit_trans(THD *thd, bool all)
goto end;
}
+done:
DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
+ RUN_HOOK(transaction, after_commit, (thd, FALSE));
goto end;
/* Come here if error and we need to rollback. */
@@ -1259,38 +1347,59 @@ err:
ha_rollback_trans(thd, all);
end:
- if (rw_trans)
- start_waiting_global_read_lock(thd);
-#endif /* USING_TRANSACTIONS */
+ if (rw_trans && mdl_request.ticket)
+ {
+ /*
+ We do not always immediately release transactional locks
+ after ha_commit_trans() (see uses of ha_enable_transaction()),
+ thus we release the commit blocker lock as soon as it's
+ not needed.
+ */
+ thd->mdl_context.release_lock(mdl_request.ticket);
+ }
DBUG_RETURN(error);
}
/**
@note
This function does not care about global read lock. A caller should.
+
+ @param[in] all Is set in case of explicit commit
+ (COMMIT statement), or implicit commit
+ issued by DDL. Is not set when called
+ at the end of statement, even if
+ autocommit=1.
*/
+
int ha_commit_one_phase(THD *thd, bool all)
{
THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
/*
"real" is a nick name for a transaction for which a commit will
make persistent changes. E.g. a 'stmt' transaction inside a 'all'
- transation is not 'real': even though it's possible to commit it,
+ transaction is not 'real': even though it's possible to commit it,
the changes are not durable as they might be rolled back if the
enclosing 'all' transaction is rolled back.
+ We establish the value of 'is_real_trans' by checking
+ if it's an explicit COMMIT/BEGIN statement, or implicit
+ commit issued by DDL (all == TRUE), or if we're running
+ in autocommit mode (it's only in the autocommit mode
+ ha_commit_one_phase() can be called with an empty
+ transaction.all.ha_list, see why in trans_register_ha()).
*/
bool is_real_trans=all || thd->transaction.all.ha_list == 0;
DBUG_ENTER("ha_commit_one_phase");
- DBUG_RETURN(commit_one_phase_2(thd, all, trans, is_real_trans));
+ int res= commit_one_phase_2(thd, all, trans, is_real_trans);
+ DBUG_RETURN(res);
}
+
static int
commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
{
int error= 0;
Ha_trx_info *ha_info= trans->ha_list, *ha_info_next;
DBUG_ENTER("commit_one_phase_2");
-#ifdef USING_TRANSACTIONS
if (ha_info)
{
for (; ha_info; ha_info= ha_info_next)
@@ -1315,13 +1424,12 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
if (thd->transaction.changed_tables)
query_cache.invalidate(thd, thd->transaction.changed_tables);
#endif
- thd->variables.tx_isolation=thd->session_tx_isolation;
}
}
/* Free resources and perform other cleanup even for 'empty' transactions. */
if (is_real_trans)
thd->transaction.cleanup();
-#endif /* USING_TRANSACTIONS */
+
DBUG_RETURN(error);
}
@@ -1334,9 +1442,15 @@ int ha_rollback_trans(THD *thd, bool all)
/*
"real" is a nick name for a transaction for which a commit will
make persistent changes. E.g. a 'stmt' transaction inside a 'all'
- transation is not 'real': even though it's possible to commit it,
+ transaction is not 'real': even though it's possible to commit it,
the changes are not durable as they might be rolled back if the
enclosing 'all' transaction is rolled back.
+ We establish the value of 'is_real_trans' by checking
+ if it's an explicit COMMIT or BEGIN statement, or implicit
+ commit issued by DDL (in these cases all == TRUE),
+ or if we're running in autocommit mode (it's only in the autocommit mode
+ ha_commit_one_phase() is called with an empty
+ transaction.all.ha_list, see why in trans_register_ha()).
*/
bool is_real_trans=all || thd->transaction.all.ha_list == 0;
DBUG_ENTER("ha_rollback_trans");
@@ -1350,6 +1464,7 @@ int ha_rollback_trans(THD *thd, bool all)
if (thd->in_sub_stmt)
{
+ DBUG_ASSERT(0);
/*
If we are inside stored function or trigger we should not commit or
rollback current statement transaction. See comment in ha_commit_trans()
@@ -1357,11 +1472,10 @@ int ha_rollback_trans(THD *thd, bool all)
*/
if (!all)
DBUG_RETURN(0);
- DBUG_ASSERT(0);
my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
DBUG_RETURN(1);
}
-#ifdef USING_TRANSACTIONS
+
if (ha_info)
{
/* Close all cursors that can not survive ROLLBACK */
@@ -1383,16 +1497,19 @@ int ha_rollback_trans(THD *thd, bool all)
}
trans->ha_list= 0;
trans->no_2pc=0;
- if (is_real_trans && thd->transaction_rollback_request &&
- thd->transaction.xid_state.xa_state != XA_NOTR)
- thd->transaction.xid_state.rm_error= thd->main_da.sql_errno();
- if (all)
- thd->variables.tx_isolation=thd->session_tx_isolation;
}
- /* Always cleanup. Even if there nht==0. There may be savepoints. */
+
+ /*
+ Thanks to possibility of MDL deadlock rollback request can come even if
+ transaction hasn't been started in any transactional storage engine.
+ */
+ if (is_real_trans && thd->transaction_rollback_request &&
+ thd->transaction.xid_state.xa_state != XA_NOTR)
+ thd->transaction.xid_state.rm_error= thd->stmt_da->sql_errno();
+
+ /* Always cleanup. Even if nht==0. There may be savepoints. */
if (is_real_trans)
thd->transaction.cleanup();
-#endif /* USING_TRANSACTIONS */
if (all)
thd->transaction_rollback_request= FALSE;
@@ -1414,41 +1531,7 @@ int ha_rollback_trans(THD *thd, bool all)
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARNING_NOT_COMPLETE_ROLLBACK,
ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
- DBUG_RETURN(error);
-}
-
-/**
- This is used to commit or rollback a single statement depending on
- the value of error.
-
- @note
- Note that if the autocommit is on, then the following call inside
- InnoDB will commit or rollback the whole transaction (= the statement). The
- autocommit mechanism built into InnoDB is based on counting locks, but if
- the user has used LOCK TABLES then that mechanism does not know to do the
- commit.
-*/
-int ha_autocommit_or_rollback(THD *thd, int error)
-{
- DBUG_ENTER("ha_autocommit_or_rollback");
-#ifdef USING_TRANSACTIONS
- if (thd->transaction.stmt.ha_list)
- {
- if (!error)
- {
- if (ha_commit_trans(thd, 0))
- error=1;
- }
- else
- {
- (void) ha_rollback_trans(thd, 0);
- if (thd->transaction_rollback_request && !thd->in_sub_stmt)
- (void) ha_rollback(thd);
- }
-
- thd->variables.tx_isolation=thd->session_tx_isolation;
- }
-#endif
+ RUN_HOOK(transaction, after_rollback, (thd, FALSE));
DBUG_RETURN(error);
}
@@ -1609,7 +1692,7 @@ static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin,
}
// recovery mode
if (info->commit_list ?
- hash_search(info->commit_list, (uchar *)&x, sizeof(x)) != 0 :
+ my_hash_search(info->commit_list, (uchar *)&x, sizeof(x)) != 0 :
tc_heuristic_recover == TC_HEURISTIC_RECOVER_COMMIT)
{
#ifndef DBUG_OFF
@@ -1670,7 +1753,7 @@ int ha_recover(HASH *commit_list)
plugin_foreach(NULL, xarecover_handlerton,
MYSQL_STORAGE_ENGINE_PLUGIN, &info);
- my_free((uchar*)info.list, MYF(0));
+ my_free(info.list);
if (info.found_foreign_xids)
sql_print_warning("Found %d prepared XA transactions",
info.found_foreign_xids);
@@ -1711,12 +1794,12 @@ bool mysql_xa_recover(THD *thd)
field_list.push_back(new Item_int("bqual_length", 0, MY_INT32_NUM_DECIMAL_DIGITS));
field_list.push_back(new Item_empty_string("data",XIDDATASIZE));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
- pthread_mutex_lock(&LOCK_xid_cache);
- while ((xs= (XID_STATE*)hash_element(&xid_cache, i++)))
+ mysql_mutex_lock(&LOCK_xid_cache);
+ while ((xs= (XID_STATE*) my_hash_element(&xid_cache, i++)))
{
if (xs->xa_state==XA_PREPARED)
{
@@ -1728,13 +1811,13 @@ bool mysql_xa_recover(THD *thd)
&my_charset_bin);
if (protocol->write())
{
- pthread_mutex_unlock(&LOCK_xid_cache);
+ mysql_mutex_unlock(&LOCK_xid_cache);
DBUG_RETURN(1);
}
}
}
- pthread_mutex_unlock(&LOCK_xid_cache);
+ mysql_mutex_unlock(&LOCK_xid_cache);
my_eof(thd);
DBUG_RETURN(0);
}
@@ -1841,7 +1924,7 @@ int ha_savepoint(THD *thd, SAVEPOINT *sv)
&thd->transaction.all);
Ha_trx_info *ha_info= trans->ha_list;
DBUG_ENTER("ha_savepoint");
-#ifdef USING_TRANSACTIONS
+
for (; ha_info; ha_info= ha_info->next())
{
int err;
@@ -1865,7 +1948,7 @@ int ha_savepoint(THD *thd, SAVEPOINT *sv)
engines are prepended to the beginning of the list.
*/
sv->ha_list= trans->ha_list;
-#endif /* USING_TRANSACTIONS */
+
DBUG_RETURN(error);
}
@@ -1918,9 +2001,9 @@ int ha_start_consistent_snapshot(THD *thd)
START TRANSACTION WITH CONSISTENT SNAPSHOT and
have a consistent binlog position.
*/
- pthread_mutex_lock(&LOCK_commit_ordered);
+ mysql_mutex_lock(&LOCK_commit_ordered);
plugin_foreach(thd, snapshot_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, &warn);
- pthread_mutex_unlock(&LOCK_commit_ordered);
+ mysql_mutex_unlock(&LOCK_commit_ordered);
/*
Same idea as when one wants to CREATE TABLE in one engine which does not
@@ -2020,23 +2103,28 @@ const char *get_canonical_filename(handler *file, const char *path,
struct Ha_delete_table_error_handler: public Internal_error_handler
{
public:
- virtual bool handle_error(uint sql_errno,
- const char *message,
- MYSQL_ERROR::enum_warning_level level,
- THD *thd);
+ virtual bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl);
char buff[MYSQL_ERRMSG_SIZE];
};
bool
Ha_delete_table_error_handler::
-handle_error(uint sql_errno,
- const char *message,
- MYSQL_ERROR::enum_warning_level level,
- THD *thd)
-{
+handle_condition(THD *,
+ uint,
+ const char*,
+ MYSQL_ERROR::enum_warning_level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl)
+{
+ *cond_hdl= NULL;
/* Grab the error message */
- strmake(buff, message, sizeof(buff)-1);
+ strmake_buf(buff, msg);
return TRUE;
}
@@ -2096,7 +2184,7 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path,
XXX: should we convert *all* errors to warnings here?
What if the error is fatal?
*/
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, error,
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, error,
ha_delete_table_error_handler.buff);
}
delete file;
@@ -2133,7 +2221,6 @@ handler *handler::clone(const char *name, MEM_ROOT *mem_root)
HA_OPEN_IGNORE_IF_LOCKED))
return NULL;
- new_handler->cloned= 1; // Marker for debugging
return new_handler;
}
@@ -2147,11 +2234,14 @@ double handler::keyread_time(uint index, uint ranges, ha_rows rows)
performs a random seek, thus the cost is proportional to the number of
blocks read. This model does not take into account clustered indexes -
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.
*/
- double keys_per_block= (stats.block_size/2.0/
- (table->key_info[index].key_length +
- ref_length) + 1);
- return (rows + keys_per_block - 1)/ keys_per_block;
+ ulong 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);
+ return (rows + keys_per_block-1)/ keys_per_block +
+ len*rows/(stats.block_size+1)/TIME_FOR_COMPARE ;
}
void **handler::ha_data(THD *thd) const
@@ -2165,6 +2255,10 @@ THD *handler::ha_thd(void) const
return (table && table->in_use) ? table->in_use : current_thd;
}
+PSI_table_share *handler::ha_table_share_psi(const TABLE_SHARE *share) const
+{
+ return share->m_psi;
+}
/** @brief
Open database-handler.
@@ -2266,18 +2360,25 @@ 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 (!(error= ha_rnd_init(1)))
{
- while ((error= ha_rnd_next(buf)) == HA_ERR_RECORD_DELETED) ;
- (void) ha_rnd_end();
+ while ((error= ha_rnd_next(buf)) == HA_ERR_RECORD_DELETED)
+ /* skip deleted row */;
+ const int end_error= ha_rnd_end();
+ if (!error)
+ error= end_error;
}
}
else
{
/* Find the first row through the primary key */
- if (!(error = ha_index_init(primary_key, 0)))
+ if (!(error= ha_index_init(primary_key, 0)))
+ {
error= ha_index_first(buf);
- (void) ha_index_end();
+ const int end_error= ha_index_end();
+ if (!error)
+ error= end_error;
+ }
}
DBUG_RETURN(error);
}
@@ -2453,6 +2554,8 @@ int handler::update_auto_increment()
bool append= FALSE;
THD *thd= table->in_use;
struct system_variables *variables= &thd->variables;
+ int result=0, tmp;
+ enum enum_check_fields save_count_cuted_fields;
DBUG_ENTER("handler::update_auto_increment");
/*
@@ -2470,8 +2573,10 @@ int handler::update_auto_increment()
statement (case of INSERT VALUES(null),(3763),(null):
the last NULL needs to insert 3764, not the value of the first NULL plus
1).
+ Ignore negative values.
*/
- adjust_next_insert_id_after_explicit_value(nr);
+ if ((longlong) nr > 0 || (table->next_number_field->flags & UNSIGNED_FLAG))
+ adjust_next_insert_id_after_explicit_value(nr);
insert_id_for_cur_row= 0; // didn't generate anything
DBUG_RETURN(0);
}
@@ -2530,7 +2635,6 @@ int handler::update_auto_increment()
else
nb_desired_values= AUTO_INC_DEFAULT_NB_MAX;
}
- /* This call ignores all its parameters but nr, currently */
get_auto_increment(variables->auto_increment_offset,
variables->auto_increment_increment,
nb_desired_values, &nr,
@@ -2567,37 +2671,33 @@ int handler::update_auto_increment()
}
if (unlikely(nr == ULONGLONG_MAX))
- DBUG_RETURN(HA_ERR_AUTOINC_ERANGE);
+ DBUG_RETURN(HA_ERR_AUTOINC_ERANGE);
- DBUG_PRINT("info",("auto_increment: %lu", (ulong) nr));
+ DBUG_PRINT("info",("auto_increment: %llu",nr));
- if (unlikely(table->next_number_field->store((longlong) nr, TRUE)))
- {
- /*
- first test if the query was aborted due to strict mode constraints
- */
- if (killed_mask_hard(thd->killed) == KILL_BAD_DATA)
- DBUG_RETURN(HA_ERR_AUTOINC_ERANGE);
+ /* 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);
+ thd->count_cuted_fields= save_count_cuted_fields;
+ if (unlikely(tmp)) // Out of range value in store
+ {
/*
- field refused this value (overflow) and truncated it, use the result of
- the truncation (which is going to be inserted); however we try to
- decrease it to honour auto_increment_* variables.
- That will shift the left bound of the reserved interval, we don't
- bother shifting the right bound (anyway any other value from this
- interval will cause a duplicate key).
+ It's better to return an error here than getting a confusing
+ 'duplicate key error' later.
*/
- nr= prev_insert_id(table->next_number_field->val_int(), variables);
- if (unlikely(table->next_number_field->store((longlong) nr, TRUE)))
- nr= table->next_number_field->val_int();
+ result= HA_ERR_AUTOINC_ERANGE;
}
if (append)
{
+ DBUG_PRINT("info",("nb_reserved_values: %llu",nb_reserved_values));
+
auto_inc_interval_for_cur_row.replace(nr, nb_reserved_values,
variables->auto_increment_increment);
auto_inc_intervals_count++;
/* Row-based replication does not need to store intervals in binlog */
- if (mysql_bin_log.is_open() && !thd->current_stmt_binlog_row_based)
+ if (mysql_bin_log.is_open() && !thd->is_current_stmt_binlog_format_row())
thd->auto_inc_intervals_in_cur_stmt_for_binlog.append(auto_inc_interval_for_cur_row.minimum(),
auto_inc_interval_for_cur_row.values(),
variables->auto_increment_increment);
@@ -2611,6 +2711,10 @@ int handler::update_auto_increment()
already set.
*/
insert_id_for_cur_row= nr;
+
+ if (result) // overflow
+ DBUG_RETURN(result);
+
/*
Set next insert id to point to next auto-increment value to be able to
handle multi-row statements.
@@ -2673,7 +2777,16 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment,
table->mark_columns_used_by_index_no_reset(table->s->next_number_index,
table->read_set);
column_bitmaps_signal();
- ha_index_init(table->s->next_number_index, 1);
+
+ if (ha_index_init(table->s->next_number_index, 1))
+ {
+ /* This should never happen, assert in debug, and fail in release build */
+ DBUG_ASSERT(0);
+ (void) extra(HA_EXTRA_NO_KEYREAD);
+ *first_value= ULONGLONG_MAX;
+ return;
+ }
+
if (table->s->next_number_keypart == 0)
{ // Autoincrement at key-start
error=ha_index_last(table->record[1]);
@@ -2704,13 +2817,20 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment,
}
if (error)
- nr=1;
+ {
+ if (error == HA_ERR_END_OF_FILE || error == HA_ERR_KEY_NOT_FOUND)
+ /* No entry found, that's fine */;
+ else
+ print_error(error, MYF(0));
+ nr= 1;
+ }
else
nr= ((ulonglong) table->next_number_field->
val_int_offset(table->s->rec_buff_length)+1);
ha_index_end();
(void) extra(HA_EXTRA_NO_KEYREAD);
*first_value= nr;
+ return;
}
@@ -2734,7 +2854,7 @@ void handler::ha_release_auto_increment()
}
-void handler::print_keydup_error(uint key_nr, const char *msg)
+void handler::print_keydup_error(uint key_nr, const char *msg, myf errflag)
{
/* Write the duplicated key in the error message */
char key[MAX_KEY_LENGTH];
@@ -2744,7 +2864,7 @@ void handler::print_keydup_error(uint key_nr, const char *msg)
{
/* Key is unknown */
str.copy("", 0, system_charset_info);
- my_printf_error(ER_DUP_ENTRY, msg, MYF(0), str.c_ptr(), "*UNKNOWN*");
+ my_printf_error(ER_DUP_ENTRY, msg, errflag, str.c_ptr(), "*UNKNOWN*");
}
else
{
@@ -2757,7 +2877,7 @@ void handler::print_keydup_error(uint key_nr, const char *msg)
str.append(STRING_WITH_LEN("..."));
}
my_printf_error(ER_DUP_ENTRY, msg,
- MYF(0), str.c_ptr_safe(), table->key_info[key_nr].name);
+ errflag, str.c_ptr_safe(), table->key_info[key_nr].name);
}
}
@@ -2798,6 +2918,7 @@ void handler::print_error(int error, myf errflag)
break;
case HA_ERR_KEY_NOT_FOUND:
case HA_ERR_NO_ACTIVE_RECORD:
+ case HA_ERR_RECORD_DELETED:
case HA_ERR_END_OF_FILE:
/*
This errors is not not normally fatal (for example for reads). However
@@ -2824,7 +2945,7 @@ void handler::print_error(int error, myf errflag)
uint key_nr=get_dup_key(error);
if ((int) key_nr >= 0)
{
- print_keydup_error(key_nr, ER(ER_DUP_ENTRY_WITH_KEY_NAME));
+ print_keydup_error(key_nr, ER(ER_DUP_ENTRY_WITH_KEY_NAME), errflag);
DBUG_VOID_RETURN;
}
}
@@ -2932,6 +3053,8 @@ void handler::print_error(int error, myf errflag)
break;
case HA_ERR_LOCK_DEADLOCK:
textno=ER_LOCK_DEADLOCK;
+ /* cannot continue. the statement was already aborted in the engine */
+ SET_FATAL_ERROR;
break;
case HA_ERR_READ_ONLY_TRANSACTION:
textno=ER_READ_ONLY_TRANSACTION;
@@ -2957,7 +3080,7 @@ void handler::print_error(int error, myf errflag)
textno=ER_TABLE_DEF_CHANGED;
break;
case HA_ERR_NO_SUCH_TABLE:
- my_error(ER_NO_SUCH_TABLE, errflag, table_share->db.str,
+ my_error(ER_NO_SUCH_TABLE_IN_ENGINE, errflag, table_share->db.str,
table_share->table_name.str);
DBUG_VOID_RETURN;
case HA_ERR_RBR_LOGGING_FAILED:
@@ -2975,6 +3098,9 @@ void handler::print_error(int error, myf errflag)
case HA_ERR_TABLE_NEEDS_UPGRADE:
textno=ER_TABLE_NEEDS_UPGRADE;
break;
+ case HA_ERR_NO_PARTITION_FOUND:
+ textno=ER_WRONG_PARTITION_NAME;
+ break;
case HA_ERR_TABLE_READONLY:
textno= ER_OPEN_AS_READONLY;
break;
@@ -2982,12 +3108,26 @@ void handler::print_error(int error, myf errflag)
textno= ER_AUTOINC_READ_FAILED;
break;
case HA_ERR_AUTOINC_ERANGE:
- textno= ER_WARN_DATA_OUT_OF_RANGE;
+ textno= error;
+ my_error(textno, errflag, table->next_number_field->field_name,
+ table->in_use->warning_info->current_row_for_warning());
+ DBUG_VOID_RETURN;
break;
case HA_ERR_TOO_MANY_CONCURRENT_TRXS:
textno= ER_TOO_MANY_CONCURRENT_TRXS;
break;
+ case HA_ERR_INDEX_COL_TOO_LONG:
+ textno= ER_INDEX_COLUMN_TOO_LONG;
+ break;
+ case HA_ERR_INDEX_CORRUPT:
+ textno= ER_INDEX_CORRUPT;
+ break;
+ case HA_ERR_UNDO_REC_TOO_BIG:
+ textno= ER_UNDO_RECORD_TOO_BIG;
+ break;
case HA_ERR_TABLE_IN_FK_CHECK:
+ textno= ER_TABLE_IN_FK_CHECK;
+ break;
default:
{
/* The error was "unknown" to this function.
@@ -3007,8 +3147,18 @@ void handler::print_error(int error, myf errflag)
my_error(ER_GET_ERRMSG, errflag, error, str.c_ptr(), engine);
}
}
+ else if (error >= HA_ERR_FIRST && error <= HA_ERR_LAST)
+ {
+ const char* engine= table_type();
+ const char *errmsg= handler_error_messages[error - HA_ERR_FIRST];
+ my_error(ER_GET_ERRMSG, errflag, error, errmsg, engine);
+ SET_FATAL_ERROR;
+ }
else
- my_error(ER_GET_ERRNO,errflag,error);
+ {
+ my_error(ER_GET_ERRNO, errflag,error);
+ /* SET_FATAL_ERROR; */
+ }
DBUG_VOID_RETURN;
}
}
@@ -3026,7 +3176,6 @@ void handler::print_error(int error, myf errflag)
}
}
my_error(textno, errflag, table_share->table_name.str, error);
- DBUG_ASSERT(!fatal_error || !debug_assert_if_crashed_table);
DBUG_VOID_RETURN;
}
@@ -3173,27 +3322,21 @@ static bool update_frm_version(TABLE *table)
strxmov(path, table->s->normalized_path.str, reg_ext, NullS);
- if ((file= my_open(path, O_RDWR|O_BINARY, MYF(MY_WME))) >= 0)
+ if ((file= mysql_file_open(key_file_frm,
+ path, O_RDWR|O_BINARY, MYF(MY_WME))) >= 0)
{
uchar version[4];
- char *key= table->s->table_cache_key.str;
- uint key_length= table->s->table_cache_key.length;
- TABLE *entry;
- HASH_SEARCH_STATE state;
int4store(version, MYSQL_VERSION_ID);
- if ((result= my_pwrite(file,(uchar*) version,4,51L,MYF_RW)))
+ if ((result= mysql_file_pwrite(file, (uchar*) version, 4, 51L, MYF_RW)))
goto err;
- for (entry=(TABLE*) hash_first(&open_cache,(uchar*) key,key_length, &state);
- entry;
- entry= (TABLE*) hash_next(&open_cache,(uchar*) key,key_length, &state))
- entry->s->mysql_version= MYSQL_VERSION_ID;
+ table->s->mysql_version= MYSQL_VERSION_ID;
}
err:
if (file >= 0)
- VOID(my_close(file,MYF(MY_WME)));
+ (void) mysql_file_close(file, MYF(MY_WME));
DBUG_RETURN(result);
}
@@ -3240,7 +3383,7 @@ int handler::delete_table(const char *name)
for (const char **ext=bas_ext(); *ext ; ext++)
{
fn_format(buff, name, "", *ext, MY_UNPACK_FILENAME|MY_APPEND_EXT);
- if (my_delete_with_symlink(buff, MYF(0)))
+ if (mysql_file_delete_with_symlink(key_file_misc, buff, MYF(0)))
{
if (my_errno != ENOENT)
{
@@ -3327,6 +3470,9 @@ int handler::ha_check(THD *thd, HA_CHECK_OPT *check_opt)
}
if ((error= check(thd, check_opt)))
return error;
+ /* Skip updating frm version if not main handler. */
+ if (table->file != this)
+ return error;
return update_frm_version(table);
}
@@ -3373,9 +3519,13 @@ int handler::ha_repair(THD* thd, HA_CHECK_OPT* check_opt)
mark_trx_read_write();
- if ((result= repair(thd, check_opt)))
- return result;
- return update_frm_version(table);
+ result= repair(thd, check_opt);
+ DBUG_ASSERT(result == HA_ADMIN_NOT_IMPLEMENTED ||
+ ha_table_flags() & HA_CAN_REPAIR);
+
+ if (result == HA_ADMIN_OK)
+ result= update_frm_version(table);
+ return result;
}
@@ -3411,47 +3561,32 @@ handler::ha_delete_all_rows()
/**
- Reset auto increment: public interface.
-
- @sa handler::reset_auto_increment()
-*/
-
-int
-handler::ha_reset_auto_increment(ulonglong value)
-{
- mark_trx_read_write();
-
- return reset_auto_increment(value);
-}
-
-
-/**
- Backup table: public interface.
+ Truncate table: public interface.
- @sa handler::backup()
+ @sa handler::truncate()
*/
int
-handler::ha_backup(THD* thd, HA_CHECK_OPT* check_opt)
+handler::ha_truncate()
{
mark_trx_read_write();
- return backup(thd, check_opt);
+ return truncate();
}
/**
- Restore table: public interface.
+ Reset auto increment: public interface.
- @sa handler::restore()
+ @sa handler::reset_auto_increment()
*/
int
-handler::ha_restore(THD* thd, HA_CHECK_OPT* check_opt)
+handler::ha_reset_auto_increment(ulonglong value)
{
mark_trx_read_write();
- return restore(thd, check_opt);
+ return reset_auto_increment(value);
}
@@ -3587,7 +3722,6 @@ int
handler::ha_delete_table(const char *name)
{
mark_trx_read_write();
-
return delete_table(name);
}
@@ -3620,8 +3754,11 @@ int
handler::ha_create(const char *name, TABLE *form, HA_CREATE_INFO *info)
{
mark_trx_read_write();
-
- return create(name, form, info);
+ int error= create(name, form, info);
+ if (!error &&
+ !(info->options & (HA_LEX_CREATE_TMP_TABLE | HA_CREATE_TMP_ALTER)))
+ mysql_audit_create_table(form);
+ return error;
}
@@ -3715,7 +3852,7 @@ int ha_enable_transaction(THD *thd, bool on)
So, let's commit an open transaction (if any) now.
*/
if (!(error= ha_commit_trans(thd, 0)))
- error= end_trans(thd, COMMIT);
+ error= trans_commit_implicit(thd);
}
DBUG_RETURN(error);
}
@@ -3773,7 +3910,7 @@ int handler::index_next_same(uchar *buf, const uchar *key, uint keylen)
}
-void handler::get_dynamic_partition_info(PARTITION_INFO *stat_info,
+void handler::get_dynamic_partition_info(PARTITION_STATS *stat_info,
uint part_id)
{
info(HA_STATUS_CONST | HA_STATUS_TIME | HA_STATUS_VARIABLE |
@@ -3816,10 +3953,10 @@ void handler::update_global_table_stats()
DBUG_ASSERT(table->s && table->s->table_cache_key.str);
- pthread_mutex_lock(&LOCK_global_table_stats);
+ mysql_mutex_lock(&LOCK_global_table_stats);
/* Gets the global table stats, creating one if necessary. */
if (!(table_stats= (TABLE_STATS*)
- hash_search(&global_table_stats,
+ my_hash_search(&global_table_stats,
(uchar*) table->s->table_cache_key.str,
table->s->table_cache_key.length)))
{
@@ -3839,7 +3976,7 @@ void handler::update_global_table_stats()
if (my_hash_insert(&global_table_stats, (uchar*) table_stats))
{
/* Out of memory error is already given */
- my_free(table_stats, 0);
+ my_free(table_stats);
goto end;
}
}
@@ -3851,7 +3988,7 @@ void handler::update_global_table_stats()
1));
rows_read= rows_changed= 0;
end:
- pthread_mutex_unlock(&LOCK_global_table_stats);
+ mysql_mutex_unlock(&LOCK_global_table_stats);
}
@@ -3882,9 +4019,9 @@ void handler::update_global_index_stats()
if (!key_info->cache_name)
continue;
key_length= table->s->table_cache_key.length + key_info->name_length + 1;
- pthread_mutex_lock(&LOCK_global_index_stats);
+ mysql_mutex_lock(&LOCK_global_index_stats);
// Gets the global index stats, creating one if necessary.
- if (!(index_stats= (INDEX_STATS*) hash_search(&global_index_stats,
+ if (!(index_stats= (INDEX_STATS*) my_hash_search(&global_index_stats,
key_info->cache_name,
key_length)))
{
@@ -3897,7 +4034,7 @@ void handler::update_global_index_stats()
index_stats->index_name_length= key_length;
if (my_hash_insert(&global_index_stats, (uchar*) index_stats))
{
- my_free(index_stats, 0);
+ my_free(index_stats);
goto end;
}
}
@@ -3905,7 +4042,7 @@ void handler::update_global_index_stats()
index_stats->rows_read+= index_rows_read[index];
index_rows_read[index]= 0;
end:
- pthread_mutex_unlock(&LOCK_global_index_stats);
+ mysql_mutex_unlock(&LOCK_global_index_stats);
}
}
}
@@ -3947,8 +4084,7 @@ int ha_create_table(THD *thd, const char *path,
name= get_canonical_filename(table.file, share.path.str, name_buff);
error= table.file->ha_create(name, &table, create_info);
-
- VOID(closefrm(&table, 0));
+ (void) closefrm(&table, 0);
if (error)
{
strxmov(name_buff, db, ".", table_name, NullS);
@@ -3999,7 +4135,7 @@ int ha_create_table_from_engine(THD* thd, const char *db, const char *name)
build_table_filename(path, sizeof(path) - 1, db, name, "", 0);
// Save the frm file
error= writefrm(path, frmblob, frmlen);
- my_free(frmblob, MYF(0));
+ my_free(frmblob);
if (error)
DBUG_RETURN(2);
@@ -4019,11 +4155,39 @@ int ha_create_table_from_engine(THD* thd, const char *db, const char *name)
get_canonical_filename(table.file, path, path);
error=table.file->ha_create(path, &table, &create_info);
- VOID(closefrm(&table, 1));
+ (void) closefrm(&table, 1);
DBUG_RETURN(error != 0);
}
+
+/**
+ Try to find a table in a storage engine.
+
+ @param db Normalized table schema name
+ @param name Normalized table name.
+ @param[out] exists Only valid if the function succeeded.
+
+ @retval TRUE An error is found
+ @retval FALSE Success, check *exists
+*/
+
+bool
+ha_check_if_table_exists(THD* thd, const char *db, const char *name,
+ bool *exists)
+{
+ uchar *frmblob= NULL;
+ size_t frmlen;
+ DBUG_ENTER("ha_check_if_table_exists");
+
+ *exists= ! ha_discover(thd, db, name, &frmblob, &frmlen);
+ if (*exists)
+ my_free(frmblob);
+
+ DBUG_RETURN(FALSE);
+}
+
+
void st_ha_check_opt::init()
{
flags= sql_flags= 0;
@@ -4044,19 +4208,20 @@ void st_ha_check_opt::init()
/**
Init a key cache if it has not been initied before.
*/
-int ha_init_key_cache(const char *name, KEY_CACHE *key_cache)
+int ha_init_key_cache(const char *name, KEY_CACHE *key_cache, void *unused
+ __attribute__((unused)))
{
DBUG_ENTER("ha_init_key_cache");
if (!key_cache->key_cache_inited)
{
- pthread_mutex_lock(&LOCK_global_system_variables);
+ mysql_mutex_lock(&LOCK_global_system_variables);
size_t tmp_buff_size= (size_t) key_cache->param_buff_size;
uint tmp_block_size= (uint) key_cache->param_block_size;
- uint division_limit= key_cache->param_division_limit;
- uint age_threshold= key_cache->param_age_threshold;
- uint partitions= key_cache->param_partitions;
- pthread_mutex_unlock(&LOCK_global_system_variables);
+ uint division_limit= (uint)key_cache->param_division_limit;
+ uint age_threshold= (uint)key_cache->param_age_threshold;
+ uint partitions= (uint)key_cache->param_partitions;
+ mysql_mutex_unlock(&LOCK_global_system_variables);
DBUG_RETURN(!init_key_cache(key_cache,
tmp_block_size,
tmp_buff_size,
@@ -4076,12 +4241,12 @@ int ha_resize_key_cache(KEY_CACHE *key_cache)
if (key_cache->key_cache_inited)
{
- pthread_mutex_lock(&LOCK_global_system_variables);
+ mysql_mutex_lock(&LOCK_global_system_variables);
size_t tmp_buff_size= (size_t) key_cache->param_buff_size;
long tmp_block_size= (long) key_cache->param_block_size;
- uint division_limit= key_cache->param_division_limit;
- uint age_threshold= key_cache->param_age_threshold;
- pthread_mutex_unlock(&LOCK_global_system_variables);
+ uint division_limit= (uint)key_cache->param_division_limit;
+ uint age_threshold= (uint)key_cache->param_age_threshold;
+ mysql_mutex_unlock(&LOCK_global_system_variables);
DBUG_RETURN(!resize_key_cache(key_cache, tmp_block_size,
tmp_buff_size,
division_limit, age_threshold));
@@ -4099,10 +4264,10 @@ int ha_change_key_cache_param(KEY_CACHE *key_cache)
if (key_cache->key_cache_inited)
{
- pthread_mutex_lock(&LOCK_global_system_variables);
- uint division_limit= key_cache->param_division_limit;
- uint age_threshold= key_cache->param_age_threshold;
- pthread_mutex_unlock(&LOCK_global_system_variables);
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ uint division_limit= (uint)key_cache->param_division_limit;
+ uint age_threshold= (uint)key_cache->param_age_threshold;
+ mysql_mutex_unlock(&LOCK_global_system_variables);
change_key_cache_param(key_cache, division_limit, age_threshold);
}
DBUG_RETURN(0);
@@ -4118,13 +4283,13 @@ int ha_repartition_key_cache(KEY_CACHE *key_cache)
if (key_cache->key_cache_inited)
{
- pthread_mutex_lock(&LOCK_global_system_variables);
+ mysql_mutex_lock(&LOCK_global_system_variables);
size_t tmp_buff_size= (size_t) key_cache->param_buff_size;
long tmp_block_size= (long) key_cache->param_block_size;
- uint division_limit= key_cache->param_division_limit;
- uint age_threshold= key_cache->param_age_threshold;
- uint partitions= key_cache->param_partitions;
- pthread_mutex_unlock(&LOCK_global_system_variables);
+ uint division_limit= (uint)key_cache->param_division_limit;
+ uint age_threshold= (uint)key_cache->param_age_threshold;
+ uint partitions= (uint)key_cache->param_partitions;
+ mysql_mutex_unlock(&LOCK_global_system_variables);
DBUG_RETURN(!repartition_key_cache(key_cache, tmp_block_size,
tmp_buff_size,
division_limit, age_threshold,
@@ -4135,15 +4300,6 @@ int ha_repartition_key_cache(KEY_CACHE *key_cache)
/**
- Free memory allocated by a key cache.
-*/
-int ha_end_key_cache(KEY_CACHE *key_cache)
-{
- end_key_cache(key_cache, 1); // Can never fail
- return 0;
-}
-
-/**
Move all tables from one key cache to another one.
*/
int ha_change_key_cache(KEY_CACHE *old_key_cache,
@@ -4243,8 +4399,7 @@ ha_find_files(THD *thd,const char *db,const char *path,
int error= 0;
DBUG_ENTER("ha_find_files");
DBUG_PRINT("enter", ("db: '%s' path: '%s' wild: '%s' dir: %d",
- val_or_null(db), val_or_null(path),
- val_or_null(wild), dir));
+ db, path, wild, dir));
st_find_files_args args= {db, path, wild, dir, files};
plugin_foreach(thd, find_files_handlerton,
@@ -4600,7 +4755,9 @@ extern "C" enum icp_result handler_index_cond_check(void* h_arg)
THD *thd= h->table->in_use;
enum icp_result res;
- if (thd_killed(thd))
+ enum thd_kill_levels abort_at= h->has_transactions() ?
+ THD_ABORT_SOFTLY : THD_ABORT_ASAP;
+ if (thd_kill_level(thd) > abort_at)
return ICP_ABORTED_BY_USER;
if (h->end_range && h->compare_key2(h->end_range) > 0)
@@ -4735,7 +4892,7 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat)
field_list.push_back(new Item_empty_string("Name",FN_REFLEN));
field_list.push_back(new Item_empty_string("Status",10));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
return TRUE;
@@ -4753,14 +4910,20 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat)
"", 0, "DISABLED", 8) ? 1 : 0;
}
else
+ {
result= db_type->show_status &&
db_type->show_status(db_type, thd, stat_print, stat) ? 1 : 0;
+ }
}
- if (!result)
+ /*
+ We also check thd->is_error() as Innodb may return 0 even if
+ there was an error.
+ */
+ if (!result && !thd->is_error())
my_eof(thd);
else if (!thd->is_error())
- my_error(ER_GET_ERRNO, MYF(0), 0);
+ my_error(ER_GET_ERRNO, MYF(0), errno);
return result;
}
@@ -4789,9 +4952,9 @@ static bool check_table_binlog_row_based(THD *thd, TABLE *table)
DBUG_ASSERT(table->s->cached_row_logging_check == 0 ||
table->s->cached_row_logging_check == 1);
- return (thd->current_stmt_binlog_row_based &&
+ return (thd->is_current_stmt_binlog_format_row() &&
table->s->cached_row_logging_check &&
- (thd->options & OPTION_BIN_LOG) &&
+ (thd->variables.option_bits & OPTION_BIN_LOG) &&
mysql_bin_log.is_open());
}
@@ -4807,9 +4970,7 @@ static bool check_table_binlog_row_based(THD *thd, TABLE *table)
DESCRIPTION
This function will generate and write table maps for all tables
- that are locked by the thread 'thd'. Either manually locked
- (stored in THD::locked_tables) and automatically locked (stored
- in THD::lock) are considered.
+ that are locked by the thread 'thd'.
RETURN VALUE
0 All OK
@@ -4817,25 +4978,22 @@ static bool check_table_binlog_row_based(THD *thd, TABLE *table)
SEE ALSO
THD::lock
- THD::locked_tables
*/
static int write_locked_table_maps(THD *thd)
{
DBUG_ENTER("write_locked_table_maps");
- DBUG_PRINT("enter", ("thd: 0x%lx thd->lock: 0x%lx thd->locked_tables: 0x%lx "
+ DBUG_PRINT("enter", ("thd: 0x%lx thd->lock: 0x%lx "
"thd->extra_lock: 0x%lx",
- (long) thd, (long) thd->lock,
- (long) thd->locked_tables, (long) thd->extra_lock));
+ (long) thd, (long) thd->lock, (long) thd->extra_lock));
DBUG_PRINT("debug", ("get_binlog_table_maps(): %d", thd->get_binlog_table_maps()));
if (thd->get_binlog_table_maps() == 0)
{
- MYSQL_LOCK *locks[3];
+ MYSQL_LOCK *locks[2];
locks[0]= thd->extra_lock;
locks[1]= thd->lock;
- locks[2]= thd->locked_tables;
my_bool with_annotate= thd->variables.binlog_annotate_row_events &&
thd->query() && thd->query_length();
@@ -4855,7 +5013,21 @@ static int write_locked_table_maps(THD *thd)
if (table->current_lock == F_WRLCK &&
check_table_binlog_row_based(thd, table))
{
- int const has_trans= table->file->has_transactions();
+ /*
+ We need to have a transactional behavior for SQLCOM_CREATE_TABLE
+ (e.g. CREATE TABLE... SELECT * FROM TABLE) in order to keep a
+ compatible behavior with the STMT based replication even when
+ the table is not transactional. In other words, if the operation
+ fails while executing the insert phase nothing is written to the
+ binlog.
+
+ Note that at this point, we check the type of a set of tables to
+ create the table map events. In the function binlog_log_row(),
+ which calls the current function, we check the type of the table
+ of the current row.
+ */
+ bool const has_trans= thd->lex->sql_command == SQLCOM_CREATE_TABLE ||
+ table->file->has_transactions();
int const error= thd->binlog_write_table_map(table, has_trans,
&with_annotate);
/*
@@ -4905,10 +5077,20 @@ static int binlog_log_row(TABLE* table,
{
bitmap_set_all(&cols);
if (likely(!(error= write_locked_table_maps(thd))))
- error= (*log_func)(thd, table, table->file->has_transactions(),
- &cols, table->s->fields,
+ {
+ /*
+ We need to have a transactional behavior for SQLCOM_CREATE_TABLE
+ (i.e. CREATE TABLE... SELECT * FROM TABLE) in order to keep a
+ compatible behavior with the STMT based replication even when
+ the table is not transactional. In other words, if the operation
+ fails while executing the insert phase nothing is written to the
+ binlog.
+ */
+ bool const has_trans= thd->lex->sql_command == SQLCOM_CREATE_TABLE ||
+ table->file->has_transactions();
+ error= (*log_func)(thd, table, has_trans, &cols, table->s->fields,
before_record, after_record);
-
+ }
if (!use_bitbuf)
bitmap_free(&cols);
}
@@ -4926,13 +5108,57 @@ int handler::ha_external_lock(THD *thd, int lock_type)
*/
DBUG_ASSERT(next_insert_id == 0);
+ if (MYSQL_HANDLER_RDLOCK_START_ENABLED() ||
+ MYSQL_HANDLER_WRLOCK_START_ENABLED() ||
+ MYSQL_HANDLER_UNLOCK_START_ENABLED())
+ {
+ if (lock_type == F_RDLCK)
+ {
+ MYSQL_HANDLER_RDLOCK_START(table_share->db.str,
+ table_share->table_name.str);
+ }
+ else if (lock_type == F_WRLCK)
+ {
+ MYSQL_HANDLER_WRLOCK_START(table_share->db.str,
+ table_share->table_name.str);
+ }
+ else if (lock_type == F_UNLCK)
+ {
+ MYSQL_HANDLER_UNLOCK_START(table_share->db.str,
+ table_share->table_name.str);
+ }
+ }
+
/*
We cache the table flags if the locking succeeded. Otherwise, we
keep them as they were when they were fetched in ha_open().
*/
int error= external_lock(thd, lock_type);
+
if (error == 0)
+ {
cached_table_flags= table_flags();
+ if (table_share->tmp_table == NO_TMP_TABLE)
+ mysql_audit_external_lock(thd, table_share, lock_type);
+ }
+
+ if (MYSQL_HANDLER_RDLOCK_DONE_ENABLED() ||
+ MYSQL_HANDLER_WRLOCK_DONE_ENABLED() ||
+ MYSQL_HANDLER_UNLOCK_DONE_ENABLED())
+ {
+ if (lock_type == F_RDLCK)
+ {
+ MYSQL_HANDLER_RDLOCK_DONE(error);
+ }
+ else if (lock_type == F_WRLCK)
+ {
+ MYSQL_HANDLER_WRLOCK_DONE(error);
+ }
+ else if (lock_type == F_UNLCK)
+ {
+ MYSQL_HANDLER_UNLOCK_DONE(error);
+ }
+ }
DBUG_RETURN(error);
}
@@ -4968,11 +5194,17 @@ int handler::ha_write_row(uchar *buf)
int error;
Log_func *log_func= Write_rows_log_event::binlog_row_logging_function;
DBUG_ENTER("handler::ha_write_row");
+ DEBUG_SYNC_C("ha_write_row_start");
+ DBUG_EXECUTE_IF("inject_error_ha_write_row",
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR); );
+ MYSQL_INSERT_ROW_START(table_share->db.str, table_share->table_name.str);
mark_trx_read_write();
increment_statistics(&SSV::ha_write_count);
- if (unlikely(error= write_row(buf)))
+ error= write_row(buf);
+ MYSQL_INSERT_ROW_DONE(error);
+ if (unlikely(error))
DBUG_RETURN(error);
rows_changed++;
if (unlikely(error= binlog_log_row(table, 0, buf, log_func)))
@@ -4993,11 +5225,15 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data)
(and the old record is in record[1]).
*/
DBUG_ASSERT(new_data == table->record[0]);
+ DBUG_ASSERT(old_data == table->record[1]);
+ MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str);
mark_trx_read_write();
increment_statistics(&SSV::ha_update_count);
- if (unlikely(error= update_row(old_data, new_data)))
+ error= update_row(old_data, new_data);
+ MYSQL_UPDATE_ROW_DONE(error);
+ if (unlikely(error))
return error;
rows_changed++;
if (unlikely(error= binlog_log_row(table, old_data, new_data, log_func)))
@@ -5009,11 +5245,21 @@ int handler::ha_delete_row(const uchar *buf)
{
int error;
Log_func *log_func= Delete_rows_log_event::binlog_row_logging_function;
+ /*
+ Normally table->record[0] is used, but sometimes table->record[1] is used.
+ */
+ DBUG_ASSERT(buf == table->record[0] ||
+ buf == table->record[1]);
+ DBUG_EXECUTE_IF("inject_error_ha_delete_row",
+ return HA_ERR_INTERNAL_ERROR; );
+ MYSQL_DELETE_ROW_START(table_share->db.str, table_share->table_name.str);
mark_trx_read_write();
increment_statistics(&SSV::ha_delete_count);
- if (unlikely(error= delete_row(buf)))
+ error= delete_row(buf);
+ MYSQL_DELETE_ROW_DONE(error);
+ if (unlikely(error))
return error;
rows_changed++;
if (unlikely(error= binlog_log_row(table, buf, 0, log_func)))
@@ -5046,6 +5292,10 @@ void signal_log_not_needed(struct handlerton, char *log_file)
DBUG_VOID_RETURN;
}
+void handler::set_lock_type(enum thr_lock_type lock)
+{
+ table->reginfo.lock_type= lock;
+}
#ifdef TRANS_LOG_MGM_EXAMPLE_CODE
/*
@@ -5074,7 +5324,8 @@ int example_of_iterator_using_for_logs_cleanup(handlerton *hton)
{
printf("%s\n", data.filename.str);
if (data.status == HA_LOG_STATUS_FREE &&
- my_delete(data.filename.str, MYF(MY_WME)))
+ mysql_file_delete(INSTRUMENT_ME,
+ data.filename.str, MYF(MY_WME)))
goto err;
}
res= 0;
@@ -5102,7 +5353,7 @@ err:
enum log_status fl_get_log_status(char *log)
{
MY_STAT stat_buff;
- if (my_stat(log, &stat_buff, MYF(0)))
+ if (mysql_file_stat(INSTRUMENT_ME, log, &stat_buff, MYF(0)))
return HA_LOG_STATUS_INUSE;
return HA_LOG_STATUS_NOSUCHLOG;
}
@@ -5134,7 +5385,7 @@ int fl_log_iterator_next(struct handler_iterator *iterator,
void fl_log_iterator_destroy(struct handler_iterator *iterator)
{
- my_free((uchar*)iterator->buffer, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(iterator->buffer);
}
diff --git a/sql/handler.h b/sql/handler.h
index 6a304951cf9..d7b92003083 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -1,6 +1,8 @@
+#ifndef HANDLER_INCLUDED
+#define HANDLER_INCLUDED
/*
Copyright (c) 2000, 2011, Oracle and/or its affiliates.
- Copyright 2009-2011 Monty Program Ab
+ Copyright (c) 2009-2011 Monty Program Ab
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -19,22 +21,21 @@
/* Definitions for parameters to do with handler-routines */
-#ifndef SQL_HANDLER_INCLUDED
-#define SQL_HANDLER_INCLUDED
-
#ifdef USE_PRAGMA_INTERFACE
#pragma interface /* gcc class implementation */
#endif
+#include "sql_const.h"
+#include "mysqld.h" /* server_id */
+#include "sql_plugin.h" /* plugin_ref, st_plugin_int, plugin */
+#include "thr_lock.h" /* thr_lock_type, THR_LOCK_DATA */
+#include "sql_cache.h"
+#include "structs.h" /* SHOW_COMP_OPTION */
+
+#include <my_compare.h>
#include <ft_global.h>
#include <keycache.h>
-#ifndef NO_HASH
-#define NO_HASH /* Not yet implemented */
-#endif
-
-#define USING_TRANSACTIONS
-
#if MAX_KEY > 128
#error MAX_KEY is too large. Values up to 128 are supported.
#endif
@@ -137,12 +138,55 @@
*/
#define HA_BINLOG_ROW_CAPABLE (LL(1) << 34)
#define HA_BINLOG_STMT_CAPABLE (LL(1) << 35)
+/*
+ When a multiple key conflict happens in a REPLACE command mysql
+ expects the conflicts to be reported in the ascending order of
+ key names.
+
+ For e.g.
+
+ CREATE TABLE t1 (a INT, UNIQUE (a), b INT NOT NULL, UNIQUE (b), c INT NOT
+ NULL, INDEX(c));
+
+ REPLACE INTO t1 VALUES (1,1,1),(2,2,2),(2,1,3);
+
+ MySQL expects the conflict with 'a' to be reported before the conflict with
+ 'b'.
+
+ If the underlying storage engine does not report the conflicting keys in
+ ascending order, it causes unexpected errors when the REPLACE command is
+ executed.
+
+ This flag helps the underlying SE to inform the server that the keys are not
+ ordered.
+*/
+#define HA_DUPLICATE_KEY_NOT_IN_ORDER (LL(1) << 36)
+
+/*
+ Engine supports REPAIR TABLE. Used by CHECK TABLE FOR UPGRADE if an
+ incompatible table is detected. If this flag is set, CHECK TABLE FOR UPGRADE
+ will report ER_TABLE_NEEDS_UPGRADE, otherwise ER_TABLE_NEED_REBUILD.
+*/
+#define HA_CAN_REPAIR (LL(1) << 37)
+
/* Has automatic checksums and uses the new checksum format */
-#define HA_HAS_NEW_CHECKSUM (LL(1) << 36)
-#define HA_CAN_VIRTUAL_COLUMNS (LL(1) << 37)
+#define HA_HAS_NEW_CHECKSUM (LL(1) << 38)
+#define HA_CAN_VIRTUAL_COLUMNS (LL(1) << 39)
+#define HA_MRR_CANT_SORT (LL(1) << 40)
+#define HA_RECORD_MUST_BE_CLEAN_ON_WRITE (LL(1) << 41)
-#define HA_MRR_CANT_SORT (LL(1) << 37)
-#define HA_RECORD_MUST_BE_CLEAN_ON_WRITE (LL(1) << 38)
+/*
+ Table condition pushdown must be performed regardless of
+ 'engine_condition_pushdown' setting.
+
+ This flag is aimed at storage engines that come with "special" predicates
+ that can only be evaluated inside the storage engine.
+ For example, when one does
+ select * from sphinx_table where query='{fulltext_query}'
+ then the "query=..." condition must be always pushed down into storage
+ engine.
+*/
+#define HA_MUST_USE_TABLE_CONDITION_PUSHDOWN (LL(1) << 42)
/*
Set of all binlog flags. Currently only contain the capabilities
@@ -174,26 +218,31 @@
bits in alter_table_flags:
*/
/*
- These bits are set if different kinds of indexes can be created
- off-line without re-create of the table (but with a table lock).
+ These bits are set if different kinds of indexes can be created or dropped
+ in-place without re-creating the table using a temporary table.
+ NO_READ_WRITE indicates that the handler needs concurrent reads and writes
+ of table data to be blocked.
+ Partitioning needs both ADD and DROP to be supported by its underlying
+ handlers, due to error handling, see bug#57778.
*/
-#define HA_ONLINE_ADD_INDEX_NO_WRITES (1L << 0) /*add index w/lock*/
-#define HA_ONLINE_DROP_INDEX_NO_WRITES (1L << 1) /*drop index w/lock*/
-#define HA_ONLINE_ADD_UNIQUE_INDEX_NO_WRITES (1L << 2) /*add unique w/lock*/
-#define HA_ONLINE_DROP_UNIQUE_INDEX_NO_WRITES (1L << 3) /*drop uniq. w/lock*/
-#define HA_ONLINE_ADD_PK_INDEX_NO_WRITES (1L << 4) /*add prim. w/lock*/
-#define HA_ONLINE_DROP_PK_INDEX_NO_WRITES (1L << 5) /*drop prim. w/lock*/
+#define HA_INPLACE_ADD_INDEX_NO_READ_WRITE (1L << 0)
+#define HA_INPLACE_DROP_INDEX_NO_READ_WRITE (1L << 1)
+#define HA_INPLACE_ADD_UNIQUE_INDEX_NO_READ_WRITE (1L << 2)
+#define HA_INPLACE_DROP_UNIQUE_INDEX_NO_READ_WRITE (1L << 3)
+#define HA_INPLACE_ADD_PK_INDEX_NO_READ_WRITE (1L << 4)
+#define HA_INPLACE_DROP_PK_INDEX_NO_READ_WRITE (1L << 5)
/*
- These are set if different kinds of indexes can be created on-line
- (without a table lock). If a handler is capable of one or more of
- these, it should also set the corresponding *_NO_WRITES bit(s).
+ These are set if different kinds of indexes can be created or dropped
+ in-place while still allowing concurrent reads (but not writes) of table
+ data. If a handler is capable of one or more of these, it should also set
+ the corresponding *_NO_READ_WRITE bit(s).
*/
-#define HA_ONLINE_ADD_INDEX (1L << 6) /*add index online*/
-#define HA_ONLINE_DROP_INDEX (1L << 7) /*drop index online*/
-#define HA_ONLINE_ADD_UNIQUE_INDEX (1L << 8) /*add unique online*/
-#define HA_ONLINE_DROP_UNIQUE_INDEX (1L << 9) /*drop uniq. online*/
-#define HA_ONLINE_ADD_PK_INDEX (1L << 10)/*add prim. online*/
-#define HA_ONLINE_DROP_PK_INDEX (1L << 11)/*drop prim. online*/
+#define HA_INPLACE_ADD_INDEX_NO_WRITE (1L << 6)
+#define HA_INPLACE_DROP_INDEX_NO_WRITE (1L << 7)
+#define HA_INPLACE_ADD_UNIQUE_INDEX_NO_WRITE (1L << 8)
+#define HA_INPLACE_DROP_UNIQUE_INDEX_NO_WRITE (1L << 9)
+#define HA_INPLACE_ADD_PK_INDEX_NO_WRITE (1L << 10)
+#define HA_INPLACE_DROP_PK_INDEX_NO_WRITE (1L << 11)
/*
HA_PARTITION_FUNCTION_SUPPORTED indicates that the function is
supported at all.
@@ -236,7 +285,14 @@
(yes, the sum is deliberately inaccurate)
TODO remove the limit, use dynarrays
*/
-#define MAX_HA 15
+#define MAX_HA 64
+
+/*
+ Use this instead of 0 as the initial value for the slot number of
+ handlerton, so that we can distinguish uninitialized slot number
+ from slot 0.
+*/
+#define HA_SLOT_UNDEF ((uint)-1)
/*
Parameters for open() (in register form->filestat)
@@ -262,6 +318,7 @@
#define HA_LEX_CREATE_TMP_TABLE 1
#define HA_LEX_CREATE_IF_NOT_EXISTS 2
#define HA_LEX_CREATE_TABLE_LIKE 4
+#define HA_CREATE_TMP_ALTER 8
#define HA_MAX_REC_LENGTH 65535
/* Table caching type */
@@ -297,7 +354,11 @@ enum legacy_db_type
DB_TYPE_MEMCACHE,
DB_TYPE_FALCON,
DB_TYPE_MARIA,
- DB_TYPE_FIRST_DYNAMIC=42,
+ /** Performance schema engine. */
+ DB_TYPE_PERFORMANCE_SCHEMA,
+ DB_TYPE_ARIA=42,
+ DB_TYPE_TOKUDB=43,
+ DB_TYPE_FIRST_DYNAMIC=44,
DB_TYPE_DEFAULT=127 // Must be last
};
/*
@@ -446,11 +507,7 @@ typedef struct xid_t XID;
/* for recover() handlerton call */
#define MIN_XID_LIST_SIZE 128
-#ifdef SAFEMALLOC
-#define MAX_XID_LIST_SIZE 256
-#else
#define MAX_XID_LIST_SIZE (1024*128)
-#endif
/*
These structures are used to pass information from a set of SQL commands
@@ -531,9 +588,55 @@ class st_alter_tablespace : public Sql_alloc
/* The handler for a table type. Will be included in the TABLE structure */
-struct st_table;
-typedef struct st_table TABLE;
-typedef struct st_table_share TABLE_SHARE;
+struct TABLE;
+
+/*
+ Make sure that the order of schema_tables and enum_schema_tables are the same.
+*/
+enum enum_schema_tables
+{
+ SCH_CHARSETS= 0,
+ SCH_CLIENT_STATS,
+ SCH_COLLATIONS,
+ SCH_COLLATION_CHARACTER_SET_APPLICABILITY,
+ SCH_COLUMNS,
+ SCH_COLUMN_PRIVILEGES,
+ SCH_ENGINES,
+ SCH_EVENTS,
+ SCH_FILES,
+ SCH_GLOBAL_STATUS,
+ SCH_GLOBAL_VARIABLES,
+ SCH_INDEX_STATS,
+ SCH_KEY_CACHES,
+ SCH_KEY_COLUMN_USAGE,
+ SCH_OPEN_TABLES,
+ SCH_PARAMETERS,
+ SCH_PARTITIONS,
+ SCH_PLUGINS,
+ SCH_PROCESSLIST,
+ SCH_PROFILES,
+ SCH_REFERENTIAL_CONSTRAINTS,
+ SCH_PROCEDURES,
+ SCH_SCHEMATA,
+ SCH_SCHEMA_PRIVILEGES,
+ SCH_SESSION_STATUS,
+ SCH_SESSION_VARIABLES,
+ SCH_STATISTICS,
+ SCH_STATUS,
+ SCH_TABLES,
+ SCH_TABLESPACES,
+ SCH_TABLE_CONSTRAINTS,
+ SCH_TABLE_NAMES,
+ SCH_TABLE_PRIVILEGES,
+ SCH_TABLE_STATS,
+ SCH_TRIGGERS,
+ SCH_USER_PRIVILEGES,
+ SCH_USER_STATS,
+ SCH_VARIABLES,
+ SCH_VIEWS
+};
+
+struct TABLE_SHARE;
struct st_foreign_key_info;
typedef struct st_foreign_key_info FOREIGN_KEY_INFO;
typedef bool (stat_print_fn)(THD *thd, const char *type, uint type_len,
@@ -656,8 +759,8 @@ enum ha_option_type { HA_OPTION_TYPE_ULL, /* unsigned long long */
HA_xOPTION_STRING(name, ha_index_option_struct, field)
#define HA_IOPTION_ENUM(name, field, values, def) \
HA_xOPTION_ENUM(name, ha_index_option_struct, field, values, def)
-#define HA_IOPTION_BOOL(name, field, values, def) \
- HA_xOPTION_BOOL(name, ha_index_option_struct, field, values, def)
+#define HA_IOPTION_BOOL(name, field, def) \
+ HA_xOPTION_BOOL(name, ha_index_option_struct, field, def)
#define HA_IOPTION_END HA_xOPTION_END
typedef struct st_ha_create_table_option {
@@ -710,6 +813,7 @@ struct handler_iterator {
void *buffer;
};
+class handler;
/*
handlerton is a singleton structure - one instance per storage engine -
to provide access to storage engine functionality that works on the
@@ -764,6 +868,10 @@ struct handlerton
*/
int (*close_connection)(handlerton *hton, THD *thd);
/*
+ Tell handler that query has been killed.
+ */
+ void (*kill_query)(handlerton *hton, THD *thd, enum thd_kill_levels level);
+ /*
sv points to an uninitialized storage area of requested size
(see savepoint_offset description)
*/
@@ -900,9 +1008,9 @@ struct handlerton
uint (*partition_flags)();
uint (*alter_table_flags)(uint flags);
int (*alter_tablespace)(handlerton *hton, THD *thd, st_alter_tablespace *ts_info);
- int (*fill_files_table)(handlerton *hton, THD *thd,
- TABLE_LIST *tables,
- class Item *cond);
+ int (*fill_is_table)(handlerton *hton, THD *thd, TABLE_LIST *tables,
+ class Item *cond,
+ enum enum_schema_tables);
uint32 flags; /* global handler flags */
/*
Those handlerton functions below are properly initialized at handler
@@ -941,6 +1049,7 @@ struct handlerton
const char *wild, bool dir, List<LEX_STRING> *files);
int (*table_exists_in_engine)(handlerton *hton, THD* thd, const char *db,
const char *name);
+
uint32 license; /* Flag for Engine License */
/*
Optional clauses in the CREATE/ALTER TABLE
@@ -962,12 +1071,13 @@ inline LEX_STRING *hton_name(const handlerton *hton)
#define HTON_NO_FLAGS 0
#define HTON_CLOSE_CURSORS_AT_COMMIT (1 << 0)
#define HTON_ALTER_NOT_SUPPORTED (1 << 1) //Engine does not support alter
-#define HTON_CAN_RECREATE (1 << 2) //Delete all is used fro truncate
+#define HTON_CAN_RECREATE (1 << 2) //Delete all is used for truncate
#define HTON_HIDDEN (1 << 3) //Engine does not appear in lists
#define HTON_NOT_USER_SELECTABLE (1 << 5)
#define HTON_TEMPORARY_NOT_SUPPORTED (1 << 6) //Having temporary tables not supported
#define HTON_SUPPORT_LOG_TABLES (1 << 7) //Engine supports log tables
#define HTON_NO_PARTITION (1 << 8) //You can not partition these tables
+#define HTON_EXTENDED_KEYS (1 << 9) //supports extended keys
class Ha_trx_info;
@@ -1008,6 +1118,7 @@ struct THD_TRANS
bool modified_non_trans_table;
void reset() { no_2pc= FALSE; modified_non_trans_table= FALSE; }
+ bool is_empty() const { return ha_list == NULL; }
THD_TRANS() {} /* Remove gcc warning */
};
@@ -1112,9 +1223,6 @@ enum enum_tx_isolation { ISO_READ_UNCOMMITTED, ISO_READ_COMMITTED,
ISO_REPEATABLE_READ, ISO_SERIALIZABLE};
-enum ndb_distribution { ND_KEYHASH= 0, ND_LINHASH= 1 };
-
-
typedef struct {
ulonglong data_file_length;
ulonglong max_data_file_length;
@@ -1126,7 +1234,7 @@ typedef struct {
time_t check_time;
time_t update_time;
ulonglong check_sum;
-} PARTITION_INFO;
+} PARTITION_STATS;
#define UNDEF_NODEGROUP 65535
class Item;
@@ -1169,9 +1277,7 @@ typedef struct st_ha_create_information
uint options; /* OR of HA_CREATE_ options */
uint merge_insert_method;
uint extra_size; /* length of extra data segment */
- /** Transactional or not. Unused; reserved for future versions. */
enum ha_choice transactional;
- bool table_existed; ///< 1 in create if table existed
bool frm_only; ///< 1 if no ha_create_table()
bool varchar; ///< 1 if table has a VARCHAR
enum ha_storage_media storage_media; ///< DEFAULT, DISK or MEMORY
@@ -1189,6 +1295,7 @@ typedef struct st_key_create_information
enum ha_key_alg algorithm;
ulong block_size;
LEX_STRING parser_name;
+ LEX_STRING comment;
} KEY_CREATE_INFO;
@@ -1567,6 +1674,31 @@ uint calculate_key_len(TABLE *, uint, const uchar *, key_part_map);
*/
#define make_prev_keypart_map(N) (((key_part_map)1 << (N)) - 1)
+
+/**
+ Index creation context.
+ Created by handler::add_index() and destroyed by handler::final_add_index().
+ And finally freed at the end of the statement.
+ (Sql_alloc does not free in delete).
+*/
+
+class handler_add_index : public Sql_alloc
+{
+public:
+ /* Table where the indexes are added */
+ TABLE* const table;
+ /* Indexes being created */
+ KEY* const key_info;
+ /* Size of key_info[] */
+ const uint num_of_keys;
+ handler_add_index(TABLE *table_arg, KEY *key_info_arg, uint num_of_keys_arg)
+ : table (table_arg), key_info (key_info_arg), num_of_keys (num_of_keys_arg)
+ {}
+ virtual ~handler_add_index() {}
+};
+
+class Query_cache;
+struct Query_cache_block_table;
/**
The handler class is the interface for dynamically loadable
storage engines. Do not add ifdefs and take care when adding or
@@ -1578,8 +1710,8 @@ class handler :public Sql_alloc
public:
typedef ulonglong Table_flags;
protected:
- struct st_table_share *table_share; /* The table definition */
- struct st_table *table; /* The current open table */
+ TABLE_SHARE *table_share; /* The table definition */
+ TABLE *table; /* The current open table */
Table_flags cached_table_flags; /* Set on init() and open() */
ha_rows estimation_rows_to_insert;
@@ -1610,25 +1742,22 @@ public:
bool eq_range;
bool internal_tmp_table; /* If internal tmp table */
+ uint errkey; /* Last dup key */
+ uint key_used_on_scan;
+ uint active_index;
/*
TRUE <=> the engine guarantees that returned records are within the range
being scanned.
*/
bool in_range_check_pushed_down;
- uint errkey; /* Last dup key */
- uint key_used_on_scan;
- uint active_index;
/** Length of ref (1-8 or the clustered key length) */
uint ref_length;
FT_INFO *ft_handler;
enum {NONE=0, INDEX, RND} inited;
bool locked;
bool implicit_emptied; /* Can be !=0 only if HEAP */
- bool cloned; /* 1 if this was created with clone */
const COND *pushed_cond;
- Item *pushed_idx_cond;
- uint pushed_idx_cond_keyno; /* The index which the above condition is for */
/**
next_insert_id is the next value which should be inserted into the
auto_increment column: in a inserting-multi-row statement (like INSERT
@@ -1657,6 +1786,9 @@ public:
/* One bigger than needed to avoid to test if key == MAX_KEY */
ulonglong index_rows_read[MAX_KEY+1];
+ Item *pushed_idx_cond;
+ uint pushed_idx_cond_keyno; /* The index which the above condition is for */
+
Discrete_interval auto_inc_interval_for_cur_row;
/**
Number of reserved auto-increment intervals. Serves as a heuristic
@@ -1666,26 +1798,40 @@ public:
*/
uint auto_inc_intervals_count;
+ /**
+ Instrumented table associated with this handler.
+ This member should be set to NULL when no instrumentation is in place,
+ so that linking an instrumented/non instrumented server/plugin works.
+ For example:
+ - the server is compiled with the instrumentation.
+ The server expects either NULL or valid pointers in m_psi.
+ - an engine plugin is compiled without instrumentation.
+ The plugin can not leave this pointer uninitialized,
+ or can not leave a trash value on purpose in this pointer,
+ as this would crash the server.
+ */
+ PSI_table *m_psi;
+
handler(handlerton *ht_arg, TABLE_SHARE *share_arg)
:table_share(share_arg), table(0),
estimation_rows_to_insert(0), ht(ht_arg),
- ref(0), end_range(NULL), in_range_check_pushed_down(FALSE),
- key_used_on_scan(MAX_KEY), active_index(MAX_KEY),
+ ref(0), end_range(NULL), key_used_on_scan(MAX_KEY), active_index(MAX_KEY),
+ in_range_check_pushed_down(FALSE),
ref_length(sizeof(my_off_t)),
ft_handler(0), inited(NONE),
- locked(FALSE), implicit_emptied(FALSE), cloned(0),
- pushed_cond(0), pushed_idx_cond(NULL),
+ locked(FALSE), implicit_emptied(0),
+ pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0),
+ pushed_idx_cond(NULL),
pushed_idx_cond_keyno(MAX_KEY),
- next_insert_id(0), insert_id_for_cur_row(0),
- auto_inc_intervals_count(0)
- {
- reset_statistics();
- }
-
+ auto_inc_intervals_count(0),
+ m_psi(NULL)
+ {
+ reset_statistics();
+ }
virtual ~handler(void)
{
DBUG_ASSERT(locked == FALSE);
- /* TODO: DBUG_ASSERT(inited == NONE); */
+ DBUG_ASSERT(inited == NONE);
}
virtual handler *clone(const char *name, MEM_ROOT *mem_root);
/** This is called after create to allow us to set up cached variables */
@@ -1698,6 +1844,7 @@ public:
int ha_open(TABLE *table, const char *name, int mode, uint test_if_locked);
int ha_index_init(uint idx, bool sorted)
{
+ DBUG_EXECUTE_IF("ha_index_init_fail", return HA_ERR_TABLE_DEF_CHANGED;);
int result;
DBUG_ENTER("ha_index_init");
DBUG_ASSERT(inited==NONE);
@@ -1720,8 +1867,19 @@ public:
}
/* This is called after index_init() if we need to do a index scan */
virtual int prepare_index_scan() { return 0; }
+ virtual int prepare_index_key_scan_map(const uchar * key, key_part_map keypart_map)
+ {
+ uint key_len= calculate_key_len(table, active_index, key, keypart_map);
+ return prepare_index_key_scan(key, key_len);
+ }
+ virtual int prepare_index_key_scan( const uchar * key, uint key_len )
+ { return 0; }
+ virtual int prepare_range_scan(const key_range *start_key, const key_range *end_key)
+ { return 0; }
+
int ha_rnd_init(bool scan) __attribute__ ((warn_unused_result))
{
+ DBUG_EXECUTE_IF("ha_rnd_init_fail", return HA_ERR_TABLE_DEF_CHANGED;);
int result;
DBUG_ENTER("ha_rnd_init");
DBUG_ASSERT(inited==NONE || (inited==RND && scan));
@@ -1767,20 +1925,23 @@ public:
int ha_repair(THD* thd, HA_CHECK_OPT* check_opt);
void ha_start_bulk_insert(ha_rows rows)
{
+ DBUG_ENTER("handler::ha_start_bulk_insert");
estimation_rows_to_insert= rows;
start_bulk_insert(rows);
+ DBUG_VOID_RETURN;
}
int ha_end_bulk_insert()
{
+ DBUG_ENTER("handler::ha_end_bulk_insert");
estimation_rows_to_insert= 0;
- return end_bulk_insert();
+ int ret= end_bulk_insert();
+ DBUG_RETURN(ret);
}
int ha_bulk_update_row(const uchar *old_data, uchar *new_data,
uint *dup_key_found);
int ha_delete_all_rows();
+ int ha_truncate();
int ha_reset_auto_increment(ulonglong value);
- int ha_backup(THD* thd, HA_CHECK_OPT* check_opt);
- int ha_restore(THD* thd, HA_CHECK_OPT* check_opt);
int ha_optimize(THD* thd, HA_CHECK_OPT* check_opt);
int ha_analyze(THD* thd, HA_CHECK_OPT* check_opt);
bool ha_check_and_repair(THD *thd);
@@ -1808,7 +1969,7 @@ public:
void adjust_next_insert_id_after_explicit_value(ulonglong nr);
int update_auto_increment();
- void print_keydup_error(uint key_nr, const char *msg);
+ void print_keydup_error(uint key_nr, const char *msg, myf errflag);
virtual void print_error(int error, myf errflag);
virtual bool get_error_message(int error, String *buf);
uint get_dup_key(int error);
@@ -1857,7 +2018,8 @@ public:
if (!error ||
((flags & HA_CHECK_DUP_KEY) &&
(error == HA_ERR_FOUND_DUPP_KEY ||
- error == HA_ERR_FOUND_DUPP_UNIQUE)))
+ error == HA_ERR_FOUND_DUPP_UNIQUE)) ||
+ error == HA_ERR_AUTOINC_ERANGE)
return FALSE;
return TRUE;
}
@@ -2099,7 +2261,7 @@ public:
*/
virtual void position(const uchar *record)=0;
virtual int info(uint)=0; // see my_base.h for full description
- virtual void get_dynamic_partition_info(PARTITION_INFO *stat_info,
+ virtual void get_dynamic_partition_info(PARTITION_STATS *stat_info,
uint part_id);
virtual int extra(enum ha_extra_function operation)
{ return 0; }
@@ -2161,9 +2323,7 @@ public:
{ return HA_ADMIN_NOT_IMPLEMENTED; }
/* end of the list of admin commands */
- virtual int dump(THD* thd, int fd = -1) { return HA_ERR_WRONG_COMMAND; }
virtual int indexes_are_disabled(void) {return 0;}
- virtual int net_read_dump(NET* net) { return HA_ERR_WRONG_COMMAND; }
virtual char *update_table_comment(const char * comment)
{ return (char*) comment;}
virtual void append_create_info(String *packet) {}
@@ -2186,8 +2346,33 @@ public:
/** used in ALTER TABLE; 1 if changing storage engine is allowed */
virtual bool can_switch_engines() { return 1; }
virtual int can_continue_handler_scan() { return 0; }
- /** used in REPLACE; is > 0 if table is referred by a FOREIGN KEY */
- virtual int get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list)
+ /**
+ Get the list of foreign keys in this table.
+
+ @remark Returns the set of foreign keys where this table is the
+ dependent or child table.
+
+ @param thd The thread handle.
+ @param f_key_list[out] The list of foreign keys.
+
+ @return The handler error code or zero for success.
+ */
+ virtual int
+ get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list)
+ { return 0; }
+ /**
+ Get the list of foreign keys referencing this table.
+
+ @remark Returns the set of foreign keys where this table is the
+ referenced or parent table.
+
+ @param thd The thread handle.
+ @param f_key_list[out] The list of foreign keys.
+
+ @return The handler error code or zero for success.
+ */
+ virtual int
+ get_parent_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list)
{ return 0; }
virtual uint referenced_by_foreign_key() { return 0;}
virtual void init_table_handle_for_HANDLER()
@@ -2221,8 +2406,36 @@ public:
virtual ulong index_flags(uint idx, uint part, bool all_parts) const =0;
- virtual int add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys)
+/**
+ First phase of in-place add index.
+ Handlers are supposed to create new indexes here but not make them
+ visible.
+
+ @param table_arg Table to add index to
+ @param key_info Information about new indexes
+ @param num_of_key Number of new indexes
+ @param add[out] Context of handler specific information needed
+ for final_add_index().
+
+ @note This function can be called with less than exclusive metadata
+ lock depending on which flags are listed in alter_table_flags.
+*/
+ virtual int add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys,
+ handler_add_index **add)
{ return (HA_ERR_WRONG_COMMAND); }
+
+/**
+ Second and last phase of in-place add index.
+ Commit or rollback pending new indexes.
+
+ @param add Context of handler specific information from add_index().
+ @param commit If true, commit. If false, rollback index changes.
+
+ @note This function is called with exclusive metadata lock.
+*/
+ virtual int final_add_index(handler_add_index *add, bool commit)
+ { return (HA_ERR_WRONG_COMMAND); }
+
virtual int prepare_drop_index(TABLE *table_arg, uint *key_num,
uint num_of_keys)
{ return (HA_ERR_WRONG_COMMAND); }
@@ -2324,6 +2537,46 @@ public:
return TRUE;
}
+ /*
+ Count tables invisible from all tables list on which current one built
+ (like myisammrg and partitioned tables)
+
+ tables_type mask for the tables should be added herdde
+
+ returns number of such tables
+ */
+
+ virtual uint count_query_cache_dependant_tables(uint8 *tables_type
+ __attribute__((unused)))
+ {
+ return 0;
+ }
+
+ /*
+ register tables invisible from all tables list on which current one built
+ (like myisammrg and partitioned tables).
+
+ @note they should be counted by method above
+
+ cache Query cache pointer
+ block Query cache block to write the table
+ n Number of the table
+
+ @retval FALSE - OK
+ @retval TRUE - Error
+ */
+
+ virtual my_bool
+ register_query_cache_dependant_tables(THD *thd
+ __attribute__((unused)),
+ Query_cache *cache
+ __attribute__((unused)),
+ Query_cache_block_table **block
+ __attribute__((unused)),
+ uint *n __attribute__((unused)))
+ {
+ return FALSE;
+ }
/*
Check if the primary key (if there is one) is a clustered and a
@@ -2388,7 +2641,33 @@ public:
Pops the top if condition stack, if stack is not empty.
*/
virtual void cond_pop() { return; };
+
+ /**
+ Push down an index condition to the handler.
+
+ The server will use this method to push down a condition it wants
+ the handler to evaluate when retrieving records using a specified
+ index. The pushed index condition will only refer to fields from
+ this handler that is contained in the index (but it may also refer
+ to fields in other handlers). Before the handler evaluates the
+ condition it must read the content of the index entry into the
+ record buffer.
+
+ The handler is free to decide if and how much of the condition it
+ will take responsibility for evaluating. Based on this evaluation
+ it should return the part of the condition it will not evaluate.
+ If it decides to evaluate the entire condition it should return
+ NULL. If it decides not to evaluate any part of the condition it
+ should return a pointer to the same condition as given as argument.
+
+ @param keyno the index number to evaluate the condition on
+ @param idx_cond the condition to be evaluated by the handler
+
+ @return The part of the pushed condition that the handler decides
+ not to evaluate
+ */
virtual Item *idx_cond_push(uint keyno, Item* idx_cond) { return idx_cond; }
+
/** Reset information about pushed index conditions */
virtual void cancel_pushed_idx_cond()
{
@@ -2424,9 +2703,11 @@ public:
@retval
TRUE if the engine supports virtual columns
*/
+
virtual bool check_if_supported_virtual_columns(void) { return FALSE;}
TABLE* get_table() { return table; }
+ TABLE_SHARE* get_table_share() { return table_share; }
protected:
/* deprecated, don't use in new engines */
inline void ha_statistic_increment(ulong SSV::*offset) const { }
@@ -2436,6 +2717,39 @@ protected:
THD *ha_thd(void) const;
/**
+ Acquire the instrumented table information from a table share.
+ @param share a table share
+ @return an instrumented table share, or NULL.
+ */
+ PSI_table_share *ha_table_share_psi(const TABLE_SHARE *share) const;
+
+ inline void psi_open()
+ {
+ DBUG_ASSERT(m_psi == NULL);
+ DBUG_ASSERT(table_share != NULL);
+#ifdef HAVE_PSI_INTERFACE
+ if (PSI_server)
+ {
+ PSI_table_share *share_psi= ha_table_share_psi(table_share);
+ if (share_psi)
+ m_psi= PSI_server->open_table(share_psi, this);
+ }
+#endif
+ }
+
+ inline void psi_close()
+ {
+#ifdef HAVE_PSI_INTERFACE
+ if (PSI_server && m_psi)
+ {
+ PSI_server->close_table(m_psi);
+ m_psi= NULL; /* instrumentation handle, invalid after close_table() */
+ }
+#endif
+ DBUG_ASSERT(m_psi == NULL);
+ }
+
+ /**
Default rename_table() and delete_table() rename/delete files with a
given name and extensions from bas_ext().
@@ -2463,7 +2777,7 @@ private:
*/
virtual int open(const char *name, int mode, uint test_if_locked)=0;
- /* Note: ha_index_read_idx_map() may buypass index_init() */
+ /* Note: ha_index_read_idx_map() may bypass index_init() */
virtual int index_init(uint idx, bool sorted) { return 0; }
virtual int index_end() { return 0; }
/**
@@ -2537,7 +2851,10 @@ private:
upon the table.
*/
virtual int repair(THD* thd, HA_CHECK_OPT* check_opt)
- { return HA_ADMIN_NOT_IMPLEMENTED; }
+ {
+ DBUG_ASSERT(!(ha_table_flags() & HA_CAN_REPAIR));
+ return HA_ADMIN_NOT_IMPLEMENTED;
+ }
virtual void start_bulk_insert(ha_rows rows) {}
virtual int end_bulk_insert() { return 0; }
virtual int index_read(uchar * buf, const uchar * key, uint key_len,
@@ -2566,27 +2883,39 @@ private:
This is called to delete all rows in a table
If the handler don't support this, then this function will
return HA_ERR_WRONG_COMMAND and MySQL will delete the rows one
- by one. It should reset auto_increment if
- thd->lex->sql_command == SQLCOM_TRUNCATE.
+ by one.
*/
virtual int delete_all_rows()
{ return (my_errno=HA_ERR_WRONG_COMMAND); }
/**
- Reset the auto-increment counter to the given value, i.e. the next row
- inserted will get the given value. This is called e.g. after TRUNCATE
- is emulated by doing a 'DELETE FROM t'. HA_ERR_WRONG_COMMAND is
- returned by storage engines that don't support this operation.
+ Quickly remove all rows from a table.
+
+ @remark This method is responsible for implementing MySQL's TRUNCATE
+ TABLE statement, which is a DDL operation. As such, a engine
+ can bypass certain integrity checks and in some cases avoid
+ fine-grained locking (e.g. row locks) which would normally be
+ required for a DELETE statement.
+
+ @remark Typically, truncate is not used if it can result in integrity
+ violation. For example, truncate is not used when a foreign
+ key references the table, but it might be used if foreign key
+ checks are disabled.
+
+ @remark Engine is responsible for resetting the auto-increment counter.
+
+ @remark The table is locked in exclusive mode.
*/
- virtual int reset_auto_increment(ulonglong value)
- { return HA_ERR_WRONG_COMMAND; }
- virtual int backup(THD* thd, HA_CHECK_OPT* check_opt)
- { return HA_ADMIN_NOT_IMPLEMENTED; }
+ virtual int truncate()
+ {
+ int error= delete_all_rows();
+ return error ? error : reset_auto_increment(0);
+ }
/**
- Restore assumes .frm file must exist, and that generate_table() has been
- called; It will just copy the data file and run repair.
+ Reset the auto-increment counter to the given value, i.e. the next row
+ inserted will get the given value.
*/
- virtual int restore(THD* thd, HA_CHECK_OPT* check_opt)
- { return HA_ADMIN_NOT_IMPLEMENTED; }
+ virtual int reset_auto_increment(ulonglong value)
+ { return 0; }
virtual int optimize(THD* thd, HA_CHECK_OPT* check_opt)
{ return HA_ADMIN_NOT_IMPLEMENTED; }
virtual int analyze(THD* thd, HA_CHECK_OPT* check_opt)
@@ -2623,12 +2952,14 @@ public:
inline int ha_write_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);
};
#include "multi_range_read.h"
-bool key_uses_partial_cols(TABLE *table, uint keyno);
+bool key_uses_partial_cols(TABLE_SHARE *table, uint keyno);
/* Some extern variables used with handlers */
@@ -2636,13 +2967,9 @@ extern const char *ha_row_type[];
extern MYSQL_PLUGIN_IMPORT const char *tx_isolation_names[];
extern MYSQL_PLUGIN_IMPORT const char *binlog_format_names[];
extern TYPELIB tx_isolation_typelib;
-extern TYPELIB myisam_stats_method_typelib;
+extern const char *myisam_stats_method_names[];
extern ulong total_ha, total_ha_2pc;
- /* Wrapper functions */
-#define ha_commit(thd) (ha_commit_trans((thd), TRUE))
-#define ha_rollback(thd) (ha_rollback_trans((thd), TRUE))
-
/* lookups */
handlerton *ha_default_handlerton(THD *thd);
plugin_ref ha_resolve_by_name(THD *thd, const LEX_STRING *name);
@@ -2685,6 +3012,7 @@ int ha_finalize_handlerton(st_plugin_int *plugin);
TYPELIB *ha_known_exts(void);
int ha_panic(enum ha_panic_function flag);
void ha_close_connection(THD* thd);
+void ha_kill_query(THD* thd, enum thd_kill_levels level);
bool ha_flush_logs(handlerton *db_type);
void ha_drop_database(char* path);
void ha_checkpoint_state(bool disable);
@@ -2700,6 +3028,8 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat);
/* discovery */
int ha_create_table_from_engine(THD* thd, const char *db, const char *name);
+bool ha_check_if_table_exists(THD* thd, const char *db, const char *name,
+ bool *exists);
int ha_discover(THD* thd, const char* dbname, const char* name,
uchar** frmblob, size_t* frmlen);
int ha_find_files(THD *thd,const char *db,const char *path,
@@ -2707,12 +3037,11 @@ int ha_find_files(THD *thd,const char *db,const char *path,
int ha_table_exists_in_engine(THD* thd, const char* db, const char* name);
/* key cache */
-extern "C" int ha_init_key_cache(const char *name, KEY_CACHE *key_cache);
+extern "C" int ha_init_key_cache(const char *name, KEY_CACHE *key_cache, void *);
int ha_resize_key_cache(KEY_CACHE *key_cache);
int ha_change_key_cache_param(KEY_CACHE *key_cache);
int ha_repartition_key_cache(KEY_CACHE *key_cache);
int ha_change_key_cache(KEY_CACHE *old_key_cache, KEY_CACHE *new_key_cache);
-int ha_end_key_cache(KEY_CACHE *key_cache);
/* report to InnoDB that control passes to the client */
int ha_release_temporary_latches(THD *thd);
@@ -2721,13 +3050,12 @@ int ha_release_temporary_latches(THD *thd);
int ha_start_consistent_snapshot(THD *thd);
int ha_commit_or_rollback_by_xid(XID *xid, bool commit);
int ha_commit_one_phase(THD *thd, bool all);
+int ha_commit_trans(THD *thd, bool all);
int ha_rollback_trans(THD *thd, bool all);
int ha_prepare(THD *thd);
int ha_recover(HASH *commit_list);
/* transactions: these functions never call handlerton functions directly */
-int ha_commit_trans(THD *thd, bool all);
-int ha_autocommit_or_rollback(THD *thd, int error);
int ha_enable_transaction(THD *thd, bool on);
/* savepoints */
@@ -2765,4 +3093,12 @@ int ha_binlog_end(THD *thd);
#define ha_binlog_end(a) do {} while (0)
#endif
+const char *get_canonical_filename(handler *file, const char *path,
+ char *tmp_path);
+bool mysql_xa_recover(THD *thd);
+
+inline const char *table_case_name(HA_CREATE_INFO *info, const char *name)
+{
+ return ((lower_case_table_names == 2 && info->alias) ? info->alias : name);
+}
#endif
diff --git a/sql/hash_filo.cc b/sql/hash_filo.cc
index 081eed4e352..7c275ffc617 100644
--- a/sql/hash_filo.cc
+++ b/sql/hash_filo.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2001, 2005, 2006 MySQL AB
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,7 +11,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
@@ -23,7 +23,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
#include "hash_filo.h"
#ifdef __WIN__
diff --git a/sql/hash_filo.h b/sql/hash_filo.h
index 3a9cb46ce40..dab54928a55 100644
--- a/sql/hash_filo.h
+++ b/sql/hash_filo.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000-2003, 2005-2007 MySQL AB
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,7 +11,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
@@ -26,6 +26,10 @@
#pragma interface /* gcc class interface */
#endif
+#include "hash.h" /* my_hash_get_key, my_hash_free_key, HASH */
+#include "m_string.h" /* bzero */
+#include "mysqld.h" /* key_hash_filo_lock */
+
class hash_filo_element
{
hash_filo_element *next_used,*prev_used;
@@ -38,18 +42,18 @@ class hash_filo_element
class hash_filo
{
const uint size, key_offset, key_length;
- const hash_get_key get_key;
- hash_free_key free_element;
+ const my_hash_get_key get_key;
+ my_hash_free_key free_element;
bool init;
CHARSET_INFO *hash_charset;
hash_filo_element *first_link,*last_link;
public:
- pthread_mutex_t lock;
+ mysql_mutex_t lock;
HASH cache;
hash_filo(uint size_arg, uint key_offset_arg , uint key_length_arg,
- hash_get_key get_key_arg, hash_free_key free_element_arg,
+ my_hash_get_key get_key_arg, my_hash_free_key free_element_arg,
CHARSET_INFO *hash_charset_arg)
:size(size_arg), key_offset(key_offset_arg), key_length(key_length_arg),
get_key(get_key_arg), free_element(free_element_arg),init(0),
@@ -63,8 +67,8 @@ public:
if (init)
{
if (cache.array.buffer) /* Avoid problems with thread library */
- (void) hash_free(&cache);
- pthread_mutex_destroy(&lock);
+ (void) my_hash_free(&cache);
+ mysql_mutex_destroy(&lock);
}
}
void clear(bool locked=0)
@@ -72,22 +76,22 @@ public:
if (!init)
{
init=1;
- (void) pthread_mutex_init(&lock,MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_hash_filo_lock, &lock, MY_MUTEX_INIT_FAST);
}
if (!locked)
- (void) pthread_mutex_lock(&lock);
- (void) hash_free(&cache);
- (void) hash_init(&cache,hash_charset,size,key_offset,
+ mysql_mutex_lock(&lock);
+ (void) my_hash_free(&cache);
+ (void) my_hash_init(&cache,hash_charset,size,key_offset,
key_length, get_key, free_element,0);
if (!locked)
- (void) pthread_mutex_unlock(&lock);
+ mysql_mutex_unlock(&lock);
first_link=last_link=0;
}
hash_filo_element *search(uchar* key, size_t length)
{
hash_filo_element *entry=(hash_filo_element*)
- hash_search(&cache,(uchar*) key,length);
+ my_hash_search(&cache,(uchar*) key,length);
if (entry)
{ // Found; link it first
if (entry != first_link)
@@ -113,7 +117,7 @@ public:
{
hash_filo_element *tmp=last_link;
last_link=last_link->prev_used;
- hash_delete(&cache,(uchar*) tmp);
+ my_hash_delete(&cache,(uchar*) tmp);
}
if (my_hash_insert(&cache,(uchar*) entry))
{
diff --git a/sql/hostname.cc b/sql/hostname.cc
index a321496c14d..f9a6f4b3fcf 100644
--- a/sql/hostname.cc
+++ b/sql/hostname.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 2011, 2014, Monty Program Ab
+ Copyright (c) 2011, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,15 +19,24 @@
@file
@brief
- Get hostname for an IP.
+ Get hostname for an IP address.
- Hostnames are checked with reverse name lookup and
- checked that they doesn't resemble an ip.
+ Hostnames are checked with reverse name lookup and checked that they
+ doesn't resemble an IP address.
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "hostname.h"
+#include "my_global.h"
+#ifndef __WIN__
+#include <netdb.h> // getservbyname, servent
+#endif
#include "hash_filo.h"
#include <m_ctype.h>
+#include "log.h" // sql_print_warning,
+ // sql_print_information
+#include "violite.h" // vio_getnameinfo,
+ // vio_get_normalized_ip_string
#ifdef __cplusplus
extern "C" { // Because of SCO 3.2V4.2
#endif
@@ -35,24 +44,54 @@ extern "C" { // Because of SCO 3.2V4.2
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
-#include <netdb.h>
#include <sys/utsname.h>
#endif // __WIN__
#ifdef __cplusplus
}
#endif
+/*
+ HOST_ENTRY_KEY_SIZE -- size of IP address string in the hash cache.
+*/
+
+#define HOST_ENTRY_KEY_SIZE INET6_ADDRSTRLEN
+
+/**
+ An entry in the hostname hash table cache.
+
+ Host name cache does two things:
+ - caches host names to save DNS look ups;
+ - counts connect errors from IP.
-class host_entry :public hash_filo_element
+ Host name can be NULL (that means DNS look up failed), but connect errors
+ still are counted.
+*/
+
+class Host_entry :public hash_filo_element
{
public:
- char ip[sizeof(((struct in_addr *) 0)->s_addr)];
- uint errors;
- char *hostname;
+ /**
+ Client IP address. This is the key used with the hash table.
+
+ The client IP address is always expressed in IPv6, even when the
+ network IPv6 stack is not present.
+
+ This IP address is never used to connect to a socket.
+ */
+ char ip_key[HOST_ENTRY_KEY_SIZE];
+
+ /**
+ Number of errors during handshake phase from the IP address.
+ */
+ uint connect_errors;
+
+ /**
+ One of the host names for the IP address. May be NULL.
+ */
+ const char *hostname;
};
static hash_filo *hostname_cache;
-static pthread_mutex_t LOCK_hostname;
void hostname_cache_refresh()
{
@@ -61,228 +100,514 @@ void hostname_cache_refresh()
bool hostname_cache_init()
{
- host_entry tmp;
- uint offset= (uint) ((char*) (&tmp.ip) - (char*) &tmp);
- if (!(hostname_cache=new hash_filo(HOST_CACHE_SIZE, offset,
- sizeof(struct in_addr),NULL,
- (hash_free_key) free,
- &my_charset_bin)))
+ Host_entry tmp;
+ uint key_offset= (uint) ((char*) (&tmp.ip_key) - (char*) &tmp);
+
+ if (!(hostname_cache= new hash_filo(HOST_CACHE_SIZE,
+ key_offset, HOST_ENTRY_KEY_SIZE,
+ NULL, (my_hash_free_key) free,
+ &my_charset_bin)))
return 1;
+
hostname_cache->clear();
- (void) pthread_mutex_init(&LOCK_hostname,MY_MUTEX_INIT_SLOW);
+
return 0;
}
void hostname_cache_free()
{
- if (hostname_cache)
- {
- (void) pthread_mutex_destroy(&LOCK_hostname);
- delete hostname_cache;
- hostname_cache= 0;
- }
+ delete hostname_cache;
+ hostname_cache= NULL;
}
+static void prepare_hostname_cache_key(const char *ip_string,
+ char *ip_key)
+{
+ int ip_string_length= strlen(ip_string);
+ DBUG_ASSERT(ip_string_length < HOST_ENTRY_KEY_SIZE);
+
+ memset(ip_key, 0, HOST_ENTRY_KEY_SIZE);
+ memcpy(ip_key, ip_string, ip_string_length);
+}
-static void add_hostname(struct in_addr *in,const char *name)
+static inline Host_entry *hostname_cache_search(const char *ip_key)
{
- if (!(specialflag & SPECIAL_NO_HOST_CACHE))
+ return (Host_entry *) hostname_cache->search((uchar *) ip_key, 0);
+}
+
+static bool add_hostname_impl(const char *ip_key, const char *hostname)
+{
+ if (hostname_cache_search(ip_key))
+ return FALSE;
+
+ size_t hostname_size= hostname ? strlen(hostname) + 1 : 0;
+
+ Host_entry *entry= (Host_entry *) malloc(sizeof (Host_entry) + hostname_size);
+
+ if (!entry)
+ return TRUE;
+
+ char *hostname_copy;
+
+ memcpy(&entry->ip_key, ip_key, HOST_ENTRY_KEY_SIZE);
+
+ if (hostname_size)
{
- VOID(pthread_mutex_lock(&hostname_cache->lock));
- host_entry *entry;
- if (!(entry=(host_entry*) hostname_cache->search((uchar*) &in->s_addr,0)))
- {
- uint length=name ? (uint) strlen(name) : 0;
+ hostname_copy= (char *) (entry + 1);
+ memcpy(hostname_copy, hostname, hostname_size);
- if ((entry=(host_entry*) malloc(sizeof(host_entry)+length+1)))
- {
- char *new_name;
- memcpy_fixed(&entry->ip, &in->s_addr, sizeof(in->s_addr));
- if (length)
- memcpy(new_name= (char *) (entry+1), name, length+1);
- else
- new_name=0;
- entry->hostname=new_name;
- entry->errors=0;
- (void) hostname_cache->add(entry);
- }
- }
- VOID(pthread_mutex_unlock(&hostname_cache->lock));
+ DBUG_PRINT("info", ("Adding '%s' -> '%s' to the hostname cache...'",
+ (const char *) ip_key,
+ (const char *) hostname_copy));
}
+ else
+ {
+ hostname_copy= NULL;
+
+ DBUG_PRINT("info", ("Adding '%s' -> NULL to the hostname cache...'",
+ (const char *) ip_key));
+ }
+
+ entry->hostname= hostname_copy;
+ entry->connect_errors= 0;
+
+ return hostname_cache->add(entry);
}
+static bool add_hostname(const char *ip_key, const char *hostname)
+{
+ if (specialflag & SPECIAL_NO_HOST_CACHE)
+ return FALSE;
+
+ mysql_mutex_lock(&hostname_cache->lock);
+
+ bool err_status= add_hostname_impl(ip_key, hostname);
+
+ mysql_mutex_unlock(&hostname_cache->lock);
-inline void add_wrong_ip(struct in_addr *in)
+ return err_status;
+}
+
+void inc_host_errors(const char *ip_string)
{
- add_hostname(in,NullS);
+ if (!ip_string)
+ return;
+
+ char ip_key[HOST_ENTRY_KEY_SIZE];
+ prepare_hostname_cache_key(ip_string, ip_key);
+
+ mysql_mutex_lock(&hostname_cache->lock);
+
+ Host_entry *entry= hostname_cache_search(ip_key);
+
+ if (entry)
+ entry->connect_errors++;
+
+ mysql_mutex_unlock(&hostname_cache->lock);
}
-void inc_host_errors(struct in_addr *in)
+
+void reset_host_errors(const char *ip_string)
{
- VOID(pthread_mutex_lock(&hostname_cache->lock));
- host_entry *entry;
- if ((entry=(host_entry*) hostname_cache->search((uchar*) &in->s_addr,0)))
- entry->errors++;
- VOID(pthread_mutex_unlock(&hostname_cache->lock));
+ if (!ip_string)
+ return;
+
+ char ip_key[HOST_ENTRY_KEY_SIZE];
+ prepare_hostname_cache_key(ip_string, ip_key);
+
+ mysql_mutex_lock(&hostname_cache->lock);
+
+ Host_entry *entry= hostname_cache_search(ip_key);
+
+ if (entry)
+ entry->connect_errors= 0;
+
+ mysql_mutex_unlock(&hostname_cache->lock);
+}
+
+
+static inline bool is_ip_loopback(const struct sockaddr *ip)
+{
+ switch (ip->sa_family) {
+ case AF_INET:
+ {
+ /* Check for IPv4 127.0.0.1. */
+ struct in_addr *ip4= &((struct sockaddr_in *) ip)->sin_addr;
+ return ntohl(ip4->s_addr) == INADDR_LOOPBACK;
+ }
+
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ {
+ /* Check for IPv6 ::1. */
+ struct in6_addr *ip6= &((struct sockaddr_in6 *) ip)->sin6_addr;
+ return IN6_IS_ADDR_LOOPBACK(ip6);
+ }
+#endif /* HAVE_IPV6 */
+
+ default:
+ return FALSE;
+ }
}
-void reset_host_errors(struct in_addr *in)
+static inline bool is_hostname_valid(const char *hostname)
{
- VOID(pthread_mutex_lock(&hostname_cache->lock));
- host_entry *entry;
- if ((entry=(host_entry*) hostname_cache->search((uchar*) &in->s_addr,0)))
- entry->errors=0;
- VOID(pthread_mutex_unlock(&hostname_cache->lock));
+ /*
+ A hostname is invalid if it starts with a number followed by a dot
+ (IPv4 address).
+ */
+
+ if (!my_isdigit(&my_charset_latin1, hostname[0]))
+ return TRUE;
+
+ const char *p= hostname + 1;
+
+ while (my_isdigit(&my_charset_latin1, *p))
+ ++p;
+
+ return *p != '.';
}
-/* Deal with systems that don't defined INADDR_LOOPBACK */
-#ifndef INADDR_LOOPBACK
-#define INADDR_LOOPBACK 0x7f000001UL
-#endif
+/**
+ Resolve IP-address to host name.
+
+ This function does the following things:
+ - resolves IP-address;
+ - employs Forward Confirmed Reverse DNS technique to validate IP-address;
+ - returns host name if IP-address is validated;
+ - set value to out-variable connect_errors -- this variable represents the
+ number of connection errors from the specified IP-address.
+
+ NOTE: connect_errors are counted (are supported) only for the clients
+ where IP-address can be resolved and FCrDNS check is passed.
+
+ @param [in] ip_storage IP address (sockaddr). Must be set.
+ @param [in] ip_string IP address (string). Must be set.
+ @param [out] hostname
+ @param [out] connect_errors
+
+ @return Error status
+ @retval FALSE Success
+ @retval TRUE Error
+
+ The function does not set/report MySQL server error in case of failure.
+ It's caller's responsibility to handle failures of this function
+ properly.
+*/
-char * ip_to_hostname(struct in_addr *in, uint *errors)
+bool ip_to_hostname(struct sockaddr_storage *ip_storage,
+ const char *ip_string,
+ char **hostname, uint *connect_errors)
{
- uint i;
- host_entry *entry;
+ const struct sockaddr *ip= (const sockaddr *) ip_storage;
+ int err_code;
+ bool err_status;
+
DBUG_ENTER("ip_to_hostname");
- *errors=0;
+ DBUG_PRINT("info", ("IP address: '%s'; family: %d.",
+ (const char *) ip_string,
+ (int) ip->sa_family));
- /* We always treat the loopback address as "localhost". */
- if (in->s_addr == htonl(INADDR_LOOPBACK)) // is expanded inline by gcc
- DBUG_RETURN((char *)my_localhost);
+ /* Check if we have loopback address (127.0.0.1 or ::1). */
+
+ if (is_ip_loopback(ip))
+ {
+ DBUG_PRINT("info", ("Loopback address detected."));
+
+ *connect_errors= 0; /* Do not count connect errors from localhost. */
+ *hostname= (char *) my_localhost;
+
+ DBUG_RETURN(FALSE);
+ }
+
+ /* Prepare host name cache key. */
+
+ char ip_key[HOST_ENTRY_KEY_SIZE];
+ prepare_hostname_cache_key(ip_string, ip_key);
+
+ /* Check first if we have host name in the cache. */
- /* Check first if we have name in cache */
if (!(specialflag & SPECIAL_NO_HOST_CACHE))
{
- VOID(pthread_mutex_lock(&hostname_cache->lock));
- if ((entry=(host_entry*) hostname_cache->search((uchar*) &in->s_addr,0)))
+ mysql_mutex_lock(&hostname_cache->lock);
+
+ Host_entry *entry= hostname_cache_search(ip_key);
+
+ if (entry)
+ {
+ *connect_errors= entry->connect_errors;
+ *hostname= NULL;
+
+ if (entry->hostname)
+ *hostname= my_strdup(entry->hostname, MYF(0));
+
+ DBUG_PRINT("info",("IP (%s) has been found in the cache. "
+ "Hostname: '%s'; connect_errors: %d",
+ (const char *) ip_key,
+ (const char *) (*hostname? *hostname : "null"),
+ (int) *connect_errors));
+
+ mysql_mutex_unlock(&hostname_cache->lock);
+
+ DBUG_RETURN(FALSE);
+ }
+
+ mysql_mutex_unlock(&hostname_cache->lock);
+ }
+
+ /*
+ Resolve host name. Return an error if a host name can not be resolved
+ (instead of returning the numeric form of the host name).
+ */
+
+ char hostname_buffer[NI_MAXHOST];
+
+ DBUG_PRINT("info", ("Resolving '%s'...", (const char *) ip_key));
+
+ err_code= vio_getnameinfo(ip, hostname_buffer, NI_MAXHOST, NULL, 0,
+ NI_NAMEREQD);
+
+ /* BEGIN : DEBUG */
+ DBUG_EXECUTE_IF("addr_fake_ipv4",
+ {
+ strcpy(hostname_buffer, "santa.claus.ipv4.example.com");
+ err_code= 0;
+ };);
+ /* END : DEBUG */
+
+ if (err_code)
+ {
+ // NOTE: gai_strerror() returns a string ending by a dot.
+
+ DBUG_PRINT("error", ("IP address '%s' could not be resolved: %s",
+ (const char *) ip_key,
+ (const char *) gai_strerror(err_code)));
+
+ sql_print_warning("IP address '%s' could not be resolved: %s",
+ (const char *) ip_key,
+ (const char *) gai_strerror(err_code));
+
+ if (vio_is_no_name_error(err_code))
{
- char *name;
- if (!entry->hostname)
- name=0; // Don't allow connection
- else
- name=my_strdup(entry->hostname,MYF(0));
- *errors= entry->errors;
- VOID(pthread_mutex_unlock(&hostname_cache->lock));
- DBUG_RETURN(name);
+ /*
+ The no-name error means that there is no reverse address mapping
+ for the IP address. A host name can not be resolved.
+
+ If it is not the no-name error, we should not cache the hostname
+ (or rather its absence), because the failure might be transient.
+ */
+
+ add_hostname(ip_key, NULL);
+
+ *hostname= NULL;
+ *connect_errors= 0; /* New IP added to the cache. */
}
- VOID(pthread_mutex_unlock(&hostname_cache->lock));
+
+ DBUG_RETURN(FALSE);
}
- struct hostent *hp, *check;
- char *name;
- LINT_INIT(check);
-#if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST)
- char buff[GETHOSTBYADDR_BUFF_SIZE],buff2[GETHOSTBYNAME_BUFF_SIZE];
- int tmp_errno;
- struct hostent tmp_hostent, tmp_hostent2;
-#ifdef HAVE_valgrind
- bzero(buff,sizeof(buff)); // Bug in purify
-#endif
- if (!(hp=gethostbyaddr_r((char*) in,sizeof(*in),
- AF_INET,
- &tmp_hostent,buff,sizeof(buff),&tmp_errno)))
+ DBUG_PRINT("info", ("IP '%s' resolved to '%s'.",
+ (const char *) ip_key,
+ (const char *) hostname_buffer));
+
+ /*
+ Validate hostname: the server does not accept host names, which
+ resemble IP addresses.
+
+ The thing is that theoretically, a host name can be in a form of IPv4
+ address (123.example.org, or 1.2 or even 1.2.3.4). We have to deny such
+ host names because ACL-systems is not designed to work with them.
+
+ For example, it is possible to specify a host name mask (like
+ 192.168.1.%) for an ACL rule. Then, if IPv4-like hostnames are allowed,
+ there is a security hole: instead of allowing access for
+ 192.168.1.0/255 network (which was assumed by the user), the access
+ will be allowed for host names like 192.168.1.example.org.
+ */
+
+ if (!is_hostname_valid(hostname_buffer))
{
- DBUG_PRINT("error",("gethostbyaddr_r returned %d",tmp_errno));
- DBUG_RETURN(0);
+ DBUG_PRINT("error", ("IP address '%s' has been resolved "
+ "to the host name '%s', which resembles "
+ "IPv4-address itself.",
+ (const char *) ip_key,
+ (const char *) hostname_buffer));
+
+ sql_print_warning("IP address '%s' has been resolved "
+ "to the host name '%s', which resembles "
+ "IPv4-address itself.",
+ (const char *) ip_key,
+ (const char *) hostname_buffer);
+
+ err_status= add_hostname(ip_key, NULL);
+
+ *hostname= NULL;
+ *connect_errors= 0; /* New IP added to the cache. */
+
+ DBUG_RETURN(err_status);
}
- if (!(check=my_gethostbyname_r(hp->h_name,&tmp_hostent2,buff2,sizeof(buff2),
- &tmp_errno)))
+
+ /*
+ To avoid crashing the server in DBUG_EXECUTE_IF,
+ Define a variable which depicts state of addr_info_list.
+ */
+ bool free_addr_info_list= false;
+
+ /* Get IP-addresses for the resolved host name (FCrDNS technique). */
+
+ struct addrinfo hints;
+ struct addrinfo *addr_info_list;
+
+ memset(&hints, 0, sizeof (struct addrinfo));
+ hints.ai_flags= AI_PASSIVE;
+ hints.ai_socktype= SOCK_STREAM;
+ hints.ai_family= AF_UNSPEC;
+
+ DBUG_PRINT("info", ("Getting IP addresses for hostname '%s'...",
+ (const char *) hostname_buffer));
+
+ err_code= getaddrinfo(hostname_buffer, NULL, &hints, &addr_info_list);
+ if (err_code == 0)
+ free_addr_info_list= true;
+
+ /* BEGIN : DEBUG */
+ DBUG_EXECUTE_IF("addr_fake_ipv4",
+ {
+ if (free_addr_info_list)
+ freeaddrinfo(addr_info_list);
+
+ struct sockaddr_in *debug_addr;
+ static struct sockaddr_in debug_sock_addr[2];
+ static struct addrinfo debug_addr_info[2];
+ /* 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");
+
+ /* 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");
+
+ debug_addr_info[0].ai_addr= (struct sockaddr*) & debug_sock_addr[0];
+ debug_addr_info[0].ai_addrlen= sizeof (struct sockaddr_in);
+ debug_addr_info[0].ai_next= & debug_addr_info[1];
+
+ debug_addr_info[1].ai_addr= (struct sockaddr*) & debug_sock_addr[1];
+ debug_addr_info[1].ai_addrlen= sizeof (struct sockaddr_in);
+ debug_addr_info[1].ai_next= NULL;
+
+ addr_info_list= & debug_addr_info[0];
+ err_code= 0;
+ free_addr_info_list= false;
+ };);
+
+ /* END : DEBUG */
+
+ if (err_code == EAI_NONAME)
{
- DBUG_PRINT("error",("gethostbyname_r returned %d",tmp_errno));
/*
- Don't cache responses when the DSN server is down, as otherwise
+ Don't cache responses when the DNS server is down, as otherwise
transient DNS failure may leave any number of clients (those
that attempted to connect during the outage) unable to connect
indefinitely.
*/
- if (tmp_errno == HOST_NOT_FOUND || tmp_errno == NO_DATA)
- add_wrong_ip(in);
- my_gethostbyname_r_free();
- DBUG_RETURN(0);
- }
- if (!hp->h_name[0])
- {
- DBUG_PRINT("error",("Got an empty hostname"));
- add_wrong_ip(in);
- my_gethostbyname_r_free();
- DBUG_RETURN(0); // Don't allow empty hostnames
- }
- if (!(name=my_strdup(hp->h_name,MYF(0))))
- {
- my_gethostbyname_r_free();
- DBUG_RETURN(0); // out of memory
- }
- my_gethostbyname_r_free();
-#else
- DBUG_EXECUTE_IF("addr_fake_ipv4",
- {
- const char* fake_host= "santa.claus.ipv4.example.com";
- name=my_strdup(fake_host, MYF(0));
- add_hostname(in,name);
- DBUG_RETURN(name);
- };);
+ err_status= add_hostname(ip_key, NULL);
- VOID(pthread_mutex_lock(&LOCK_hostname));
- if (!(hp=gethostbyaddr((char*) in,sizeof(*in), AF_INET)))
- {
- VOID(pthread_mutex_unlock(&LOCK_hostname));
- DBUG_PRINT("error",("gethostbyaddr returned %d",errno));
+ *hostname= NULL;
+ *connect_errors= 0; /* New IP added to the cache. */
- if (errno == HOST_NOT_FOUND || errno == NO_DATA)
- goto add_wrong_ip_and_return;
- /* Failure, don't cache responce */
- DBUG_RETURN(0);
- }
- if (!hp->h_name[0]) // Don't allow empty hostnames
- {
- VOID(pthread_mutex_unlock(&LOCK_hostname));
- DBUG_PRINT("error",("Got an empty hostname"));
- goto add_wrong_ip_and_return;
- }
- if (!(name=my_strdup(hp->h_name,MYF(0))))
- {
- VOID(pthread_mutex_unlock(&LOCK_hostname));
- DBUG_RETURN(0); // out of memory
+ DBUG_RETURN(err_status);
}
- check=gethostbyname(name);
- VOID(pthread_mutex_unlock(&LOCK_hostname));
- if (!check)
+ else if (err_code)
{
- DBUG_PRINT("error",("gethostbyname returned %d",errno));
- my_free(name,MYF(0));
- DBUG_RETURN(0);
+ DBUG_PRINT("error", ("getaddrinfo() failed with error code %d.", err_code));
+ DBUG_RETURN(TRUE);
}
-#endif
- /* Don't accept hostnames that starts with digits because they may be
- false ip:s */
- if (my_isdigit(&my_charset_latin1,name[0]))
+ /* Check that getaddrinfo() returned the used IP (FCrDNS technique). */
+
+ DBUG_PRINT("info", ("The following IP addresses found for '%s':",
+ (const char *) hostname_buffer));
+
+ for (struct addrinfo *addr_info= addr_info_list;
+ addr_info; addr_info= addr_info->ai_next)
{
- char *pos;
- for (pos= name+1 ; my_isdigit(&my_charset_latin1,*pos); pos++) ;
- if (*pos == '.')
+ char ip_buffer[HOST_ENTRY_KEY_SIZE];
+
+ {
+ err_status=
+ vio_get_normalized_ip_string(addr_info->ai_addr, addr_info->ai_addrlen,
+ ip_buffer, sizeof (ip_buffer));
+ DBUG_ASSERT(!err_status);
+ }
+
+ DBUG_PRINT("info", (" - '%s'", (const char *) ip_buffer));
+
+ if (strcmp(ip_key, ip_buffer) == 0)
{
- DBUG_PRINT("error",("mysqld doesn't accept hostnames that starts with a number followed by a '.'"));
- my_free(name,MYF(0));
- goto add_wrong_ip_and_return;
+ /* Copy host name string to be stored in the cache. */
+
+ *hostname= my_strdup(hostname_buffer, MYF(0));
+
+ if (!*hostname)
+ {
+ DBUG_PRINT("error", ("Out of memory."));
+
+ if (free_addr_info_list)
+ freeaddrinfo(addr_info_list);
+ DBUG_RETURN(TRUE);
+ }
+
+ break;
}
}
- /* Check that 'gethostbyname' returned the used ip */
- for (i=0; check->h_addr_list[i]; i++)
+ /* Log resolved IP-addresses if no match was found. */
+
+ if (!*hostname)
{
- if (*(uint32*)(check->h_addr_list)[i] == in->s_addr)
+ sql_print_information("Hostname '%s' does not resolve to '%s'.",
+ (const char *) hostname_buffer,
+ (const char *) ip_key);
+ sql_print_information("Hostname '%s' has the following IP addresses:",
+ (const char *) hostname_buffer);
+
+ for (struct addrinfo *addr_info= addr_info_list;
+ addr_info; addr_info= addr_info->ai_next)
{
- add_hostname(in,name);
- DBUG_RETURN(name);
+ char ip_buffer[HOST_ENTRY_KEY_SIZE];
+
+ err_status=
+ vio_get_normalized_ip_string(addr_info->ai_addr, addr_info->ai_addrlen,
+ ip_buffer, sizeof (ip_buffer));
+ DBUG_ASSERT(!err_status);
+
+ sql_print_information(" - %s\n", (const char *) ip_buffer);
}
}
- DBUG_PRINT("error",("Couldn't verify hostname with gethostbyname"));
- my_free(name,MYF(0));
-add_wrong_ip_and_return:
- add_wrong_ip(in);
- DBUG_RETURN(0);
+ /* Free the result of getaddrinfo(). */
+
+ if (free_addr_info_list)
+ freeaddrinfo(addr_info_list);
+
+ /* Add an entry for the IP to the cache. */
+
+ if (*hostname)
+ {
+ err_status= add_hostname(ip_key, *hostname);
+ *connect_errors= 0;
+ }
+ else
+ {
+ DBUG_PRINT("error",("Couldn't verify hostname with getaddrinfo()."));
+
+ err_status= add_hostname(ip_key, NULL);
+ *hostname= NULL;
+ *connect_errors= 0;
+ }
+
+ DBUG_RETURN(err_status);
}
diff --git a/sql/hostname.h b/sql/hostname.h
new file mode 100644
index 00000000000..6e9535c2947
--- /dev/null
+++ b/sql/hostname.h
@@ -0,0 +1,30 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef HOSTNAME_INCLUDED
+#define HOSTNAME_INCLUDED
+
+#include "my_global.h" /* uint */
+
+bool ip_to_hostname(struct sockaddr_storage *ip_storage,
+ const char *ip_string,
+ char **hostname, uint *connect_errors);
+void inc_host_errors(const char *ip_string);
+void reset_host_errors(const char *ip_string);
+bool hostname_cache_init();
+void hostname_cache_free();
+void hostname_cache_refresh(void);
+
+#endif /* HOSTNAME_INCLUDED */
diff --git a/sql/init.cc b/sql/init.cc
index b66f7e31fde..86915b7aa01 100644
--- a/sql/init.cc
+++ b/sql/init.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000-2008 MySQL AB
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,7 +11,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@@ -21,14 +21,19 @@
Init and dummy functions for interface with unireg
*/
-#include "mysql_priv.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
#include <m_ctype.h>
void unireg_init(ulong options)
{
DBUG_ENTER("unireg_init");
- MYSYS_PROGRAM_DONT_USE_CURSES();
+ error_handler_hook = my_message_stderr;
abort_loop=0;
my_disable_async_io=1; /* aioread is only in shared library */
@@ -40,7 +45,7 @@ void unireg_init(ulong options)
my_abort_hook=unireg_abort; /* Abort with close of databases */
#endif
- VOID(strmov(reg_ext,".frm"));
+ (void) strmov(reg_ext,".frm");
reg_ext_length= 4;
specialflag=SPECIAL_SAME_DB_NAME | options; /* Set options from argv */
DBUG_VOID_RETURN;
diff --git a/sql/init.h b/sql/init.h
new file mode 100644
index 00000000000..88cd8e6e178
--- /dev/null
+++ b/sql/init.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef INIT_INCLUDED
+#define INIT_INCLUDED
+
+#include "my_global.h" /* ulong */
+
+void unireg_init(ulong options);
+void unireg_end(void) __attribute__((noreturn));
+
+#endif /* INIT_INCLUDED */
diff --git a/sql/innodb_priv.h b/sql/innodb_priv.h
new file mode 100644
index 00000000000..33ba7b0f5b3
--- /dev/null
+++ b/sql/innodb_priv.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+#ifndef INNODB_PRIV_INCLUDED
+#define INNODB_PRIV_INCLUDED
+
+/** @file Declaring server-internal functions that are used by InnoDB. */
+
+#include <sql_priv.h>
+
+class THD;
+
+int get_quote_char_for_identifier(THD *thd, const char *name, uint length);
+bool schema_table_store_record(THD *thd, TABLE *table);
+void localtime_to_TIME(MYSQL_TIME *to, struct tm *from);
+bool check_global_access(THD *thd, ulong want_access, bool no_errors=false);
+uint strconvert(CHARSET_INFO *from_cs, const char *from,
+ CHARSET_INFO *to_cs, char *to, uint to_length,
+ uint *errors);
+void sql_print_error(const char *format, ...);
+
+
+
+#endif /* INNODB_PRIV_INCLUDED */
diff --git a/sql/item.cc b/sql/item.cc
index 3a1913bd4a1..cfc5f67dc8e 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -1,5 +1,6 @@
/*
- Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2000, 2014, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2014, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,14 +13,15 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_priv.h"
+#include "unireg.h" // REQUIRED: for other includes
#include <mysql.h>
#include <m_ctype.h>
#include "my_dir.h"
@@ -27,6 +29,20 @@
#include "sp_head.h"
#include "sql_trigger.h"
#include "sql_select.h"
+#include "sql_show.h" // append_identifier
+#include "sql_view.h" // VIEW_ANY_SQL
+#include "sql_time.h" // str_to_datetime_with_warn,
+ // make_truncated_value_warning
+#include "sql_acl.h" // get_column_grant,
+ // SELECT_ACL, UPDATE_ACL,
+ // INSERT_ACL,
+ // check_grant_column
+#include "sql_base.h" // enum_resolution_type,
+ // REPORT_EXCEPT_NOT_FOUND,
+ // find_item_in_list,
+ // RESOLVED_AGAINST_ALIAS, ...
+#include "log_event.h" // append_query_string
+#include "sql_expression_cache.h"
const String my_null_string("NULL", 4, default_charset_info);
@@ -218,6 +234,35 @@ bool Item::val_bool()
}
+/*
+ For the items which don't have its own fast val_str_ascii()
+ implementation we provide a generic slower version,
+ which converts from the Item character set to ASCII.
+ For better performance conversion happens only in
+ case of a "tricky" Item character set (e.g. UCS2).
+ Normally conversion does not happen.
+*/
+String *Item::val_str_ascii(String *str)
+{
+ if (!(collation.collation->state & MY_CS_NONASCII))
+ return val_str(str);
+
+ DBUG_ASSERT(str != &str_value);
+
+ uint errors;
+ String *res= val_str(&str_value);
+ if (!res)
+ return 0;
+
+ if ((null_value= str->copy(res->ptr(), res->length(),
+ collation.collation, &my_charset_latin1,
+ &errors)))
+ return 0;
+
+ return str;
+}
+
+
String *Item::val_str(String *str, String *converter, CHARSET_INFO *cs)
{
String *res= val_str(str);
@@ -241,7 +286,7 @@ String *Item::val_string_from_real(String *str)
double nr= val_real();
if (null_value)
return 0; /* purecov: inspected */
- str->set_real(nr,decimals, &my_charset_bin);
+ str->set_real(nr,decimals, &my_charset_numeric);
return str;
}
@@ -251,7 +296,7 @@ String *Item::val_string_from_int(String *str)
longlong nr= val_int();
if (null_value)
return 0;
- str->set_int(nr, unsigned_flag, &my_charset_bin);
+ str->set_int(nr, unsigned_flag, &my_charset_numeric);
return str;
}
@@ -278,7 +323,7 @@ String *Item::val_string_from_date(String *str)
return (String *) 0;
}
str->length(my_TIME_to_str(&ltime, const_cast<char*>(str->ptr()), decimals));
- str->set_charset(&my_charset_bin);
+ str->set_charset(&my_charset_numeric);
return str;
}
@@ -314,10 +359,11 @@ my_decimal *Item::val_decimal_from_string(my_decimal *decimal_value)
res->ptr(), res->length(), res->charset(),
decimal_value) & E_DEC_BAD_NUM)
{
+ ErrConvString err(res);
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), "DECIMAL",
- str_value.c_ptr());
+ err.ptr());
}
return decimal_value;
}
@@ -407,7 +453,9 @@ int Item::save_time_in_field(Field *field)
int Item::save_date_in_field(Field *field)
{
MYSQL_TIME ltime;
- if (get_date(&ltime, 0))
+ if (get_date(&ltime, (current_thd->variables.sql_mode &
+ (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE |
+ MODE_INVALID_DATES))))
return set_field_to_null_with_conversions(field, 0);
field->set_notnull();
return field->store_time_dec(&ltime, decimals);
@@ -520,10 +568,11 @@ uint Item::decimal_precision() const
if ((restype == DECIMAL_RESULT) || (restype == INT_RESULT))
{
uint prec=
- my_decimal_length_to_precision(max_length, decimals, unsigned_flag);
+ my_decimal_length_to_precision(max_char_length(), decimals,
+ unsigned_flag);
return min(prec, DECIMAL_MAX_PRECISION);
}
- return min(max_length, DECIMAL_MAX_PRECISION);
+ return min(max_char_length(), DECIMAL_MAX_PRECISION);
}
@@ -554,9 +603,9 @@ uint Item::temporal_precision(enum_field_types type)
DBUG_ASSERT(fixed);
if ((tmp= val_str(&buf)) &&
(type == MYSQL_TYPE_TIME ?
- str_to_time(tmp->ptr(), tmp->length(),
+ str_to_time(tmp->charset(), tmp->ptr(), tmp->length(),
&ltime, TIME_TIME_ONLY, &was_cut) :
- str_to_datetime(tmp->ptr(), tmp->length(),
+ str_to_datetime(tmp->charset(), tmp->ptr(), tmp->length(),
&ltime, TIME_FUZZY_DATES, &was_cut)) >
MYSQL_TIMESTAMP_ERROR)
return min(ms_to_precision(ltime.second_part), TIME_SECOND_PART_DIGITS);
@@ -686,7 +735,7 @@ Item_result Item::cmp_type() const
return TIME_RESULT;
};
DBUG_ASSERT(0);
- return (Item_result)-1;
+ return IMPOSSIBLE_RESULT;
}
/**
@@ -720,7 +769,7 @@ Item_result Item::cmp_type() const
Item* Item::transform(Item_transformer transformer, uchar *arg)
{
- DBUG_ASSERT(!current_thd->is_stmt_prepare());
+ DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare());
return (this->*transformer)(arg);
}
@@ -805,20 +854,20 @@ Item_ident::Item_ident(THD *thd, Item_ident *item)
void Item_ident::cleanup()
{
DBUG_ENTER("Item_ident::cleanup");
-#ifdef CANT_BE_USED_AS_MEMORY_IS_FREED
- db_name ? db_name : "(null)",
- orig_db_name ? orig_db_name : "(null)",
- table_name ? table_name : "(null)",
- orig_table_name ? orig_table_name : "(null)",
- field_name ? field_name : "(null)",
- orig_field_name ? orig_field_name : "(null)"));
-#endif
+ bool was_fixed= fixed;
Item::cleanup();
db_name= orig_db_name;
table_name= orig_table_name;
field_name= orig_field_name;
/* Store if this Item was depended */
- can_be_depended= test(depended_from);
+ if (was_fixed)
+ {
+ /*
+ We can trust that depended_from set correctly only if this item
+ was fixed
+ */
+ can_be_depended= test(depended_from);
+ }
DBUG_VOID_RETURN;
}
@@ -1099,15 +1148,40 @@ Item *Item::safe_charset_converter(CHARSET_INFO *tocs)
*/
Item *Item_num::safe_charset_converter(CHARSET_INFO *tocs)
{
+ /*
+ Item_num returns pure ASCII result,
+ so conversion is needed only in case of "tricky" character
+ sets like UCS2. If tocs is not "tricky", return the item itself.
+ */
+ if (!(tocs->state & MY_CS_NONASCII))
+ return this;
+
Item_string *conv;
- char buf[64];
- String *s, tmp(buf, sizeof(buf), &my_charset_bin);
- s= val_str(&tmp);
- if ((conv= new Item_string(s->ptr(), s->length(), s->charset())))
+ uint conv_errors;
+ char buf[64], buf2[64];
+ String tmp(buf, sizeof(buf), &my_charset_bin);
+ String cstr(buf2, sizeof(buf2), &my_charset_bin);
+ String *ostr= val_str(&tmp);
+ char *ptr;
+ cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors);
+ if (conv_errors || !(conv= new Item_string(cstr.ptr(), cstr.length(),
+ cstr.charset(),
+ collation.derivation)))
{
- conv->str_value.copy();
- conv->str_value.mark_as_const();
+ /*
+ Safe conversion is not possible (or EOM).
+ We could not convert a string into the requested character set
+ without data loss. The target charset does not cover all the
+ characters from the string. Operation cannot be done correctly.
+ */
+ return NULL;
}
+ if (!(ptr= current_thd->strmake(cstr.ptr(), cstr.length())))
+ return NULL;
+ conv->str_value.set(ptr, cstr.length(), cstr.charset());
+ /* Ensure that no one is going to change the result string */
+ conv->str_value.mark_as_const();
+ conv->fix_char_length(max_char_length());
return conv;
}
@@ -1170,7 +1244,7 @@ Item *Item_param::safe_charset_converter(CHARSET_INFO *tocs)
cnvitem->max_length= cnvitem->str_value.numchars() * tocs->mbmaxlen;
return cnvitem;
}
- return NULL;
+ return Item::safe_charset_converter(tocs);
}
@@ -1219,7 +1293,7 @@ bool Item_string::eq(const Item *item, bool binary_cmp) const
As a extra convenience the time structure is reset on error or NULL values!
*/
-bool Item::get_date(MYSQL_TIME *ltime,uint fuzzydate)
+bool Item::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
{
if (field_type() == MYSQL_TYPE_TIME)
fuzzydate|= TIME_TIME_ONLY;
@@ -1268,7 +1342,7 @@ bool Item::get_date(MYSQL_TIME *ltime,uint fuzzydate)
char buff[40];
String tmp(buff,sizeof(buff), &my_charset_bin),*res;
if (!(res=val_str(&tmp)) ||
- str_to_datetime_with_warn(res->ptr(), res->length(),
+ str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR)
goto err;
break;
@@ -1325,11 +1399,13 @@ int Item::save_in_field_no_warnings(Field *field, bool no_conversions)
THD *thd= table->in_use;
enum_check_fields tmp= thd->count_cuted_fields;
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
- ulong sql_mode= thd->variables.sql_mode;
+ ulonglong sql_mode= thd->variables.sql_mode;
thd->variables.sql_mode&= ~(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE);
thd->variables.sql_mode|= MODE_INVALID_DATES;
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
+
res= save_in_field(field, no_conversions);
+
thd->count_cuted_fields= tmp;
dbug_tmp_restore_column_map(table->write_set, old_map);
thd->variables.sql_mode= sql_mode;
@@ -1453,7 +1529,9 @@ Item_splocal::Item_splocal(const LEX_STRING &sp_var_name,
enum_field_types sp_var_type,
uint pos_in_q, uint len_in_q)
:Item_sp_variable(sp_var_name.str, sp_var_name.length),
- m_var_idx(sp_var_idx), pos_in_query(pos_in_q), len_in_query(len_in_q)
+ m_var_idx(sp_var_idx),
+ limit_clause_param(FALSE),
+ pos_in_query(pos_in_q), len_in_query(len_in_q)
{
maybe_null= TRUE;
@@ -1547,7 +1625,7 @@ void Item_case_expr::print(String *str, enum_query_type)
{
if (str->reserve(MAX_INT_WIDTH + sizeof("case_expr@")))
return; /* purecov: inspected */
- VOID(str->append(STRING_WITH_LEN("case_expr@")));
+ (void) str->append(STRING_WITH_LEN("case_expr@"));
str->qs_append(m_case_expr_id);
}
@@ -1803,7 +1881,12 @@ left_is_superset(DTCollation *left, DTCollation *right)
if (left->collation->state & MY_CS_UNICODE &&
(left->derivation < right->derivation ||
(left->derivation == right->derivation &&
- !(right->collation->state & MY_CS_UNICODE))))
+ (!(right->collation->state & MY_CS_UNICODE) ||
+ /* The code below makes 4-byte utf8 a superset over 3-byte utf8 */
+ (left->collation->state & MY_CS_UNICODE_SUPPLEMENT &&
+ !(right->collation->state & MY_CS_UNICODE_SUPPLEMENT) &&
+ left->collation->mbmaxlen > right->collation->mbmaxlen &&
+ left->collation->mbminlen == right->collation->mbminlen)))))
return TRUE;
/* Allow convert from ASCII */
if (right->repertoire == MY_REPERTOIRE_ASCII &&
@@ -1991,7 +2074,7 @@ bool agg_item_collations(DTCollation &c, const char *fname,
bool unknown_cs= 0;
c.set(av[0]->collation);
- for (i= 1, arg= &av[item_sep]; i < count; i++, arg++)
+ for (i= 1, arg= &av[item_sep]; i < count; i++, arg+= item_sep)
{
if (c.aggregate((*arg)->collation, flags))
{
@@ -2019,6 +2102,12 @@ bool agg_item_collations(DTCollation &c, const char *fname,
my_coll_agg_error(av, count, fname, item_sep);
return TRUE;
}
+
+ /* If all arguments where numbers, reset to @@collation_connection */
+ if (flags & MY_COLL_ALLOW_NUMERIC_CONV &&
+ c.derivation == DERIVATION_NUMERIC)
+ c.set(Item::default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_NUMERIC);
+
return FALSE;
}
@@ -2050,25 +2139,45 @@ bool agg_item_set_converter(DTCollation &coll, const char *fname,
}
THD *thd= current_thd;
- Query_arena *arena, backup;
bool res= FALSE;
uint i;
+
/*
In case we're in statement prepare, create conversion item
in its memory: it will be reused on each execute.
*/
- arena= thd->is_stmt_prepare() ? thd->activate_stmt_arena_if_needed(&backup)
- : NULL;
+ Query_arena backup;
+ Query_arena *arena= thd->stmt_arena->is_stmt_prepare() ?
+ thd->activate_stmt_arena_if_needed(&backup) :
+ NULL;
for (i= 0, arg= args; i < nargs; i++, arg+= item_sep)
{
Item* conv;
uint32 dummy_offset;
- if (!String::needs_conversion(0, (*arg)->collation.collation,
+ if (!String::needs_conversion(1, (*arg)->collation.collation,
coll.collation,
&dummy_offset))
continue;
+ /*
+ No needs to add converter if an "arg" is NUMERIC or DATETIME
+ value (which is pure ASCII) and at the same time target DTCollation
+ is ASCII-compatible. For example, no needs to rewrite:
+ SELECT * FROM t1 WHERE datetime_field = '2010-01-01';
+ to
+ SELECT * FROM t1 WHERE CONVERT(datetime_field USING cs) = '2010-01-01';
+
+ TODO: avoid conversion of any values with
+ repertoire ASCII and 7bit-ASCII-compatible,
+ not only numeric/datetime origin.
+ */
+ if ((*arg)->collation.derivation == DERIVATION_NUMERIC &&
+ (*arg)->collation.repertoire == MY_REPERTOIRE_ASCII &&
+ !((*arg)->collation.collation->state & MY_CS_NONASCII) &&
+ !(coll.collation->state & MY_CS_NONASCII))
+ continue;
+
if (!(conv= (*arg)->safe_charset_converter(coll.collation)) &&
((*arg)->collation.repertoire == MY_REPERTOIRE_ASCII))
conv= new Item_func_conv_charset(*arg, coll.collation, 1);
@@ -2097,15 +2206,16 @@ bool agg_item_set_converter(DTCollation &coll, const char *fname,
been created in prepare. In this case register the change for
rollback.
*/
- if (thd->is_stmt_prepare())
+ if (thd->stmt_arena->is_stmt_prepare())
*arg= conv;
else
thd->change_item_tree(arg, conv);
- /*
- We do not check conv->fixed, because Item_func_conv_charset which can
- be return by safe_charset_converter can't be fixed at creation
- */
- conv->fix_fields(thd, arg);
+
+ if (conv->fix_fields(thd, arg))
+ {
+ res= TRUE;
+ break; // we cannot return here, we need to restore "arena".
+ }
}
if (arena)
thd->restore_active_arena(arena, &backup);
@@ -2263,18 +2373,77 @@ Item_field::Item_field(THD *thd, Item_field *item)
with_field= 1;
}
+
+/**
+ 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();
- max_length= field_par->max_display_length();
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=test(field_par->flags & UNSIGNED_FLAG);
- collation.set(field_par->charset(), field_par->derivation());
+ 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)
any_privileges= 0;
@@ -2432,7 +2601,7 @@ String *Item_field::str_result(String *str)
return result_field->val_str(str,&str_value);
}
-bool Item_field::get_date(MYSQL_TIME *ltime,uint fuzzydate)
+bool Item_field::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
{
if ((null_value=field->is_null()) || field->get_date(ltime,fuzzydate))
{
@@ -2442,17 +2611,17 @@ bool Item_field::get_date(MYSQL_TIME *ltime,uint fuzzydate)
return 0;
}
-bool Item_field::get_date_result(MYSQL_TIME *ltime,uint fuzzydate)
+bool Item_field::get_date_result(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
- if ((null_value=result_field->is_null()) ||
- result_field->get_date(ltime,fuzzydate))
+ if (result_field->is_null() || result_field->get_date(ltime,fuzzydate))
{
bzero((char*) ltime,sizeof(*ltime));
- return 1;
+ return (null_value= 1);
}
- return 0;
+ return (null_value= 0);
}
+
void Item_field::save_result(Field *to)
{
save_field_in_field(result_field, &null_value, to, TRUE);
@@ -2620,7 +2789,7 @@ String *Item_int::val_str(String *str)
{
// following assert is redundant, because fixed=1 assigned in constructor
DBUG_ASSERT(fixed == 1);
- str->set_int(value, unsigned_flag, &my_charset_bin);
+ str->set_int(value, unsigned_flag, collation.collation);
return str;
}
@@ -2650,7 +2819,7 @@ String *Item_uint::val_str(String *str)
{
// following assert is redundant, because fixed=1 assigned in constructor
DBUG_ASSERT(fixed == 1);
- str->set((ulonglong) value, &my_charset_bin);
+ str->set((ulonglong) value, collation.collation);
return str;
}
@@ -2750,7 +2919,7 @@ double Item_decimal::val_real()
String *Item_decimal::val_str(String *result)
{
- result->set_charset(&my_charset_bin);
+ result->set_charset(&my_charset_numeric);
my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, result);
return result;
}
@@ -2812,7 +2981,9 @@ my_decimal *Item_float::val_decimal(my_decimal *decimal_value)
void Item_string::print(String *str, enum_query_type query_type)
{
- if (query_type != QT_IS && is_cs_specified())
+ const bool print_introducer=
+ !(query_type & QT_WITHOUT_INTRODUCERS) && is_cs_specified();
+ if (print_introducer)
{
str->append('_');
str->append(collation.collation->csname);
@@ -2820,27 +2991,52 @@ void Item_string::print(String *str, enum_query_type query_type)
str->append('\'');
- if (query_type != QT_IS ||
- my_charset_same(str_value.charset(), system_charset_info))
+ if (query_type & QT_TO_SYSTEM_CHARSET)
{
- str_value.print(str);
- }
- else
- {
- THD *thd= current_thd;
- LEX_STRING utf8_lex_str;
+ if (print_introducer)
+ {
+ /*
+ Because we wrote an introducer, we must print str_value in its
+ charset, and the resulting bytes must not be changed until they
+ reach the end client.
+ But the caller is asking for system_charset_info, and may later
+ convert into character_set_results. That means two conversions: we
+ must ensure that they don't change our printed bytes.
+ So we print str_value in the least common denominator of the three
+ charsets involved: ASCII. Non-ASCII characters are printed as \xFF
+ sequences (which is ASCII too). This way, our bytes will not be
+ changed.
+ */
+ ErrConvString tmp(str_value.ptr(), str_value.length(), &my_charset_bin);
+ str->append(tmp.ptr());
+ }
+ else
+ {
+ if (my_charset_same(str_value.charset(), system_charset_info))
+ str_value.print(str); // already in system_charset_info
+ else // need to convert
+ {
+ THD *thd= current_thd;
+ LEX_STRING utf8_lex_str;
- thd->convert_string(&utf8_lex_str,
- system_charset_info,
- str_value.c_ptr_safe(),
- str_value.length(),
- str_value.charset());
+ thd->convert_string(&utf8_lex_str,
+ system_charset_info,
+ str_value.c_ptr_safe(),
+ str_value.length(),
+ str_value.charset());
- String utf8_str(utf8_lex_str.str,
- utf8_lex_str.length,
- system_charset_info);
+ String utf8_str(utf8_lex_str.str,
+ utf8_lex_str.length,
+ system_charset_info);
- utf8_str.print(str);
+ utf8_str.print(str);
+ }
+ }
+ }
+ else
+ {
+ // Caller wants a result in the charset of str_value.
+ str_value.print(str);
}
str->append('\'');
@@ -2859,12 +3055,15 @@ double_from_string_with_check(CHARSET_INFO *cs, const char *cptr,
if (error || (end != end_of_num &&
!check_if_only_end_space(cs, end_of_num, end)))
{
- char buff[80];
- strmake(buff, cptr, min(sizeof(buff)-1, (size_t) (end-cptr)));
+ ErrConvString err(cptr, end - cptr, cs);
+ /*
+ We can use err.ptr() here as ErrConvString is guranteed to put an
+ end \0 here.
+ */
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), "DOUBLE",
- buff);
+ err.ptr());
}
return tmp;
}
@@ -2897,10 +3096,11 @@ longlong_from_string_with_check(CHARSET_INFO *cs, const char *cptr,
(err > 0 ||
(end != end_of_num && !check_if_only_end_space(cs, end_of_num, end))))
{
+ ErrConvString err(cptr, end - cptr, cs);
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER",
- cptr);
+ err.ptr());
}
return tmp;
}
@@ -2987,7 +3187,8 @@ Item_param::Item_param(uint pos_in_query_arg) :
param_type(MYSQL_TYPE_VARCHAR),
pos_in_query(pos_in_query_arg),
set_param_func(default_set_param_func),
- limit_clause_param(FALSE)
+ limit_clause_param(FALSE),
+ m_out_param_info(NULL)
{
name= (char*) "?";
/*
@@ -3069,6 +3270,17 @@ void Item_param::set_decimal(const char *str, ulong length)
DBUG_VOID_RETURN;
}
+void Item_param::set_decimal(const my_decimal *dv)
+{
+ state= DECIMAL_VALUE;
+
+ my_decimal2decimal(dv, &decimal_value);
+
+ decimals= (uint8) decimal_value.frac;
+ unsigned_flag= !decimal_value.sign();
+ max_length= my_decimal_precision_to_length(decimal_value.intg + decimals,
+ decimals, unsigned_flag);
+}
/**
Set parameter value from MYSQL_TIME value.
@@ -3097,7 +3309,7 @@ void Item_param::set_time(MYSQL_TIME *tm, timestamp_type time_type,
value.time.minute > 59 || value.time.second > 59 ||
value.time.second_part > TIME_MAX_SECOND_PART)
{
- Lazy_string_time str(&value.time);
+ ErrConvTime str(&value.time);
make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
&str, time_type, 0);
set_zero_time(&value.time, MYSQL_TIMESTAMP_ERROR);
@@ -3320,7 +3532,7 @@ int Item_param::save_in_field(Field *field, bool no_conversions)
}
-bool Item_param::get_date(MYSQL_TIME *res, uint fuzzydate)
+bool Item_param::get_date(MYSQL_TIME *res, ulonglong fuzzydate)
{
if (state == TIME_VALUE)
{
@@ -3696,6 +3908,155 @@ Item_param::set_param_type_and_swap_value(Item_param *src)
str_value_ptr.swap(src->str_value_ptr);
}
+
+/**
+ This operation is intended to store some item value in Item_param to be
+ used later.
+
+ @param thd thread context
+ @param ctx stored procedure runtime context
+ @param it a pointer to an item in the tree
+
+ @return Error status
+ @retval TRUE on error
+ @retval FALSE on success
+*/
+
+bool
+Item_param::set_value(THD *thd, sp_rcontext *ctx, Item **it)
+{
+ Item *arg= *it;
+
+ if (arg->is_null())
+ {
+ set_null();
+ return FALSE;
+ }
+
+ null_value= FALSE;
+
+ 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);
+ break;
+ }
+
+ default:
+ /* That can not happen. */
+
+ DBUG_ASSERT(TRUE); // Abort in debug mode.
+
+ set_null(); // Set to NULL in release mode.
+ return FALSE;
+ }
+
+ item_result_type= arg->result_type();
+ item_type= arg->type();
+ return FALSE;
+}
+
+
+/**
+ Setter of Item_param::m_out_param_info.
+
+ m_out_param_info is used to store information about store routine
+ OUT-parameters, such as stored routine name, database, stored routine
+ variable name. It is supposed to be set in sp_head::execute() after
+ Item_param::set_value() is called.
+*/
+
+void
+Item_param::set_out_param_info(Send_field *info)
+{
+ m_out_param_info= info;
+ param_type= m_out_param_info->type;
+}
+
+
+/**
+ Getter of Item_param::m_out_param_info.
+
+ m_out_param_info is used to store information about store routine
+ OUT-parameters, such as stored routine name, database, stored routine
+ variable name. It is supposed to be retrieved in
+ Protocol_binary::send_out_parameters() during creation of OUT-parameter
+ result set.
+*/
+
+const Send_field *
+Item_param::get_out_param_info() const
+{
+ return m_out_param_info;
+}
+
+
+/**
+ Fill meta-data information for the corresponding column in a result set.
+ If this is an OUT-parameter of a stored procedure, preserve meta-data of
+ stored-routine variable.
+
+ @param field container for meta-data to be filled
+*/
+
+void Item_param::make_field(Send_field *field)
+{
+ Item::make_field(field);
+
+ if (!m_out_param_info)
+ return;
+
+ /*
+ This is an OUT-parameter of stored procedure. We should use
+ OUT-parameter info to fill out the names.
+ */
+
+ field->db_name= m_out_param_info->db_name;
+ field->table_name= m_out_param_info->table_name;
+ field->org_table_name= m_out_param_info->org_table_name;
+ field->col_name= m_out_param_info->col_name;
+ 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;
+}
+
/****************************************************************************
Item_copy
****************************************************************************/
@@ -3929,7 +4290,7 @@ void Item_copy_decimal::copy()
/*
- Functions to convert item to field (for send_fields)
+ Functions to convert item to field (for send_result_set_metadata)
*/
/* ARGSUSED */
@@ -3996,7 +4357,7 @@ String* Item_ref_null_helper::val_str(String* s)
}
-bool Item_ref_null_helper::get_date(MYSQL_TIME *ltime, uint fuzzydate)
+bool Item_ref_null_helper::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
return (owner->was_null|= null_value= (*ref)->get_date_result(ltime, fuzzydate));
}
@@ -4144,7 +4505,7 @@ static Item** find_field_in_group_list(Item *find_item, ORDER *group_list)
if (db_name && lower_case_table_names)
{
/* Convert database to lower case for comparison */
- strmake(name_buff, db_name, sizeof(name_buff)-1);
+ strmake_buf(name_buff, db_name);
my_casedn_str(files_charset_info, name_buff);
db_name= name_buff;
}
@@ -4356,6 +4717,12 @@ bool is_outer_table(TABLE_LIST *table, SELECT_LEX *select)
/**
Resolve the name of an outer select column reference.
+ @param[in] thd current thread
+ @param[in,out] from_field found field reference or (Field*)not_found_field
+ @param[in,out] reference view column if this item was resolved to a
+ view column
+
+ @description
The method resolves the column reference represented by 'this' as a column
present in outer selects that contain current select.
@@ -4365,10 +4732,16 @@ bool is_outer_table(TABLE_LIST *table, SELECT_LEX *select)
current select as dependent. The found reference of field should be
provided in 'from_field'.
- @param[in] thd current thread
- @param[in,out] from_field found field reference or (Field*)not_found_field
- @param[in,out] reference view column if this item was resolved to a
- view column
+ The cache is critical for prepared statements of type:
+
+ SELECT a FROM (SELECT a FROM test.t1) AS s1 NATURAL JOIN t2 AS s2;
+
+ This is internally converted to a join similar to
+
+ SELECT a FROM t1 AS s1,t2 AS s2 WHERE t2.a=t1.a;
+
+ Without the cache, we would on re-prepare not know if 'a' did match
+ s1.a or s2.a.
@note
This is the inner loop of Item_field::fix_fields:
@@ -4398,7 +4771,12 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
enum_parsing_place place= NO_MATTER;
bool field_found= (*from_field != not_found_field);
bool upward_lookup= FALSE;
+ TABLE_LIST *table_list;
+ /* Calulate the TABLE_LIST for the table */
+ table_list= (cached_table ? cached_table :
+ field_found && (*from_field) != view_ref_found ?
+ (*from_field)->table->pos_in_table_list : 0);
/*
If there are outer contexts (outer selects, but current select is
not derived table or view) try to resolve this reference in the
@@ -4417,6 +4795,15 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
if (current_sel->master_unit()->first_select()->linkage !=
DERIVED_TABLE_TYPE)
outer_context= context->outer_context;
+
+ /*
+ This assert is to ensure we have an outer contex when *from_field
+ is set.
+ If this would not be the case, we would assert in mark_as_dependent
+ as last_checked_countex == context
+ */
+ DBUG_ASSERT(outer_context || !*from_field ||
+ *from_field == not_found_field);
for (;
outer_context;
outer_context= outer_context->outer_context)
@@ -4433,7 +4820,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
to find_field_in_tables(). Only need to find appropriate context.
*/
if (field_found && outer_context->select_lex !=
- cached_table->select_lex)
+ table_list->select_lex)
continue;
/*
In case of a view, find_field_in_tables() writes the pointer to
@@ -4632,9 +5019,9 @@ 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 Item_ref(context,
- (cached_table->db[0] ? cached_table->db : 0),
- (char*) cached_table->alias, (char*) field_name);
+ rf= new Item_ref(context, (*from_field)->table->s->db.str,
+ (*from_field)->table->alias.c_ptr(),
+ (char*) field_name);
if (!rf)
return -1;
thd->change_item_tree(reference, rf);
@@ -4705,6 +5092,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
if (!field) // If field is not checked
{
+ TABLE_LIST *table_list;
/*
In case of view, find_field_in_tables() write pointer to view field
expression to 'reference', i.e. it substitute that expression instead
@@ -4790,11 +5178,14 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
else if (!from_field)
goto error;
- if (!outer_fixed && cached_table && cached_table->select_lex &&
+ table_list= (cached_table ? cached_table :
+ from_field != view_ref_found ?
+ from_field->table->pos_in_table_list : 0);
+ if (!outer_fixed && table_list && table_list->select_lex &&
context->select_lex &&
- cached_table->select_lex != context->select_lex &&
- !context->select_lex->is_merged_child_of(cached_table->select_lex) &&
- is_outer_table(cached_table, context->select_lex))
+ table_list->select_lex != context->select_lex &&
+ !context->select_lex->is_merged_child_of(table_list->select_lex) &&
+ is_outer_table(table_list, context->select_lex))
{
int ret;
if ((ret= fix_outer_field(thd, &from_field, reference)) < 0)
@@ -4856,8 +5247,8 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
if (any_privileges)
{
char *db, *tab;
- db= cached_table->get_db_name();
- tab= cached_table->get_table_name();
+ 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) &
VIEW_ANY_ACL)))
@@ -4878,20 +5269,33 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
marker= thd->lex->current_select->cur_pos_in_select_list;
}
mark_non_agg_field:
- if (fixed && thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY)
+ /*
+ table->pos_in_table_list can be 0 when fixing partition functions
+ or virtual fields.
+ */
+ if (fixed && (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY) &&
+ field->table->pos_in_table_list)
{
/*
Mark selects according to presence of non aggregated fields.
Fields from outer selects added to the aggregate function
- outer_fields list as its unknown at the moment whether it's
+ outer_fields list as it's unknown at the moment whether it's
aggregated or not.
- We're using either the select lex of the cached table (if present)
- or the field's resolution context. context->select_lex is
- safe for use because it's either the SELECT we want to use
- (the current level) or a stub added by non-SELECT queries.
+ We're using the select lex of the cached table (if present).
*/
- SELECT_LEX *select_lex= cached_table ?
- cached_table->select_lex : context->select_lex;
+ SELECT_LEX *select_lex;
+ if (cached_table)
+ select_lex= cached_table->select_lex;
+ else if (!(select_lex= field->table->pos_in_table_list->select_lex))
+ {
+ /*
+ This can only happen when there is no real table in the query.
+ We are using the field's resolution context. context->select_lex is eee
+ safe for use because it's either the SELECT we want to use
+ (the current level) or a stub added by non-SELECT queries.
+ */
+ select_lex= context->select_lex;
+ }
if (!thd->lex->in_sum_func)
select_lex->set_non_agg_field_used(true);
else
@@ -5061,7 +5465,6 @@ static void convert_zerofill_number_to_string(Item **item, Field_num *field)
Set a pointer to the multiple equality the field reference belongs to
(if any).
- @details
The function looks for a multiple equality containing the field item
among those referenced by arg.
In the case such equality exists the function does the following.
@@ -5098,16 +5501,12 @@ Item *Item_field::equal_fields_propagator(uchar *arg)
e.g. <bin_col> = <int_col> AND <bin_col> = <hex_string>) since
Items don't know the context they are in and there are functions like
IF (<hex_string>, 'yes', 'no').
- The same problem occurs when comparing a DATE/TIME field with a
- DATE/TIME represented as an int and as a string.
*/
- if (!item ||
- (cmp_context != IMPOSSIBLE_RESULT && item->cmp_context != cmp_context))
+ if (!item || !has_compatible_context(item))
item= this;
else if (field && (field->flags & ZEROFILL_FLAG) && IS_NUM(field->type()))
{
- if (item && field->type() != FIELD_TYPE_TIMESTAMP &&
- cmp_context != INT_RESULT)
+ if (item && (cmp_context == STRING_RESULT || cmp_context == IMPOSSIBLE_RESULT))
convert_zerofill_number_to_string(&item, (Field_num *)field);
else
item= this;
@@ -5134,7 +5533,6 @@ bool Item_field::set_no_const_sub(uchar *arg)
Replace an Item_field for an equal Item_field that evaluated earlier
(if any).
- @details
If this->item_equal points to some item and coincides with arg then
the function returns a pointer to an item that is taken from
the very beginning of the item_equal list which the Item_field
@@ -5165,8 +5563,7 @@ Item *Item_field::replace_equal_field(uchar *arg)
Item *const_item= item_equal->get_const();
if (const_item)
{
- if (cmp_context != IMPOSSIBLE_RESULT &&
- const_item->cmp_context != cmp_context)
+ if (!has_compatible_context(const_item))
return this;
return const_item;
}
@@ -5192,7 +5589,7 @@ void Item::init_make_field(Send_field *tmp_field,
tmp_field->col_name= name;
tmp_field->charsetnr= collation.collation->number;
tmp_field->flags= (maybe_null ? 0 : NOT_NULL_FLAG) |
- (my_binary_compare(collation.collation) ?
+ (my_binary_compare(charset_for_protocol()) ?
BINARY_FLAG : 0);
tmp_field->type= field_type_arg;
tmp_field->length=max_length;
@@ -5241,6 +5638,18 @@ enum_field_types Item::field_type() const
}
+/**
+ Verifies that the input string is well-formed according to its character set.
+ @param send_error If true, call my_error if string is not well-formed.
+
+ Will truncate input string if it is not well-formed.
+
+ @return
+ If well-formed: input string.
+ If not well-formed:
+ if strict mode: NULL pointer and we set this Item's value to NULL
+ if not strict mode: input string truncated up to last good character
+ */
String *Item::check_well_formed_result(String *str, bool send_error)
{
/* Check whether we got a well-formed string */
@@ -5253,7 +5662,6 @@ String *Item::check_well_formed_result(String *str, bool send_error)
{
THD *thd= current_thd;
char hexbuf[7];
- enum MYSQL_ERROR::enum_warning_level level;
uint diff= str->length() - wlen;
set_if_smaller(diff, 3);
octet2hex(hexbuf, str->ptr() + wlen, diff);
@@ -5266,16 +5674,14 @@ String *Item::check_well_formed_result(String *str, bool send_error)
if ((thd->variables.sql_mode &
(MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)))
{
- level= MYSQL_ERROR::WARN_LEVEL_ERROR;
null_value= 1;
str= 0;
}
else
{
- level= MYSQL_ERROR::WARN_LEVEL_WARN;
str->length(wlen);
}
- push_warning_printf(thd, level, ER_INVALID_CHARACTER_STRING,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_INVALID_CHARACTER_STRING,
ER(ER_INVALID_CHARACTER_STRING), cs->csname, hexbuf);
}
return str;
@@ -5328,7 +5734,7 @@ 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 max_length > CONVERT_IF_BIGGER_TO_BLOB create a blob @n
+ 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)
@@ -5343,9 +5749,9 @@ Field *Item::make_string_field(TABLE *table)
Note: the following check is repeated in
subquery_types_allow_materialization():
*/
- if (max_length/collation.collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB)
+ if (too_big_for_varchar())
field= new Field_blob(max_length, maybe_null, name,
- collation.collation);
+ 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))
@@ -5448,7 +5854,7 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table, bool fixed_length)
DBUG_ASSERT(0);
/* If something goes awfully wrong, it's better to get a string than die */
case MYSQL_TYPE_STRING:
- if (fixed_length && max_length < CONVERT_IF_BIGGER_TO_BLOB)
+ if (fixed_length && !too_big_for_varchar())
{
field= new Field_string(max_length, maybe_null, name,
collation.collation);
@@ -5605,16 +6011,14 @@ int Item_null::save_safe_in_field(Field *field)
Item uses str_value to store something, it should
reimplement it's ::save_in_field() as Item_string, for example, does.
- Note: all Item_XXX::val_str(str) methods must NOT rely on the fact that
+ 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_in_field(Field *field, bool no_conversions)
{
int error;
- if (result_type() == STRING_RESULT ||
- (result_type() == REAL_RESULT &&
- field->result_type() == STRING_RESULT))
+ if (result_type() == STRING_RESULT)
{
String *result;
CHARSET_INFO *cs= collation.collation;
@@ -5637,7 +6041,7 @@ int Item::save_in_field(Field *field, bool no_conversions)
{
double nr= val_real();
if (null_value)
- return set_field_to_null(field);
+ return set_field_to_null_with_conversions(field, no_conversions);
field->set_notnull();
error=field->store(nr);
}
@@ -5762,10 +6166,27 @@ static uint nr_of_decimals(const char *str, const char *end)
break;
}
decimal_point= str;
- for (; my_isdigit(system_charset_info, *str) ; str++)
+ for ( ; str < end && my_isdigit(system_charset_info, *str) ; str++)
;
- if (*str == 'e' || *str == 'E')
+ if (str < end && (*str == 'e' || *str == 'E'))
return NOT_FIXED_DEC;
+ /*
+ QQ:
+ The number of decimal digist in fact should be (str - decimal_point - 1).
+ But it seems the result of nr_of_decimals() is never used!
+
+ In case of 'e' and 'E' nr_of_decimals returns NOT_FIXED_DEC.
+ In case if there is no 'e' or 'E' parser code in sql_yacc.yy
+ never calls Item_float::Item_float() - it creates Item_decimal instead.
+
+ The only piece of code where we call Item_float::Item_float(str, len)
+ without having 'e' or 'E' is item_xmlfunc.cc, but this Item_float
+ never appears in metadata itself. Changing the code to return
+ (str - decimal_point - 1) does not make any changes in the test results.
+
+ This should be addressed somehow.
+ Looks like a reminder from before real DECIMAL times.
+ */
return (uint) (str - decimal_point);
}
@@ -5855,17 +6276,8 @@ inline uint char_val(char X)
X-'a'+10);
}
-Item_hex_string::Item_hex_string()
-{
- hex_string_init("", 0);
-}
-
-Item_hex_string::Item_hex_string(const char *str, uint str_length)
-{
- hex_string_init(str, str_length);
-}
-void Item_hex_string::hex_string_init(const char *str, uint str_length)
+void Item_hex_constant::hex_string_init(const char *str, uint str_length)
{
max_length=(str_length+1)/2;
char *ptr=(char*) sql_alloc(max_length+1);
@@ -5889,7 +6301,7 @@ void Item_hex_string::hex_string_init(const char *str, uint str_length)
unsigned_flag= 1;
}
-longlong Item_hex_string::val_int()
+longlong Item_hex_hybrid::val_int()
{
// following assert is redundant, because fixed=1 assigned in constructor
DBUG_ASSERT(fixed == 1);
@@ -5903,17 +6315,7 @@ longlong Item_hex_string::val_int()
}
-my_decimal *Item_hex_string::val_decimal(my_decimal *decimal_value)
-{
- // following assert is redundant, because fixed=1 assigned in constructor
- DBUG_ASSERT(fixed == 1);
- ulonglong value= (ulonglong)val_int();
- int2my_decimal(E_DEC_FATAL_ERROR, value, TRUE, decimal_value);
- return (decimal_value);
-}
-
-
-int Item_hex_string::save_in_field(Field *field, bool no_conversions)
+int Item_hex_hybrid::save_in_field(Field *field, bool no_conversions)
{
field->set_notnull();
if (field->result_type() == STRING_RESULT)
@@ -5946,22 +6348,27 @@ warn:
}
-void Item_hex_string::print(String *str, enum_query_type query_type)
+void Item_hex_hybrid::print(String *str, enum_query_type query_type)
{
- char *end= (char*) str_value.ptr() + str_value.length(),
- *ptr= end - min(str_value.length(), sizeof(longlong));
+ uint32 len= min(str_value.length(), sizeof(longlong));
+ const char *ptr= str_value.ptr() + str_value.length() - len;
str->append("0x");
- for (; ptr != end ; ptr++)
- {
- str->append(_dig_vec_lower[((uchar) *ptr) >> 4]);
- str->append(_dig_vec_lower[((uchar) *ptr) & 0x0F]);
- }
+ str->append_hex(ptr, len);
}
-bool Item_hex_string::eq(const Item *arg, bool binary_cmp) const
+void Item_hex_string::print(String *str, enum_query_type query_type)
{
- if (arg->basic_const_item() && arg->type() == type())
+ str->append("X'");
+ str->append_hex(str_value.ptr(), str_value.length());
+ str->append("'");
+}
+
+
+bool Item_hex_constant::eq(const Item *arg, bool binary_cmp) const
+{
+ if (arg->basic_const_item() && arg->type() == type() &&
+ arg->cast_to_int_type() == cast_to_int_type())
{
if (binary_cmp)
return !stringcmp(&str_value, &arg->str_value);
@@ -5971,7 +6378,7 @@ bool Item_hex_string::eq(const Item *arg, bool binary_cmp) const
}
-Item *Item_hex_string::safe_charset_converter(CHARSET_INFO *tocs)
+Item *Item_hex_constant::safe_charset_converter(CHARSET_INFO *tocs)
{
Item_string *conv;
String tmp, *str= val_str(&tmp);
@@ -6154,6 +6561,68 @@ bool Item::send(Protocol *protocol, String *buffer)
}
+/**
+ Check if an item is a constant one and can be cached.
+
+ @param arg [out] TRUE <=> Cache this item.
+
+ @return TRUE Go deeper in item tree.
+ @return FALSE Don't go deeper in item tree.
+*/
+
+bool Item::cache_const_expr_analyzer(uchar **arg)
+{
+ bool *cache_flag= (bool*)*arg;
+ if (!*cache_flag)
+ {
+ Item *item= real_item();
+ /*
+ Cache constant items unless it's a basic constant, constant field or
+ a subselect (they use their own cache).
+ */
+ if (const_item() &&
+ !(basic_const_item() || item->basic_const_item() ||
+ item->type() == Item::FIELD_ITEM ||
+ item->type() == SUBSELECT_ITEM ||
+ /*
+ Do not cache GET_USER_VAR() function as its const_item() may
+ return TRUE for the current thread but it still may change
+ during the execution.
+ */
+ (item->type() == Item::FUNC_ITEM &&
+ ((Item_func*)item)->functype() == Item_func::GUSERVAR_FUNC)))
+ *cache_flag= TRUE;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/**
+ Cache item if needed.
+
+ @param arg TRUE <=> Cache this item.
+
+ @return cache if cache needed.
+ @return this otherwise.
+*/
+
+Item* Item::cache_const_expr_transformer(uchar *arg)
+{
+ if (*(bool*)arg)
+ {
+ *((bool*)arg)= FALSE;
+ Item_cache *cache= Item_cache::get_cache(this);
+ if (!cache)
+ return NULL;
+ cache->setup(this);
+ cache->store(this);
+ return cache;
+ }
+ return this;
+}
+
+
bool Item_field::send(Protocol *protocol, String *buffer)
{
return protocol->store(result_field);
@@ -6511,7 +6980,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
if (from_field != not_found_field)
{
Item_field* fld;
- if (!(fld= new Item_field(thd, last_checked_context, from_field)))
+ if (!(fld= new Item_field(from_field)))
goto error;
thd->change_item_tree(reference, fld);
mark_as_dependent(thd, last_checked_context->select_lex,
@@ -6654,7 +7123,7 @@ void Item_ref::cleanup()
Item* Item_ref::transform(Item_transformer transformer, uchar *arg)
{
- DBUG_ASSERT(!current_thd->is_stmt_prepare());
+ DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare());
DBUG_ASSERT((*ref) != NULL);
/* Transform the object we are referencing. */
@@ -6899,7 +7368,7 @@ bool Item_ref::is_null()
}
-bool Item_ref::get_date(MYSQL_TIME *ltime,uint fuzzydate)
+bool Item_ref::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
{
return (null_value=(*ref)->get_date_result(ltime,fuzzydate));
}
@@ -7036,7 +7505,7 @@ bool Item_direct_ref::is_null()
}
-bool Item_direct_ref::get_date(MYSQL_TIME *ltime,uint fuzzydate)
+bool Item_direct_ref::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
{
return (null_value=(*ref)->get_date(ltime,fuzzydate));
}
@@ -7047,7 +7516,6 @@ Item_cache_wrapper::~Item_cache_wrapper()
DBUG_ASSERT(expr_cache == 0);
}
-
Item_cache_wrapper::Item_cache_wrapper(Item *item_arg)
:orig_item(item_arg), expr_cache(NULL), expr_value(NULL)
{
@@ -7114,6 +7582,12 @@ bool Item_cache_wrapper::fix_fields(THD *thd __attribute__((unused)),
return FALSE;
}
+bool Item_cache_wrapper::send(Protocol *protocol, String *buffer)
+{
+ if (result_field)
+ return protocol->store(result_field);
+ return Item::send(protocol, buffer);
+}
/**
Clean the expression cache wrapper up before reusing it.
@@ -7393,7 +7867,7 @@ bool Item_cache_wrapper::is_null()
Get the date value of the possibly cached item
*/
-bool Item_cache_wrapper::get_date(MYSQL_TIME *ltime, uint fuzzydate)
+bool Item_cache_wrapper::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
Item *cached_value;
DBUG_ENTER("Item_cache_wrapper::get_date");
@@ -7426,6 +7900,13 @@ Item* Item_cache_wrapper::get_tmp_table_item(THD *thd_arg)
}
+bool Item_direct_view_ref::send(Protocol *protocol, String *buffer)
+{
+ if (check_null_ref())
+ return protocol->store_null();
+ return Item_direct_ref::send(protocol, buffer);
+}
+
/**
Prepare referenced field then call usual Item_direct_ref::fix_fields .
@@ -7753,7 +8234,7 @@ bool Item_default_value::fix_fields(THD *thd, Item **items)
}
if (!(def_field= (Field*) sql_alloc(field_arg->field->size_of())))
goto error;
- memcpy(def_field, field_arg->field, field_arg->field->size_of());
+ memcpy((void *)def_field, (void *)field_arg->field, field_arg->field->size_of());
def_field->move_field_offset((my_ptrdiff_t)
(def_field->table->s->default_values -
def_field->table->record[0]));
@@ -7795,7 +8276,7 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions)
if (context->error_processor == &view_error_processor)
{
- TABLE_LIST *view= cached_table->top_table();
+ TABLE_LIST *view= field_arg->table->pos_in_table_list->top_table();
push_warning_printf(field_arg->table->in_use,
MYSQL_ERROR::WARN_LEVEL_WARN,
ER_NO_DEFAULT_FOR_VIEW_FIELD,
@@ -7827,7 +8308,7 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions)
Item *Item_default_value::transform(Item_transformer transformer, uchar *args)
{
- DBUG_ASSERT(!current_thd->is_stmt_prepare());
+ DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare());
/*
If the value of arg is NULL, then this object represents a constant,
@@ -7889,7 +8370,7 @@ bool Item_insert_value::fix_fields(THD *thd, Item **items)
Field *def_field= (Field*) sql_alloc(field_arg->field->size_of());
if (!def_field)
return TRUE;
- memcpy(def_field, field_arg->field, field_arg->field->size_of());
+ memcpy((void *)def_field, (void *)field_arg->field, field_arg->field->size_of());
def_field->move_field_offset((my_ptrdiff_t)
(def_field->table->insert_values -
def_field->table->record[0]));
@@ -7987,8 +8468,26 @@ bool Item_trigger_field::set_value(THD *thd, sp_rcontext * /*ctx*/, Item **it)
{
Item *item= sp_prepare_func_item(thd, it);
- return (!item || (!fixed && fix_fields(thd, 0)) ||
- (item->save_in_field(field, 0) < 0));
+ if (!item)
+ 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;
+
+ field->table->copy_blobs= true;
+
+ int err_code= item->save_in_field(field, 0);
+
+ field->table->copy_blobs= copy_blobs_saved;
+
+ return err_code < 0;
}
@@ -8185,10 +8684,14 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item)
or less than the original Item. A 0 may also be returned if
out of memory.
- @note We only use this on the range optimizer/partition pruning,
+ @note We use this in the range optimizer/partition pruning,
because in some cases we can't store the value in the field
without some precision/character loss.
+ We similarly use it to verify that expressions like
+ BIGINT_FIELD <cmp> <literal value>
+ is done correctly (as int/decimal/float according to literal type).
+
@todo rewrite it to use Arg_comparator (currently it's a simplified and
incomplete version of it)
*/
@@ -8265,10 +8768,15 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
}
return my_time_compare(&field_time, &item_time);
}
- double result= item->val_real();
+ /*
+ The patch for Bug#13463415 started using this function for comparing
+ BIGINTs. That uncovered a bug in Visual Studio 32bit optimized mode.
+ Prefixing the auto variables with volatile fixes the problem....
+ */
+ volatile double result= item->val_real();
if (item->null_value)
return 0;
- double field_result= field->val_real();
+ volatile double field_result= field->val_real();
if (field_result < result)
return -1;
else if (field_result > result)
@@ -8278,7 +8786,7 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
Item_cache* Item_cache::get_cache(const Item *item)
{
- return get_cache(item, item->result_type());
+ return get_cache(item, item->cmp_type());
}
@@ -8365,11 +8873,8 @@ bool Item_cache_int::cache_value()
String *Item_cache_int::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
- if ((!value_cached && !cache_value()) || null_value)
- {
- null_value= TRUE;
+ if (!has_value())
return NULL;
- }
str->set_int(value, unsigned_flag, default_charset());
return str;
}
@@ -8378,11 +8883,8 @@ String *Item_cache_int::val_str(String *str)
my_decimal *Item_cache_int::val_decimal(my_decimal *decimal_val)
{
DBUG_ASSERT(fixed == 1);
- if ((!value_cached && !cache_value()) || null_value)
- {
- null_value= TRUE;
+ if (!has_value())
return NULL;
- }
int2my_decimal(E_DEC_FATAL_ERROR, value, unsigned_flag, decimal_val);
return decimal_val;
}
@@ -8390,30 +8892,23 @@ my_decimal *Item_cache_int::val_decimal(my_decimal *decimal_val)
double Item_cache_int::val_real()
{
DBUG_ASSERT(fixed == 1);
- if ((!value_cached && !cache_value()) || null_value)
- {
- null_value= TRUE;
+ if (!has_value())
return 0.0;
- }
return (double) value;
}
longlong Item_cache_int::val_int()
{
DBUG_ASSERT(fixed == 1);
- if ((!value_cached && !cache_value()) || null_value)
- {
- null_value= TRUE;
+ if (!has_value())
return 0;
- }
return value;
}
-
int Item_cache_int::save_in_field(Field *field, bool no_conversions)
{
int error;
- if ((!value_cached && !cache_value()) || null_value)
+ if (!has_value())
return set_field_to_null_with_conversions(field, no_conversions);
field->set_notnull();
@@ -8423,6 +8918,14 @@ int Item_cache_int::save_in_field(Field *field, bool no_conversions)
}
+Item_cache_temporal::Item_cache_temporal(enum_field_types field_type_arg):
+ Item_cache_int(field_type_arg)
+{
+ if (mysql_type_to_time_type(cached_field_type) == MYSQL_TIMESTAMP_ERROR)
+ cached_field_type= MYSQL_TYPE_DATETIME;
+}
+
+
longlong Item_cache_temporal::val_temporal_packed()
{
DBUG_ASSERT(fixed == 1);
@@ -8438,7 +8941,7 @@ longlong Item_cache_temporal::val_temporal_packed()
String *Item_cache_temporal::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
- if ((!value_cached && !cache_value()) || null_value)
+ if (!has_value())
{
null_value= true;
return NULL;
@@ -8487,6 +8990,7 @@ bool Item_cache_temporal::cache_value()
{
if (!example)
return false;
+
value_cached= true;
MYSQL_TIME ltime;
@@ -8499,11 +9003,11 @@ bool Item_cache_temporal::cache_value()
}
-bool Item_cache_temporal::get_date(MYSQL_TIME *ltime, uint fuzzydate)
+bool Item_cache_temporal::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
- Lazy_string_num str(value);
+ ErrConvInteger str(value);
- if (!value_cached && !cache_value())
+ if (!has_value())
{
bzero((char*) ltime,sizeof(*ltime));
return 1;
@@ -8523,18 +9027,11 @@ bool Item_cache_temporal::get_date(MYSQL_TIME *ltime, uint fuzzydate)
int Item_cache_temporal::save_in_field(Field *field, bool no_conversions)
{
- int error;
- if ((!value_cached && !cache_value()) || null_value)
+ MYSQL_TIME ltime;
+ if (get_date(&ltime, 0))
return set_field_to_null_with_conversions(field, no_conversions);
-
field->set_notnull();
-
- MYSQL_TIME ltime;
- unpack_time(value, &ltime);
- ltime.time_type= mysql_type_to_time_type(field_type());
- error= field->store_time_dec(&ltime, decimals);
-
-
+ int error= field->store_time_dec(&ltime, decimals);
return error ? error : field->table->in_use->is_error() ? 1 : 0;
}
@@ -8563,22 +9060,16 @@ bool Item_cache_real::cache_value()
double Item_cache_real::val_real()
{
DBUG_ASSERT(fixed == 1);
- if ((!value_cached && !cache_value()) || null_value)
- {
- null_value= TRUE;
+ if (!has_value())
return 0.0;
- }
return value;
}
longlong Item_cache_real::val_int()
{
DBUG_ASSERT(fixed == 1);
- if ((!value_cached && !cache_value()) || null_value)
- {
- null_value= TRUE;
+ if (!has_value())
return 0;
- }
return (longlong) rint(value);
}
@@ -8586,11 +9077,8 @@ longlong Item_cache_real::val_int()
String* Item_cache_real::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
- if ((!value_cached && !cache_value()) || null_value)
- {
- null_value= TRUE;
+ if (!has_value())
return NULL;
- }
str->set_real(value, decimals, default_charset());
return str;
}
@@ -8599,11 +9087,8 @@ String* Item_cache_real::val_str(String *str)
my_decimal *Item_cache_real::val_decimal(my_decimal *decimal_val)
{
DBUG_ASSERT(fixed == 1);
- if ((!value_cached && !cache_value()) || null_value)
- {
- null_value= TRUE;
+ if (!has_value())
return NULL;
- }
double2my_decimal(E_DEC_FATAL_ERROR, value, decimal_val);
return decimal_val;
}
@@ -8624,11 +9109,8 @@ double Item_cache_decimal::val_real()
{
DBUG_ASSERT(fixed);
double res;
- if ((!value_cached && !cache_value()) || null_value)
- {
- null_value= TRUE;
+ if (!has_value())
return 0.0;
- }
my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &res);
return res;
}
@@ -8637,11 +9119,8 @@ longlong Item_cache_decimal::val_int()
{
DBUG_ASSERT(fixed);
longlong res;
- if ((!value_cached && !cache_value()) || null_value)
- {
- null_value= TRUE;
+ if (!has_value())
return 0;
- }
my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, unsigned_flag, &res);
return res;
}
@@ -8649,11 +9128,8 @@ longlong Item_cache_decimal::val_int()
String* Item_cache_decimal::val_str(String *str)
{
DBUG_ASSERT(fixed);
- if ((!value_cached && !cache_value()) || null_value)
- {
- null_value= TRUE;
+ if (!has_value())
return NULL;
- }
my_decimal_round(E_DEC_FATAL_ERROR, &decimal_value, decimals, FALSE,
&decimal_value);
my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, str);
@@ -8663,11 +9139,8 @@ String* Item_cache_decimal::val_str(String *str)
my_decimal *Item_cache_decimal::val_decimal(my_decimal *val)
{
DBUG_ASSERT(fixed);
- if ((!value_cached && !cache_value()) || null_value)
- {
- null_value= TRUE;
+ if (!has_value())
return NULL;
- }
return &decimal_value;
}
@@ -8702,13 +9175,12 @@ double Item_cache_str::val_real()
DBUG_ASSERT(fixed == 1);
int err_not_used;
char *end_not_used;
- if ((!value_cached && !cache_value()) || null_value)
- {
- null_value= TRUE;
+ if (!has_value())
return 0.0;
- }
- return my_strntod(value->charset(), (char*) value->ptr(),
- value->length(), &end_not_used, &err_not_used);
+ if (value)
+ return my_strntod(value->charset(), (char*) value->ptr(),
+ value->length(), &end_not_used, &err_not_used);
+ return (double) 0;
}
@@ -8716,24 +9188,21 @@ longlong Item_cache_str::val_int()
{
DBUG_ASSERT(fixed == 1);
int err;
- if ((!value_cached && !cache_value()) || null_value)
- {
- null_value= TRUE;
+ if (!has_value())
return 0;
- }
- return my_strntoll(value->charset(), value->ptr(),
- value->length(), 10, (char**) 0, &err);
+ if (value)
+ return my_strntoll(value->charset(), value->ptr(),
+ value->length(), 10, (char**) 0, &err);
+ else
+ return (longlong)0;
}
String* Item_cache_str::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
- if ((!value_cached && !cache_value()) || null_value)
- {
- null_value= TRUE;
+ if (!has_value())
return 0;
- }
return value;
}
@@ -8741,21 +9210,20 @@ String* Item_cache_str::val_str(String *str)
my_decimal *Item_cache_str::val_decimal(my_decimal *decimal_val)
{
DBUG_ASSERT(fixed == 1);
- if ((!value_cached && !cache_value()) || null_value)
- {
- null_value= TRUE;
+ if (!has_value())
return NULL;
- }
- string2my_decimal(E_DEC_FATAL_ERROR, value, decimal_val);
+ if (value)
+ string2my_decimal(E_DEC_FATAL_ERROR, value, decimal_val);
+ else
+ decimal_val= 0;
return decimal_val;
}
int Item_cache_str::save_in_field(Field *field, bool no_conversions)
{
- if ((!value_cached && !cache_value()) || null_value)
+ if (!has_value())
return set_field_to_null_with_conversions(field, no_conversions);
-
int res= Item_cache::save_in_field(field, no_conversions);
return (is_varbinary && field->type() == MYSQL_TYPE_STRING &&
value->length() < field->field_length) ? 1 : res;
diff --git a/sql/item.h b/sql/item.h
index 33296b619db..ed50605ef7b 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -1,5 +1,9 @@
+#ifndef SQL_ITEM_INCLUDED
+#define SQL_ITEM_INCLUDED
+
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2009, 2013, Monty Program Ab.
+ Copyright (c) 2009, 2013 Monty Program Ab.
+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
@@ -18,11 +22,18 @@
#pragma interface /* gcc class implementation */
#endif
+#include "sql_priv.h" /* STRING_BUFFER_USUAL_SIZE */
+#include "unireg.h"
+#include "sql_const.h" /* RAND_TABLE_BIT, MAX_FIELD_NAME */
+#include "unireg.h" // REQUIRED: for other includes
+#include "thr_malloc.h" /* sql_calloc */
+#include "field.h" /* Derivation */
+
C_MODE_START
#include <ma_dyncol.h>
C_MODE_END
-inline
+static inline
bool trace_unsupported_func(const char *where, const char *processor_name)
{
char buff[64];
@@ -33,17 +44,18 @@ bool trace_unsupported_func(const char *where, const char *processor_name)
DBUG_RETURN(TRUE);
}
-inline
+static inline
bool trace_unsupported_by_check_vcol_func_processor(const char *where)
{
return trace_unsupported_func(where, "check_vcol_func_processor");
}
-
class Protocol;
struct TABLE_LIST;
void item_init(void); /* Init item functions */
class Item_field;
+class user_var_entry;
+
static inline uint32
char_to_byte_length_safe(uint32 char_length_arg, uint32 mbmaxlen_arg)
@@ -63,6 +75,8 @@ char_to_byte_length_safe(uint32 char_length_arg, uint32 mbmaxlen_arg)
(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
@@ -71,9 +85,11 @@ char_to_byte_length_safe(uint32 char_length_arg, uint32 mbmaxlen_arg)
#define MY_COLL_ALLOW_SUPERSET_CONV 1
#define MY_COLL_ALLOW_COERCIBLE_CONV 2
-#define MY_COLL_ALLOW_CONV 3
#define MY_COLL_DISALLOW_NONE 4
-#define MY_COLL_CMP_CONV 7
+#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)
class DTCollation {
public:
@@ -118,6 +134,12 @@ public:
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;
@@ -132,6 +154,7 @@ public:
{
switch(derivation)
{
+ case DERIVATION_NUMERIC: return "NUMERIC";
case DERIVATION_IGNORABLE: return "IGNORABLE";
case DERIVATION_COERCIBLE: return "COERCIBLE";
case DERIVATION_IMPLICIT: return "IMPLICIT";
@@ -309,6 +332,8 @@ struct Name_resolution_context: Sql_alloc
*/
TABLE_LIST *last_name_resolution_table;
+ /* Cache first_name_resolution_table in setup_natural_join_row_types */
+ TABLE_LIST *natural_join_first_table;
/*
SELECT_LEX item belong to, in case of merged VIEW it can differ from
SELECT_LEX where item was created, so we can't use table_list/field_list
@@ -496,6 +521,11 @@ public:
TRUE if error has occured.
*/
virtual bool set_value(THD *thd, sp_rcontext *ctx, Item **it)= 0;
+
+ virtual void set_out_param_info(Send_field *info) {}
+
+ virtual const Send_field *get_out_param_info() const
+ { return NULL; }
};
@@ -589,6 +619,10 @@ public:
@see Query_arena::free_list
*/
Item *next;
+ /*
+ The maximum value length in characters multiplied by collation->mbmaxlen.
+ Almost always it's the maximum value length in bytes.
+ */
uint32 max_length;
/*
TODO: convert name and name_length fields into LEX_STRING to keep them in
@@ -641,7 +675,7 @@ public:
void init_make_field(Send_field *tmp_field,enum enum_field_types type);
virtual void cleanup();
virtual void make_field(Send_field *field);
- Field *make_string_field(TABLE *table);
+ virtual Field *make_string_field(TABLE *table);
virtual bool fix_fields(THD *, Item **);
/*
Fix after some tables has been pulled out. Basically re-calculate all
@@ -793,6 +827,77 @@ public:
If value is not null null_value flag will be reset to FALSE.
*/
virtual String *val_str(String *str)=0;
+
+ /*
+ Returns string representation of this item in ASCII format.
+
+ SYNOPSIS
+ val_str_ascii()
+ str - similar to val_str();
+
+ NOTE
+ This method is introduced for performance optimization purposes.
+
+ 1. val_str() result of some Items in string context
+ depends on @@character_set_results.
+ @@character_set_results can be set to a "real multibyte" character
+ set like UCS2, UTF16, UTF32. (We'll use only UTF32 in the examples
+ below for convenience.)
+
+ So the default string result of such functions
+ in these circumstances is real multi-byte character set, like UTF32.
+
+ For example, all numbers in string context
+ return result in @@character_set_results:
+
+ SELECT CONCAT(20010101); -> UTF32
+
+ We do sprintf() first (to get ASCII representation)
+ and then convert to UTF32;
+
+ So these kind "data sources" can use ASCII representation
+ internally, but return multi-byte data only because
+ @@character_set_results wants so.
+ Therefore, conversion from ASCII to UTF32 is applied internally.
+
+
+ 2. Some other functions need in fact ASCII input.
+
+ For example,
+ inet_aton(), GeometryFromText(), Convert_TZ(), GET_FORMAT().
+
+ Similar, fields of certain type, like DATE, TIME,
+ when you insert string data into them, expect in fact ASCII input.
+ If they get non-ASCII input, for example UTF32, they
+ convert input from UTF32 to ASCII, and then use ASCII
+ representation to do further processing.
+
+
+ 3. Now imagine we pass result of a data source of the first type
+ to a data destination of the second type.
+
+ What happens:
+ a. data source converts data from ASCII to UTF32, because
+ @@character_set_results wants so and passes the result to
+ data destination.
+ b. data destination gets UTF32 string.
+ c. data destination converts UTF32 string to ASCII,
+ because it needs ASCII representation to be able to handle data
+ correctly.
+
+ As a result we get two steps of unnecessary conversion:
+ From ASCII to UTF32, then from UTF32 to ASCII.
+
+ A better way to handle these situations is to pass ASCII
+ representation directly from the source to the destination.
+
+ This is why val_str_ascii() introduced.
+
+ RETURN
+ Similar to val_str()
+ */
+ virtual String *val_str_ascii(String *str);
+
/*
Returns the val_str() value converted to the given character set.
*/
@@ -990,11 +1095,11 @@ public:
/* Called for items that really have to be split */
void split_sum_func2(THD *thd, Item **ref_pointer_array, List<Item> &fields,
Item **ref, bool skip_registered);
- virtual bool get_date(MYSQL_TIME *ltime,uint fuzzydate);
+ virtual bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
bool get_time(MYSQL_TIME *ltime)
{ return get_date(ltime, TIME_TIME_ONLY); }
bool get_seconds(ulonglong *sec, ulong *sec_part);
- virtual bool get_date_result(MYSQL_TIME *ltime,uint fuzzydate)
+ virtual bool get_date_result(MYSQL_TIME *ltime, ulonglong fuzzydate)
{ return get_date(ltime,fuzzydate); }
/*
The method allows to determine nullness of a complex expression
@@ -1042,6 +1147,16 @@ public:
static CHARSET_INFO *default_charset();
virtual CHARSET_INFO *compare_collation() { return NULL; }
+ /*
+ For backward compatibility, to make numeric
+ data types return "binary" charset in client-side metadata.
+ */
+ virtual CHARSET_INFO *charset_for_protocol(void) const
+ {
+ return result_type() == STRING_RESULT ? collation.collation :
+ &my_charset_bin;
+ };
+
virtual bool walk(Item_processor processor, bool walk_subquery, uchar *arg)
{
return (this->*processor)(arg);
@@ -1079,6 +1194,15 @@ public:
(*traverser)(this, arg);
}
+ /*
+ This is used to get the most recent version of any function in
+ an item tree. The version is the version where a MySQL function
+ was introduced in. So any function which is added should use
+ this function and set the int_arg to maximum of the input data
+ and their own version info.
+ */
+ virtual bool intro_version(uchar *int_arg) { return 0; }
+
virtual bool remove_dependence_processor(uchar * arg) { return 0; }
virtual bool remove_fixed(uchar * arg) { fixed= 0; return 0; }
virtual bool cleanup_processor(uchar *arg);
@@ -1124,6 +1248,10 @@ public:
is passed as uchar *arg.
*/
virtual bool register_field_in_bitmap(uchar *arg) { return 0; }
+
+ bool cache_const_expr_analyzer(uchar **arg);
+ Item* cache_const_expr_transformer(uchar *arg);
+
/*
Check if a partition function is allowed
SYNOPSIS
@@ -1317,22 +1445,33 @@ public:
{
return 0;
}
+ /**
+ Check whether this and the given item has compatible comparison context.
+ Used by the equality propagation. See Item_field::equal_fields_propagator.
- /*
+ @return
+ TRUE if the context is the same
+ FALSE otherwise.
+ */
+ inline bool has_compatible_context(Item *item) const
+ {
+ return cmp_context == IMPOSSIBLE_RESULT || item->cmp_context == cmp_context;
+ }
+ /**
Test whether an expression is expensive to compute. Used during
optimization to avoid computing expensive expressions during this
phase. Also used to force temp tables when sorting on expensive
functions.
- TODO:
+ @todo
Normally we should have a method:
cost Item::execution_cost(),
where 'cost' is either 'double' or some structure of various cost
parameters.
- NOTE
- This function is now used to prevent evaluation of materialized IN
- subquery predicates before it is allowed. grep for
- DontEvaluateMaterializedSubqueryTooEarly to see the uses.
+ @note
+ This function is now used to prevent evaluation of expensive subquery
+ predicates during the optimization phase. It also prevents evaluation
+ of predicates that are not computable at this moment.
*/
virtual bool is_expensive()
{
@@ -1344,13 +1483,31 @@ public:
{ return Field::GEOM_GEOMETRY; };
String *check_well_formed_result(String *str, bool send_error= 0);
bool eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs);
+ 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)
+ {
+ 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);
}
+ /*
+ Return TRUE if the item points to a column of an outer-joined table.
+ */
+ virtual bool is_outer_field() const { DBUG_ASSERT(fixed); return FALSE; }
+
+ /**
+ Checks if this item or any of its decendents contains a subquery.
+ */
+ virtual bool has_subquery() const { return with_subselect; }
+
Item* set_expr_cache(THD *thd);
- virtual Item *get_cached_item() { return NULL; }
virtual Item_equal *get_item_equal() { return NULL; }
virtual void set_item_equal(Item_equal *item_eq) {};
@@ -1384,11 +1541,6 @@ public:
virtual void get_cache_parameters(List<Item> &parameters) { };
virtual void mark_as_condition_AND_part(TABLE_LIST *embedding) {};
-
- /**
- Checks if this item or any of its decendents contains a subquery.
- */
- virtual bool has_subquery() const { return with_subselect; }
};
@@ -1424,10 +1576,8 @@ public:
Field_enumerator() {} /* Remove gcc warning */
};
-
class sp_head;
-
class Item_basic_constant :public Item
{
table_map used_table_map;
@@ -1543,6 +1693,13 @@ class Item_splocal :public Item_sp_variable,
Item_result m_result_type;
enum_field_types m_field_type;
public:
+ /*
+ If this variable is a parameter in LIMIT clause.
+ Used only during NAME_CONST substitution, to not append
+ NAME_CONST to the resulting query and thus not break
+ the slave.
+ */
+ bool limit_clause_param;
/*
Position of this reference to SP variable in the statement (the
statement itself is in sp_instr_stmt::m_query).
@@ -1728,12 +1885,44 @@ bool agg_item_set_converter(DTCollation &coll, const char *fname,
Item **args, uint nargs, uint flags, int item_sep);
bool agg_item_charsets(DTCollation &c, const char *name,
Item **items, uint nitems, uint flags, int item_sep);
+inline bool
+agg_item_charsets_for_string_result(DTCollation &c, const char *name,
+ 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_item_charsets(c, name, items, nitems, flags, item_sep);
+}
+inline bool
+agg_item_charsets_for_comparison(DTCollation &c, const char *name,
+ 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_item_charsets(c, name, items, nitems, flags, item_sep);
+}
+inline bool
+agg_item_charsets_for_string_result_with_comparison(DTCollation &c,
+ const char *name,
+ 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_item_charsets(c, name, items, nitems, flags, item_sep);
+}
class Item_num: public Item_basic_constant
{
public:
- Item_num() {} /* Remove gcc warning */
+ Item_num() { collation.set_numeric(); } /* Remove gcc warning */
virtual Item_num *neg()= 0;
Item *safe_charset_converter(CHARSET_INFO *tocs);
bool check_partition_func_processor(uchar *int_arg) { return FALSE;}
@@ -1829,6 +2018,8 @@ public:
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(Send_field *tmp_field);
+ CHARSET_INFO *charset_for_protocol(void) const
+ { return field->charset_for_protocol(); }
};
@@ -1907,8 +2098,8 @@ public:
longlong val_int_endpoint(bool left_endp, bool *incl_endp);
Field *get_tmp_table_field() { return result_field; }
Field *tmp_table_field(TABLE *t_arg) { return result_field; }
- bool get_date(MYSQL_TIME *ltime,uint fuzzydate);
- bool get_date_result(MYSQL_TIME *ltime,uint fuzzydate);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool get_date_result(MYSQL_TIME *ltime,ulonglong fuzzydate);
bool is_null() { return field->is_null(); }
void update_null_value();
void update_table_bitmaps()
@@ -1958,11 +2149,18 @@ public:
int fix_outer_field(THD *thd, Field **field, Item **reference);
virtual Item *update_value_transformer(uchar *select_arg);
virtual void print(String *str, enum_query_type query_type);
+ bool is_outer_field() const
+ {
+ DBUG_ASSERT(fixed);
+ return field->table->pos_in_table_list->outer_join;
+ }
Field::geometry_type get_geometry_type() const
{
DBUG_ASSERT(field_type() == MYSQL_TYPE_GEOMETRY);
return field->get_geometry_type();
}
+ 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;
@@ -2023,7 +2221,8 @@ public:
/* Item represents one placeholder ('?') of prepared statement */
-class Item_param :public Item
+class Item_param :public Item,
+ private Settable_routine_parameter
{
char cnvbuf[MAX_FIELD_WIDTH];
String cnvstr;
@@ -2104,13 +2303,14 @@ public:
longlong val_int();
my_decimal *val_decimal(my_decimal*);
String *val_str(String*);
- bool get_date(MYSQL_TIME *tm, uint fuzzydate);
+ bool get_date(MYSQL_TIME *tm, ulonglong fuzzydate);
int save_in_field(Field *field, bool no_conversions);
void set_null();
void set_int(longlong i, uint32 max_length_arg);
void set_double(double i);
void set_decimal(const char *str, ulong length);
+ void set_decimal(const my_decimal *dv);
bool set_str(const char *str, ulong length);
bool set_longdata(const char *str, ulong length);
void set_time(MYSQL_TIME *tm, timestamp_type type, uint32 max_length_arg);
@@ -2161,6 +2361,24 @@ public:
bool limit_clause_param;
void set_param_type_and_swap_value(Item_param *from);
+private:
+ virtual inline Settable_routine_parameter *
+ get_settable_routine_parameter()
+ {
+ return this;
+ }
+
+ virtual bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
+
+ virtual void set_out_param_info(Send_field *info);
+
+public:
+ virtual const Send_field *get_out_param_info() const;
+
+ virtual void make_field(Send_field *field);
+
+private:
+ Send_field *m_out_param_info;
};
@@ -2481,7 +2699,7 @@ double_from_string_with_check(CHARSET_INFO *cs, const char *cptr,
class Item_static_string_func :public Item_string
{
const char *func_name;
- public:
+public:
Item_static_string_func(const char *name_par, const char *str, uint length,
CHARSET_INFO *cs,
Derivation dv= DERIVATION_COERCIBLE)
@@ -2543,7 +2761,7 @@ public:
/**
Item_empty_string -- is a utility class to put an item into List<Item>
- which is then used in protocol.send_fields() when sending SHOW output to
+ which is then used in protocol.send_result_set_metadata() when sending SHOW output to
the client.
*/
@@ -2571,36 +2789,112 @@ public:
};
-class Item_hex_string: public Item_basic_constant
+/**
+ Item_hex_constant -- a common class for hex literals: X'HHHH' and 0xHHHH
+*/
+class Item_hex_constant: public Item_basic_constant
{
+private:
+ void hex_string_init(const char *str, uint str_length);
public:
- Item_hex_string();
- Item_hex_string(const char *str,uint str_length);
+ Item_hex_constant()
+ {
+ hex_string_init("", 0);
+ }
+ Item_hex_constant(const char *str, uint str_length)
+ {
+ hex_string_init(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; }
+ virtual Item *safe_charset_converter(CHARSET_INFO *tocs);
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *arg) { return FALSE;}
+ bool basic_const_item() const { return 1; }
+ bool eq(const Item *item, bool binary_cmp) const;
+ String *val_str(String*) { DBUG_ASSERT(fixed == 1); return &str_value; }
+};
+
+
+/**
+ Item_hex_hybrid -- is a class implementing 0xHHHH literals, e.g.:
+ SELECT 0x3132;
+ They can behave as numbers and as strings depending on context.
+*/
+class Item_hex_hybrid: public Item_hex_constant
+{
+public:
+ Item_hex_hybrid(): Item_hex_constant() {}
+ Item_hex_hybrid(const char *str, uint str_length):
+ Item_hex_constant(str, str_length) {}
double val_real()
{
DBUG_ASSERT(fixed == 1);
- return (double) (ulonglong) Item_hex_string::val_int();
+ return (double) (ulonglong) Item_hex_hybrid::val_int();
}
longlong val_int();
- bool basic_const_item() const { return 1; }
- String *val_str(String*) { DBUG_ASSERT(fixed == 1); return &str_value; }
- my_decimal *val_decimal(my_decimal *);
+ my_decimal *val_decimal(my_decimal *decimal_value)
+ {
+ // following assert is redundant, because fixed=1 assigned in constructor
+ DBUG_ASSERT(fixed == 1);
+ ulonglong value= (ulonglong) Item_hex_hybrid::val_int();
+ int2my_decimal(E_DEC_FATAL_ERROR, value, TRUE, decimal_value);
+ return decimal_value;
+ }
int save_in_field(Field *field, bool no_conversions);
- enum Item_result result_type () const { return STRING_RESULT; }
enum Item_result cast_to_int_type() const { return INT_RESULT; }
- enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
- virtual void print(String *str, enum_query_type query_type);
- bool eq(const Item *item, bool binary_cmp) const;
- virtual Item *safe_charset_converter(CHARSET_INFO *tocs);
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
- bool check_vcol_func_processor(uchar *arg) { return FALSE;}
-private:
- void hex_string_init(const char *str, uint str_length);
+ void print(String *str, enum_query_type query_type);
+};
+
+
+/**
+ Item_hex_string -- is a class implementing X'HHHH' literals, e.g.:
+ SELECT X'3132';
+ Unlike Item_hex_hybrid, X'HHHH' literals behave as strings in all contexts.
+ X'HHHH' are also used in replication of string constants in case of
+ "dangerous" charsets (sjis, cp932, big5, gbk) who can have backslash (0x5C)
+ as the second byte of a multi-byte character, so using '\' escaping for
+ these charsets is not desirable.
+*/
+class Item_hex_string: public Item_hex_constant
+{
+public:
+ Item_hex_string(): Item_hex_constant() {}
+ Item_hex_string(const char *str, uint str_length):
+ Item_hex_constant(str, str_length) {}
+ longlong val_int()
+ {
+ DBUG_ASSERT(fixed == 1);
+ return longlong_from_string_with_check(str_value.charset(),
+ str_value.ptr(),
+ str_value.ptr()+
+ str_value.length());
+ }
+ double val_real()
+ {
+ DBUG_ASSERT(fixed == 1);
+ return double_from_string_with_check(str_value.charset(),
+ str_value.ptr(),
+ str_value.ptr() +
+ str_value.length());
+ }
+ my_decimal *val_decimal(my_decimal *decimal_value)
+ {
+ return val_decimal_from_string(decimal_value);
+ }
+ int save_in_field(Field *field, bool no_conversions)
+ {
+ field->set_notnull();
+ 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);
};
-class Item_bin_string: public Item_hex_string
+class Item_bin_string: public Item_hex_hybrid
{
public:
Item_bin_string(const char *str,uint str_length);
@@ -2701,7 +2995,7 @@ public:
bool val_bool();
String *val_str(String* tmp);
bool is_null();
- bool get_date(MYSQL_TIME *ltime,uint fuzzydate);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
double val_result();
longlong val_int_result();
String *str_result(String* tmp);
@@ -2797,6 +3091,13 @@ public:
{
return trace_unsupported_by_check_vcol_func_processor("ref");
}
+ bool basic_const_item() const { return ref && (*ref)->basic_const_item(); }
+ bool is_outer_field() const
+ {
+ DBUG_ASSERT(fixed);
+ DBUG_ASSERT(ref);
+ return (*ref)->is_outer_field();
+ }
/**
Checks if the item tree that ref points to contains a subquery.
@@ -2838,7 +3139,7 @@ public:
my_decimal *val_decimal(my_decimal *);
bool val_bool();
bool is_null();
- bool get_date(MYSQL_TIME *ltime,uint fuzzydate);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
virtual Ref_Type ref_type() { return DIRECT_REF; }
};
@@ -2876,9 +3177,8 @@ public:
};
-class Expression_cache;
class Item_cache;
-
+class Expression_cache;
/**
The objects of this class can store its values in an expression cache.
@@ -2927,13 +3227,8 @@ public:
my_decimal *val_decimal(my_decimal *);
bool val_bool();
bool is_null();
- bool get_date(MYSQL_TIME *ltime, uint fuzzydate);
- bool send(Protocol *protocol, String *buffer)
- {
- if (result_field)
- return protocol->store(result_field);
- return Item::send(protocol, buffer);
- }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ bool send(Protocol *protocol, String *buffer);
void save_org_in_field(Field *field)
{
save_val(field);
@@ -3121,7 +3416,7 @@ public:
else
return Item_direct_ref::is_null();
}
- bool get_date(MYSQL_TIME *ltime, uint fuzzydate)
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
if (check_null_ref())
{
@@ -3130,12 +3425,7 @@ public:
}
return Item_direct_ref::get_date(ltime, fuzzydate);
}
- bool send(Protocol *protocol, String *buffer)
- {
- if (check_null_ref())
- return protocol->store_null();
- return Item_direct_ref::send(protocol, buffer);
- }
+ bool send(Protocol *protocol, String *buffer);
void save_org_in_field(Field *field)
{
if (check_null_ref())
@@ -3212,7 +3502,6 @@ public:
return (*ref)->const_item() ? 0 : OUTER_REF_TABLE_BIT;
}
table_map not_null_tables() const { return 0; }
-
virtual Ref_Type ref_type() { return OUTER_REF; }
bool check_inner_refs_processor(uchar * arg);
};
@@ -3246,7 +3535,7 @@ public:
String* val_str(String* s);
my_decimal *val_decimal(my_decimal *);
bool val_bool();
- bool get_date(MYSQL_TIME *ltime, uint fuzzydate);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
virtual void print(String *str, enum_query_type query_type);
table_map used_tables() const;
};
@@ -3289,6 +3578,7 @@ public:
#include "item_timefunc.h"
#include "item_subselect.h"
#include "item_xmlfunc.h"
+#include "item_create.h"
#endif
/**
@@ -3346,6 +3636,8 @@ protected:
cached_field_type= item->field_type();
cached_result_type= item->result_type();
unsigned_flag= item->unsigned_flag;
+ fixed= item->fixed;
+ collation.set(item->collation);
}
public:
@@ -3647,6 +3939,7 @@ public:
return arg->walk(processor, walk_subquery, args) ||
(this->*processor)(args);
}
+ bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
bool check_vcol_func_processor(uchar *arg)
{
return trace_unsupported_by_check_vcol_func_processor("values");
@@ -3654,18 +3947,6 @@ public:
};
-/*
- We need this two enums here instead of sql_lex.h because
- at least one of them is used by Item_trigger_field interface.
-
- Time when trigger is invoked (i.e. before or after row actually
- inserted/updated/deleted).
-*/
-enum trg_action_time_type
-{
- TRG_ACTION_BEFORE= 0, TRG_ACTION_AFTER= 1, TRG_ACTION_MAX
-};
-
class Table_triggers_list;
/*
@@ -3757,7 +4038,7 @@ private:
/**
@todo
Implement the is_null() method for this class. Currently calling is_null()
- on any Item_cache object resolves to Item::is_null(), which reutns FALSE
+ on any Item_cache object resolves to Item::is_null(), which returns FALSE
for any value.
*/
@@ -3783,7 +4064,8 @@ protected:
bool value_cached;
public:
Item_cache():
- example(0), used_table_map(0), cached_field(0), cached_field_type(MYSQL_TYPE_STRING),
+ example(0), used_table_map(0), cached_field(0),
+ cached_field_type(MYSQL_TYPE_STRING),
value_cached(0)
{
fixed= 1;
@@ -3791,7 +4073,8 @@ public:
null_value= 1;
}
Item_cache(enum_field_types field_type_arg):
- example(0), used_table_map(0), cached_field(0), cached_field_type(field_type_arg),
+ example(0), used_table_map(0), cached_field(0),
+ cached_field_type(field_type_arg),
value_cached(0)
{
fixed= 1;
@@ -3832,20 +4115,24 @@ public:
{
return trace_unsupported_by_check_vcol_func_processor("cache");
}
-
- /**
- If this item caches a field value, return pointer to underlying field.
-
- @return Pointer to field, or NULL if this is not a cache for a field value.
- */
- Field* field() { return cached_field; }
+ /**
+ Check if saved item has a non-NULL value.
+ Will cache value of saved item if not already done.
+ @return TRUE if cached value is non-NULL.
+ */
+ bool has_value()
+ {
+ return (value_cached || cache_value()) && !null_value;
+ }
virtual void store(Item *item);
virtual bool cache_value()= 0;
+ bool basic_const_item() const
+ { return test(example && example->basic_const_item());}
+ virtual void clear() { null_value= TRUE; value_cached= FALSE; }
bool is_null() { return null_value; }
virtual bool is_expensive()
{
- DBUG_ASSERT(example);
if (value_cached)
return false;
return example->is_expensive();
@@ -3858,6 +4145,12 @@ public:
return example->is_expensive_processor(arg);
}
virtual void set_null();
+ bool walk(Item_processor processor, bool walk_subquery, uchar *arg)
+ {
+ if (example && example->walk(processor, walk_subquery, arg))
+ return TRUE;
+ return (this->*processor)(arg);
+ }
};
@@ -3884,21 +4177,16 @@ public:
class Item_cache_temporal: public Item_cache_int
{
public:
- Item_cache_temporal(enum_field_types field_type_arg):
- Item_cache_int(field_type_arg)
- {
- if (mysql_type_to_time_type(cached_field_type) == MYSQL_TIMESTAMP_ERROR)
- cached_field_type= MYSQL_TYPE_DATETIME;
- }
-
+ Item_cache_temporal(enum_field_types field_type_arg);
String* val_str(String *str);
my_decimal *val_decimal(my_decimal *);
longlong val_int();
longlong val_temporal_packed();
double val_real();
bool cache_value();
- bool get_date(MYSQL_TIME *ltime, uint fuzzydate);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
int save_in_field(Field *field, bool no_conversions);
+ 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
@@ -3913,6 +4201,7 @@ public:
}
};
+
class Item_cache_real: public Item_cache
{
double value;
@@ -3953,12 +4242,13 @@ class Item_cache_str: public Item_cache
public:
Item_cache_str(const Item *item) :
- Item_cache(), value(0),
+ Item_cache(item->field_type()), value(0),
is_varbinary(item->type() == FIELD_ITEM &&
- ((const Item_field *) item)->field->type() ==
- MYSQL_TYPE_VARCHAR &&
+ cached_field_type == MYSQL_TYPE_VARCHAR &&
!((const Item_field *) item)->field->has_charset())
- {}
+ {
+ collation.set(const_cast<DTCollation&>(item->collation));
+ }
double val_real();
longlong val_int();
String* val_str(String *);
@@ -4088,3 +4378,91 @@ extern Cached_item *new_Cached_item(THD *thd, Item *item,
extern Item_result item_cmp_type(Item_result a,Item_result b);
extern void resolve_const_item(THD *thd, Item **ref, Item *cmp_item);
extern int stored_field_cmp_to_item(THD *thd, Field *field, Item *item);
+
+extern const String my_null_string;
+
+/**
+ Interface for Item iterator
+*/
+
+class Item_iterator
+{
+public:
+ /**
+ Shall set this iterator to the position before the first item
+
+ @note
+ This method also may perform some other initialization actions like
+ allocation of certain resources.
+ */
+ virtual void open()= 0;
+ /**
+ Shall return the next Item (or NULL if there is no next item) and
+ move pointer to position after it.
+ */
+ virtual Item *next()= 0;
+ /**
+ Shall force iterator to free resources (if it holds them)
+
+ @note
+ One should not use the iterator without open() call after close()
+ */
+ virtual void close()= 0;
+
+ virtual ~Item_iterator() {}
+};
+
+
+/**
+ Item iterator over List_iterator_fast for Item references
+*/
+
+class Item_iterator_ref_list: public Item_iterator
+{
+ List_iterator<Item*> list;
+public:
+ Item_iterator_ref_list(List_iterator<Item*> &arg_list):
+ list(arg_list) {}
+ void open() { list.rewind(); }
+ Item *next() { return *(list++); }
+ void close() {}
+};
+
+
+/**
+ Item iterator over List_iterator_fast for Items
+*/
+
+class Item_iterator_list: public Item_iterator
+{
+ List_iterator<Item> list;
+public:
+ Item_iterator_list(List_iterator<Item> &arg_list):
+ list(arg_list) {}
+ void open() { list.rewind(); }
+ Item *next() { return (list++); }
+ void close() {}
+};
+
+
+/**
+ Item iterator over Item interface for rows
+*/
+
+class Item_iterator_row: public Item_iterator
+{
+ Item *base_item;
+ uint current;
+public:
+ Item_iterator_row(Item *base) : base_item(base), current(0) {}
+ void open() { current= 0; }
+ Item *next()
+ {
+ if (current >= base_item->cols())
+ return NULL;
+ return base_item->element_index(current++);
+ }
+ void close() {}
+};
+
+#endif /* SQL_ITEM_INCLUDED */
diff --git a/sql/item_buff.cc b/sql/item_buff.cc
index f09ca08cc84..86e0fd32774 100644
--- a/sql/item_buff.cc
+++ b/sql/item_buff.cc
@@ -12,8 +12,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@@ -23,7 +22,14 @@
Buffers to save and compare item values
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+/*
+ It is necessary to include set_var.h instead of item.h because there
+ are dependencies on include order for set_var.h and item.h. This
+ will be resolved later.
+*/
+#include "sql_class.h" // THD
+#include "set_var.h" // Cached_item, Cached_item_field, ...
/**
Create right type of Cached_item for an item.
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index d40b19a7f08..998cb1cbd64 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2009, 2014, Monty Program Ab.
+ Copyright (c) 2009, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,9 +26,12 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
#include <m_ctype.h>
#include "sql_select.h"
+#include "sql_parse.h" // check_stack_overrun
+#include "sql_time.h" // make_truncated_value_warning
+#include "sql_base.h" // dynamic_column_error_message
static Item_result item_store_type(Item_result a, Item *item,
my_bool unsigned_flag)
@@ -238,15 +241,15 @@ static uint collect_cmp_types(Item **items, uint nitems, bool skip_nulls= FALSE)
items[i]->cmp_type() == ROW_RESULT) &&
cmp_row_type(items[0], items[i]))
return 0;
- found_types|= 1<< (uint)item_cmp_type(left_result,
- items[i]->cmp_type());
+ found_types|= 1U << (uint)item_cmp_type(left_result,
+ items[i]->cmp_type());
}
/*
Even if all right-hand items are NULLs and we are skipping them all, we need
at least one type bit in the found_type bitmask.
*/
if (skip_nulls && !found_types)
- found_types= 1 << (uint)left_result;
+ found_types= 1U << (uint)left_result;
return found_types;
}
@@ -453,20 +456,15 @@ static bool convert_const_to_int(THD *thd, Item_field *field_item,
But we still convert it if it is compared with a Field_year,
as YEAR(2) may change the value of an integer when converting it
to an integer (say, 0 to 70).
-
- As a special hack, to avoid reevaluation of stored routines
- where 5.2 didn't reevaluate them, we "convert" for BIGINT too.
- In 5.5 it isn't necessary, as it caches constant expressions correctly.
*/
if ((*item)->cmp_type() == INT_RESULT &&
- field_item->field_type() != MYSQL_TYPE_YEAR &&
- field_item->field_type() != MYSQL_TYPE_LONGLONG)
+ field_item->field_type() != MYSQL_TYPE_YEAR)
return 1;
- if ((*item)->const_item())
+ if ((*item)->const_item() && !(*item)->is_expensive())
{
TABLE *table= field->table;
- ulong orig_sql_mode= thd->variables.sql_mode;
+ ulonglong orig_sql_mode= thd->variables.sql_mode;
enum_check_fields orig_count_cuted_fields= thd->count_cuted_fields;
my_bitmap_map *old_maps[2];
ulonglong UNINIT_VAR(orig_field_val); /* original field value if valid */
@@ -492,13 +490,24 @@ static bool convert_const_to_int(THD *thd, Item_field *field_item,
!(field->table->status & STATUS_NO_RECORD));
if (save_field_value)
orig_field_val= field->val_int();
- if (!(*item)->is_null() && !(*item)->save_in_field(field, 1))
+ if (!(*item)->save_in_field(field, 1) && !field->is_null())
{
- Item *tmp= new Item_int_with_ref(field->val_int(), *item,
- test(field->flags & UNSIGNED_FLAG));
- if (tmp)
- thd->change_item_tree(item, tmp);
- result= 1; // Item was replaced
+ int field_cmp= 0;
+ // If item is a decimal value, we must reject it if it was truncated.
+ if (field->type() == MYSQL_TYPE_LONGLONG)
+ {
+ field_cmp= stored_field_cmp_to_item(thd, field, *item);
+ DBUG_PRINT("info", ("convert_const_to_int %d", field_cmp));
+ }
+
+ if (0 == field_cmp)
+ {
+ Item *tmp= new Item_int_with_ref(field->val_int(), *item,
+ test(field->flags & UNSIGNED_FLAG));
+ if (tmp)
+ thd->change_item_tree(item, tmp);
+ result= 1; // Item was replaced
+ }
}
/* Restore the original field value. */
if (save_field_value)
@@ -540,9 +549,9 @@ void Item_bool_func2::fix_length_and_dec()
*/
DTCollation coll;
- if (args[0]->result_type() == STRING_RESULT &&
- args[1]->result_type() == STRING_RESULT &&
- agg_arg_charsets(coll, args, 2, MY_COLL_CMP_CONV, 1))
+ if (args[0]->cmp_type() == STRING_RESULT &&
+ args[1]->cmp_type() == STRING_RESULT &&
+ agg_arg_charsets_for_comparison(coll, args, 2))
return;
args[0]->cmp_context= args[1]->cmp_context=
@@ -583,6 +592,7 @@ int Arg_comparator::set_compare_func(Item_result_field *item, Item_result type)
switch (type) {
case TIME_RESULT:
+ cmp_collation.collation= &my_charset_numeric;
break;
case ROW_RESULT:
{
@@ -714,12 +724,14 @@ bool get_mysql_time_from_str(THD *thd, String *str, timestamp_type warn_type,
int error;
enum_mysql_timestamp_type timestamp_type;
int flags= TIME_FUZZY_DATES | MODE_INVALID_DATES;
+ ErrConvString err(str);
if (warn_type == MYSQL_TIMESTAMP_TIME)
flags|= TIME_TIME_ONLY;
timestamp_type=
- str_to_datetime(str->ptr(), str->length(), l_time, flags, &error);
+ str_to_datetime(str->charset(), str->ptr(), str->length(),
+ l_time, flags, &error);
if (timestamp_type > MYSQL_TIMESTAMP_ERROR)
/*
@@ -735,8 +747,7 @@ bool get_mysql_time_from_str(THD *thd, String *str, timestamp_type warn_type,
if (error > 0)
make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- str->ptr(), str->length(),
- warn_type, warn_name);
+ &err, warn_type, warn_name);
return value;
}
@@ -812,7 +823,7 @@ Item** Arg_comparator::cache_converted_constant(THD *thd_arg, Item **value,
Also, get_datetime_value creates Item_cache internally.
Unless fixed, we should not do it here.
*/
- if (!thd->lex->is_ps_or_view_context_analysis() &&
+ if (!thd_arg->lex->is_ps_or_view_context_analysis() &&
(*value)->const_item() && type != (*value)->result_type() &&
type != TIME_RESULT)
{
@@ -898,18 +909,9 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
}
if ((*is_null= item->null_value))
return ~(ulonglong) 0;
- if (cache_arg && item->const_item() && item->type() != Item::CACHE_ITEM)
+ if (cache_arg && item->const_item() &&
+ !(item->type() == Item::CACHE_ITEM && item->cmp_type() == TIME_RESULT))
{
- /*
- cache the packed datetime value in the Item_cache object.
- Because the packed datetime value is longlong, we use Item_cache_int,
- and it has result_type() == INT_RESULT.
- But we create it to have field_type() == MYSQL_TYPE_TIME (or
- MYSQL_TIMESTAMP_DATE or MYSQL_TYPE_DATETIME), and thus it will have
- cmp_type() == TIME_RESULT.
- As no other item can have this combination of cmp_type() and
- result_type(), it allows us to identify our cache items.
- */
Query_arena backup;
Query_arena *save_arena= thd->switch_to_arena_for_cached_items(&backup);
Item_cache_temporal *cache= new Item_cache_temporal(f_type);
@@ -1449,7 +1451,11 @@ bool Item_in_optimizer::fix_left(THD *thd, Item **ref)
if (cache->cols() == 1)
{
DBUG_ASSERT(args[0]->type() != ROW_ITEM);
- if ((used_tables_cache= args[0]->used_tables()))
+ /*
+ Note: there can be cases when used_tables()==0 && !const_item(). See
+ Item_sum::update_used_tables for details.
+ */
+ if ((used_tables_cache= args[0]->used_tables()) || !args[0]->const_item())
cache->set_used_tables(OUTER_REF_TABLE_BIT);
else
cache->set_used_tables(0);
@@ -1467,9 +1473,11 @@ bool Item_in_optimizer::fix_left(THD *thd, Item **ref)
"SUBQUERY in ROW in left expression of IN/ALL/ANY");
DBUG_RETURN(1);
}
- if (args[0]->element_index(i)->used_tables())
+ Item *element=args[0]->element_index(i);
+ if (element->used_tables() || !element->const_item())
{
- ((Item_cache *)cache->element_index(i))->set_used_tables(OUTER_REF_TABLE_BIT);
+ ((Item_cache *)cache->element_index(i))->
+ set_used_tables(OUTER_REF_TABLE_BIT);
cache->set_used_tables(OUTER_REF_TABLE_BIT);
}
else
@@ -1575,8 +1583,7 @@ void Item_in_optimizer::get_cache_parameters(List<Item> &parameters)
args[1]->get_cache_parameters(parameters);
}
-
-/*
+/**
The implementation of optimized \<outer expression\> [NOT] IN \<subquery\>
predicates. The implementation works as follows.
@@ -1645,7 +1652,7 @@ void Item_in_optimizer::get_cache_parameters(List<Item> &parameters)
@see Item_in_subselect::val_bool()
@see Item_is_not_null_test::val_int()
- */
+*/
longlong Item_in_optimizer::val_int()
{
@@ -1794,7 +1801,7 @@ Item *Item_in_optimizer::transform(Item_transformer transformer, uchar *argument
{
Item *new_item;
- DBUG_ASSERT(!current_thd->is_stmt_prepare());
+ DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare());
DBUG_ASSERT(arg_count == 2);
/* Transform the left IN operand. */
@@ -2200,7 +2207,7 @@ void Item_func_between::fix_length_and_dec()
if ( agg_cmp_type(&cmp_type, args, 3))
return;
if (cmp_type == STRING_RESULT &&
- agg_arg_charsets(cmp_collation, args, 3, MY_COLL_CMP_CONV, 1))
+ agg_arg_charsets_for_comparison(cmp_collation, args, 3))
return;
/*
@@ -2222,14 +2229,10 @@ void Item_func_between::fix_length_and_dec()
if (field_item->field_type() == MYSQL_TYPE_LONGLONG ||
field_item->field_type() == MYSQL_TYPE_YEAR)
{
- /*
- The following can't be recoded with || as convert_const_to_int
- changes the argument
- */
- if (convert_const_to_int(thd, field_item, &args[1]))
- cmp_type=INT_RESULT;
- if (convert_const_to_int(thd, field_item, &args[2]))
- cmp_type=INT_RESULT;
+ 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)
+ cmp_type=INT_RESULT; // Works for all types.
}
}
}
@@ -2390,6 +2393,7 @@ void Item_func_between::print(String *str, enum_query_type query_type)
void
Item_func_ifnull::fix_length_and_dec()
{
+ uint32 char_length;
agg_result_type(&cached_result_type, args, 2);
cached_field_type= agg_field_type(args, 2);
maybe_null=args[1]->maybe_null;
@@ -2398,16 +2402,16 @@ Item_func_ifnull::fix_length_and_dec()
if (cached_result_type == DECIMAL_RESULT || cached_result_type == INT_RESULT)
{
- int len0= args[0]->max_length - args[0]->decimals
+ int len0= args[0]->max_char_length() - args[0]->decimals
- (args[0]->unsigned_flag ? 0 : 1);
- int len1= args[1]->max_length - args[1]->decimals
+ int len1= args[1]->max_char_length() - args[1]->decimals
- (args[1]->unsigned_flag ? 0 : 1);
- max_length= max(len0, len1) + decimals + (unsigned_flag ? 0 : 1);
+ char_length= max(len0, len1) + decimals + (unsigned_flag ? 0 : 1);
}
else
- max_length= max(args[0]->max_length, args[1]->max_length);
+ char_length= max(args[0]->max_char_length(), args[1]->max_char_length());
switch (cached_result_type) {
case STRING_RESULT:
@@ -2425,6 +2429,7 @@ Item_func_ifnull::fix_length_and_dec()
case IMPOSSIBLE_RESULT:
DBUG_ASSERT(0);
}
+ fix_char_length(char_length);
}
@@ -2584,45 +2589,55 @@ void Item_func_if::fix_after_pullout(st_select_lex *new_parent, Item **ref)
}
+void Item_func_if::cache_type_info(Item *source)
+{
+ collation.set(source->collation);
+ cached_field_type= source->field_type();
+ cached_result_type= source->result_type();
+ decimals= source->decimals;
+ max_length= source->max_length;
+ maybe_null= source->maybe_null;
+ unsigned_flag= source->unsigned_flag;
+}
+
+
void
Item_func_if::fix_length_and_dec()
{
- maybe_null=args[1]->maybe_null || args[2]->maybe_null;
+ // 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)
+ cached_field_type= MYSQL_TYPE_STRING;
+ return;
+ }
+ if (args[2]->type() == NULL_ITEM)
+ {
+ cache_type_info(args[1]);
+ maybe_null= true;
+ return;
+ }
+
+ agg_result_type(&cached_result_type, args + 1, 2);
+ cached_field_type= agg_field_type(args + 1, 2);
+ maybe_null= args[1]->maybe_null || args[2]->maybe_null;
decimals= max(args[1]->decimals, args[2]->decimals);
unsigned_flag=args[1]->unsigned_flag && args[2]->unsigned_flag;
- enum Item_result arg1_type=args[1]->result_type();
- enum Item_result arg2_type=args[2]->result_type();
- bool null1=args[1]->basic_const_item() && args[1]->null_value;
- bool null2=args[2]->basic_const_item() && args[2]->null_value;
-
- if (null1)
- {
- cached_result_type= arg2_type;
- collation.set(args[2]->collation.collation);
- cached_field_type= args[2]->field_type();
- }
- else if (null2)
+ if (cached_result_type == STRING_RESULT)
{
- cached_result_type= arg1_type;
- collation.set(args[1]->collation.collation);
- cached_field_type= args[1]->field_type();
+ count_string_result_length(cached_field_type, args + 1, 2);
+ return;
}
else
{
- agg_result_type(&cached_result_type, args+1, 2);
- cached_field_type= agg_field_type(args + 1, 2);
- if (cached_result_type == STRING_RESULT)
- {
- count_string_result_length(cached_field_type, args + 1, 2);
- return;
- }
- else
- {
- collation.set(&my_charset_bin); // Number
- }
+ collation.set_numeric(); // Number
}
+ uint32 char_length;
if ((cached_result_type == DECIMAL_RESULT )
|| (cached_result_type == INT_RESULT))
{
@@ -2632,10 +2647,11 @@ Item_func_if::fix_length_and_dec()
int len2= args[2]->max_length - args[2]->decimals
- (args[2]->unsigned_flag ? 0 : 1);
- max_length=max(len1, len2) + decimals + (unsigned_flag ? 0 : 1);
+ char_length= max(len1, len2) + decimals + (unsigned_flag ? 0 : 1);
}
else
- max_length= max(args[1]->max_length, args[2]->max_length);
+ char_length= max(args[1]->max_char_length(), args[2]->max_char_length());
+ fix_char_length(char_length);
}
@@ -2707,13 +2723,13 @@ Item_func_nullif::fix_length_and_dec()
maybe_null=1;
if (args[0]) // Only false if EOM
{
- max_length=args[0]->max_length;
decimals=args[0]->decimals;
unsigned_flag= args[0]->unsigned_flag;
cached_result_type= args[0]->result_type();
if (cached_result_type == STRING_RESULT &&
- agg_arg_charsets(collation, args, arg_count, MY_COLL_CMP_CONV, 1))
+ agg_arg_charsets_for_comparison(collation, args, arg_count))
return;
+ fix_char_length(args[0]->max_char_length());
}
}
@@ -2842,12 +2858,12 @@ Item *Item_func_case::find_item(String *str)
cmp_type= item_cmp_type(left_result_type, args[i]->cmp_type());
DBUG_ASSERT(cmp_type != ROW_RESULT);
DBUG_ASSERT(cmp_items[(uint)cmp_type]);
- if (!(value_added_map & (1<<(uint)cmp_type)))
+ if (!(value_added_map & (1U << (uint)cmp_type)))
{
cmp_items[(uint)cmp_type]->store_value(args[first_expr_num]);
if ((null_value=args[first_expr_num]->null_value))
return else_expr_num != -1 ? args[else_expr_num] : 0;
- value_added_map|= 1<<(uint)cmp_type;
+ value_added_map|= 1U << (uint)cmp_type;
}
if (!cmp_items[(uint)cmp_type]->cmp(args[i]) && !args[i]->null_value)
return args[i + 1];
@@ -2951,9 +2967,7 @@ bool Item_func_case::fix_fields(THD *thd, Item **ref)
buff should match stack usage from
Item_func_case::val_int() -> Item_func_case::find_item()
*/
-#ifndef EMBEDDED_LIBRARY
uchar buff[MAX_FIELD_WIDTH*2+sizeof(String)*2+sizeof(String*)*2+sizeof(double)*2+sizeof(longlong)*2];
-#endif
bool res= Item_func::fix_fields(thd, ref);
/*
Call check_stack_overrun after fix_fields to be sure that stack variable
@@ -2967,7 +2981,7 @@ bool Item_func_case::fix_fields(THD *thd, Item **ref)
void Item_func_case::agg_str_lengths(Item* arg)
{
- set_if_bigger(max_length, arg->max_length);
+ fix_char_length(max(max_char_length(), arg->max_char_length()));
set_if_bigger(decimals, arg->decimals);
unsigned_flag= unsigned_flag && arg->unsigned_flag;
}
@@ -2983,11 +2997,35 @@ void Item_func_case::agg_num_lengths(Item *arg)
}
+/**
+ Check if (*place) and new_value points to different Items and call
+ THD::change_item_tree() if needed.
+
+ This function is a workaround for implementation deficiency in
+ Item_func_case. The problem there is that the 'args' attribute contains
+ Items from different expressions.
+
+ The function must not be used elsewhere and will be remove eventually.
+*/
+
+static void change_item_tree_if_needed(THD *thd,
+ Item **place,
+ Item *new_value)
+{
+ if (*place == new_value)
+ return;
+
+ thd->change_item_tree(place, new_value);
+}
+
+
void Item_func_case::fix_length_and_dec()
{
Item **agg;
uint nagg;
uint found_types= 0;
+ THD *thd= current_thd;
+
if (!(agg= (Item**) sql_alloc(sizeof(Item*)*(ncases+1))))
return;
@@ -3012,21 +3050,31 @@ void Item_func_case::fix_length_and_dec()
{
if (count_string_result_length(cached_field_type, agg, nagg))
return;
+ /*
+ Copy all THEN and ELSE items back to args[] array.
+ Some of the items might have been changed to Item_func_conv_charset.
+ */
+ for (nagg= 0 ; nagg < ncases / 2 ; nagg++)
+ change_item_tree_if_needed(thd, &args[nagg * 2 + 1], agg[nagg]);
+
+ if (else_expr_num != -1)
+ change_item_tree_if_needed(thd, &args[else_expr_num], agg[nagg++]);
}
else
{
- max_length= 0;
- decimals= 0;
+ collation.set_numeric();
+ max_length=0;
+ decimals=0;
unsigned_flag= TRUE;
- for (uint i= 0; i < nagg; i++)
- agg_num_lengths(agg[i]);
+ for (uint i= 0; i < ncases; i+= 2)
+ agg_num_lengths(args[i + 1]);
if (else_expr_num != -1)
agg_num_lengths(args[else_expr_num]);
- max_length= my_decimal_precision_to_length(max_length + decimals, decimals,
+ max_length= my_decimal_precision_to_length_no_truncation(max_length +
+ decimals, decimals,
unsigned_flag);
}
-
-
+
/*
Aggregate first expression and all WHEN expression types
and collations when string comparison
@@ -3037,34 +3085,66 @@ void Item_func_case::fix_length_and_dec()
agg[0]= args[first_expr_num];
left_result_type= agg[0]->cmp_type();
+ /*
+ As the first expression and WHEN expressions
+ are intermixed in args[] array THEN and ELSE items,
+ extract the first expression and all WHEN expressions into
+ a temporary array, to process them easier.
+ */
for (nagg= 0; nagg < ncases/2 ; nagg++)
agg[nagg+1]= args[nagg*2];
nagg++;
if (!(found_types= collect_cmp_types(agg, nagg)))
return;
- if (with_sum_func || current_thd->lex->current_select->group_list.elements)
+
+ Item *date_arg= 0;
+ if (found_types & (1U << TIME_RESULT))
+ date_arg= find_date_time_item(args, arg_count, 0);
+
+ if (found_types & (1U << STRING_RESULT))
{
/*
- See TODO commentary in the setup_copy_fields function:
- item in a group may be wrapped with an Item_copy_string item.
- That item has a STRING_RESULT result type, so we need
- to take this type into account.
+ 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:
+
+ 1. Converter installed for the first expression:
+
+ CASE latin1_item WHEN utf16_item THEN ... END
+
+ is replaced to:
+
+ CASE CONVERT(latin1_item USING utf16) WHEN utf16_item THEN ... END
+
+ 2. Converter installed for the left WHEN item:
+
+ CASE utf16_item WHEN latin1_item THEN ... END
+
+ is replaced to:
+
+ CASE utf16_item WHEN CONVERT(latin1_item USING utf16) THEN ... END
*/
- found_types |= (1 << item_cmp_type(left_result_type, STRING_RESULT));
+ if (agg_arg_charsets_for_comparison(cmp_collation, agg, nagg))
+ return;
+ /*
+ Now copy first expression and all WHEN expressions back to args[]
+ arrray, because some of the items might have been changed to converters
+ (e.g. Item_func_conv_charset, or Item_string for constants).
+ */
+ change_item_tree_if_needed(thd, &args[first_expr_num], agg[0]);
+
+ for (nagg= 0; nagg < ncases / 2; nagg++)
+ change_item_tree_if_needed(thd, &args[nagg * 2], agg[nagg + 1]);
}
- Item *date_arg= 0;
for (i= 0; i <= (uint)TIME_RESULT; i++)
{
- if (found_types & (1 << i) && !cmp_items[i])
+ if (found_types & (1U << i) && !cmp_items[i])
{
DBUG_ASSERT((Item_result)i != ROW_RESULT);
- if ((Item_result)i == STRING_RESULT &&
- agg_arg_charsets(cmp_collation, agg, nagg, MY_COLL_CMP_CONV, 1))
- return;
-
- if ((Item_result)i == TIME_RESULT)
- date_arg= find_date_time_item(args, arg_count, 0);
if (!(cmp_items[i]=
cmp_item::get_comparator((Item_result)i, date_arg,
@@ -3570,6 +3650,7 @@ cmp_item* cmp_item::get_comparator(Item_result type, Item *warn_item,
case DECIMAL_RESULT:
return new cmp_item_decimal;
case TIME_RESULT:
+ DBUG_ASSERT(warn_item);
return new cmp_item_datetime(warn_item);
case IMPOSSIBLE_RESULT:
DBUG_ASSERT(0);
@@ -3883,7 +3964,7 @@ void Item_func_in::fix_length_and_dec()
}
for (i= 0; i <= (uint)TIME_RESULT; i++)
{
- if (found_types & 1 << i)
+ if (found_types & (1U << i))
{
(type_cnt)++;
cmp_type= (Item_result) i;
@@ -3893,7 +3974,7 @@ void Item_func_in::fix_length_and_dec()
if (type_cnt == 1)
{
if (cmp_type == STRING_RESULT &&
- agg_arg_charsets(cmp_collation, args, arg_count, MY_COLL_CMP_CONV, 1))
+ agg_arg_charsets_for_comparison(cmp_collation, args, arg_count))
return;
arg_types_compatible= TRUE;
@@ -3947,7 +4028,7 @@ void Item_func_in::fix_length_and_dec()
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() && cmp_type != INT_RESULT)
+ !thd->lex->is_view_context_analysis() && cmp_type != INT_RESULT)
{
Item_field *field_item= (Item_field*) (args[0]->real_item());
if (field_item->field_type() == MYSQL_TYPE_LONGLONG ||
@@ -4010,17 +4091,15 @@ void Item_func_in::fix_length_and_dec()
}
else
{
+ if (found_types & (1U << TIME_RESULT))
+ date_arg= find_date_time_item(args, arg_count, 0);
+ if (found_types & (1U << STRING_RESULT) &&
+ agg_arg_charsets_for_comparison(cmp_collation, args, arg_count))
+ return;
for (i= 0; i <= (uint) TIME_RESULT; i++)
{
- if (found_types & (1 << i) && !cmp_items[i])
+ if (found_types & (1U << i) && !cmp_items[i])
{
- if ((Item_result)i == STRING_RESULT &&
- agg_arg_charsets(cmp_collation, args, arg_count,
- MY_COLL_CMP_CONV, 1))
- return;
- if ((Item_result)i == TIME_RESULT)
- date_arg= find_date_time_item(args, arg_count, 0);
-
if (!cmp_items[i] && !(cmp_items[i]=
cmp_item::get_comparator((Item_result)i, date_arg,
cmp_collation.collation)))
@@ -4105,12 +4184,12 @@ longlong Item_func_in::val_int()
Item_result cmp_type= item_cmp_type(left_result_type, args[i]->cmp_type());
in_item= cmp_items[(uint)cmp_type];
DBUG_ASSERT(in_item);
- if (!(value_added_map & (1 << (uint)cmp_type)))
+ if (!(value_added_map & (1U << (uint)cmp_type)))
{
in_item->store_value(args[0]);
if ((null_value= args[0]->null_value))
return 0;
- value_added_map|= 1 << (uint)cmp_type;
+ value_added_map|= 1U << (uint)cmp_type;
}
if (!in_item->cmp(args[i]) && !args[i]->null_value)
return (longlong) (!negated);
@@ -4186,9 +4265,7 @@ Item_cond::fix_fields(THD *thd, Item **ref)
DBUG_ASSERT(fixed == 0);
List_iterator<Item> li(list);
Item *item;
-#ifndef EMBEDDED_LIBRARY
uchar buff[sizeof(char*)]; // Max local vars in function
-#endif
not_null_tables_cache= used_tables_cache= 0;
const_item_cache= 1;
@@ -4251,12 +4328,32 @@ Item_cond::fix_fields(THD *thd, Item **ref)
return TRUE; /* purecov: inspected */
used_tables_cache|= item->used_tables();
if (item->const_item())
- and_tables_cache= (table_map) 0;
+ {
+ if (!item->is_expensive() && !cond_has_datetime_is_null(item) &&
+ item->val_int() == 0)
+ {
+ /*
+ This is "... OR false_cond OR ..."
+ In this case, false_cond has no effect on cond_or->not_null_tables()
+ */
+ }
+ else
+ {
+ /*
+ This is "... OR const_cond OR ..."
+ In this case, cond_or->not_null_tables()=0, because the condition
+ const_cond might evaluate to true (regardless of whether some tables
+ were NULL-complemented).
+ */
+ and_tables_cache= (table_map) 0;
+ }
+ }
else
{
table_map tmp_table_map= item->not_null_tables();
not_null_tables_cache|= tmp_table_map;
and_tables_cache&= tmp_table_map;
+
const_item_cache= FALSE;
}
@@ -4284,7 +4381,26 @@ Item_cond::eval_not_null_tables(uchar *opt_arg)
{
table_map tmp_table_map;
if (item->const_item())
- and_tables_cache= (table_map) 0;
+ {
+ if (!item->is_expensive() && !cond_has_datetime_is_null(item) &&
+ item->val_int() == 0)
+ {
+ /*
+ This is "... OR false_cond OR ..."
+ In this case, false_cond has no effect on cond_or->not_null_tables()
+ */
+ }
+ else
+ {
+ /*
+ This is "... OR const_cond OR ..."
+ In this case, cond_or->not_null_tables()=0, because the condition
+ some_cond_or might be true regardless of what tables are
+ NULL-complemented.
+ */
+ and_tables_cache= (table_map) 0;
+ }
+ }
else
{
tmp_table_map= item->not_null_tables();
@@ -4359,7 +4475,7 @@ bool Item_cond::walk(Item_processor processor, bool walk_subquery, uchar *arg)
Item *Item_cond::transform(Item_transformer transformer, uchar *arg)
{
- DBUG_ASSERT(!current_thd->is_stmt_prepare());
+ DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare());
List_iterator<Item> li(list);
Item *item;
@@ -4532,7 +4648,7 @@ void Item_cond::neg_arguments(THD *thd)
if (!(new_item= new Item_func_not(item)))
return; // Fatal OEM error
}
- VOID(li.replace(new_item));
+ (void) li.replace(new_item);
}
}
@@ -4650,7 +4766,6 @@ Item *and_expressions(Item *a, Item *b, Item **org_item)
longlong Item_func_isnull::val_int()
{
DBUG_ASSERT(fixed == 1);
-
if (const_item() && !args[0]->maybe_null)
return 0;
return args[0]->is_null() ? 1: 0;
@@ -4843,8 +4958,8 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref)
}
if (canDoTurboBM)
{
- pattern = first + 1;
pattern_len = (int) len - 2;
+ pattern = thd->strmake(first + 1, pattern_len);
DBUG_PRINT("info", ("Initializing pattern: '%s'", first));
int *suff = (int*) thd->alloc((int) (sizeof(int)*
((pattern_len + 1)*2+
@@ -4867,8 +4982,6 @@ void Item_func_like::cleanup()
Item_bool_func2::cleanup();
}
-#ifdef USE_REGEX
-
/**
@brief Compile regular expression.
@@ -4937,11 +5050,11 @@ Item_func_regex::fix_fields(THD *thd, Item **ref)
return TRUE; /* purecov: inspected */
with_sum_func=args[0]->with_sum_func || args[1]->with_sum_func;
with_field= args[0]->with_field || args[1]->with_field;
- with_subselect|= args[0]->has_subquery() || args[1]->has_subquery();
+ with_subselect= args[0]->has_subquery() || args[1]->has_subquery();
max_length= 1;
decimals= 0;
- if (agg_arg_charsets(cmp_collation, args, 2, MY_COLL_CMP_CONV, 1))
+ if (agg_arg_charsets_for_comparison(cmp_collation, args, 2))
return TRUE;
regex_lib_flags= (cmp_collation.collation->state &
@@ -5022,9 +5135,6 @@ void Item_func_regex::cleanup()
}
-#endif /* USE_REGEX */
-
-
#ifdef LIKE_CMP_TOUPPER
#define likeconv(cs,A) (uchar) (cs)->toupper(A)
#else
@@ -5570,7 +5680,15 @@ void Item_equal::add_const(Item *c, Item *f)
else
{
Item_func_eq *func= new Item_func_eq(c, const_item);
- func->set_cmp_func();
+ if (func->set_cmp_func())
+ {
+ /*
+ Setting a comparison function fails when trying to compare
+ incompatible charsets. Charset compatibility is checked earlier,
+ except for constant subqueries where we may do it here.
+ */
+ return;
+ }
func->quick_fix_field();
cond_false= !func->val_int();
}
@@ -5808,7 +5926,21 @@ void Item_equal::update_const()
Item *item;
while ((item= it++))
{
- if (item->const_item() && !item->is_expensive())
+ if (item->const_item() && !item->is_expensive() &&
+ /*
+ Don't propagate constant status of outer-joined column.
+ Such a constant status here is a result of:
+ a) empty outer-joined table: in this case such a column has a
+ value of NULL; but at the same time other arguments of
+ Item_equal don't have to be NULLs and the value of the whole
+ multiple equivalence expression doesn't have to be NULL or FALSE
+ because of the outer join nature;
+ or
+ b) outer-joined table contains only 1 row: the result of
+ this column is equal to a row field value *or* NULL.
+ Both values are inacceptable as Item_equal constants.
+ */
+ !item->is_outer_field())
{
if (item == equal_items.head())
with_const= TRUE;
@@ -5889,7 +6021,8 @@ void Item_equal::update_used_tables()
{
item->update_used_tables();
used_tables_cache|= item->used_tables();
- const_item_cache&= item->const_item();
+ /* see commentary at Item_equal::update_const() */
+ const_item_cache&= item->const_item() && !item->is_outer_field();
}
}
@@ -5974,7 +6107,7 @@ bool Item_equal::walk(Item_processor processor, bool walk_subquery, uchar *arg)
Item *Item_equal::transform(Item_transformer transformer, uchar *arg)
{
- DBUG_ASSERT(!current_thd->is_stmt_prepare());
+ DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare());
Item *item;
Item_equal_fields_iterator it(*this);
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 176e374b3bd..9504be57a90 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -1,5 +1,7 @@
+#ifndef ITEM_CMPFUNC_INCLUDED
+#define ITEM_CMPFUNC_INCLUDED
/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 2009, 2014, Monty Program Ab.
+ Copyright (c) 2009, 2011, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,6 +23,10 @@
#pragma interface /* gcc class implementation */
#endif
+#include "thr_malloc.h" /* sql_calloc */
+#include "item_func.h" /* Item_int_func, Item_bool_func */
+#include "my_regex.h"
+
extern Item_result item_cmp_type(Item_result a,Item_result b);
class Item_bool_func2;
class Arg_comparator;
@@ -106,7 +112,6 @@ public:
delete [] comparators;
comparators= 0;
}
-
friend class Item_func;
};
@@ -242,7 +247,7 @@ protected:
int result_for_null_param;
public:
Item_in_optimizer(Item *a, Item_in_subselect *b):
- Item_bool_func(a, my_reinterpret_cast(Item *)(b)), cache(0), expr_cache(0),
+ Item_bool_func(a, reinterpret_cast<Item *>(b)), cache(0), expr_cache(0),
save_cache(0), result_for_null_param(UNKNOWN)
{ with_subselect= true; }
bool fix_fields(THD *, Item **);
@@ -367,9 +372,9 @@ public:
:Item_int_func(a,b), cmp(tmp_arg, tmp_arg+1),
abort_on_null(FALSE) { sargable= TRUE; }
void fix_length_and_dec();
- void set_cmp_func()
+ int set_cmp_func()
{
- cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, TRUE);
+ return cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, TRUE);
}
optimize_type select_optimize() const { return OPTIMIZE_OP; }
virtual enum Functype rev_functype() const { return UNKNOWN_FUNC; }
@@ -698,6 +703,11 @@ public:
{
Item_func::print(str, query_type);
}
+ void fix_length_and_dec()
+ {
+ Item_bool_func2::fix_length_and_dec();
+ fix_char_length(2); // returns "1" or "0" or "-1"
+ }
};
@@ -777,6 +787,8 @@ public:
const char *func_name() const { return "if"; }
bool eval_not_null_tables(uchar *opt_arg);
void fix_after_pullout(st_select_lex *new_parent, Item **ref);
+private:
+ void cache_type_info(Item *source);
};
@@ -1405,6 +1417,7 @@ public:
*/
table_map used_tables() const
{ return used_tables_cache | RAND_TABLE_BIT; }
+ bool const_item() const { return FALSE; }
};
@@ -1468,9 +1481,6 @@ public:
void cleanup();
};
-#ifdef USE_REGEX
-
-#include "my_regex.h"
class Item_func_regex :public Item_bool_func
{
@@ -1499,23 +1509,6 @@ public:
CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
};
-#else
-
-class Item_func_regex :public Item_bool_func
-{
-public:
- Item_func_regex(Item *a,Item *b) :Item_bool_func(a,b) {}
- longlong val_int() { return 0;}
- const char *func_name() const { return "regex"; }
-
- virtual inline void print(String *str, enum_query_type query_type)
- {
- print_op(str, query_type);
- }
-};
-
-#endif /* USE_REGEX */
-
typedef class Item COND;
@@ -1925,6 +1918,14 @@ public:
Item *neg_transformer(THD *thd);
};
+class Item_func_dyncol_exists :public Item_bool_func
+{
+public:
+ Item_func_dyncol_exists(Item *str, Item *num) :Item_bool_func(str, num) {}
+ longlong val_int();
+ const char *func_name() const { return "column_exists"; }
+};
+
inline bool is_cond_or(Item *item)
{
if (item->type() != Item::COND_ITEM)
@@ -1943,15 +1944,27 @@ inline Item *and_conds(Item *a, Item *b)
return new Item_cond_and(a, b);
}
+
Item *and_expressions(Item *a, Item *b, Item **org_item);
-bool get_mysql_time_from_str(THD *thd, String *str, timestamp_type warn_type,
+longlong get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
+ Item *warn_item, bool *is_null);
+
+
+bool get_mysql_time_from_str(THD *thd, String *str, timestamp_type warn_type,
const char *warn_name, MYSQL_TIME *l_time);
-class Item_func_dyncol_exists :public Item_bool_func
-{
-public:
- Item_func_dyncol_exists(Item *str, Item *num) :Item_bool_func(str, num) {}
- longlong val_int();
- const char *func_name() const { return "column_exists"; }
-};
+/*
+ These need definitions from this file but the variables are defined
+ in mysqld.h. The variables really belong in this component, but for
+ the time being we leave them in mysqld.cc to avoid merge problems.
+*/
+extern Eq_creator eq_creator;
+extern Ne_creator ne_creator;
+extern Gt_creator gt_creator;
+extern Lt_creator lt_creator;
+extern Ge_creator ge_creator;
+extern Le_creator le_creator;
+
+#endif /* ITEM_CMPFUNC_INCLUDED */
+
diff --git a/sql/item_create.cc b/sql/item_create.cc
index 7b1fa917c5c..45de3850fcd 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -13,8 +13,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@file
@@ -23,8 +22,14 @@
Functions to create an item. Used by sql_yac.yy
*/
-#include "mysql_priv.h"
-#include "item_create.h"
+#include "sql_priv.h"
+/*
+ It is necessary to include set_var.h instead of item.h because there
+ are dependencies on include order for set_var.h and item.h. This
+ will be resolved later.
+*/
+#include "sql_class.h" // set_var.h: THD
+#include "set_var.h"
#include "sp_head.h"
#include "sp.h"
@@ -1045,10 +1050,10 @@ protected:
};
-class Create_func_format : public Create_func_arg2
+class Create_func_format : public Create_native_func
{
public:
- virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
static Create_func_format s_singleton;
@@ -1520,6 +1525,34 @@ protected:
};
+#ifndef DBUG_OFF
+class Create_func_like_range_min : public Create_func_arg2
+{
+public:
+ virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_like_range_min s_singleton;
+
+protected:
+ Create_func_like_range_min() {}
+ virtual ~Create_func_like_range_min() {}
+};
+
+
+class Create_func_like_range_max : public Create_func_arg2
+{
+public:
+ virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_like_range_max s_singleton;
+
+protected:
+ Create_func_like_range_max() {}
+ virtual ~Create_func_like_range_max() {}
+};
+#endif
+
+
class Create_func_ln : public Create_func_arg1
{
public:
@@ -2037,6 +2070,19 @@ protected:
};
+class Create_func_sha2 : public Create_func_arg2
+{
+public:
+ virtual Item* create_2_arg(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_sha2 s_singleton;
+
+protected:
+ Create_func_sha2() {}
+ virtual ~Create_func_sha2() {}
+};
+
+
class Create_func_sign : public Create_func_arg1
{
public:
@@ -2261,6 +2307,18 @@ protected:
virtual ~Create_func_to_days() {}
};
+class Create_func_to_seconds : public Create_func_arg1
+{
+public:
+ virtual Item* create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_to_seconds s_singleton;
+
+protected:
+ Create_func_to_seconds() {}
+ virtual ~Create_func_to_seconds() {}
+};
+
#ifdef HAVE_SPATIAL
class Create_func_touches : public Create_func_arg2
@@ -2597,10 +2655,11 @@ Create_udf_func::create(THD *thd, udf_func *udf, List<Item> *item_list)
Item *func= NULL;
int arg_count= 0;
+ DBUG_ENTER("Create_udf_func::create");
if (item_list != NULL)
arg_count= item_list->elements;
- thd->lex->set_stmt_unsafe();
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_UDF);
DBUG_ASSERT( (udf->type == UDFTYPE_FUNCTION)
|| (udf->type == UDFTYPE_AGGREGATE));
@@ -2684,7 +2743,7 @@ Create_udf_func::create(THD *thd, udf_func *udf, List<Item> *item_list)
}
}
thd->lex->safe_to_cache_query= 0;
- return func;
+ DBUG_RETURN(func);
}
#endif
@@ -3165,9 +3224,7 @@ Create_func_cot Create_func_cot::s_singleton;
Item*
Create_func_cot::create_1_arg(THD *thd, Item *arg1)
{
- Item *i1= new (thd->mem_root) Item_int((char*) "1", 1, 1);
- Item *i2= new (thd->mem_root) Item_func_tan(arg1);
- return new (thd->mem_root) Item_func_div(i1, i2);
+ return new (thd->mem_root) Item_func_cot(arg1);
}
@@ -3613,9 +3670,34 @@ Create_func_floor::create_1_arg(THD *thd, Item *arg1)
Create_func_format Create_func_format::s_singleton;
Item*
-Create_func_format::create_2_arg(THD *thd, Item *arg1, Item *arg2)
+Create_func_format::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
{
- return new (thd->mem_root) Item_func_format(arg1, arg2);
+ 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_format(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_format(param_1, param_2, param_3);
+ break;
+ }
+ default:
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ break;
+ }
+
+ return func;
}
@@ -3624,9 +3706,10 @@ Create_func_found_rows Create_func_found_rows::s_singleton;
Item*
Create_func_found_rows::create_builder(THD *thd)
{
- thd->lex->set_stmt_unsafe();
+ DBUG_ENTER("Create_func_found_rows::create");
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
thd->lex->safe_to_cache_query= 0;
- return new (thd->mem_root) Item_func_found_rows();
+ DBUG_RETURN(new (thd->mem_root) Item_func_found_rows());
}
@@ -3785,7 +3868,7 @@ Create_func_get_lock Create_func_get_lock::s_singleton;
Item*
Create_func_get_lock::create_2_arg(THD *thd, Item *arg1, Item *arg2)
{
- thd->lex->set_stmt_unsafe();
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
return new (thd->mem_root) Item_func_get_lock(arg1, arg2);
}
@@ -3956,7 +4039,7 @@ Create_func_is_free_lock Create_func_is_free_lock::s_singleton;
Item*
Create_func_is_free_lock::create_1_arg(THD *thd, Item *arg1)
{
- thd->lex->set_stmt_unsafe();
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
return new (thd->mem_root) Item_func_is_free_lock(arg1);
}
@@ -3967,7 +4050,7 @@ Create_func_is_used_lock Create_func_is_used_lock::s_singleton;
Item*
Create_func_is_used_lock::create_1_arg(THD *thd, Item *arg1)
{
- thd->lex->set_stmt_unsafe();
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
return new (thd->mem_root) Item_func_is_used_lock(arg1);
}
@@ -4100,6 +4183,26 @@ Create_func_length::create_1_arg(THD *thd, Item *arg1)
}
+#ifndef DBUG_OFF
+Create_func_like_range_min Create_func_like_range_min::s_singleton;
+
+Item*
+Create_func_like_range_min::create_2_arg(THD *thd, Item *arg1, Item *arg2)
+{
+ return new (thd->mem_root) Item_func_like_range_min(arg1, arg2);
+}
+
+
+Create_func_like_range_max Create_func_like_range_max::s_singleton;
+
+Item*
+Create_func_like_range_max::create_2_arg(THD *thd, Item *arg1, Item *arg2)
+{
+ return new (thd->mem_root) Item_func_like_range_max(arg1, arg2);
+}
+#endif
+
+
Create_func_ln Create_func_ln::s_singleton;
Item*
@@ -4114,9 +4217,10 @@ Create_func_load_file Create_func_load_file::s_singleton;
Item*
Create_func_load_file::create_1_arg(THD *thd, Item *arg1)
{
- thd->lex->set_stmt_unsafe();
+ DBUG_ENTER("Create_func_load_file::create");
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
- return new (thd->mem_root) Item_load_file(arg1);
+ DBUG_RETURN(new (thd->mem_root) Item_load_file(arg1));
}
@@ -4283,7 +4387,7 @@ Create_func_master_pos_wait::create_native(THD *thd, LEX_STRING name,
Item *func= NULL;
int arg_count= 0;
- thd->lex->set_stmt_unsafe();
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
if (item_list != NULL)
arg_count= item_list->elements;
@@ -4518,7 +4622,7 @@ Create_func_rand::create_native(THD *thd, LEX_STRING name,
For normal INSERT's this is howevever safe
*/
if (thd->lex->sql_command != SQLCOM_INSERT)
- thd->lex->set_stmt_unsafe();
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
switch (arg_count) {
case 0:
@@ -4550,7 +4654,7 @@ Create_func_release_lock Create_func_release_lock::s_singleton;
Item*
Create_func_release_lock::create_1_arg(THD *thd, Item *arg1)
{
- thd->lex->set_stmt_unsafe();
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
return new (thd->mem_root) Item_func_release_lock(arg1);
}
@@ -4608,9 +4712,10 @@ Create_func_row_count Create_func_row_count::s_singleton;
Item*
Create_func_row_count::create_builder(THD *thd)
{
- thd->lex->set_stmt_unsafe();
+ DBUG_ENTER("Create_func_row_count::create");
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
thd->lex->safe_to_cache_query= 0;
- return new (thd->mem_root) Item_func_row_count();
+ DBUG_RETURN(new (thd->mem_root) Item_func_row_count());
}
@@ -4650,6 +4755,15 @@ Create_func_sha::create_1_arg(THD *thd, Item *arg1)
}
+Create_func_sha2 Create_func_sha2::s_singleton;
+
+Item*
+Create_func_sha2::create_2_arg(THD *thd, Item *arg1, Item *arg2)
+{
+ return new (thd->mem_root) Item_func_sha2(arg1, arg2);
+}
+
+
Create_func_sign Create_func_sign::s_singleton;
Item*
@@ -4673,7 +4787,7 @@ Create_func_sleep Create_func_sleep::s_singleton;
Item*
Create_func_sleep::create_1_arg(THD *thd, Item *arg1)
{
- thd->lex->set_stmt_unsafe();
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
return new (thd->mem_root) Item_func_sleep(arg1);
}
@@ -4829,6 +4943,15 @@ Create_func_to_days::create_1_arg(THD *thd, Item *arg1)
}
+Create_func_to_seconds Create_func_to_seconds::s_singleton;
+
+Item*
+Create_func_to_seconds::create_1_arg(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_to_seconds(arg1);
+}
+
+
#ifdef HAVE_SPATIAL
Create_func_touches Create_func_touches::s_singleton;
@@ -4918,9 +5041,10 @@ Create_func_uuid Create_func_uuid::s_singleton;
Item*
Create_func_uuid::create_builder(THD *thd)
{
- thd->lex->set_stmt_unsafe();
+ DBUG_ENTER("Create_func_uuid::create");
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
thd->lex->safe_to_cache_query= 0;
- return new (thd->mem_root) Item_func_uuid();
+ DBUG_RETURN(new (thd->mem_root) Item_func_uuid());
}
@@ -4929,9 +5053,10 @@ Create_func_uuid_short Create_func_uuid_short::s_singleton;
Item*
Create_func_uuid_short::create_builder(THD *thd)
{
- thd->lex->set_stmt_unsafe();
+ DBUG_ENTER("Create_func_uuid_short::create");
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
thd->lex->safe_to_cache_query= 0;
- return new (thd->mem_root) Item_func_uuid_short();
+ DBUG_RETURN(new (thd->mem_root) Item_func_uuid_short());
}
@@ -4940,7 +5065,7 @@ Create_func_version Create_func_version::s_singleton;
Item*
Create_func_version::create_builder(THD *thd)
{
- thd->lex->set_stmt_unsafe();
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
return new (thd->mem_root) Item_static_string_func("version()",
server_version,
(uint) strlen(server_version),
@@ -5189,6 +5314,10 @@ static Native_func_registry func_array[] =
{ { 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)},
+#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)},
+#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)},
@@ -5260,6 +5389,7 @@ static Native_func_registry func_array[] =
{ { 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)},
@@ -5336,6 +5466,7 @@ static Native_func_registry func_array[] =
{ { 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_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)},
@@ -5379,14 +5510,14 @@ int item_create_init()
DBUG_ENTER("item_create_init");
- if (hash_init(& native_functions_hash,
- system_charset_info,
- array_elements(func_array),
- 0,
- 0,
- (hash_get_key) get_native_fct_hash_key,
- NULL, /* Nothing to free */
- MYF(0)))
+ if (my_hash_init(& native_functions_hash,
+ system_charset_info,
+ array_elements(func_array),
+ 0,
+ 0,
+ (my_hash_get_key) get_native_fct_hash_key,
+ NULL, /* Nothing to free */
+ MYF(0)))
DBUG_RETURN(1);
for (func= func_array; func->builder != NULL; func++)
@@ -5398,7 +5529,7 @@ int item_create_init()
#ifndef DBUG_OFF
for (uint i=0 ; i < native_functions_hash.records ; i++)
{
- func= (Native_func_registry*) hash_element(& native_functions_hash, i);
+ func= (Native_func_registry*) my_hash_element(& native_functions_hash, i);
DBUG_PRINT("info", ("native function: %s length: %u",
func->name.str, (uint) func->name.length));
}
@@ -5416,7 +5547,7 @@ int item_create_init()
void item_create_cleanup()
{
DBUG_ENTER("item_create_cleanup");
- hash_free(& native_functions_hash);
+ my_hash_free(& native_functions_hash);
DBUG_VOID_RETURN;
}
@@ -5427,9 +5558,9 @@ find_native_function_builder(THD *thd, LEX_STRING name)
Create_func *builder= NULL;
/* Thread safe */
- func= (Native_func_registry*) 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)
{
diff --git a/sql/item_create.h b/sql/item_create.h
index 693b77bb739..ac6b0f8454f 100644
--- a/sql/item_create.h
+++ b/sql/item_create.h
@@ -12,14 +12,15 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* Functions to create an item. Used by sql/sql_yacc.yy */
#ifndef ITEM_CREATE_H
#define ITEM_CREATE_H
+typedef struct st_udf_func udf_func;
+
/**
Public function builder interface.
The parser (sql/sql_yacc.yy) uses a factory / builder pattern to
@@ -167,6 +168,10 @@ create_func_cast(THD *thd, Item *a, Cast_target cast_type,
const char *len, const char *dec,
CHARSET_INFO *cs);
+
+int item_create_init();
+void item_create_cleanup();
+
Item *create_func_dyncol_create(THD *thd, List<DYNCALL_CREATE_DEF> &list);
Item *create_func_dyncol_add(THD *thd, Item *str,
List<DYNCALL_CREATE_DEF> &list);
@@ -175,6 +180,5 @@ Item *create_func_dyncol_get(THD *thd, Item *num, Item *str,
Cast_target cast_type,
const char *c_len, const char *c_dec,
CHARSET_INFO *cs);
-
#endif
diff --git a/sql/item_func.cc b/sql/item_func.cc
index f2b07a37575..52693b1961a 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
Copyright (c) 2009, 2014, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
@@ -25,9 +25,23 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_priv.h"
+/*
+ It is necessary to include set_var.h instead of item.h because there
+ are dependencies on include order for set_var.h and item.h. This
+ will be resolved later.
+*/
+#include "sql_class.h" // set_var.h: THD
+#include "set_var.h"
#include "slave.h" // for wait_for_master_pos
+#include "sql_show.h" // append_identifier
+#include "strfunc.h" // find_type
+#include "sql_parse.h" // is_update_query
+#include "sql_acl.h" // EXECUTE_ACL
+#include "mysqld.h" // LOCK_short_uuid_generator
#include "rpl_mi.h"
+#include "sql_time.h"
#include <m_ctype.h>
#include <hash.h>
#include <time.h>
@@ -37,6 +51,10 @@
#include "sp_head.h"
#include "sp_rcontext.h"
#include "sp.h"
+#include "set_var.h"
+#include "debug_sync.h"
+#include <mysql/plugin.h>
+#include <mysql/service_thd_wait.h>
#ifdef NO_EMBEDDED_ACCESS_CHECKS
#define sp_restore_security_context(A,B) while (0) {}
@@ -64,6 +82,14 @@ eval_const_cond(COND *cond)
}
+/**
+ Test if the sum of arguments overflows the ulonglong range.
+*/
+static inline bool test_if_sum_overflows_ull(ulonglong arg1, ulonglong arg2)
+{
+ return ULONGLONG_MAX - arg1 < arg2;
+}
+
void Item_func::set_arguments(List<Item> &list)
{
allowed_arg_cols= 1;
@@ -152,9 +178,7 @@ Item_func::fix_fields(THD *thd, Item **ref)
{
DBUG_ASSERT(fixed == 0);
Item **arg,**arg_end;
-#ifndef EMBEDDED_LIBRARY // Avoid compiler warning
uchar buff[STACK_BUFF_ALLOC]; // Max argument in function
-#endif
used_tables_cache= not_null_tables_cache= 0;
const_item_cache=1;
@@ -327,7 +351,7 @@ void Item_func::traverse_cond(Cond_traverser traverser,
Item *Item_func::transform(Item_transformer transformer, uchar *argument)
{
- DBUG_ASSERT(!current_thd->is_stmt_prepare());
+ DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare());
if (arg_count)
{
@@ -503,13 +527,15 @@ Field *Item_func::tmp_table_field(TABLE *table)
switch (result_type()) {
case INT_RESULT:
- if (max_length > MY_INT32_NUM_DECIMAL_DIGITS)
- field= new Field_longlong(max_length, maybe_null, name, unsigned_flag);
+ if (max_char_length() > MY_INT32_NUM_DECIMAL_DIGITS)
+ field= new Field_longlong(max_char_length(), maybe_null, name,
+ unsigned_flag);
else
- field= new Field_long(max_length, maybe_null, name, unsigned_flag);
+ field= new Field_long(max_char_length(), maybe_null, name,
+ unsigned_flag);
break;
case REAL_RESULT:
- field= new Field_double(max_length, maybe_null, name, decimals);
+ field= new Field_double(max_char_length(), maybe_null, name, decimals);
break;
case STRING_RESULT:
return make_string_field(table);
@@ -553,7 +579,7 @@ String *Item_real_func::val_str(String *str)
double nr= val_real();
if (null_value)
return 0; /* purecov: inspected */
- str->set_real(nr,decimals, &my_charset_bin);
+ str->set_real(nr, decimals, collation.collation);
return str;
}
@@ -627,8 +653,9 @@ void Item_func::count_decimal_length()
set_if_smaller(unsigned_flag, args[i]->unsigned_flag);
}
int precision= min(max_int_part + decimals, DECIMAL_MAX_PRECISION);
- max_length= my_decimal_precision_to_length_no_truncation(precision, decimals,
- unsigned_flag);
+ fix_char_length(my_decimal_precision_to_length_no_truncation(precision,
+ decimals,
+ unsigned_flag));
}
@@ -638,13 +665,14 @@ void Item_func::count_decimal_length()
void Item_func::count_only_length(Item **item, uint nitems)
{
- max_length= 0;
+ uint32 char_length= 0;
unsigned_flag= 0;
for (uint i= 0; i < nitems ; i++)
{
- set_if_bigger(max_length, item[i]->max_length);
+ set_if_bigger(char_length, item[i]->max_char_length());
set_if_bigger(unsigned_flag, item[i]->unsigned_flag);
}
+ fix_char_length(char_length);
}
@@ -697,7 +725,7 @@ bool Item_func::count_string_result_length(enum_field_types field_type,
count_datetime_length(items, nitems);
else
{
- decimals= NOT_FIXED_DEC; // TODO
+ decimals= NOT_FIXED_DEC;
count_only_length(items, nitems);
}
return false;
@@ -708,7 +736,7 @@ void Item_func::signal_divide_by_null()
{
THD *thd= current_thd;
if (thd->variables.sql_mode & MODE_ERROR_FOR_DIVISION_BY_ZERO)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, ER_DIVISION_BY_ZERO,
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_DIVISION_BY_ZERO,
ER(ER_DIVISION_BY_ZERO));
null_value= 1;
}
@@ -745,7 +773,7 @@ String *Item_int_func::val_str(String *str)
longlong nr=val_int();
if (null_value)
return 0;
- str->set_int(nr, unsigned_flag, &my_charset_bin);
+ str->set_int(nr, unsigned_flag, collation.collation);
return str;
}
@@ -862,6 +890,7 @@ String *Item_func_hybrid_result_type::val_str(String *str)
if (!(val= decimal_op(&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;
}
@@ -870,7 +899,7 @@ String *Item_func_hybrid_result_type::val_str(String *str)
longlong nr= int_op();
if (null_value)
return 0; /* purecov: inspected */
- str->set_int(nr, unsigned_flag, &my_charset_bin);
+ str->set_int(nr, unsigned_flag, collation.collation);
break;
}
case REAL_RESULT:
@@ -878,7 +907,7 @@ String *Item_func_hybrid_result_type::val_str(String *str)
double nr= real_op();
if (null_value)
return 0; /* purecov: inspected */
- str->set_real(nr,decimals,&my_charset_bin);
+ str->set_real(nr, decimals, collation.collation);
break;
}
case STRING_RESULT:
@@ -1057,7 +1086,8 @@ my_decimal *Item_func_hybrid_result_type::val_decimal(my_decimal *decimal_value)
}
-bool Item_func_hybrid_result_type::get_date(MYSQL_TIME *ltime, uint fuzzydate)
+bool Item_func_hybrid_result_type::get_date(MYSQL_TIME *ltime,
+ ulonglong fuzzydate)
{
DBUG_ASSERT(fixed == 1);
switch (cached_result_type) {
@@ -1095,7 +1125,7 @@ bool Item_func_hybrid_result_type::get_date(MYSQL_TIME *ltime, uint fuzzydate)
char buff[40];
String tmp(buff,sizeof(buff), &my_charset_bin),*res;
if (!(res= str_op(&tmp)) ||
- str_to_datetime_with_warn(res->ptr(), res->length(),
+ str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR)
goto err;
break;
@@ -1130,6 +1160,7 @@ longlong Item_func_signed::val_int_from_str(int *error)
uint32 length;
String tmp(buff,sizeof(buff), &my_charset_bin), *res;
longlong value;
+ CHARSET_INFO *cs;
/*
For a string result, we must first get the string and then convert it
@@ -1145,9 +1176,10 @@ longlong Item_func_signed::val_int_from_str(int *error)
null_value= 0;
start= (char *)res->ptr();
length= res->length();
+ cs= res->charset();
end= start + length;
- value= my_strtoll10(start, &end, error);
+ value= cs->cset->strtoll10(cs, start, &end, error);
if (*error > 0 || end != start+ length)
{
char err_buff[128];
@@ -1190,7 +1222,7 @@ longlong Item_func_signed::val_int()
return value;
err:
- push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+ push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR,
"Cast to signed converted positive out-of-range integer to "
"it's negative complement");
return value;
@@ -1246,7 +1278,7 @@ longlong Item_func_unsigned::val_int()
return value;
err:
- push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+ push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR,
"Cast to unsigned converted negative integer to it's "
"positive complement");
return value;
@@ -1314,7 +1346,7 @@ my_decimal *Item_decimal_typecast::val_decimal(my_decimal *dec)
return dec;
err:
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_DATA_OUT_OF_RANGE,
ER(ER_WARN_DATA_OUT_OF_RANGE),
name, 1L);
@@ -1396,16 +1428,68 @@ double Item_func_plus::real_op()
double value= args[0]->val_real() + args[1]->val_real();
if ((null_value=args[0]->null_value || args[1]->null_value))
return 0.0;
- return fix_result(value);
+ return check_float_overflow(value);
}
longlong Item_func_plus::int_op()
{
- longlong value=args[0]->val_int()+args[1]->val_int();
- if ((null_value=args[0]->null_value || args[1]->null_value))
+ longlong val0= args[0]->val_int();
+ longlong val1= args[1]->val_int();
+ longlong res= val0 + val1;
+ bool res_unsigned= FALSE;
+
+ if ((null_value= args[0]->null_value || args[1]->null_value))
return 0;
- return value;
+
+ /*
+ First check whether the result can be represented as a
+ (bool unsigned_flag, longlong value) pair, then check if it is compatible
+ with this Item's unsigned_flag by calling check_integer_overflow().
+ */
+ if (args[0]->unsigned_flag)
+ {
+ if (args[1]->unsigned_flag || val1 >= 0)
+ {
+ if (test_if_sum_overflows_ull((ulonglong) val0, (ulonglong) val1))
+ goto err;
+ res_unsigned= TRUE;
+ }
+ else
+ {
+ /* val1 is negative */
+ if ((ulonglong) val0 > (ulonglong) LONGLONG_MAX)
+ res_unsigned= TRUE;
+ }
+ }
+ else
+ {
+ if (args[1]->unsigned_flag)
+ {
+ if (val0 >= 0)
+ {
+ if (test_if_sum_overflows_ull((ulonglong) val0, (ulonglong) val1))
+ goto err;
+ res_unsigned= TRUE;
+ }
+ else
+ {
+ if ((ulonglong) val1 > (ulonglong) LONGLONG_MAX)
+ res_unsigned= TRUE;
+ }
+ }
+ else
+ {
+ if (val0 >=0 && val1 >= 0)
+ res_unsigned= TRUE;
+ else if (val0 < 0 && val1 < 0 && res >= 0)
+ goto err;
+ }
+ }
+ return check_integer_overflow(res, res_unsigned);
+
+err:
+ return raise_integer_overflow();
}
@@ -1429,8 +1513,10 @@ my_decimal *Item_func_plus::decimal_op(my_decimal *decimal_value)
return 0;
val2= args[1]->val_decimal(&value2);
if (!(null_value= (args[1]->null_value ||
- (my_decimal_add(E_DEC_FATAL_ERROR, decimal_value, val1,
- val2) > 3))))
+ check_decimal_overflow(my_decimal_add(E_DEC_FATAL_ERROR &
+ ~E_DEC_OVERFLOW,
+ decimal_value,
+ val1, val2)) > 3)))
return decimal_value;
return 0;
}
@@ -1477,16 +1563,71 @@ double Item_func_minus::real_op()
double value= args[0]->val_real() - args[1]->val_real();
if ((null_value=args[0]->null_value || args[1]->null_value))
return 0.0;
- return fix_result(value);
+ return check_float_overflow(value);
}
longlong Item_func_minus::int_op()
{
- longlong value=args[0]->val_int() - args[1]->val_int();
- if ((null_value=args[0]->null_value || args[1]->null_value))
+ longlong val0= args[0]->val_int();
+ longlong val1= args[1]->val_int();
+ longlong res= val0 - val1;
+ bool res_unsigned= FALSE;
+
+ if ((null_value= args[0]->null_value || args[1]->null_value))
return 0;
- return value;
+
+ /*
+ First check whether the result can be represented as a
+ (bool unsigned_flag, longlong value) pair, then check if it is compatible
+ with this Item's unsigned_flag by calling check_integer_overflow().
+ */
+ if (args[0]->unsigned_flag)
+ {
+ if (args[1]->unsigned_flag)
+ {
+ if ((ulonglong) val0 < (ulonglong) val1)
+ {
+ if (res >= 0)
+ goto err;
+ }
+ else
+ res_unsigned= TRUE;
+ }
+ else
+ {
+ if (val1 >= 0)
+ {
+ if ((ulonglong) val0 > (ulonglong) val1)
+ res_unsigned= TRUE;
+ }
+ else
+ {
+ if (test_if_sum_overflows_ull((ulonglong) val0, (ulonglong) -val1))
+ goto err;
+ res_unsigned= TRUE;
+ }
+ }
+ }
+ else
+ {
+ if (args[1]->unsigned_flag)
+ {
+ if ((ulonglong) (val0 - LONGLONG_MIN) < (ulonglong) val1)
+ goto err;
+ }
+ else
+ {
+ if (val0 > 0 && val1 < 0)
+ res_unsigned= TRUE;
+ else if (val0 < 0 && val1 > 0 && res >= 0)
+ goto err;
+ }
+ }
+ return check_integer_overflow(res, res_unsigned);
+
+err:
+ return raise_integer_overflow();
}
@@ -1504,8 +1645,10 @@ my_decimal *Item_func_minus::decimal_op(my_decimal *decimal_value)
return 0;
val2= args[1]->val_decimal(&value2);
if (!(null_value= (args[1]->null_value ||
- (my_decimal_sub(E_DEC_FATAL_ERROR, decimal_value, val1,
- val2) > 3))))
+ (check_decimal_overflow(my_decimal_sub(E_DEC_FATAL_ERROR &
+ ~E_DEC_OVERFLOW,
+ decimal_value, val1,
+ val2)) > 3))))
return decimal_value;
return 0;
}
@@ -1517,17 +1660,86 @@ double Item_func_mul::real_op()
double value= args[0]->val_real() * args[1]->val_real();
if ((null_value=args[0]->null_value || args[1]->null_value))
return 0.0;
- return fix_result(value);
+ return check_float_overflow(value);
}
longlong Item_func_mul::int_op()
{
DBUG_ASSERT(fixed == 1);
- longlong value=args[0]->val_int()*args[1]->val_int();
- if ((null_value=args[0]->null_value || args[1]->null_value))
+ longlong a= args[0]->val_int();
+ longlong b= args[1]->val_int();
+ longlong res;
+ ulonglong res0, res1;
+ ulong a0, a1, b0, b1;
+ bool res_unsigned= FALSE;
+ bool a_negative= FALSE, b_negative= FALSE;
+
+ if ((null_value= args[0]->null_value || args[1]->null_value))
return 0;
- return value;
+
+ /*
+ First check whether the result can be represented as a
+ (bool unsigned_flag, longlong value) pair, then check if it is compatible
+ with this Item's unsigned_flag by calling check_integer_overflow().
+
+ Let a = a1 * 2^32 + a0 and b = b1 * 2^32 + b0. Then
+ a * b = (a1 * 2^32 + a0) * (b1 * 2^32 + b0) = a1 * b1 * 2^64 +
+ + (a1 * b0 + a0 * b1) * 2^32 + a0 * b0;
+ We can determine if the above sum overflows the ulonglong range by
+ sequentially checking the following conditions:
+ 1. If both a1 and b1 are non-zero.
+ 2. Otherwise, if (a1 * b0 + a0 * b1) is greater than ULONG_MAX.
+ 3. Otherwise, if (a1 * b0 + a0 * b1) * 2^32 + a0 * b0 is greater than
+ ULONGLONG_MAX.
+
+ Since we also have to take the unsigned_flag for a and b into account,
+ it is easier to first work with absolute values and set the
+ correct sign later.
+ */
+ if (!args[0]->unsigned_flag && a < 0)
+ {
+ a_negative= TRUE;
+ a= -a;
+ }
+ if (!args[1]->unsigned_flag && b < 0)
+ {
+ b_negative= TRUE;
+ b= -b;
+ }
+
+ a0= 0xFFFFFFFFUL & a;
+ a1= ((ulonglong) a) >> 32;
+ b0= 0xFFFFFFFFUL & b;
+ b1= ((ulonglong) b) >> 32;
+
+ if (a1 && b1)
+ goto err;
+
+ res1= (ulonglong) a1 * b0 + (ulonglong) a0 * b1;
+ if (res1 > 0xFFFFFFFFUL)
+ goto err;
+
+ res1= res1 << 32;
+ res0= (ulonglong) a0 * b0;
+
+ if (test_if_sum_overflows_ull(res1, res0))
+ goto err;
+ res= res1 + res0;
+
+ if (a_negative != b_negative)
+ {
+ if ((ulonglong) res > (ulonglong) LONGLONG_MIN + 1)
+ goto err;
+ res= -res;
+ }
+ else
+ res_unsigned= TRUE;
+
+ return check_integer_overflow(res, res_unsigned);
+
+err:
+ return raise_integer_overflow();
}
@@ -1542,8 +1754,10 @@ my_decimal *Item_func_mul::decimal_op(my_decimal *decimal_value)
return 0;
val2= args[1]->val_decimal(&value2);
if (!(null_value= (args[1]->null_value ||
- (my_decimal_mul(E_DEC_FATAL_ERROR, decimal_value, val1,
- val2) > 3))))
+ (check_decimal_overflow(my_decimal_mul(E_DEC_FATAL_ERROR &
+ ~E_DEC_OVERFLOW,
+ decimal_value, val1,
+ val2)) > 3))))
return decimal_value;
return 0;
}
@@ -1577,7 +1791,7 @@ double Item_func_div::real_op()
signal_divide_by_null();
return 0.0;
}
- return fix_result(value/val2);
+ return check_float_overflow(value/val2);
}
@@ -1593,8 +1807,12 @@ my_decimal *Item_func_div::decimal_op(my_decimal *decimal_value)
val2= args[1]->val_decimal(&value2);
if ((null_value= args[1]->null_value))
return 0;
- if ((err= my_decimal_div(E_DEC_FATAL_ERROR & ~E_DEC_DIV_ZERO, decimal_value,
- val1, val2, prec_increment)) > 3)
+ if ((err= check_decimal_overflow(my_decimal_div(E_DEC_FATAL_ERROR &
+ ~E_DEC_OVERFLOW &
+ ~E_DEC_DIV_ZERO,
+ decimal_value,
+ val1, val2,
+ prec_increment))) > 3)
{
if (err == E_DEC_DIV_ZERO)
signal_divide_by_null();
@@ -1678,22 +1896,71 @@ void Item_func_div::fix_length_and_dec()
longlong Item_func_int_div::val_int()
{
DBUG_ASSERT(fixed == 1);
- longlong value=args[0]->val_int();
- longlong val2=args[1]->val_int();
+
+ /*
+ Perform division using DECIMAL math if either of the operands has a
+ non-integer type
+ */
+ if (args[0]->result_type() != INT_RESULT ||
+ args[1]->result_type() != INT_RESULT)
+ {
+ my_decimal tmp;
+ my_decimal *val0p= args[0]->val_decimal(&tmp);
+ if ((null_value= args[0]->null_value))
+ return 0;
+ my_decimal val0= *val0p;
+
+ my_decimal *val1p= args[1]->val_decimal(&tmp);
+ if ((null_value= args[1]->null_value))
+ return 0;
+ my_decimal val1= *val1p;
+
+ int err;
+ if ((err= my_decimal_div(E_DEC_FATAL_ERROR & ~E_DEC_DIV_ZERO, &tmp,
+ &val0, &val1, 0)) > 3)
+ {
+ if (err == E_DEC_DIV_ZERO)
+ signal_divide_by_null();
+ return 0;
+ }
+
+ my_decimal truncated;
+ const bool do_truncate= true;
+ if (my_decimal_round(E_DEC_FATAL_ERROR, &tmp, 0, do_truncate, &truncated))
+ DBUG_ASSERT(false);
+
+ longlong res;
+ if (my_decimal2int(E_DEC_FATAL_ERROR, &truncated, unsigned_flag, &res) &
+ E_DEC_OVERFLOW)
+ raise_integer_overflow();
+ return res;
+ }
+
+ longlong val0=args[0]->val_int();
+ longlong val1=args[1]->val_int();
+ bool val0_negative, val1_negative, res_negative;
+ ulonglong uval0, uval1, res;
if ((null_value= (args[0]->null_value || args[1]->null_value)))
return 0;
- if (val2 == 0)
+ if (val1 == 0)
{
signal_divide_by_null();
return 0;
}
- if (unsigned_flag)
- return ((ulonglong) value / (ulonglong) val2);
- else if (value == LONGLONG_MIN && val2 == -1)
- return LONGLONG_MIN;
- else
- return value / val2;
+ val0_negative= !args[0]->unsigned_flag && val0 < 0;
+ val1_negative= !args[1]->unsigned_flag && val1 < 0;
+ res_negative= val0_negative != val1_negative;
+ uval0= (ulonglong) (val0_negative ? -val0 : val0);
+ uval1= (ulonglong) (val1_negative ? -val1 : val1);
+ res= uval0 / uval1;
+ if (res_negative)
+ {
+ if (res > (ulonglong) LONGLONG_MAX)
+ return raise_integer_overflow();
+ res= (ulonglong) (-(longlong) res);
+ }
+ return check_integer_overflow(res, !res_negative);
}
@@ -1701,9 +1968,11 @@ void Item_func_int_div::fix_length_and_dec()
{
Item_result argtype= args[0]->result_type();
/* use precision ony for the data type it is applicable for and valid */
- max_length=args[0]->max_length -
- (argtype == DECIMAL_RESULT || argtype == INT_RESULT ?
- args[0]->decimals : 0);
+ 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);
maybe_null=1;
unsigned_flag=args[0]->unsigned_flag | args[1]->unsigned_flag;
}
@@ -1712,26 +1981,32 @@ void Item_func_int_div::fix_length_and_dec()
longlong Item_func_mod::int_op()
{
DBUG_ASSERT(fixed == 1);
- longlong value= args[0]->val_int();
- longlong val2= args[1]->val_int();
- longlong result;
+ longlong val0= args[0]->val_int();
+ longlong val1= args[1]->val_int();
+ bool val0_negative, val1_negative;
+ ulonglong uval0, uval1;
+ ulonglong res;
if ((null_value= args[0]->null_value || args[1]->null_value))
return 0; /* purecov: inspected */
- if (val2 == 0)
+ if (val1 == 0)
{
signal_divide_by_null();
return 0;
}
- if (args[0]->unsigned_flag)
- result= args[1]->unsigned_flag ?
- ((ulonglong) value) % ((ulonglong) val2) : ((ulonglong) value) % val2;
- else result= args[1]->unsigned_flag ?
- value % ((ulonglong) val2) :
- (val2 == -1) ? 0 : value % val2;
-
- return result;
+ /*
+ '%' is calculated by integer division internally. Since dividing
+ LONGLONG_MIN by -1 generates SIGFPE, we calculate using unsigned values and
+ then adjust the sign appropriately.
+ */
+ val0_negative= !args[0]->unsigned_flag && val0 < 0;
+ val1_negative= !args[1]->unsigned_flag && val1 < 0;
+ uval0= (ulonglong) (val0_negative ? -val0 : val0);
+ uval1= (ulonglong) (val1_negative ? -val1 : val1);
+ res= uval0 % uval1;
+ return check_integer_overflow(val0_negative ? -(longlong) res : res,
+ !val0_negative);
}
double Item_func_mod::real_op()
@@ -1801,8 +2076,22 @@ double Item_func_neg::real_op()
longlong Item_func_neg::int_op()
{
longlong value= args[0]->val_int();
- null_value= args[0]->null_value;
- return -value;
+ if ((null_value= args[0]->null_value))
+ return 0;
+ if (args[0]->unsigned_flag &&
+ (ulonglong) value > (ulonglong) LONGLONG_MAX + 1)
+ return raise_integer_overflow();
+
+ if (value == LONGLONG_MIN)
+ {
+ if (args[0]->unsigned_flag != unsigned_flag)
+ /* negation of LONGLONG_MIN is LONGLONG_MIN. */
+ return LONGLONG_MIN;
+ else
+ return raise_integer_overflow();
+ }
+
+ return check_integer_overflow(-value, !args[0]->unsigned_flag && value < 0);
}
@@ -1865,7 +2154,12 @@ longlong Item_func_abs::int_op()
longlong value= args[0]->val_int();
if ((null_value= args[0]->null_value))
return 0;
- return (value >= 0) || unsigned_flag ? value : -value;
+ if (unsigned_flag)
+ return value;
+ /* -LONGLONG_MIN = LONGLONG_MAX + 1 => outside of signed longlong range */
+ if (value == LONGLONG_MIN)
+ return raise_integer_overflow();
+ return (value >= 0) ? value : -value;
}
@@ -1972,7 +2266,7 @@ double Item_func_exp::val_real()
double value= args[0]->val_real();
if ((null_value=args[0]->null_value))
return 0.0; /* purecov: inspected */
- return fix_result(exp(value));
+ return check_float_overflow(exp(value));
}
double Item_func_sqrt::val_real()
@@ -1991,7 +2285,7 @@ double Item_func_pow::val_real()
double val2= args[1]->val_real();
if ((null_value=(args[0]->null_value || args[1]->null_value)))
return 0.0; /* purecov: inspected */
- return fix_result(pow(value,val2));
+ return check_float_overflow(pow(value,val2));
}
// Trigonometric functions
@@ -1999,6 +2293,8 @@ double Item_func_pow::val_real()
double Item_func_acos::val_real()
{
DBUG_ASSERT(fixed == 1);
+ /* One can use this to defer SELECT processing. */
+ DEBUG_SYNC(current_thd, "before_acos_function");
// the volatile's for BUG #2338 to calm optimizer down (because of gcc's bug)
volatile double value= args[0]->val_real();
if ((null_value=(args[0]->null_value || (value < -1.0 || value > 1.0))))
@@ -2027,7 +2323,7 @@ double Item_func_atan::val_real()
double val2= args[1]->val_real();
if ((null_value=args[1]->null_value))
return 0.0;
- return fix_result(atan2(value,val2));
+ return check_float_overflow(atan2(value,val2));
}
return atan(value);
}
@@ -2056,7 +2352,17 @@ double Item_func_tan::val_real()
double value= args[0]->val_real();
if ((null_value=args[0]->null_value))
return 0.0;
- return fix_result(tan(value));
+ return check_float_overflow(tan(value));
+}
+
+
+double Item_func_cot::val_real()
+{
+ DBUG_ASSERT(fixed == 1);
+ double value= args[0]->val_real();
+ if ((null_value=args[0]->null_value))
+ return 0.0;
+ return check_float_overflow(1.0 / tan(value));
}
@@ -2122,8 +2428,8 @@ void Item_func_int_val::fix_length_and_dec()
ulonglong tmp_max_length= (ulonglong ) args[0]->max_length -
(args[0]->decimals ? args[0]->decimals + 1 : 0) + 2;
- max_length= tmp_max_length > (ulonglong) max_field_size ?
- max_field_size : (uint32) tmp_max_length;
+ max_length= tmp_max_length > (ulonglong) 4294967295U ?
+ (uint32) 4294967295U : (uint32) tmp_max_length;
uint tmp= float_length(decimals);
set_if_smaller(max_length,tmp);
decimals= 0;
@@ -2349,25 +2655,31 @@ double my_double_round(double value, longlong dec, bool dec_unsigned,
/*
tmp2 is here to avoid return the value with 80 bit precision
This will fix that the test round(0.1,1) = round(0.1,1) is true
+ Tagging with volatile is no guarantee, it may still be optimized away...
*/
volatile double tmp2;
tmp=(abs_dec < array_elements(log_10) ?
log_10[abs_dec] : pow(10.0,(double) abs_dec));
+ // Pre-compute these, to avoid optimizing away e.g. 'floor(v/tmp) * tmp'.
+ volatile double value_div_tmp= value / tmp;
+ volatile double value_mul_tmp= value * tmp;
+
if (dec_negative && my_isinf(tmp))
- tmp2= 0;
- else if (!dec_negative && my_isinf(value * tmp))
+ tmp2= 0.0;
+ else if (!dec_negative && my_isinf(value_mul_tmp))
tmp2= value;
else if (truncate)
{
- if (value >= 0)
- tmp2= dec < 0 ? floor(value/tmp)*tmp : floor(value*tmp)/tmp;
+ if (value >= 0.0)
+ tmp2= dec < 0 ? floor(value_div_tmp) * tmp : floor(value_mul_tmp) / tmp;
else
- tmp2= dec < 0 ? ceil(value/tmp)*tmp : ceil(value*tmp)/tmp;
+ tmp2= dec < 0 ? ceil(value_div_tmp) * tmp : ceil(value_mul_tmp) / tmp;
}
else
- tmp2=dec < 0 ? rint(value/tmp)*tmp : rint(value*tmp)/tmp;
+ tmp2=dec < 0 ? rint(value_div_tmp) * tmp : rint(value_mul_tmp) / tmp;
+
return tmp2;
}
@@ -2536,7 +2848,7 @@ double Item_func_units::val_real()
double value= args[0]->val_real();
if ((null_value=args[0]->null_value))
return 0;
- return value*mul+add;
+ return check_float_overflow(value * mul + add);
}
@@ -2559,13 +2871,18 @@ void Item_func_min_max::fix_length_and_dec()
cmp_type= item_cmp_type(cmp_type,args[i]->result_type());
}
if (cmp_type == STRING_RESULT)
- agg_arg_charsets(collation, args, arg_count, MY_COLL_CMP_CONV, 1);
+ agg_arg_charsets_for_string_result_with_comparison(collation,
+ args, arg_count);
else if ((cmp_type == DECIMAL_RESULT) || (cmp_type == INT_RESULT))
- max_length= my_decimal_precision_to_length_no_truncation(max_int_part +
- decimals, decimals,
- unsigned_flag);
+ {
+ collation.set_numeric();
+ fix_char_length(my_decimal_precision_to_length_no_truncation(max_int_part +
+ decimals,
+ decimals,
+ unsigned_flag));
+ }
else if (cmp_type == REAL_RESULT)
- max_length= float_length(decimals);
+ fix_char_length(float_length(decimals));
compare_as_dates= find_date_time_item(args, arg_count, 0);
if (compare_as_dates)
@@ -2595,13 +2912,13 @@ void Item_func_min_max::fix_length_and_dec()
0 Otherwise
*/
-bool Item_func_min_max::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
+bool Item_func_min_max::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
longlong UNINIT_VAR(min_max);
DBUG_ASSERT(fixed == 1);
/*
- just like ::val_int() method of an string item can be called,
+ 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"))
@@ -2610,8 +2927,6 @@ bool Item_func_min_max::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
if (!compare_as_dates)
return Item_func::get_date(ltime, fuzzy_date);
- null_value= 0;
-
for (uint i=0; i < arg_count ; i++)
{
Item **arg= args + i;
@@ -2621,8 +2936,7 @@ bool Item_func_min_max::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
/* Check if we need to stop (because of error or KILL) and stop the loop */
if (thd->is_error() || args[i]->null_value)
{
- null_value= 1;
- return 1;
+ return (null_value= 1);
}
if (i == 0 || (res < min_max ? cmp_sign : -cmp_sign) > 0)
@@ -2650,7 +2964,7 @@ bool Item_func_min_max::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
MYSQL_TIMESTAMP_ERROR))))
return true;
- return 0;
+ return (null_value= 0);
}
@@ -2839,7 +3153,7 @@ longlong Item_func_coercibility::val_int()
void Item_func_locate::fix_length_and_dec()
{
max_length= MY_INT32_NUM_DECIMAL_DIGITS;
- agg_arg_charsets(cmp_collation, args, 2, MY_COLL_CMP_CONV, 1);
+ agg_arg_charsets_for_comparison(cmp_collation, args, 2);
}
@@ -2963,7 +3277,7 @@ void Item_func_field::fix_length_and_dec()
for (uint i=1; i < arg_count ; i++)
cmp_type= item_cmp_type(cmp_type, args[i]->result_type());
if (cmp_type == STRING_RESULT)
- agg_arg_charsets(cmp_collation, args, arg_count, MY_COLL_CMP_CONV, 1);
+ agg_arg_charsets_for_comparison(cmp_collation, args, arg_count);
}
@@ -3022,6 +3336,8 @@ void Item_func_find_in_set::fix_length_and_dec()
String *find=args[0]->val_str(&value);
if (find)
{
+ // find is not NULL pointer so args[0] is not a null-value
+ DBUG_ASSERT(!args[0]->null_value);
enum_value= find_type(((Field_enum*) field)->typelib,find->ptr(),
find->length(), 0);
enum_bit=0;
@@ -3030,7 +3346,7 @@ void Item_func_find_in_set::fix_length_and_dec()
}
}
}
- agg_arg_charsets(cmp_collation, args, 2, MY_COLL_CMP_CONV, 1);
+ agg_arg_charsets_for_comparison(cmp_collation, args, 2);
}
static const char separator=',';
@@ -3040,11 +3356,22 @@ longlong Item_func_find_in_set::val_int()
DBUG_ASSERT(fixed == 1);
if (enum_value)
{
- ulonglong tmp=(ulonglong) args[1]->val_int();
- if (!(null_value=args[1]->null_value || args[0]->null_value))
+ // enum_value is set iff args[0]->const_item() in fix_length_and_dec().
+ DBUG_ASSERT(args[0]->const_item());
+
+ ulonglong tmp= (ulonglong) args[1]->val_int();
+ null_value= args[1]->null_value;
+ /*
+ No need to check args[0]->null_value since enum_value is set iff
+ args[0] is a non-null const item. Note: no DBUG_ASSERT on
+ args[0]->null_value here because args[0] may have been replaced
+ by an Item_cache on which val_int() has not been called. See
+ BUG#11766317
+ */
+ if (!null_value)
{
if (tmp & enum_bit)
- return enum_value;
+ return enum_value;
}
return 0L;
}
@@ -3145,9 +3472,7 @@ bool
udf_handler::fix_fields(THD *thd, Item_result_field *func,
uint arg_count, Item **arguments)
{
-#ifndef EMBEDDED_LIBRARY // Avoid compiler warning
uchar buff[STACK_BUFF_ALLOC]; // Max argument in function
-#endif
DBUG_ENTER("Item_udf_func::fix_fields");
if (check_stack_overrun(thd, STACK_MIN_SIZE, buff))
@@ -3398,7 +3723,7 @@ String *udf_handler::val_str(String *str,String *save_str)
if (res == str->ptr())
{
str->length(res_length);
- DBUG_PRINT("exit", ("str: %s", str->ptr()));
+ DBUG_PRINT("exit", ("str: %*.s", (int) str->length(), str->ptr()));
DBUG_RETURN(str);
}
save_str->set(res, res_length, str->charset());
@@ -3602,7 +3927,7 @@ bool udf_handler::get_arguments() { return 0; }
** User level locks
*/
-pthread_mutex_t LOCK_user_locks;
+mysql_mutex_t LOCK_user_locks;
static HASH hash_user_locks;
class User_level_lock
@@ -3613,7 +3938,7 @@ class User_level_lock
public:
int count;
bool locked;
- pthread_cond_t cond;
+ mysql_cond_t cond;
my_thread_id thread_id;
void set_thread(THD *thd) { thread_id= thd->thread_id; }
@@ -3621,12 +3946,12 @@ public:
:key_length(length),count(1),locked(1), thread_id(id)
{
key= (uchar*) my_memdup(key_arg,length,MYF(0));
- pthread_cond_init(&cond,NULL);
+ mysql_cond_init(key_user_level_lock_cond, &cond, NULL);
if (key)
{
if (my_hash_insert(&hash_user_locks,(uchar*) this))
{
- my_free(key,MYF(0));
+ my_free(key);
key=0;
}
}
@@ -3635,10 +3960,10 @@ public:
{
if (key)
{
- hash_delete(&hash_user_locks,(uchar*) this);
- my_free(key, MYF(0));
+ my_hash_delete(&hash_user_locks,(uchar*) this);
+ my_free(key);
}
- pthread_cond_destroy(&cond);
+ mysql_cond_destroy(&cond);
}
inline bool initialized() { return key != 0; }
friend void item_user_lock_release(User_level_lock *ull);
@@ -3653,14 +3978,38 @@ uchar *ull_get_key(const User_level_lock *ull, size_t *length,
return ull->key;
}
+#ifdef HAVE_PSI_INTERFACE
+static PSI_mutex_key key_LOCK_user_locks;
+
+static PSI_mutex_info all_user_mutexes[]=
+{
+ { &key_LOCK_user_locks, "LOCK_user_locks", PSI_FLAG_GLOBAL}
+};
+
+static void init_user_lock_psi_keys(void)
+{
+ const char* category= "sql";
+ int count;
+
+ if (PSI_server == NULL)
+ return;
+
+ count= array_elements(all_user_mutexes);
+ PSI_server->register_mutex(category, all_user_mutexes, count);
+}
+#endif
static bool item_user_lock_inited= 0;
void item_user_lock_init(void)
{
- pthread_mutex_init(&LOCK_user_locks,MY_MUTEX_INIT_SLOW);
- hash_init(&hash_user_locks,system_charset_info,
- 16,0,0,(hash_get_key) ull_get_key,NULL,0);
+#ifdef HAVE_PSI_INTERFACE
+ init_user_lock_psi_keys();
+#endif
+
+ mysql_mutex_init(key_LOCK_user_locks, &LOCK_user_locks, MY_MUTEX_INIT_SLOW);
+ my_hash_init(&hash_user_locks,system_charset_info,
+ 16,0,0,(my_hash_get_key) ull_get_key,NULL,0);
item_user_lock_inited= 1;
}
@@ -3669,8 +4018,8 @@ void item_user_lock_free(void)
if (item_user_lock_inited)
{
item_user_lock_inited= 0;
- hash_free(&hash_user_locks);
- pthread_mutex_destroy(&LOCK_user_locks);
+ my_hash_free(&hash_user_locks);
+ mysql_mutex_destroy(&LOCK_user_locks);
}
}
@@ -3679,7 +4028,7 @@ void item_user_lock_release(User_level_lock *ull)
ull->locked=0;
ull->thread_id= 0;
if (--ull->count)
- pthread_cond_signal(&ull->cond);
+ mysql_cond_signal(&ull->cond);
else
delete ull;
}
@@ -3716,6 +4065,92 @@ longlong Item_master_pos_wait::val_int()
/**
+ Enables a session to wait on a condition until a timeout or a network
+ disconnect occurs.
+
+ @remark The connection is polled every m_interrupt_interval nanoseconds.
+*/
+
+class Interruptible_wait
+{
+ THD *m_thd;
+ struct timespec m_abs_timeout;
+ static const ulonglong m_interrupt_interval;
+
+ public:
+ Interruptible_wait(THD *thd)
+ : m_thd(thd) {}
+
+ ~Interruptible_wait() {}
+
+ public:
+ /**
+ Set the absolute timeout.
+
+ @param timeout The amount of time in nanoseconds to wait
+ */
+ void set_timeout(ulonglong timeout)
+ {
+ /*
+ Calculate the absolute system time at the start so it can
+ be controlled in slices. It relies on the fact that once
+ the absolute time passes, the timed wait call will fail
+ automatically with a timeout error.
+ */
+ set_timespec_nsec(m_abs_timeout, timeout);
+ }
+
+ /** The timed wait. */
+ int wait(mysql_cond_t *, mysql_mutex_t *);
+};
+
+
+/** Time to wait before polling the connection status. */
+const ulonglong Interruptible_wait::m_interrupt_interval= 5 * ULL(1000000000);
+
+
+/**
+ Wait for a given condition to be signaled.
+
+ @param cond The condition variable to wait on.
+ @param mutex The associated mutex.
+
+ @remark The absolute timeout is preserved across calls.
+
+ @retval return value from mysql_cond_timedwait
+*/
+
+int Interruptible_wait::wait(mysql_cond_t *cond, mysql_mutex_t *mutex)
+{
+ int error;
+ struct timespec timeout;
+
+ while (1)
+ {
+ /* Wait for a fixed interval. */
+ set_timespec_nsec(timeout, m_interrupt_interval);
+
+ /* But only if not past the absolute timeout. */
+ if (cmp_timespec(timeout, m_abs_timeout) > 0)
+ timeout= m_abs_timeout;
+
+ error= mysql_cond_timedwait(cond, mutex, &timeout);
+ if (error == ETIMEDOUT || error == ETIME)
+ {
+ /* Return error if timed out or connection is broken. */
+ if (!cmp_timespec(timeout, m_abs_timeout) || !m_thd->is_connected())
+ break;
+ }
+ /* Otherwise, propagate status to the caller. */
+ else
+ break;
+ }
+
+ return error;
+}
+
+
+/**
Get a user level lock. If the thread has an old lock this is first released.
@retval
@@ -3730,11 +4165,11 @@ longlong Item_func_get_lock::val_int()
{
DBUG_ASSERT(fixed == 1);
String *res=args[0]->val_str(&value);
- longlong timeout=args[1]->val_int();
- struct timespec abstime;
+ ulonglong timeout= args[1]->val_int();
THD *thd=current_thd;
User_level_lock *ull;
int error;
+ Interruptible_wait timed_cond(thd);
DBUG_ENTER("Item_func_get_lock::val_int");
/*
@@ -3747,11 +4182,11 @@ longlong Item_func_get_lock::val_int()
if (thd->slave_thread)
DBUG_RETURN(1);
- pthread_mutex_lock(&LOCK_user_locks);
+ mysql_mutex_lock(&LOCK_user_locks);
if (!res || !res->length())
{
- pthread_mutex_unlock(&LOCK_user_locks);
+ mysql_mutex_unlock(&LOCK_user_locks);
null_value=1;
DBUG_RETURN(0);
}
@@ -3765,22 +4200,22 @@ longlong Item_func_get_lock::val_int()
thd->ull=0;
}
- if (!(ull= ((User_level_lock *) hash_search(&hash_user_locks,
- (uchar*) res->ptr(),
- (size_t) res->length()))))
+ if (!(ull= ((User_level_lock *) my_hash_search(&hash_user_locks,
+ (uchar*) res->ptr(),
+ (size_t) res->length()))))
{
ull= new User_level_lock((uchar*) res->ptr(), (size_t) res->length(),
thd->thread_id);
if (!ull || !ull->initialized())
{
delete ull;
- pthread_mutex_unlock(&LOCK_user_locks);
+ mysql_mutex_unlock(&LOCK_user_locks);
null_value=1; // Probably out of memory
DBUG_RETURN(0);
}
ull->set_thread(thd);
thd->ull=ull;
- pthread_mutex_unlock(&LOCK_user_locks);
+ mysql_mutex_unlock(&LOCK_user_locks);
DBUG_PRINT("info", ("made new lock"));
DBUG_RETURN(1); // Got new lock
}
@@ -3795,12 +4230,14 @@ longlong Item_func_get_lock::val_int()
thd->mysys_var->current_mutex= &LOCK_user_locks;
thd->mysys_var->current_cond= &ull->cond;
- set_timespec(abstime,timeout);
+ timed_cond.set_timeout(timeout * ULL(1000000000));
+
error= 0;
+ thd_wait_begin(thd, THD_WAIT_USER_LOCK);
while (ull->locked && !thd->killed)
{
DBUG_PRINT("info", ("waiting on lock"));
- error= pthread_cond_timedwait(&ull->cond,&LOCK_user_locks,&abstime);
+ error= timed_cond.wait(&ull->cond, &LOCK_user_locks);
if (error == ETIMEDOUT || error == ETIME)
{
DBUG_PRINT("info", ("lock wait timeout"));
@@ -3808,6 +4245,7 @@ longlong Item_func_get_lock::val_int()
}
error= 0;
}
+ thd_wait_end(thd);
if (ull->locked)
{
@@ -3831,13 +4269,13 @@ longlong Item_func_get_lock::val_int()
error=0;
DBUG_PRINT("info", ("got the lock"));
}
- pthread_mutex_unlock(&LOCK_user_locks);
+ mysql_mutex_unlock(&LOCK_user_locks);
- pthread_mutex_lock(&thd->mysys_var->mutex);
+ mysql_mutex_lock(&thd->mysys_var->mutex);
thd_proc_info(thd, 0);
thd->mysys_var->current_mutex= 0;
thd->mysys_var->current_cond= 0;
- pthread_mutex_unlock(&thd->mysys_var->mutex);
+ mysql_mutex_unlock(&thd->mysys_var->mutex);
DBUG_RETURN(!error ? 1 : 0);
}
@@ -3868,10 +4306,10 @@ longlong Item_func_release_lock::val_int()
null_value=0;
result=0;
- pthread_mutex_lock(&LOCK_user_locks);
- if (!(ull= ((User_level_lock*) hash_search(&hash_user_locks,
- (const uchar*) res->ptr(),
- (size_t) res->length()))))
+ mysql_mutex_lock(&LOCK_user_locks);
+ if (!(ull= ((User_level_lock*) my_hash_search(&hash_user_locks,
+ (const uchar*) res->ptr(),
+ (size_t) res->length()))))
{
null_value=1;
}
@@ -3889,7 +4327,7 @@ longlong Item_func_release_lock::val_int()
thd->ull=0;
}
}
- pthread_mutex_unlock(&LOCK_user_locks);
+ mysql_mutex_unlock(&LOCK_user_locks);
DBUG_RETURN(result);
}
@@ -3945,7 +4383,7 @@ longlong Item_func_benchmark::val_int()
{
char buff[22];
llstr(((longlong) loop_count), buff);
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WRONG_VALUE_FOR_TYPE, ER(ER_WRONG_VALUE_FOR_TYPE),
"count", buff, "benchmark");
}
@@ -3996,50 +4434,53 @@ void Item_func_benchmark::print(String *str, enum_query_type query_type)
longlong Item_func_sleep::val_int()
{
THD *thd= current_thd;
- struct timespec abstime;
- pthread_cond_t cond;
+ Interruptible_wait timed_cond(thd);
+ mysql_cond_t cond;
+ double timeout;
int error;
DBUG_ASSERT(fixed == 1);
- double time= args[0]->val_real();
+ timeout= args[0]->val_real();
/*
- On 64-bit OSX pthread_cond_timedwait() waits forever
+ On 64-bit OSX mysql_cond_timedwait() waits forever
if passed abstime time has already been exceeded by
the system time.
When given a very short timeout (< 10 mcs) just return
immediately.
We assume that the lines between this test and the call
- to pthread_cond_timedwait() will be executed in less than 0.00001 sec.
+ to mysql_cond_timedwait() will be executed in less than 0.00001 sec.
*/
- if (time < 0.00001)
+ if (timeout < 0.00001)
return 0;
-
- set_timespec_nsec(abstime, (ulonglong)(time * ULL(1000000000)));
- pthread_cond_init(&cond, NULL);
- pthread_mutex_lock(&LOCK_user_locks);
+ timed_cond.set_timeout((ulonglong) (timeout * 1000000000.0));
+
+ mysql_cond_init(key_item_func_sleep_cond, &cond, NULL);
+ mysql_mutex_lock(&LOCK_user_locks);
thd_proc_info(thd, "User sleep");
thd->mysys_var->current_mutex= &LOCK_user_locks;
thd->mysys_var->current_cond= &cond;
error= 0;
+ thd_wait_begin(thd, THD_WAIT_SLEEP);
while (!thd->killed)
{
- error= pthread_cond_timedwait(&cond, &LOCK_user_locks, &abstime);
+ error= timed_cond.wait(&cond, &LOCK_user_locks);
if (error == ETIMEDOUT || error == ETIME)
break;
error= 0;
}
+ thd_wait_end(thd);
thd_proc_info(thd, 0);
- pthread_mutex_unlock(&LOCK_user_locks);
- pthread_mutex_lock(&thd->mysys_var->mutex);
+ mysql_mutex_unlock(&LOCK_user_locks);
+ mysql_mutex_lock(&thd->mysys_var->mutex);
thd->mysys_var->current_mutex= 0;
thd->mysys_var->current_cond= 0;
- pthread_mutex_unlock(&thd->mysys_var->mutex);
+ mysql_mutex_unlock(&thd->mysys_var->mutex);
- pthread_cond_destroy(&cond);
+ mysql_cond_destroy(&cond);
return test(!error); // Return 1 killed
}
@@ -4052,14 +4493,14 @@ static user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
{
user_var_entry *entry;
- if (!(entry = (user_var_entry*) 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;
- if (!hash_inited(hash))
+ if (!my_hash_inited(hash))
return 0;
- if (!(entry = (user_var_entry*) my_malloc(size,MYF(MY_WME))))
+ if (!(entry = (user_var_entry*) my_malloc(size,MYF(MY_WME | ME_FATALERROR))))
return 0;
entry->name.str=(char*) entry+ ALIGN_SIZE(sizeof(user_var_entry))+
extra_size;
@@ -4084,7 +4525,7 @@ static user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
memcpy(entry->name.str, name.str, name.length+1);
if (my_hash_insert(hash,(uchar*) entry))
{
- my_free((char*) entry,MYF(0));
+ my_free(entry);
return 0;
}
}
@@ -4148,7 +4589,9 @@ bool Item_func_set_user_var::fix_fields(THD *thd, Item **ref)
*/
null_item= (args[0]->type() == NULL_ITEM);
if (!entry->collation.collation || !null_item)
- entry->collation.set(args[0]->collation.collation, DERIVATION_IMPLICIT);
+ entry->collation.set(args[0]->collation.derivation == DERIVATION_NUMERIC ?
+ default_charset() : args[0]->collation.collation,
+ DERIVATION_IMPLICIT);
collation.set(entry->collation.collation, DERIVATION_IMPLICIT);
cached_result_type= args[0]->result_type();
if (thd->lex->current_select)
@@ -4174,10 +4617,16 @@ void
Item_func_set_user_var::fix_length_and_dec()
{
maybe_null=args[0]->maybe_null;
- max_length=args[0]->max_length;
decimals=args[0]->decimals;
+ collation.set(DERIVATION_IMPLICIT);
+ if (args[0]->collation.derivation == DERIVATION_NUMERIC)
+ fix_length_and_charset(args[0]->max_char_length(), default_charset());
+ else
+ {
+ fix_length_and_charset(args[0]->max_char_length(),
+ args[0]->collation.collation);
+ }
unsigned_flag= args[0]->unsigned_flag;
- collation.set(args[0]->collation.collation, DERIVATION_IMPLICIT);
}
@@ -4233,6 +4682,8 @@ bool Item_func_set_user_var::register_field_in_bitmap(uchar *arg)
@param dv derivation for new value
@param unsigned_arg indiates if a value of type INT_RESULT is unsigned
+ @note Sets error and fatal error if allocation fails.
+
@retval
false success
@retval
@@ -4248,7 +4699,7 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length,
{
char *pos= (char*) entry+ ALIGN_SIZE(sizeof(user_var_entry));
if (entry->value && entry->value != pos)
- my_free(entry->value,MYF(0));
+ my_free(entry->value);
entry->value= 0;
entry->length= 0;
}
@@ -4263,7 +4714,7 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length,
if (entry->value != pos)
{
if (entry->value)
- my_free(entry->value,MYF(0));
+ my_free(entry->value);
entry->value=pos;
}
}
@@ -4276,7 +4727,8 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length,
if (entry->value == pos)
entry->value=0;
entry->value= (char*) my_realloc(entry->value, length,
- MYF(MY_ALLOW_ZERO_PTR | MY_WME));
+ MYF(MY_ALLOW_ZERO_PTR | MY_WME |
+ ME_FATALERROR));
if (!entry->value)
return 1;
}
@@ -4313,7 +4765,6 @@ Item_func_set_user_var::update_hash(void *ptr, uint length,
if (::update_hash(entry, (null_value= args[0]->null_value),
ptr, length, res_type, cs, dv, unsigned_arg))
{
- current_thd->fatal_error(); // Probably end of memory
null_value= 1;
return 1;
}
@@ -4394,16 +4845,16 @@ String *user_var_entry::val_str(bool *null_value, String *str,
switch (type) {
case REAL_RESULT:
- str->set_real(*(double*) value, decimals, &my_charset_bin);
+ str->set_real(*(double*) value, decimals, collation.collation);
break;
case INT_RESULT:
if (!unsigned_flag)
- str->set(*(longlong*) value, &my_charset_bin);
+ str->set(*(longlong*) value, collation.collation);
else
- str->set(*(ulonglong*) value, &my_charset_bin);
+ str->set(*(ulonglong*) value, collation.collation);
break;
case DECIMAL_RESULT:
- my_decimal2string(E_DEC_FATAL_ERROR, (my_decimal *)value, 0, 0, 0, str);
+ str_set_decimal((my_decimal *) value, str, collation.collation);
break;
case STRING_RESULT:
if (str->copy(value, length, collation.collation))
@@ -4567,13 +5018,13 @@ Item_func_set_user_var::update()
case REAL_RESULT:
{
res= update_hash((void*) &save_result.vreal,sizeof(save_result.vreal),
- REAL_RESULT, &my_charset_bin, DERIVATION_IMPLICIT, 0);
+ REAL_RESULT, default_charset(), DERIVATION_IMPLICIT, 0);
break;
}
case INT_RESULT:
{
res= update_hash((void*) &save_result.vint, sizeof(save_result.vint),
- INT_RESULT, &my_charset_bin, DERIVATION_IMPLICIT,
+ INT_RESULT, default_charset(), DERIVATION_IMPLICIT,
unsigned_flag);
break;
}
@@ -4597,7 +5048,7 @@ Item_func_set_user_var::update()
else
res= update_hash((void*) save_result.vdec,
sizeof(my_decimal), DECIMAL_RESULT,
- &my_charset_bin, DERIVATION_IMPLICIT, 0);
+ default_charset(), DERIVATION_IMPLICIT, 0);
break;
}
case ROW_RESULT:
@@ -4899,8 +5350,9 @@ longlong Item_func_get_user_var::val_int()
*/
-int get_var_with_binlog(THD *thd, enum_sql_command sql_command,
- LEX_STRING &name, user_var_entry **out_entry)
+static int
+get_var_with_binlog(THD *thd, enum_sql_command sql_command,
+ LEX_STRING &name, user_var_entry **out_entry)
{
BINLOG_USER_VAR_EVENT *user_var_event;
user_var_entry *var_entry;
@@ -4989,6 +5441,7 @@ int get_var_with_binlog(THD *thd, enum_sql_command sql_command,
user_var_event->user_var_event= var_entry;
user_var_event->type= var_entry->type;
user_var_event->charset_number= var_entry->collation.collation->number;
+ user_var_event->unsigned_flag= var_entry->unsigned_flag;
if (!var_entry->value)
{
/* NULL value*/
@@ -5029,7 +5482,7 @@ void Item_func_get_user_var::fix_length_and_dec()
'var_entry' is NULL only if there occured an error during the call to
get_var_with_binlog.
*/
- if (var_entry)
+ if (!error && var_entry)
{
m_cached_result_type= var_entry->type;
unsigned_flag= var_entry->unsigned_flag;
@@ -5038,17 +5491,17 @@ void Item_func_get_user_var::fix_length_and_dec()
collation.set(var_entry->collation);
switch (m_cached_result_type) {
case REAL_RESULT:
- max_length= DBL_DIG + 8;
+ fix_char_length(DBL_DIG + 8);
break;
case INT_RESULT:
- max_length= MAX_BIGINT_WIDTH;
+ fix_char_length(MAX_BIGINT_WIDTH);
decimals=0;
break;
case STRING_RESULT:
max_length= MAX_BLOB_WIDTH - 1;
break;
case DECIMAL_RESULT:
- max_length= DECIMAL_MAX_STR_LENGTH;
+ fix_char_length(DECIMAL_MAX_STR_LENGTH);
decimals= DECIMAL_MAX_SCALE;
break;
case ROW_RESULT: // Keep compiler happy
@@ -5065,11 +5518,6 @@ void Item_func_get_user_var::fix_length_and_dec()
m_cached_result_type= STRING_RESULT;
max_length= MAX_BLOB_WIDTH;
}
-
- if (error)
- thd->fatal_error();
-
- return;
}
@@ -5143,18 +5591,16 @@ bool Item_user_var_as_out_param::fix_fields(THD *thd, Item **ref)
void Item_user_var_as_out_param::set_null_value(CHARSET_INFO* cs)
{
- if (::update_hash(entry, TRUE, 0, 0, STRING_RESULT, cs,
- DERIVATION_IMPLICIT, 0 /* unsigned_arg */))
- current_thd->fatal_error(); // Probably end of memory
+ ::update_hash(entry, TRUE, 0, 0, STRING_RESULT, cs,
+ DERIVATION_IMPLICIT, 0 /* unsigned_arg */);
}
void Item_user_var_as_out_param::set_value(const char *str, uint length,
CHARSET_INFO* cs)
{
- if (::update_hash(entry, FALSE, (void*)str, length, STRING_RESULT, cs,
- DERIVATION_IMPLICIT, 0 /* unsigned_arg */))
- current_thd->fatal_error(); // Probably end of memory
+ ::update_hash(entry, FALSE, (void*)str, length, STRING_RESULT, cs,
+ DERIVATION_IMPLICIT, 0 /* unsigned_arg */);
}
@@ -5232,7 +5678,7 @@ void Item_func_get_system_var::fix_length_and_dec()
if (var_type != OPT_DEFAULT)
{
my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0),
- var->name, var_type == OPT_GLOBAL ? "SESSION" : "GLOBAL");
+ var->name.str, var_type == OPT_GLOBAL ? "SESSION" : "GLOBAL");
return;
}
/* As there was no local variable, return the global value */
@@ -5241,47 +5687,60 @@ void Item_func_get_system_var::fix_length_and_dec()
switch (var->show_type())
{
- case SHOW_LONG:
case SHOW_HA_ROWS:
+ case SHOW_UINT:
+ case SHOW_ULONG:
+ case SHOW_ULONGLONG:
unsigned_flag= TRUE;
- max_length= MY_INT64_NUM_DECIMAL_DIGITS;
- decimals=0;
- break;
- case SHOW_INT:
- unsigned_flag= FALSE;
- max_length= MY_INT64_NUM_DECIMAL_DIGITS;
- decimals=0;
- break;
- case SHOW_LONGLONG:
- unsigned_flag= TRUE;
- max_length= MY_INT64_NUM_DECIMAL_DIGITS;
+ /* fall through */
+ case SHOW_SINT:
+ case SHOW_SLONG:
+ case SHOW_SLONGLONG:
+ collation.set_numeric();
+ fix_char_length(MY_INT64_NUM_DECIMAL_DIGITS);
decimals=0;
break;
case SHOW_CHAR:
case SHOW_CHAR_PTR:
- pthread_mutex_lock(&LOCK_global_system_variables);
- cptr= var->show_type() == SHOW_CHAR_PTR ?
- *(char**) var->value_ptr(current_thd, var_type, &component) :
- (char*) var->value_ptr(current_thd, var_type, &component);
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ cptr= var->show_type() == SHOW_CHAR ?
+ (char*) var->value_ptr(current_thd, var_type, &component) :
+ *(char**) var->value_ptr(current_thd, var_type, &component);
if (cptr)
- max_length= strlen(cptr) * system_charset_info->mbmaxlen;
- pthread_mutex_unlock(&LOCK_global_system_variables);
+ max_length= system_charset_info->cset->numchars(system_charset_info,
+ cptr,
+ cptr + strlen(cptr));
+ mysql_mutex_unlock(&LOCK_global_system_variables);
collation.set(system_charset_info, DERIVATION_SYSCONST);
+ max_length*= system_charset_info->mbmaxlen;
decimals=NOT_FIXED_DEC;
break;
+ case SHOW_LEX_STRING:
+ {
+ 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,
+ ls->str,
+ ls->str + ls->length);
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ collation.set(system_charset_info, DERIVATION_SYSCONST);
+ max_length*= system_charset_info->mbmaxlen;
+ decimals=NOT_FIXED_DEC;
+ }
+ break;
case SHOW_BOOL:
case SHOW_MY_BOOL:
- unsigned_flag= FALSE;
- max_length= 1;
+ collation.set_numeric();
+ fix_char_length(1);
decimals=0;
break;
case SHOW_DOUBLE:
- unsigned_flag= FALSE;
decimals= 6;
- max_length= DBL_DIG + 6;
+ collation.set_numeric();
+ fix_char_length(DBL_DIG + 6);
break;
default:
- my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name);
+ my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name.str);
break;
}
}
@@ -5299,18 +5758,22 @@ enum Item_result Item_func_get_system_var::result_type() const
{
case SHOW_BOOL:
case SHOW_MY_BOOL:
- case SHOW_INT:
- case SHOW_LONG:
- case SHOW_LONGLONG:
+ 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);
+ my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name.str);
return STRING_RESULT; // keep the compiler happy
}
}
@@ -5322,18 +5785,22 @@ enum_field_types Item_func_get_system_var::field_type() const
{
case SHOW_BOOL:
case SHOW_MY_BOOL:
- case SHOW_INT:
- case SHOW_LONG:
- case SHOW_LONGLONG:
+ case SHOW_SINT:
+ case SHOW_SLONG:
+ case SHOW_SLONGLONG:
+ case SHOW_UINT:
+ case SHOW_ULONG:
+ case SHOW_ULONGLONG:
case SHOW_HA_ROWS:
return MYSQL_TYPE_LONGLONG;
case SHOW_CHAR:
case SHOW_CHAR_PTR:
+ case SHOW_LEX_STRING:
return MYSQL_TYPE_VARCHAR;
case SHOW_DOUBLE:
return MYSQL_TYPE_DOUBLE;
default:
- my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name);
+ my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name.str);
return MYSQL_TYPE_VARCHAR; // keep the compiler happy
}
}
@@ -5346,9 +5813,9 @@ enum_field_types Item_func_get_system_var::field_type() const
#define get_sys_var_safe(type) \
do { \
type value; \
- pthread_mutex_lock(&LOCK_global_system_variables); \
+ mysql_mutex_lock(&LOCK_global_system_variables); \
value= *(type*) var->value_ptr(thd, var_type, &component); \
- pthread_mutex_unlock(&LOCK_global_system_variables); \
+ mysql_mutex_unlock(&LOCK_global_system_variables); \
cache_present |= GET_SYS_VAR_CACHE_LONG; \
used_query_id= thd->query_id; \
cached_llval= null_value ? 0 : (longlong) value; \
@@ -5392,9 +5859,12 @@ longlong Item_func_get_system_var::val_int()
switch (var->show_type())
{
- case SHOW_INT: get_sys_var_safe (int);
- case SHOW_LONG: get_sys_var_safe (ulong);
- case SHOW_LONGLONG: get_sys_var_safe (ulonglong);
+ case SHOW_SINT: get_sys_var_safe (int);
+ case SHOW_SLONG: get_sys_var_safe (long);
+ case SHOW_SLONGLONG:get_sys_var_safe (longlong);
+ case SHOW_UINT: get_sys_var_safe (uint);
+ case SHOW_ULONG: get_sys_var_safe (ulong);
+ case SHOW_ULONGLONG:get_sys_var_safe (ulonglong);
case SHOW_HA_ROWS: get_sys_var_safe (ha_rows);
case SHOW_BOOL: get_sys_var_safe (bool);
case SHOW_MY_BOOL: get_sys_var_safe (my_bool);
@@ -5409,6 +5879,7 @@ longlong Item_func_get_system_var::val_int()
}
case SHOW_CHAR:
case SHOW_CHAR_PTR:
+ case SHOW_LEX_STRING:
{
String *str_val= val_str(NULL);
@@ -5428,7 +5899,7 @@ longlong Item_func_get_system_var::val_int()
}
default:
- my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name);
+ my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name.str);
return 0; // keep the compiler happy
}
}
@@ -5468,14 +5939,18 @@ String* Item_func_get_system_var::val_str(String* str)
{
case SHOW_CHAR:
case SHOW_CHAR_PTR:
+ case SHOW_LEX_STRING:
{
- pthread_mutex_lock(&LOCK_global_system_variables);
- char *cptr= var->show_type() == SHOW_CHAR_PTR ?
- *(char**) var->value_ptr(thd, var_type, &component) :
- (char*) var->value_ptr(thd, var_type, &component);
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ char *cptr= var->show_type() == SHOW_CHAR ?
+ (char*) var->value_ptr(thd, var_type, &component) :
+ *(char**) var->value_ptr(thd, var_type, &component);
if (cptr)
{
- if (str->copy(cptr, strlen(cptr), collation.collation))
+ size_t len= var->show_type() == SHOW_LEX_STRING ?
+ ((LEX_STRING*)(var->value_ptr(thd, var_type, &component)))->length :
+ strlen(cptr);
+ if (str->copy(cptr, len, collation.collation))
{
null_value= TRUE;
str= NULL;
@@ -5486,13 +5961,16 @@ String* Item_func_get_system_var::val_str(String* str)
null_value= TRUE;
str= NULL;
}
- pthread_mutex_unlock(&LOCK_global_system_variables);
+ mysql_mutex_unlock(&LOCK_global_system_variables);
break;
}
- case SHOW_INT:
- case SHOW_LONG:
- case SHOW_LONGLONG:
+ case SHOW_SINT:
+ case SHOW_SLONG:
+ case SHOW_SLONGLONG:
+ case SHOW_UINT:
+ case SHOW_ULONG:
+ case SHOW_ULONGLONG:
case SHOW_HA_ROWS:
case SHOW_BOOL:
case SHOW_MY_BOOL:
@@ -5503,7 +5981,7 @@ String* Item_func_get_system_var::val_str(String* str)
break;
default:
- my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name);
+ my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name.str);
str= NULL;
break;
}
@@ -5551,9 +6029,9 @@ double Item_func_get_system_var::val_real()
switch (var->show_type())
{
case SHOW_DOUBLE:
- pthread_mutex_lock(&LOCK_global_system_variables);
+ mysql_mutex_lock(&LOCK_global_system_variables);
cached_dval= *(double*) var->value_ptr(thd, var_type, &component);
- pthread_mutex_unlock(&LOCK_global_system_variables);
+ mysql_mutex_unlock(&LOCK_global_system_variables);
used_query_id= thd->query_id;
cached_null_value= null_value;
if (null_value)
@@ -5561,12 +6039,11 @@ double Item_func_get_system_var::val_real()
cache_present|= GET_SYS_VAR_CACHE_DOUBLE;
return cached_dval;
case SHOW_CHAR:
+ case SHOW_LEX_STRING:
case SHOW_CHAR_PTR:
{
- char *cptr;
-
- pthread_mutex_lock(&LOCK_global_system_variables);
- cptr= var->show_type() == SHOW_CHAR ?
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ char *cptr= var->show_type() == SHOW_CHAR ?
(char*) var->value_ptr(thd, var_type, &component) :
*(char**) var->value_ptr(thd, var_type, &component);
if (cptr)
@@ -5577,15 +6054,18 @@ double Item_func_get_system_var::val_real()
null_value= TRUE;
cached_dval= 0;
}
- pthread_mutex_unlock(&LOCK_global_system_variables);
+ mysql_mutex_unlock(&LOCK_global_system_variables);
used_query_id= thd->query_id;
cached_null_value= null_value;
cache_present|= GET_SYS_VAR_CACHE_DOUBLE;
return cached_dval;
}
- case SHOW_INT:
- case SHOW_LONG:
- case SHOW_LONGLONG:
+ case SHOW_SINT:
+ case SHOW_SLONG:
+ case SHOW_SLONGLONG:
+ case SHOW_UINT:
+ case SHOW_ULONG:
+ case SHOW_ULONGLONG:
case SHOW_HA_ROWS:
case SHOW_BOOL:
case SHOW_MY_BOOL:
@@ -5595,7 +6075,7 @@ double Item_func_get_system_var::val_real()
cached_null_value= null_value;
return cached_dval;
default:
- my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name);
+ my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name.str);
return 0;
}
}
@@ -5634,8 +6114,8 @@ longlong Item_func_inet_aton::val_int()
char buff[36];
int dot_count= 0;
- String *s,tmp(buff,sizeof(buff),&my_charset_bin);
- if (!(s = args[0]->val_str(&tmp))) // If null value
+ String *s, tmp(buff, sizeof(buff), &my_charset_latin1);
+ if (!(s = args[0]->val_str_ascii(&tmp))) // If null value
goto err;
null_value=0;
@@ -5643,7 +6123,7 @@ longlong Item_func_inet_aton::val_int()
while (p < end)
{
c = *p++;
- int digit = (int) (c - '0'); // Assume ascii
+ int digit = (int) (c - '0');
if (digit >= 0 && digit <= 9)
{
if ((byte_result = byte_result * 10 + digit) > 255)
@@ -5756,6 +6236,8 @@ bool Item_func_match::fix_fields(THD *thd, Item **ref)
DBUG_ASSERT(fixed == 0);
Item *UNINIT_VAR(item); // Safe as arg_count is > 1
+ status_var_increment(thd->status_var.feature_fulltext);
+
maybe_null=1;
join_key=0;
@@ -5773,16 +6255,37 @@ bool Item_func_match::fix_fields(THD *thd, Item **ref)
}
const_item_cache=0;
+ table= 0;
for (uint i=1 ; i < arg_count ; i++)
{
item=args[i];
if (item->type() == Item::REF_ITEM)
args[i]= item= *((Item_ref *)item)->ref;
- if (item->type() != Item::FIELD_ITEM)
+ /*
+ When running in PS mode, some Item_field's can already be replaced
+ to Item_func_conv_charset during PREPARE time. This is possible
+ in case of "MATCH (f1,..,fN) AGAINST (... IN BOOLEAN MODE)"
+ when running without any fulltext indexes and when fields f1..fN
+ have different character sets.
+ So we check for FIELD_ITEM only during prepare time and in non-PS mode,
+ and do not check in PS execute time.
+ */
+ if (!thd->stmt_arena->is_stmt_execute() &&
+ item->type() != Item::FIELD_ITEM)
{
my_error(ER_WRONG_ARGUMENTS, MYF(0), "AGAINST");
return TRUE;
}
+ /*
+ During the prepare-time execution of fix_fields() of a PS query some
+ Item_fields's could have been already replaced to Item_func_conv_charset
+ (by the call for agg_arg_charsets_for_comparison below()).
+ But agg_arg_charsets_for_comparison() is written in a way that
+ at least *one* of the Item_field's is not replaced.
+ This makes sure that "table" gets initialized during PS execution time.
+ */
+ if (item->type() == Item::FIELD_ITEM)
+ table= ((Item_field *)item)->field->table;
}
/*
Check that all columns come from the same table.
@@ -5797,15 +6300,13 @@ bool Item_func_match::fix_fields(THD *thd, Item **ref)
my_error(ER_WRONG_ARGUMENTS,MYF(0),"MATCH");
return TRUE;
}
- table=((Item_field *)item)->field->table;
if (!(table->file->ha_table_flags() & HA_CAN_FULLTEXT))
{
my_error(ER_TABLE_CANT_HANDLE_FT, MYF(0));
return 1;
}
table->fulltext_searched=1;
- return agg_arg_collations_for_comparison(cmp_collation,
- args+1, arg_count-1, 0);
+ return agg_arg_charsets_for_comparison(cmp_collation, args+1, arg_count-1);
}
bool Item_func_match::fix_index()
@@ -6052,10 +6553,10 @@ longlong Item_func_is_free_lock::val_int()
return 0;
}
- pthread_mutex_lock(&LOCK_user_locks);
- ull= (User_level_lock *) hash_search(&hash_user_locks, (uchar*) res->ptr(),
- (size_t) res->length());
- pthread_mutex_unlock(&LOCK_user_locks);
+ mysql_mutex_lock(&LOCK_user_locks);
+ ull= (User_level_lock *) my_hash_search(&hash_user_locks, (uchar*) res->ptr(),
+ (size_t) res->length());
+ mysql_mutex_unlock(&LOCK_user_locks);
if (!ull || !ull->locked)
return 1;
return 0;
@@ -6071,10 +6572,10 @@ longlong Item_func_is_used_lock::val_int()
if (!res || !res->length())
return 0;
- pthread_mutex_lock(&LOCK_user_locks);
- ull= (User_level_lock *) hash_search(&hash_user_locks, (uchar*) res->ptr(),
- (size_t) res->length());
- pthread_mutex_unlock(&LOCK_user_locks);
+ mysql_mutex_lock(&LOCK_user_locks);
+ ull= (User_level_lock *) my_hash_search(&hash_user_locks, (uchar*) res->ptr(),
+ (size_t) res->length());
+ mysql_mutex_unlock(&LOCK_user_locks);
if (!ull || !ull->locked)
return 0;
@@ -6088,7 +6589,7 @@ longlong Item_func_row_count::val_int()
DBUG_ASSERT(fixed == 1);
THD *thd= current_thd;
- return thd->row_count_func;
+ return thd->get_row_count_func();
}
@@ -6154,12 +6655,12 @@ Item_func_sp::func_name() const
}
-int my_missing_function_error(const LEX_STRING &token, const char *func_name)
+void my_missing_function_error(const LEX_STRING &token, const char *func_name)
{
if (token.length && is_lex_native_function (&token))
- return my_error(ER_FUNC_INEXISTENT_NAME_COLLISION, MYF(0), func_name);
+ my_error(ER_FUNC_INEXISTENT_NAME_COLLISION, MYF(0), func_name);
else
- return my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", func_name);
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", func_name);
}
@@ -6464,7 +6965,7 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
if (res)
DBUG_RETURN(res);
- if (thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW)
+ if (thd->lex->is_view_context_analysis())
{
/*
Here we check privileges of the stored routine only during view
@@ -6541,8 +7042,67 @@ void uuid_short_init()
longlong Item_func_uuid_short::val_int()
{
ulonglong val;
- pthread_mutex_lock(&LOCK_short_uuid_generator);
+ mysql_mutex_lock(&LOCK_short_uuid_generator);
val= uuid_value++;
- pthread_mutex_unlock(&LOCK_short_uuid_generator);
+ mysql_mutex_unlock(&LOCK_short_uuid_generator);
return (longlong) val;
}
+
+
+/**
+ Last_value - return last argument.
+*/
+
+void Item_func_last_value::evaluate_sideeffects()
+{
+ DBUG_ASSERT(fixed == 1 && arg_count > 0);
+ for (uint i= 0; i < arg_count-1 ; i++)
+ args[i]->val_int();
+}
+
+String *Item_func_last_value::val_str(String *str)
+{
+ String *tmp;
+ evaluate_sideeffects();
+ tmp= last_value->val_str(str);
+ null_value= last_value->null_value;
+ return tmp;
+}
+
+longlong Item_func_last_value::val_int()
+{
+ longlong tmp;
+ evaluate_sideeffects();
+ tmp= last_value->val_int();
+ null_value= last_value->null_value;
+ return tmp;
+}
+
+double Item_func_last_value::val_real()
+{
+ double tmp;
+ evaluate_sideeffects();
+ tmp= last_value->val_real();
+ null_value= last_value->null_value;
+ return tmp;
+}
+
+my_decimal *Item_func_last_value::val_decimal(my_decimal *decimal_value)
+{
+ my_decimal *tmp;
+ evaluate_sideeffects();
+ tmp= last_value->val_decimal(decimal_value);
+ null_value= last_value->null_value;
+ return tmp;
+}
+
+
+void Item_func_last_value::fix_length_and_dec()
+{
+ last_value= args[arg_count -1];
+ decimals= last_value->decimals;
+ max_length= last_value->max_length;
+ collation.set(last_value->collation.collation);
+ maybe_null= last_value->maybe_null;
+ unsigned_flag= last_value->unsigned_flag;
+}
diff --git a/sql/item_func.h b/sql/item_func.h
index deaaee2daa0..ab0ae5f0bda 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -1,5 +1,7 @@
+#ifndef ITEM_FUNC_INCLUDED
+#define ITEM_FUNC_INCLUDED
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2009, 2014, Monty Program Ab.
+ Copyright (c) 2009, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +14,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
/* Function items used by mysql */
@@ -148,13 +149,13 @@ public:
void count_only_length(Item **item, uint nitems);
void count_real_length();
void count_decimal_length();
- void count_datetime_length(Item **item, uint nitems);
- bool count_string_result_length(enum_field_types field_type,
- Item **item, uint nitems);
- inline bool get_arg0_date(MYSQL_TIME *ltime, uint fuzzy_date)
+ inline bool get_arg0_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
return (null_value=args[0]->get_date(ltime, fuzzy_date));
}
+ void count_datetime_length(Item **item, uint nitems);
+ bool count_string_result_length(enum_field_types field_type,
+ Item **item, uint nitems);
inline bool get_arg0_time(MYSQL_TIME *ltime)
{
null_value= args[0]->get_time(ltime);
@@ -174,23 +175,62 @@ public:
my_decimal *val_decimal(my_decimal *);
- bool agg_arg_collations(DTCollation &c, Item **items, uint nitems,
- uint flags)
- {
- return agg_item_collations(c, func_name(), items, nitems, flags, 1);
- }
- bool agg_arg_collations_for_comparison(DTCollation &c,
- Item **items, uint nitems,
- uint flags)
+ void fix_char_length_ulonglong(ulonglong max_char_length_arg)
{
- return agg_item_collations_for_comparison(c, func_name(),
- items, nitems, flags);
+ ulonglong max_result_length= max_char_length_arg *
+ collation.collation->mbmaxlen;
+ if (max_result_length >= MAX_BLOB_WIDTH)
+ {
+ max_length= MAX_BLOB_WIDTH;
+ maybe_null= 1;
+ }
+ else
+ max_length= (uint32) max_result_length;
}
bool agg_arg_charsets(DTCollation &c, Item **items, uint nitems,
uint flags, int item_sep)
{
return agg_item_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)
+ {
+ return agg_item_charsets_for_string_result(c, func_name(),
+ items, nitems, 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,
+ Item **items, uint nitems,
+ int item_sep= 1)
+ {
+ return agg_item_charsets_for_comparison(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)
+ {
+ return agg_item_charsets_for_string_result_with_comparison(c, func_name(),
+ items, nitems,
+ item_sep);
+ }
bool walk(Item_processor processor, bool walk_subquery, uchar *arg);
Item *transform(Item_transformer transformer, uchar *arg);
Item* compile(Item_analyzer analyzer, uchar **arg_p,
@@ -200,13 +240,56 @@ public:
bool eval_not_null_tables(uchar *opt_arg);
// bool is_expensive_processor(uchar *arg);
// virtual bool is_expensive() { return 0; }
- inline double fix_result(double value)
+ inline void raise_numeric_overflow(const char *type_name)
+ {
+ char buf[256];
+ String str(buf, sizeof(buf), system_charset_info);
+ str.length(0);
+ print(&str, QT_ORDINARY);
+ my_error(ER_DATA_OUT_OF_RANGE, MYF(0), type_name, str.c_ptr_safe());
+ }
+ inline double raise_float_overflow()
{
- if (isfinite(value))
- return value;
- null_value=1;
+ raise_numeric_overflow("DOUBLE");
return 0.0;
}
+ inline longlong raise_integer_overflow()
+ {
+ raise_numeric_overflow(unsigned_flag ? "BIGINT UNSIGNED": "BIGINT");
+ return 0;
+ }
+ inline int raise_decimal_overflow()
+ {
+ raise_numeric_overflow("DECIMAL");
+ return E_DEC_OVERFLOW;
+ }
+ /**
+ Throw an error if the input double number is not finite, i.e. is either
+ +/-INF or NAN.
+ */
+ inline double check_float_overflow(double value)
+ {
+ return isfinite(value) ? value : raise_float_overflow();
+ }
+ /**
+ Throw an error if the input BIGINT value represented by the
+ (longlong value, bool unsigned flag) pair cannot be returned by the
+ function, i.e. is not compatible with this Item's unsigned_flag.
+ */
+ inline longlong check_integer_overflow(longlong value, bool val_unsigned)
+ {
+ if ((unsigned_flag && !val_unsigned && value < 0) ||
+ (!unsigned_flag && val_unsigned && (ulonglong) value > LONGLONG_MAX))
+ return raise_integer_overflow();
+ return value;
+ }
+ /**
+ Throw an error if the error code of a DECIMAL operation is E_DEC_OVERFLOW.
+ */
+ inline int check_decimal_overflow(int error)
+ {
+ return (error == E_DEC_OVERFLOW) ? raise_decimal_overflow() : error;
+ }
bool has_timestamp_args()
{
@@ -311,10 +394,10 @@ public:
class Item_real_func :public Item_func
{
public:
- Item_real_func() :Item_func() {}
- Item_real_func(Item *a) :Item_func(a) {}
- Item_real_func(Item *a,Item *b) :Item_func(a,b) {}
- Item_real_func(List<Item> &list) :Item_func(list) {}
+ Item_real_func() :Item_func() { collation.set_numeric(); }
+ Item_real_func(Item *a) :Item_func(a) { collation.set_numeric(); }
+ Item_real_func(Item *a,Item *b) :Item_func(a,b) { collation.set_numeric(); }
+ Item_real_func(List<Item> &list) :Item_func(list) { collation.set_numeric(); }
String *val_str(String*str);
my_decimal *val_decimal(my_decimal *decimal_value);
longlong val_int()
@@ -332,18 +415,18 @@ protected:
public:
Item_func_hybrid_result_type() :Item_func(), cached_result_type(REAL_RESULT)
- {}
+ { collation.set_numeric(); }
Item_func_hybrid_result_type(Item *a) :Item_func(a), cached_result_type(REAL_RESULT)
- {}
+ { collation.set_numeric(); }
Item_func_hybrid_result_type(Item *a,Item *b)
:Item_func(a,b), cached_result_type(REAL_RESULT)
- {}
+ { collation.set_numeric(); }
Item_func_hybrid_result_type(Item *a,Item *b,Item *c)
:Item_func(a,b,c), cached_result_type(REAL_RESULT)
- {}
+ { collation.set_numeric(); }
Item_func_hybrid_result_type(List<Item> &list)
:Item_func(list), cached_result_type(REAL_RESULT)
- {}
+ { collation.set_numeric(); }
enum Item_result result_type () const { return cached_result_type; }
@@ -351,7 +434,7 @@ public:
longlong val_int();
my_decimal *val_decimal(my_decimal *);
String *val_str(String*str);
- bool get_date(MYSQL_TIME *res, uint fuzzy_date);
+ bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
/**
@brief Performs the operation that this functions implements when the
@@ -437,18 +520,18 @@ protected:
public:
Item_func_numhybrid() :Item_func_hybrid_result_type()
- {}
+ { }
Item_func_numhybrid(Item *a) :Item_func_hybrid_result_type(a)
- {}
+ { }
Item_func_numhybrid(Item *a,Item *b)
:Item_func_hybrid_result_type(a,b)
- {}
+ { }
Item_func_numhybrid(Item *a,Item *b,Item *c)
:Item_func_hybrid_result_type(a,b,c)
- {}
+ { }
Item_func_numhybrid(List<Item> &list)
:Item_func_hybrid_result_type(list)
- {}
+ { }
String *str_op(String *str) { DBUG_ASSERT(0); return 0; }
bool date_op(MYSQL_TIME *ltime, uint fuzzydate) { DBUG_ASSERT(0); return true; }
};
@@ -485,16 +568,18 @@ class Item_int_func :public Item_func
protected:
bool sargable;
public:
- Item_int_func() :Item_func() { max_length= 21; sargable= false; }
- Item_int_func(Item *a) :Item_func(a) { max_length= 21; sargable= false; }
+ Item_int_func() :Item_func()
+ { collation.set_numeric(); fix_char_length(21); sargable= false; }
+ Item_int_func(Item *a) :Item_func(a)
+ { collation.set_numeric(); fix_char_length(21); sargable= false; }
Item_int_func(Item *a,Item *b) :Item_func(a,b)
- { max_length= 21; sargable= false; }
+ { collation.set_numeric(); fix_char_length(21); sargable= false; }
Item_int_func(Item *a,Item *b,Item *c) :Item_func(a,b,c)
- { max_length= 21; sargable= false; }
+ { collation.set_numeric(); fix_char_length(21); sargable= false; }
Item_int_func(List<Item> &list) :Item_func(list)
- { max_length= 21; sargable= false; }
+ { collation.set_numeric(); fix_char_length(21); sargable= false; }
Item_int_func(THD *thd, Item_int_func *item) :Item_func(thd, item)
- { sargable= false;}
+ { collation.set_numeric(); sargable= false; }
double val_real();
String *val_str(String*str);
enum Item_result result_type () const { return INT_RESULT; }
@@ -529,7 +614,8 @@ public:
longlong val_int_from_str(int *error);
void fix_length_and_dec()
{
- max_length= min(args[0]->max_length,MY_INT64_NUM_DECIMAL_DIGITS);
+ fix_char_length(min(args[0]->max_char_length(),
+ MY_INT64_NUM_DECIMAL_DIGITS));
}
virtual void print(String *str, enum_query_type query_type);
uint decimal_precision() const { return args[0]->decimal_precision(); }
@@ -556,8 +642,9 @@ public:
Item_decimal_typecast(Item *a, int len, int dec) :Item_func(a)
{
decimals= (uint8) dec;
- max_length= my_decimal_precision_to_length_no_truncation(len, dec,
- unsigned_flag);
+ collation.set_numeric();
+ fix_char_length(my_decimal_precision_to_length_no_truncation(len, dec,
+ unsigned_flag));
}
String *val_str(String *str);
double val_real();
@@ -838,6 +925,14 @@ public:
const char *func_name() const { return "tan"; }
};
+class Item_func_cot :public Item_dec_func
+{
+public:
+ Item_func_cot(Item *a) :Item_dec_func(a) {}
+ double val_real();
+ const char *func_name() const { return "cot"; }
+};
+
class Item_func_integer :public Item_int_func
{
public:
@@ -952,12 +1047,12 @@ protected:
enum_field_types cached_field_type;
public:
Item_func_min_max(List<Item> &list,int cmp_sign_arg) :Item_func(list),
- cmp_type(INT_RESULT), cmp_sign(cmp_sign_arg), compare_as_dates(FALSE) {}
+ cmp_type(INT_RESULT), cmp_sign(cmp_sign_arg), compare_as_dates(0) {}
double val_real();
longlong val_int();
String *val_str(String *);
my_decimal *val_decimal(my_decimal *);
- bool get_date(MYSQL_TIME *res, uint fuzzy_date);
+ bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
void fix_length_and_dec();
enum Item_result result_type () const { return cmp_type; }
enum_field_types field_type() const { return cached_field_type; }
@@ -1189,6 +1284,7 @@ public:
unsigned_flag= TRUE;
if (arg_count)
max_length= args[0]->max_length;
+ unsigned_flag=1;
}
bool fix_fields(THD *thd, Item **ref);
bool check_vcol_func_processor(uchar *int_arg)
@@ -1600,6 +1696,7 @@ public:
bool register_field_in_bitmap(uchar *arg);
bool set_entry(THD *thd, bool create_if_not_exists);
void cleanup();
+ bool check_vcol_func_processor(uchar *int_arg) {return TRUE;}
};
@@ -1639,6 +1736,7 @@ public:
{
return this;
}
+ bool check_vcol_func_processor(uchar *int_arg) { return TRUE;}
};
@@ -1656,7 +1754,8 @@ class Item_user_var_as_out_param :public Item
LEX_STRING name;
user_var_entry *entry;
public:
- Item_user_var_as_out_param(LEX_STRING a) : name(a) {}
+ Item_user_var_as_out_param(LEX_STRING a) : name(a)
+ { set_name(a.str, 0, system_charset_info); }
/* We should return something different from FIELD_ITEM here */
enum Type type() const { return STRING_ITEM;}
double val_real();
@@ -1720,6 +1819,7 @@ public:
bool eq(const Item *item, bool binary_cmp) const;
void cleanup();
+ bool check_vcol_func_processor(uchar *int_arg) { return TRUE;}
};
@@ -1938,6 +2038,11 @@ public:
return str;
}
+ void update_null_value()
+ {
+ execute();
+ }
+
virtual bool change_context_processor(uchar *cntx)
{ context= (Name_resolution_context *)cntx; return FALSE; }
@@ -1952,6 +2057,7 @@ public:
{
return sp_result_field;
}
+
bool check_vcol_func_processor(uchar *int_arg)
{
return trace_unsupported_by_check_vcol_func_processor(func_name());
@@ -1987,9 +2093,47 @@ public:
longlong val_int();
void fix_length_and_dec()
{ max_length= 21; unsigned_flag=1; }
- bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_vcol_func_processor(uchar *int_arg)
{
return trace_unsupported_by_check_vcol_func_processor(func_name());
}
};
+
+
+class Item_func_last_value :public Item_func
+{
+protected:
+ Item *last_value;
+public:
+ Item_func_last_value(List<Item> &list) :Item_func(list) {}
+ double val_real();
+ longlong val_int();
+ String *val_str(String *);
+ my_decimal *val_decimal(my_decimal *);
+ void fix_length_and_dec();
+ enum Item_result result_type () const { return last_value->result_type(); }
+ const char *func_name() const { return "last_value"; }
+ table_map not_null_tables() const { 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()
+ {
+ Item_func::update_used_tables();
+ maybe_null= last_value->maybe_null;
+ }
+};
+
+
+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);
+Item *find_date_time_item(Item **args, uint nargs, uint col);
+double my_double_round(double value, longlong dec, bool dec_unsigned,
+ bool truncate);
+bool eval_const_cond(COND *cond);
+
+extern bool volatile mqh_used;
+
+#endif /* ITEM_FUNC_INCLUDED */
diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc
index d8612abb59c..a38e9d416a7 100644
--- a/sql/item_geofunc.cc
+++ b/sql/item_geofunc.cc
@@ -13,8 +13,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@@ -28,12 +27,14 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
/*
It is necessary to include set_var.h instead of item.h because there
are dependencies on include order for set_var.h and item.h. This
will be resolved later.
*/
+#include "sql_class.h" // THD, set_var.h: THD
+#include "set_var.h"
#ifdef HAVE_SPATIAL
#include <m_ctype.h>
@@ -61,7 +62,7 @@ String *Item_func_geometry_from_text::val_str(String *str)
DBUG_ASSERT(fixed == 1);
Geometry_buffer buffer;
String arg_val;
- String *wkt= args[0]->val_str(&arg_val);
+ String *wkt= args[0]->val_str_ascii(&arg_val);
if ((null_value= args[0]->null_value))
return 0;
@@ -119,7 +120,7 @@ String *Item_func_geometry_from_wkb::val_str(String *str)
}
-String *Item_func_as_wkt::val_str(String *str)
+String *Item_func_as_wkt::val_str_ascii(String *str)
{
DBUG_ASSERT(fixed == 1);
String arg_val;
@@ -134,6 +135,7 @@ String *Item_func_as_wkt::val_str(String *str)
return 0;
str->length(0);
+ str->set_charset(&my_charset_latin1);
if ((null_value= geom->as_wkt(str, &dummy)))
return 0;
@@ -167,7 +169,7 @@ String *Item_func_as_wkb::val_str(String *str)
}
-String *Item_func_geometry_type::val_str(String *str)
+String *Item_func_geometry_type::val_str_ascii(String *str)
{
DBUG_ASSERT(fixed == 1);
String *swkb= args[0]->val_str(str);
@@ -181,7 +183,7 @@ String *Item_func_geometry_type::val_str(String *str)
/* String will not move */
str->copy(geom->get_class_info()->m_name.str,
geom->get_class_info()->m_name.length,
- default_charset());
+ &my_charset_latin1);
return str;
}
diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h
index ea891a457ec..2d715dc8765 100644
--- a/sql/item_geofunc.h
+++ b/sql/item_geofunc.h
@@ -1,7 +1,7 @@
#ifndef ITEM_GEOFUNC_INCLUDED
#define ITEM_GEOFUNC_INCLUDED
-/* Copyright (c) 2000, 2010 Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2000, 2010 Oracle and/or its affiliates.
Copyright (C) 2011 Monty Program Ab.
This program is free software; you can redistribute it and/or modify
@@ -61,12 +61,12 @@ public:
String *val_str(String *);
};
-class Item_func_as_wkt: public Item_str_func
+class Item_func_as_wkt: public Item_str_ascii_func
{
public:
- Item_func_as_wkt(Item *a): Item_str_func(a) {}
+ Item_func_as_wkt(Item *a): Item_str_ascii_func(a) {}
const char *func_name() const { return "st_astext"; }
- String *val_str(String *);
+ String *val_str_ascii(String *);
void fix_length_and_dec();
};
@@ -79,16 +79,16 @@ public:
enum_field_types field_type() const { return MYSQL_TYPE_BLOB; }
};
-class Item_func_geometry_type: public Item_str_func
+class Item_func_geometry_type: public Item_str_ascii_func
{
public:
- Item_func_geometry_type(Item *a): Item_str_func(a) {}
- String *val_str(String *);
+ Item_func_geometry_type(Item *a): Item_str_ascii_func(a) {}
+ String *val_str_ascii(String *);
const char *func_name() const { return "st_geometrytype"; }
void fix_length_and_dec()
{
// "GeometryCollection" is the longest
- max_length= 20;
+ fix_length_and_charset(20, default_charset());
maybe_null= 1;
};
};
@@ -503,5 +503,4 @@ public:
#define GEOM_NEW(thd, obj_constructor) NULL
#endif /*HAVE_SPATIAL*/
-#endif /*ITEM_GEOFUNC_INCLUDED*/
-
+#endif /* ITEM_GEOFUNC_INCLUDED */
diff --git a/sql/item_row.cc b/sql/item_row.cc
index bfbeba2a859..ee1d17213ee 100644
--- a/sql/item_row.cc
+++ b/sql/item_row.cc
@@ -12,10 +12,16 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+/*
+ It is necessary to include set_var.h instead of item.h because there
+ are dependencies on include order for set_var.h and item.h. This
+ will be resolved later.
+*/
+#include "sql_class.h" // THD, set_var.h: THD
+#include "set_var.h"
/**
Row items used for comparing rows and IN operations on rows:
@@ -140,13 +146,11 @@ void Item_row::update_used_tables()
{
used_tables_cache= 0;
const_item_cache= 1;
- maybe_null= 0;
for (uint i= 0; i < arg_count; i++)
{
items[i]->update_used_tables();
used_tables_cache|= items[i]->used_tables();
const_item_cache&= items[i]->const_item();
- maybe_null|= items[i]->maybe_null;
}
}
@@ -202,7 +206,7 @@ bool Item_row::walk(Item_processor processor, bool walk_subquery, uchar *arg)
Item *Item_row::transform(Item_transformer transformer, uchar *arg)
{
- DBUG_ASSERT(!current_thd->is_stmt_prepare());
+ DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare());
for (uint i= 0; i < arg_count; i++)
{
diff --git a/sql/item_row.h b/sql/item_row.h
index 6366ccba102..aa56068f8ba 100644
--- a/sql/item_row.h
+++ b/sql/item_row.h
@@ -1,3 +1,6 @@
+#ifndef ITEM_ROW_INCLUDED
+#define ITEM_ROW_INCLUDED
+
/*
Copyright (c) 2002, 2010, Oracle and/or its affiliates.
@@ -12,8 +15,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
class Item_row: public Item
{
@@ -84,4 +86,6 @@ public:
bool null_inside() { return with_null; };
void bring_value();
bool check_vcol_func_processor(uchar *int_arg) {return FALSE; }
- };
+};
+
+#endif /* ITEM_ROW_INCLUDED */
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 4a58ad339e2..eedf1499403 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -1,5 +1,6 @@
/*
- Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -30,7 +31,25 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+/* May include caustic 3rd-party defs. Use early, so it can override nothing. */
+#include "sha2.h"
+#include "my_global.h" // HAVE_*
+
+
+#include "sql_priv.h"
+/*
+ It is necessary to include set_var.h instead of item.h because there
+ are dependencies on include order for set_var.h and item.h. This
+ will be resolved later.
+*/
+#include "sql_class.h" // set_var.h: THD
+#include "set_var.h"
+#include "sql_base.h"
+#include "sql_time.h"
+#include "sql_acl.h" // SUPER_ACL
+#include "des_key_file.h" // st_des_keyschedule, st_des_keyblock
+#include "password.h" // my_make_scrambled_password,
+ // my_make_scrambled_password_323
#include <m_ctype.h>
#include "my_md5.h"
#include "sha1.h"
@@ -40,15 +59,53 @@ C_MODE_START
#include "../mysys/my_static.h" // For soundex_map
C_MODE_END
+size_t username_char_length= 16;
+
+/*
+ For the Items which have only val_str_ascii() method
+ and don't have their own "native" val_str(),
+ we provide a "wrapper" method to convert from ASCII
+ to Item character set when it's necessary.
+ Conversion happens only in case of "tricky" Item character set (e.g. UCS2).
+ Normally conversion does not happen, and val_str_ascii() is immediately
+ returned instead.
+*/
+String *Item_str_func::val_str_from_val_str_ascii(String *str, String *str2)
+{
+ DBUG_ASSERT(fixed == 1);
+
+ if (!(collation.collation->state & MY_CS_NONASCII))
+ {
+ String *res= val_str_ascii(str);
+ if (res)
+ res->set_charset(collation.collation);
+ return res;
+ }
+
+ DBUG_ASSERT(str != str2);
+
+ uint errors;
+ String *res= val_str_ascii(str);
+ if (!res)
+ return 0;
+
+ if ((null_value= str2->copy(res->ptr(), res->length(),
+ &my_charset_latin1, collation.collation,
+ &errors)))
+ return 0;
+
+ return str2;
+}
+
/*
Convert an array of bytes to a hexadecimal representation.
Used to generate a hexadecimal representation of a message digest.
*/
-static void array_to_hex(char *to, const char *str, uint len)
+static void array_to_hex(char *to, const unsigned char *str, uint len)
{
- const char *str_end= str + len;
+ const unsigned char *str_end= str + len;
for (; str != str_end; ++str)
{
*to++= _dig_vec_lower[((uchar) *str) >> 4];
@@ -111,7 +168,7 @@ longlong Item_str_func::val_int()
}
-String *Item_func_md5::val_str(String *str)
+String *Item_func_md5::val_str_ascii(String *str)
{
DBUG_ASSERT(fixed == 1);
String * sptr= args[0]->val_str(str);
@@ -126,8 +183,8 @@ String *Item_func_md5::val_str(String *str)
null_value=1;
return 0;
}
- array_to_hex((char *) str->ptr(), (const char*) digest, 16);
- str->set_charset(&my_charset_bin);
+ array_to_hex((char *) str->ptr(), digest, 16);
+ str->set_charset(&my_charset_numeric);
str->length((uint) 32);
return str;
}
@@ -136,21 +193,32 @@ String *Item_func_md5::val_str(String *str)
}
+/*
+ The MD5()/SHA() functions treat their parameter as being a case sensitive.
+ Thus we set binary collation on it so different instances of MD5() will be
+ compared properly.
+*/
+static CHARSET_INFO *get_checksum_charset(const char *csname)
+{
+ CHARSET_INFO *cs= get_charset_by_csname(csname, MY_CS_BINSORT, MYF(0));
+ if (!cs)
+ {
+ // Charset has no binary collation: use my_charset_bin.
+ cs= &my_charset_bin;
+ }
+ return cs;
+}
+
+
void Item_func_md5::fix_length_and_dec()
{
- max_length=32;
- /*
- The MD5() function treats its parameter as being a case sensitive. Thus
- we set binary collation on it so different instances of MD5() will be
- compared properly.
- */
- args[0]->collation.set(
- get_charset_by_csname(args[0]->collation.collation->csname,
- MY_CS_BINSORT,MYF(0)), DERIVATION_COERCIBLE);
+ CHARSET_INFO *cs= get_checksum_charset(args[0]->collation.collation->csname);
+ args[0]->collation.set(cs, DERIVATION_COERCIBLE);
+ fix_length_and_charset(32, default_charset());
}
-String *Item_func_sha::val_str(String *str)
+String *Item_func_sha::val_str_ascii(String *str)
{
DBUG_ASSERT(fixed == 1);
String * sptr= args[0]->val_str(str);
@@ -168,8 +236,8 @@ String *Item_func_sha::val_str(String *str)
if (!( str->alloc(SHA1_HASH_SIZE*2) ||
(mysql_sha1_result(&context,digest))))
{
- array_to_hex((char *) str->ptr(), (const char*) digest, SHA1_HASH_SIZE);
- str->set_charset(&my_charset_bin);
+ array_to_hex((char *) str->ptr(), digest, SHA1_HASH_SIZE);
+ str->set_charset(&my_charset_numeric);
str->length((uint) SHA1_HASH_SIZE*2);
null_value=0;
return str;
@@ -181,18 +249,142 @@ String *Item_func_sha::val_str(String *str)
void Item_func_sha::fix_length_and_dec()
{
- max_length=SHA1_HASH_SIZE*2; // size of hex representation of hash
- /*
- The SHA() function treats its parameter as being a case sensitive. Thus
- we set binary collation on it so different instances of MD5() will be
- compared properly.
+ CHARSET_INFO *cs= get_checksum_charset(args[0]->collation.collation->csname);
+ args[0]->collation.set(cs, DERIVATION_COERCIBLE);
+ // size of hex representation of hash
+ fix_length_and_charset(SHA1_HASH_SIZE * 2, default_charset());
+}
+
+String *Item_func_sha2::val_str_ascii(String *str)
+{
+ DBUG_ASSERT(fixed == 1);
+#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
+ unsigned char digest_buf[SHA512_DIGEST_LENGTH];
+ String *input_string;
+ unsigned char *input_ptr;
+ size_t input_len;
+ uint digest_length= 0;
+
+ str->set_charset(&my_charset_bin);
+
+ input_string= args[0]->val_str(str);
+ if (input_string == NULL)
+ {
+ null_value= TRUE;
+ return (String *) NULL;
+ }
+
+ null_value= args[0]->null_value;
+ if (null_value)
+ return (String *) NULL;
+
+ input_ptr= (unsigned char *) input_string->ptr();
+ input_len= input_string->length();
+
+ switch ((uint) args[1]->val_int()) {
+#ifndef OPENSSL_NO_SHA512
+ case 512:
+ digest_length= SHA512_DIGEST_LENGTH;
+ (void) SHA512(input_ptr, input_len, digest_buf);
+ break;
+ case 384:
+ digest_length= SHA384_DIGEST_LENGTH;
+ (void) SHA384(input_ptr, input_len, digest_buf);
+ break;
+#endif
+#ifndef OPENSSL_NO_SHA256
+ case 224:
+ digest_length= SHA224_DIGEST_LENGTH;
+ (void) SHA224(input_ptr, input_len, digest_buf);
+ break;
+ case 256:
+ case 0: // SHA-256 is the default
+ digest_length= SHA256_DIGEST_LENGTH;
+ (void) SHA256(input_ptr, input_len, digest_buf);
+ break;
+#endif
+ default:
+ if (!args[1]->const_item())
+ push_warning_printf(current_thd,
+ MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WRONG_PARAMETERS_TO_NATIVE_FCT,
+ ER(ER_WRONG_PARAMETERS_TO_NATIVE_FCT), "sha2");
+ null_value= TRUE;
+ return NULL;
+ }
+
+ /*
+ Since we're subverting the usual String methods, we must make sure that
+ the destination has space for the bytes we're about to write.
*/
- args[0]->collation.set(
- get_charset_by_csname(args[0]->collation.collation->csname,
- MY_CS_BINSORT,MYF(0)), DERIVATION_COERCIBLE);
+ str->realloc((uint) digest_length*2 + 1); /* Each byte as two nybbles */
+
+ /* Convert the large number to a string-hex representation. */
+ array_to_hex((char *) str->ptr(), digest_buf, digest_length);
+
+ /* We poked raw bytes in. We must inform the the String of its length. */
+ str->length((uint) digest_length*2); /* Each byte as two nybbles */
+
+ null_value= FALSE;
+ return str;
+
+#else
+ push_warning_printf(current_thd,
+ MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_FEATURE_DISABLED,
+ ER(ER_FEATURE_DISABLED),
+ "sha2", "--with-ssl");
+ null_value= TRUE;
+ return (String *) NULL;
+#endif /* defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) */
}
+void Item_func_sha2::fix_length_and_dec()
+{
+ maybe_null= 1;
+ max_length = 0;
+
+#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
+ int sha_variant= args[1]->const_item() ? args[1]->val_int() : 512;
+
+ switch (sha_variant) {
+#ifndef OPENSSL_NO_SHA512
+ case 512:
+ fix_length_and_charset(SHA512_DIGEST_LENGTH * 2, default_charset());
+ break;
+ case 384:
+ fix_length_and_charset(SHA384_DIGEST_LENGTH * 2, default_charset());
+ break;
+#endif
+#ifndef OPENSSL_NO_SHA256
+ case 256:
+ case 0: // SHA-256 is the default
+ fix_length_and_charset(SHA256_DIGEST_LENGTH * 2, default_charset());
+ break;
+ case 224:
+ fix_length_and_charset(SHA224_DIGEST_LENGTH * 2, default_charset());
+ break;
+#endif
+ default:
+ push_warning_printf(current_thd,
+ MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WRONG_PARAMETERS_TO_NATIVE_FCT,
+ ER(ER_WRONG_PARAMETERS_TO_NATIVE_FCT), "sha2");
+ }
+
+ CHARSET_INFO *cs= get_checksum_charset(args[0]->collation.collation->csname);
+ args[0]->collation.set(cs, DERIVATION_COERCIBLE);
+
+#else
+ push_warning_printf(current_thd,
+ MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_FEATURE_DISABLED,
+ ER(ER_FEATURE_DISABLED),
+ "sha2", "--with-ssl");
+#endif /* defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) */
+}
+
/* Implementation of AES encryption routines */
String *Item_func_aes_encrypt::val_str(String *str)
@@ -412,27 +604,15 @@ null:
void Item_func_concat::fix_length_and_dec()
{
- ulonglong max_result_length= 0;
+ ulonglong char_length= 0;
- if (agg_arg_charsets(collation, args, arg_count, MY_COLL_ALLOW_CONV, 1))
+ if (agg_arg_charsets_for_string_result(collation, args, arg_count))
return;
for (uint i=0 ; i < arg_count ; i++)
- {
- if (args[i]->collation.collation->mbmaxlen != collation.collation->mbmaxlen)
- max_result_length+= (args[i]->max_length /
- args[i]->collation.collation->mbmaxlen) *
- collation.collation->mbmaxlen;
- else
- max_result_length+= args[i]->max_length;
- }
+ char_length+= args[i]->max_char_length();
- if (max_result_length >= MAX_BLOB_WIDTH)
- {
- max_result_length= MAX_BLOB_WIDTH;
- maybe_null= 1;
- }
- max_length= (ulong) max_result_length;
+ fix_char_length_ulonglong(char_length);
}
/**
@@ -449,7 +629,7 @@ void Item_func_concat::fix_length_and_dec()
String *Item_func_des_encrypt::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
-#ifdef HAVE_OPENSSL
+#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
uint code= ER_WRONG_PARAMETERS_TO_PROCEDURE;
DES_cblock ivec;
struct st_des_keyblock keyblock;
@@ -462,22 +642,21 @@ String *Item_func_des_encrypt::val_str(String *str)
return 0; // ENCRYPT(NULL) == NULL
if ((res_length=res->length()) == 0)
return make_empty_result();
-
if (arg_count == 1)
{
/* Protect against someone doing FLUSH DES_KEY_FILE */
- VOID(pthread_mutex_lock(&LOCK_des_key_file));
+ mysql_mutex_lock(&LOCK_des_key_file);
keyschedule= des_keyschedule[key_number=des_default_key];
- VOID(pthread_mutex_unlock(&LOCK_des_key_file));
+ mysql_mutex_unlock(&LOCK_des_key_file);
}
else if (args[1]->result_type() == INT_RESULT)
{
key_number= (uint) args[1]->val_int();
if (key_number > 9)
goto error;
- VOID(pthread_mutex_lock(&LOCK_des_key_file));
+ mysql_mutex_lock(&LOCK_des_key_file);
keyschedule= des_keyschedule[key_number];
- VOID(pthread_mutex_unlock(&LOCK_des_key_file));
+ mysql_mutex_unlock(&LOCK_des_key_file);
}
else
{
@@ -531,14 +710,14 @@ String *Item_func_des_encrypt::val_str(String *str)
return &tmp_value;
error:
- push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN,
code, ER(code),
"des_encrypt");
#else
- push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN,
ER_FEATURE_DISABLED, ER(ER_FEATURE_DISABLED),
- "des_encrypt","--with-openssl");
-#endif /* HAVE_OPENSSL */
+ "des_encrypt", "--with-ssl");
+#endif /* defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) */
null_value=1;
return 0;
}
@@ -547,7 +726,7 @@ error:
String *Item_func_des_decrypt::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
-#ifdef HAVE_OPENSSL
+#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
uint code= ER_WRONG_PARAMETERS_TO_PROCEDURE;
DES_cblock ivec;
struct st_des_keyblock keyblock;
@@ -569,9 +748,9 @@ String *Item_func_des_decrypt::val_str(String *str)
key_number > 9)
goto error;
- VOID(pthread_mutex_lock(&LOCK_des_key_file));
+ mysql_mutex_lock(&LOCK_des_key_file);
keyschedule= des_keyschedule[key_number];
- VOID(pthread_mutex_unlock(&LOCK_des_key_file));
+ mysql_mutex_unlock(&LOCK_des_key_file);
}
else
{
@@ -609,15 +788,15 @@ String *Item_func_des_decrypt::val_str(String *str)
return &tmp_value;
error:
- push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN,
code, ER(code),
"des_decrypt");
wrong_key:
#else
- push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN,
ER_FEATURE_DISABLED, ER(ER_FEATURE_DISABLED),
- "des_decrypt","--with-openssl");
-#endif /* HAVE_OPENSSL */
+ "des_decrypt", "--with-ssl");
+#endif /* defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) */
null_value=1;
return 0;
}
@@ -773,9 +952,9 @@ null:
void Item_func_concat_ws::fix_length_and_dec()
{
- ulonglong max_result_length;
+ ulonglong char_length;
- if (agg_arg_charsets(collation, args, arg_count, MY_COLL_ALLOW_CONV, 1))
+ if (agg_arg_charsets_for_string_result(collation, args, arg_count))
return;
/*
@@ -783,16 +962,11 @@ void Item_func_concat_ws::fix_length_and_dec()
it is done on parser level in sql_yacc.yy
so, (arg_count - 2) is safe here.
*/
- max_result_length= (ulonglong) args[0]->max_length * (arg_count - 2);
+ char_length= (ulonglong) args[0]->max_char_length() * (arg_count - 2);
for (uint i=1 ; i < arg_count ; i++)
- max_result_length+=args[i]->max_length;
+ char_length+= args[i]->max_char_length();
- if (max_result_length >= MAX_BLOB_WIDTH)
- {
- max_result_length= MAX_BLOB_WIDTH;
- maybe_null= 1;
- }
- max_length= (ulong) max_result_length;
+ fix_char_length_ulonglong(char_length);
}
@@ -827,6 +1001,7 @@ String *Item_func_reverse::val_str(String *str)
if ((l= my_ismbchar(res->charset(),ptr,end)))
{
tmp-= l;
+ DBUG_ASSERT(tmp >= tmp_value.ptr());
memcpy(tmp,ptr,l);
ptr+= l;
}
@@ -846,8 +1021,9 @@ String *Item_func_reverse::val_str(String *str)
void Item_func_reverse::fix_length_and_dec()
{
- collation.set(args[0]->collation);
- max_length = args[0]->max_length;
+ agg_arg_charsets_for_string_result(collation, args, 1);
+ DBUG_ASSERT(collation.collation != NULL);
+ fix_char_length(args[0]->max_char_length());
}
/**
@@ -982,22 +1158,17 @@ null:
void Item_func_replace::fix_length_and_dec()
{
- ulonglong max_result_length= args[0]->max_length;
- int diff=(int) (args[2]->max_length - args[1]->max_length);
- if (diff > 0 && args[1]->max_length)
+ ulonglong char_length= (ulonglong) args[0]->max_char_length();
+ int diff=(int) (args[2]->max_char_length() - args[1]->max_char_length());
+ if (diff > 0 && args[1]->max_char_length())
{ // Calculate of maxreplaces
- ulonglong max_substrs= max_result_length/args[1]->max_length;
- max_result_length+= max_substrs * (uint) diff;
- }
- if (max_result_length >= MAX_BLOB_WIDTH)
- {
- max_result_length= MAX_BLOB_WIDTH;
- maybe_null= 1;
+ ulonglong max_substrs= char_length / args[1]->max_char_length();
+ char_length+= max_substrs * (uint) diff;
}
- max_length= (ulong) max_result_length;
-
- if (agg_arg_charsets(collation, args, 3, MY_COLL_CMP_CONV, 1))
+
+ if (agg_arg_charsets_for_string_result_with_comparison(collation, args, 3))
return;
+ fix_char_length_ulonglong(char_length);
}
@@ -1066,19 +1237,14 @@ null:
void Item_func_insert::fix_length_and_dec()
{
- ulonglong max_result_length;
+ ulonglong char_length;
// Handle character set for args[0] and args[3].
- if (agg_arg_charsets(collation, &args[0], 2, MY_COLL_ALLOW_CONV, 3))
+ if (agg_arg_charsets_for_string_result(collation, args, 2, 3))
return;
- max_result_length= ((ulonglong) args[0]->max_length+
- (ulonglong) args[3]->max_length);
- if (max_result_length >= MAX_BLOB_WIDTH)
- {
- max_result_length= MAX_BLOB_WIDTH;
- maybe_null= 1;
- }
- max_length= (ulong) max_result_length;
+ char_length= ((ulonglong) args[0]->max_char_length() +
+ (ulonglong) args[3]->max_char_length());
+ fix_char_length_ulonglong(char_length);
}
@@ -1117,18 +1283,20 @@ String *Item_str_conv::val_str(String *str)
void Item_func_lcase::fix_length_and_dec()
{
- collation.set(args[0]->collation);
+ agg_arg_charsets_for_string_result(collation, args, 1);
+ DBUG_ASSERT(collation.collation != NULL);
multiply= collation.collation->casedn_multiply;
converter= collation.collation->cset->casedn;
- max_length= args[0]->max_length * multiply;
+ fix_char_length_ulonglong((ulonglong) args[0]->max_char_length() * multiply);
}
void Item_func_ucase::fix_length_and_dec()
{
- collation.set(args[0]->collation);
+ agg_arg_charsets_for_string_result(collation, args, 1);
+ DBUG_ASSERT(collation.collation != NULL);
multiply= collation.collation->caseup_multiply;
converter= collation.collation->cset->caseup;
- max_length= args[0]->max_length * multiply;
+ fix_char_length_ulonglong((ulonglong) args[0]->max_char_length() * multiply);
}
@@ -1158,21 +1326,23 @@ String *Item_func_left::val_str(String *str)
void Item_str_func::left_right_max_length()
{
- max_length=args[0]->max_length;
+ uint32 char_length= args[0]->max_char_length();
if (args[1]->const_item())
{
- int length=(int) args[1]->val_int()*collation.collation->mbmaxlen;
+ int length= (int) args[1]->val_int();
if (args[1]->null_value || length <= 0)
- max_length=0;
+ char_length=0;
else
- set_if_smaller(max_length,(uint) length);
+ set_if_smaller(char_length, (uint) length);
}
+ fix_char_length(char_length);
}
void Item_func_left::fix_length_and_dec()
{
- collation.set(args[0]->collation);
+ agg_arg_charsets_for_string_result(collation, args, 1);
+ DBUG_ASSERT(collation.collation != NULL);
left_right_max_length();
}
@@ -1205,7 +1375,8 @@ String *Item_func_right::val_str(String *str)
void Item_func_right::fix_length_and_dec()
{
- collation.set(args[0]->collation);
+ agg_arg_charsets_for_string_result(collation, args, 1);
+ DBUG_ASSERT(collation.collation != NULL);
left_right_max_length();
}
@@ -1261,7 +1432,8 @@ void Item_func_substr::fix_length_and_dec()
{
max_length=args[0]->max_length;
- collation.set(args[0]->collation);
+ agg_arg_charsets_for_string_result(collation, args, 1);
+ DBUG_ASSERT(collation.collation != NULL);
if (args[1]->const_item())
{
int32 start= (int32) args[1]->val_int();
@@ -1286,10 +1458,9 @@ void Item_func_substr::fix_length_and_dec()
void Item_func_substr_index::fix_length_and_dec()
{
- max_length= args[0]->max_length;
-
- if (agg_arg_charsets(collation, args, 2, MY_COLL_CMP_CONV, 1))
+ if (agg_arg_charsets_for_string_result_with_comparison(collation, args, 2))
return;
+ fix_char_length(args[0]->max_char_length());
}
@@ -1449,7 +1620,7 @@ String *Item_func_ltrim::val_str(String *str)
if ((remove_length= remove_str->length()) == 0 ||
remove_length > res->length())
- return res;
+ return non_trimmed_value(res);
ptr= (char*) res->ptr();
end= ptr+res->length();
@@ -1468,9 +1639,8 @@ String *Item_func_ltrim::val_str(String *str)
end+=remove_length;
}
if (ptr == res->ptr())
- return res;
- tmp_value.set(*res,(uint) (ptr - res->ptr()),(uint) (end-ptr));
- return &tmp_value;
+ return non_trimmed_value(res);
+ return trimmed_value(res, (uint32) (ptr - res->ptr()), (uint32) (end - ptr));
}
@@ -1496,7 +1666,7 @@ String *Item_func_rtrim::val_str(String *str)
if ((remove_length= remove_str->length()) == 0 ||
remove_length > res->length())
- return res;
+ return non_trimmed_value(res);
ptr= (char*) res->ptr();
end= ptr+res->length();
@@ -1508,11 +1678,11 @@ String *Item_func_rtrim::val_str(String *str)
{
char chr=(*remove_str)[0];
#ifdef USE_MB
- if (use_mb(res->charset()))
+ if (use_mb(collation.collation))
{
while (ptr < end)
{
- if ((l=my_ismbchar(res->charset(), ptr,end))) ptr+=l,p=ptr;
+ if ((l= my_ismbchar(collation.collation, ptr, end))) ptr+= l, p=ptr;
else ++ptr;
}
ptr=p;
@@ -1525,12 +1695,12 @@ String *Item_func_rtrim::val_str(String *str)
{
const char *r_ptr=remove_str->ptr();
#ifdef USE_MB
- if (use_mb(res->charset()))
+ if (use_mb(collation.collation))
{
loop:
while (ptr + remove_length < end)
{
- if ((l=my_ismbchar(res->charset(), ptr,end))) ptr+=l;
+ if ((l= my_ismbchar(collation.collation, ptr, end))) ptr+= l;
else ++ptr;
}
if (ptr + remove_length == end && !memcmp(ptr,r_ptr,remove_length))
@@ -1549,9 +1719,8 @@ String *Item_func_rtrim::val_str(String *str)
}
}
if (end == res->ptr()+res->length())
- return res;
- tmp_value.set(*res,0,(uint) (end-res->ptr()));
- return &tmp_value;
+ return non_trimmed_value(res);
+ return trimmed_value(res, 0, (uint32) (end - res->ptr()));
}
@@ -1578,7 +1747,7 @@ String *Item_func_trim::val_str(String *str)
if ((remove_length= remove_str->length()) == 0 ||
remove_length > res->length())
- return res;
+ return non_trimmed_value(res);
ptr= (char*) res->ptr();
end= ptr+res->length();
@@ -1586,15 +1755,17 @@ String *Item_func_trim::val_str(String *str)
while (ptr+remove_length <= end && !memcmp(ptr,r_ptr,remove_length))
ptr+=remove_length;
#ifdef USE_MB
- if (use_mb(res->charset()))
+ if (use_mb(collation.collation))
{
char *p=ptr;
register uint32 l;
loop:
while (ptr + remove_length < end)
{
- if ((l=my_ismbchar(res->charset(), ptr,end))) ptr+=l;
- else ++ptr;
+ if ((l= my_ismbchar(collation.collation, ptr, end)))
+ ptr+= l;
+ else
+ ++ptr;
}
if (ptr + remove_length == end && !memcmp(ptr,r_ptr,remove_length))
{
@@ -1612,17 +1783,16 @@ String *Item_func_trim::val_str(String *str)
end-=remove_length;
}
if (ptr == res->ptr() && end == ptr+res->length())
- return res;
- tmp_value.set(*res,(uint) (ptr - res->ptr()),(uint) (end-ptr));
- return &tmp_value;
+ return non_trimmed_value(res);
+ return trimmed_value(res, (uint32) (ptr - res->ptr()), (uint32) (end - ptr));
}
void Item_func_trim::fix_length_and_dec()
{
- max_length= args[0]->max_length;
if (arg_count == 1)
{
- collation.set(args[0]->collation);
+ agg_arg_charsets_for_string_result(collation, args, 1);
+ DBUG_ASSERT(collation.collation != NULL);
remove.set_charset(collation.collation);
remove.set_ascii(" ",1);
}
@@ -1630,9 +1800,11 @@ void Item_func_trim::fix_length_and_dec()
{
// Handle character set for args[1] and args[0].
// Note that we pass args[1] as the first item, and args[0] as the second.
- if (agg_arg_charsets(collation, &args[1], 2, MY_COLL_CMP_CONV, -1))
+ if (agg_arg_charsets_for_string_result_with_comparison(collation,
+ &args[1], 2, -1))
return;
}
+ fix_char_length(args[0]->max_char_length());
}
void Item_func_trim::print(String *str, enum_query_type query_type)
@@ -1655,7 +1827,7 @@ void Item_func_trim::print(String *str, enum_query_type query_type)
/* Item_func_password */
-String *Item_func_password::val_str(String *str)
+String *Item_func_password::val_str_ascii(String *str)
{
DBUG_ASSERT(fixed == 1);
String *res= args[0]->val_str(str);
@@ -1664,7 +1836,7 @@ String *Item_func_password::val_str(String *str)
if (res->length() == 0)
return make_empty_result();
my_make_scrambled_password(tmp_value, res->ptr(), res->length());
- str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH, res->charset());
+ str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH, &my_charset_latin1);
return str;
}
@@ -1679,7 +1851,7 @@ char *Item_func_password::alloc(THD *thd, const char *password,
/* Item_func_old_password */
-String *Item_func_old_password::val_str(String *str)
+String *Item_func_old_password::val_str_ascii(String *str)
{
DBUG_ASSERT(fixed == 1);
String *res= args[0]->val_str(str);
@@ -1688,7 +1860,7 @@ String *Item_func_old_password::val_str(String *str)
if (res->length() == 0)
return make_empty_result();
my_make_scrambled_password_323(tmp_value, res->ptr(), res->length());
- str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH_323, res->charset());
+ str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH_323, &my_charset_latin1);
return str;
}
@@ -1730,17 +1902,17 @@ String *Item_func_encrypt::val_str(String *str)
return 0;
salt_ptr= salt_str->c_ptr_safe();
}
- pthread_mutex_lock(&LOCK_crypt);
+ mysql_mutex_lock(&LOCK_crypt);
char *tmp= crypt(res->c_ptr_safe(),salt_ptr);
if (!tmp)
{
- pthread_mutex_unlock(&LOCK_crypt);
+ mysql_mutex_unlock(&LOCK_crypt);
null_value= 1;
return 0;
}
str->set(tmp, (uint) strlen(tmp), &my_charset_bin);
str->copy();
- pthread_mutex_unlock(&LOCK_crypt);
+ mysql_mutex_unlock(&LOCK_crypt);
return str;
#else
null_value=1;
@@ -1907,9 +2079,11 @@ bool Item_func_current_user::fix_fields(THD *thd, Item **ref)
void Item_func_soundex::fix_length_and_dec()
{
- collation.set(args[0]->collation);
- max_length=args[0]->max_length;
- set_if_bigger(max_length, 4 * collation.collation->mbminlen);
+ uint32 char_length= args[0]->max_char_length();
+ agg_arg_charsets_for_string_result(collation, args, 1);
+ DBUG_ASSERT(collation.collation != NULL);
+ set_if_bigger(char_length, 4);
+ fix_char_length(char_length);
tmp_value.set_charset(collation.collation);
}
@@ -2066,18 +2240,34 @@ String *Item_func_soundex::val_str(String *str)
const int FORMAT_MAX_DECIMALS= 30;
-Item_func_format::Item_func_format(Item *org, Item *dec)
-: Item_str_func(org, dec)
+
+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())))
+ {
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_UNKNOWN_LOCALE,
+ ER(ER_UNKNOWN_LOCALE),
+ locale_name ? locale_name->c_ptr_safe() : "NULL");
+ lc= &my_locale_en_US;
+ }
+ return lc;
}
void Item_func_format::fix_length_and_dec()
{
- uint char_length= args[0]->max_length/args[0]->collation.collation->mbmaxlen;
- uint max_sep_count= char_length/3 + (decimals ? 1 : 0) + /*sign*/1;
+ uint32 char_length= args[0]->max_char_length();
+ uint32 max_sep_count= (char_length / 3) + (decimals ? 1 : 0) + /*sign*/1;
collation.set(default_charset());
- max_length= (char_length + max_sep_count + decimals) *
- collation.collation->mbmaxlen;
+ fix_char_length(char_length + max_sep_count + decimals);
+ if (arg_count == 3)
+ locale= args[2]->basic_const_item() ? get_locale(args[2]) : NULL;
+ else
+ locale= &my_locale_en_US; /* Two arguments */
}
@@ -2087,15 +2277,14 @@ void Item_func_format::fix_length_and_dec()
are stored in more than one byte
*/
-String *Item_func_format::val_str(String *str)
+String *Item_func_format::val_str_ascii(String *str)
{
- uint32 length;
uint32 str_length;
/* Number of decimal digits */
int dec;
/* Number of characters used to represent the decimals, including '.' */
uint32 dec_length;
- int diff;
+ MY_LOCALE *lc;
DBUG_ASSERT(fixed == 1);
dec= (int) args[1]->val_int();
@@ -2105,6 +2294,8 @@ String *Item_func_format::val_str(String *str)
return NULL;
}
+ lc= locale ? locale : get_locale(args[2]);
+
dec= set_zone(dec, 0, FORMAT_MAX_DECIMALS);
dec_length= dec ? dec+1 : 0;
null_value=0;
@@ -2119,8 +2310,6 @@ String *Item_func_format::val_str(String *str)
my_decimal_round(E_DEC_FATAL_ERROR, res, dec, false, &rnd_dec);
my_decimal2string(E_DEC_FATAL_ERROR, &rnd_dec, 0, 0, 0, str);
str_length= str->length();
- if (rnd_dec.sign())
- str_length--;
}
else
{
@@ -2128,36 +2317,65 @@ String *Item_func_format::val_str(String *str)
if ((null_value=args[0]->null_value))
return 0; /* purecov: inspected */
nr= my_double_round(nr, (longlong) dec, FALSE, FALSE);
- /* Here default_charset() is right as this is not an automatic conversion */
- str->set_real(nr, dec, default_charset());
- if (isnan(nr))
+ str->set_real(nr, dec, &my_charset_numeric);
+ if (isnan(nr) || my_isinf(nr))
return str;
str_length=str->length();
- if (nr < 0)
- str_length--; // Don't count sign
- }
- /* We need this test to handle 'nan' values */
- if (str_length >= dec_length+4)
- {
- char *tmp,*pos;
- length= str->length()+(diff=((int)(str_length- dec_length-1))/3);
- str= copy_if_not_alloced(&tmp_str,str,length);
- str->length(length);
- tmp= (char*) str->ptr()+length - dec_length-1;
- for (pos= (char*) str->ptr()+length-1; pos != tmp; pos--)
- pos[0]= pos[-diff];
- while (diff)
+ }
+ /* We need this test to handle 'nan' and short values */
+ if (lc->grouping[0] > 0 &&
+ str_length >= dec_length + 1 + lc->grouping[0])
+ {
+ /* We need space for ',' between each group of digits as well. */
+ char buf[2 * FLOATING_POINT_BUFFER];
+ int count;
+ const char *grouping= lc->grouping;
+ char sign_length= *str->ptr() == '-' ? 1 : 0;
+ const char *src= str->ptr() + str_length - dec_length - 1;
+ const char *src_begin= str->ptr() + sign_length;
+ char *dst= buf + sizeof(buf);
+
+ /* Put the fractional part */
+ if (dec)
{
- *pos= *(pos - diff);
- pos--;
- *pos= *(pos - diff);
- pos--;
- *pos= *(pos - diff);
- pos--;
- pos[0]=',';
- pos--;
- diff--;
+ dst-= (dec + 1);
+ *dst= lc->decimal_point;
+ memcpy(dst + 1, src + 2, dec);
}
+
+ /* Put the integer part with grouping */
+ for (count= *grouping; src >= src_begin; count--)
+ {
+ /*
+ When *grouping==0x80 (which means "end of grouping")
+ count will be initialized to -1 and
+ we'll never get into this "if" anymore.
+ */
+ if (count == 0)
+ {
+ *--dst= lc->thousand_sep;
+ if (grouping[1])
+ grouping++;
+ count= *grouping;
+ }
+ DBUG_ASSERT(dst > buf);
+ *--dst= *src--;
+ }
+
+ if (sign_length) /* Put '-' */
+ *--dst= *str->ptr();
+
+ /* Put the rest of the integer part without grouping */
+ str->copy(dst, buf + sizeof(buf) - dst, &my_charset_latin1);
+ }
+ else if (dec_length && lc->decimal_point != '.')
+ {
+ /*
+ For short values without thousands (<1000)
+ replace decimal point to localized value.
+ */
+ DBUG_ASSERT(dec_length <= str_length);
+ ((char*) str->ptr())[str_length - dec_length]= lc->decimal_point;
}
return str;
}
@@ -2169,22 +2387,28 @@ void Item_func_format::print(String *str, enum_query_type query_type)
args[0]->print(str, query_type);
str->append(',');
args[1]->print(str, query_type);
+ if(arg_count > 2)
+ {
+ str->append(',');
+ args[2]->print(str,query_type);
+ }
str->append(')');
}
void Item_func_elt::fix_length_and_dec()
{
- max_length=0;
+ uint32 char_length= 0;
decimals=0;
- if (agg_arg_charsets(collation, args+1, arg_count-1, MY_COLL_ALLOW_CONV, 1))
+ if (agg_arg_charsets_for_string_result(collation, args + 1, arg_count - 1))
return;
for (uint i= 1 ; i < arg_count ; i++)
{
- set_if_bigger(max_length,args[i]->max_length);
+ set_if_bigger(char_length, args[i]->max_char_length());
set_if_bigger(decimals,args[i]->decimals);
}
+ fix_char_length(char_length);
maybe_null=1; // NULL if wrong first arg
}
@@ -2234,12 +2458,14 @@ String *Item_func_elt::val_str(String *str)
void Item_func_make_set::fix_length_and_dec()
{
- if (agg_arg_charsets(collation, args+1, arg_count-1, MY_COLL_ALLOW_CONV, 1))
+ uint32 char_length= arg_count - 2; /* Separators */
+
+ if (agg_arg_charsets_for_string_result(collation, args + 1, arg_count - 1))
return;
- max_length=arg_count-2;
for (uint i=1 ; i < arg_count ; i++)
- max_length+=args[i]->max_length;
+ char_length+= args[i]->max_char_length();
+ fix_char_length(char_length);
}
@@ -2306,17 +2532,27 @@ String *Item_func_char::val_str(String *str)
int32 num=(int32) args[i]->val_int();
if (!args[i]->null_value)
{
- char char_num= (char) num;
- if (num&0xFF000000L) {
- str->append((char)(num>>24));
- goto b2;
- } else if (num&0xFF0000L) {
- b2: str->append((char)(num>>16));
- goto b1;
- } else if (num&0xFF00L) {
- b1: str->append((char)(num>>8));
+ 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);
}
- str->append(&char_num, 1);
}
}
str->realloc(str->length()); // Add end 0 (for Purify)
@@ -2348,7 +2584,8 @@ inline String* alloc_buffer(String *res,String *str,String *tmp_value,
void Item_func_repeat::fix_length_and_dec()
{
- collation.set(args[0]->collation);
+ agg_arg_charsets_for_string_result(collation, args, 1);
+ DBUG_ASSERT(collation.collation != NULL);
if (args[1]->const_item())
{
/* must be longlong to avoid truncation */
@@ -2361,13 +2598,8 @@ void Item_func_repeat::fix_length_and_dec()
else if (count > INT_MAX32)
count= INT_MAX32;
- ulonglong max_result_length= (ulonglong) args[0]->max_length * count;
- if (max_result_length >= MAX_BLOB_WIDTH)
- {
- max_result_length= MAX_BLOB_WIDTH;
- maybe_null= 1;
- }
- max_length= (ulong) max_result_length;
+ ulonglong char_length= (ulonglong) args[0]->max_char_length() * count;
+ fix_char_length_ulonglong(char_length);
}
else
{
@@ -2434,32 +2666,19 @@ err:
void Item_func_rpad::fix_length_and_dec()
{
// Handle character set for args[0] and args[2].
- if (agg_arg_charsets(collation, &args[0], 2, MY_COLL_ALLOW_CONV, 2))
+ if (agg_arg_charsets_for_string_result(collation, &args[0], 2, 2))
return;
if (args[1]->const_item())
{
- ulonglong length= 0;
-
- if (collation.collation->mbmaxlen > 0)
- {
- ulonglong temp= (ulonglong) 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)
- temp= 0;
- else if (temp > INT_MAX32)
- temp = INT_MAX32;
-
- length= temp * collation.collation->mbmaxlen;
- }
-
- if (length >= MAX_BLOB_WIDTH)
- {
- length= MAX_BLOB_WIDTH;
- maybe_null= 1;
- }
- max_length= (ulong) length;
+ 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);
}
else
{
@@ -2502,6 +2721,19 @@ String *Item_func_rpad::val_str(String *str)
res->set_charset(&my_charset_bin);
rpad->set_charset(&my_charset_bin);
}
+#if MARIADB_VERSION_ID < 1000000
+ /*
+ Well-formedness is handled on a higher level in 10.0,
+ no needs to check it here again.
+ */
+ else
+ {
+ // This will chop off any trailing illegal characters from rpad.
+ String *well_formed_pad= args[2]->check_well_formed_result(rpad, false);
+ if (!well_formed_pad)
+ goto err;
+ }
+#endif
if (count <= (res_char_length= res->numchars()))
{ // String to pad is big enough
@@ -2552,33 +2784,20 @@ String *Item_func_rpad::val_str(String *str)
void Item_func_lpad::fix_length_and_dec()
{
// Handle character set for args[0] and args[2].
- if (agg_arg_charsets(collation, &args[0], 2, MY_COLL_ALLOW_CONV, 2))
+ if (agg_arg_charsets_for_string_result(collation, &args[0], 2, 2))
return;
if (args[1]->const_item())
{
- ulonglong length= 0;
-
- if (collation.collation->mbmaxlen > 0)
- {
- ulonglong temp= (ulonglong) 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)
- temp= 0;
- else if (temp > INT_MAX32)
- temp= INT_MAX32;
-
- length= temp * collation.collation->mbmaxlen;
- }
-
- if (length >= MAX_BLOB_WIDTH)
- {
- length= MAX_BLOB_WIDTH;
- maybe_null= 1;
- }
- max_length= (ulong) length;
+ 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);
}
else
{
@@ -2620,6 +2839,18 @@ String *Item_func_lpad::val_str(String *str)
res->set_charset(&my_charset_bin);
pad->set_charset(&my_charset_bin);
}
+#if MARIADB_VERSION_ID < 1000000
+ /*
+ Well-formedness is handled on a higher level in 10.0,
+ no needs to check it here again.
+ */ else
+ {
+ // This will chop off any trailing illegal characters from pad.
+ String *well_formed_pad= args[2]->check_well_formed_result(pad, false);
+ if (!well_formed_pad)
+ goto err;
+ }
+#endif
res_char_length= res->numchars();
@@ -2676,7 +2907,9 @@ String *Item_func_conv::val_str(String *str)
int to_base= (int) args[2]->val_int();
int err;
+ // Note that abs(INT_MIN) is undefined.
if (args[0]->null_value || args[1]->null_value || args[2]->null_value ||
+ from_base == INT_MIN || to_base == INT_MIN ||
abs(to_base) > 36 || abs(to_base) < 2 ||
abs(from_base) > 36 || abs(from_base) < 2 || !(res->length()))
{
@@ -2705,9 +2938,12 @@ String *Item_func_conv::val_str(String *str)
from_base, &endptr, &err);
}
- ptr= longlong2str(dec, ans, to_base, 1);
- if (str->copy(ans, (uint32) (ptr-ans), default_charset()))
- return make_empty_result();
+ if (!(ptr= longlong2str(dec, ans, to_base)) ||
+ str->copy(ans, (uint32) (ptr - ans), default_charset()))
+ {
+ null_value= 1;
+ return NULL;
+ }
return str;
}
@@ -2719,7 +2955,7 @@ String *Item_func_conv_charset::val_str(String *str)
return null_value ? 0 : &str_value;
String *arg= args[0]->val_str(str);
uint dummy_errors;
- if (!arg)
+ if (args[0]->null_value)
{
null_value=1;
return 0;
@@ -2732,7 +2968,7 @@ String *Item_func_conv_charset::val_str(String *str)
void Item_func_conv_charset::fix_length_and_dec()
{
collation.set(conv_charset, DERIVATION_IMPLICIT);
- max_length = args[0]->max_length*conv_charset->mbmaxlen;
+ fix_char_length(args[0]->max_char_length());
}
void Item_func_conv_charset::print(String *str, enum_query_type query_type)
@@ -2822,7 +3058,7 @@ String *Item_func_charset::val_str(String *str)
DBUG_ASSERT(fixed == 1);
uint dummy_errors;
- CHARSET_INFO *cs= args[0]->collation.collation;
+ CHARSET_INFO *cs= args[0]->charset_for_protocol();
null_value= 0;
str->copy(cs->csname, (uint) strlen(cs->csname),
&my_charset_latin1, collation.collation, &dummy_errors);
@@ -2833,7 +3069,7 @@ String *Item_func_collation::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
uint dummy_errors;
- CHARSET_INFO *cs= args[0]->collation.collation;
+ CHARSET_INFO *cs= args[0]->charset_for_protocol();
null_value= 0;
str->copy(cs->name, (uint) strlen(cs->name),
@@ -2842,7 +3078,7 @@ String *Item_func_collation::val_str(String *str)
}
-String *Item_func_hex::val_str(String *str)
+String *Item_func_hex::val_str_ascii(String *str)
{
String *res;
DBUG_ASSERT(fixed == 1);
@@ -2866,9 +3102,11 @@ String *Item_func_hex::val_str(String *str)
if ((null_value= args[0]->null_value))
return 0;
- ptr= longlong2str(dec,ans,16,1);
- if (str->copy(ans,(uint32) (ptr-ans),default_charset()))
- return make_empty_result(); // End of memory
+
+ if (!(ptr= longlong2str(dec, ans, 16)) ||
+ str->copy(ans,(uint32) (ptr - ans),
+ &my_charset_numeric))
+ return make_empty_result(); // End of memory
return str;
}
@@ -2881,6 +3119,7 @@ String *Item_func_hex::val_str(String *str)
}
null_value=0;
tmp_value.length(res->length()*2);
+ tmp_value.set_charset(&my_charset_latin1);
octet2hex((char*) tmp_value.ptr(), res->ptr(), res->length());
return &tmp_value;
@@ -2928,6 +3167,41 @@ String *Item_func_unhex::val_str(String *str)
}
+#ifndef DBUG_OFF
+String *Item_func_like_range::val_str(String *str)
+{
+ DBUG_ASSERT(fixed == 1);
+ longlong nbytes= args[1]->val_int();
+ String *res= args[0]->val_str(str);
+ size_t min_len, max_len;
+ CHARSET_INFO *cs= collation.collation;
+
+ if (!res || args[0]->null_value || args[1]->null_value ||
+ nbytes < 0 || nbytes > MAX_BLOB_WIDTH ||
+ min_str.alloc(nbytes) || max_str.alloc(nbytes))
+ goto err;
+ null_value=0;
+
+ if (cs->coll->like_range(cs, res->ptr(), res->length(),
+ '\\', '_', '%', nbytes,
+ (char*) min_str.ptr(), (char*) max_str.ptr(),
+ &min_len, &max_len))
+ goto err;
+
+ min_str.set_charset(collation.collation);
+ max_str.set_charset(collation.collation);
+ min_str.length(min_len);
+ max_str.length(max_len);
+
+ return is_min ? &min_str : &max_str;
+
+err:
+ null_value= 1;
+ return 0;
+}
+#endif
+
+
void Item_func_binary::print(String *str, enum_query_type query_type)
{
str->append(STRING_WITH_LEN("cast("));
@@ -2961,7 +3235,7 @@ String *Item_load_file::val_str(String *str)
if (!is_secure_file_path(path))
goto err;
- if (!my_stat(path, &stat_info, MYF(0)))
+ if (!mysql_file_stat(key_file_loadfile, path, &stat_info, MYF(0)))
goto err;
if (!(stat_info.st_mode & S_IROTH))
@@ -2979,15 +3253,17 @@ String *Item_load_file::val_str(String *str)
}
if (tmp_value.alloc((size_t)stat_info.st_size))
goto err;
- if ((file = my_open(file_name->ptr(), O_RDONLY, MYF(0))) < 0)
+ if ((file= mysql_file_open(key_file_loadfile,
+ file_name->ptr(), O_RDONLY, MYF(0))) < 0)
goto err;
- if (my_read(file, (uchar*) tmp_value.ptr(), (size_t)stat_info.st_size, MYF(MY_NABP)))
+ if (mysql_file_read(file, (uchar*) tmp_value.ptr(), stat_info.st_size,
+ MYF(MY_NABP)))
{
- my_close(file, MYF(0));
+ mysql_file_close(file, MYF(0));
goto err;
}
tmp_value.length((uint32)stat_info.st_size);
- my_close(file, MYF(0));
+ mysql_file_close(file, MYF(0));
null_value = 0;
DBUG_RETURN(&tmp_value);
@@ -3084,13 +3360,13 @@ String* Item_func_export_set::val_str(String* str)
void Item_func_export_set::fix_length_and_dec()
{
- uint length=max(args[1]->max_length,args[2]->max_length);
- uint sep_length=(arg_count > 3 ? args[3]->max_length : 1);
- max_length=length*64+sep_length*63;
+ uint32 length= max(args[1]->max_char_length(), args[2]->max_char_length());
+ uint32 sep_length= (arg_count > 3 ? args[3]->max_char_length() : 1);
- if (agg_arg_charsets(collation, args+1, min(4,arg_count)-1,
- MY_COLL_ALLOW_CONV, 1))
+ if (agg_arg_charsets_for_string_result(collation,
+ args + 1, min(4, arg_count) - 1))
return;
+ fix_char_length(length * 64 + sep_length * 63);
}
String* Item_func_inet_ntoa::val_str(String* str)
@@ -3127,11 +3403,11 @@ String* Item_func_inet_ntoa::val_str(String* str)
num[0]=(char) n1+'0';
num[1]=(char) n2+'0';
num[2]=(char) c+'0';
- uint length=(n1 ? 4 : n2 ? 3 : 2); // Remove pre-zero
-
- (void) str->append(num+4-length,length);
+ uint length= (n1 ? 4 : n2 ? 3 : 2); // Remove pre-zero
+ uint dot_length= (p <= buf) ? 1 : 0;
+ (void) str->append(num + 4 - length, length - dot_length,
+ &my_charset_latin1);
}
- str->length(str->length()-1); // Remove last '.';
return str;
}
@@ -3308,7 +3584,7 @@ longlong Item_func_uncompressed_length::val_int()
*/
if (res->length() <= 4)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_ZLIB_Z_DATA_ERROR,
ER(ER_ZLIB_Z_DATA_ERROR));
null_value= 1;
@@ -3385,7 +3661,7 @@ String *Item_func_compress::val_str(String *str)
res->length())) != Z_OK)
{
code= err==Z_MEM_ERROR ? ER_ZLIB_Z_MEM_ERROR : ER_ZLIB_Z_BUF_ERROR;
- push_warning(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR,code,ER(code));
+ push_warning(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN,code,ER(code));
null_value= 1;
return 0;
}
@@ -3423,7 +3699,7 @@ String *Item_func_uncompress::val_str(String *str)
/* If length is less than 4 bytes, data is corrupt */
if (res->length() <= 4)
{
- push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN,
ER_ZLIB_Z_DATA_ERROR,
ER(ER_ZLIB_Z_DATA_ERROR));
goto err;
@@ -3433,7 +3709,7 @@ String *Item_func_uncompress::val_str(String *str)
new_size= uint4korr(res->ptr()) & 0x3FFFFFFF;
if (new_size > current_thd->variables.max_allowed_packet)
{
- push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TOO_BIG_FOR_UNCOMPRESS,
ER(ER_TOO_BIG_FOR_UNCOMPRESS),
static_cast<int>(current_thd->variables.
@@ -3444,7 +3720,7 @@ String *Item_func_uncompress::val_str(String *str)
goto err;
if ((err= uncompress((Byte*)buffer.ptr(), &new_size,
- ((const Bytef*)res->ptr())+4,res->length())) == Z_OK)
+ ((const Bytef*)res->ptr())+4,res->length()-4)) == Z_OK)
{
buffer.length((uint32) new_size);
return &buffer;
@@ -3452,7 +3728,7 @@ String *Item_func_uncompress::val_str(String *str)
code= ((err == Z_BUF_ERROR) ? ER_ZLIB_Z_BUF_ERROR :
((err == Z_MEM_ERROR) ? ER_ZLIB_Z_MEM_ERROR : ER_ZLIB_Z_DATA_ERROR));
- push_warning(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR,code,ER(code));
+ push_warning(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN,code,ER(code));
err:
null_value= 1;
@@ -3492,6 +3768,7 @@ bool Item_func_dyncol_create::fix_fields(THD *thd, Item **ref)
(arg_count / 2));
nums= (uint *) alloc_root(thd->mem_root,
sizeof(uint) * (arg_count / 2));
+ status_var_increment(thd->status_var.feature_dynamic_columns);
return res || vals == 0 || nums == 0;
}
@@ -3632,7 +3909,7 @@ void Item_func_dyncol_create::prepare_arguments()
if (vals[i].type != DYN_COL_NULL && args[valpos]->null_value)
{
if (vals[i].type == DYN_COL_STRING)
- my_free(vals[i].x.string.value.str, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(vals[i].x.string.value.str);
vals[i].type= DYN_COL_NULL;
}
}
@@ -3646,7 +3923,7 @@ void Item_func_dyncol_create::cleanup_arguments()
for (i= 0; i < column_count; i++)
{
if (vals[i].type == DYN_COL_STRING)
- my_free(vals[i].x.string.value.str, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(vals[i].x.string.value.str);
}
}
@@ -3891,8 +4168,7 @@ String *Item_dyncol_get::val_str(String *str_result)
case DYN_COL_DECIMAL:
{
int res;
- int length=
- my_decimal_string_length((const my_decimal*)&val.x.decimal.value);
+ int length= decimal_string_size(&val.x.decimal.value);
if (str_result->alloc(length))
goto null;
if ((res= decimal2string(&val.x.decimal.value, (char*) str_result->ptr(),
@@ -4130,7 +4406,7 @@ null:
}
-bool Item_dyncol_get::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
+bool Item_dyncol_get::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
DYNAMIC_COLUMN_VALUE val;
char buff[STRING_BUFFER_USUAL_SIZE];
@@ -4170,7 +4446,8 @@ bool Item_dyncol_get::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
goto null;
return 0;
case DYN_COL_STRING:
- if (str_to_datetime_with_warn(val.x.string.value.str,
+ if (str_to_datetime_with_warn(&my_charset_numeric,
+ val.x.string.value.str,
val.x.string.value.length,
ltime, fuzzy_date) <= MYSQL_TIMESTAMP_ERROR)
goto null;
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index 9e3cb877803..fcc1991774c 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -1,5 +1,9 @@
+#ifndef ITEM_STRFUNC_INCLUDED
+#define ITEM_STRFUNC_INCLUDED
+
/*
Copyright (c) 2000, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +16,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
/* This file defines all string functions */
@@ -22,6 +25,10 @@
#pragma interface /* gcc class implementation */
#endif
+extern size_t username_char_length;
+
+class MY_LOCALE;
+
class Item_str_func :public Item_func
{
protected:
@@ -55,34 +62,59 @@ public:
enum Item_result result_type () const { return STRING_RESULT; }
void left_right_max_length();
bool fix_fields(THD *thd, Item **ref);
+ String *val_str_from_val_str_ascii(String *str, String *str2);
};
-class Item_func_md5 :public Item_str_func
+
+
+/*
+ Functions that return values with ASCII repertoire
+*/
+class Item_str_ascii_func :public Item_str_func
{
- String tmp_value;
+ String ascii_buf;
public:
- Item_func_md5(Item *a) :Item_str_func(a)
+ Item_str_ascii_func() :Item_str_func() {}
+ Item_str_ascii_func(Item *a) :Item_str_func(a) {}
+ Item_str_ascii_func(Item *a,Item *b) :Item_str_func(a,b) {}
+ Item_str_ascii_func(Item *a,Item *b,Item *c) :Item_str_func(a,b,c) {}
+ String *val_str(String *str)
{
- collation.set(&my_charset_bin);
+ return val_str_from_val_str_ascii(str, &ascii_buf);
}
- String *val_str(String *);
+ virtual String *val_str_ascii(String *)= 0;
+};
+
+
+class Item_func_md5 :public Item_str_ascii_func
+{
+ String tmp_value;
+public:
+ Item_func_md5(Item *a) :Item_str_ascii_func(a) {}
+ String *val_str_ascii(String *);
void fix_length_and_dec();
const char *func_name() const { return "md5"; }
};
-class Item_func_sha :public Item_str_func
+class Item_func_sha :public Item_str_ascii_func
{
public:
- Item_func_sha(Item *a) :Item_str_func(a)
- {
- collation.set(&my_charset_bin);
- }
- String *val_str(String *);
+ Item_func_sha(Item *a) :Item_str_ascii_func(a) {}
+ String *val_str_ascii(String *);
void fix_length_and_dec();
const char *func_name() const { return "sha"; }
};
+class Item_func_sha2 :public Item_str_ascii_func
+{
+public:
+ Item_func_sha2(Item *a, Item *b) :Item_str_ascii_func(a, b) {}
+ String *val_str_ascii(String *);
+ void fix_length_and_dec();
+ const char *func_name() const { return "sha2"; }
+};
+
class Item_func_aes_encrypt :public Item_str_func
{
public:
@@ -238,6 +270,21 @@ class Item_func_trim :public Item_str_func
protected:
String tmp_value;
String remove;
+ String *trimmed_value(String *res, uint32 offset, uint32 length)
+ {
+ tmp_value.set(*res, offset, length);
+ /*
+ Make sure to return correct charset and collation:
+ TRIM(0x000000 FROM _ucs2 0x0061)
+ should set charset to "binary" rather than to "ucs2".
+ */
+ tmp_value.set_charset(collation.collation);
+ return &tmp_value;
+ }
+ String *non_trimmed_value(String *res)
+ {
+ return trimmed_value(res, 0, res->length());
+ }
public:
Item_func_trim(Item *a,Item *b) :Item_str_func(a,b) {}
Item_func_trim(Item *a) :Item_str_func(a) {}
@@ -279,13 +326,16 @@ public:
authentication procedure works, see comments in password.c.
*/
-class Item_func_password :public Item_str_func
+class Item_func_password :public Item_str_ascii_func
{
char tmp_value[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
public:
- Item_func_password(Item *a) :Item_str_func(a) {}
- String *val_str(String *str);
- void fix_length_and_dec() { max_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; }
+ Item_func_password(Item *a) :Item_str_ascii_func(a) {}
+ String *val_str_ascii(String *str);
+ void fix_length_and_dec()
+ {
+ fix_length_and_charset(SCRAMBLED_PASSWORD_CHAR_LENGTH, default_charset());
+ }
const char *func_name() const { return "password"; }
static char *alloc(THD *thd, const char *password, size_t pass_len);
};
@@ -298,13 +348,16 @@ public:
function.
*/
-class Item_func_old_password :public Item_str_func
+class Item_func_old_password :public Item_str_ascii_func
{
char tmp_value[SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1];
public:
- Item_func_old_password(Item *a) :Item_str_func(a) {}
- String *val_str(String *str);
- void fix_length_and_dec() { max_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; }
+ Item_func_old_password(Item *a) :Item_str_ascii_func(a) {}
+ String *val_str_ascii(String *str);
+ void fix_length_and_dec()
+ {
+ fix_length_and_charset(SCRAMBLED_PASSWORD_CHAR_LENGTH_323, default_charset());
+ }
const char *func_name() const { return "old_password"; }
static char *alloc(THD *thd, const char *password, size_t pass_len);
};
@@ -337,7 +390,9 @@ public:
{
maybe_null=1;
/* 9 = MAX ((8- (arg_len % 8)) + 1) */
- max_length = args[0]->max_length - 9;
+ max_length= args[0]->max_length;
+ if (max_length >= 9U)
+ max_length-= 9U;
}
const char *func_name() const { return "des_decrypt"; }
};
@@ -456,8 +511,8 @@ public:
bool fix_fields(THD *thd, Item **ref);
void fix_length_and_dec()
{
- max_length= (USERNAME_LENGTH +
- (HOSTNAME_LENGTH + 1) * SYSTEM_CHARSET_MBMAXLEN);
+ max_length= (username_char_length +
+ HOSTNAME_LENGTH + 1) * SYSTEM_CHARSET_MBMAXLEN;
}
const char *func_name() const { return "user"; }
const char *fully_qualified_func_name() const { return "user()"; }
@@ -516,12 +571,17 @@ public:
};
-class Item_func_format :public Item_str_func
+class Item_func_format :public Item_str_ascii_func
{
String tmp_str;
+ MY_LOCALE *locale;
public:
- Item_func_format(Item *org, Item *dec);
- String *val_str(String *);
+ Item_func_format(Item *org, Item *dec): Item_str_ascii_func(org, dec) {}
+ Item_func_format(Item *org, Item *dec, Item *lang):
+ Item_str_ascii_func(org, dec, lang) {}
+
+ MY_LOCALE *get_locale(Item *item);
+ String *val_str_ascii(String *);
void fix_length_and_dec();
const char *func_name() const { return "format"; }
virtual void print(String *str, enum_query_type query_type);
@@ -594,18 +654,18 @@ public:
};
-class Item_func_hex :public Item_str_func
+class Item_func_hex :public Item_str_ascii_func
{
String tmp_value;
public:
- Item_func_hex(Item *a) :Item_str_func(a) {}
+ Item_func_hex(Item *a) :Item_str_ascii_func(a) {}
const char *func_name() const { return "hex"; }
- String *val_str(String *);
+ String *val_str_ascii(String *);
void fix_length_and_dec()
{
collation.set(default_charset());
decimals=0;
- max_length=args[0]->max_length*2*collation.collation->mbmaxlen;
+ fix_char_length(args[0]->max_length * 2);
}
};
@@ -629,6 +689,46 @@ public:
};
+#ifndef DBUG_OFF
+class Item_func_like_range :public Item_str_func
+{
+protected:
+ String min_str;
+ String max_str;
+ const bool is_min;
+public:
+ Item_func_like_range(Item *a, Item *b, bool is_min_arg)
+ :Item_str_func(a, b), is_min(is_min_arg)
+ { maybe_null= 1; }
+ String *val_str(String *);
+ void fix_length_and_dec()
+ {
+ collation.set(args[0]->collation);
+ decimals=0;
+ max_length= MAX_BLOB_WIDTH;
+ }
+};
+
+
+class Item_func_like_range_min :public Item_func_like_range
+{
+public:
+ Item_func_like_range_min(Item *a, Item *b)
+ :Item_func_like_range(a, b, true) { }
+ const char *func_name() const { return "like_range_min"; }
+};
+
+
+class Item_func_like_range_max :public Item_func_like_range
+{
+public:
+ Item_func_like_range_max(Item *a, Item *b)
+ :Item_func_like_range(a, b, false) { }
+ const char *func_name() const { return "like_range_max"; }
+};
+#endif
+
+
class Item_func_binary :public Item_str_func
{
public:
@@ -694,7 +794,7 @@ public:
void fix_length_and_dec()
{
decimals= 0;
- max_length= 3 * 8 + 7;
+ fix_length_and_charset(3 * 8 + 7, default_charset());
maybe_null= 1;
}
};
@@ -729,7 +829,7 @@ public:
{
DBUG_ASSERT(args[0]->fixed);
conv_charset= cs;
- if (cache_if_const && args[0]->const_item() && !args[0]->with_subselect)
+ if (cache_if_const && args[0]->const_item() && !args[0]->is_expensive())
{
uint errors= 0;
String tmp, *str= args[0]->val_str(&tmp);
@@ -754,6 +854,42 @@ public:
}
}
String *val_str(String *);
+ longlong val_int()
+ {
+ if (args[0]->result_type() == STRING_RESULT)
+ return Item_str_func::val_int();
+ longlong res= args[0]->val_int();
+ if ((null_value= args[0]->null_value))
+ return 0;
+ return res;
+ }
+ double val_real()
+ {
+ if (args[0]->result_type() == STRING_RESULT)
+ return Item_str_func::val_real();
+ double res= args[0]->val_real();
+ if ((null_value= args[0]->null_value))
+ return 0;
+ return res;
+ }
+ my_decimal *val_decimal(my_decimal *d)
+ {
+ if (args[0]->result_type() == STRING_RESULT)
+ return Item_str_func::val_decimal(d);
+ my_decimal *res= args[0]->val_decimal(d);
+ if ((null_value= args[0]->null_value))
+ return NULL;
+ return res;
+ }
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+ {
+ if (args[0]->result_type() == STRING_RESULT)
+ return Item_str_func::get_date(ltime, fuzzydate);
+ bool res= args[0]->get_date(ltime, fuzzydate);
+ if ((null_value= args[0]->null_value))
+ return 1;
+ return res;
+ }
void fix_length_and_dec();
const char *func_name() const { return "convert"; }
virtual void print(String *str, enum_query_type query_type);
@@ -857,14 +993,11 @@ class Item_func_uuid: public Item_str_func
{
public:
Item_func_uuid(): Item_str_func() {}
- void fix_length_and_dec() {
- collation.set(system_charset_info);
- /*
- NOTE! uuid() should be changed to use 'ascii'
- charset when hex(), format(), md5(), etc, and implicit
- number-to-string conversion will use 'ascii'
- */
- max_length= MY_UUID_STRING_LENGTH * system_charset_info->mbmaxlen;
+ void fix_length_and_dec()
+ {
+ collation.set(system_charset_info,
+ DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
+ fix_char_length(MY_UUID_STRING_LENGTH);
}
const char *func_name() const{ return "uuid"; }
String *val_str(String *);
@@ -929,7 +1062,7 @@ public:
double val_real();
my_decimal *val_decimal(my_decimal *);
bool get_dyn_value(DYNAMIC_COLUMN_VALUE *val, String *tmp);
- bool get_date(MYSQL_TIME *ltime,uint fuzzydate);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
void print(String *str, enum_query_type query_type);
};
@@ -942,3 +1075,5 @@ public:
const char *func_name() const{ return "column_list"; }
String *val_str(String *);
};
+
+#endif /* ITEM_STRFUNC_INCLUDED */
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 7535ab49389..62df666b71f 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -1,4 +1,5 @@
-/* Copyright (c) 2002, 2013, Oracle and/or its affiliates.
+/* Copyright (c) 2002, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2012, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,7 +12,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@file
@@ -28,8 +29,17 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
+/*
+ It is necessary to include set_var.h instead of item.h because there
+ are dependencies on include order for set_var.h and item.h. This
+ will be resolved later.
+*/
+#include "sql_class.h" // set_var.h: THD
+#include "set_var.h"
#include "sql_select.h"
+#include "sql_parse.h" // check_stack_overrun
+#include "sql_test.h"
double get_post_group_estimate(JOIN* join, double join_op_rows);
@@ -39,7 +49,7 @@ Item_subselect::Item_subselect():
used_tables_cache(0), have_to_be_excluded(0), const_item_cache(1),
inside_first_fix_fields(0), done_first_fix_fields(FALSE),
expr_cache(0), forced_const(FALSE), substitution(0), engine(0), eliminated(FALSE),
- engine_changed(0), changed(0), is_correlated(FALSE)
+ changed(0), is_correlated(FALSE)
{
DBUG_ENTER("Item_subselect::Item_subselect");
DBUG_PRINT("enter", ("this: 0x%lx", (ulong) this));
@@ -210,6 +220,8 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
uint8 uncacheable;
bool res;
+ status_var_increment(thd_param->status_var.feature_subquery);
+
DBUG_ASSERT(fixed == 0);
engine->set_thd((thd= thd_param));
if (!done_first_fix_fields)
@@ -277,7 +289,7 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref)
else
goto end;
- if ((uncacheable= engine->uncacheable()))
+ if ((uncacheable= engine->uncacheable() & ~UNCACHEABLE_EXPLAIN))
{
const_item_cache= 0;
if (uncacheable & UNCACHEABLE_RAND)
@@ -513,6 +525,59 @@ void Item_subselect::recalc_used_tables(st_select_lex *new_parent,
*/
}
+
+/**
+ Determine if a subquery is expensive to execute during query optimization.
+
+ @details The cost of execution of a subquery is estimated based on an
+ estimate of the number of rows the subquery will access during execution.
+ This measure is used instead of JOIN::read_time, because it is considered
+ to be much more reliable than the cost estimate.
+
+ @return true if the subquery is expensive
+ @return false otherwise
+*/
+bool Item_subselect::is_expensive()
+{
+ double examined_rows= 0;
+
+ for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select())
+ {
+ JOIN *cur_join= sl->join;
+ if (!cur_join)
+ continue;
+
+ /*
+ Subqueries whose result is known after optimization are not expensive.
+ Such subqueries have all tables optimized away, thus have no join plan.
+ */
+ if (cur_join->optimized &&
+ (cur_join->zero_result_cause || !cur_join->tables_list))
+ return false;
+
+ /*
+ If a subquery is not optimized we cannot estimate its cost. A subquery is
+ considered optimized if it has a join plan.
+ */
+ if (!(cur_join->optimized && cur_join->join_tab))
+ return true;
+
+ if (sl->first_inner_unit())
+ {
+ /*
+ Subqueries that contain subqueries are considered expensive.
+ @todo: accumulate the cost of subqueries.
+ */
+ return true;
+ }
+
+ examined_rows+= cur_join->get_examined_rows();
+ }
+
+ return (examined_rows > thd->variables.expensive_subquery_limit);
+}
+
+
bool Item_subselect::walk(Item_processor processor, bool walk_subquery,
uchar *argument)
{
@@ -569,32 +634,39 @@ bool Item_subselect::walk(Item_processor processor, bool walk_subquery,
bool Item_subselect::exec()
{
- int res;
+ subselect_engine *org_engine= engine;
+
+ DBUG_ENTER("Item_subselect::exec");
/*
Do not execute subselect in case of a fatal error
or if the query has been killed.
*/
if (thd->is_error() || thd->killed)
- return 1;
+ DBUG_RETURN(true);
DBUG_ASSERT(!thd->lex->context_analysis_only);
/*
Simulate a failure in sub-query execution. Used to test e.g.
out of memory or query being killed conditions.
*/
- DBUG_EXECUTE_IF("subselect_exec_fail", return 1;);
+ DBUG_EXECUTE_IF("subselect_exec_fail", DBUG_RETURN(true););
+
+ bool res= engine->exec();
- res= engine->exec();
#ifndef DBUG_OFF
++exec_counter;
#endif
- if (engine_changed)
+ if (engine != org_engine)
{
- engine_changed= 0;
- return exec();
+ /*
+ If the subquery engine changed during execution due to lazy subquery
+ optimization, or because the original engine found a more efficient other
+ engine, re-execute the subquery with the new engine.
+ */
+ DBUG_RETURN(exec());
}
- return (res);
+ DBUG_RETURN(res);
}
@@ -1026,6 +1098,11 @@ 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.
@@ -1218,7 +1295,7 @@ bool Item_singlerow_subselect::val_bool()
}
-bool Item_singlerow_subselect::get_date(MYSQL_TIME *ltime,uint fuzzydate)
+bool Item_singlerow_subselect::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
{
DBUG_ASSERT(fixed == 1);
if (forced_const)
@@ -1669,7 +1746,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
of the statement. Thus one of 'substitution' arguments
can be broken in case of PS.
*/
- substitution= func->create(left_expr->real_item(), where_item);
+ substitution= func->create(left_expr, where_item);
have_to_be_excluded= 1;
if (thd->lex->describe)
{
@@ -1800,7 +1877,8 @@ 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|= 1 << thd->lex->current_select->nest_level;
+ thd->lex->allow_sum_func|=
+ (nesting_map)1 << 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
@@ -2622,7 +2700,7 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref)
}
}
- if ((thd_arg->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW) &&
+ if (thd_arg->lex->is_view_context_analysis() &&
left_expr && !left_expr->fixed &&
left_expr->fix_fields(thd_arg, &left_expr))
return TRUE;
@@ -2870,6 +2948,14 @@ bool subselect_union_engine::no_rows()
void subselect_uniquesubquery_engine::cleanup()
{
DBUG_ENTER("subselect_uniquesubquery_engine::cleanup");
+ /*
+ Note for mergers: we don't have to, and actually must not de-initialize
+ tab->table->file here.
+ - We don't have to, because free_tmp_table() will call ha_index_or_rnd_end
+ - We must not do it, because tab->table may be a derived table which
+ has been already dropped by close_thread_tables(), while we here are
+ called from cleanup_items()
+ */
DBUG_VOID_RETURN;
}
@@ -2880,8 +2966,6 @@ subselect_union_engine::subselect_union_engine(THD *thd_arg, st_select_lex_unit
:subselect_engine(thd_arg, item_arg, result_arg)
{
unit= u;
- if (!result_arg) //out of memory
- current_thd->fatal_error();
unit->item= item_arg;
}
@@ -2923,10 +3007,7 @@ int subselect_single_select_engine::prepare()
join= new JOIN(thd, select_lex->item_list,
select_lex->options | SELECT_NO_UNLOCK, result);
if (!join || !result)
- {
- thd->fatal_error(); //out of memory
- return 1;
- }
+ return 1; /* Fatal error is set already. */
prepared= 1;
SELECT_LEX *save_select= thd->lex->current_select;
thd->lex->current_select= select_lex;
@@ -2989,12 +3070,13 @@ void subselect_engine::set_row(List<Item> &item_list, Item_cache **row)
{
Item *sel_item;
List_iterator_fast<Item> li(item_list);
- res_type= STRING_RESULT;
+ cmp_type= res_type= STRING_RESULT;
res_field_type= MYSQL_TYPE_VAR_STRING;
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();
item->decimals= sel_item->decimals;
item->unsigned_flag= sel_item->unsigned_flag;
@@ -3005,7 +3087,7 @@ void subselect_engine::set_row(List<Item> &item_list, Item_cache **row)
//psergey-backport-timours: row[i]->store(sel_item);
}
if (item_list.elements > 1)
- res_type= ROW_RESULT;
+ cmp_type= res_type= ROW_RESULT;
}
void subselect_single_select_engine::fix_length_and_dec(Item_cache **row)
@@ -3040,7 +3122,8 @@ void subselect_uniquesubquery_engine::fix_length_and_dec(Item_cache **row)
DBUG_ASSERT(0);
}
-int init_read_record_seq(JOIN_TAB *tab);
+int read_first_record_seq(JOIN_TAB *tab);
+int rr_sequential(READ_RECORD *info);
int join_read_always_key_or_null(JOIN_TAB *tab);
int join_read_next_same_or_null(READ_RECORD *info);
@@ -3088,10 +3171,8 @@ int subselect_single_select_engine::exec()
DBUG_RETURN(1); /* purecov: inspected */
}
}
- if (item->engine_changed)
- {
+ if (item->engine_changed(this))
DBUG_RETURN(1);
- }
}
if (select_lex->uncacheable &&
select_lex->uncacheable != UNCACHEABLE_EXPLAIN
@@ -3119,8 +3200,9 @@ int subselect_single_select_engine::exec()
pushed down into the subquery. Those optimizations are ref[_or_null]
acceses. Change them to be full table scans.
*/
- for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_CONST_TABLES); tab;
- tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS))
+ JOIN_TAB *tab;
+ for (tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
+ tab; tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS))
{
if (tab && tab->keyuse)
{
@@ -3132,7 +3214,8 @@ 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_first_record= init_read_record_seq;
+ tab->read_record.read_record= 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;
@@ -3201,11 +3284,14 @@ int subselect_uniquesubquery_engine::scan_table()
TABLE *table= tab->table;
DBUG_ENTER("subselect_uniquesubquery_engine::scan_table");
- if (table->file->inited)
- table->file->ha_index_end();
-
- if (table->file->ha_rnd_init_with_error(1))
- DBUG_RETURN(1);
+ if ((table->file->inited &&
+ (error= table->file->ha_index_end())) ||
+ (error= table->file->ha_rnd_init(1)))
+ {
+ (void) report_error(table, error);
+ DBUG_RETURN(true);
+ }
+
table->file->extra_opt(HA_EXTRA_CACHE,
current_thd->variables.read_buff_size);
table->null_row= 0;
@@ -3341,8 +3427,13 @@ int subselect_uniquesubquery_engine::exec()
DBUG_RETURN(0);
}
- if (!table->file->inited)
- table->file->ha_index_init(tab->ref.key, 0);
+ if (!table->file->inited &&
+ (error= table->file->ha_index_init(tab->ref.key, 0)))
+ {
+ (void) report_error(table, error);
+ DBUG_RETURN(true);
+ }
+
error= table->file->ha_index_read_map(table->record[0],
tab->ref.key_buff,
make_prev_keypart_map(tab->
@@ -3506,8 +3597,13 @@ int subselect_indexsubquery_engine::exec()
DBUG_RETURN(0);
}
- if (!table->file->inited)
- table->file->ha_index_init(tab->ref.key, 1);
+ if (!table->file->inited &&
+ (error= table->file->ha_index_init(tab->ref.key, 1)))
+ {
+ (void) report_error(table, error);
+ DBUG_RETURN(true);
+ }
+
error= table->file->ha_index_read_map(table->record[0],
tab->ref.key_buff,
make_prev_keypart_map(tab->
@@ -4167,7 +4263,7 @@ bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id)
{
select_union *result_sink;
/* Options to create_tmp_table. */
- ulonglong tmp_create_options= thd->options | TMP_TABLE_ALL_COLUMNS;
+ ulonglong tmp_create_options= thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS;
/* | TMP_TABLE_FORCE_MYISAM; TIMOUR: force MYISAM */
DBUG_ENTER("subselect_hash_sj_engine::init");
@@ -4312,7 +4408,10 @@ 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("", tmp_table->alias.c_ptr(), TL_READ);
+ tmp_table_ref->init_one_table(STRING_WITH_LEN(""),
+ tmp_table->alias.c_ptr(),
+ tmp_table->alias.length(),
+ NULL, TL_READ);
tmp_table_ref->table= tmp_table;
context= new Name_resolution_context;
@@ -4891,7 +4990,7 @@ Ordered_key::Ordered_key(uint keyid_arg, TABLE *tbl_arg, Item *search_key_arg,
Ordered_key::~Ordered_key()
{
- my_free((char*) key_buff, MYF(0));
+ my_free(key_buff);
bitmap_free(&null_key);
}
@@ -5034,10 +5133,20 @@ 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. */
- error= tbl->file->ha_rnd_pos(tbl->record[0], rowid_a);
- DBUG_ASSERT(!error);
- error= tbl->file->ha_rnd_pos(tbl->record[1], rowid_b);
- DBUG_ASSERT(!error);
+ if ((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)))
+ {
+ /* purecov: begin inspected */
+ tbl->file->print_error(error, MYF(ME_FATALERROR)); // Sets fatal_error
+ return 0;
+ /* purecov: end */
+ }
/*
Compare the two rows by the corresponding values of the indexed
columns.
@@ -5112,8 +5221,13 @@ int Ordered_key::cmp_key_with_search_key(rownum_t row_num)
uchar *cur_rowid= row_num_to_rowid + row_num * rowid_length;
int error, cmp_res;
- error= tbl->file->ha_rnd_pos(tbl->record[0], cur_rowid);
- DBUG_ASSERT(!error);
+ if ((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
+ return 0;
+ /* purecov: end */
+ }
for (uint i= 0; i < key_column_count; i++)
{
@@ -5531,7 +5645,7 @@ subselect_rowid_merge_engine::~subselect_rowid_merge_engine()
/* None of the resources below is allocated if there are no ordered keys. */
if (merge_keys_count)
{
- my_free((char*) row_num_to_rowid, MYF(0));
+ my_free(row_num_to_rowid);
for (uint i= 0; i < merge_keys_count; i++)
delete merge_keys[i];
delete_queue(&pq);
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index bdd9ca023bf..592e7711a10 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -1,5 +1,7 @@
-/*
- Copyright (c) 2002, 2011, Oracle and/or its affiliates.
+#ifndef ITEM_SUBSELECT_INCLUDED
+#define ITEM_SUBSELECT_INCLUDED
+
+/* Copyright (c) 2002, 2011, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +14,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* subselect Item */
@@ -21,6 +22,8 @@
#pragma interface /* gcc class implementation */
#endif
+#include <queues.h>
+
class st_select_lex;
class st_select_lex_unit;
class JOIN;
@@ -28,6 +31,15 @@ class select_result_interceptor;
class subselect_engine;
class subselect_hash_sj_engine;
class Item_bool_func2;
+class Comp_creator;
+
+typedef class st_select_lex SELECT_LEX;
+
+/**
+ Convenience typedef used in this file, and further used by any files
+ including this file.
+*/
+typedef Comp_creator* (*chooser_compare_func_creator)(bool invert);
class Cached_item;
/* base class for subselects */
@@ -110,8 +122,6 @@ public:
*/
bool eliminated;
- /* changed engine indicator */
- bool engine_changed;
/* subquery is transformed */
bool changed;
@@ -188,16 +198,16 @@ public:
{
old_engine= engine;
engine= eng;
- engine_changed= 1;
return eng == 0;
}
+ bool engine_changed(subselect_engine *eng) { return engine != eng; }
/*
True if this subquery has been already evaluated. Implemented only for
single select and union subqueries only.
*/
bool is_evaluated() const;
bool is_uncacheable() const;
- bool is_expensive() { return TRUE; }
+ bool is_expensive();
/*
Used by max/min subquery to initialize value presence registration
@@ -223,7 +233,7 @@ public:
@retval TRUE if the predicate is expensive
@retval FALSE otherwise
*/
- bool is_expensive_processor(uchar *arg) { return TRUE; }
+ bool is_expensive_processor(uchar *arg) { return is_expensive(); }
/**
Get the SELECT_LEX structure associated with this Item.
@@ -248,7 +258,6 @@ public:
st_select_lex*, st_select_lex*,
Field*, Item*, Item_ident*);
friend bool convert_join_subqueries_to_semijoins(JOIN *join);
-
};
/* single value subselect */
@@ -275,13 +284,14 @@ public:
String *val_str (String *);
my_decimal *val_decimal(my_decimal *);
bool val_bool();
- bool get_date(MYSQL_TIME *ltime,uint fuzzydate);
+ 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;
void fix_length_and_dec();
uint cols();
- Item* element_index(uint i) { return my_reinterpret_cast(Item*)(row[i]); }
+ Item* element_index(uint i) { return reinterpret_cast<Item*>(row[i]); }
Item** addr(uint i) { return (Item**)row + i; }
bool check_cols(uint c);
bool null_inside();
@@ -569,6 +579,10 @@ public:
bool fix_fields(THD *thd, Item **ref);
void fix_length_and_dec();
void fix_after_pullout(st_select_lex *new_parent, Item **ref);
+ bool const_item() const
+ {
+ return Item_subselect::const_item() && left_expr->const_item();
+ }
void update_used_tables();
bool setup_mat_engine();
bool init_left_expr_cache();
@@ -685,6 +699,7 @@ protected:
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:
@@ -699,7 +714,7 @@ public:
{
result= res;
item= si;
- res_type= STRING_RESULT;
+ cmp_type= res_type= STRING_RESULT;
res_field_type= MYSQL_TYPE_VAR_STRING;
maybe_null= 0;
set_thd(thd_arg);
@@ -739,6 +754,7 @@ public:
virtual uint cols()= 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; };
@@ -931,6 +947,15 @@ public:
virtual enum_engine_type engine_type() { return INDEXSUBQUERY_ENGINE; }
};
+/*
+ This function is actually defined in sql_parse.cc, but it depends on
+ chooser_compare_func_creator defined in this file.
+ */
+Item * all_any_subquery_creator(Item *left_expr,
+ chooser_compare_func_creator cmp,
+ bool all,
+ SELECT_LEX *select_lex);
+
inline bool Item_subselect::is_evaluated() const
{
@@ -943,7 +968,6 @@ inline bool Item_subselect::is_uncacheable() const
return engine->uncacheable();
}
-
/**
Compute an IN predicate via a hash semi-join. This class is responsible for
the materialization of the subquery, and the selection of the correct and
@@ -1405,3 +1429,4 @@ public:
void cleanup();
virtual enum_engine_type engine_type() { return TABLE_SCAN_ENGINE; }
};
+#endif /* ITEM_SUBSELECT_INCLUDED */
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 515ef46e85d..27456a94543 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2008, 2014, Monty Program Ab
+ Copyright (c) 2008, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,7 +12,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@@ -26,10 +26,22 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
#include "sql_select.h"
/**
+ Calculate the affordable RAM limit for structures like TREE or Unique
+ used in Item_sum_*
+*/
+
+ulonglong Item_sum::ram_limitation(THD *thd)
+{
+ return min(thd->variables.tmp_table_size,
+ thd->variables.max_heap_table_size);
+}
+
+
+/**
Prepare an aggregate function item for checking context conditions.
The function initializes the members of the Item_sum object created
@@ -151,9 +163,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 & (1 << max_arg_level));
+ invalid= !(allow_sum_func & ((nesting_map)1 << max_arg_level));
}
- else if (max_arg_level >= 0 || !(allow_sum_func & (1 << nest_level)))
+ else if (max_arg_level >= 0 ||
+ !(allow_sum_func & ((nesting_map)1 << nest_level)))
{
/*
The set function can be aggregated only in outer subqueries.
@@ -162,7 +175,8 @@ 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 & (1 << nest_level));
+ invalid= aggr_level < 0 &&
+ !(allow_sum_func & ((nesting_map)1 << nest_level));
if (!invalid && thd->variables.sql_mode & MODE_ANSI)
invalid= aggr_level < 0 && max_arg_level < nest_level;
}
@@ -247,7 +261,7 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref)
List_iterator<Item_field> of(outer_fields);
while ((field= of++))
{
- SELECT_LEX *sel= field->cached_table->select_lex;
+ SELECT_LEX *sel= field->field->table->pos_in_table_list->select_lex;
if (sel->nest_level < aggr_level)
{
if (in_sum_func)
@@ -310,14 +324,15 @@ bool Item_sum::register_sum_func(THD *thd, Item **ref)
sl && sl->nest_level > max_arg_level;
sl= sl->context.outer_select())
{
- if (aggr_level < 0 && (allow_sum_func & (1 << sl->nest_level)))
+ if (aggr_level < 0 &&
+ (allow_sum_func & ((nesting_map)1 << 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 & (1 << sl->nest_level)))
+ if (sl && (allow_sum_func & ((nesting_map)1 << sl->nest_level)))
{
/*
We reached the subquery of level max_arg_level and checked
@@ -398,6 +413,7 @@ Item_sum::Item_sum(List<Item> &list) :arg_count(list.elements),
args= NULL;
}
mark_as_sum_func();
+ init_aggregator();
list.empty(); // Fields are used
}
@@ -429,6 +445,10 @@ Item_sum::Item_sum(THD *thd, Item_sum *item):
}
memcpy(args, item->args, sizeof(Item*)*arg_count);
memcpy(orig_args, item->orig_args, sizeof(Item*)*arg_count);
+ init_aggregator();
+ with_distinct= item->with_distinct;
+ if (item->aggr)
+ set_aggregator(item->aggr->Aggrtype());
}
@@ -547,17 +567,561 @@ void Item_sum::update_used_tables ()
args[i]->update_used_tables();
used_tables_cache|= args[i]->used_tables();
}
+ /*
+ MariaDB: don't run the following {
+
+ used_tables_cache&= PSEUDO_TABLE_BITS;
+
+ // the aggregate function is aggregated into its local context
+ used_tables_cache|= ((table_map)1 << aggr_sel->join->tables) - 1;
+
+ } because if we do it, table elimination will assume that
+ - constructs like "COUNT(*)" use columns from all tables
+ - so, it is not possible to eliminate any table
+ our solution for COUNT(*) is that it has
+ item->used_tables() == 0 && !item->const_item()
+ */
}
}
-Item *Item_sum::set_arg(int i, THD *thd, Item *new_val)
+Item *Item_sum::set_arg(uint i, THD *thd, Item *new_val)
{
thd->change_item_tree(args + i, new_val);
return new_val;
}
+int Item_sum::set_aggregator(Aggregator::Aggregator_type aggregator)
+{
+ /*
+ Dependent subselects may be executed multiple times, making
+ set_aggregator to be called multiple times. The aggregator type
+ will be the same, but it needs to be reset so that it is
+ reevaluated with the new dependent data.
+ This function may also be called multiple times during query optimization.
+ In this case, the type may change, so we delete the old aggregator,
+ and create a new one.
+ */
+ if (aggr && aggregator == aggr->Aggrtype())
+ {
+ aggr->clear();
+ return FALSE;
+ }
+
+ delete aggr;
+ switch (aggregator)
+ {
+ case Aggregator::DISTINCT_AGGREGATOR:
+ aggr= new Aggregator_distinct(this);
+ break;
+ case Aggregator::SIMPLE_AGGREGATOR:
+ aggr= new Aggregator_simple(this);
+ break;
+ };
+ return aggr ? FALSE : TRUE;
+}
+
+
+void Item_sum::cleanup()
+{
+ if (aggr)
+ {
+ delete aggr;
+ aggr= NULL;
+ }
+ Item_result_field::cleanup();
+ forced_const= FALSE;
+}
+
+
+/**
+ Compare keys consisting of single field that cannot be compared as binary.
+
+ Used by the Unique class to compare keys. Will do correct comparisons
+ for all field types.
+
+ @param arg Pointer to the relevant Field class instance
+ @param key1 left key image
+ @param key2 right key image
+ @return comparison result
+ @retval < 0 if key1 < key2
+ @retval = 0 if key1 = key2
+ @retval > 0 if key1 > key2
+*/
+
+static int simple_str_key_cmp(void* arg, uchar* key1, uchar* key2)
+{
+ Field *f= (Field*) arg;
+ return f->cmp(key1, key2);
+}
+
+
+/**
+ Correctly compare composite keys.
+
+ Used by the Unique class to compare keys. Will do correct comparisons
+ for composite keys with various field types.
+
+ @param arg Pointer to the relevant Aggregator_distinct instance
+ @param key1 left key image
+ @param key2 right key image
+ @return comparison result
+ @retval <0 if key1 < key2
+ @retval =0 if key1 = key2
+ @retval >0 if key1 > key2
+*/
+
+int Aggregator_distinct::composite_key_cmp(void* arg, uchar* key1, uchar* key2)
+{
+ Aggregator_distinct *aggr= (Aggregator_distinct *) arg;
+ Field **field = aggr->table->field;
+ Field **field_end= field + aggr->table->s->fields;
+ uint32 *lengths=aggr->field_lengths;
+ for (; field < field_end; ++field)
+ {
+ Field* f = *field;
+ int len = *lengths++;
+ int res = f->cmp(key1, key2);
+ if (res)
+ return res;
+ key1 += len;
+ key2 += len;
+ }
+ return 0;
+}
+
+
+static enum enum_field_types
+calc_tmp_field_type(enum enum_field_types table_field_type,
+ Item_result result_type)
+{
+ /* Adjust tmp table type according to the chosen aggregation type */
+ switch (result_type) {
+ case STRING_RESULT:
+ case REAL_RESULT:
+ if (table_field_type != MYSQL_TYPE_FLOAT)
+ table_field_type= MYSQL_TYPE_DOUBLE;
+ break;
+ case INT_RESULT:
+ table_field_type= MYSQL_TYPE_LONGLONG;
+ /* fallthrough */
+ case DECIMAL_RESULT:
+ if (table_field_type != MYSQL_TYPE_LONGLONG)
+ table_field_type= MYSQL_TYPE_NEWDECIMAL;
+ break;
+ case ROW_RESULT:
+ default:
+ DBUG_ASSERT(0);
+ }
+ return table_field_type;
+}
+
+
+/***************************************************************************/
+
+C_MODE_START
+
+/* Declarations for auxilary C-callbacks */
+
+static int simple_raw_key_cmp(void* arg, const void* key1, const void* key2)
+{
+ return memcmp(key1, key2, *(uint *) arg);
+}
+
+
+static int item_sum_distinct_walk_for_count(void *element,
+ element_count num_of_dups,
+ void *item)
+{
+ return ((Aggregator_distinct*) (item))->unique_walk_function_for_count(element);
+}
+
+
+static int item_sum_distinct_walk(void *element, element_count num_of_dups,
+ void *item)
+{
+ return ((Aggregator_distinct*) (item))->unique_walk_function(element);
+}
+
+C_MODE_END
+
+/***************************************************************************/
+/**
+ Called before feeding the first row. Used to allocate/setup
+ the internal structures used for aggregation.
+
+ @param thd Thread descriptor
+ @return status
+ @retval FALSE success
+ @retval TRUE faliure
+
+ Prepares Aggregator_distinct to process the incoming stream.
+ Creates the temporary table and the Unique class if needed.
+ Called by Item_sum::aggregator_setup()
+*/
+
+bool Aggregator_distinct::setup(THD *thd)
+{
+ endup_done= FALSE;
+ /*
+ Setup can be called twice for ROLLUP items. This is a bug.
+ Please add DBUG_ASSERT(tree == 0) here when it's fixed.
+ */
+ if (tree || table || tmp_table_param)
+ return FALSE;
+
+ if (item_sum->setup(thd))
+ return TRUE;
+ if (item_sum->sum_func() == Item_sum::COUNT_FUNC ||
+ item_sum->sum_func() == Item_sum::COUNT_DISTINCT_FUNC)
+ {
+ List<Item> list;
+ SELECT_LEX *select_lex= thd->lex->current_select;
+
+ if (!(tmp_table_param= new TMP_TABLE_PARAM))
+ return TRUE;
+
+ /* Create a table with an unique key over all parameters */
+ for (uint i=0; i < item_sum->get_arg_count() ; i++)
+ {
+ Item *item=item_sum->get_arg(i);
+ if (list.push_back(item))
+ return TRUE; // End of memory
+ if (item->const_item() && item->is_null())
+ always_null= true;
+ }
+ if (always_null)
+ return FALSE;
+ count_field_types(select_lex, tmp_table_param, list, 0);
+ tmp_table_param->force_copy_fields= item_sum->has_force_copy_fields();
+ DBUG_ASSERT(table == 0);
+ /*
+ Make create_tmp_table() convert BIT columns to BIGINT.
+ This is needed because BIT fields store parts of their data in table's
+ null bits, and we don't have methods to compare two table records, which
+ is needed by Unique which is used when HEAP table is used.
+ */
+ {
+ List_iterator_fast<Item> li(list);
+ Item *item;
+ while ((item= li++))
+ {
+ if (item->type() == Item::FIELD_ITEM &&
+ ((Item_field*)item)->field->type() == FIELD_TYPE_BIT)
+ item->marker=4;
+ }
+ }
+ 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*>(""))))
+ return TRUE;
+ table->file->extra(HA_EXTRA_NO_ROWS); // Don't update rows
+ table->no_rows=1;
+
+ if (table->s->db_type() == heap_hton)
+ {
+ /*
+ No blobs, otherwise it would have been MyISAM: set up a compare
+ function and its arguments to use with Unique.
+ */
+ qsort_cmp2 compare_key;
+ void* cmp_arg;
+ Field **field= table->field;
+ Field **field_end= field + table->s->fields;
+ bool all_binary= TRUE;
+
+ for (tree_key_length= 0; field < field_end; ++field)
+ {
+ Field *f= *field;
+ enum enum_field_types type= f->type();
+ tree_key_length+= f->pack_length();
+ if ((type == MYSQL_TYPE_VARCHAR) ||
+ (!f->binary() && (type == MYSQL_TYPE_STRING ||
+ type == MYSQL_TYPE_VAR_STRING)))
+ {
+ all_binary= FALSE;
+ break;
+ }
+ }
+ if (all_binary)
+ {
+ cmp_arg= (void*) &tree_key_length;
+ compare_key= (qsort_cmp2) simple_raw_key_cmp;
+ }
+ else
+ {
+ if (table->s->fields == 1)
+ {
+ /*
+ If we have only one field, which is the most common use of
+ count(distinct), it is much faster to use a simpler key
+ compare method that can take advantage of not having to worry
+ about other fields.
+ */
+ compare_key= (qsort_cmp2) simple_str_key_cmp;
+ cmp_arg= (void*) table->field[0];
+ /* tree_key_length has been set already */
+ }
+ else
+ {
+ uint32 *length;
+ compare_key= (qsort_cmp2) composite_key_cmp;
+ cmp_arg= (void*) this;
+ field_lengths= (uint32*) thd->alloc(table->s->fields * sizeof(uint32));
+ for (tree_key_length= 0, length= field_lengths, field= table->field;
+ field < field_end; ++field, ++length)
+ {
+ *length= (*field)->pack_length();
+ tree_key_length+= *length;
+ }
+ }
+ }
+ DBUG_ASSERT(tree == 0);
+ tree= new Unique(compare_key, cmp_arg, tree_key_length,
+ item_sum->ram_limitation(thd));
+ /*
+ The only time tree_key_length could be 0 is if someone does
+ count(distinct) on a char(0) field - stupid thing to do,
+ but this has to be handled - otherwise someone can crash
+ the server with a DoS attack
+ */
+ if (! tree)
+ return TRUE;
+ }
+ return FALSE;
+ }
+ else
+ {
+ List<Create_field> field_list;
+ Create_field field_def; /* field definition */
+ Item *arg;
+ DBUG_ENTER("Aggregator_distinct::setup");
+ /* It's legal to call setup() more than once when in a subquery */
+ if (tree)
+ DBUG_RETURN(FALSE);
+
+ /*
+ Virtual table and the tree are created anew on each re-execution of
+ PS/SP. Hence all further allocations are performed in the runtime
+ mem_root.
+ */
+ if (field_list.push_back(&field_def))
+ DBUG_RETURN(TRUE);
+
+ item_sum->null_value= item_sum->maybe_null= 1;
+ item_sum->quick_group= 0;
+
+ DBUG_ASSERT(item_sum->get_arg(0)->fixed);
+
+ arg= item_sum->get_arg(0);
+ if (arg->const_item())
+ {
+ (void) arg->val_int();
+ if (arg->null_value)
+ always_null= true;
+ }
+
+ if (always_null)
+ DBUG_RETURN(FALSE);
+
+ enum enum_field_types field_type;
+
+ field_type= calc_tmp_field_type(arg->field_type(),
+ arg->result_type());
+ field_def.init_for_tmp_table(field_type,
+ arg->max_length,
+ arg->decimals,
+ arg->maybe_null,
+ arg->unsigned_flag);
+
+ if (! (table= create_virtual_tmp_table(thd, field_list)))
+ DBUG_RETURN(TRUE);
+
+ /* XXX: check that the case of CHAR(0) works OK */
+ tree_key_length= table->s->reclength - table->s->null_bytes;
+
+ /*
+ Unique handles all unique elements in a tree until they can't fit
+ in. Then the tree is dumped to the temporary file. We can use
+ simple_raw_key_cmp because the table contains numbers only; decimals
+ are converted to binary representation as well.
+ */
+ tree= new Unique(simple_raw_key_cmp, &tree_key_length, tree_key_length,
+ item_sum->ram_limitation(thd));
+
+ DBUG_RETURN(tree == 0);
+ }
+}
+
+
+/**
+ Invalidate calculated value and clear the distinct rows.
+
+ Frees space used by the internal data structures.
+ Removes the accumulated distinct rows. Invalidates the calculated result.
+*/
+
+void Aggregator_distinct::clear()
+{
+ endup_done= FALSE;
+ item_sum->clear();
+ if (tree)
+ tree->reset();
+ /* tree and table can be both null only if always_null */
+ if (item_sum->sum_func() == Item_sum::COUNT_FUNC ||
+ item_sum->sum_func() == Item_sum::COUNT_DISTINCT_FUNC)
+ {
+ if (!tree && table)
+ {
+ table->file->extra(HA_EXTRA_NO_CACHE);
+ table->file->ha_delete_all_rows();
+ table->file->extra(HA_EXTRA_WRITE_CACHE);
+ }
+ }
+ else
+ {
+ item_sum->null_value= 1;
+ }
+}
+
+
+/**
+ Process incoming row.
+
+ Add it to Unique/temp hash table if it's unique. Skip the row if
+ not unique.
+ Prepare Aggregator_distinct to process the incoming stream.
+ Create the temporary table and the Unique class if needed.
+ Called by Item_sum::aggregator_add().
+ To actually get the result value in item_sum's buffers
+ Aggregator_distinct::endup() must be called.
+
+ @return status
+ @retval FALSE success
+ @retval TRUE failure
+*/
+
+bool Aggregator_distinct::add()
+{
+ if (always_null)
+ return 0;
+
+ if (item_sum->sum_func() == Item_sum::COUNT_FUNC ||
+ item_sum->sum_func() == Item_sum::COUNT_DISTINCT_FUNC)
+ {
+ int error;
+ copy_fields(tmp_table_param);
+ if (copy_funcs(tmp_table_param->items_to_copy, table->in_use))
+ return TRUE;
+
+ for (Field **field=table->field ; *field ; field++)
+ if ((*field)->is_real_null(0))
+ return 0; // Don't count NULL
+
+ if (tree)
+ {
+ /*
+ The first few bytes of record (at least one) are just markers
+ for deleted and NULLs. We want to skip them since they will
+ bloat the tree without providing any valuable info. Besides,
+ key_length used to initialize the tree didn't include space for them.
+ */
+ return tree->unique_add(table->record[0] + table->s->null_bytes);
+ }
+ if ((error= table->file->ha_write_tmp_row(table->record[0])) &&
+ table->file->is_fatal_error(error, HA_CHECK_DUP))
+ return TRUE;
+ return FALSE;
+ }
+ else
+ {
+ item_sum->get_arg(0)->save_in_field(table->field[0], FALSE);
+ if (table->field[0]->is_null())
+ return 0;
+ DBUG_ASSERT(tree);
+ item_sum->null_value= 0;
+ /*
+ '0' values are also stored in the tree. This doesn't matter
+ for SUM(DISTINCT), but is important for AVG(DISTINCT)
+ */
+ return tree->unique_add(table->field[0]->ptr);
+ }
+}
+
+
+/**
+ Calculate the aggregate function value.
+
+ Since Distinct_aggregator::add() just collects the distinct rows,
+ we must go over the distinct rows and feed them to the aggregation
+ function before returning its value.
+ This is what endup () does. It also sets the result validity flag
+ endup_done to TRUE so it will not recalculate the aggregate value
+ again if the Item_sum hasn't been reset.
+*/
+
+void Aggregator_distinct::endup()
+{
+ /* prevent consecutive recalculations */
+ if (endup_done)
+ return;
+
+ /* we are going to calculate the aggregate value afresh */
+ item_sum->clear();
+
+ /* The result will definitely be null : no more calculations needed */
+ if (always_null)
+ return;
+
+ if (item_sum->sum_func() == Item_sum::COUNT_FUNC ||
+ item_sum->sum_func() == Item_sum::COUNT_DISTINCT_FUNC)
+ {
+ DBUG_ASSERT(item_sum->fixed == 1);
+ Item_sum_count *sum= (Item_sum_count *)item_sum;
+ if (tree && tree->elements == 0)
+ {
+ /* everything fits in memory */
+ sum->count= (longlong) tree->elements_in_tree();
+ endup_done= TRUE;
+ }
+ if (!tree)
+ {
+ /* there were blobs */
+ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
+ sum->count= table->file->stats.records;
+ endup_done= TRUE;
+ }
+ }
+
+ /*
+ We don't have a tree only if 'setup()' hasn't been called;
+ this is the case of sql_executor.cc:return_zero_rows.
+ */
+ if (tree && !endup_done)
+ {
+ /*
+ All tree's values are not NULL.
+ Note that value of field is changed as we walk the tree, in
+ Aggregator_distinct::unique_walk_function, but it's always not NULL.
+ */
+ table->field[0]->set_notnull();
+ /* go over the tree of distinct keys and calculate the aggregate value */
+ use_distinct_values= TRUE;
+ tree_walk_action func;
+ if (item_sum->sum_func() == Item_sum::COUNT_DISTINCT_FUNC)
+ func= item_sum_distinct_walk_for_count;
+ else
+ func= item_sum_distinct_walk;
+ tree->walk(table, func, (void*) this);
+ use_distinct_values= FALSE;
+ }
+ /* prevent consecutive recalculations */
+ endup_done= TRUE;
+}
+
+
String *
Item_sum_num::val_str(String *str)
{
@@ -836,8 +1400,9 @@ bool Item_sum_sum::add()
DBUG_ENTER("Item_sum_sum::add");
if (hybrid_type == DECIMAL_RESULT)
{
- my_decimal value, *val= args[0]->val_decimal(&value);
- if (!args[0]->null_value)
+ my_decimal value;
+ const my_decimal *val= aggr->arg_val_decimal(&value);
+ if (!aggr->arg_is_null(true))
{
my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff^1),
val, dec_buffs + curr_dec_buff);
@@ -847,8 +1412,8 @@ bool Item_sum_sum::add()
}
else
{
- sum+= args[0]->val_real();
- if (!args[0]->null_value)
+ sum+= aggr->arg_val_real();
+ if (!aggr->arg_is_null(true))
null_value= 0;
}
DBUG_RETURN(0);
@@ -858,6 +1423,8 @@ bool Item_sum_sum::add()
longlong Item_sum_sum::val_int()
{
DBUG_ASSERT(fixed == 1);
+ if (aggr)
+ aggr->endup();
if (hybrid_type == DECIMAL_RESULT)
{
longlong result;
@@ -872,6 +1439,8 @@ longlong Item_sum_sum::val_int()
double Item_sum_sum::val_real()
{
DBUG_ASSERT(fixed == 1);
+ if (aggr)
+ aggr->endup();
if (hybrid_type == DECIMAL_RESULT)
my_decimal2double(E_DEC_FATAL_ERROR, dec_buffs + curr_dec_buff, &sum);
return sum;
@@ -880,6 +1449,8 @@ double Item_sum_sum::val_real()
String *Item_sum_sum::val_str(String *str)
{
+ if (aggr)
+ aggr->endup();
if (hybrid_type == DECIMAL_RESULT)
return val_string_from_decimal(str);
return val_string_from_real(str);
@@ -888,314 +1459,135 @@ String *Item_sum_sum::val_str(String *str)
my_decimal *Item_sum_sum::val_decimal(my_decimal *val)
{
+ if (aggr)
+ aggr->endup();
if (hybrid_type == DECIMAL_RESULT)
return (dec_buffs + curr_dec_buff);
return val_decimal_from_real(val);
}
-/***************************************************************************/
-
-C_MODE_START
-
-/* Declarations for auxilary C-callbacks */
-
-static int simple_raw_key_cmp(void* arg, const void* key1, const void* key2)
-{
- return memcmp(key1, key2, *(uint *) arg);
-}
-
-
-static int item_sum_distinct_walk(void *element, element_count num_of_dups,
- void *item)
+/**
+ Aggregate a distinct row from the distinct hash table.
+
+ Called for each row into the hash table 'Aggregator_distinct::table'.
+ Includes the current distinct row into the calculation of the
+ aggregate value. Uses the Field classes to get the value from the row.
+ This function is used for AVG/SUM(DISTINCT). For COUNT(DISTINCT)
+ it's called only when there are no blob arguments and the data don't
+ fit into memory (so Unique makes persisted trees on disk).
+
+ @param element pointer to the row data.
+
+ @return status
+ @retval FALSE success
+ @retval TRUE failure
+*/
+
+bool Aggregator_distinct::unique_walk_function(void *element)
{
- return ((Item_sum_distinct*) (item))->unique_walk_function(element);
+ memcpy(table->field[0]->ptr, element, tree_key_length);
+ item_sum->add();
+ return 0;
}
-C_MODE_END
-
-/* Item_sum_distinct */
-Item_sum_distinct::Item_sum_distinct(Item *item_arg)
- :Item_sum_num(item_arg), tree(0)
-{
- /*
- quick_group is an optimizer hint, which means that GROUP BY can be
- handled with help of index on grouped columns.
- By setting quick_group to zero we force creation of temporary table
- to perform GROUP BY.
- */
- quick_group= 0;
-}
+/*
+ A variant of unique_walk_function() that is to be used with Item_sum_count.
+ COUNT is a special aggregate function: it doesn't need the values, it only
+ needs to count them. COUNT needs to know the values are not NULLs, but NULL
+ values are not put into the Unique, so we don't need to check for NULLs here.
+*/
-Item_sum_distinct::Item_sum_distinct(THD *thd, Item_sum_distinct *original)
- :Item_sum_num(thd, original), val(original->val), tree(0),
- table_field_type(original->table_field_type)
+bool Aggregator_distinct::unique_walk_function_for_count(void *element)
{
- quick_group= 0;
+ Item_sum_count *sum= (Item_sum_count *)item_sum;
+ sum->count++;
+ return 0;
}
-/**
- Behaves like an Integer except to fix_length_and_dec().
- Additionally div() converts val with this traits to a val with true
- decimal traits along with conversion of integer value to decimal value.
- This is to speedup SUM/AVG(DISTINCT) evaluation for 8-32 bit integer
- values.
-*/
-struct Hybrid_type_traits_fast_decimal: public
- Hybrid_type_traits_integer
+Aggregator_distinct::~Aggregator_distinct()
{
- virtual Item_result type() const { return DECIMAL_RESULT; }
- virtual void fix_length_and_dec(Item *item, Item *arg) const
- { Hybrid_type_traits_decimal::instance()->fix_length_and_dec(item, arg); }
-
- virtual void div(Hybrid_type *val, ulonglong u) const
+ if (tree)
{
- int2my_decimal(E_DEC_FATAL_ERROR, val->integer, 0, val->dec_buf);
- val->used_dec_buf_no= 0;
- val->traits= Hybrid_type_traits_decimal::instance();
- val->traits->div(val, u);
+ delete tree;
+ tree= NULL;
}
- static const Hybrid_type_traits_fast_decimal *instance();
- Hybrid_type_traits_fast_decimal() {};
-};
-
-static const Hybrid_type_traits_fast_decimal fast_decimal_traits_instance;
-
-const Hybrid_type_traits_fast_decimal
- *Hybrid_type_traits_fast_decimal::instance()
-{
- return &fast_decimal_traits_instance;
-}
-
-void Item_sum_distinct::fix_length_and_dec()
-{
- DBUG_ASSERT(args[0]->fixed);
-
- table_field_type= args[0]->field_type();
-
- /* Adjust tmp table type according to the chosen aggregation type */
- switch (args[0]->result_type()) {
- case STRING_RESULT:
- case REAL_RESULT:
- val.traits= Hybrid_type_traits::instance();
- if (table_field_type != MYSQL_TYPE_FLOAT)
- table_field_type= MYSQL_TYPE_DOUBLE;
- break;
- case INT_RESULT:
- /*
- Preserving int8, int16, int32 field types gives ~10% performance boost
- as the size of result tree becomes significantly smaller.
- Another speed up we gain by using longlong for intermediate
- calculations. The range of int64 is enough to hold sum 2^32 distinct
- integers each <= 2^32.
- */
- if (table_field_type == MYSQL_TYPE_INT24 ||
- (table_field_type >= MYSQL_TYPE_TINY &&
- table_field_type <= MYSQL_TYPE_LONG))
+ if (table)
{
- val.traits= Hybrid_type_traits_fast_decimal::instance();
- break;
- }
- table_field_type= MYSQL_TYPE_LONGLONG;
- /* fallthrough */
- case DECIMAL_RESULT:
- val.traits= Hybrid_type_traits_decimal::instance();
- if (table_field_type != MYSQL_TYPE_LONGLONG)
- table_field_type= MYSQL_TYPE_NEWDECIMAL;
- break;
- case ROW_RESULT:
- case TIME_RESULT:
- case IMPOSSIBLE_RESULT:
- DBUG_ASSERT(0);
+ free_tmp_table(table->in_use, table);
+ table=NULL;
}
- val.traits->fix_length_and_dec(this, args[0]);
-}
-
-
-/**
- @todo
- check that the case of CHAR(0) works OK
-*/
-bool Item_sum_distinct::setup(THD *thd)
-{
- List<Create_field> field_list;
- Create_field field_def; /* field definition */
- DBUG_ENTER("Item_sum_distinct::setup");
- /* It's legal to call setup() more than once when in a subquery */
- if (tree)
- DBUG_RETURN(FALSE);
-
- /*
- Virtual table and the tree are created anew on each re-execution of
- PS/SP. Hence all further allocations are performed in the runtime
- mem_root.
- */
- if (field_list.push_back(&field_def))
- DBUG_RETURN(TRUE);
-
- null_value= maybe_null= 1;
- quick_group= 0;
-
- DBUG_ASSERT(args[0]->fixed);
-
- field_def.init_for_tmp_table(table_field_type, args[0]->max_length,
- args[0]->decimals, args[0]->maybe_null,
- args[0]->unsigned_flag);
-
- if (! (table= create_virtual_tmp_table(thd, field_list)))
- DBUG_RETURN(TRUE);
-
- /* XXX: check that the case of CHAR(0) works OK */
- tree_key_length= table->s->reclength - table->s->null_bytes;
-
- /*
- Unique handles all unique elements in a tree until they can't fit
- in. Then the tree is dumped to the temporary file. We can use
- simple_raw_key_cmp because the table contains numbers only; decimals
- are converted to binary representation as well.
- */
- tree= new Unique(simple_raw_key_cmp, &tree_key_length, tree_key_length,
- thd->variables.max_heap_table_size);
-
- is_evaluated= FALSE;
- DBUG_RETURN(tree == 0);
-}
-
-
-bool Item_sum_distinct::add()
-{
- args[0]->save_in_field(table->field[0], FALSE);
- is_evaluated= FALSE;
- if (!table->field[0]->is_null())
+ if (tmp_table_param)
{
- DBUG_ASSERT(tree);
- null_value= 0;
- /*
- '0' values are also stored in the tree. This doesn't matter
- for SUM(DISTINCT), but is important for AVG(DISTINCT)
- */
- return tree->unique_add(table->field[0]->ptr);
+ delete tmp_table_param;
+ tmp_table_param= NULL;
}
- return 0;
}
-bool Item_sum_distinct::unique_walk_function(void *element)
+my_decimal *Aggregator_simple::arg_val_decimal(my_decimal *value)
{
- memcpy(table->field[0]->ptr, element, tree_key_length);
- ++count;
- val.traits->add(&val, table->field[0]);
- return 0;
+ return item_sum->args[0]->val_decimal(value);
}
-void Item_sum_distinct::clear()
+double Aggregator_simple::arg_val_real()
{
- DBUG_ENTER("Item_sum_distinct::clear");
- /* During EXPLAIN there is no tree because it is created during execution. */
- if (tree != 0)
- tree->reset();
- null_value= 1;
- is_evaluated= FALSE;
- DBUG_VOID_RETURN;
+ return item_sum->args[0]->val_real();
}
-void Item_sum_distinct::cleanup()
-{
- Item_sum_num::cleanup();
- delete tree;
- tree= 0;
- table= 0;
- is_evaluated= FALSE;
-}
-Item_sum_distinct::~Item_sum_distinct()
+bool Aggregator_simple::arg_is_null(bool use_null_value)
{
- delete tree;
- /* no need to free the table */
-}
-
-
-void Item_sum_distinct::calculate_val_and_count()
-{
- if (!is_evaluated)
+ Item **item= item_sum->args;
+ const uint item_count= item_sum->arg_count;
+ if (use_null_value)
{
- count= 0;
- val.traits->set_zero(&val);
- /*
- We don't have a tree only if 'setup()' hasn't been called;
- this is the case of sql_select.cc:return_zero_rows.
- */
- if (tree)
+ for (uint i= 0; i < item_count; i++)
{
- table->field[0]->set_notnull();
- tree->walk(table, item_sum_distinct_walk, (void*) this);
+ if (item[i]->null_value)
+ return true;
}
- is_evaluated= TRUE;
}
+ else
+ {
+ for (uint i= 0; i < item_count; i++)
+ {
+ if (item[i]->maybe_null && item[i]->is_null())
+ return true;
+ }
+ }
+ return false;
}
-double Item_sum_distinct::val_real()
-{
- calculate_val_and_count();
- return val.traits->val_real(&val);
-}
-
-
-my_decimal *Item_sum_distinct::val_decimal(my_decimal *to)
-{
- calculate_val_and_count();
- if (null_value)
- return 0;
- return val.traits->val_decimal(&val, to);
-}
-
-
-longlong Item_sum_distinct::val_int()
-{
- calculate_val_and_count();
- return val.traits->val_int(&val, unsigned_flag);
-}
-
-
-String *Item_sum_distinct::val_str(String *str)
+my_decimal *Aggregator_distinct::arg_val_decimal(my_decimal * value)
{
- calculate_val_and_count();
- if (null_value)
- return 0;
- return val.traits->val_str(&val, str, decimals);
+ return use_distinct_values ? table->field[0]->val_decimal(value) :
+ item_sum->args[0]->val_decimal(value);
}
-/* end of Item_sum_distinct */
-/* Item_sum_avg_distinct */
-
-void
-Item_sum_avg_distinct::fix_length_and_dec()
+double Aggregator_distinct::arg_val_real()
{
- Item_sum_distinct::fix_length_and_dec();
- prec_increment= current_thd->variables.div_precincrement;
- /*
- AVG() will divide val by count. We need to reserve digits
- after decimal point as the result can be fractional.
- */
- decimals= min(decimals + prec_increment, NOT_FIXED_DEC);
+ return use_distinct_values ? table->field[0]->val_real() :
+ item_sum->args[0]->val_real();
}
-void
-Item_sum_avg_distinct::calculate_val_and_count()
+bool Aggregator_distinct::arg_is_null(bool use_null_value)
{
- if (!is_evaluated)
+ if (use_distinct_values)
{
- Item_sum_distinct::calculate_val_and_count();
- if (count)
- val.traits->div(&val, count);
- is_evaluated= TRUE;
+ const bool rc= table->field[0]->is_null();
+ DBUG_ASSERT(!rc); // NULLs are never stored in 'tree'
+ return rc;
}
+ return use_null_value ?
+ item_sum->args[0]->null_value :
+ (item_sum->args[0]->maybe_null && item_sum->args[0]->is_null());
}
@@ -1213,14 +1605,17 @@ void Item_sum_count::clear()
bool Item_sum_count::add()
{
- if (!args[0]->maybe_null || !args[0]->is_null())
- count++;
+ if (aggr->arg_is_null(false))
+ return 0;
+ count++;
return 0;
}
longlong Item_sum_count::val_int()
{
DBUG_ASSERT(fixed == 1);
+ if (aggr)
+ aggr->endup();
return (longlong) count;
}
@@ -1303,7 +1698,7 @@ bool Item_sum_avg::add()
{
if (Item_sum_sum::add())
return TRUE;
- if (!args[0]->null_value)
+ if (!aggr->arg_is_null(true))
count++;
return FALSE;
}
@@ -1311,6 +1706,8 @@ bool Item_sum_avg::add()
double Item_sum_avg::val_real()
{
DBUG_ASSERT(fixed == 1);
+ if (aggr)
+ aggr->endup();
if (!count)
{
null_value=1;
@@ -1322,9 +1719,11 @@ double Item_sum_avg::val_real()
my_decimal *Item_sum_avg::val_decimal(my_decimal *val)
{
- my_decimal sum_buff, cnt;
+ my_decimal cnt;
const my_decimal *sum_dec;
DBUG_ASSERT(fixed == 1);
+ if (aggr)
+ aggr->endup();
if (!count)
{
null_value=1;
@@ -1347,6 +1746,8 @@ my_decimal *Item_sum_avg::val_decimal(my_decimal *val)
String *Item_sum_avg::val_str(String *str)
{
+ if (aggr)
+ aggr->endup();
if (hybrid_type == DECIMAL_RESULT)
return val_string_from_decimal(str);
return val_string_from_real(str);
@@ -1605,7 +2006,7 @@ void Item_sum_variance::update_field()
void Item_sum_hybrid::clear()
{
- value->null_value= 1;
+ value->clear();
null_value= 1;
}
@@ -1614,7 +2015,10 @@ double Item_sum_hybrid::val_real()
DBUG_ASSERT(fixed == 1);
if (null_value)
return 0.0;
- return value->val_real();
+ double retval= value->val_real();
+ if ((null_value= value->null_value))
+ DBUG_ASSERT(retval == 0.0);
+ return retval;
}
longlong Item_sum_hybrid::val_int()
@@ -1622,7 +2026,10 @@ longlong Item_sum_hybrid::val_int()
DBUG_ASSERT(fixed == 1);
if (null_value)
return 0;
- return value->val_int();
+ longlong retval= value->val_int();
+ if ((null_value= value->null_value))
+ DBUG_ASSERT(retval == 0);
+ return retval;
}
@@ -1631,7 +2038,10 @@ my_decimal *Item_sum_hybrid::val_decimal(my_decimal *val)
DBUG_ASSERT(fixed == 1);
if (null_value)
return 0;
- return value->val_decimal(val);
+ my_decimal *retval= value->val_decimal(val);
+ if ((null_value= value->null_value))
+ DBUG_ASSERT(retval == NULL);
+ return retval;
}
@@ -1641,7 +2051,10 @@ Item_sum_hybrid::val_str(String *str)
DBUG_ASSERT(fixed == 1);
if (null_value)
return 0;
- return value->val_str(str);
+ String *retval= value->val_str(str);
+ if ((null_value= value->null_value))
+ DBUG_ASSERT(retval == NULL);
+ return retval;
}
@@ -1895,6 +2308,7 @@ void Item_sum_hybrid::reset_field()
void Item_sum_sum::reset_field()
{
+ DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR);
if (hybrid_type == DECIMAL_RESULT)
{
my_decimal value, *arg_val= args[0]->val_decimal(&value);
@@ -1919,6 +2333,7 @@ void 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;
@@ -1929,6 +2344,7 @@ void Item_sum_count::reset_field()
void Item_sum_avg::reset_field()
{
uchar *res=result_field->ptr;
+ DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR);
if (hybrid_type == DECIMAL_RESULT)
{
longlong tmp;
@@ -1963,7 +2379,7 @@ void Item_sum_avg::reset_field()
void Item_sum_bit::reset_field()
{
- reset();
+ reset_and_add();
int8store(result_field->ptr, bits);
}
@@ -1982,6 +2398,7 @@ void Item_sum_bit::update_field()
void Item_sum_sum::update_field()
{
+ DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR);
if (hybrid_type == DECIMAL_RESULT)
{
my_decimal value, *arg_val= args[0]->val_decimal(&value);
@@ -2034,6 +2451,9 @@ void Item_sum_avg::update_field()
{
longlong field_count;
uchar *res=result_field->ptr;
+
+ DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR);
+
if (hybrid_type == DECIMAL_RESULT)
{
my_decimal value, *arg_val= args[0]->val_decimal(&value);
@@ -2337,319 +2757,6 @@ double Item_variance_field::val_real()
/****************************************************************************
-** COUNT(DISTINCT ...)
-****************************************************************************/
-
-int simple_str_key_cmp(void* arg, uchar* key1, uchar* key2)
-{
- Field *f= (Field*) arg;
- return f->cmp(key1, key2);
-}
-
-/**
- Did not make this one static - at least gcc gets confused when
- I try to declare a static function as a friend. If you can figure
- out the syntax to make a static function a friend, make this one
- static
-*/
-
-int composite_key_cmp(void* arg, uchar* key1, uchar* key2)
-{
- Item_sum_count_distinct* item = (Item_sum_count_distinct*)arg;
- Field **field = item->table->field;
- Field **field_end= field + item->table->s->fields;
- uint32 *lengths=item->field_lengths;
- for (; field < field_end; ++field)
- {
- Field* f = *field;
- int len = *lengths++;
- int res = f->cmp(key1, key2);
- if (res)
- return res;
- key1 += len;
- key2 += len;
- }
- return 0;
-}
-
-
-C_MODE_START
-
-static int count_distinct_walk(void *elem, element_count count, void *arg)
-{
- (*((ulonglong*)arg))++;
- return 0;
-}
-
-C_MODE_END
-
-
-void Item_sum_count_distinct::cleanup()
-{
- DBUG_ENTER("Item_sum_count_distinct::cleanup");
- Item_sum_int::cleanup();
-
- /* Free objects only if we own them. */
- if (!original)
- {
- /*
- We need to delete the table and the tree in cleanup() as
- they were allocated in the runtime memroot. Using the runtime
- memroot reduces memory footprint for PS/SP and simplifies setup().
- */
- delete tree;
- tree= 0;
- is_evaluated= FALSE;
- if (table)
- {
- free_tmp_table(table->in_use, table);
- table= 0;
- }
- delete tmp_table_param;
- tmp_table_param= 0;
- }
- always_null= FALSE;
- DBUG_VOID_RETURN;
-}
-
-
-/**
- This is used by rollup to create a separate usable copy of
- the function.
-*/
-
-void Item_sum_count_distinct::make_unique()
-{
- table=0;
- original= 0;
- force_copy_fields= 1;
- tree= 0;
- is_evaluated= FALSE;
- tmp_table_param= 0;
- always_null= FALSE;
-}
-
-
-Item_sum_count_distinct::~Item_sum_count_distinct()
-{
- cleanup();
-}
-
-
-bool Item_sum_count_distinct::setup(THD *thd)
-{
- List<Item> list;
- SELECT_LEX *select_lex= thd->lex->current_select;
-
- /*
- Setup can be called twice for ROLLUP items. This is a bug.
- Please add DBUG_ASSERT(tree == 0) here when it's fixed.
- It's legal to call setup() more than once when in a subquery
- */
- if (tree || table || tmp_table_param)
- return FALSE;
-
- if (!(tmp_table_param= new TMP_TABLE_PARAM))
- return TRUE;
-
- /* Create a table with an unique key over all parameters */
- for (uint i=0; i < arg_count ; i++)
- {
- Item *item=args[i];
- if (list.push_back(item))
- return TRUE; // End of memory
- if (item->const_item() && item->is_null())
- always_null= 1;
- }
- if (always_null)
- return FALSE;
- count_field_types(select_lex, tmp_table_param, list, 0);
- tmp_table_param->force_copy_fields= force_copy_fields;
- DBUG_ASSERT(table == 0);
- /*
- Make create_tmp_table() convert BIT columns to BIGINT.
- This is needed because BIT fields store parts of their data in table's
- null bits, and we don't have methods to compare two table records, which
- is needed by Unique which is used when HEAP table is used.
- */
- {
- List_iterator_fast<Item> li(list);
- Item *item;
- while ((item= li++))
- {
- if (item->type() == Item::FIELD_ITEM &&
- ((Item_field*)item)->field->type() == FIELD_TYPE_BIT)
- item->marker=4;
- }
- }
-
- if (!(table= create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, 1,
- 0,
- (select_lex->options | thd->options),
- HA_POS_ERROR, (char*)"")))
- return TRUE;
- table->file->extra(HA_EXTRA_NO_ROWS); // Don't update rows
- table->no_rows=1;
-
- if (table->s->db_type() == heap_hton)
- {
- /*
- No blobs, otherwise it would have been MyISAM: set up a compare
- function and its arguments to use with Unique.
- */
- qsort_cmp2 compare_key;
- void* cmp_arg;
- Field **field= table->field;
- Field **field_end= field + table->s->fields;
- bool all_binary= TRUE;
-
- for (tree_key_length= 0; field < field_end; ++field)
- {
- Field *f= *field;
- enum enum_field_types f_type= f->type();
- tree_key_length+= f->pack_length();
- if ((f_type == MYSQL_TYPE_VARCHAR) ||
- (!f->binary() && (f_type == MYSQL_TYPE_STRING ||
- f_type == MYSQL_TYPE_VAR_STRING)))
- {
- all_binary= FALSE;
- break;
- }
- }
- if (all_binary)
- {
- cmp_arg= (void*) &tree_key_length;
- compare_key= (qsort_cmp2) simple_raw_key_cmp;
- }
- else
- {
- if (table->s->fields == 1)
- {
- /*
- If we have only one field, which is the most common use of
- count(distinct), it is much faster to use a simpler key
- compare method that can take advantage of not having to worry
- about other fields.
- */
- compare_key= (qsort_cmp2) simple_str_key_cmp;
- cmp_arg= (void*) table->field[0];
- /* tree_key_length has been set already */
- }
- else
- {
- uint32 *length;
- compare_key= (qsort_cmp2) composite_key_cmp;
- cmp_arg= (void*) this;
- field_lengths= (uint32*) thd->alloc(table->s->fields * sizeof(uint32));
- for (tree_key_length= 0, length= field_lengths, field= table->field;
- field < field_end; ++field, ++length)
- {
- *length= (*field)->pack_length();
- tree_key_length+= *length;
- }
- }
- }
- DBUG_ASSERT(tree == 0);
- tree= new Unique(compare_key, cmp_arg, tree_key_length,
- thd->variables.max_heap_table_size);
- /*
- The only time tree_key_length could be 0 is if someone does
- count(distinct) on a char(0) field - stupid thing to do,
- but this has to be handled - otherwise someone can crash
- the server with a DoS attack
- */
- is_evaluated= FALSE;
- if (! tree)
- return TRUE;
- }
- return FALSE;
-}
-
-
-Item *Item_sum_count_distinct::copy_or_same(THD* thd)
-{
- return new (thd->mem_root) Item_sum_count_distinct(thd, this);
-}
-
-
-void Item_sum_count_distinct::clear()
-{
- /* tree and table can be both null only if always_null */
- is_evaluated= FALSE;
- if (tree)
- {
- tree->reset();
- }
- else if (table)
- {
- table->file->extra(HA_EXTRA_NO_CACHE);
- table->file->ha_delete_all_rows();
- table->file->extra(HA_EXTRA_WRITE_CACHE);
- }
-}
-
-bool Item_sum_count_distinct::add()
-{
- int error;
- if (always_null)
- return 0;
- copy_fields(tmp_table_param);
- if (copy_funcs(tmp_table_param->items_to_copy, table->in_use))
- return TRUE;
-
- for (Field **field=table->field ; *field ; field++)
- if ((*field)->is_real_null(0))
- return 0; // Don't count NULL
-
- is_evaluated= FALSE;
- if (tree)
- {
- /*
- The first few bytes of record (at least one) are just markers
- for deleted and NULLs. We want to skip them since they will
- bloat the tree without providing any valuable info. Besides,
- key_length used to initialize the tree didn't include space for them.
- */
- return tree->unique_add(table->record[0] + table->s->null_bytes);
- }
- if ((error= table->file->ha_write_tmp_row(table->record[0])) &&
- table->file->is_fatal_error(error, HA_CHECK_DUP))
- return TRUE;
- return FALSE;
-}
-
-
-longlong Item_sum_count_distinct::val_int()
-{
- int error;
- DBUG_ASSERT(fixed == 1);
- if (!table) // Empty query
- return LL(0);
- if (tree)
- {
- if (is_evaluated)
- return count;
-
- if (tree->elements == 0)
- return (longlong) tree->elements_in_tree(); // everything fits in memory
- count= 0;
- tree->walk(table, count_distinct_walk, (void*) &count);
- is_evaluated= TRUE;
- return (longlong) count;
- }
-
- error= table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
-
- if(error)
- {
- table->file->print_error(error, MYF(0));
- }
-
- return table->file->stats.records;
-}
-
-
-/****************************************************************************
** Functions to handle dynamic loadable aggregates
** Original source by: Alexis Mikhailov <root@medinf.chuvashia.su>
** Adapted for UDAs by: Andreas F. Bobak <bobak@relog.ch>.
@@ -2864,11 +2971,11 @@ String *Item_sum_udf_str::val_str(String *str)
@retval 1 : key1 > key2
*/
+extern "C"
int group_concat_key_cmp_with_distinct(void* arg, const void* key1,
const void* key2)
{
Item_func_group_concat *item_func= (Item_func_group_concat*)arg;
- TABLE *table= item_func->table;
for (uint i= 0; i < item_func->arg_count_field; i++)
{
@@ -2889,7 +2996,8 @@ int group_concat_key_cmp_with_distinct(void* arg, const void* key1,
if (!field)
continue;
- uint offset= field->offset(field->table->record[0])-table->s->null_bytes;
+ uint offset= (field->offset(field->table->record[0]) -
+ field->table->s->null_bytes);
int res= field->cmp((uchar*)key1 + offset, (uchar*)key2 + offset);
if (res)
return res;
@@ -2902,18 +3010,24 @@ int group_concat_key_cmp_with_distinct(void* arg, const void* key1,
function of sort for syntax: GROUP_CONCAT(expr,... ORDER BY col,... )
*/
+extern "C"
int group_concat_key_cmp_with_order(void* arg, const void* key1,
const void* key2)
{
Item_func_group_concat* grp_item= (Item_func_group_concat*) arg;
ORDER **order_item, **end;
- TABLE *table= grp_item->table;
for (order_item= grp_item->order, end=order_item+ grp_item->arg_count_order;
order_item < end;
order_item++)
{
Item *item= *(*order_item)->item;
+ /*
+ If field_item is a const item then either get_tmp_table_field returns 0
+ or it is an item over a const table.
+ */
+ if (item->const_item())
+ continue;
/*
If item is a const item then either get_tmp_table_field returns 0
or it is an item over a const table.
@@ -2924,13 +3038,17 @@ int group_concat_key_cmp_with_order(void* arg, const void* key1,
We have to use get_tmp_table_field() instead of
real_item()->get_tmp_table_field() because we want the field in
the temporary table, not the original field
- */
+
+ Note that for the case of ROLLUP, field may point to another table
+ tham grp_item->table. This is however ok as the table definitions are
+ the same.
+ */
Field *field= item->get_tmp_table_field();
if (!field)
continue;
uint offset= (field->offset(field->table->record[0]) -
- table->s->null_bytes);
+ field->table->s->null_bytes);
int res= field->cmp((uchar*)key1 + offset, (uchar*)key2 + offset);
if (res)
return (*order_item)->asc ? res : -res;
@@ -2948,13 +3066,17 @@ int group_concat_key_cmp_with_order(void* arg, const void* key1,
Append data from current leaf to item->result.
*/
-int dump_leaf_key(uchar* key, element_count count __attribute__((unused)),
- Item_func_group_concat *item)
+extern "C"
+int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)),
+ void* item_arg)
{
+ Item_func_group_concat *item= (Item_func_group_concat *) item_arg;
TABLE *table= item->table;
+ uint max_length= table->in_use->variables.group_concat_max_len;
String tmp((char *)table->record[1], table->s->reclength,
default_charset_info);
String tmp2;
+ uchar *key= (uchar *) key_arg;
String *result= &item->result;
Item **arg= item->args, **arg_end= item->args + item->arg_count_field;
uint old_length= result->length();
@@ -2995,8 +3117,10 @@ int dump_leaf_key(uchar* key, element_count count __attribute__((unused)),
result->append(*res);
}
+ item->row_count++;
+
/* stop if length of result more than max_length */
- if (result->length() > item->max_length)
+ if (result->length() > max_length)
{
int well_formed_error;
CHARSET_INFO *cs= item->collation.collation;
@@ -3009,12 +3133,15 @@ int dump_leaf_key(uchar* key, element_count count __attribute__((unused)),
*/
add_length= cs->cset->well_formed_len(cs,
ptr + old_length,
- ptr + item->max_length,
+ ptr + max_length,
result->length(),
&well_formed_error);
result->length(old_length + add_length);
- item->count_cut_values++;
item->warning_for_row= TRUE;
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_CUT_VALUE_GROUP_CONCAT, ER(ER_CUT_VALUE_GROUP_CONCAT),
+ item->row_count);
+
return 1;
}
return 0;
@@ -3035,12 +3162,12 @@ Item_func_group_concat(Name_resolution_context *context_arg,
bool distinct_arg, List<Item> *select_list,
const SQL_I_List<ORDER> &order_list,
String *separator_arg)
- :tmp_table_param(0), warning(0),
- separator(separator_arg), tree(0), unique_filter(NULL), table(0),
+ :tmp_table_param(0), separator(separator_arg), tree(0),
+ unique_filter(NULL), table(0),
order(0), context(context_arg),
arg_count_order(order_list.elements),
arg_count_field(select_list->elements),
- count_cut_values(0),
+ row_count(0),
distinct(distinct_arg),
warning_for_row(FALSE),
force_copy_fields(0), original(0)
@@ -3054,19 +3181,13 @@ Item_func_group_concat(Name_resolution_context *context_arg,
/*
We need to allocate:
args - arg_count_field+arg_count_order
- (for possible order items in temporare tables)
+ (for possible order items in temporary tables)
order - arg_count_order
*/
- if (!(args= (Item**) sql_alloc(sizeof(Item*) * arg_count +
+ if (!(args= (Item**) sql_alloc(sizeof(Item*) * arg_count * 2 +
sizeof(ORDER*)*arg_count_order)))
return;
- if (!(orig_args= (Item **) sql_alloc(sizeof(Item *) * arg_count)))
- {
- args= NULL;
- return;
- }
-
order= (ORDER**)(args + arg_count);
/* fill args items of show and sort */
@@ -3087,6 +3208,9 @@ Item_func_group_concat(Name_resolution_context *context_arg,
order_item->item= arg_ptr++;
}
}
+
+ /* orig_args is only used for print() */
+ orig_args= (Item**) (order + arg_count_order);
memcpy(orig_args, args, sizeof(Item*) * arg_count);
}
@@ -3095,7 +3219,6 @@ Item_func_group_concat::Item_func_group_concat(THD *thd,
Item_func_group_concat *item)
:Item_sum(thd, item),
tmp_table_param(item->tmp_table_param),
- warning(item->warning),
separator(item->separator),
tree(item->tree),
unique_filter(item->unique_filter),
@@ -3103,7 +3226,7 @@ Item_func_group_concat::Item_func_group_concat(THD *thd,
context(item->context),
arg_count_order(item->arg_count_order),
arg_count_field(item->arg_count_field),
- count_cut_values(item->count_cut_values),
+ row_count(item->row_count),
distinct(item->distinct),
warning_for_row(item->warning_for_row),
always_null(item->always_null),
@@ -3122,7 +3245,7 @@ Item_func_group_concat::Item_func_group_concat(THD *thd,
*/
ORDER *tmp;
if (!(tmp= (ORDER *) thd->alloc(sizeof(ORDER *) * arg_count_order +
- sizeof(ORDER) * arg_count_order)))
+ sizeof(ORDER) * arg_count_order)))
return;
order= (ORDER **)(tmp + arg_count_order);
for (uint i= 0; i < arg_count_order; i++, tmp++)
@@ -3145,15 +3268,6 @@ void Item_func_group_concat::cleanup()
DBUG_ENTER("Item_func_group_concat::cleanup");
Item_sum::cleanup();
- /* Adjust warning message to include total number of cut values */
- if (warning)
- {
- char warn_buff[MYSQL_ERRMSG_SIZE];
- sprintf(warn_buff, ER(ER_CUT_VALUE_GROUP_CONCAT), count_cut_values);
- warning->set_msg(current_thd, warn_buff);
- warning= 0;
- }
-
/*
Free table and tree if they belong to this item (if item have not pointer
to original item from which was made copy => it own its objects )
@@ -3177,20 +3291,31 @@ void Item_func_group_concat::cleanup()
delete unique_filter;
unique_filter= NULL;
}
- if (warning)
- {
- char warn_buff[MYSQL_ERRMSG_SIZE];
- sprintf(warn_buff, ER(ER_CUT_VALUE_GROUP_CONCAT), count_cut_values);
- warning->set_msg(thd, warn_buff);
- warning= 0;
- }
}
- DBUG_ASSERT(tree == 0 && warning == 0);
+ DBUG_ASSERT(tree == 0);
}
+
DBUG_VOID_RETURN;
}
+Field *Item_func_group_concat::make_string_field(TABLE *table)
+{
+ 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->s, collation.collation);
+
+ if (field)
+ field->init(table);
+ return field;
+}
+
+
Item *Item_func_group_concat::copy_or_same(THD* thd)
{
return new (thd->mem_root) Item_func_group_concat(thd, this);
@@ -3246,8 +3371,12 @@ bool Item_func_group_concat::add()
TREE_ELEMENT *el= 0; // Only for safety
if (row_eligible && tree)
{
+ DBUG_EXECUTE_IF("trigger_OOM_in_gconcat_add",
+ DBUG_SET("+d,simulate_persistent_out_of_memory"););
el= tree_insert(tree, table->record[0] + table->s->null_bytes, 0,
tree->custom_arg);
+ DBUG_EXECUTE_IF("trigger_OOM_in_gconcat_add",
+ DBUG_SET("-d,simulate_persistent_out_of_memory"););
/* check if there was enough memory to insert the row */
if (!el)
return 1;
@@ -3289,17 +3418,17 @@ Item_func_group_concat::fix_fields(THD *thd, Item **ref)
with_subselect|= args[i]->with_subselect;
}
- if (agg_item_charsets(collation, func_name(),
- args,
- /* skip charset aggregation for order columns */
- arg_count - arg_count_order,
- MY_COLL_ALLOW_CONV, 1))
+ /* skip charset aggregation for order columns */
+ if (agg_item_charsets_for_string_result(collation, func_name(),
+ args, arg_count - arg_count_order))
return 1;
result.set_charset(collation.collation);
result_field= 0;
null_value= 1;
- max_length= thd->variables.group_concat_max_len;
+ max_length= thd->variables.group_concat_max_len
+ / collation.collation->mbminlen
+ * collation.collation->mbmaxlen;
uint32 offset;
if (separator->needs_conversion(separator->length(), separator->charset(),
@@ -3417,7 +3546,8 @@ bool Item_func_group_concat::setup(THD *thd)
*/
if (!(table= create_tmp_table(thd, tmp_table_param, all_fields,
(ORDER*) 0, 0, TRUE,
- (select_lex->options | thd->options),
+ (select_lex->options |
+ thd->variables.option_bits),
HA_POS_ERROR, (char*) "")))
DBUG_RETURN(TRUE);
table->file->extra(HA_EXTRA_NO_ROWS);
@@ -3448,7 +3578,7 @@ bool Item_func_group_concat::setup(THD *thd)
unique_filter= new Unique(group_concat_key_cmp_with_distinct,
(void*)this,
tree_key_length,
- thd->variables.max_heap_table_size);
+ ram_limitation(thd));
DBUG_RETURN(FALSE);
}
@@ -3473,19 +3603,7 @@ String* Item_func_group_concat::val_str(String* str)
return 0;
if (no_appended && tree)
/* Tree is used for sorting as in ORDER BY */
- tree_walk(tree, (tree_walk_action)&dump_leaf_key, (void*)this,
- left_root_right);
- if (count_cut_values && !warning)
- {
- /*
- ER_CUT_VALUE_GROUP_CONCAT needs an argument, but this gets set in
- Item_func_group_concat::cleanup().
- */
- DBUG_ASSERT(table);
- warning= push_warning(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_CUT_VALUE_GROUP_CONCAT,
- ER(ER_CUT_VALUE_GROUP_CONCAT));
- }
+ tree_walk(tree, &dump_leaf_key, this, left_root_right);
return &result;
}
diff --git a/sql/item_sum.h b/sql/item_sum.h
index ac7df6f5952..f074cc6c822 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -1,5 +1,7 @@
-/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2008, 2014, Monty Program Ab
+#ifndef ITEM_SUM_INCLUDED
+#define ITEM_SUM_INCLUDED
+/* Copyright (c) 2000, 2013 Oracle and/or its affiliates.
+ Copyright (c) 2008, 2013 Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +14,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* classes for sum functions */
@@ -23,8 +24,93 @@
#endif
#include <my_tree.h>
+#include "sql_udf.h" /* udf_handler */
+
+class Item_sum;
+class Aggregator_distinct;
+class Aggregator_simple;
+
+/**
+ The abstract base class for the Aggregator_* classes.
+ It implements the data collection functions (setup/add/clear)
+ as either pass-through to the real functionality or
+ as collectors into an Unique (for distinct) structure.
+
+ Note that update_field/reset_field are not in that
+ class, because they're simply not called when
+ GROUP BY/DISTINCT can be handled with help of index on grouped
+ fields (quick_group = 0);
+*/
-/*
+class Aggregator : public Sql_alloc
+{
+ friend class Item_sum;
+ friend class Item_sum_sum;
+ friend class Item_sum_count;
+ friend class Item_sum_avg;
+
+ /*
+ All members are protected as this class is not usable outside of an
+ Item_sum descendant.
+ */
+protected:
+ /* the aggregate function class to act on */
+ Item_sum *item_sum;
+
+public:
+ Aggregator (Item_sum *arg): item_sum(arg) {}
+ virtual ~Aggregator () {} /* Keep gcc happy */
+
+ enum Aggregator_type { SIMPLE_AGGREGATOR, DISTINCT_AGGREGATOR };
+ virtual Aggregator_type Aggrtype() = 0;
+
+ /**
+ Called before adding the first row.
+ Allocates and sets up the internal aggregation structures used,
+ e.g. the Unique instance used to calculate distinct.
+ */
+ virtual bool setup(THD *) = 0;
+
+ /**
+ Called when we need to wipe out all the data from the aggregator :
+ all the values acumulated and all the state.
+ Cleans up the internal structures and resets them to their initial state.
+ */
+ virtual void clear() = 0;
+
+ /**
+ Called when there's a new value to be aggregated.
+ Updates the internal state of the aggregator to reflect the new value.
+ */
+ virtual bool add() = 0;
+
+ /**
+ Called when there are no more data and the final value is to be retrieved.
+ Finalises the state of the aggregator, so the final result can be retrieved.
+ */
+ virtual void endup() = 0;
+
+ /** Decimal value of being-aggregated argument */
+ virtual my_decimal *arg_val_decimal(my_decimal * value) = 0;
+ /** Floating point value of being-aggregated argument */
+ virtual double arg_val_real() = 0;
+ /**
+ NULLness of being-aggregated argument.
+
+ @param use_null_value Optimization: to determine if the argument is NULL
+ we must, in the general case, call is_null() on it, which itself might
+ call val_*() on it, which might be costly. If you just have called
+ arg_val*(), you can pass use_null_value=true; this way, arg_is_null()
+ might avoid is_null() and instead do a cheap read of the Item's null_value
+ (updated by arg_val*()).
+ */
+ virtual bool arg_is_null(bool use_null_value) = 0;
+};
+
+
+class st_select_lex;
+
+/**
Class Item_sum is the base class used for special expressions that SQL calls
'set functions'. These expressions are formed with the help of aggregate
functions such as SUM, MAX, GROUP_CONCAT etc.
@@ -217,13 +303,41 @@
TODO: to catch queries where the limit is exceeded to make the
code clean here.
-*/
-
-class st_select_lex;
+*/
class Item_sum :public Item_result_field
{
+ friend class Aggregator_distinct;
+ friend class Aggregator_simple;
+
+protected:
+ /**
+ Aggregator class instance. Not set initially. Allocated only after
+ it is determined if the incoming data are already distinct.
+ */
+ Aggregator *aggr;
+
+private:
+ /**
+ Used in making ROLLUP. Set for the ROLLUP copies of the original
+ Item_sum and passed to create_tmp_field() to cause it to work
+ over the temp table buffer that is referenced by
+ Item_result_field::result_field.
+ */
+ bool force_copy_fields;
+
+ /**
+ Indicates how the aggregate function was specified by the parser :
+ 1 if it was written as AGGREGATE(DISTINCT),
+ 0 if it was AGGREGATE()
+ */
+ bool with_distinct;
+
public:
+
+ bool has_force_copy_fields() const { return force_copy_fields; }
+ bool has_with_distinct() const { return with_distinct; }
+
enum Sumfunctype
{ COUNT_FUNC, COUNT_DISTINCT_FUNC, SUM_FUNC, SUM_DISTINCT_FUNC, AVG_FUNC,
AVG_DISTINCT_FUNC, MIN_FUNC, MAX_FUNC, STD_FUNC,
@@ -264,6 +378,7 @@ protected:
steps.
*/
bool forced_const;
+ static ulonglong ram_limitation(THD *thd);
public:
@@ -271,54 +386,43 @@ public:
Item_sum() :quick_group(1), arg_count(0), forced_const(FALSE)
{
mark_as_sum_func();
+ init_aggregator();
}
Item_sum(Item *a) :quick_group(1), arg_count(1), args(tmp_args),
orig_args(tmp_orig_args), forced_const(FALSE)
{
args[0]=a;
mark_as_sum_func();
+ init_aggregator();
}
Item_sum( Item *a, Item *b ) :quick_group(1), arg_count(2), args(tmp_args),
orig_args(tmp_orig_args), forced_const(FALSE)
{
args[0]=a; args[1]=b;
mark_as_sum_func();
+ init_aggregator();
}
Item_sum(List<Item> &list);
//Copy constructor, need to perform subselects with temporary tables
Item_sum(THD *thd, Item_sum *item);
enum Type type() const { return SUM_FUNC_ITEM; }
virtual enum Sumfunctype sum_func () const=0;
-
- /*
- This method is similar to add(), but it is called when the current
- aggregation group changes. Thus it performs a combination of
- clear() and add().
- */
- inline bool reset() { clear(); return add(); };
-
- /*
- Prepare this item for evaluation of an aggregate value. This is
- called by reset() when a group changes, or, for correlated
- subqueries, between subquery executions. E.g. for COUNT(), this
- method should set count= 0;
- */
- virtual void clear()= 0;
-
- /*
- This method is called for the next row in the same group. Its
- purpose is to aggregate the new value to the previous values in
- the group (i.e. since clear() was called last time). For example,
- for COUNT(), do count++.
- */
- virtual bool add()=0;
+ /**
+ Resets the aggregate value to its default and aggregates the current
+ value of its attribute(s).
+ */
+ inline bool reset_and_add()
+ {
+ aggregator_clear();
+ return aggregator_add();
+ };
/*
Called when new group is started and results are being saved in
- a temporary table. Similar to reset(), but must also store value in
- result_field. Like reset() it is supposed to reset start value to
- default.
- This set of methods (reult_field(), reset_field, update_field()) of
+ a temporary table. Similarly to reset_and_add() it resets the
+ value to its default and aggregates the value of its
+ attribute(s), but must also store it in result_field.
+ This set of methods (result_item(), reset_field, update_field()) of
Item_sum is used only if quick_group is not null. Otherwise
copy_or_same() is used to obtain a copy of this item.
*/
@@ -344,11 +448,6 @@ public:
*/
table_map used_tables() const { return used_tables_cache; }
void update_used_tables ();
- void cleanup()
- {
- Item::cleanup();
- forced_const= FALSE;
- }
bool is_null() { return null_value; }
void make_const ()
{
@@ -360,7 +459,9 @@ public:
virtual void print(String *str, enum_query_type query_type);
void fix_num_length_and_dec();
- /*
+ /**
+ Mark an aggregate as having no rows.
+
This function is called by the execution engine to assign 'NO ROWS
FOUND' value to an aggregate item, when the underlying result set
has no rows. Such value, in a general case, may be different from
@@ -368,10 +469,14 @@ public:
may be initialized to 0 by clear() and to NULL by
no_rows_in_result().
*/
- void no_rows_in_result() { clear(); }
-
- virtual bool setup(THD* thd) {return 0;}
- virtual void make_unique() {}
+ virtual void no_rows_in_result()
+ {
+ set_aggregator(with_distinct ?
+ Aggregator::DISTINCT_AGGREGATOR :
+ Aggregator::SIMPLE_AGGREGATOR);
+ aggregator_clear();
+ }
+ virtual void make_unique() { force_copy_fields= TRUE; }
Item *get_tmp_table_item(THD *thd);
virtual Field *create_tmp_field(bool group, TABLE *table,
uint convert_blob_length);
@@ -383,9 +488,56 @@ public:
st_select_lex *depended_from()
{ return (nest_level == aggr_level ? 0 : aggr_sel); }
- Item *get_arg(int i) { return args[i]; }
- Item *set_arg(int i, THD *thd, Item *new_val);
- uint get_arg_count() { return arg_count; }
+ Item *get_arg(uint i) { return args[i]; }
+ Item *set_arg(uint i, THD *thd, Item *new_val);
+ uint get_arg_count() const { return arg_count; }
+
+ /* Initialization of distinct related members */
+ void init_aggregator()
+ {
+ aggr= NULL;
+ with_distinct= FALSE;
+ force_copy_fields= FALSE;
+ }
+
+ /**
+ Called to initialize the aggregator.
+ */
+
+ inline bool aggregator_setup(THD *thd) { return aggr->setup(thd); };
+
+ /**
+ Called to cleanup the aggregator.
+ */
+
+ inline void aggregator_clear() { aggr->clear(); }
+
+ /**
+ Called to add value to the aggregator.
+ */
+
+ inline bool aggregator_add() { return aggr->add(); };
+
+ /* stores the declared DISTINCT flag (from the parser) */
+ void set_distinct(bool distinct)
+ {
+ with_distinct= distinct;
+ quick_group= with_distinct ? 0 : 1;
+ }
+
+ /*
+ Set the type of aggregation : DISTINCT or not.
+
+ May be called multiple times.
+ */
+
+ int set_aggregator(Aggregator::Aggregator_type aggregator);
+
+ virtual void clear()= 0;
+ virtual bool add()= 0;
+ virtual bool setup(THD *thd) { return false; }
+
+ virtual void cleanup();
bool check_vcol_func_processor(uchar *int_arg)
{
return trace_unsupported_by_check_vcol_func_processor(func_name());
@@ -393,6 +545,136 @@ public:
};
+class Unique;
+
+
+/**
+ The distinct aggregator.
+ Implements AGGFN (DISTINCT ..)
+ Collects all the data into an Unique (similarly to what Item_sum_distinct
+ does currently) and then (if applicable) iterates over the list of
+ unique values and pumps them back into its object
+*/
+
+class Aggregator_distinct : public Aggregator
+{
+ friend class Item_sum_sum;
+
+ /*
+ flag to prevent consecutive runs of endup(). Normally in endup there are
+ expensive calculations (like walking the distinct tree for example)
+ which we must do only once if there are no data changes.
+ We can re-use the data for the second and subsequent val_xxx() calls.
+ endup_done set to TRUE also means that the calculated values for
+ the aggregate functions are correct and don't need recalculation.
+ */
+ bool endup_done;
+
+ /*
+ Used depending on the type of the aggregate function and the presence of
+ blob columns in it:
+ - For COUNT(DISTINCT) and no blob fields this points to a real temporary
+ table. It's used as a hash table.
+ - For AVG/SUM(DISTINCT) or COUNT(DISTINCT) with blob fields only the
+ in-memory data structure of a temporary table is constructed.
+ It's used by the Field classes to transform data into row format.
+ */
+ TABLE *table;
+
+ /*
+ An array of field lengths on row allocated and used only for
+ COUNT(DISTINCT) with multiple columns and no blobs. Used in
+ Aggregator_distinct::composite_key_cmp (called from Unique to compare
+ nodes
+ */
+ uint32 *field_lengths;
+
+ /*
+ Used in conjunction with 'table' to support the access to Field classes
+ for COUNT(DISTINCT). Needed by copy_fields()/copy_funcs().
+ */
+ TMP_TABLE_PARAM *tmp_table_param;
+
+ /*
+ If there are no blobs in the COUNT(DISTINCT) arguments, we can use a tree,
+ which is faster than heap table. In that case, we still use the table
+ to help get things set up, but we insert nothing in it.
+ For AVG/SUM(DISTINCT) we always use this tree (as it takes a single
+ argument) to get the distinct rows.
+ */
+ Unique *tree;
+
+ /*
+ The length of the temp table row. Must be a member of the class as it
+ gets passed down to simple_raw_key_cmp () as a compare function argument
+ to Unique. simple_raw_key_cmp () is used as a fast comparison function
+ when the entire row can be binary compared.
+ */
+ uint tree_key_length;
+
+ /*
+ Set to true if the result is known to be always NULL.
+ If set deactivates creation and usage of the temporary table (in the
+ 'table' member) and the Unique instance (in the 'tree' member) as well as
+ the calculation of the final value on the first call to
+ Item_[sum|avg|count]::val_xxx().
+ */
+ bool always_null;
+
+ /**
+ When feeding back the data in endup() from Unique/temp table back to
+ Item_sum::add() methods we must read the data from Unique (and not
+ recalculate the functions that are given as arguments to the aggregate
+ function.
+ This flag is to tell the arg_*() methods to take the data from the Unique
+ instead of calling the relevant val_..() method.
+ */
+ bool use_distinct_values;
+
+public:
+ Aggregator_distinct (Item_sum *sum) :
+ Aggregator(sum), table(NULL), tmp_table_param(NULL), tree(NULL),
+ always_null(false), use_distinct_values(false) {}
+ virtual ~Aggregator_distinct ();
+ Aggregator_type Aggrtype() { return DISTINCT_AGGREGATOR; }
+
+ bool setup(THD *);
+ void clear();
+ bool add();
+ void endup();
+ virtual my_decimal *arg_val_decimal(my_decimal * value);
+ virtual double arg_val_real();
+ virtual bool arg_is_null(bool use_null_value);
+
+ bool unique_walk_function(void *element);
+ bool unique_walk_function_for_count(void *element);
+ static int composite_key_cmp(void* arg, uchar* key1, uchar* key2);
+};
+
+
+/**
+ The pass-through aggregator.
+ Implements AGGFN (DISTINCT ..) by knowing it gets distinct data on input.
+ So it just pumps them back to the Item_sum descendant class.
+*/
+class Aggregator_simple : public Aggregator
+{
+public:
+
+ Aggregator_simple (Item_sum *sum) :
+ Aggregator(sum) {}
+ Aggregator_type Aggrtype() { return Aggregator::SIMPLE_AGGREGATOR; }
+
+ bool setup(THD * thd) { return item_sum->setup(thd); }
+ void clear() { item_sum->clear(); }
+ bool add() { return item_sum->add(); }
+ void endup() {};
+ virtual my_decimal *arg_val_decimal(my_decimal * value);
+ virtual double arg_val_real();
+ virtual bool arg_is_null(bool use_null_value);
+};
+
+
class Item_sum_num :public Item_sum
{
protected:
@@ -449,9 +731,15 @@ protected:
void fix_length_and_dec();
public:
- Item_sum_sum(Item *item_par) :Item_sum_num(item_par) {}
+ Item_sum_sum(Item *item_par, bool distinct) :Item_sum_num(item_par)
+ {
+ set_distinct(distinct);
+ }
Item_sum_sum(THD *thd, Item_sum_sum *item);
- enum Sumfunctype sum_func () const {return SUM_FUNC;}
+ enum Sumfunctype sum_func () const
+ {
+ return has_with_distinct() ? SUM_DISTINCT_FUNC : SUM_FUNC;
+ }
void clear();
bool add();
double val_real();
@@ -462,109 +750,50 @@ public:
void reset_field();
void update_field();
void no_rows_in_result() {}
- const char *func_name() const { return "sum("; }
+ const char *func_name() const
+ {
+ return has_with_distinct() ? "sum(distinct " : "sum(";
+ }
Item *copy_or_same(THD* thd);
};
-
-/* Common class for SUM(DISTINCT), AVG(DISTINCT) */
-
-class Unique;
-
-class Item_sum_distinct :public Item_sum_num
+class Item_sum_count :public Item_sum_int
{
-protected:
- /* storage for the summation result */
- ulonglong count;
- Hybrid_type val;
- /* storage for unique elements */
- Unique *tree;
- TABLE *table;
- enum enum_field_types table_field_type;
- uint tree_key_length;
-protected:
- Item_sum_distinct(THD *thd, Item_sum_distinct *item);
-public:
- Item_sum_distinct(Item *item_par);
- ~Item_sum_distinct();
+ longlong count;
+
+ friend class Aggregator_distinct;
- bool setup(THD *thd);
void clear();
- void cleanup();
bool add();
- double val_real();
- my_decimal *val_decimal(my_decimal *);
- longlong val_int();
- String *val_str(String *str);
-
- /* XXX: does it need make_unique? */
-
- enum Sumfunctype sum_func () const { return SUM_DISTINCT_FUNC; }
- void reset_field() {} // not used
- void update_field() {} // not used
- void no_rows_in_result() {}
- void fix_length_and_dec();
- enum Item_result result_type () const { return val.traits->type(); }
- virtual void calculate_val_and_count();
- virtual bool unique_walk_function(void *elem);
-};
-
-
-/*
- Item_sum_sum_distinct - implementation of SUM(DISTINCT expr).
- See also: MySQL manual, chapter 'Adding New Functions To MySQL'
- and comments in item_sum.cc.
-*/
-
-class Item_sum_sum_distinct :public Item_sum_distinct
-{
-private:
- Item_sum_sum_distinct(THD *thd, Item_sum_sum_distinct *item)
- :Item_sum_distinct(thd, item) {}
-public:
- Item_sum_sum_distinct(Item *item_arg) :Item_sum_distinct(item_arg) {}
-
- enum Sumfunctype sum_func () const { return SUM_DISTINCT_FUNC; }
- const char *func_name() const { return "sum(distinct "; }
- Item *copy_or_same(THD* thd) { return new Item_sum_sum_distinct(thd, this); }
-};
-
-
-/* Item_sum_avg_distinct - SELECT AVG(DISTINCT expr) FROM ... */
-
-class Item_sum_avg_distinct: public Item_sum_distinct
-{
-private:
- Item_sum_avg_distinct(THD *thd, Item_sum_avg_distinct *original)
- :Item_sum_distinct(thd, original) {}
-public:
- uint prec_increment;
- Item_sum_avg_distinct(Item *item_arg) : Item_sum_distinct(item_arg) {}
-
- void fix_length_and_dec();
- virtual void calculate_val_and_count();
- enum Sumfunctype sum_func () const { return AVG_DISTINCT_FUNC; }
- const char *func_name() const { return "avg(distinct "; }
- Item *copy_or_same(THD* thd) { return new Item_sum_avg_distinct(thd, this); }
-};
-
-
-class Item_sum_count :public Item_sum_int
-{
- longlong count;
+ void cleanup();
public:
Item_sum_count(Item *item_par)
:Item_sum_int(item_par),count(0)
{}
+
+ /**
+ Constructs an instance for COUNT(DISTINCT)
+
+ @param list a list of the arguments to the aggregate function
+
+ This constructor is called by the parser only for COUNT (DISTINCT).
+ */
+
+ Item_sum_count(List<Item> &list)
+ :Item_sum_int(list),count(0)
+ {
+ set_distinct(TRUE);
+ }
Item_sum_count(THD *thd, Item_sum_count *item)
:Item_sum_int(thd, item), count(item->count)
{}
- enum Sumfunctype sum_func () const { return COUNT_FUNC; }
- void clear();
+ enum Sumfunctype sum_func () const
+ {
+ return has_with_distinct() ? COUNT_DISTINCT_FUNC : COUNT_FUNC;
+ }
void no_rows_in_result() { count=0; }
- bool add();
void make_const(longlong count_arg)
{
count=count_arg;
@@ -572,76 +801,12 @@ class Item_sum_count :public Item_sum_int
}
longlong val_int();
void reset_field();
- void cleanup();
void update_field();
- const char *func_name() const { return "count("; }
- Item *copy_or_same(THD* thd);
-};
-
-
-class TMP_TABLE_PARAM;
-
-class Item_sum_count_distinct :public Item_sum_int
-{
- TABLE *table;
- uint32 *field_lengths;
- TMP_TABLE_PARAM *tmp_table_param;
- bool force_copy_fields;
- /*
- If there are no blobs, we can use a tree, which
- is faster than heap table. In that case, we still use the table
- to help get things set up, but we insert nothing in it
- */
- Unique *tree;
- /*
- Storage for the value of count between calls to val_int() so val_int()
- will not recalculate on each call. Validitiy of the value is stored in
- is_evaluated.
- */
- longlong count;
- /*
- Following is 0 normal object and pointer to original one for copy
- (to correctly free resources)
- */
- Item_sum_count_distinct *original;
- uint tree_key_length;
-
-
- bool always_null; // Set to 1 if the result is always NULL
-
-
- friend int composite_key_cmp(void* arg, uchar* key1, uchar* key2);
- friend int simple_str_key_cmp(void* arg, uchar* key1, uchar* key2);
-
-public:
- Item_sum_count_distinct(List<Item> &list)
- :Item_sum_int(list), table(0), field_lengths(0), tmp_table_param(0),
- force_copy_fields(0), tree(0), count(0),
- original(0), always_null(FALSE)
- { quick_group= 0; }
- Item_sum_count_distinct(THD *thd, Item_sum_count_distinct *item)
- :Item_sum_int(thd, item), table(item->table),
- field_lengths(item->field_lengths),
- tmp_table_param(item->tmp_table_param),
- force_copy_fields(0), tree(item->tree), count(item->count),
- original(item), tree_key_length(item->tree_key_length),
- always_null(item->always_null)
- {}
- ~Item_sum_count_distinct();
-
- void cleanup();
-
- enum Sumfunctype sum_func () const { return COUNT_DISTINCT_FUNC; }
- void clear();
- bool add();
- longlong val_int();
- void reset_field() { return ;} // Never called
- void update_field() { return ; } // Never called
- const char *func_name() const { return "count(distinct "; }
- bool setup(THD *thd);
- void make_unique();
+ const char *func_name() const
+ {
+ return has_with_distinct() ? "count(distinct " : "count(";
+ }
Item *copy_or_same(THD* thd);
- void no_rows_in_result() {}
};
@@ -685,13 +850,18 @@ public:
uint prec_increment;
uint f_precision, f_scale, dec_bin_size;
- Item_sum_avg(Item *item_par) :Item_sum_sum(item_par), count(0) {}
+ Item_sum_avg(Item *item_par, bool distinct)
+ :Item_sum_sum(item_par, distinct), count(0)
+ {}
Item_sum_avg(THD *thd, Item_sum_avg *item)
:Item_sum_sum(thd, item), count(item->count),
prec_increment(item->prec_increment) {}
void fix_length_and_dec();
- enum Sumfunctype sum_func () const {return AVG_FUNC;}
+ enum Sumfunctype sum_func () const
+ {
+ return has_with_distinct() ? AVG_DISTINCT_FUNC : AVG_FUNC;
+ }
void clear();
bool add();
double val_real();
@@ -704,7 +874,10 @@ public:
Item *result_item(Field *field)
{ return new Item_avg_field(hybrid_type, this); }
void no_rows_in_result() {}
- const char *func_name() const { return "avg("; }
+ const char *func_name() const
+ {
+ return has_with_distinct() ? "avg(distinct " : "avg(";
+ }
Item *copy_or_same(THD* thd);
Field *create_tmp_field(bool group, TABLE *table, uint convert_blob_length);
void cleanup()
@@ -1204,12 +1377,19 @@ public:
#endif /* HAVE_DLOPEN */
-class MYSQL_ERROR;
+C_MODE_START
+int group_concat_key_cmp_with_distinct(void* arg, const void* key1,
+ const void* key2);
+int group_concat_key_cmp_with_order(void* arg, const void* key1,
+ const void* key2);
+int dump_leaf_key(void* key_arg,
+ element_count count __attribute__((unused)),
+ void* item_arg);
+C_MODE_END
class Item_func_group_concat : public Item_sum
{
TMP_TABLE_PARAM *tmp_table_param;
- MYSQL_ERROR *warning;
String result;
String *separator;
TREE tree_base;
@@ -1230,7 +1410,7 @@ class Item_func_group_concat : public Item_sum
uint arg_count_order;
/** The number of selected items, aka the expr list. */
uint arg_count_field;
- uint count_cut_values;
+ uint row_count;
bool distinct;
bool warning_for_row;
bool always_null;
@@ -1246,9 +1426,9 @@ class Item_func_group_concat : public Item_sum
const void* key2);
friend int group_concat_key_cmp_with_order(void* arg, const void* key1,
const void* key2);
- friend int dump_leaf_key(uchar* key,
+ friend int dump_leaf_key(void* key_arg,
element_count count __attribute__((unused)),
- Item_func_group_concat *group_concat_item);
+ void* item_arg);
public:
Item_func_group_concat(Name_resolution_context *context_arg,
@@ -1262,9 +1442,10 @@ 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 Field *make_string_field(TABLE *table);
enum_field_types field_type() const
{
- if (max_length/collation.collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB )
+ if (too_big_for_varchar())
return MYSQL_TYPE_BLOB;
else
return MYSQL_TYPE_VARCHAR;
@@ -1307,3 +1488,5 @@ public:
virtual bool change_context_processor(uchar *cntx)
{ context= (Name_resolution_context *)cntx; return FALSE; }
};
+
+#endif /* ITEM_SUM_INCLUDED */
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index bb93fc08faa..522004e965b 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 2010, 2013, Monty Program Ab
+ Copyright (c) 2009, 2013, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,8 +13,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@@ -31,7 +30,27 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
+/*
+ It is necessary to include set_var.h instead of item.h because there
+ are dependencies on include order for set_var.h and item.h. This
+ will be resolved later.
+*/
+#include "sql_class.h" // set_var.h: THD
+#include "set_var.h"
+#include "sql_locale.h" // MY_LOCALE my_locale_en_US
+#include "strfunc.h" // check_word
+#include "sql_time.h" // make_truncated_value_warning,
+ // get_date_from_daynr,
+ // calc_weekday, calc_week,
+ // convert_month_to_period,
+ // convert_period_to_month,
+ // TIME_to_timestamp,
+ // calc_time_diff,
+ // calc_time_from_sec,
+ // get_date_time_format_str
+#include "tztime.h" // struct Time_zone
+#include "sql_class.h" // THD
#include <m_ctype.h>
#include <time.h>
@@ -87,7 +106,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
timestamp_type cached_timestamp_type,
const char **sub_pattern_end,
const char *date_time_type,
- uint fuzzy_date)
+ ulonglong fuzzy_date)
{
int weekday= 0, yearday= 0, daypart= 0;
int week_number= -1;
@@ -419,7 +438,7 @@ err:
{
char buff[128];
strmake(buff, val_begin, min(length, sizeof(buff)-1));
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WRONG_VALUE_FOR_TYPE, ER(ER_WRONG_VALUE_FOR_TYPE),
date_time_type, buff, "str_to_date");
}
@@ -431,16 +450,14 @@ err:
Create a formated date/time value in a string.
*/
-bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time,
- timestamp_type type, String *str)
+static bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time,
+ timestamp_type type, MY_LOCALE *locale, String *str)
{
char intbuff[15];
uint hours_i;
uint weekday;
ulong length;
const char *ptr, *end;
- THD *thd= current_thd;
- MY_LOCALE *locale= thd->variables.lc_time_names;
str->length(0);
@@ -576,13 +593,11 @@ bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time,
str->append(hours_i < 12 ? "AM" : "PM",2);
break;
case 'r':
- length= my_sprintf(intbuff,
- (intbuff,
- ((l_time->hour % 24) < 12) ?
+ length= sprintf(intbuff, ((l_time->hour % 24) < 12) ?
"%02d:%02d:%02d AM" : "%02d:%02d:%02d PM",
(l_time->hour+11)%12+1,
l_time->minute,
- l_time->second));
+ l_time->second);
str->append(intbuff, length);
break;
case 'S':
@@ -591,12 +606,8 @@ bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time,
str->append_with_prefill(intbuff, length, 2, '0');
break;
case 'T':
- length= my_sprintf(intbuff,
- (intbuff,
- "%02d:%02d:%02d",
- l_time->hour,
- l_time->minute,
- l_time->second));
+ length= sprintf(intbuff, "%02d:%02d:%02d",
+ l_time->hour, l_time->minute, l_time->second);
str->append(intbuff, length);
break;
case 'U':
@@ -687,6 +698,8 @@ static bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs,
{
const char *end=str+length;
uint i;
+ long msec_length= 0;
+
while (str != end && !my_isdigit(cs,*str))
str++;
@@ -696,12 +709,7 @@ static bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs,
const char *start= str;
for (value=0; str != end && my_isdigit(cs,*str) ; str++)
value= value*LL(10) + (longlong) (*str - '0');
- if (transform_msec && i == count - 1) // microseconds always last
- {
- int msec_length= 6 - (int) (str - start);
- if (msec_length > 0)
- value*= (long)log_10_int[msec_length];
- }
+ msec_length= 6 - (str - start);
values[i]= value;
while (str != end && !my_isdigit(cs,*str))
str++;
@@ -715,6 +723,10 @@ static bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs,
break;
}
}
+
+ if (transform_msec && msec_length > 0)
+ values[count - 1] *= (long) log_10_int[msec_length];
+
return (str != end);
}
@@ -758,6 +770,48 @@ longlong Item_func_to_days::val_int()
}
+longlong Item_func_to_seconds::val_int_endpoint(bool left_endp,
+ bool *incl_endp)
+{
+ DBUG_ASSERT(fixed == 1);
+ MYSQL_TIME ltime;
+ longlong seconds;
+ longlong days;
+ int dummy; /* unused */
+ if (get_arg0_date(&ltime, TIME_FUZZY_DATES))
+ {
+ /* got NULL, leave the incl_endp intact */
+ return LONGLONG_MIN;
+ }
+ seconds= ltime.hour * 3600L + ltime.minute * 60 + ltime.second;
+ seconds= ltime.neg ? -seconds : seconds;
+ days= (longlong) calc_daynr(ltime.year, ltime.month, ltime.day);
+ seconds+= days * 24L * 3600L;
+ /* Set to NULL if invalid date, but keep the value */
+ null_value= check_date(&ltime,
+ (ltime.year || ltime.month || ltime.day),
+ (TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE),
+ &dummy);
+ /*
+ Even if the evaluation return NULL, seconds is useful for pruning
+ */
+ return seconds;
+}
+
+longlong Item_func_to_seconds::val_int()
+{
+ DBUG_ASSERT(fixed == 1);
+ MYSQL_TIME ltime;
+ longlong seconds;
+ longlong days;
+ if (get_arg0_date(&ltime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE))
+ return 0;
+ seconds= ltime.hour * 3600L + ltime.minute * 60 + ltime.second;
+ seconds=ltime.neg ? -seconds : seconds;
+ days= (longlong) calc_daynr(ltime.year, ltime.month, ltime.day);
+ return seconds + days * 24L * 3600L;
+}
+
/*
Get information about this Item tree monotonicity
@@ -784,6 +838,17 @@ enum_monotonicity_info Item_func_to_days::get_monotonicity_info() const
return NON_MONOTONIC;
}
+enum_monotonicity_info Item_func_to_seconds::get_monotonicity_info() const
+{
+ if (args[0]->type() == Item::FIELD_ITEM)
+ {
+ if (args[0]->field_type() == MYSQL_TYPE_DATE ||
+ args[0]->field_type() == MYSQL_TYPE_DATETIME)
+ return MONOTONIC_STRICT_INCREASING_NOT_NULL;
+ }
+ return NON_MONOTONIC;
+}
+
longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp)
{
@@ -1158,6 +1223,30 @@ my_decimal *Item_func_unix_timestamp::decimal_op(my_decimal* buf)
}
+enum_monotonicity_info Item_func_unix_timestamp::get_monotonicity_info() const
+{
+ if (args[0]->type() == Item::FIELD_ITEM &&
+ (args[0]->field_type() == MYSQL_TYPE_TIMESTAMP))
+ return MONOTONIC_INCREASING;
+ return NON_MONOTONIC;
+}
+
+
+longlong Item_func_unix_timestamp::val_int_endpoint(bool left_endp, bool *incl_endp)
+{
+ DBUG_ASSERT(fixed == 1);
+ DBUG_ASSERT(arg_count == 1 &&
+ args[0]->type() == Item::FIELD_ITEM &&
+ args[0]->field_type() == MYSQL_TYPE_TIMESTAMP);
+ Field_timestamp *field=(Field_timestamp *)(((Item_field*)args[0])->field);
+ /* Leave the incl_endp intact */
+ ulong unused;
+ my_time_t ts= field->get_timestamp(&unused);
+ null_value= field->is_null();
+ return ts;
+}
+
+
longlong Item_func_time_to_sec::int_op()
{
DBUG_ASSERT(fixed == 1);
@@ -1188,14 +1277,15 @@ my_decimal *Item_func_time_to_sec::decimal_op(my_decimal* buf)
To make code easy, allow interval objects without separators.
*/
-bool get_interval_value(Item *args,interval_type int_type,
- String *str_value, INTERVAL *interval)
+bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval)
{
ulonglong array[5];
longlong UNINIT_VAR(value);
const char *UNINIT_VAR(str);
size_t UNINIT_VAR(length);
- CHARSET_INFO *cs=str_value->charset();
+ CHARSET_INFO *UNINIT_VAR(cs);
+ char buf[100];
+ String str_value(buf, sizeof(buf), &my_charset_bin);
bzero((char*) interval,sizeof(*interval));
if (int_type == INTERVAL_SECOND && args->decimals)
@@ -1236,11 +1326,12 @@ bool get_interval_value(Item *args,interval_type int_type,
else
{
String *res;
- if (!(res=args->val_str(str_value)))
+ if (!(res= args->val_str_ascii(&str_value)))
return (1);
/* record negative intervalls in interval->neg */
str=res->ptr();
+ cs= res->charset();
const char *end=str+res->length();
while (str != end && my_isspace(cs,*str))
str++;
@@ -1364,6 +1455,29 @@ bool get_interval_value(Item *args,interval_type int_type,
}
+void Item_temporal_func::fix_length_and_dec()
+{
+ /*
+ We set maybe_null to 1 as default as any bad argument with date or
+ time can get us to return NULL.
+ */
+ maybe_null= 1;
+ max_length= mysql_temporal_int_part_length(field_type());
+ if (decimals)
+ {
+ if (decimals == NOT_FIXED_DEC)
+ max_length+= TIME_SECOND_PART_DIGITS + 1;
+ else
+ {
+ set_if_smaller(decimals, TIME_SECOND_PART_DIGITS);
+ max_length+= decimals + 1;
+ }
+ }
+ sql_mode= current_thd->variables.sql_mode &
+ (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE);
+ collation.set(&my_charset_numeric, DERIVATION_NUMERIC, MY_REPERTOIRE_ASCII);
+}
+
String *Item_temporal_func::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
@@ -1371,13 +1485,13 @@ String *Item_temporal_func::val_str(String *str)
}
-bool Item_func_from_days::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
+bool Item_func_from_days::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
longlong value=args[0]->val_int();
- if ((null_value=args[0]->null_value))
- return 1;
+ if ((null_value= (args[0]->null_value ||
+ ((fuzzy_date & TIME_NO_ZERO_DATE) && value == 0))))
+ return true;
bzero(ltime, sizeof(MYSQL_TIME));
-
if (get_date_from_daynr((long) value, &ltime->year, &ltime->month,
&ltime->day))
return 0;
@@ -1426,7 +1540,7 @@ void Item_func_curdate_utc::store_now_in_TIME(MYSQL_TIME *now_time)
bool Item_func_curdate::get_date(MYSQL_TIME *res,
- uint fuzzy_date __attribute__((unused)))
+ ulonglong fuzzy_date __attribute__((unused)))
{
*res=ltime;
return 0;
@@ -1445,7 +1559,7 @@ bool Item_func_curtime::fix_fields(THD *thd, Item **items)
}
bool Item_func_curtime::get_date(MYSQL_TIME *res,
- uint fuzzy_date __attribute__((unused)))
+ ulonglong fuzzy_date __attribute__((unused)))
{
*res= ltime;
return 0;
@@ -1536,7 +1650,7 @@ void Item_func_now_utc::store_now_in_TIME(MYSQL_TIME *now_time)
bool Item_func_now::get_date(MYSQL_TIME *res,
- uint fuzzy_date __attribute__((unused)))
+ ulonglong fuzzy_date __attribute__((unused)))
{
*res= ltime;
return 0;
@@ -1558,13 +1672,13 @@ void Item_func_sysdate_local::store_now_in_TIME(MYSQL_TIME *now_time)
bool Item_func_sysdate_local::get_date(MYSQL_TIME *res,
- uint fuzzy_date __attribute__((unused)))
+ ulonglong fuzzy_date __attribute__((unused)))
{
store_now_in_TIME(res);
return 0;
}
-bool Item_func_sec_to_time::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
+bool Item_func_sec_to_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
DBUG_ASSERT(fixed == 1);
bool sign;
@@ -1609,6 +1723,8 @@ overflow:
void Item_func_date_format::fix_length_and_dec()
{
THD* thd= current_thd;
+ locale= thd->variables.lc_time_names;
+
/*
Must use this_item() in case it's a local SP variable
(for ->max_length and ->str_value)
@@ -1772,7 +1888,7 @@ String *Item_func_date_format::val_str(String *str)
if (!make_date_time(&date_time_format, &l_time,
is_time_format ? MYSQL_TIMESTAMP_TIME :
MYSQL_TIMESTAMP_DATE,
- str))
+ locale, str))
return str;
null_date:
@@ -1783,15 +1899,16 @@ null_date:
void Item_func_from_unixtime::fix_length_and_dec()
{
- thd= current_thd;
+ THD *thd= current_thd;
thd->time_zone_used= 1;
+ tz= thd->variables.time_zone;
decimals= args[0]->decimals;
Item_temporal_func::fix_length_and_dec();
}
bool Item_func_from_unixtime::get_date(MYSQL_TIME *ltime,
- uint fuzzy_date __attribute__((unused)))
+ ulonglong fuzzy_date __attribute__((unused)))
{
bool sign;
ulonglong sec;
@@ -1805,7 +1922,7 @@ bool Item_func_from_unixtime::get_date(MYSQL_TIME *ltime,
if (args[0]->null_value || sign || sec > TIMESTAMP_MAX_VALUE)
return (null_value= 1);
- thd->variables.time_zone->gmt_sec_to_TIME(ltime, (my_time_t)sec);
+ tz->gmt_sec_to_TIME(ltime, (my_time_t)sec);
ltime->second_part= sec_part;
@@ -1821,7 +1938,7 @@ void Item_func_convert_tz::fix_length_and_dec()
bool Item_func_convert_tz::get_date(MYSQL_TIME *ltime,
- uint fuzzy_date __attribute__((unused)))
+ ulonglong fuzzy_date __attribute__((unused)))
{
my_time_t my_time_tmp;
String str;
@@ -1829,22 +1946,19 @@ bool Item_func_convert_tz::get_date(MYSQL_TIME *ltime,
if (!from_tz_cached)
{
- from_tz= my_tz_find(thd, args[1]->val_str(&str));
+ from_tz= my_tz_find(thd, args[1]->val_str_ascii(&str));
from_tz_cached= args[1]->const_item();
}
if (!to_tz_cached)
{
- to_tz= my_tz_find(thd, args[2]->val_str(&str));
+ to_tz= my_tz_find(thd, args[2]->val_str_ascii(&str));
to_tz_cached= args[2]->const_item();
}
if (from_tz==0 || to_tz==0 ||
get_arg0_date(ltime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE))
- {
- null_value= 1;
- return 1;
- }
+ return (null_value= 1);
{
uint not_used;
@@ -1857,8 +1971,7 @@ bool Item_func_convert_tz::get_date(MYSQL_TIME *ltime,
ltime->second_part= sec_part;
}
- null_value= 0;
- return 0;
+ return (null_value= 0);
}
@@ -1925,20 +2038,19 @@ void Item_date_add_interval::fix_length_and_dec()
else
decimals= max(args[0]->temporal_precision(MYSQL_TYPE_DATETIME), interval_dec);
Item_temporal_func::fix_length_and_dec();
- value.alloc(max_length);
}
/* Here arg[1] is a Item_interval object */
-bool Item_date_add_interval::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
+bool Item_date_add_interval::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
INTERVAL interval;
if (args[0]->get_date(ltime,
cached_field_type == MYSQL_TYPE_TIME ?
TIME_TIME_ONLY : 0) ||
- get_interval_value(args[1], int_type, &value, &interval))
+ get_interval_value(args[1], int_type, &interval))
return (null_value=1);
if (ltime->time_type != MYSQL_TIMESTAMP_TIME &&
@@ -1949,9 +2061,9 @@ bool Item_date_add_interval::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
if (date_sub_interval)
interval.neg = !interval.neg;
- if ((null_value= date_add_interval(ltime, int_type, interval)))
- return 1;
- return 0;
+ if (date_add_interval(ltime, int_type, interval))
+ return (null_value=1);
+ return (null_value= 0);
}
@@ -2174,6 +2286,18 @@ String *Item_char_typecast::val_str(String *str)
String *res;
uint32 length;
+ if (cast_length != ~0U &&
+ cast_length > current_thd->variables.max_allowed_packet)
+ {
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_ALLOWED_PACKET_OVERFLOWED,
+ ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED),
+ cast_cs == &my_charset_bin ?
+ "cast_as_binary" : func_name(),
+ current_thd->variables.max_allowed_packet);
+ cast_length= current_thd->variables.max_allowed_packet;
+ }
+
if (!charset_conversion)
{
if (!(res= args[0]->val_str(str)))
@@ -2222,10 +2346,11 @@ String *Item_char_typecast::val_str(String *str)
str_value= *res; // Not malloced string
res= &str_value;
}
+ ErrConvString err(res);
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), char_type,
- res->c_ptr_safe());
+ err.ptr());
res->length((uint) length);
}
else if (cast_cs == &my_charset_bin && res->length() < cast_length)
@@ -2306,7 +2431,7 @@ void Item_char_typecast::fix_length_and_dec()
}
-bool Item_time_typecast::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
+bool Item_time_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
if (get_arg0_time(ltime))
return 1;
@@ -2325,7 +2450,7 @@ bool Item_time_typecast::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
}
-bool Item_date_typecast::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
+bool Item_date_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
if (get_arg0_date(ltime, fuzzy_date & ~TIME_TIME_ONLY))
return 1;
@@ -2337,7 +2462,7 @@ bool Item_date_typecast::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
}
-bool Item_datetime_typecast::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
+bool Item_datetime_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
if (get_arg0_date(ltime, fuzzy_date & ~TIME_TIME_ONLY))
return 1;
@@ -2363,7 +2488,7 @@ bool Item_datetime_typecast::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
0099-12-31
*/
-bool Item_func_makedate::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
+bool Item_func_makedate::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
DBUG_ASSERT(fixed == 1);
long daynr= (long) args[1]->val_int();
@@ -2435,7 +2560,7 @@ void Item_func_add_time::fix_length_and_dec()
Result: Time value or datetime value
*/
-bool Item_func_add_time::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
+bool Item_func_add_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
DBUG_ASSERT(fixed == 1);
MYSQL_TIME l_time1, l_time2;
@@ -2526,23 +2651,23 @@ void Item_func_add_time::print(String *str, enum_query_type query_type)
Result: Time value
*/
-bool Item_func_timediff::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
+bool Item_func_timediff::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
DBUG_ASSERT(fixed == 1);
longlong seconds;
long microseconds;
int l_sign= 1;
MYSQL_TIME l_time1,l_time2,l_time3;
- Lazy_string_time str(&l_time3);
+ ErrConvTime str(&l_time3);
/* the following may be true in, for example, date_add(timediff(...), ... */
if (fuzzy_date & TIME_NO_ZERO_IN_DATE)
- goto null_date;
+ return (null_value= 1);
if (args[0]->get_time(&l_time1) ||
args[1]->get_time(&l_time2) ||
l_time1.time_type != l_time2.time_type)
- goto null_date;
+ return (null_value= 1);
if (l_time1.neg != l_time2.neg)
l_sign= -l_sign;
@@ -2572,13 +2697,11 @@ bool Item_func_timediff::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
if ((fuzzy_date & TIME_NO_ZERO_DATE) && (seconds == 0) &&
(microseconds == 0))
- goto null_date;
+ return (null_value= 1);
*ltime= l_time3;
return (null_value= adjust_time_range_with_warn(ltime, decimals));
-null_date:
- return (null_value= 1);
}
/**
@@ -2587,7 +2710,7 @@ null_date:
Result: Time value
*/
-bool Item_func_maketime::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
+bool Item_func_maketime::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
DBUG_ASSERT(fixed == 1);
bool overflow= 0;
@@ -2597,12 +2720,9 @@ bool Item_func_maketime::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
ulong microsecond;
bool neg= args[2]->get_seconds(&second, &microsecond);
- if ((null_value=(args[0]->null_value ||
- args[1]->null_value ||
- args[2]->null_value ||
- minute < 0 || minute > 59 ||
- neg || second > 59)))
- return 1;
+ if (args[0]->null_value || args[1]->null_value || args[2]->null_value ||
+ minute < 0 || minute > 59 || neg || second > 59)
+ return (null_value= 1);
bzero(ltime, sizeof(*ltime));
ltime->time_type= MYSQL_TIMESTAMP_TIME;
@@ -2632,14 +2752,13 @@ bool Item_func_maketime::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
ltime->second= TIME_MAX_SECOND;
char buf[28];
char *ptr= longlong10_to_str(hour, buf, args[0]->unsigned_flag ? 10 : -10);
- int len = (int)(ptr - buf) +
- my_sprintf(ptr, (ptr, ":%02u:%02u", (uint)minute, (uint)second));
+ int len = (int)(ptr - buf) + sprintf(ptr, ":%02u:%02u", (uint)minute, (uint)second);
make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
buf, len, MYSQL_TIMESTAMP_TIME,
NullS);
}
- return 0;
+ return (null_value= 0);
}
@@ -2812,12 +2931,12 @@ void Item_func_timestamp_diff::print(String *str, enum_query_type query_type)
}
-String *Item_func_get_format::val_str(String *str)
+String *Item_func_get_format::val_str_ascii(String *str)
{
DBUG_ASSERT(fixed == 1);
const char *format_name;
KNOWN_DATE_TIME_FORMAT *format;
- String *val= args[0]->val_str(str);
+ String *val= args[0]->val_str_ascii(str);
ulong val_len;
if ((null_value= args[0]->null_value))
@@ -2836,7 +2955,7 @@ String *Item_func_get_format::val_str(String *str)
(const uchar *) format_name, val_len))
{
const char *format_str= get_date_time_format_str(format, type);
- str->set(format_str, (uint) strlen(format_str), &my_charset_bin);
+ str->set(format_str, (uint) strlen(format_str), &my_charset_numeric);
return str;
}
}
@@ -2988,7 +3107,7 @@ void Item_func_str_to_date::fix_length_and_dec()
}
-bool Item_func_str_to_date::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
+bool Item_func_str_to_date::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
DATE_TIME_FORMAT date_time_format;
char val_buff[64], format_buff[64];
@@ -2999,14 +3118,14 @@ bool Item_func_str_to_date::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
val= args[0]->val_str(&val_string, &subject_converter, internal_charset);
format= args[1]->val_str(&format_str, &format_converter, internal_charset);
if (args[0]->null_value || args[1]->null_value)
- goto null_date;
+ return (null_value=1);
date_time_format.format.str= (char*) format->ptr();
date_time_format.format.length= format->length();
if (extract_date_time(&date_time_format, val->ptr(), val->length(),
ltime, cached_timestamp_type, 0, "datetime",
fuzzy_date))
- goto null_date;
+ return (null_value=1);
if (cached_timestamp_type == MYSQL_TIMESTAMP_TIME && ltime->day)
{
/*
@@ -3017,23 +3136,15 @@ bool Item_func_str_to_date::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
ltime->hour+= ltime->day*24;
ltime->day= 0;
}
- null_value= 0;
- return 0;
-
-null_date:
- return (null_value=1);
+ return (null_value= 0);
}
-bool Item_func_last_day::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
+bool Item_func_last_day::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
if (get_arg0_date(ltime, fuzzy_date) ||
(ltime->month == 0))
- {
- null_value= 1;
- return 1;
- }
- null_value= 0;
+ return (null_value=1);
uint month_idx= ltime->month-1;
ltime->day= days_in_month[month_idx];
if ( month_idx == 1 && calc_days_in_year(ltime->year) == 366)
@@ -3041,5 +3152,5 @@ bool Item_func_last_day::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
ltime->hour= ltime->minute= ltime->second= 0;
ltime->second_part= 0;
ltime->time_type= MYSQL_TIMESTAMP_DATE;
- return 0;
+ return (null_value= 0);
}
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index 1e8d876ec94..3a03ee4b27a 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -1,3 +1,5 @@
+#ifndef ITEM_TIMEFUNC_INCLUDED
+#define ITEM_TIMEFUNC_INCLUDED
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates.
Copyright (c) 2009-2011, Monty Program Ab
@@ -12,8 +14,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
/* Function items used by mysql */
@@ -22,13 +23,25 @@
#pragma interface /* gcc class implementation */
#endif
+class MY_LOCALE;
+
enum date_time_format_types
{
TIME_ONLY= 0, TIME_MICROSECOND, DATE_ONLY, DATE_TIME, DATE_TIME_MICROSECOND
};
-bool get_interval_value(Item *args,interval_type int_type,
- String *str_value, INTERVAL *interval);
+
+static inline uint
+mysql_temporal_int_part_length(enum enum_field_types mysql_type)
+{
+ 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 get_interval_value(Item *args,interval_type int_type, INTERVAL *interval);
class Item_func_period_add :public Item_int_func
{
@@ -80,6 +93,39 @@ public:
};
+class Item_func_to_seconds :public Item_int_func
+{
+public:
+ Item_func_to_seconds(Item *a) :Item_int_func(a) {}
+ longlong val_int();
+ const char *func_name() const { return "to_seconds"; }
+ void fix_length_and_dec()
+ {
+ decimals=0;
+ max_length=6*MY_CHARSET_BIN_MB_MAXLEN;
+ maybe_null= 1;
+ }
+ enum_monotonicity_info get_monotonicity_info() const;
+ longlong val_int_endpoint(bool left_endp, bool *incl_endp);
+ bool check_partition_func_processor(uchar *bool_arg) { return FALSE;}
+
+ bool intro_version(uchar *int_arg)
+ {
+ int *input_version= (int*)int_arg;
+ /* This function was introduced in 5.5 */
+ int output_version= max(*input_version, 50500);
+ *input_version= output_version;
+ return 0;
+ }
+
+ /* Only meaningful with date part and optional time part */
+ bool check_valid_arguments_processor(uchar *int_arg)
+ {
+ return !has_date_args();
+ }
+};
+
+
class Item_func_dayofmonth :public Item_int_func
{
public:
@@ -104,7 +150,7 @@ public:
class Item_func_month :public Item_func
{
public:
- Item_func_month(Item *a) :Item_func(a) {}
+ Item_func_month(Item *a) :Item_func(a) { collation.set_numeric(); }
longlong val_int();
double val_real()
{ DBUG_ASSERT(fixed == 1); return (double) Item_func_month::val_int(); }
@@ -113,17 +159,16 @@ public:
longlong nr= val_int();
if (null_value)
return 0;
- str->set(nr, &my_charset_bin);
+ str->set(nr, collation.collation);
return str;
}
const char *func_name() const { return "month"; }
enum Item_result result_type () const { return INT_RESULT; }
void fix_length_and_dec()
{
- collation.set(&my_charset_bin);
- decimals=0;
- max_length=2*MY_CHARSET_BIN_MB_MAXLEN;
- maybe_null=1;
+ decimals= 0;
+ fix_char_length(2);
+ maybe_null=1;
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
@@ -144,6 +189,10 @@ public:
void fix_length_and_dec();
bool check_partition_func_processor(uchar *int_arg) {return TRUE;}
bool check_vcol_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_valid_arguments_processor(uchar *int_arg)
+ {
+ return !has_date_args();
+ }
};
@@ -155,9 +204,9 @@ public:
const char *func_name() const { return "dayofyear"; }
void fix_length_and_dec()
{
- decimals=0;
- max_length=3*MY_CHARSET_BIN_MB_MAXLEN;
- maybe_null=1;
+ decimals= 0;
+ fix_char_length(3);
+ maybe_null=1;
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
@@ -315,7 +364,7 @@ class Item_func_weekday :public Item_func
bool odbc_type;
public:
Item_func_weekday(Item *a,bool type_arg)
- :Item_func(a), odbc_type(type_arg) {}
+ :Item_func(a), odbc_type(type_arg) { collation.set_numeric(); }
longlong val_int();
double val_real() { DBUG_ASSERT(fixed == 1); return (double) val_int(); }
String *val_str(String *str)
@@ -331,9 +380,8 @@ public:
enum Item_result result_type () const { return INT_RESULT; }
void fix_length_and_dec()
{
- collation.set(&my_charset_bin);
- decimals=0;
- max_length=1*MY_CHARSET_BIN_MB_MAXLEN;
+ decimals= 0;
+ fix_char_length(1);
maybe_null=1;
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
@@ -389,6 +437,8 @@ public:
Item_func_unix_timestamp() :Item_func_seconds_hybrid() {}
Item_func_unix_timestamp(Item *a) :Item_func_seconds_hybrid(a) {}
const char *func_name() const { return "unix_timestamp"; }
+ enum_monotonicity_info get_monotonicity_info() const;
+ longlong val_int_endpoint(bool left_endp, bool *incl_endp);
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
/*
UNIX_TIMESTAMP() depends on the current timezone
@@ -412,7 +462,6 @@ public:
};
-
class Item_func_time_to_sec :public Item_func_seconds_hybrid
{
protected:
@@ -433,43 +482,27 @@ public:
class Item_temporal_func: public Item_func
{
+ ulonglong sql_mode;
public:
Item_temporal_func() :Item_func() {}
Item_temporal_func(Item *a) :Item_func(a) {}
Item_temporal_func(Item *a, Item *b) :Item_func(a,b) {}
Item_temporal_func(Item *a, Item *b, Item *c) :Item_func(a,b,c) {}
enum Item_result result_type () const { return STRING_RESULT; }
+ CHARSET_INFO *charset_for_protocol(void) const { return &my_charset_bin; }
enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
+ Item_result cmp_type() const { return TIME_RESULT; }
String *val_str(String *str);
longlong val_int() { return val_int_from_date(); }
double val_real() { return val_real_from_date(); }
- bool get_date(MYSQL_TIME *res, uint fuzzy_date) { DBUG_ASSERT(0); return 1; }
+ 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 *tmp_table_field(TABLE *table)
{ return tmp_table_field_from_field_type(table, 0); }
int save_in_field(Field *field, bool no_conversions)
{ return save_date_in_field(field); }
- void fix_length_and_dec()
- {
- maybe_null= true;
- max_length= mysql_temporal_int_part_length(field_type());
- if (decimals)
- {
- if (decimals == NOT_FIXED_DEC)
- max_length+= TIME_SECOND_PART_DIGITS + 1;
- else
- {
- set_if_smaller(decimals, TIME_SECOND_PART_DIGITS);
- max_length+= decimals + 1;
- }
- }
- /*
- We set maybe_null to 1 as default as any bad argument with date or
- time can get us to return NULL.
- */
- maybe_null= 1;
- }
+ void fix_length_and_dec();
};
@@ -507,7 +540,7 @@ public:
Item_timefunc::fix_length_and_dec();
maybe_null= false;
}
- bool get_date(MYSQL_TIME *res, uint fuzzy_date);
+ bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
/*
Abstract method that defines which time zone is used for conversion.
Converts time current time in my_time_t representation to broken-down
@@ -547,7 +580,7 @@ class Item_func_curdate :public Item_datefunc
public:
Item_func_curdate() :Item_datefunc() {}
void fix_length_and_dec();
- bool get_date(MYSQL_TIME *res, uint fuzzy_date);
+ bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0;
bool check_vcol_func_processor(uchar *int_arg)
{
@@ -589,7 +622,7 @@ public:
Item_temporal_func::fix_length_and_dec();
maybe_null= false;
}
- bool get_date(MYSQL_TIME *res, uint fuzzy_date);
+ bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0;
bool check_vcol_func_processor(uchar *int_arg)
{
@@ -628,10 +661,11 @@ public:
bool const_item() const { return 0; }
const char *func_name() const { return "sysdate"; }
void store_now_in_TIME(MYSQL_TIME *now_time);
- bool get_date(MYSQL_TIME *res, uint fuzzy_date);
+ bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
void update_used_tables()
{
Item_func_now::update_used_tables();
+ maybe_null= 0;
used_tables_cache|= RAND_TABLE_BIT;
}
};
@@ -642,7 +676,7 @@ class Item_func_from_days :public Item_datefunc
public:
Item_func_from_days(Item *a) :Item_datefunc(a) {}
const char *func_name() const { return "from_days"; }
- bool get_date(MYSQL_TIME *res, uint fuzzy_date);
+ bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
bool check_valid_arguments_processor(uchar *int_arg)
@@ -654,6 +688,7 @@ public:
class Item_func_date_format :public Item_str_func
{
+ MY_LOCALE *locale;
int fixed_length;
const bool is_time_format;
String value;
@@ -671,12 +706,12 @@ public:
class Item_func_from_unixtime :public Item_temporal_func
{
- THD *thd;
+ Time_zone *tz;
public:
Item_func_from_unixtime(Item *a) :Item_temporal_func(a) {}
const char *func_name() const { return "from_unixtime"; }
void fix_length_and_dec();
- bool get_date(MYSQL_TIME *res, uint fuzzy_date);
+ bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
};
@@ -709,7 +744,7 @@ class Item_func_convert_tz :public Item_temporal_func
Item_temporal_func(a, b, c), from_tz_cached(0), to_tz_cached(0) {}
const char *func_name() const { return "convert_tz"; }
void fix_length_and_dec();
- bool get_date(MYSQL_TIME *res, uint fuzzy_date);
+ bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
void cleanup();
};
@@ -718,7 +753,7 @@ class Item_func_sec_to_time :public Item_timefunc
{
public:
Item_func_sec_to_time(Item *item) :Item_timefunc(item) {}
- bool get_date(MYSQL_TIME *res, uint fuzzy_date);
+ bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
void fix_length_and_dec()
{
decimals= args[0]->decimals;
@@ -730,9 +765,7 @@ public:
class Item_date_add_interval :public Item_temporal_func
{
- String value;
enum_field_types cached_field_type;
-
public:
const interval_type int_type; // keep it public
const bool date_sub_interval; // keep it public
@@ -741,9 +774,9 @@ public:
const char *func_name() const { return "date_add_interval"; }
void fix_length_and_dec();
enum_field_types field_type() const { return cached_field_type; }
- bool get_date(MYSQL_TIME *res, uint fuzzy_date);
+ bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
bool eq(const Item *item, bool binary_cmp) const;
- virtual void print(String *str, enum_query_type query_type);
+ void print(String *str, enum_query_type query_type);
};
@@ -759,7 +792,7 @@ class Item_extract :public Item_int_func
const char *func_name() const { return "extract"; }
void fix_length_and_dec();
bool eq(const Item *item, bool binary_cmp) const;
- virtual void print(String *str, enum_query_type query_type);
+ void print(String *str, enum_query_type query_type);
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
bool check_valid_arguments_processor(uchar *int_arg)
@@ -838,7 +871,7 @@ class Item_date_typecast :public Item_temporal_typecast
public:
Item_date_typecast(Item *a) :Item_temporal_typecast(a) {}
const char *func_name() const { return "cast_as_date"; }
- bool get_date(MYSQL_TIME *ltime, uint fuzzy_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; }
};
@@ -850,7 +883,7 @@ public:
Item_time_typecast(Item *a, uint dec_arg)
:Item_temporal_typecast(a) { decimals= dec_arg; }
const char *func_name() const { return "cast_as_time"; }
- bool get_date(MYSQL_TIME *ltime, uint fuzzy_date);
+ 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; }
};
@@ -864,7 +897,7 @@ public:
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; }
- bool get_date(MYSQL_TIME *ltime, uint fuzzy_date);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
};
@@ -874,7 +907,7 @@ public:
Item_func_makedate(Item *a,Item *b) :Item_temporal_func(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, uint fuzzy_date);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
};
@@ -889,8 +922,8 @@ public:
:Item_temporal_func(a, b), is_date(type_arg) { sign= neg_arg ? -1 : 1; }
enum_field_types field_type() const { return cached_field_type; }
void fix_length_and_dec();
- bool get_date(MYSQL_TIME *ltime, uint fuzzy_date);
- virtual void print(String *str, enum_query_type query_type);
+ 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"; }
};
@@ -906,7 +939,7 @@ public:
args[1]->temporal_precision(MYSQL_TYPE_TIME));
Item_timefunc::fix_length_and_dec();
}
- bool get_date(MYSQL_TIME *ltime, uint fuzzy_date);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
};
class Item_func_maketime :public Item_timefunc
@@ -921,7 +954,7 @@ public:
Item_timefunc::fix_length_and_dec();
}
const char *func_name() const { return "maketime"; }
- bool get_date(MYSQL_TIME *ltime, uint fuzzy_date);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
};
@@ -967,20 +1000,20 @@ enum date_time_format
USA_FORMAT, JIS_FORMAT, ISO_FORMAT, EUR_FORMAT, INTERNAL_FORMAT
};
-class Item_func_get_format :public Item_str_func
+class Item_func_get_format :public Item_str_ascii_func
{
public:
const timestamp_type type; // keep it public
Item_func_get_format(timestamp_type type_arg, Item *a)
- :Item_str_func(a), type(type_arg)
+ :Item_str_ascii_func(a), type(type_arg)
{}
- String *val_str(String *str);
+ String *val_str_ascii(String *str);
const char *func_name() const { return "get_format"; }
void fix_length_and_dec()
{
maybe_null= 1;
decimals=0;
- max_length=17*MY_CHARSET_BIN_MB_MAXLEN;
+ fix_length_and_charset(17, default_charset());
}
virtual void print(String *str, enum_query_type query_type);
};
@@ -999,7 +1032,7 @@ public:
:Item_temporal_func(a, b), const_item(false),
internal_charset(NULL)
{}
- bool get_date(MYSQL_TIME *ltime, uint fuzzy_date);
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
const char *func_name() const { return "str_to_date"; }
enum_field_types field_type() const { return cached_field_type; }
void fix_length_and_dec();
@@ -1011,5 +1044,7 @@ class Item_func_last_day :public Item_datefunc
public:
Item_func_last_day(Item *a) :Item_datefunc(a) {}
const char *func_name() const { return "last_day"; }
- bool get_date(MYSQL_TIME *res, uint fuzzy_date);
+ bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
};
+
+#endif /* ITEM_TIMEFUNC_INCLUDED */
diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc
index e6c0b3ba0d2..064038a06fe 100644
--- a/sql/item_xmlfunc.cc
+++ b/sql/item_xmlfunc.cc
@@ -1,5 +1,4 @@
-/*
- Copyright (c) 2005, 2012, Oracle and/or its affiliates.
+/* Copyright (c) 2005, 2013, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,16 +11,23 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifdef __GNUC__
#pragma implementation
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
+/*
+ It is necessary to include set_var.h instead of item.h because there
+ are dependencies on include order for set_var.h and item.h. This
+ will be resolved later.
+*/
+#include "sql_class.h" // set_var.h: THD
+#include "set_var.h"
#include "my_xml.h"
#include "sp_pcontext.h"
+#include "sql_class.h" // THD
/*
TODO: future development directions:
@@ -220,6 +226,9 @@ public:
{
max_length= MAX_BLOB_WIDTH;
collation.collation= pxml->charset();
+ // To avoid premature evaluation, mark all nodeset functions as non-const.
+ used_tables_cache= RAND_TABLE_BIT;
+ const_item_cache= false;
}
const char *func_name() const { return "nodeset"; }
bool check_vcol_func_processor(uchar *int_arg)
@@ -1976,6 +1985,9 @@ static int my_xpath_parse_UnionExpr(MY_XPATH *xpath)
static int
my_xpath_parse_FilterExpr_opt_slashes_RelativeLocationPath(MY_XPATH *xpath)
{
+ Item *context= xpath->context;
+ int rc;
+
if (!my_xpath_parse_FilterExpr(xpath))
return 0;
@@ -1989,8 +2001,22 @@ my_xpath_parse_FilterExpr_opt_slashes_RelativeLocationPath(MY_XPATH *xpath)
return 0;
}
- my_xpath_parse_term(xpath, MY_XPATH_LEX_SLASH);
- return my_xpath_parse_RelativeLocationPath(xpath);
+ /*
+ The context for the next relative path is the nodeset
+ returned by FilterExpr
+ */
+ xpath->context= xpath->item;
+
+ /* treat double slash (//) as /descendant-or-self::node()/ */
+ if (my_xpath_parse_term(xpath, MY_XPATH_LEX_SLASH))
+ xpath->context= new Item_nodeset_func_descendantbyname(xpath->context,
+ "*", 1, xpath->pxml, 1);
+ rc= my_xpath_parse_RelativeLocationPath(xpath);
+
+ /* push back the context and restore the item */
+ xpath->item= xpath->context;
+ xpath->context= context;
+ return rc;
}
static int my_xpath_parse_PathExpr(MY_XPATH *xpath)
{
@@ -2578,9 +2604,11 @@ void Item_xml_str_func::fix_length_and_dec()
MY_XPATH xpath;
int rc;
+ status_var_increment(current_thd->status_var.feature_xml);
+
nodeset_func= 0;
- if (agg_arg_charsets(collation, args, arg_count, MY_COLL_CMP_CONV, 1))
+ if (agg_arg_charsets_for_comparison(collation, args, arg_count))
return;
if (collation.collation->mbminlen > 1)
diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h
index c71b0032dbb..800cf6ed760 100644
--- a/sql/item_xmlfunc.h
+++ b/sql/item_xmlfunc.h
@@ -1,4 +1,8 @@
-/* Copyright (c) 2000-2007 MySQL AB
+#ifndef ITEM_XMLFUNC_INCLUDED
+#define ITEM_XMLFUNC_INCLUDED
+
+/* Copyright (c) 2000-2007 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
@@ -11,7 +15,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* This file defines all XML functions */
@@ -65,3 +69,4 @@ public:
String *val_str(String *);
};
+#endif /* ITEM_XMLFUNC_INCLUDED */
diff --git a/sql/key.cc b/sql/key.cc
index b8edb2b3119..110b13000ed 100644
--- a/sql/key.cc
+++ b/sql/key.cc
@@ -1,6 +1,4 @@
-/*
- Copyright (c) 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,13 +11,15 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* Functions to handle keys and fields in forms */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h" // REQUIRED: by includes later
+#include "key.h" // key_rec_cmp
+#include "field.h" // Field
/*
Search after a key that starts with 'field'
@@ -335,6 +335,70 @@ bool key_cmp_if_same(TABLE *table,const uchar *key,uint idx,uint key_length)
return 0;
}
+
+/**
+ Unpack a field and append it.
+
+ @param[inout] to String to append the field contents to.
+ @param field Field to unpack.
+ @param rec Record which contains the field data.
+ @param max_length Maximum length of field to unpack
+ or 0 for unlimited.
+ @param prefix_key The field is used as a prefix key.
+*/
+
+void field_unpack(String *to, Field *field, const uchar *rec, uint max_length,
+ bool prefix_key)
+{
+ String tmp;
+ DBUG_ENTER("field_unpack");
+ if (!max_length)
+ max_length= field->pack_length();
+ if (field)
+ {
+ if (field->is_null())
+ {
+ to->append(STRING_WITH_LEN("NULL"));
+ DBUG_VOID_RETURN;
+ }
+ CHARSET_INFO *cs= field->charset();
+ field->val_str(&tmp);
+ /*
+ For BINARY(N) strip trailing zeroes to make
+ the error message nice-looking
+ */
+ if (field->binary() && field->type() == MYSQL_TYPE_STRING && tmp.length())
+ {
+ const char *tmp_end= tmp.ptr() + tmp.length();
+ while (tmp_end > tmp.ptr() && !*--tmp_end) ;
+ tmp.length(tmp_end - tmp.ptr() + 1);
+ }
+ if (cs->mbmaxlen > 1 && prefix_key)
+ {
+ /*
+ Prefix key, multi-byte charset.
+ For the columns of type CHAR(N), the above val_str()
+ call will return exactly "key_part->length" bytes,
+ 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;
+ if ((charpos= my_charpos(cs, tmp.ptr(),
+ tmp.ptr() + tmp.length(),
+ char_length)) < tmp.length())
+ tmp.length(charpos);
+ }
+ if (max_length < field->pack_length())
+ tmp.length(min(tmp.length(),max_length));
+ ErrConvString err(&tmp);
+ to->append(err.ptr());
+ }
+ else
+ to->append(STRING_WITH_LEN("???"));
+ DBUG_VOID_RETURN;
+}
+
+
/*
unpack key-fields from record to some buffer.
@@ -352,8 +416,6 @@ bool key_cmp_if_same(TABLE *table,const uchar *key,uint idx,uint key_length)
void key_unpack(String *to,TABLE *table,uint idx)
{
KEY_PART_INFO *key_part,*key_part_end;
- Field *field;
- String tmp;
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
DBUG_ENTER("key_unpack");
@@ -369,20 +431,13 @@ void key_unpack(String *to,TABLE *table,uint idx)
{
if (table->record[0][key_part->null_offset] & key_part->null_bit)
{
- to->append(STRING_WITH_LEN("NULL"));
- continue;
+ to->append(STRING_WITH_LEN("NULL"));
+ continue;
}
}
- if ((field=key_part->field))
- {
- field->val_str(&tmp);
- if (key_part->length < field->pack_length())
- tmp.length(min(tmp.length(),key_part->length));
- to->append(tmp);
- }
- else
- to->append(STRING_WITH_LEN("???"));
- }
+ field_unpack(to, key_part->field, table->record[0], key_part->length,
+ test(key_part->key_part_flag & HA_PART_KEY_SEG));
+ }
dbug_tmp_restore_column_map(table->read_set, old_map);
DBUG_VOID_RETURN;
}
diff --git a/sql/key.h b/sql/key.h
new file mode 100644
index 00000000000..f5f9a19178c
--- /dev/null
+++ b/sql/key.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef KEY_INCLUDED
+#define KEY_INCLUDED
+
+#include "my_global.h" /* uchar */
+
+class Field;
+class String;
+struct TABLE;
+typedef struct st_bitmap MY_BITMAP;
+typedef struct st_key KEY;
+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_restore(uchar *to_record, uchar *from_key, KEY *key_info,
+ uint key_length);
+bool key_cmp_if_same(TABLE *form,const uchar *key,uint index,uint key_length);
+void key_unpack(String *to,TABLE *form,uint index);
+void field_unpack(String *to, Field *field, const uchar *rec, uint max_length,
+ bool prefix_key);
+bool is_key_used(TABLE *table, uint idx, const MY_BITMAP *fields);
+int key_cmp(KEY_PART_INFO *key_part, const uchar *key, uint key_length);
+ulong key_hashnr(KEY *key_info, uint used_key_parts, const uchar *key);
+bool key_buf_cmp(KEY *key_info, uint used_key_parts,
+ const uchar *key1, const uchar *key2);
+extern "C" int key_rec_cmp(void *key_info, uchar *a, uchar *b);
+int key_tuple_cmp(KEY_PART_INFO *part, uchar *key1, uchar *key2, uint tuple_length);
+
+#endif /* KEY_INCLUDED */
diff --git a/sql/keycaches.cc b/sql/keycaches.cc
new file mode 100644
index 00000000000..26a39808c56
--- /dev/null
+++ b/sql/keycaches.cc
@@ -0,0 +1,165 @@
+/* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "keycaches.h"
+
+/****************************************************************************
+ Named list handling
+****************************************************************************/
+
+NAMED_ILIST key_caches;
+
+/**
+ ilink (intrusive list element) with a name
+*/
+class NAMED_ILINK :public ilink
+{
+public:
+ const char *name;
+ uint name_length;
+ uchar* data;
+
+ NAMED_ILINK(I_List<NAMED_ILINK> *links, const char *name_arg,
+ uint 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)
+ {
+ return length == name_length && !memcmp(name, name_cmp, length);
+ }
+ ~NAMED_ILINK()
+ {
+ my_free((void *) name);
+ }
+};
+
+uchar* find_named(I_List<NAMED_ILINK> *list, const char *name, uint length,
+ NAMED_ILINK **found)
+{
+ I_List_iterator<NAMED_ILINK> it(*list);
+ NAMED_ILINK *element;
+ while ((element= it++))
+ {
+ if (element->cmp(name, length))
+ {
+ if (found)
+ *found= element;
+ return element->data;
+ }
+ }
+ return 0;
+}
+
+
+void NAMED_ILIST::delete_elements(void (*free_element)(const char *name, uchar*))
+{
+ NAMED_ILINK *element;
+ DBUG_ENTER("NAMED_ILIST::delete_elements");
+ while ((element= get()))
+ {
+ (*free_element)(element->name, element->data);
+ delete element;
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/* Key cache functions */
+
+LEX_STRING default_key_cache_base= {C_STRING_WITH_LEN("default")};
+
+KEY_CACHE zero_key_cache; ///< @@nonexistent_cache.param->value_ptr() points here
+
+KEY_CACHE *get_key_cache(LEX_STRING *cache_name)
+{
+ if (!cache_name || ! cache_name->length)
+ cache_name= &default_key_cache_base;
+ return ((KEY_CACHE*) find_named(&key_caches,
+ cache_name->str, cache_name->length, 0));
+}
+
+KEY_CACHE *create_key_cache(const char *name, uint length)
+{
+ KEY_CACHE *key_cache;
+ DBUG_ENTER("create_key_cache");
+ DBUG_PRINT("enter",("name: %.*s", length, name));
+
+ if ((key_cache= (KEY_CACHE*) my_malloc(sizeof(KEY_CACHE),
+ MYF(MY_ZEROFILL | MY_WME))))
+ {
+ if (!new NAMED_ILINK(&key_caches, name, length, (uchar*) key_cache))
+ {
+ my_free(key_cache);
+ key_cache= 0;
+ }
+ else
+ {
+ /*
+ Set default values for a key cache
+ The values in dflt_key_cache_var is set by my_getopt() at startup
+
+ We don't set 'buff_size' as this is used to enable the key cache
+ */
+ key_cache->param_block_size= dflt_key_cache_var.param_block_size;
+ key_cache->param_division_limit= dflt_key_cache_var.param_division_limit;
+ key_cache->param_age_threshold= dflt_key_cache_var.param_age_threshold;
+ key_cache->param_partitions= dflt_key_cache_var.param_partitions;
+ }
+ }
+ DBUG_RETURN(key_cache);
+}
+
+
+KEY_CACHE *get_or_create_key_cache(const char *name, uint length)
+{
+ LEX_STRING key_cache_name;
+ KEY_CACHE *key_cache;
+
+ key_cache_name.str= (char *) name;
+ key_cache_name.length= length;
+ if (!(key_cache= get_key_cache(&key_cache_name)))
+ key_cache= create_key_cache(name, length);
+ return key_cache;
+}
+
+
+void free_key_cache(const char *name, KEY_CACHE *key_cache)
+{
+ end_key_cache(key_cache, 1); // Can never fail
+ my_free(key_cache);
+}
+
+
+bool process_key_caches(process_key_cache_t func, void *param)
+{
+ I_List_iterator<NAMED_ILINK> it(key_caches);
+ NAMED_ILINK *element;
+ int res= 0;
+
+ while ((element= it++))
+ {
+ KEY_CACHE *key_cache= (KEY_CACHE *) element->data;
+ res |= func(element->name, key_cache, param);
+ }
+ return res != 0;
+}
+
+#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
+template class I_List_iterator<NAMED_ILINK>;
+#endif
+
diff --git a/sql/keycaches.h b/sql/keycaches.h
new file mode 100644
index 00000000000..04d3f6145e7
--- /dev/null
+++ b/sql/keycaches.h
@@ -0,0 +1,45 @@
+#ifndef KEYCACHES_INCLUDED
+#define KEYCACHES_INCLUDED
+
+/* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "sql_list.h"
+#include <keycache.h>
+
+extern "C"
+{
+ typedef int (*process_key_cache_t) (const char *, KEY_CACHE *, void *);
+}
+
+class NAMED_ILINK;
+
+class NAMED_ILIST: public I_List<NAMED_ILINK>
+{
+ public:
+ void delete_elements(void (*free_element)(const char*, uchar*));
+};
+
+extern LEX_STRING 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(LEX_STRING *cache_name);
+KEY_CACHE *get_or_create_key_cache(const char *name, uint length);
+void free_key_cache(const char *name, KEY_CACHE *key_cache);
+bool process_key_caches(process_key_cache_t func, void *param);
+
+#endif /* KEYCACHES_INCLUDED */
diff --git a/sql/lex.h b/sql/lex.h
index 5d7546dcdc9..9bf4c439cb6 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -1,6 +1,7 @@
-/*
- Copyright (c) 2000-2008 MySQL AB, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+#ifndef LEX_INCLUDED
+#define LEX_INCLUDED
+
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,8 +14,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* This file includes all reserved words and functions */
@@ -100,6 +100,7 @@ static SYMBOL symbols[] = {
{ "CASCADE", SYM(CASCADE)},
{ "CASCADED", SYM(CASCADED)},
{ "CASE", SYM(CASE_SYM)},
+ { "CATALOG_NAME", SYM(CATALOG_NAME_SYM)},
{ "CHAIN", SYM(CHAIN_SYM)},
{ "CHANGE", SYM(CHANGE)},
{ "CHANGED", SYM(CHANGED)},
@@ -110,6 +111,7 @@ static SYMBOL symbols[] = {
{ "CHECKPOINT", SYM(CHECKPOINT_SYM)},
{ "CHECKSUM", SYM(CHECKSUM_SYM)},
{ "CIPHER", SYM(CIPHER_SYM)},
+ { "CLASS_ORIGIN", SYM(CLASS_ORIGIN_SYM)},
{ "CLIENT", SYM(CLIENT_SYM)},
{ "CLIENT_STATISTICS", SYM(CLIENT_STATS_SYM)},
{ "CLOSE", SYM(CLOSE_SYM)},
@@ -118,6 +120,7 @@ static SYMBOL symbols[] = {
{ "COLLATE", SYM(COLLATE_SYM)},
{ "COLLATION", SYM(COLLATION_SYM)},
{ "COLUMN", SYM(COLUMN_SYM)},
+ { "COLUMN_NAME", SYM(COLUMN_NAME_SYM)},
{ "COLUMNS", SYM(COLUMNS)},
{ "COLUMN_ADD", SYM(COLUMN_ADD_SYM)},
{ "COLUMN_CREATE", SYM(COLUMN_CREATE_SYM)},
@@ -136,6 +139,9 @@ static SYMBOL symbols[] = {
{ "CONNECTION", SYM(CONNECTION_SYM)},
{ "CONSISTENT", SYM(CONSISTENT_SYM)},
{ "CONSTRAINT", SYM(CONSTRAINT)},
+ { "CONSTRAINT_CATALOG", SYM(CONSTRAINT_CATALOG_SYM)},
+ { "CONSTRAINT_NAME", SYM(CONSTRAINT_NAME_SYM)},
+ { "CONSTRAINT_SCHEMA", SYM(CONSTRAINT_SCHEMA_SYM)},
{ "CONTAINS", SYM(CONTAINS_SYM)},
{ "CONTEXT", SYM(CONTEXT_SYM)},
{ "CONTINUE", SYM(CONTINUE_SYM)},
@@ -150,6 +156,7 @@ static SYMBOL symbols[] = {
{ "CURRENT_TIMESTAMP", SYM(NOW_SYM)},
{ "CURRENT_USER", SYM(CURRENT_USER)},
{ "CURSOR", SYM(CURSOR_SYM)},
+ { "CURSOR_NAME", SYM(CURSOR_NAME_SYM)},
{ "DATA", SYM(DATA_SYM)},
{ "DATABASE", SYM(DATABASE)},
{ "DATABASES", SYM(DATABASES)},
@@ -198,12 +205,14 @@ static SYMBOL symbols[] = {
{ "ENGINE", SYM(ENGINE_SYM)},
{ "ENGINES", SYM(ENGINES_SYM)},
{ "ENUM", SYM(ENUM)},
+ { "ERROR", SYM(ERROR_SYM)},
{ "ERRORS", SYM(ERRORS)},
{ "ESCAPE", SYM(ESCAPE_SYM)},
{ "ESCAPED", SYM(ESCAPED)},
{ "EVENT", SYM(EVENT_SYM)},
{ "EVENTS", SYM(EVENTS_SYM)},
{ "EVERY", SYM(EVERY_SYM)},
+ { "EXAMINED", SYM(EXAMINED_SYM)},
{ "EXECUTE", SYM(EXECUTE_SYM)},
{ "EXISTS", SYM(EXISTS)},
{ "EXIT", SYM(EXIT_SYM)},
@@ -227,11 +236,11 @@ static SYMBOL symbols[] = {
{ "FORCE", SYM(FORCE_SYM)},
{ "FOREIGN", SYM(FOREIGN)},
{ "FOUND", SYM(FOUND_SYM)},
- { "FRAC_SECOND", SYM(FRAC_SECOND_SYM)},
{ "FROM", SYM(FROM)},
{ "FULL", SYM(FULL)},
{ "FULLTEXT", SYM(FULLTEXT_SYM)},
{ "FUNCTION", SYM(FUNCTION_SYM)},
+ { "GENERAL", SYM(GENERAL)},
{ "GENERATED", SYM(GENERATED_SYM)},
{ "GEOMETRY", SYM(GEOMETRY_SYM)},
{ "GEOMETRYCOLLECTION",SYM(GEOMETRYCOLLECTION)},
@@ -255,6 +264,7 @@ static SYMBOL symbols[] = {
{ "IDENTIFIED", SYM(IDENTIFIED_SYM)},
{ "IF", SYM(IF)},
{ "IGNORE", SYM(IGNORE_SYM)},
+ { "IGNORE_SERVER_IDS", SYM(IGNORE_SERVER_IDS_SYM)},
{ "IMPORT", SYM(IMPORT)},
{ "IN", SYM(IN_SYM)},
{ "INDEX", SYM(INDEX_SYM)},
@@ -263,8 +273,6 @@ static SYMBOL symbols[] = {
{ "INFILE", SYM(INFILE)},
{ "INITIAL_SIZE", SYM(INITIAL_SIZE_SYM)},
{ "INNER", SYM(INNER_SYM)},
- { "INNOBASE", SYM(INNOBASE_SYM)},
- { "INNODB", SYM(INNOBASE_SYM)},
{ "INOUT", SYM(INOUT_SYM)},
{ "INSENSITIVE", SYM(INSENSITIVE_SYM)},
{ "INSERT", SYM(INSERT)},
@@ -294,6 +302,7 @@ static SYMBOL symbols[] = {
{ "KILL", SYM(KILL_SYM)},
{ "LANGUAGE", SYM(LANGUAGE_SYM)},
{ "LAST", SYM(LAST_SYM)},
+ { "LAST_VALUE", SYM(LAST_VALUE)},
{ "LEADING", SYM(LEADING)},
{ "LEAVE", SYM(LEAVE_SYM)},
{ "LEAVES", SYM(LEAVES)},
@@ -335,6 +344,7 @@ static SYMBOL symbols[] = {
{ "MASTER_SSL_KEY", SYM(MASTER_SSL_KEY_SYM)},
{ "MASTER_SSL_VERIFY_SERVER_CERT", SYM(MASTER_SSL_VERIFY_SERVER_CERT_SYM)},
{ "MASTER_USER", SYM(MASTER_USER_SYM)},
+ { "MASTER_HEARTBEAT_PERIOD", SYM(MASTER_HEARTBEAT_PERIOD_SYM)},
{ "MATCH", SYM(MATCH)},
{ "MAX_CONNECTIONS_PER_HOUR", SYM(MAX_CONNECTIONS_PER_HOUR)},
{ "MAX_QUERIES_PER_HOUR", SYM(MAX_QUERIES_PER_HOUR)},
@@ -349,6 +359,7 @@ static SYMBOL symbols[] = {
{ "MEDIUMTEXT", SYM(MEDIUMTEXT)},
{ "MEMORY", SYM(MEMORY_SYM)},
{ "MERGE", SYM(MERGE_SYM)},
+ { "MESSAGE_TEXT", SYM(MESSAGE_TEXT_SYM)},
{ "MICROSECOND", SYM(MICROSECOND_SYM)},
{ "MIDDLEINT", SYM(MEDIUMINT)}, /* For powerbuilder */
{ "MIGRATE", SYM(MIGRATE_SYM)},
@@ -365,6 +376,7 @@ static SYMBOL symbols[] = {
{ "MULTIPOINT", SYM(MULTIPOINT)},
{ "MULTIPOLYGON", SYM(MULTIPOLYGON)},
{ "MUTEX", SYM(MUTEX_SYM)},
+ { "MYSQL_ERRNO", SYM(MYSQL_ERRNO_SYM)},
{ "NAME", SYM(NAME_SYM)},
{ "NAMES", SYM(NAMES_SYM)},
{ "NATIONAL", SYM(NATIONAL_SYM)},
@@ -423,11 +435,12 @@ static SYMBOL symbols[] = {
{ "PREV", SYM(PREV_SYM)},
{ "PRIMARY", SYM(PRIMARY_SYM)},
{ "PRIVILEGES", SYM(PRIVILEGES)},
- { "PROCEDURE", SYM(PROCEDURE)},
+ { "PROCEDURE", SYM(PROCEDURE_SYM)},
{ "PROCESS" , SYM(PROCESS)},
{ "PROCESSLIST", SYM(PROCESSLIST_SYM)},
- { "PROFILE", SYM(PROFILE_SYM)},
- { "PROFILES", SYM(PROFILES_SYM)},
+ { "PROFILE", SYM(PROFILE_SYM)},
+ { "PROFILES", SYM(PROFILES_SYM)},
+ { "PROXY", SYM(PROXY_SYM)},
{ "PURGE", SYM(PURGE)},
{ "QUARTER", SYM(QUARTER_SYM)},
{ "QUERY", SYM(QUERY_SYM)},
@@ -445,6 +458,8 @@ static SYMBOL symbols[] = {
{ "REDUNDANT", SYM(REDUNDANT_SYM)},
{ "REFERENCES", SYM(REFERENCES)},
{ "REGEXP", SYM(REGEXP)},
+ { "RELAY", SYM(RELAY)},
+ { "RELAYLOG", SYM(RELAYLOG_SYM)},
{ "RELAY_LOG_FILE", SYM(RELAY_LOG_FILE_SYM)},
{ "RELAY_LOG_POS", SYM(RELAY_LOG_POS_SYM)},
{ "RELAY_THREAD", SYM(RELAY_THREAD)},
@@ -460,6 +475,7 @@ static SYMBOL symbols[] = {
{ "REPEAT", SYM(REPEAT_SYM)},
{ "REQUIRE", SYM(REQUIRE_SYM)},
{ "RESET", SYM(RESET_SYM)},
+ { "RESIGNAL", SYM(RESIGNAL_SYM)},
{ "RESTORE", SYM(RESTORE_SYM)},
{ "RESTRICT", SYM(RESTRICT)},
{ "RESUME", SYM(RESUME_SYM)},
@@ -478,6 +494,7 @@ static SYMBOL symbols[] = {
{ "SAVEPOINT", SYM(SAVEPOINT_SYM)},
{ "SCHEDULE", SYM(SCHEDULE_SYM)},
{ "SCHEMA", SYM(DATABASE)},
+ { "SCHEMA_NAME", SYM(SCHEMA_NAME_SYM)},
{ "SCHEMAS", SYM(DATABASES)},
{ "SECOND", SYM(SECOND_SYM)},
{ "SECOND_MICROSECOND", SYM(SECOND_MICROSECOND_SYM)},
@@ -493,10 +510,11 @@ static SYMBOL symbols[] = {
{ "SHARE", SYM(SHARE_SYM)},
{ "SHOW", SYM(SHOW)},
{ "SHUTDOWN", SYM(SHUTDOWN)},
+ { "SIGNAL", SYM(SIGNAL_SYM)},
{ "SIGNED", SYM(SIGNED_SYM)},
{ "SIMPLE", SYM(SIMPLE_SYM)},
{ "SLAVE", SYM(SLAVE)},
- { "SLOW", SYM(SLOW_SYM)},
+ { "SLOW", SYM(SLOW)},
{ "SNAPSHOT", SYM(SNAPSHOT_SYM)},
{ "SMALLINT", SYM(SMALLINT)},
{ "SOCKET", SYM(SOCKET_SYM)},
@@ -518,7 +536,6 @@ static SYMBOL symbols[] = {
{ "SQL_NO_CACHE", SYM(SQL_NO_CACHE_SYM)},
{ "SQL_SMALL_RESULT", SYM(SQL_SMALL_RESULT)},
{ "SQL_THREAD", SYM(SQL_THREAD)},
- { "SQL_TSI_FRAC_SECOND", SYM(FRAC_SECOND_SYM)},
{ "SQL_TSI_SECOND", SYM(SECOND_SYM)},
{ "SQL_TSI_MINUTE", SYM(MINUTE_SYM)},
{ "SQL_TSI_HOUR", SYM(HOUR_SYM)},
@@ -536,6 +553,7 @@ static SYMBOL symbols[] = {
{ "STORAGE", SYM(STORAGE_SYM)},
{ "STRAIGHT_JOIN", SYM(STRAIGHT_JOIN)},
{ "STRING", SYM(STRING_SYM)},
+ { "SUBCLASS_ORIGIN", SYM(SUBCLASS_ORIGIN_SYM)},
{ "SUBJECT", SYM(SUBJECT_SYM)},
{ "SUBPARTITION", SYM(SUBPARTITION_SYM)},
{ "SUBPARTITIONS", SYM(SUBPARTITIONS_SYM)},
@@ -544,6 +562,7 @@ static SYMBOL symbols[] = {
{ "SWAPS", SYM(SWAPS_SYM)},
{ "SWITCHES", SYM(SWITCHES_SYM)},
{ "TABLE", SYM(TABLE_SYM)},
+ { "TABLE_NAME", SYM(TABLE_NAME_SYM)},
{ "TABLES", SYM(TABLES)},
{ "TABLESPACE", SYM(TABLESPACE)},
{ "TABLE_STATISTICS", SYM(TABLE_STATS_SYM)},
@@ -619,6 +638,7 @@ static SYMBOL symbols[] = {
{ "X509", SYM(X509_SYM)},
{ "XOR", SYM(XOR)},
{ "XA", SYM(XA_SYM)},
+ { "XML", SYM(XML_SYM)}, /* LOAD XML Arnold/Erik */
{ "YEAR", SYM(YEAR_SYM)},
{ "YEAR_MONTH", SYM(YEAR_MONTH_SYM)},
{ "ZEROFILL", SYM(ZEROFILL)},
@@ -660,3 +680,5 @@ static SYMBOL sql_functions[] = {
{ "VAR_POP", SYM(VARIANCE_SYM)},
{ "VAR_SAMP", SYM(VAR_SAMP_SYM)},
};
+
+#endif /* LEX_INCLUDED */
diff --git a/sql/lex_symbol.h b/sql/lex_symbol.h
index 71247222727..d48ca57df85 100644
--- a/sql/lex_symbol.h
+++ b/sql/lex_symbol.h
@@ -1,4 +1,5 @@
/* Copyright (c) 2000, 2001, 2004, 2006, 2007 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
diff --git a/sql/lock.cc b/sql/lock.cc
index ba6bac22945..31ef1314f5a 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2000, 2010, Oracle and/or its affiliates.
+ Copyright (c) 2000, 2011, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +12,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@@ -75,7 +74,13 @@
we are forced to use mysql_lock_merge.
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "debug_sync.h"
+#include "unireg.h" // REQUIRED: for other includes
+#include "lock.h"
+#include "sql_base.h" // close_tables_for_reopen
+#include "sql_parse.h" // is_log_table_write_query
+#include "sql_acl.h" // SUPER_ACL
#include <hash.h>
#include <assert.h>
@@ -90,10 +95,9 @@ static int lock_external(THD *thd, TABLE **table,uint count);
static int unlock_external(THD *thd, TABLE **table,uint count);
static void print_lock_error(int error, const char *);
-
/* Map the return value of thr_lock to an error from errmsg.txt */
static int thr_lock_errno_to_mysql[]=
-{ 0, 1, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK };
+{ 0, ER_LOCK_ABORTED, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK };
/**
Perform semantic checks for mysql_lock_tables.
@@ -104,17 +108,18 @@ static int thr_lock_errno_to_mysql[]=
@return 0 if all the check passed, non zero if a check failed.
*/
-int mysql_lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags)
+static int
+lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags)
{
- bool log_table_write_query;
- uint system_count;
- uint i;
+ uint system_count, i;
+ bool is_superuser, log_table_write_query;
- DBUG_ENTER("mysql_lock_tables_check");
+ DBUG_ENTER("lock_tables_check");
system_count= 0;
+ is_superuser= thd->security_ctx->master_access & SUPER_ACL;
log_table_write_query= (is_log_table_write_query(thd->lex->sql_command)
- || ((flags & MYSQL_LOCK_PERF_SCHEMA) != 0));
+ || ((flags & MYSQL_LOCK_LOG_TABLE) != 0));
for (i=0 ; i<count; i++)
{
@@ -145,10 +150,49 @@ int mysql_lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags)
}
}
- if ((t->s->table_category == TABLE_CATEGORY_SYSTEM) &&
- (t->reginfo.lock_type >= TL_WRITE_ALLOW_WRITE))
+ if (t->reginfo.lock_type >= TL_WRITE_ALLOW_WRITE)
{
- system_count++;
+ if (t->s->table_category == TABLE_CATEGORY_SYSTEM)
+ system_count++;
+
+ if (t->db_stat & HA_READ_ONLY)
+ {
+ my_error(ER_OPEN_AS_READONLY, MYF(0), t->alias.c_ptr_safe());
+ DBUG_RETURN(1);
+ }
+ }
+
+ /*
+ If we are going to lock a non-temporary table we must own metadata
+ lock of appropriate type on it (I.e. for table to be locked for
+ write we must own metadata lock of MDL_SHARED_WRITE or stronger
+ type. For table to be locked for read we must own metadata lock
+ of MDL_SHARED_READ or stronger type).
+ The only exception are HANDLER statements which are allowed to
+ lock table for read while having only MDL_SHARED lock on it.
+ */
+ DBUG_ASSERT(t->s->tmp_table ||
+ thd->mdl_context.is_lock_owner(MDL_key::TABLE,
+ t->s->db.str, t->s->table_name.str,
+ t->reginfo.lock_type >= TL_WRITE_ALLOW_WRITE ?
+ MDL_SHARED_WRITE : MDL_SHARED_READ) ||
+ (t->open_by_handler &&
+ thd->mdl_context.is_lock_owner(MDL_key::TABLE,
+ t->s->db.str, t->s->table_name.str,
+ MDL_SHARED)));
+
+ /*
+ Prevent modifications to base tables if READ_ONLY is activated.
+ In any case, read only does not apply to temporary tables.
+ */
+ if (!(flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY) && !t->s->tmp_table)
+ {
+ if (t->reginfo.lock_type >= TL_WRITE_ALLOW_WRITE &&
+ !is_superuser && opt_readonly && !thd->slave_thread)
+ {
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
+ DBUG_RETURN(1);
+ }
}
}
@@ -166,218 +210,133 @@ int mysql_lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags)
DBUG_RETURN(0);
}
+/**
+ Reset lock type in lock data
-/*
- Lock tables.
-
- SYNOPSIS
- mysql_lock_tables()
- thd The current thread.
- tables An array of pointers to the tables to lock.
- count The number of tables to lock.
- flags Options:
- MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK Ignore a global read lock
- MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY Ignore SET GLOBAL READ_ONLY
- MYSQL_LOCK_IGNORE_FLUSH Ignore a flush tables.
- MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN Instead of reopening altered
- or dropped tables by itself,
- mysql_lock_tables() should
- notify upper level and rely
- on caller doing this.
- need_reopen Out parameter, TRUE if some tables were altered
- or deleted and should be reopened by caller.
-
- RETURN
- A lock structure pointer on success.
- NULL on error or if some tables should be reopen.
+ @param mysql_lock Lock structures to reset.
+ @param unlock If set, then set lock type to TL_UNLOCK,
+ otherwise set to original lock type from
+ get_store_lock().
+
+ @note After a locking error we want to quit the locking of the table(s).
+ The test case in the bug report for Bug #18544 has the following
+ cases: 1. Locking error in lock_external() due to InnoDB timeout.
+ 2. Locking error in get_lock_data() due to missing write permission.
+ 3. Locking error in wait_if_global_read_lock() due to lock conflict.
+
+ @note In all these cases we have already set the lock type into the lock
+ data of the open table(s). If the table(s) are in the open table
+ cache, they could be reused with the non-zero lock type set. This
+ could lead to ignoring a different lock type with the next lock.
+
+ @note Clear the lock type of all lock data. This ensures that the next
+ lock request will set its lock type properly.
*/
-MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
- uint flags, bool *need_reopen)
+
+void reset_lock_data(MYSQL_LOCK *sql_lock, bool unlock)
+{
+ THR_LOCK_DATA **ldata, **ldata_end;
+ DBUG_ENTER("reset_lock_data");
+
+ /* Clear the lock type of all lock data to avoid reusage. */
+ for (ldata= sql_lock->locks, ldata_end= ldata + sql_lock->lock_count;
+ ldata < ldata_end;
+ ldata++)
+ (*ldata)->type= unlock ? TL_UNLOCK : (*ldata)->org_type;
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Lock tables.
+
+ @param thd The current thread.
+ @param tables An array of pointers to the tables to lock.
+ @param count The number of tables to lock.
+ @param flags Options:
+ MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY Ignore SET GLOBAL READ_ONLY
+ MYSQL_LOCK_IGNORE_TIMEOUT Use maximum timeout value.
+
+ @retval A lock structure pointer on success.
+ @retval NULL if an error or if wait on a lock was killed.
+*/
+
+MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags)
{
MYSQL_LOCK *sql_lock;
- TABLE *write_lock_used;
- int rc;
DBUG_ENTER("mysql_lock_tables(tables)");
- *need_reopen= FALSE;
+ if (lock_tables_check(thd, tables, count, flags))
+ DBUG_RETURN(NULL);
- if (mysql_lock_tables_check(thd, tables, count, flags))
- DBUG_RETURN (NULL);
+ if (! (sql_lock= get_lock_data(thd, tables, count, GET_LOCK_STORE_LOCKS)))
+ DBUG_RETURN(NULL);
- for (;;)
+ if (mysql_lock_tables(thd, sql_lock, flags))
{
- if (!(sql_lock= get_lock_data(thd, tables, count, GET_LOCK_STORE_LOCKS,
- &write_lock_used)) ||
- !sql_lock->table_count)
- break;
- rc= mysql_lock_tables(thd, sql_lock, write_lock_used != 0, flags,
- need_reopen);
- if (!rc)
- break; // Got lock
- my_free(sql_lock, MYF(0));
- if (rc > 0)
- DBUG_RETURN(0); // Failed
+ /* Clear the lock type of all lock data to avoid reusage. */
+ reset_lock_data(sql_lock, 1);
+ my_free(sql_lock);
+ sql_lock= 0;
}
DBUG_RETURN(sql_lock);
}
/**
- Lock a table based on a MYSQL_LOCK structure.
+ Lock tables based on a MYSQL_LOCK structure.
mysql_lock_tables()
@param thd The current thread.
@param sql_lock Tables that should be locked
- @param write_lock_used 1 if any of the tables are write locked
- @param flags See mysql_lock_tables()
- @param need_reopen Out parameter, TRUE if some tables were altered
- or deleted and should be reopened by caller.
-
- @return 0 ok
- @return 1 fatal error
- @return -1 retry
+ @param flags See mysql_lock_tables() above
+
+ @return 0 ok
+ @return 1 error
*/
-int mysql_lock_tables(THD *thd, MYSQL_LOCK *sql_lock,
- bool write_lock_used,
- uint flags, bool *need_reopen)
+bool mysql_lock_tables(THD *thd, MYSQL_LOCK *sql_lock, uint flags)
{
- int res= 0;
- int rc;
- DBUG_ENTER("mysql_lock_tables(sql_lock)");
- *need_reopen= FALSE;
-
- if (write_lock_used && !(flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK))
- {
- if (global_read_lock)
- {
- /*
- Someone has issued LOCK ALL TABLES FOR READ and we want a write lock
- Wait until the lock is gone
- */
- if (wait_if_global_read_lock(thd, 1, 1))
- {
- /* Clear the lock type of all lock data to avoid reusage. */
- reset_lock_data(sql_lock, 1);
- DBUG_RETURN(1); // Fatal error
- }
- if (thd->version != refresh_version)
- {
- /* Clear the lock type of all lock data to avoid reusage. */
- reset_lock_data(sql_lock, 1);
- goto retry;
- }
- }
+ int rc= 1;
+ ulong timeout= (flags & MYSQL_LOCK_IGNORE_TIMEOUT) ?
+ LONG_TIMEOUT : thd->variables.lock_wait_timeout;
- if (opt_readonly &&
- !(thd->security_ctx->master_access & SUPER_ACL) &&
- !thd->slave_thread)
- {
- /*
- Someone has issued SET GLOBAL READ_ONLY=1 and we want a write lock.
- We do not wait for READ_ONLY=0, and fail.
- */
- my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
- reset_lock_data(sql_lock, 1);
- DBUG_RETURN(1); // Fatal error
- }
- }
+ DBUG_ENTER("mysql_lock_tables(sql_lock)");
thd_proc_info(thd, "System lock");
- if (lock_external(thd, sql_lock->table, sql_lock->table_count))
- {
- /* Clear the lock type of all lock data to avoid reusage. */
- res= 1; // Fatal error
+ if (sql_lock->table_count && lock_external(thd, sql_lock->table,
+ sql_lock->table_count))
goto end;
- }
+
thd_proc_info(thd, "Table lock");
- DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
- /* Copy the lock data array. thr_multi_lock() reorders its contens. */
+
+ /* Copy the lock data array. thr_multi_lock() reorders its contents. */
memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
sql_lock->lock_count * sizeof(*sql_lock->locks));
/* Lock on the copied half of the lock data array. */
rc= thr_lock_errno_to_mysql[(int) thr_multi_lock(sql_lock->locks +
sql_lock->lock_count,
sql_lock->lock_count,
- thd->lock_id)];
- if (rc) // Locking failed
- {
- if (sql_lock->table_count)
- VOID(unlock_external(thd, sql_lock->table, sql_lock->table_count));
-
- /*
- reset_lock_data is required here. If thr_multi_lock fails it
- resets lock type for tables, which were locked before (and
- including) one that caused error. Lock type for other tables
- preserved.
- */
- reset_lock_data(sql_lock, 1);
-
- if (rc > 1)
- {
- my_error(rc, MYF(0));
- DBUG_RETURN(1);
- }
- DBUG_ASSERT(rc == 1); // Timeout
- thd->some_tables_deleted= 1; // Reopen tables
- sql_lock->lock_count= 0; // Locks are already freed
- /* Retry */
- }
- else
- {
- /*
- Lock worked. Now check that nothing happend while we where waiting
- to get the lock that would require us to free it.
- */
- if (!thd->some_tables_deleted || (flags & MYSQL_LOCK_IGNORE_FLUSH))
- {
- res= 0;
- goto end; /* Lock was not aborted. Return to upper level */
- }
- if (!thd->open_tables && !(flags & MYSQL_LOCK_NOT_TEMPORARY))
- {
- /*
- Only using temporary tables, no need to unlock.
- We need the flag as open_tables is not enough to distingush if
- we are only using temporary tables for tables used trough
- the HANDLER interface.
-
- We reset some_tables_deleted as it doesn't make sense to have this
- one when we are only using temporary tables.
- */
- thd->some_tables_deleted=0;
- goto end;
- }
- /* Free lock and retry */
- }
-
- /* some table was altered or deleted. reopen tables marked deleted */
- mysql_unlock_tables(thd, sql_lock, 0);
+ &thd->lock_info, timeout)];
+ if (rc && sql_lock->table_count)
+ (void) unlock_external(thd, sql_lock->table, sql_lock->table_count);
-retry:
- res= -1; // Retry
- if (flags & MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN)
- {
- *need_reopen= TRUE; // Upper level will retry
- DBUG_RETURN(1); // Fatal error
- }
- if (wait_for_tables(thd))
- res= 1; // Couldn't open tables
+end:
+ thd_proc_info(thd, "After table lock");
-end:
- thd_proc_info(thd, 0);
if (thd->killed)
{
thd->send_kill_message();
- if (res == 0)
- mysql_unlock_tables(thd,sql_lock,0);
- else
- reset_lock_data(sql_lock, 1);
- res= 1; // Fatal
+ if (!rc)
+ mysql_unlock_tables(thd, sql_lock, 0);
+ rc= 1;
}
+ else if (rc > 1)
+ my_error(rc, MYF(0));
+
thd->set_time_after_lock();
- DBUG_RETURN(res);
+ DBUG_RETURN(rc);
}
@@ -417,15 +376,16 @@ static int lock_external(THD *thd, TABLE **tables, uint count)
DBUG_RETURN(0);
}
+
void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock)
{
DBUG_ENTER("mysql_unlock_tables");
if (sql_lock->table_count)
- VOID(unlock_external(thd,sql_lock->table,sql_lock->table_count));
+ unlock_external(thd, sql_lock->table, sql_lock->table_count);
if (sql_lock->lock_count)
- thr_multi_unlock(sql_lock->locks,sql_lock->lock_count, 0);
+ thr_multi_unlock(sql_lock->locks, sql_lock->lock_count, 0);
if (free_lock)
- my_free((uchar*) sql_lock,MYF(0));
+ my_free(sql_lock);
DBUG_VOID_RETURN;
}
@@ -438,10 +398,8 @@ void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock)
void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count)
{
MYSQL_LOCK *sql_lock;
- TABLE *write_lock_used;
- if ((sql_lock= get_lock_data(thd, table, count, GET_LOCK_UNLOCK,
- &write_lock_used)))
- mysql_unlock_tables(thd, sql_lock);
+ if ((sql_lock= get_lock_data(thd, table, count, GET_LOCK_UNLOCK)))
+ mysql_unlock_tables(thd, sql_lock, 1);
}
@@ -461,7 +419,7 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
for (i=found=0 ; i < sql_lock->table_count ; i++)
{
DBUG_ASSERT(sql_lock->table[i]->lock_position == i);
- if ((uint) sql_lock->table[i]->reginfo.lock_type >= TL_WRITE_ALLOW_READ)
+ if ((uint) sql_lock->table[i]->reginfo.lock_type > TL_WRITE_ALLOW_WRITE)
{
swap_variables(TABLE *, *table, sql_lock->table[i]);
table++;
@@ -471,7 +429,7 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
/* Unlock all read locked tables */
if (i != found)
{
- VOID(unlock_external(thd,table,i-found));
+ (void) unlock_external(thd,table,i-found);
sql_lock->table_count=found;
}
@@ -481,7 +439,7 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
THR_LOCK_DATA **lock=sql_lock->locks;
for (i=found=0 ; i < sql_lock->lock_count ; i++)
{
- if (sql_lock->locks[i]->type >= TL_WRITE_ALLOW_READ)
+ if (sql_lock->locks[i]->type >= TL_WRITE_ALLOW_WRITE)
{
swap_variables(THR_LOCK_DATA *, *lock, sql_lock->locks[i]);
lock++;
@@ -513,28 +471,15 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
/**
Try to find the table in the list of locked tables.
In case of success, unlock the table and remove it from this list.
-
- @note This function has a legacy side effect: the table is
- unlocked even if it is not found in the locked list.
- It's not clear if this side effect is intentional or still
- desirable. It might lead to unmatched calls to
- unlock_external(). Moreover, a discrepancy can be left
- unnoticed by the storage engine, because in
- unlock_external() we call handler::external_lock(F_UNLCK) only
- if table->current_lock is not F_UNLCK.
+ If a table has more than one lock instance, removes them all.
@param thd thread context
@param locked list of locked tables
@param table the table to unlock
- @param always_unlock specify explicitly if the legacy side
- effect is desired.
*/
-void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table,
- bool always_unlock)
+void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table)
{
- if (always_unlock == TRUE)
- mysql_unlock_some_tables(thd, &table, /* table count */ 1);
if (locked)
{
reg1 uint i;
@@ -548,9 +493,8 @@ void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table,
DBUG_ASSERT(table->lock_position == i);
- /* Unlock if not yet unlocked */
- if (always_unlock == FALSE)
- mysql_unlock_some_tables(thd, &table, /* table count */ 1);
+ /* Unlock the table. */
+ mysql_unlock_some_tables(thd, &table, /* table count */ 1);
/* Decrement table_count in advance, making below expressions easier */
old_tables= --locked->table_count;
@@ -594,50 +538,19 @@ void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table,
}
}
-/* Downgrade all locks on a table to new WRITE level from WRITE_ONLY */
-
-void mysql_lock_downgrade_write(THD *thd, TABLE *table,
- thr_lock_type new_lock_type)
-{
- MYSQL_LOCK *locked;
- TABLE *write_lock_used;
- if ((locked = get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK,
- &write_lock_used)))
- {
- for (uint i=0; i < locked->lock_count; i++)
- thr_downgrade_write_lock(locked->locks[i], new_lock_type);
- my_free((uchar*) locked,MYF(0));
- }
-}
-
/** Abort all other threads waiting to get lock in table. */
void mysql_lock_abort(THD *thd, TABLE *table, bool upgrade_lock)
{
MYSQL_LOCK *locked;
- TABLE *write_lock_used;
DBUG_ENTER("mysql_lock_abort");
- if ((locked= get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK,
- &write_lock_used)))
+ if ((locked= get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK)))
{
- if (table->children_attached)
- {
- /*
- Don't abort locks for underlying tables just because merge table
- is deleted. Doing would cause anyone accessing these tables to
- spin in open_table/close_table forever until lock is released.
- */
- thr_multi_unlock(locked->locks, locked->lock_count,
- THR_UNLOCK_UPDATE_STATUS);
- }
- else
- {
- for (uint i=0; i < locked->lock_count; i++)
- thr_abort_locks(locked->locks[i]->lock, upgrade_lock);
- }
- my_free((uchar*) locked,MYF(0));
+ for (uint i=0; i < locked->lock_count; i++)
+ thr_abort_locks(locked->locks[i]->lock, upgrade_lock);
+ my_free(locked);
}
DBUG_VOID_RETURN;
}
@@ -658,12 +571,10 @@ void mysql_lock_abort(THD *thd, TABLE *table, bool upgrade_lock)
bool mysql_lock_abort_for_thread(THD *thd, TABLE *table)
{
MYSQL_LOCK *locked;
- TABLE *write_lock_used;
bool result= FALSE;
DBUG_ENTER("mysql_lock_abort_for_thread");
- if ((locked= get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK,
- &write_lock_used)))
+ if ((locked= get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK)))
{
for (uint i=0; i < locked->lock_count; i++)
{
@@ -671,7 +582,7 @@ bool mysql_lock_abort_for_thread(THD *thd, TABLE *table)
table->in_use->thread_id))
result= TRUE;
}
- my_free((uchar*) locked,MYF(0));
+ my_free(locked);
}
DBUG_RETURN(result);
}
@@ -740,114 +651,12 @@ MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
a->lock_count, b->lock_count);
/* Delete old, not needed locks */
- my_free((uchar*) a,MYF(0));
- my_free((uchar*) b,MYF(0));
+ my_free(a);
+ my_free(b);
DBUG_RETURN(sql_lock);
}
-/**
- Find duplicate lock in tables.
-
- Temporary tables are ignored here like they are ignored in
- get_lock_data(). If we allow two opens on temporary tables later,
- both functions should be checked.
-
- @param thd The current thread.
- @param needle The table to check for duplicate lock.
- @param haystack The list of tables to search for the dup lock.
-
- @note
- This is mainly meant for MERGE tables in INSERT ... SELECT
- situations. The 'real', underlying tables can be found only after
- the MERGE tables are opened. This function assumes that the tables are
- already locked.
-
- @retval
- NULL No duplicate lock found.
- @retval
- !NULL First table from 'haystack' that matches a lock on 'needle'.
-*/
-
-TABLE_LIST *mysql_lock_have_duplicate(THD *thd, TABLE_LIST *needle,
- TABLE_LIST *haystack)
-{
- MYSQL_LOCK *mylock;
- TABLE *table;
- TABLE *table2;
- THR_LOCK_DATA **lock_locks;
- THR_LOCK_DATA **table_lock_data;
- THR_LOCK_DATA **end_data;
- THR_LOCK_DATA **lock_data2;
- THR_LOCK_DATA **end_data2;
- DBUG_ENTER("mysql_lock_have_duplicate");
-
- /*
- Table may not be defined for derived or view tables.
- Table may not be part of a lock for delayed operations.
- */
- if (! (table= needle->table) || ! table->lock_count)
- goto end;
-
- /* A temporary table does not have locks. */
- if (table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE)
- goto end;
-
- /* Get command lock or LOCK TABLES lock. Maybe empty for INSERT DELAYED. */
- if (! (mylock= thd->lock ? thd->lock : thd->locked_tables))
- goto end;
-
- /* If we have less than two tables, we cannot have duplicates. */
- if (mylock->table_count < 2)
- goto end;
-
- lock_locks= mylock->locks;
-
- /* Prepare table related variables that don't change in loop. */
- DBUG_ASSERT((table->lock_position < mylock->table_count) &&
- (table == mylock->table[table->lock_position]));
- table_lock_data= lock_locks + table->lock_data_start;
- end_data= table_lock_data + table->lock_count;
-
- for (; haystack; haystack= haystack->next_global)
- {
- if (haystack->placeholder())
- continue;
- table2= haystack->table;
- if (table2->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE)
- continue;
-
- /* All tables in list must be in lock. */
- DBUG_ASSERT((table2->lock_position < mylock->table_count) &&
- (table2 == mylock->table[table2->lock_position]));
-
- for (lock_data2= lock_locks + table2->lock_data_start,
- end_data2= lock_data2 + table2->lock_count;
- lock_data2 < end_data2;
- lock_data2++)
- {
- THR_LOCK_DATA **lock_data;
- THR_LOCK *lock2= (*lock_data2)->lock;
-
- for (lock_data= table_lock_data;
- lock_data < end_data;
- lock_data++)
- {
- if ((*lock_data)->lock == lock2)
- {
- DBUG_PRINT("info", ("haystack match: '%s'", haystack->table_name));
- DBUG_RETURN(haystack);
- }
- }
- }
- }
-
- end:
- DBUG_PRINT("info", ("no duplicate found"));
- DBUG_RETURN(NULL);
-}
-
-
/** Unlock a set of external. */
static int unlock_external(THD *thd, TABLE **table,uint count)
@@ -881,11 +690,9 @@ 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
- @param write_lock_used Store pointer to last table with WRITE_ALLOW_WRITE
*/
-MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
- uint flags, TABLE **write_lock_used)
+MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags)
{
uint i,tables,lock_count;
MYSQL_LOCK *sql_lock;
@@ -894,9 +701,8 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
DBUG_ENTER("get_lock_data");
DBUG_ASSERT((flags == GET_LOCK_UNLOCK) || (flags == GET_LOCK_STORE_LOCKS));
-
DBUG_PRINT("info", ("count %d", count));
- *write_lock_used=0;
+
for (i=tables=lock_count=0 ; i < count ; i++)
{
TABLE *t= table_ptr[i];
@@ -937,19 +743,6 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
continue;
lock_type= table->reginfo.lock_type;
DBUG_ASSERT(lock_type != TL_WRITE_DEFAULT && lock_type != TL_READ_DEFAULT);
- if (lock_type >= TL_WRITE_ALLOW_WRITE)
- {
- *write_lock_used=table;
- if (table->db_stat & HA_READ_ONLY)
- {
- my_error(ER_OPEN_AS_READONLY,MYF(0),table->alias.c_ptr());
- /* Clear the lock type of the lock data that are stored already. */
- sql_lock->lock_count= (uint) (locks - sql_lock->locks);
- reset_lock_data(sql_lock, 1);
- my_free((uchar*) sql_lock,MYF(0));
- DBUG_RETURN(0);
- }
- }
locks_start= locks;
locks= table->file->store_lock(thd, locks,
(flags & GET_LOCK_UNLOCK) ? TL_IGNORE :
@@ -993,390 +786,112 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
/**
- Reset lock type in lock data.
-
- After a locking error we want to quit the locking of the table(s).
- The test case in the bug report for Bug #18544 has the following
- cases:
- -# Locking error in lock_external() due to InnoDB timeout.
- -# Locking error in get_lock_data() due to missing write permission.
- -# Locking error in wait_if_global_read_lock() due to lock conflict.
-
- In all these cases we have already set the lock type into the lock
- data of the open table(s). If the table(s) are in the open table
- cache, they could be reused with the non-zero lock type set. This
- could lead to ignoring a different lock type with the next lock.
-
- Clear the lock type of all lock data. This ensures that the next
- lock request will set its lock type properly.
-
- @param sql_lock The MySQL lock.
- @param unlock If set, then set lock type to TL_UNLOCK,
- otherwise set to original lock type from
- get_store_lock().
-*/
-
-void reset_lock_data(MYSQL_LOCK *sql_lock, bool unlock)
-{
- THR_LOCK_DATA **ldata;
- THR_LOCK_DATA **ldata_end;
-
- for (ldata= sql_lock->locks, ldata_end= ldata + sql_lock->lock_count;
- ldata < ldata_end;
- ldata++)
- (*ldata)->type= unlock ? TL_UNLOCK : (*ldata)->org_type;
-}
-
-
-/*****************************************************************************
- Lock table based on the name.
- This is used when we need total access to a closed, not open table
-*****************************************************************************/
-
-/**
- Lock and wait for the named lock.
-
- @param thd Thread handler
- @param table_list Lock first table in this list
-
-
- @note
- Works together with global read lock.
-
- @retval
- 0 ok
- @retval
- 1 error
-*/
-
-int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list)
-{
- int lock_retcode;
- int error= -1;
- DBUG_ENTER("lock_and_wait_for_table_name");
-
- if (wait_if_global_read_lock(thd, 0, 1))
- DBUG_RETURN(1);
- VOID(pthread_mutex_lock(&LOCK_open));
- if ((lock_retcode = lock_table_name(thd, table_list, TRUE)) < 0)
- goto end;
- if (lock_retcode && wait_for_locked_table_names(thd, table_list))
- {
- unlock_table_name(thd, table_list);
- goto end;
- }
- error=0;
-
-end:
- pthread_mutex_unlock(&LOCK_open);
- start_waiting_global_read_lock(thd);
- DBUG_RETURN(error);
-}
-
-
-/**
- Put a not open table with an old refresh version in the table cache.
-
- @param thd Thread handler
- @param table_list Lock first table in this list
- @param check_in_use Do we need to check if table already in use by us
+ Obtain an exclusive metadata lock on a schema name.
- @note
- One must have a lock on LOCK_open!
+ @param thd Thread handle.
+ @param db The database name.
- @warning
- If you are going to update the table, you should use
- lock_and_wait_for_table_name instead of this function as this works
- together with 'FLUSH TABLES WITH READ LOCK'
+ This function cannot be called while holding LOCK_open mutex.
+ To avoid deadlocks, we do not try to obtain exclusive metadata
+ locks in LOCK TABLES mode, since in this mode there may be
+ other metadata locks already taken by the current connection,
+ and we must not wait for MDL locks while holding locks.
- @note
- This will force any other threads that uses the table to release it
- as soon as possible.
-
- @return
- < 0 error
- @return
- == 0 table locked
- @return
- > 0 table locked, but someone is using it
+ @retval FALSE Success.
+ @retval TRUE Failure: we're in LOCK TABLES mode, or out of memory,
+ or this connection was killed.
*/
-int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use)
+bool lock_schema_name(THD *thd, const char *db)
{
- TABLE *table;
- char key[MAX_DBKEY_LENGTH];
- char *db= table_list->db;
- uint key_length;
- HASH_SEARCH_STATE state;
- DBUG_ENTER("lock_table_name");
- DBUG_PRINT("enter",("db: %s name: %s", db, table_list->table_name));
-
- key_length= create_table_def_key(thd, key, table_list, 0);
+ MDL_request_list mdl_requests;
+ MDL_request global_request;
+ MDL_request mdl_request;
- if (check_in_use)
+ if (thd->locked_tables_mode)
{
- /* Only insert the table if we haven't insert it already */
- for (table=(TABLE*) hash_first(&open_cache, (uchar*)key,
- key_length, &state);
- table ;
- table = (TABLE*) hash_next(&open_cache,(uchar*) key,
- key_length, &state))
- {
- if (table->in_use == thd)
- {
- DBUG_PRINT("info", ("Table is in use"));
- table->s->version= 0; // Ensure no one can use this
- table->locked_by_name= 1;
- DBUG_RETURN(0);
- }
- }
- }
-
- if (!(table= table_cache_insert_placeholder(thd, key, key_length)))
- DBUG_RETURN(-1);
-
- table_list->table=table;
- table->s->deleting= table_list->deleting;
-
- /* Return 1 if table is in use */
- DBUG_RETURN(test(remove_table_from_cache(thd, db, table_list->table_name,
- (check_in_use ?
- RTFC_NO_FLAG :
- RTFC_WAIT_OTHER_THREAD_FLAG),
- table_list->deleting)));
-}
-
-
-void unlock_table_name(THD *thd, TABLE_LIST *table_list)
-{
- DBUG_ENTER("unlock_table_name");
- if (table_list->table)
- {
- hash_delete(&open_cache, (uchar*) table_list->table);
- broadcast_refresh();
- }
- DBUG_VOID_RETURN;
-}
-
-
-static bool locked_named_table(THD *thd, TABLE_LIST *table_list)
-{
- for (; table_list ; table_list=table_list->next_local)
- {
- TABLE *table= table_list->table;
- if (table)
- {
- TABLE *save_next= table->next;
- bool result;
- table->next= 0;
- result= table_is_used(table_list->table, 0);
- table->next= save_next;
- if (result)
- return 1;
- }
- }
- return 0; // All tables are locked
-}
-
-
-bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list)
-{
- bool result=0;
- DBUG_ENTER("wait_for_locked_table_names");
-
- safe_mutex_assert_owner(&LOCK_open);
-
- while (locked_named_table(thd,table_list))
- {
- if (thd->killed)
- {
- result=1;
- break;
- }
- wait_for_condition(thd, &LOCK_open, &COND_refresh);
- pthread_mutex_lock(&LOCK_open);
- }
- DBUG_RETURN(result);
-}
-
-
-/**
- Lock all tables in list with a name lock.
-
- REQUIREMENTS
- - One must have a lock on LOCK_open when calling this
-
- @param thd Thread handle
- @param table_list Names of tables to lock
-
- @note
- If you are just locking one table, you should use
- lock_and_wait_for_table_name().
-
- @retval
- 0 ok
- @retval
- 1 Fatal error (end of memory ?)
-*/
-
-bool lock_table_names(THD *thd, TABLE_LIST *table_list)
-{
- bool got_all_locks=1;
- TABLE_LIST *lock_table;
-
- for (lock_table= table_list; lock_table; lock_table= lock_table->next_local)
- {
- int got_lock;
- if ((got_lock=lock_table_name(thd,lock_table, TRUE)) < 0)
- goto end; // Fatal error
- if (got_lock)
- got_all_locks=0; // Someone is using table
+ my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
+ ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
+ return TRUE;
}
- /* If some table was in use, wait until we got the lock */
- if (!got_all_locks && wait_for_locked_table_names(thd, table_list))
- goto end;
- return 0;
-
-end:
- unlock_table_names(thd, table_list, lock_table);
- return 1;
-}
-
-
-/**
- Unlock all tables in list with a name lock.
-
- @param thd Thread handle.
- @param table_list Names of tables to lock.
-
- @note
- This function needs to be protected by LOCK_open. If we're
- under LOCK TABLES, this function does not work as advertised. Namely,
- it does not exclude other threads from using this table and does not
- put an exclusive name lock on this table into the table cache.
-
- @see lock_table_names
- @see unlock_table_names
+ if (thd->global_read_lock.can_acquire_protection())
+ return TRUE;
+ global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
+ MDL_STATEMENT);
+ mdl_request.init(MDL_key::SCHEMA, db, "", MDL_EXCLUSIVE, MDL_TRANSACTION);
- @retval TRUE An error occured.
- @retval FALSE Name lock successfully acquired.
-*/
+ mdl_requests.push_front(&mdl_request);
+ mdl_requests.push_front(&global_request);
-bool lock_table_names_exclusively(THD *thd, TABLE_LIST *table_list)
-{
- if (lock_table_names(thd, table_list))
+ if (thd->mdl_context.acquire_locks(&mdl_requests,
+ thd->variables.lock_wait_timeout))
return TRUE;
- /*
- Upgrade the table name locks from semi-exclusive to exclusive locks.
- */
- for (TABLE_LIST *table= table_list; table; table= table->next_global)
- {
- if (table->table)
- table->table->open_placeholder= 1;
- }
+ DEBUG_SYNC(thd, "after_wait_locked_schema_name");
return FALSE;
}
/**
- Test is 'table' is protected by an exclusive name lock.
-
- @param[in] thd The current thread handler
- @param[in] table_list Table container containing the single table to be
- tested
-
- @note Needs to be protected by LOCK_open mutex.
-
- @return Error status code
- @retval TRUE Table is protected
- @retval FALSE Table is not protected
+ Obtain an exclusive metadata lock on an object name.
+
+ @param thd Thread handle.
+ @param mdl_type Object type (currently functions, procedures
+ and events can be name-locked).
+ @param db The schema the object belongs to.
+ @param name Object name in the schema.
+
+ This function assumes that no metadata locks were acquired
+ before calling it. Additionally, it cannot be called while
+ holding LOCK_open mutex. Both these invariants are enforced by
+ asserts in MDL_context::acquire_locks().
+ To avoid deadlocks, we do not try to obtain exclusive metadata
+ locks in LOCK TABLES mode, since in this mode there may be
+ other metadata locks already taken by the current connection,
+ and we must not wait for MDL locks while holding locks.
+
+ @retval FALSE Success.
+ @retval TRUE Failure: we're in LOCK TABLES mode, or out of memory,
+ or this connection was killed.
*/
-bool
-is_table_name_exclusively_locked_by_this_thread(THD *thd,
- TABLE_LIST *table_list)
+bool lock_object_name(THD *thd, MDL_key::enum_mdl_namespace mdl_type,
+ const char *db, const char *name)
{
- char key[MAX_DBKEY_LENGTH];
- uint key_length;
-
- key_length= create_table_def_key(thd, key, table_list, 0);
-
- return is_table_name_exclusively_locked_by_this_thread(thd, (uchar *)key,
- key_length);
-}
-
+ MDL_request_list mdl_requests;
+ MDL_request global_request;
+ MDL_request schema_request;
+ MDL_request mdl_request;
-/**
- Test is 'table key' is protected by an exclusive name lock.
-
- @param[in] thd The current thread handler.
- @param[in] key
- @param[in] key_length
-
- @note Needs to be protected by LOCK_open mutex
-
- @retval TRUE Table is protected
- @retval FALSE Table is not protected
- */
-
-bool
-is_table_name_exclusively_locked_by_this_thread(THD *thd, uchar *key,
- int key_length)
-{
- HASH_SEARCH_STATE state;
- TABLE *table;
-
- for (table= (TABLE*) hash_first(&open_cache, key,
- key_length, &state);
- table ;
- table= (TABLE*) hash_next(&open_cache, key,
- key_length, &state))
+ if (thd->locked_tables_mode)
{
- if (table->in_use == thd &&
- table->open_placeholder == 1 &&
- table->s->version == 0)
- return TRUE;
+ my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
+ ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
+ return TRUE;
}
- return FALSE;
-}
-
-/**
- Unlock all tables in list with a name lock.
+ DBUG_ASSERT(name);
+ DEBUG_SYNC(thd, "before_wait_locked_pname");
- @param
- thd Thread handle
- @param
- table_list Names of tables to unlock
- @param
- last_table Don't unlock any tables after this one.
- (default 0, which will unlock all tables)
-
- @note
- One must have a lock on LOCK_open when calling this.
-
- @note
- This function will broadcast refresh signals to inform other threads
- that the name locks are removed.
-
- @retval
- 0 ok
- @retval
- 1 Fatal error (end of memory ?)
-*/
+ if (thd->global_read_lock.can_acquire_protection())
+ return TRUE;
+ global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
+ MDL_STATEMENT);
+ schema_request.init(MDL_key::SCHEMA, db, "", MDL_INTENTION_EXCLUSIVE,
+ MDL_TRANSACTION);
+ mdl_request.init(mdl_type, db, name, MDL_EXCLUSIVE, MDL_TRANSACTION);
+
+ mdl_requests.push_front(&mdl_request);
+ mdl_requests.push_front(&schema_request);
+ mdl_requests.push_front(&global_request);
+
+ if (thd->mdl_context.acquire_locks(&mdl_requests,
+ thd->variables.lock_wait_timeout))
+ return TRUE;
-void unlock_table_names(THD *thd, TABLE_LIST *table_list,
- TABLE_LIST *last_table)
-{
- DBUG_ENTER("unlock_table_names");
- for (TABLE_LIST *table= table_list;
- table != last_table;
- table= table->next_local)
- unlock_table_name(thd,table);
- broadcast_refresh();
- DBUG_VOID_RETURN;
+ DEBUG_SYNC(thd, "after_wait_locked_pname");
+ return FALSE;
}
@@ -1415,45 +930,24 @@ static void print_lock_error(int error, const char *table)
/****************************************************************************
Handling of global read locks
+ Global read lock is implemented using metadata lock infrastructure.
+
Taking the global read lock is TWO steps (2nd step is optional; without
it, COMMIT of existing transactions will be allowed):
lock_global_read_lock() THEN make_global_read_lock_block_commit().
- The global locks are handled through the global variables:
- global_read_lock
- count of threads which have the global read lock (i.e. have completed at
- least the first step above)
- global_read_lock_blocks_commit
- count of threads which have the global read lock and block
- commits (i.e. are in or have completed the second step above)
- waiting_for_read_lock
- count of threads which want to take a global read lock but cannot
- protect_against_global_read_lock
- count of threads which have set protection against global read lock.
-
- access to them is protected with a mutex LOCK_global_read_lock
-
- (XXX: one should never take LOCK_open if LOCK_global_read_lock is
- taken, otherwise a deadlock may occur. Other mutexes could be a
- problem too - grep the code for global_read_lock if you want to use
- any other mutex here) Also one must not hold LOCK_open when calling
- wait_if_global_read_lock(). When the thread with the global read lock
- tries to close its tables, it needs to take LOCK_open in
- close_thread_table().
-
How blocking of threads by global read lock is achieved: that's
- advisory. Any piece of code which should be blocked by global read lock must
- be designed like this:
- - call to wait_if_global_read_lock(). When this returns 0, no global read
- lock is owned; if argument abort_on_refresh was 0, none can be obtained.
- - job
- - if abort_on_refresh was 0, call to start_waiting_global_read_lock() to
- allow other threads to get the global read lock. I.e. removal of the
- protection.
- (Note: it's a bit like an implementation of rwlock).
-
- [ I am sorry to mention some SQL syntaxes below I know I shouldn't but found
- no better descriptive way ]
+ semi-automatic. We assume that any statement which should be blocked
+ by global read lock will either open and acquires write-lock on tables
+ or acquires metadata locks on objects it is going to modify. For any
+ such statement global IX metadata lock is automatically acquired for
+ its duration (in case of LOCK TABLES until end of LOCK TABLES mode).
+ And lock_global_read_lock() simply acquires global S metadata lock
+ and thus prohibits execution of statements which modify data (unless
+ they modify only temporary tables). If deadlock happens it is detected
+ by MDL subsystem and resolved in the standard fashion (by backing-off
+ metadata locks acquired so far and restarting open tables process
+ if possible).
Why does FLUSH TABLES WITH READ LOCK need to block COMMIT: because it's used
to read a non-moving SHOW MASTER STATUS, and a COMMIT writes to the binary
@@ -1487,40 +981,37 @@ static void print_lock_error(int error, const char *table)
****************************************************************************/
-volatile uint global_read_lock=0, global_disable_checkpoint= 0;
-volatile uint global_read_lock_blocks_commit=0;
-static volatile uint protect_against_global_read_lock=0;
-static volatile uint waiting_for_read_lock=0;
+/**
+ Take global read lock, wait if there is protection against lock.
+
+ If the global read lock is already taken by this thread, then nothing is done.
-#define GOT_GLOBAL_READ_LOCK 1
-#define MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT 2
+ See also "Handling of global read locks" above.
-bool lock_global_read_lock(THD *thd)
+ @param thd Reference to thread.
+
+ @retval False Success, global read lock set, commits are NOT blocked.
+ @retval True Failure, thread was killed.
+*/
+
+bool Global_read_lock::lock_global_read_lock(THD *thd)
{
DBUG_ENTER("lock_global_read_lock");
- if (!thd->global_read_lock)
+ if (!m_state)
{
- const char *old_message;
- (void) pthread_mutex_lock(&LOCK_global_read_lock);
- old_message=thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
- "Waiting to get readlock");
- DBUG_PRINT("info",
- ("waiting_for: %d protect_against: %d",
- waiting_for_read_lock, protect_against_global_read_lock));
-
- waiting_for_read_lock++;
- while (protect_against_global_read_lock && !thd->killed)
- pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
- waiting_for_read_lock--;
- if (thd->killed)
- {
- thd->exit_cond(old_message);
+ MDL_request mdl_request;
+
+ DBUG_ASSERT(! thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, "", "",
+ MDL_SHARED));
+ mdl_request.init(MDL_key::GLOBAL, "", "", MDL_SHARED, MDL_EXPLICIT);
+
+ if (thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout))
DBUG_RETURN(1);
- }
- thd->global_read_lock= GOT_GLOBAL_READ_LOCK;
- global_read_lock++;
- thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
+
+ m_mdl_global_shared_lock= mdl_request.ticket;
+ m_state= GRL_ACQUIRED;
}
/*
We DON'T set global_read_lock_blocks_commit now, it will be set after
@@ -1534,18 +1025,22 @@ bool lock_global_read_lock(THD *thd)
}
-void unlock_global_read_lock(THD *thd)
+/**
+ Unlock global read lock.
+
+ Commits may or may not be blocked when this function is called.
+
+ See also "Handling of global read locks" above.
+
+ @param thd Reference to thread.
+*/
+
+void Global_read_lock::unlock_global_read_lock(THD *thd)
{
- uint tmp;
DBUG_ENTER("unlock_global_read_lock");
- DBUG_PRINT("info",
- ("global_read_lock: %u global_read_lock_blocks_commit: %u",
- global_read_lock, global_read_lock_blocks_commit));
-
- pthread_mutex_lock(&LOCK_global_read_lock);
- tmp= --global_read_lock;
- if (thd->global_read_lock == MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT)
- --global_read_lock_blocks_commit;
+
+ DBUG_ASSERT(m_mdl_global_shared_lock && m_state);
+
if (thd->global_disable_checkpoint)
{
thd->global_disable_checkpoint= 0;
@@ -1554,168 +1049,71 @@ void unlock_global_read_lock(THD *thd)
ha_checkpoint_state(0); // Enable checkpoints
}
}
- pthread_mutex_unlock(&LOCK_global_read_lock);
- /* Send the signal outside the mutex to avoid a context switch */
- if (!tmp)
+
+ if (m_mdl_blocks_commits_lock)
{
- DBUG_PRINT("signal", ("Broadcasting COND_global_read_lock"));
- pthread_cond_broadcast(&COND_global_read_lock);
+ thd->mdl_context.release_lock(m_mdl_blocks_commits_lock);
+ m_mdl_blocks_commits_lock= NULL;
}
- thd->global_read_lock= 0;
+ thd->mdl_context.release_lock(m_mdl_global_shared_lock);
+ m_mdl_global_shared_lock= NULL;
+ m_state= GRL_NONE;
DBUG_VOID_RETURN;
}
-#define must_wait (global_read_lock && \
- (is_not_commit || \
- global_read_lock_blocks_commit))
-bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh,
- bool is_not_commit)
-{
- const char *UNINIT_VAR(old_message);
- bool result= 0, need_exit_cond;
- DBUG_ENTER("wait_if_global_read_lock");
+/**
+ Make global read lock also block commits.
- /*
- Assert that we do not own LOCK_open. If we would own it, other
- threads could not close their tables. This would make a pretty
- deadlock.
- */
- safe_mutex_assert_not_owner(&LOCK_open);
+ The scenario is:
+ - This thread has the global read lock.
+ - Global read lock blocking of commits is not set.
- (void) pthread_mutex_lock(&LOCK_global_read_lock);
- if ((need_exit_cond= must_wait))
- {
- if (thd->global_read_lock) // This thread had the read locks
- {
- if (is_not_commit)
- my_message(ER_CANT_UPDATE_WITH_READLOCK,
- ER(ER_CANT_UPDATE_WITH_READLOCK), MYF(0));
- (void) pthread_mutex_unlock(&LOCK_global_read_lock);
- /*
- We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
- This allowance is needed to not break existing versions of innobackup
- which do a BEGIN; INSERT; FLUSH TABLES WITH READ LOCK; COMMIT.
- */
- DBUG_RETURN(is_not_commit);
- }
- old_message=thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
- "Waiting for release of readlock");
- while (must_wait && ! thd->killed &&
- (!abort_on_refresh || thd->version == refresh_version))
- {
- DBUG_PRINT("signal", ("Waiting for COND_global_read_lock"));
- (void) pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
- DBUG_PRINT("signal", ("Got COND_global_read_lock"));
- }
- if (thd->killed)
- result=1;
- }
- if (!abort_on_refresh && !result)
- protect_against_global_read_lock++;
- /*
- The following is only true in case of a global read locks (which is rare)
- and if old_message is set
- */
- if (unlikely(need_exit_cond))
- thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
- else
- pthread_mutex_unlock(&LOCK_global_read_lock);
- DBUG_RETURN(result);
-}
+ See also "Handling of global read locks" above.
+ @param thd Reference to thread.
-void start_waiting_global_read_lock(THD *thd)
-{
- bool tmp;
- DBUG_ENTER("start_waiting_global_read_lock");
- if (unlikely(thd->global_read_lock))
- DBUG_VOID_RETURN;
- (void) pthread_mutex_lock(&LOCK_global_read_lock);
- DBUG_ASSERT(protect_against_global_read_lock);
- tmp= (!--protect_against_global_read_lock &&
- (waiting_for_read_lock || global_read_lock_blocks_commit));
- (void) pthread_mutex_unlock(&LOCK_global_read_lock);
- if (tmp)
- pthread_cond_broadcast(&COND_global_read_lock);
- DBUG_VOID_RETURN;
-}
-
+ @retval False Success, global read lock set, commits are blocked.
+ @retval True Failure, thread was killed.
+*/
-bool make_global_read_lock_block_commit(THD *thd)
+bool Global_read_lock::make_global_read_lock_block_commit(THD *thd)
{
- bool error;
- const char *old_message;
+ MDL_request mdl_request;
DBUG_ENTER("make_global_read_lock_block_commit");
/*
If we didn't succeed lock_global_read_lock(), or if we already suceeded
make_global_read_lock_block_commit(), do nothing.
*/
- if (thd->global_read_lock != GOT_GLOBAL_READ_LOCK)
+ if (m_state != GRL_ACQUIRED)
DBUG_RETURN(0);
- pthread_mutex_lock(&LOCK_global_read_lock);
- /* increment this BEFORE waiting on cond (otherwise race cond) */
- global_read_lock_blocks_commit++;
- /* For testing we set up some blocking, to see if we can be killed */
- DBUG_EXECUTE_IF("make_global_read_lock_block_commit_loop",
- protect_against_global_read_lock++;);
- old_message= thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
- "Waiting for all running commits to finish");
- while (protect_against_global_read_lock && !thd->killed)
- pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
- DBUG_EXECUTE_IF("make_global_read_lock_block_commit_loop",
- protect_against_global_read_lock--;);
- if ((error= test(thd->killed)))
- global_read_lock_blocks_commit--; // undo what we did
- else
- thd->global_read_lock= MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT;
- thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
- DBUG_RETURN(error);
-}
+ mdl_request.init(MDL_key::COMMIT, "", "", MDL_SHARED, MDL_EXPLICIT);
-/**
- Disable checkpoints for all handlers
- This is released in unlock_global_read_lock()
-*/
+ if (thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout))
+ DBUG_RETURN(TRUE);
-void disable_checkpoints(THD *thd)
-{
- pthread_mutex_lock(&LOCK_global_read_lock);
- if (!thd->global_disable_checkpoint)
- {
- thd->global_disable_checkpoint= 1;
- if (!global_disable_checkpoint++)
- ha_checkpoint_state(1); // Disable checkpoints
- }
- pthread_mutex_unlock(&LOCK_global_read_lock);
+ m_mdl_blocks_commits_lock= mdl_request.ticket;
+ m_state= GRL_ACQUIRED_AND_BLOCKS_COMMIT;
+
+ DBUG_RETURN(FALSE);
}
/**
- Broadcast COND_refresh and COND_global_read_lock.
-
- Due to a bug in a threading library it could happen that a signal
- did not reach its target. A condition for this was that the same
- condition variable was used with different mutexes in
- pthread_cond_wait(). Some time ago we changed LOCK_open to
- LOCK_global_read_lock in global read lock handling. So COND_refresh
- was used with LOCK_open and LOCK_global_read_lock.
+ Set explicit duration for metadata locks which are used to implement GRL.
- We did now also change from COND_refresh to COND_global_read_lock
- in global read lock handling. But now it is necessary to signal
- both conditions at the same time.
-
- @note
- When signalling COND_global_read_lock within the global read lock
- handling, it is not necessary to also signal COND_refresh.
+ @param thd Reference to thread.
*/
-void broadcast_refresh(void)
+void Global_read_lock::set_explicit_lock_duration(THD *thd)
{
- VOID(pthread_cond_broadcast(&COND_refresh));
- VOID(pthread_cond_broadcast(&COND_global_read_lock));
+ if (m_mdl_global_shared_lock)
+ thd->mdl_context.set_lock_duration(m_mdl_global_shared_lock, MDL_EXPLICIT);
+ if (m_mdl_blocks_commits_lock)
+ thd->mdl_context.set_lock_duration(m_mdl_blocks_commits_lock, MDL_EXPLICIT);
}
/**
diff --git a/sql/lock.h b/sql/lock.h
new file mode 100644
index 00000000000..a4833cdc38e
--- /dev/null
+++ b/sql/lock.h
@@ -0,0 +1,51 @@
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef LOCK_INCLUDED
+#define LOCK_INCLUDED
+
+#include "thr_lock.h" /* thr_lock_type */
+#include "mdl.h"
+
+// Forward declarations
+struct TABLE;
+struct TABLE_LIST;
+class THD;
+typedef struct st_mysql_lock MYSQL_LOCK;
+
+
+MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, uint flags);
+bool mysql_lock_tables(THD *thd, MYSQL_LOCK *sql_lock, uint flags);
+void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock= 1);
+void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);
+void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count);
+void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table);
+void mysql_lock_abort(THD *thd, TABLE *table, bool upgrade_lock);
+bool mysql_lock_abort_for_thread(THD *thd, TABLE *table);
+MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b);
+/* Lock based on name */
+bool lock_schema_name(THD *thd, const char *db);
+/* Lock based on stored routine name */
+bool lock_object_name(THD *thd, MDL_key::enum_mdl_namespace mdl_type,
+ const char *db, const char *name);
+
+/* flags for get_lock_data */
+#define GET_LOCK_UNLOCK 1
+#define GET_LOCK_STORE_LOCKS 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);
+
+#endif /* LOCK_INCLUDED */
diff --git a/sql/log.cc b/sql/log.cc
index 3a36edd72e9..b639ff8115e 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 2009, 2014, Monty Program Ab.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -25,26 +25,37 @@
Abort logging when we get an error in reading or writing log files
*/
-#include "mysql_priv.h"
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_priv.h"
+#include "log.h"
+#include "sql_base.h" // open_log_table
#include "sql_repl.h"
+#include "sql_delete.h" // mysql_truncate
+#include "sql_parse.h" // command_name
+#include "sql_time.h" // calc_time_from_sec, my_time_compare
+#include "tztime.h" // my_tz_OFFSET0, struct Time_zone
+#include "sql_acl.h" // SUPER_ACL
+#include "log_event.h" // Query_log_event
#include "rpl_filter.h"
#include "rpl_rli.h"
+#include "sql_audit.h"
+#include "log_slow.h"
#include <my_dir.h>
#include <stdarg.h>
#include <m_ctype.h> // For test_if_number
-#ifdef __NT__
+#ifdef _WIN32
#include "message.h"
#endif
-#include <mysql/plugin.h>
+#include "sql_plugin.h"
+#include "rpl_handler.h"
#include "debug_sync.h"
#include "sql_show.h"
/* max size of the log message */
#define MAX_LOG_BUFFER_SIZE 1024
-#define MAX_USER_HOST_SIZE 512
#define MAX_TIME_SIZE 32
#define MY_OFF_T_UNDEF (~(my_off_t)0UL)
@@ -52,11 +63,10 @@
LOGGER logger;
-MYSQL_BIN_LOG mysql_bin_log;
-ulong sync_binlog_counter= 0;
+MYSQL_BIN_LOG mysql_bin_log(&sync_binlog_period);
static bool test_if_number(const char *str,
- long *res, bool allow_wildcards);
+ ulong *res, bool allow_wildcards);
static int binlog_init(void *p);
static int binlog_close_connection(handlerton *hton, THD *thd);
static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv);
@@ -75,9 +85,8 @@ ulong binlog_checksum_options;
ulong opt_binlog_dbug_fsync_sleep= 0;
#endif
-static my_bool mutexes_inited;
-pthread_mutex_t LOCK_prepare_ordered;
-pthread_mutex_t LOCK_commit_ordered;
+mysql_mutex_t LOCK_prepare_ordered;
+mysql_mutex_t LOCK_commit_ordered;
static ulonglong binlog_status_var_num_commits;
static ulonglong binlog_status_var_num_group_commits;
@@ -99,6 +108,35 @@ static SHOW_VAR binlog_status_vars_detail[]=
/**
+ purge logs, master and slave sides both, related error code
+ convertor.
+ Called from @c purge_error_message(), @c MYSQL_BIN_LOG::reset_logs()
+
+ @param res an internal to purging routines error code
+
+ @return the user level error code ER_*
+*/
+uint purge_log_get_error_code(int res)
+{
+ uint errcode= 0;
+
+ switch (res) {
+ case 0: break;
+ case LOG_INFO_EOF: errcode= ER_UNKNOWN_TARGET_BINLOG; break;
+ case LOG_INFO_IO: errcode= ER_IO_ERR_LOG_INDEX_READ; break;
+ case LOG_INFO_INVALID:errcode= ER_BINLOG_PURGE_PROHIBITED; break;
+ case LOG_INFO_SEEK: errcode= ER_FSEEK_FAIL; break;
+ case LOG_INFO_MEM: errcode= ER_OUT_OF_RESOURCES; break;
+ case LOG_INFO_FATAL: errcode= ER_BINLOG_PURGE_FATAL_ERR; break;
+ case LOG_INFO_IN_USE: errcode= ER_LOG_IN_USE; break;
+ case LOG_INFO_EMFILE: errcode= ER_BINLOG_PURGE_EMFILE; break;
+ default: errcode= ER_LOG_PURGE_UNKNOWN_ERR; break;
+ }
+
+ return errcode;
+}
+
+/**
Silence all errors and warnings reported when performing a write
to a log table.
Errors and warnings are not reported to the client or SQL exception
@@ -116,23 +154,28 @@ public:
virtual ~Silence_log_table_errors() {}
- virtual bool handle_error(uint sql_errno, const char *message,
- MYSQL_ERROR::enum_warning_level level,
- THD *thd);
+ virtual bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sql_state,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl);
const char *message() const { return m_message; }
};
bool
-Silence_log_table_errors::handle_error(uint /* sql_errno */,
- const char *message_arg,
- MYSQL_ERROR::enum_warning_level /* level */,
- THD * /* thd */)
-{
- strmake(m_message, message_arg, sizeof(m_message)-1);
+Silence_log_table_errors::handle_condition(THD *,
+ uint,
+ const char*,
+ MYSQL_ERROR::enum_warning_level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl)
+{
+ *cond_hdl= NULL;
+ strmake_buf(m_message, msg);
return TRUE;
}
-
sql_print_message_func sql_print_message_handlers[3] =
{
sql_print_information,
@@ -141,149 +184,285 @@ sql_print_message_func sql_print_message_handlers[3] =
};
-char *make_default_log_name(char *buff,const char* log_ext)
-{
- strmake(buff, pidfile_name, FN_REFLEN-5);
- return fn_format(buff, buff, mysql_data_home, log_ext,
- MYF(MY_UNPACK_FILENAME|MY_REPLACE_EXT));
-}
-
-
-/*
- Create a filename from a base with a given suffix.
- The name is allocated trough my_once_alloc(), so one should only
- use this for startup options that can all be freed at once.
+/**
+ Create the name of the log file
+
+ @param[OUT] out a pointer to a new allocated name will go there
+ @param[IN] log_ext The extension for the file (e.g .log)
+ @param[IN] once whether to use malloc_once or a normal malloc.
*/
-
-char *make_once_alloced_filename(const char *basename, const char *ext)
+void make_default_log_name(char **out, const char* log_ext, bool once)
{
- char buff[FN_REFLEN+10], *end, *res;
- size_t length;
- strmake(buff, basename, sizeof(buff)-10);
- end= strmov(fn_ext(buff), ext);
- length= (size_t) (end - buff) + 1;
-
- if ((res= (char*) my_once_alloc(length, MYF(MY_WME))))
- memcpy(res, buff, length);
- return res;
+ char buff[FN_REFLEN+10];
+ fn_format(buff, opt_log_basename, "", log_ext, MYF(MY_REPLACE_EXT));
+ if (once)
+ *out= my_once_strdup(buff, MYF(MY_WME));
+ else
+ {
+ my_free(*out);
+ *out= my_strdup(buff, MYF(MY_WME));
+ }
}
/*
- Helper class to store binary log transaction data.
+ Helper classes to store non-transactional and transactional data
+ before copying it to the binary log.
*/
-class binlog_trx_data {
+class binlog_cache_data
+{
public:
- binlog_trx_data()
- : at_least_one_stmt_committed(0), incident(FALSE), m_pending(0),
- before_stmt_pos(MY_OFF_T_UNDEF), last_commit_pos_offset(0),
- using_xa(FALSE), xa_xid(0)
+ binlog_cache_data(): m_pending(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)
+ { }
+
+ ~binlog_cache_data()
{
- trans_log.end_of_file= max_binlog_cache_size;
- last_commit_pos_file[0]= 0;
+ DBUG_ASSERT(empty());
+ close_cached_file(&cache_log);
}
- ~binlog_trx_data()
+ bool empty() const
{
- DBUG_ASSERT(pending() == NULL);
- close_cached_file(&trans_log);
+ return pending() == NULL && my_b_tell(&cache_log) == 0;
}
- my_off_t position() const {
- return my_b_tell(&trans_log);
+ Rows_log_event *pending() const
+ {
+ return m_pending;
}
- bool empty() const
+ void set_pending(Rows_log_event *const pending)
{
- return pending() == NULL && my_b_tell(&trans_log) == 0;
+ m_pending= pending;
}
- /*
- Truncate the transaction cache to a certain position. This
- includes deleting the pending event.
- */
- void truncate(my_off_t pos)
+ void set_incident(void)
{
- DBUG_PRINT("info", ("truncating to position %lu", (ulong) pos));
- DBUG_PRINT("info", ("before_stmt_pos=%lu", (ulong) pos));
- if (pending())
- {
- delete pending();
- }
- set_pending(0);
- reinit_io_cache(&trans_log, WRITE_CACHE, pos, 0, 0);
- trans_log.end_of_file= max_binlog_cache_size;
- if (pos < before_stmt_pos)
- before_stmt_pos= MY_OFF_T_UNDEF;
+ incident= TRUE;
+ }
+
+ bool has_incident(void)
+ {
+ return(incident);
+ }
- /*
- The only valid positions that can be truncated to are at the
- beginning of a statement. We are relying on this fact to be able
- to set the at_least_one_stmt_committed flag correctly. In other word, if
- we are truncating to the beginning of the transaction cache,
- there will be no statements in the cache, otherwhise, we will
- have at least one statement in the transaction cache.
- */
- at_least_one_stmt_committed= (pos > 0);
+ void set_changes_to_non_trans_temp_table()
+ {
+ changes_to_non_trans_temp_table_flag= TRUE;
}
- /*
- Reset the entire contents of the transaction cache, emptying it
- completely.
- */
- void reset() {
- if (trans_log.type != WRITE_CACHE || !empty())
- truncate(0);
- before_stmt_pos= MY_OFF_T_UNDEF;
+ bool changes_to_non_trans_temp_table()
+ {
+ return (changes_to_non_trans_temp_table_flag);
+ }
+
+ void reset()
+ {
+ compute_statistics();
+ truncate(0);
+ changes_to_non_trans_temp_table_flag= FALSE;
incident= FALSE;
- trans_log.end_of_file= max_binlog_cache_size;
- using_xa= FALSE;
- last_commit_pos_file[0]= 0;
- last_commit_pos_offset= 0;
+ 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());
}
- Rows_log_event *pending() const
+ my_off_t get_byte_position() const
{
- return m_pending;
+ return my_b_tell(&cache_log);
}
- void set_pending(Rows_log_event *const pending)
+ my_off_t get_prev_position()
{
- m_pending= pending;
+ return(before_stmt_pos);
}
- IO_CACHE trans_log; // The transaction cache
-
- void set_incident(void)
+ void set_prev_position(my_off_t pos)
{
- incident= TRUE;
+ before_stmt_pos= pos;
}
- bool has_incident(void)
+ void restore_prev_position()
{
- return(incident);
+ truncate(before_stmt_pos);
}
- /**
- Boolean that is true if there is at least one statement in the
- transaction cache.
+ void restore_savepoint(my_off_t pos)
+ {
+ truncate(pos);
+ if (pos < before_stmt_pos)
+ before_stmt_pos= MY_OFF_T_UNDEF;
+ }
+
+ void set_binlog_cache_info(my_off_t param_max_binlog_cache_size,
+ ulong *param_ptr_binlog_cache_use,
+ ulong *param_ptr_binlog_cache_disk_use)
+ {
+ /*
+ The assertions guarantee that the set_binlog_cache_info is
+ called just once and information passed as parameters are
+ never zero.
+
+ This is done while calling the constructor binlog_cache_mngr.
+ We cannot set informaton in the constructor binlog_cache_data
+ because the space for binlog_cache_mngr is allocated through
+ a placement new.
+
+ In the future, we can refactor this and change it to avoid
+ the set_binlog_info.
+ */
+ DBUG_ASSERT(saved_max_binlog_cache_size == 0 &&
+ param_max_binlog_cache_size != 0 &&
+ ptr_binlog_cache_use == 0 &&
+ param_ptr_binlog_cache_use != 0 &&
+ ptr_binlog_cache_disk_use == 0 &&
+ param_ptr_binlog_cache_disk_use != 0);
+
+ saved_max_binlog_cache_size= param_max_binlog_cache_size;
+ ptr_binlog_cache_use= param_ptr_binlog_cache_use;
+ ptr_binlog_cache_disk_use= param_ptr_binlog_cache_disk_use;
+ cache_log.end_of_file= saved_max_binlog_cache_size;
+ }
+
+ /*
+ Cache to store data before copying it to the binary log.
*/
- bool at_least_one_stmt_committed;
- bool incident;
+ IO_CACHE cache_log;
private:
/*
- Pending binrows event. This event is the event where the rows are
- currently written.
+ Pending binrows event. This event is the event where the rows are currently
+ written.
*/
Rows_log_event *m_pending;
-public:
/*
Binlog position before the start of the current statement.
*/
my_off_t before_stmt_pos;
+
+ /*
+ This indicates that some events did not get into the cache and most likely
+ it is corrupted.
+ */
+ bool incident;
+
+ /*
+ This flag indicates if the cache has changes to temporary tables.
+ @TODO This a temporary fix and should be removed after BUG#54562.
+ */
+ bool changes_to_non_trans_temp_table_flag;
+
+ /**
+ This function computes binlog cache and disk usage.
+ */
+ void compute_statistics()
+ {
+ if (!empty())
+ {
+ statistic_increment(*ptr_binlog_cache_use, &LOCK_status);
+ if (cache_log.disk_writes != 0)
+ statistic_increment(*ptr_binlog_cache_disk_use, &LOCK_status);
+ }
+ }
+
+ /*
+ Stores the values of maximum size of the cache allowed when this cache
+ is configured. This corresponds to either
+ . max_binlog_cache_size or max_binlog_stmt_cache_size.
+ */
+ my_off_t saved_max_binlog_cache_size;
+
+ /*
+ Stores a pointer to the status variable that keeps track of the in-memory
+ cache usage. This corresponds to either
+ . binlog_cache_use or binlog_stmt_cache_use.
+ */
+ ulong *ptr_binlog_cache_use;
+
+ /*
+ Stores a pointer to the status variable that keeps track of the disk
+ cache usage. This corresponds to either
+ . binlog_cache_disk_use or binlog_stmt_cache_disk_use.
+ */
+ ulong *ptr_binlog_cache_disk_use;
+
+ /*
+ It truncates the cache to a certain position. This includes deleting the
+ pending event.
+ */
+ void truncate(my_off_t pos)
+ {
+ DBUG_PRINT("info", ("truncating to position %lu", (ulong) pos));
+ if (pending())
+ {
+ delete pending();
+ set_pending(0);
+ }
+ reinit_io_cache(&cache_log, WRITE_CACHE, pos, 0, 0);
+ cache_log.end_of_file= saved_max_binlog_cache_size;
+ }
+
+ binlog_cache_data& operator=(const binlog_cache_data& info);
+ binlog_cache_data(const binlog_cache_data& info);
+};
+
+class binlog_cache_mngr {
+public:
+ binlog_cache_mngr(my_off_t param_max_binlog_stmt_cache_size,
+ my_off_t param_max_binlog_cache_size,
+ ulong *param_ptr_binlog_stmt_cache_use,
+ ulong *param_ptr_binlog_stmt_cache_disk_use,
+ ulong *param_ptr_binlog_cache_use,
+ ulong *param_ptr_binlog_cache_disk_use)
+ : last_commit_pos_offset(0), using_xa(FALSE), xa_xid(0)
+ {
+ stmt_cache.set_binlog_cache_info(param_max_binlog_stmt_cache_size,
+ param_ptr_binlog_stmt_cache_use,
+ param_ptr_binlog_stmt_cache_disk_use);
+ trx_cache.set_binlog_cache_info(param_max_binlog_cache_size,
+ param_ptr_binlog_cache_use,
+ param_ptr_binlog_cache_disk_use);
+ last_commit_pos_file[0]= 0;
+ }
+
+ void reset(bool do_stmt, bool do_trx)
+ {
+ if (do_stmt)
+ stmt_cache.reset();
+ if (do_trx)
+ {
+ trx_cache.reset();
+ using_xa= FALSE;
+ last_commit_pos_file[0]= 0;
+ last_commit_pos_offset= 0;
+ }
+ }
+
+ binlog_cache_data* get_binlog_cache_data(bool is_transactional)
+ {
+ return (is_transactional ? &trx_cache : &stmt_cache);
+ }
+
+ IO_CACHE* get_binlog_cache_log(bool is_transactional)
+ {
+ return (is_transactional ? &trx_cache.cache_log : &stmt_cache.cache_log);
+ }
+
+ binlog_cache_data stmt_cache;
+
+ binlog_cache_data trx_cache;
+
/*
Binlog position for current transaction.
For START TRANSACTION WITH CONSISTENT SNAPSHOT, this is the binlog
@@ -300,6 +479,11 @@ public:
*/
bool using_xa;
my_xid xa_xid;
+
+private:
+
+ binlog_cache_mngr& operator=(const binlog_cache_mngr& info);
+ binlog_cache_mngr(const binlog_cache_mngr& info);
};
handlerton *binlog_hton;
@@ -319,8 +503,8 @@ bool LOGGER::is_log_table_enabled(uint log_table_type)
/* Check if a given table is opened log table */
-int check_if_log_table(uint db_len, const char *db, uint table_name_len,
- const char *table_name, uint check_if_opened)
+int check_if_log_table(size_t db_len, const char *db, size_t table_name_len,
+ const char *table_name, bool check_if_opened)
{
if (db_len == 5 &&
!(lower_case_table_names ?
@@ -412,7 +596,7 @@ bool Log_to_csv_event_handler::
bool need_rnd_end= FALSE;
uint field_index;
Silence_log_table_errors error_handler;
- Open_tables_state open_tables_backup;
+ Open_tables_backup open_tables_backup;
ulonglong save_thd_options;
bool save_time_zone_used;
DBUG_ENTER("log_general");
@@ -423,20 +607,16 @@ bool Log_to_csv_event_handler::
*/
save_time_zone_used= thd->time_zone_used;
- save_thd_options= thd->options;
- thd->options&= ~OPTION_BIN_LOG;
-
- bzero(& table_list, sizeof(TABLE_LIST));
- table_list.alias= table_list.table_name= GENERAL_LOG_NAME.str;
- table_list.table_name_length= GENERAL_LOG_NAME.length;
-
- table_list.lock_type= TL_WRITE_CONCURRENT_INSERT;
+ save_thd_options= thd->variables.option_bits;
+ thd->variables.option_bits&= ~OPTION_BIN_LOG;
- table_list.db= MYSQL_SCHEMA_NAME.str;
- table_list.db_length= MYSQL_SCHEMA_NAME.length;
+ 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,
+ TL_WRITE_CONCURRENT_INSERT);
/*
- 1) open_performance_schema_table generates an error of the
+ 1) open_log_table generates an error of the
table can not be opened or is corrupted.
2) "INSERT INTO general_log" can generate warning sometimes.
@@ -449,8 +629,7 @@ bool Log_to_csv_event_handler::
thd->push_internal_handler(& error_handler);
need_pop= TRUE;
- if (!(table= open_performance_schema_table(thd, & table_list,
- & open_tables_backup)))
+ if (!(table= open_log_table(thd, &table_list, &open_tables_backup)))
goto err;
need_close= TRUE;
@@ -530,9 +709,9 @@ err:
if (need_pop)
thd->pop_internal_handler();
if (need_close)
- close_performance_schema_table(thd, & open_tables_backup);
+ close_log_table(thd, &open_tables_backup);
- thd->options= save_thd_options;
+ thd->variables.option_bits= save_thd_options;
thd->time_zone_used= save_time_zone_used;
DBUG_RETURN(result);
}
@@ -578,7 +757,7 @@ bool Log_to_csv_event_handler::
bool need_close= FALSE;
bool need_rnd_end= FALSE;
Silence_log_table_errors error_handler;
- Open_tables_state open_tables_backup;
+ Open_tables_backup open_tables_backup;
CHARSET_INFO *client_cs= thd->variables.character_set_client;
bool save_time_zone_used;
long query_time= (long) min(query_utime/1000000, TIME_MAX_VALUE_SECONDS);
@@ -595,17 +774,12 @@ bool Log_to_csv_event_handler::
*/
save_time_zone_used= thd->time_zone_used;
- bzero(& table_list, sizeof(TABLE_LIST));
- table_list.alias= table_list.table_name= SLOW_LOG_NAME.str;
- table_list.table_name_length= SLOW_LOG_NAME.length;
+ 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,
+ TL_WRITE_CONCURRENT_INSERT);
- table_list.lock_type= TL_WRITE_CONCURRENT_INSERT;
-
- table_list.db= MYSQL_SCHEMA_NAME.str;
- table_list.db_length= MYSQL_SCHEMA_NAME.length;
-
- if (!(table= open_performance_schema_table(thd, & table_list,
- & open_tables_backup)))
+ if (!(table= open_log_table(thd, &table_list, &open_tables_backup)))
goto err;
need_close= TRUE;
@@ -719,7 +893,7 @@ err:
table->file->ha_release_auto_increment();
}
if (need_close)
- close_performance_schema_table(thd, & open_tables_backup);
+ close_log_table(thd, &open_tables_backup);
thd->time_zone_used= save_time_zone_used;
DBUG_RETURN(result);
}
@@ -729,36 +903,31 @@ int Log_to_csv_event_handler::
{
TABLE_LIST table_list;
TABLE *table;
+ LEX_STRING *UNINIT_VAR(log_name);
int result;
- Open_tables_state open_tables_backup;
+ Open_tables_backup open_tables_backup;
DBUG_ENTER("Log_to_csv_event_handler::activate_log");
- bzero(& table_list, sizeof(TABLE_LIST));
-
if (log_table_type == QUERY_LOG_GENERAL)
{
- table_list.alias= table_list.table_name= GENERAL_LOG_NAME.str;
- table_list.table_name_length= GENERAL_LOG_NAME.length;
+ log_name= &GENERAL_LOG_NAME;
}
else
{
DBUG_ASSERT(log_table_type == QUERY_LOG_SLOW);
- table_list.alias= table_list.table_name= SLOW_LOG_NAME.str;
- table_list.table_name_length= SLOW_LOG_NAME.length;
- }
-
- table_list.lock_type= TL_WRITE_CONCURRENT_INSERT;
- table_list.db= MYSQL_SCHEMA_NAME.str;
- table_list.db_length= MYSQL_SCHEMA_NAME.length;
+ 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= open_performance_schema_table(thd, & table_list,
- & open_tables_backup);
+ table= open_log_table(thd, &table_list, &open_tables_backup);
if (table)
{
result= 0;
- close_performance_schema_table(thd, & open_tables_backup);
+ close_log_table(thd, &open_tables_backup);
}
else
result= 1;
@@ -835,10 +1004,10 @@ bool Log_to_file_event_handler::init()
if (!is_initialized)
{
if (opt_slow_log)
- mysql_slow_log.open_slow_log(sys_var_slow_log_path.value);
+ mysql_slow_log.open_slow_log(opt_slow_logname);
if (opt_log)
- mysql_log.open_query_log(sys_var_general_log_path.value);
+ mysql_log.open_query_log(opt_logname);
is_initialized= TRUE;
}
@@ -862,13 +1031,6 @@ void Log_to_file_event_handler::flush()
mysql_slow_log.reopen_file();
}
-void Log_to_file_event_handler::flush_slow_log()
-{
- /* reopen slow log file */
- if (opt_slow_log)
- mysql_slow_log.reopen_file();
-}
-
/*
Log error with all enabled log event handlers
@@ -902,7 +1064,7 @@ bool LOGGER::error_log_print(enum loglevel level, const char *format,
void LOGGER::cleanup_base()
{
DBUG_ASSERT(inited == 1);
- rwlock_destroy(&LOCK_logger);
+ mysql_rwlock_destroy(&LOCK_logger);
if (table_log_handler)
{
table_log_handler->cleanup();
@@ -947,7 +1109,7 @@ void LOGGER::init_base()
init_error_log(LOG_FILE);
file_log_handler->init_pthread_objects();
- my_rwlock_init(&LOCK_logger, NULL);
+ mysql_rwlock_init(key_rwlock_LOCK_logger, &LOCK_logger);
}
@@ -979,7 +1141,12 @@ bool LOGGER::flush_logs(THD *thd)
}
-bool LOGGER::flush_slow_log(THD *thd)
+/**
+ Close and reopen the slow log (with locks).
+
+ @returns FALSE.
+*/
+bool LOGGER::flush_slow_log()
{
/*
Now we lock logger, as nobody should be able to use logging routines while
@@ -987,11 +1154,37 @@ bool LOGGER::flush_slow_log(THD *thd)
*/
logger.lock_exclusive();
- /* reopen log files */
- file_log_handler->flush_slow_log();
+ /* Reopen slow log file */
+ if (opt_slow_log)
+ file_log_handler->get_mysql_slow_log()->reopen_file();
- /* end of log flush */
+ /* End of log flush */
+ logger.unlock();
+
+ return 0;
+}
+
+
+/**
+ Close and reopen the general log (with locks).
+
+ @returns FALSE.
+*/
+bool LOGGER::flush_general_log()
+{
+ /*
+ Now we lock logger, as nobody should be able to use logging routines while
+ log tables are closed
+ */
+ logger.lock_exclusive();
+
+ /* Reopen general log file */
+ if (opt_log)
+ file_log_handler->get_mysql_log()->reopen_file();
+
+ /* End of log flush */
logger.unlock();
+
return 0;
}
@@ -1081,35 +1274,34 @@ bool LOGGER::general_log_write(THD *thd, enum enum_server_command command,
bool error= FALSE;
Log_event_handler **current_handler= general_log_handler_list;
char user_host_buff[MAX_USER_HOST_SIZE + 1];
- Security_context *sctx= thd->security_ctx;
uint user_host_len= 0;
my_hrtime_t current_time;
DBUG_ASSERT(thd);
- lock_shared();
- if (!opt_log)
+ user_host_len= make_user_name(thd, user_host_buff);
+
+ current_time= my_hrtime();
+
+ 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);
+
+ if (opt_log && log_command(thd, command))
{
+ lock_shared();
+ while (*current_handler)
+ error|= (*current_handler++)->
+ log_general(thd, current_time, user_host_buff,
+ user_host_len, thd->thread_id,
+ command_name[(uint) command].str,
+ command_name[(uint) command].length,
+ query, query_length,
+ thd->variables.character_set_client) || error;
unlock();
- return 0;
}
- user_host_len= strxnmov(user_host_buff, MAX_USER_HOST_SIZE,
- sctx->priv_user ? sctx->priv_user : "", "[",
- sctx->user ? sctx->user : "", "] @ ",
- sctx->host ? sctx->host : "", " [",
- sctx->ip ? sctx->ip : "", "]", NullS) -
- user_host_buff;
-
- current_time= my_hrtime();
- while (*current_handler)
- error|= (*current_handler++)->
- log_general(thd, current_time, user_host_buff,
- user_host_len, thd->thread_id,
- command_name[(uint) command].str,
- command_name[(uint) command].length,
- query, query_length,
- thd->variables.character_set_client) || error;
- unlock();
return error;
}
@@ -1130,7 +1322,7 @@ bool LOGGER::general_log_print(THD *thd, enum enum_server_command command,
return general_log_write(thd, command, message_buff, message_buff_len);
}
-void LOGGER::init_error_log(uint error_log_printer)
+void LOGGER::init_error_log(ulonglong error_log_printer)
{
if (error_log_printer & LOG_NONE)
{
@@ -1153,7 +1345,7 @@ void LOGGER::init_error_log(uint error_log_printer)
}
}
-void LOGGER::init_slow_log(uint slow_log_printer)
+void LOGGER::init_slow_log(ulonglong slow_log_printer)
{
if (slow_log_printer & LOG_NONE)
{
@@ -1178,7 +1370,7 @@ void LOGGER::init_slow_log(uint slow_log_printer)
}
}
-void LOGGER::init_general_log(uint general_log_printer)
+void LOGGER::init_general_log(ulonglong general_log_printer)
{
if (general_log_printer & LOG_NONE)
{
@@ -1215,7 +1407,7 @@ bool LOGGER::activate_log_handler(THD* thd, uint log_type)
{
file_log= file_log_handler->get_mysql_slow_log();
- file_log->open_slow_log(sys_var_slow_log_path.value);
+ file_log->open_slow_log(opt_slow_logname);
if (table_log_handler->activate_log(thd, QUERY_LOG_SLOW))
{
/* Error printed by open table in activate_log() */
@@ -1234,7 +1426,7 @@ bool LOGGER::activate_log_handler(THD* thd, uint log_type)
{
file_log= file_log_handler->get_mysql_log();
- file_log->open_query_log(sys_var_general_log_path.value);
+ file_log->open_query_log(opt_logname);
if (table_log_handler->activate_log(thd, QUERY_LOG_GENERAL))
{
/* Error printed by open table in activate_log() */
@@ -1291,9 +1483,9 @@ bool Log_to_csv_event_handler::init()
return 0;
}
-int LOGGER::set_handlers(uint error_log_printer,
- uint slow_log_printer,
- uint general_log_printer)
+int LOGGER::set_handlers(ulonglong error_log_printer,
+ ulonglong slow_log_printer,
+ ulonglong general_log_printer)
{
/* error log table is not supported yet */
DBUG_ASSERT(error_log_printer < LOG_TABLE);
@@ -1319,26 +1511,6 @@ int LOGGER::set_handlers(uint error_log_printer,
return 0;
}
-/**
- This function checks if a transactional talbe was updated by the
- current statement.
-
- @param thd The client thread that executed the current statement.
- @return
- @c true if a transactional table was updated, @false otherwise.
-*/
-static bool stmt_has_updated_trans_table(THD *thd)
-{
- Ha_trx_info *ha_info;
-
- for (ha_info= thd->transaction.stmt.ha_list; ha_info && ha_info->is_started(); ha_info= ha_info->next())
- {
- if (ha_info->is_trx_read_write() && ha_info->ht() != binlog_hton)
- return (TRUE);
- }
- return (FALSE);
-}
-
/*
Save position of binary log transaction cache.
@@ -1359,12 +1531,9 @@ binlog_trans_log_savepos(THD *thd, my_off_t *pos)
{
DBUG_ENTER("binlog_trans_log_savepos");
DBUG_ASSERT(pos != NULL);
- if (thd_get_ha_data(thd, binlog_hton) == NULL)
- thd->binlog_setup_trx_data();
- binlog_trx_data *const trx_data=
- (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
+ binlog_cache_mngr *const cache_mngr= thd->binlog_setup_trx_data();
DBUG_ASSERT(mysql_bin_log.is_open());
- *pos= trx_data->position();
+ *pos= cache_mngr->trx_cache.get_byte_position();
DBUG_PRINT("return", ("*pos: %lu", (ulong) *pos));
DBUG_VOID_RETURN;
}
@@ -1395,9 +1564,9 @@ binlog_trans_log_truncate(THD *thd, my_off_t pos)
/* Only true if binlog_trans_log_savepos() wasn't called before */
DBUG_ASSERT(pos != ~(my_off_t) 0);
- binlog_trx_data *const trx_data=
- (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
- trx_data->truncate(pos);
+ binlog_cache_mngr *const cache_mngr=
+ (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
+ cache_mngr->trx_cache.restore_savepoint(pos);
DBUG_VOID_RETURN;
}
@@ -1427,130 +1596,214 @@ int binlog_init(void *p)
static int binlog_close_connection(handlerton *hton, THD *thd)
{
- binlog_trx_data *const trx_data=
- (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
- DBUG_ASSERT(trx_data->empty());
+ binlog_cache_mngr *const cache_mngr=
+ (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
+ DBUG_ASSERT(cache_mngr->trx_cache.empty() && cache_mngr->stmt_cache.empty());
thd_set_ha_data(thd, binlog_hton, NULL);
- trx_data->~binlog_trx_data();
- my_free((uchar*)trx_data, MYF(0));
+ cache_mngr->~binlog_cache_mngr();
+ my_free(cache_mngr);
return 0;
}
/*
- End a transaction, writing events to the binary log.
+ This function flushes a cache upon commit/rollback.
SYNOPSIS
- binlog_flush_trx_cache()
+ binlog_flush_cache()
- thd The thread whose transaction should be ended
- trx_data Pointer to the transaction data to use
- all True if the entire transaction should be ended, false if
- only the statement transaction should be ended.
- end_ev The end event to use (COMMIT, ROLLBACK, or commit XID)
+ thd The thread whose transaction should be ended
+ cache_mngr Pointer to the binlog_cache_mngr to use
+ all True if the entire transaction should be ended, false if
+ only the statement transaction should be ended.
+ end_ev The end event to use (COMMIT, ROLLBACK, or commit XID)
+ using_stmt True if the statement cache should be flushed
+ using_trx True if the transaction cache should be flushed
DESCRIPTION
- End the currently open transaction. The transaction can be either
+ End the currently transaction or statement. The transaction can be either
a real transaction or a statement transaction.
This can be to commit a transaction, with a COMMIT query event or an XA
commit XID event. But it can also be to rollback a transaction with a
ROLLBACK query event, used for rolling back transactions which also
- contain updates to non-transactional tables.
+ contain updates to non-transactional tables. Or it can be a flush of
+ a statement cache.
*/
static int
-binlog_flush_trx_cache(THD *thd, binlog_trx_data *trx_data,
- Log_event *end_ev, bool all)
+binlog_flush_cache(THD *thd, binlog_cache_mngr *cache_mngr,
+ Log_event *end_ev, bool all, bool using_stmt,
+ bool using_trx)
{
- DBUG_ENTER("binlog_flush_trx_cache");
- IO_CACHE *trans_log= &trx_data->trans_log;
- DBUG_PRINT("info", ("thd->options={ %s%s}",
- FLAGSTR(thd->options, OPTION_NOT_AUTOCOMMIT),
- FLAGSTR(thd->options, OPTION_BEGIN)));
-
- if (thd->binlog_flush_pending_rows_event(TRUE))
- DBUG_RETURN(1);
-
- /*
- Doing a commit or a rollback including non-transactional tables,
- i.e., ending a transaction where we might write the transaction
- cache to the binary log.
-
- We can always end the statement when ending a transaction since
- transactions are not allowed inside stored functions. If they
- were, we would have to ensure that we're not ending a statement
- inside a stored function.
- */
- int error= mysql_bin_log.write_transaction_to_binlog(thd, trx_data,
- end_ev, all);
-
- trx_data->reset();
+ int error= 0;
+ DBUG_ENTER("binlog_flush_cache");
- statistic_increment(binlog_cache_use, &LOCK_status);
- if (trans_log->disk_writes != 0)
+ if ((using_stmt && !cache_mngr->stmt_cache.empty()) ||
+ (using_trx && !cache_mngr->trx_cache.empty()))
{
- statistic_increment(binlog_cache_disk_use, &LOCK_status);
- trans_log->disk_writes= 0;
+ if (using_stmt && thd->binlog_flush_pending_rows_event(TRUE, FALSE))
+ DBUG_RETURN(1);
+ if (using_trx && thd->binlog_flush_pending_rows_event(TRUE, TRUE))
+ DBUG_RETURN(1);
+
+ /*
+ Doing a commit or a rollback including non-transactional tables,
+ i.e., ending a transaction where we might write the transaction
+ cache to the binary log.
+
+ We can always end the statement when ending a transaction since
+ transactions are not allowed inside stored functions. If they
+ were, we would have to ensure that we're not ending a statement
+ inside a stored function.
+ */
+ error= mysql_bin_log.write_transaction_to_binlog(thd, cache_mngr,
+ end_ev, all,
+ using_stmt, using_trx);
}
+ cache_mngr->reset(using_stmt, using_trx);
- DBUG_ASSERT(thd->binlog_get_pending_rows_event() == NULL);
+ DBUG_ASSERT((!using_stmt || cache_mngr->stmt_cache.empty()) &&
+ (!using_trx || cache_mngr->trx_cache.empty()));
DBUG_RETURN(error);
}
-/*
- Discard a transaction, ie. ROLLBACK with only transactional table updates.
- SYNOPSIS
- binlog_truncate_trx_cache()
+/**
+ This function flushes the stmt-cache upon commit.
- thd The thread whose transaction should be ended
- trx_data Pointer to the transaction data to use
- all True if the entire transaction should be ended, false if
- only the statement transaction should be ended.
+ @param thd The thread whose transaction should be flushed
+ @param cache_mngr Pointer to the cache manager
- DESCRIPTION
+ @return
+ nonzero if an error pops up when flushing the cache.
+*/
+static inline int
+binlog_commit_flush_stmt_cache(THD *thd, bool all,
+ binlog_cache_mngr *cache_mngr)
+{
+ Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"),
+ FALSE, TRUE, TRUE, 0);
+ return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, FALSE));
+}
- Rollback (and end) a transaction that only modifies transactional
- tables. The transaction can be either a real transaction (if 'all' is
- true) or a statement transaction (if 'all' is false).
+/**
+ This function flushes the trx-cache upon commit.
- The transaction cache will be truncated to either just before the last
- opened statement transaction (if 'all' is false), or reset completely (if
- 'all' is true).
- */
+ @param thd The thread whose transaction should be flushed
+ @param cache_mngr Pointer to the cache manager
+
+ @return
+ nonzero if an error pops up when flushing the cache.
+*/
+static inline int
+binlog_commit_flush_trx_cache(THD *thd, bool all, binlog_cache_mngr *cache_mngr)
+{
+ Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"),
+ TRUE, TRUE, TRUE, 0);
+ return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, FALSE, TRUE));
+}
+
+/**
+ This function flushes the trx-cache upon rollback.
+
+ @param thd The thread whose transaction should be flushed
+ @param cache_mngr Pointer to the cache manager
+
+ @return
+ nonzero if an error pops up when flushing the cache.
+*/
+static inline int
+binlog_rollback_flush_trx_cache(THD *thd, bool all,
+ binlog_cache_mngr *cache_mngr)
+{
+ Query_log_event end_evt(thd, STRING_WITH_LEN("ROLLBACK"),
+ TRUE, TRUE, TRUE, 0);
+ return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, FALSE, TRUE));
+}
+
+/**
+ This function flushes the trx-cache upon commit.
+
+ @param thd The thread whose transaction should be flushed
+ @param cache_mngr Pointer to the cache manager
+ @param xid Transaction Id
+
+ @return
+ nonzero if an error pops up when flushing the cache.
+*/
+static inline int
+binlog_commit_flush_xid_caches(THD *thd, binlog_cache_mngr *cache_mngr,
+ bool all, my_xid xid)
+{
+ if (xid)
+ {
+ Xid_log_event end_evt(thd, xid, TRUE);
+ return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
+ }
+ else
+ {
+ /*
+ Empty xid occurs in XA COMMIT ... ONE PHASE.
+ In this case, we do not have a MySQL xid for the transaction, and the
+ external XA transaction coordinator will have to handle recovery if
+ needed. So we end the transaction with a plain COMMIT query event.
+ */
+ Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"),
+ TRUE, TRUE, TRUE, 0);
+ return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
+ }
+}
+
+/**
+ This function truncates the transactional cache upon committing or rolling
+ back either a transaction or a statement.
+
+ @param thd The thread whose transaction should be flushed
+ @param cache_mngr Pointer to the cache data to be flushed
+ @param all @c true means truncate the transaction, otherwise the
+ statement must be truncated.
+
+ @return
+ nonzero if an error pops up when truncating the transactional cache.
+*/
static int
-binlog_truncate_trx_cache(THD *thd, binlog_trx_data *trx_data, bool all)
+binlog_truncate_trx_cache(THD *thd, binlog_cache_mngr *cache_mngr, bool all)
{
DBUG_ENTER("binlog_truncate_trx_cache");
- int error= 0;
- DBUG_PRINT("enter", ("transaction: %s", all ? "all" : "stmt"));
- DBUG_PRINT("info", ("thd->options={ %s%s}",
- FLAGSTR(thd->options, OPTION_NOT_AUTOCOMMIT),
- FLAGSTR(thd->options, OPTION_BEGIN)));
-
+ int error=0;
/*
- ROLLBACK with nothing to replicate: i.e., rollback of only transactional
- tables.
+ This function handles transactional changes and as such this flag
+ equals to true.
*/
+ bool const is_transactional= TRUE;
+ DBUG_PRINT("info", ("thd->options={ %s %s}, transaction: %s",
+ FLAGSTR(thd->variables.option_bits, OPTION_NOT_AUTOCOMMIT),
+ FLAGSTR(thd->variables.option_bits, OPTION_BEGIN),
+ all ? "all" : "stmt"));
+
+ thd->binlog_remove_pending_rows_event(TRUE, is_transactional);
/*
If rolling back an entire transaction or a single statement not
inside a transaction, we reset the transaction cache.
-
- If rolling back a statement in a transaction, we truncate the
- transaction cache to remove the statement.
- */
- thd->binlog_remove_pending_rows_event(TRUE);
- if (all || !(thd->options & (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT)))
+ */
+ if (ending_trans(thd, all))
{
- if (trx_data->has_incident())
+ if (cache_mngr->trx_cache.has_incident())
error= mysql_bin_log.write_incident(thd);
- trx_data->reset();
+
+ thd->clear_binlog_table_maps();
+
+ cache_mngr->reset(false, true);
}
- else // ...statement
- trx_data->truncate(trx_data->before_stmt_pos);
+ /*
+ If rolling back a statement in a transaction, we truncate the
+ transaction cache to remove the statement.
+ */
+ else
+ cache_mngr->trx_cache.restore_prev_position();
- DBUG_ASSERT(thd->binlog_get_pending_rows_event() == NULL);
+ DBUG_ASSERT(thd->binlog_get_pending_rows_event(is_transactional) == NULL);
DBUG_RETURN(error);
}
@@ -1568,8 +1821,7 @@ static int binlog_prepare(handlerton *hton, THD *thd, bool all)
/**
This function is called once after each statement.
- It has the responsibility to flush the transaction cache to the
- binlog file on commits.
+ It has the responsibility to flush the caches to the binary log on commits.
@param hton The binlog handlerton.
@param thd The client thread that executes the transaction.
@@ -1582,53 +1834,50 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
{
int error= 0;
DBUG_ENTER("binlog_commit");
- binlog_trx_data *const trx_data=
- (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
-
- if (trx_data->empty())
- {
- // we're here because trans_log was flushed in MYSQL_BIN_LOG::log_xid()
- trx_data->reset();
- DBUG_RETURN(0);
- }
+ binlog_cache_mngr *const cache_mngr=
+ (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
- /*
- We flush the cache if:
-
- - we are committing a transaction or;
- - no statement was committed before and just non-transactional
- tables were updated.
-
- Otherwise, we collect the changes.
- */
DBUG_PRINT("debug",
- ("all: %d, empty: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s",
+ ("all: %d, in_transaction: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s",
all,
- YESNO(trx_data->empty()),
+ YESNO(thd->in_multi_stmt_transaction_mode()),
YESNO(thd->transaction.all.modified_non_trans_table),
YESNO(thd->transaction.stmt.modified_non_trans_table)));
- if (ending_trans(thd, all) ||
- (trans_has_no_stmt_committed(thd, all) &&
- !stmt_has_updated_trans_table(thd) && stmt_has_updated_non_trans_table(thd)))
+
+ if (!cache_mngr->stmt_cache.empty())
{
- Query_log_event end_ev(thd, STRING_WITH_LEN("COMMIT"), TRUE, TRUE, 0);
- error= binlog_flush_trx_cache(thd, trx_data, &end_ev, all);
+ error= binlog_commit_flush_stmt_cache(thd, all, cache_mngr);
}
- trx_data->at_least_one_stmt_committed = my_b_tell(&trx_data->trans_log) > 0;
+ if (cache_mngr->trx_cache.empty())
+ {
+ /*
+ we're here because cache_log was flushed in MYSQL_BIN_LOG::log_xid()
+ */
+ cache_mngr->reset(false, true);
+ DBUG_RETURN(error);
+ }
+ /*
+ We commit the transaction if:
+ - We are not in a transaction and committing a statement, or
+ - We are in a transaction and a full transaction is committed.
+ Otherwise, we accumulate the changes.
+ */
+ if (!error && ending_trans(thd, all))
+ error= binlog_commit_flush_trx_cache(thd, all, cache_mngr);
+
+ /*
+ This is part of the stmt rollback.
+ */
if (!all)
- trx_data->before_stmt_pos = MY_OFF_T_UNDEF; // part of the stmt commit
+ cache_mngr->trx_cache.set_prev_position(MY_OFF_T_UNDEF);
+
DBUG_RETURN(error);
}
/**
- This function is called when a transaction involving a transactional
- table is rolled back.
-
- It has the responsibility to flush the transaction cache to the
- binlog file. However, if the transaction does not involve
- non-transactional tables, nothing needs to be logged.
+ This function is called when a transaction or a statement is rolled back.
@param hton The binlog handlerton.
@param thd The client thread that executes the transaction.
@@ -1640,19 +1889,38 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
static int binlog_rollback(handlerton *hton, THD *thd, bool all)
{
DBUG_ENTER("binlog_rollback");
- int error=0;
- binlog_trx_data *const trx_data=
- (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
-
- if (trx_data->empty()) {
- trx_data->reset();
- DBUG_RETURN(0);
- }
+ int error= 0;
+ binlog_cache_mngr *const cache_mngr=
+ (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
DBUG_PRINT("debug", ("all: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s",
YESNO(all),
YESNO(thd->transaction.all.modified_non_trans_table),
YESNO(thd->transaction.stmt.modified_non_trans_table)));
+
+ /*
+ If an incident event is set we do not flush the content of the statement
+ cache because it may be corrupted.
+ */
+ if (cache_mngr->stmt_cache.has_incident())
+ {
+ error= mysql_bin_log.write_incident(thd);
+ cache_mngr->reset(true, false);
+ }
+ else if (!cache_mngr->stmt_cache.empty())
+ {
+ error= binlog_commit_flush_stmt_cache(thd, all, cache_mngr);
+ }
+
+ if (cache_mngr->trx_cache.empty())
+ {
+ /*
+ we're here because cache_log was flushed in MYSQL_BIN_LOG::log_xid()
+ */
+ cache_mngr->reset(false, true);
+ DBUG_RETURN(error);
+ }
+
if (mysql_bin_log.check_write_error(thd))
{
/*
@@ -1663,68 +1931,61 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
*/
DBUG_ASSERT(!all);
/*
- We reach this point if either only transactional tables were modified or
- the effect of a statement that did not get into the binlog needs to be
- rolled back. In the latter case, if a statement changed non-transactional
- tables or had the OPTION_KEEP_LOG associated, we write an incident event
- to the binlog in order to stop slaves and notify users that some changes
- on the master did not get into the binlog and slaves will be inconsistent.
- On the other hand, if a statement is transactional, we just safely roll it
- back.
+ We reach this point if the effect of a statement did not properly get into
+ a cache and need to be rolled back.
*/
- if ((stmt_has_updated_non_trans_table(thd) ||
- (thd->options & OPTION_KEEP_LOG)) &&
- mysql_bin_log.check_write_error(thd))
- trx_data->set_incident();
- error= binlog_truncate_trx_cache(thd, trx_data, all);
+ error |= binlog_truncate_trx_cache(thd, cache_mngr, all);
}
- else
- {
- /*
- We flush the cache with a rollback, wrapped in a begin/rollback if:
- . aborting a transaction that modified a non-transactional table or
- the OPTION_KEEP_LOG is activate.
- . aborting a statement that modified both transactional and
- non-transactional tables but which is not in the boundaries of any
- transaction or there was no early change;
+ else if (!error)
+ {
+ /*
+ We flush the cache wrapped in a beging/rollback if:
+ . aborting a single or multi-statement transaction and;
+ . the OPTION_KEEP_LOG is active or;
+ . the format is STMT and a non-trans table was updated or;
+ . the format is MIXED and a temporary non-trans table was
+ updated or;
+ . the format is MIXED, non-trans table was updated and
+ aborting a single statement transaction;
*/
- if ((ending_trans(thd, all) &&
- (trans_has_updated_non_trans_table(thd) ||
- (thd->options & OPTION_KEEP_LOG))) ||
- (trans_has_no_stmt_committed(thd, all) &&
- stmt_has_updated_non_trans_table(thd) &&
- thd->current_stmt_binlog_row_based))
- {
- Query_log_event end_ev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, TRUE, 0);
- error= binlog_flush_trx_cache(thd, trx_data, &end_ev, all);
- }
+ if (ending_trans(thd, all) &&
+ ((thd->variables.option_bits & OPTION_KEEP_LOG) ||
+ (trans_has_updated_non_trans_table(thd) &&
+ thd->variables.binlog_format == BINLOG_FORMAT_STMT) ||
+ (cache_mngr->trx_cache.changes_to_non_trans_temp_table() &&
+ thd->variables.binlog_format == BINLOG_FORMAT_MIXED) ||
+ (trans_has_updated_non_trans_table(thd) &&
+ ending_single_stmt_trans(thd,all) &&
+ thd->variables.binlog_format == BINLOG_FORMAT_MIXED)))
+ error= binlog_rollback_flush_trx_cache(thd, all, cache_mngr);
/*
- Otherwise, we simply truncate the cache as there is no change on
- non-transactional tables as follows.
+ Truncate the cache if:
+ . aborting a single or multi-statement transaction or;
+ . the OPTION_KEEP_LOG is not active and;
+ . the format is not STMT or no non-trans table was
+ updated and;
+ . the format is not MIXED or no temporary non-trans table
+ was updated.
*/
else if (ending_trans(thd, all) ||
- (!(thd->options & OPTION_KEEP_LOG) && !stmt_has_updated_non_trans_table(thd)))
- error= binlog_truncate_trx_cache(thd, trx_data, all);
+ (!(thd->variables.option_bits & OPTION_KEEP_LOG) &&
+ (!stmt_has_updated_non_trans_table(thd) ||
+ thd->variables.binlog_format != BINLOG_FORMAT_STMT) &&
+ (!cache_mngr->trx_cache.changes_to_non_trans_temp_table() ||
+ thd->variables.binlog_format != BINLOG_FORMAT_MIXED)))
+ error= binlog_truncate_trx_cache(thd, cache_mngr, all);
}
- if (!all)
- trx_data->before_stmt_pos = MY_OFF_T_UNDEF; // part of the stmt rollback
- DBUG_RETURN(error);
-}
-/**
- Cleanup the cache.
-
- @param thd The client thread that wants to clean up the cache.
-*/
-void MYSQL_BIN_LOG::reset_gathered_updates(THD *thd)
-{
- binlog_trx_data *const trx_data=
- (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
+ /*
+ This is part of the stmt rollback.
+ */
+ if (!all)
+ cache_mngr->trx_cache.set_prev_position(MY_OFF_T_UNDEF);
- trx_data->reset();
+ DBUG_RETURN(error);
}
-void MYSQL_BIN_LOG::set_write_error(THD *thd)
+void MYSQL_BIN_LOG::set_write_error(THD *thd, bool is_transactional)
{
DBUG_ENTER("MYSQL_BIN_LOG::set_write_error");
@@ -1734,9 +1995,20 @@ void MYSQL_BIN_LOG::set_write_error(THD *thd)
DBUG_VOID_RETURN;
if (my_errno == EFBIG)
- my_message(ER_TRANS_CACHE_FULL, ER(ER_TRANS_CACHE_FULL), MYF(MY_WME));
+ {
+ if (is_transactional)
+ {
+ my_message(ER_TRANS_CACHE_FULL, ER(ER_TRANS_CACHE_FULL), MYF(MY_WME));
+ }
+ else
+ {
+ my_message(ER_STMT_CACHE_FULL, ER(ER_STMT_CACHE_FULL), MYF(MY_WME));
+ }
+ }
else
+ {
my_error(ER_ERROR_ON_WRITE, MYF(MY_WME), name, errno);
+ }
DBUG_VOID_RETURN;
}
@@ -1750,9 +2022,10 @@ bool MYSQL_BIN_LOG::check_write_error(THD *thd)
if (!thd->is_error())
DBUG_RETURN(checked);
- switch (thd->main_da.sql_errno())
+ switch (thd->stmt_da->sql_errno())
{
case ER_TRANS_CACHE_FULL:
+ case ER_STMT_CACHE_FULL:
case ER_ERROR_ON_WRITE:
case ER_BINLOG_LOGGING_IMPOSSIBLE:
checked= TRUE;
@@ -1794,15 +2067,17 @@ static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv)
binlog_trans_log_savepos(thd, (my_off_t*) sv);
/* Write it to the binary log */
- String log_query;
- if (log_query.append(STRING_WITH_LEN("SAVEPOINT ")) ||
+ char buf[1024];
+ String log_query(buf, sizeof(buf), &my_charset_bin);
+ if (log_query.copy(STRING_WITH_LEN("SAVEPOINT "), &my_charset_bin) ||
append_identifier(thd, &log_query,
thd->lex->ident.str, thd->lex->ident.length))
DBUG_RETURN(1);
int errcode= query_error_code(thd, thd->killed == NOT_KILLED);
Query_log_event qinfo(thd, log_query.ptr(), log_query.length(),
- TRUE, TRUE, errcode);
- DBUG_RETURN(mysql_bin_log.write(&qinfo));
+ TRUE, FALSE, TRUE, errcode);
+ int ret= mysql_bin_log.write(&qinfo);
+ DBUG_RETURN(ret);
}
static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
@@ -1814,17 +2089,18 @@ static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
non-transactional table. Otherwise, truncate the binlog cache starting
from the SAVEPOINT command.
*/
- if (unlikely(trans_has_updated_non_trans_table(thd) ||
- (thd->options & OPTION_KEEP_LOG)))
+ if (unlikely(trans_has_updated_non_trans_table(thd) ||
+ (thd->variables.option_bits & OPTION_KEEP_LOG)))
{
- String log_query;
- if (log_query.append(STRING_WITH_LEN("ROLLBACK TO ")) ||
+ 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))
DBUG_RETURN(1);
int errcode= query_error_code(thd, thd->killed == NOT_KILLED);
Query_log_event qinfo(thd, log_query.ptr(), log_query.length(),
- TRUE, TRUE, errcode);
+ TRUE, FALSE, TRUE, errcode);
DBUG_RETURN(mysql_bin_log.write(&qinfo));
}
binlog_trans_log_truncate(thd, *(my_off_t*)sv);
@@ -1858,8 +2134,9 @@ File open_binlog(IO_CACHE *log, const char *log_file_name, const char **errmsg)
File file;
DBUG_ENTER("open_binlog");
- if ((file = my_open(log_file_name, O_RDONLY | O_BINARY | O_SHARE,
- MYF(MY_WME))) < 0)
+ if ((file= mysql_file_open(key_file_binlog,
+ log_file_name, O_RDONLY | O_BINARY | O_SHARE,
+ MYF(MY_WME))) < 0)
{
sql_print_error("Failed to open log (file '%s', errno %d)",
log_file_name, my_errno);
@@ -1881,13 +2158,13 @@ File open_binlog(IO_CACHE *log, const char *log_file_name, const char **errmsg)
err:
if (file >= 0)
{
- my_close(file,MYF(0));
+ mysql_file_close(file, MYF(0));
end_io_cache(log);
}
DBUG_RETURN(-1);
}
-#ifdef __NT__
+#ifdef _WIN32
static int eventSource = 0;
static void setup_windows_event_source()
@@ -1922,48 +2199,52 @@ static void setup_windows_event_source()
RegCloseKey(hRegKey);
}
-#endif /* __NT__ */
+#endif /* _WIN32 */
/**
Find a unique filename for 'filename.#'.
- Set '#' to a number as low as possible.
+ Set '#' to the number next to the maximum found in the most
+ recent log file extension.
+
+ This function will return nonzero if: (i) the generated name
+ exceeds FN_REFLEN; (ii) if the number of extensions is exhausted;
+ or (iii) some other error happened while examining the filesystem.
@return
- nonzero if not possible to get unique filename
+ nonzero if not possible to get unique filename.
*/
static int find_uniq_filename(char *name)
{
- long number;
uint i;
- char buff[FN_REFLEN];
+ char buff[FN_REFLEN], ext_buf[FN_REFLEN];
struct st_my_dir *dir_info;
reg1 struct fileinfo *file_info;
- ulong max_found=0;
+ ulong max_found= 0, next= 0, number= 0;
size_t buf_length, length;
char *start, *end;
+ int error= 0;
DBUG_ENTER("find_uniq_filename");
- LINT_INIT(number);
length= dirname_part(buff, name, &buf_length);
start= name + length;
end= strend(start);
*end='.';
- length= (size_t) (end-start+1);
+ length= (size_t) (end - start + 1);
if ((DBUG_EVALUATE_IF("error_unique_log_filename", 1,
- !(dir_info = my_dir(buff,MYF(MY_DONT_SORT))))))
+ !(dir_info= my_dir(buff,MYF(MY_DONT_SORT))))))
{ // This shouldn't happen
strmov(end,".1"); // use name+1
DBUG_RETURN(1);
}
file_info= dir_info->dir_entry;
- for (i=dir_info->number_off_files ; i-- ; file_info++)
+ for (i= dir_info->number_off_files ; i-- ; file_info++)
{
- if (memcmp(file_info->name, start, length) == 0 &&
+ if (strncmp(file_info->name, start, length) == 0 &&
test_if_number(file_info->name+length, &number,0))
{
set_if_bigger(max_found,(ulong) number);
@@ -1971,8 +2252,52 @@ static int find_uniq_filename(char *name)
}
my_dirend(dir_info);
+ /* check if reached the maximum possible extension number */
+ if (max_found == MAX_LOG_UNIQUE_FN_EXT)
+ {
+ sql_print_error("Log filename extension number exhausted: %06lu. \
+Please fix this by archiving old logs and \
+updating the index files.", max_found);
+ error= 1;
+ goto end;
+ }
+
+ next= max_found + 1;
+ if (sprintf(ext_buf, "%06lu", next)<0)
+ {
+ error= 1;
+ goto end;
+ }
*end++='.';
- DBUG_RETURN((sprintf(end,"%06ld",max_found+1) < 0));
+
+ /*
+ Check if the generated extension size + the file name exceeds the
+ buffer size used. If one did not check this, then the filename might be
+ truncated, resulting in error.
+ */
+ if (((strlen(ext_buf) + (end - name)) >= FN_REFLEN))
+ {
+ sql_print_error("Log filename too large: %s%s (%zu). \
+Please fix this by archiving old logs and updating the \
+index files.", name, ext_buf, (strlen(ext_buf) + (end - name)));
+ error= 1;
+ goto end;
+ }
+
+ if (sprintf(end, "%06lu", next)<0)
+ {
+ error= 1;
+ goto end;
+ }
+
+ /* print warning if reaching the end of available extensions. */
+ if ((next > (MAX_LOG_UNIQUE_FN_EXT - LOG_WARN_UNIQUE_FN_EXT_LEFT)))
+ sql_print_warning("Next log extension: %lu. \
+Remaining log filename extensions: %lu. \
+Please consider archiving some logs.", next, (MAX_LOG_UNIQUE_FN_EXT - next));
+
+end:
+ DBUG_RETURN(error);
}
@@ -2024,10 +2349,15 @@ bool MYSQL_LOG::init_and_set_log_file_name(const char *log_name,
1 error
*/
-bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
+bool MYSQL_LOG::open(
+#ifdef HAVE_PSI_INTERFACE
+ PSI_file_key log_file_key,
+#endif
+ const char *log_name, enum_log_type log_type_arg,
const char *new_name, enum cache_type io_cache_type_arg)
{
char buff[FN_REFLEN];
+ MY_STAT f_stat;
File file= -1;
int open_flags= O_CREAT | O_BINARY;
DBUG_ENTER("MYSQL_LOG::open");
@@ -2045,6 +2375,10 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
log_type_arg, io_cache_type_arg))
goto err;
+ /* File is regular writable file */
+ if (my_stat(log_file_name, &f_stat, MYF(0)) && !MY_S_ISREG(f_stat.st_mode))
+ goto err;
+
if (io_cache_type == SEQ_READ_APPEND)
open_flags |= O_RDWR | O_APPEND;
else
@@ -2052,10 +2386,16 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
db[0]= 0;
- if ((file= my_open(log_file_name, open_flags,
- MYF(MY_WME | ME_WAITTANG))) < 0 ||
+#ifdef HAVE_PSI_INTERFACE
+ /* Keep the key for reopen */
+ m_log_file_key= log_file_key;
+#endif
+
+ if ((file= mysql_file_open(log_file_key,
+ log_file_name, open_flags,
+ MYF(MY_WME | ME_WAITTANG))) < 0 ||
init_io_cache(&log_file, file, IO_SIZE, io_cache_type,
- my_tell(file, MYF(MY_WME)), 0,
+ mysql_file_tell(file, MYF(MY_WME)), 0,
MYF(MY_WME | MY_NABP |
((log_type == LOG_BIN) ? MY_WAIT_IF_FULL : 0))))
goto err;
@@ -2067,7 +2407,7 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
#ifdef EMBEDDED_LIBRARY
"embedded library\n",
my_progname, server_version, MYSQL_COMPILATION_COMMENT
-#elif __NT__
+#elif _WIN32
"started with:\nTCP Port: %d, Named Pipe: %s\n",
my_progname, server_version, MYSQL_COMPILATION_COMMENT,
mysqld_port, mysqld_unix_port
@@ -2093,9 +2433,10 @@ Turning logging off for the whole duration of the MySQL server process. \
To turn it on again: fix the cause, \
shutdown the MySQL server and restart it.", name, errno);
if (file >= 0)
- my_close(file, MYF(0));
+ mysql_file_close(file, MYF(0));
end_io_cache(&log_file);
- safeFree(name);
+ my_free(name);
+ name= NULL;
log_state= LOG_CLOSED;
DBUG_RETURN(1);
}
@@ -2117,7 +2458,7 @@ void MYSQL_LOG::init_pthread_objects()
{
DBUG_ASSERT(inited == 0);
inited= 1;
- (void) pthread_mutex_init(&LOCK_log, MY_MUTEX_INIT_SLOW);
+ mysql_mutex_init(key_LOG_LOCK_log, &LOCK_log, MY_MUTEX_INIT_SLOW);
}
/*
@@ -2125,9 +2466,10 @@ void MYSQL_LOG::init_pthread_objects()
SYNOPSIS
close()
- exiting Bitmask. For the slow and general logs the only used bit is
- LOG_CLOSE_TO_BE_OPENED. This is used if we intend to call
- open at once after close.
+ exiting Bitmask. LOG_CLOSE_TO_BE_OPENED is used if we intend to call
+ open at once after close. LOG_CLOSE_DELAYED_CLOSE is used for
+ binlog rotation, to delay actual close of the old file until
+ we have successfully created the new file.
NOTES
One can do an open on the object at once after doing a close.
@@ -2142,13 +2484,14 @@ void MYSQL_LOG::close(uint exiting)
{
end_io_cache(&log_file);
- if (my_sync(log_file.file, MYF(MY_WME)) && ! write_error)
+ if (mysql_file_sync(log_file.file, MYF(MY_WME)) && ! write_error)
{
write_error= 1;
sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
}
- if (my_close(log_file.file, MYF(MY_WME)) && ! write_error)
+ if (!(exiting & LOG_CLOSE_DELAYED_CLOSE) &&
+ mysql_file_close(log_file.file, MYF(MY_WME)) && ! write_error)
{
write_error= 1;
sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
@@ -2156,7 +2499,8 @@ void MYSQL_LOG::close(uint exiting)
}
log_state= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED : LOG_CLOSED;
- safeFree(name);
+ my_free(name);
+ name= NULL;
DBUG_VOID_RETURN;
}
@@ -2168,7 +2512,7 @@ void MYSQL_LOG::cleanup()
if (inited)
{
inited= 0;
- (void) pthread_mutex_destroy(&LOCK_log);
+ mysql_mutex_destroy(&LOCK_log);
close(0);
}
DBUG_VOID_RETURN;
@@ -2184,9 +2528,10 @@ int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name)
{
if (find_uniq_filename(new_name))
{
- my_printf_error(ER_NO_UNIQUE_LOGFILE, ER(ER_NO_UNIQUE_LOGFILE),
- MYF(ME_FATALERROR), log_name);
- sql_print_error(ER(ER_NO_UNIQUE_LOGFILE), log_name);
+ if (current_thd)
+ my_printf_error(ER_NO_UNIQUE_LOGFILE, ER(ER_NO_UNIQUE_LOGFILE),
+ MYF(ME_FATALERROR), log_name);
+ sql_print_error(ER_DEFAULT(ER_NO_UNIQUE_LOGFILE), log_name);
return 1;
}
}
@@ -2218,7 +2563,7 @@ void MYSQL_QUERY_LOG::reopen_file()
DBUG_VOID_RETURN;
}
- pthread_mutex_lock(&LOCK_log);
+ mysql_mutex_lock(&LOCK_log);
save_name= name;
name= 0; // Don't free name
@@ -2228,10 +2573,14 @@ void MYSQL_QUERY_LOG::reopen_file()
Note that at this point, log_state != LOG_CLOSED (important for is_open()).
*/
- open(save_name, log_type, 0, io_cache_type);
- my_free(save_name, MYF(0));
+ open(
+#ifdef HAVE_PSI_INTERFACE
+ m_log_file_key,
+#endif
+ save_name, log_type, 0, io_cache_type);
+ my_free(save_name);
- pthread_mutex_unlock(&LOCK_log);
+ mysql_mutex_unlock(&LOCK_log);
DBUG_VOID_RETURN;
}
@@ -2273,7 +2622,7 @@ bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host,
struct tm start;
uint time_buff_len= 0;
- (void) pthread_mutex_lock(&LOCK_log);
+ mysql_mutex_lock(&LOCK_log);
/* Test if someone closed between the is_open test and lock */
if (is_open())
@@ -2322,7 +2671,7 @@ bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host,
goto err;
}
- (void) pthread_mutex_unlock(&LOCK_log);
+ mysql_mutex_unlock(&LOCK_log);
return FALSE;
err:
@@ -2331,7 +2680,7 @@ err:
write_error= 1;
sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
}
- (void) pthread_mutex_unlock(&LOCK_log);
+ mysql_mutex_unlock(&LOCK_log);
return TRUE;
}
@@ -2373,11 +2722,11 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time,
bool error= 0;
DBUG_ENTER("MYSQL_QUERY_LOG::write");
- (void) pthread_mutex_lock(&LOCK_log);
+ mysql_mutex_lock(&LOCK_log);
if (!is_open())
{
- (void) pthread_mutex_unlock(&LOCK_log);
+ mysql_mutex_unlock(&LOCK_log);
DBUG_RETURN(0);
}
@@ -2509,7 +2858,7 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time,
}
}
}
- (void) pthread_mutex_unlock(&LOCK_log);
+ mysql_mutex_unlock(&LOCK_log);
DBUG_RETURN(error);
}
@@ -2542,12 +2891,13 @@ const char *MYSQL_LOG::generate_name(const char *log_name,
-MYSQL_BIN_LOG::MYSQL_BIN_LOG()
+MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period)
:bytes_written(0), prepared_xids(0), file_id(1), open_count(1),
need_start_event(TRUE),
group_commit_queue(0), group_commit_queue_busy(FALSE),
num_commits(0), num_group_commits(0),
- is_relay_log(0),
+ sync_period_ptr(sync_period), sync_counter(0),
+ is_relay_log(0), signal_cnt(0),
checksum_alg_reset(BINLOG_CHECKSUM_ALG_UNDEF),
relay_log_checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF),
description_event_for_exec(0), description_event_for_queue(0)
@@ -2574,9 +2924,9 @@ void MYSQL_BIN_LOG::cleanup()
close(LOG_CLOSE_INDEX|LOG_CLOSE_STOP_EVENT);
delete description_event_for_queue;
delete description_event_for_exec;
- (void) pthread_mutex_destroy(&LOCK_log);
- (void) pthread_mutex_destroy(&LOCK_index);
- (void) pthread_cond_destroy(&update_cond);
+ mysql_mutex_destroy(&LOCK_log);
+ mysql_mutex_destroy(&LOCK_index);
+ mysql_cond_destroy(&update_cond);
}
DBUG_VOID_RETURN;
}
@@ -2595,17 +2945,11 @@ void MYSQL_BIN_LOG::init(bool no_auto_events_arg, ulong max_size_arg)
void MYSQL_BIN_LOG::init_pthread_objects()
{
- DBUG_ASSERT(inited == 0);
- inited= 1;
- (void) pthread_mutex_init(&LOCK_log, MY_MUTEX_INIT_SLOW);
- /*
- LOCK_index and LOCK_log are taken in wrong order
- Can be seen with 'mysql-test-run ndb.ndb_binlog_basic'
- */
- (void) my_pthread_mutex_init(&LOCK_index, MY_MUTEX_INIT_SLOW, "LOCK_index",
- MYF_NO_DEADLOCK_DETECTION);
- (void) pthread_cond_init(&update_cond, 0);
- (void) pthread_cond_init(&COND_queue_busy, 0);
+ MYSQL_LOG::init_pthread_objects();
+ mysql_mutex_init(m_key_LOCK_index, &LOCK_index, MY_MUTEX_INIT_SLOW);
+ mysql_mutex_setflags(&LOCK_index, MYF_NO_DEADLOCK_DETECTION);
+ mysql_cond_init(m_key_update_cond, &update_cond, 0);
+ mysql_cond_init(m_key_COND_queue_busy, &COND_queue_busy, 0);
}
@@ -2628,23 +2972,25 @@ bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg,
}
fn_format(index_file_name, index_file_name_arg, mysql_data_home,
".index", opt);
- if ((index_file_nr= my_open(index_file_name,
- O_RDWR | O_CREAT | O_BINARY ,
- MYF(MY_WME))) < 0 ||
- my_sync(index_file_nr, MYF(MY_WME)) ||
+ if ((index_file_nr= mysql_file_open(m_key_file_log_index,
+ index_file_name,
+ O_RDWR | O_CREAT | O_BINARY,
+ MYF(MY_WME))) < 0 ||
+ mysql_file_sync(index_file_nr, MYF(MY_WME)) ||
init_io_cache(&index_file, index_file_nr,
IO_SIZE, WRITE_CACHE,
- my_seek(index_file_nr,0L,MY_SEEK_END,MYF(0)),
- 0, MYF(MY_WME | MY_WAIT_IF_FULL)) ||
+ mysql_file_seek(index_file_nr, 0L, MY_SEEK_END, MYF(0)),
+ 0, MYF(MY_WME | MY_WAIT_IF_FULL)) ||
DBUG_EVALUATE_IF("fault_injection_openning_index", 1, 0))
{
/*
TODO: all operations creating/deleting the index file or a log, should
call my_sync_dir() or my_sync_dir_by_file() to be durable.
- TODO: file creation should be done with my_create() not my_open().
+ TODO: file creation should be done with mysql_file_create()
+ not mysql_file_open().
*/
if (index_file_nr >= 0)
- my_close(index_file_nr,MYF(0));
+ mysql_file_close(index_file_nr, MYF(0));
return TRUE;
}
@@ -2740,8 +3086,11 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
write_error= 0;
/* open the main log file */
- if (MYSQL_LOG::open(log_name, log_type_arg, new_name,
- io_cache_type_arg))
+ if (MYSQL_LOG::open(
+#ifdef HAVE_PSI_INTERFACE
+ m_key_file_log,
+#endif
+ log_name, log_type_arg, new_name, io_cache_type_arg))
{
#ifdef HAVE_REPLICATION
close_purge_index_file();
@@ -2801,7 +3150,6 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
if (!s.is_valid())
goto err;
s.dont_set_created= null_created_arg;
- s.pre_55_writing_direct();
if (s.write(&log_file))
goto err;
bytes_written+= s.data_written;
@@ -2833,23 +3181,25 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
/* Don't set log_pos in event header */
description_event_for_queue->set_artificial_event();
- description_event_for_queue->pre_55_writing_direct();
if (description_event_for_queue->write(&log_file))
goto err;
bytes_written+= description_event_for_queue->data_written;
}
if (flush_io_cache(&log_file) ||
- my_sync(log_file.file, MYF(MY_WME|MY_SYNC_FILESIZE)))
+ mysql_file_sync(log_file.file, MYF(MY_WME|MY_SYNC_FILESIZE)))
goto err;
- pthread_mutex_lock(&LOCK_commit_ordered);
- strmake(last_commit_pos_file, log_file_name,
- sizeof(last_commit_pos_file)-1);
+ mysql_mutex_lock(&LOCK_commit_ordered);
+ strmake_buf(last_commit_pos_file, log_file_name);
last_commit_pos_offset= my_b_tell(&log_file);
- pthread_mutex_unlock(&LOCK_commit_ordered);
+ mysql_mutex_unlock(&LOCK_commit_ordered);
if (write_file_name_to_index_file)
{
#ifdef HAVE_REPLICATION
+#ifdef ENABLED_DEBUG_SYNC
+ if (current_thd)
+ DEBUG_SYNC(current_thd, "binlog_open_before_update_index");
+#endif
DBUG_EXECUTE_IF("crash_create_critical_before_update_index", DBUG_SUICIDE(););
#endif
@@ -2865,7 +3215,7 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
strlen(log_file_name)) ||
my_b_write(&index_file, (uchar*) "\n", 1) ||
flush_io_cache(&index_file) ||
- my_sync(index_file.file, MYF(MY_WME|MY_SYNC_FILESIZE)))
+ mysql_file_sync(index_file.file, MYF(MY_WME|MY_SYNC_FILESIZE)))
goto err;
#ifdef HAVE_REPLICATION
@@ -2892,26 +3242,23 @@ Turning logging off for the whole duration of the MySQL server process. \
To turn it on again: fix the cause, \
shutdown the MySQL server and restart it.", name, errno);
if (file >= 0)
- my_close(file,MYF(0));
- end_io_cache(&log_file);
- end_io_cache(&index_file);
- safeFree(name);
- log_state= LOG_CLOSED;
+ mysql_file_close(file, MYF(0));
+ close(LOG_CLOSE_INDEX);
DBUG_RETURN(1);
}
int MYSQL_BIN_LOG::get_current_log(LOG_INFO* linfo)
{
- pthread_mutex_lock(&LOCK_log);
+ mysql_mutex_lock(&LOCK_log);
int ret = raw_get_current_log(linfo);
- pthread_mutex_unlock(&LOCK_log);
+ mysql_mutex_unlock(&LOCK_log);
return ret;
}
int MYSQL_BIN_LOG::raw_get_current_log(LOG_INFO* linfo)
{
- strmake(linfo->log_file_name, log_file_name, sizeof(linfo->log_file_name)-1);
+ strmake_buf(linfo->log_file_name, log_file_name);
linfo->pos = my_b_tell(&log_file);
return 0;
}
@@ -2945,19 +3292,20 @@ static bool copy_up_file_and_fill(IO_CACHE *index_file, my_off_t offset)
for (;; offset+= bytes_read)
{
- (void) my_seek(file, offset, MY_SEEK_SET, MYF(0));
- if ((bytes_read= (int) my_read(file, io_buf, sizeof(io_buf), MYF(MY_WME)))
+ mysql_file_seek(file, offset, MY_SEEK_SET, MYF(0));
+ if ((bytes_read= (int) mysql_file_read(file, io_buf, sizeof(io_buf),
+ MYF(MY_WME)))
< 0)
goto err;
if (!bytes_read)
break; // end of file
- (void) my_seek(file, offset-init_offset, MY_SEEK_SET, MYF(0));
- if (my_write(file, io_buf, bytes_read, MYF(MY_WME | MY_NABP)))
+ mysql_file_seek(file, offset-init_offset, MY_SEEK_SET, MYF(0));
+ if (mysql_file_write(file, io_buf, bytes_read, MYF(MY_WME | MY_NABP)))
goto err;
}
/* The following will either truncate the file or fill the end with \n' */
- if (my_chsize(file, offset - init_offset, '\n', MYF(MY_WME)) ||
- my_sync(file, MYF(MY_WME|MY_SYNC_FILESIZE)))
+ if (mysql_file_chsize(file, offset - init_offset, '\n', MYF(MY_WME)) ||
+ mysql_file_sync(file, MYF(MY_WME|MY_SYNC_FILESIZE)))
goto err;
/* Reset data in old index cache */
@@ -2996,18 +3344,33 @@ int MYSQL_BIN_LOG::find_log_pos(LOG_INFO *linfo, const char *log_name,
bool need_lock)
{
int error= 0;
- char *fname= linfo->log_file_name;
- uint log_name_len= log_name ? (uint) strlen(log_name) : 0;
+ char *full_fname= linfo->log_file_name;
+ char full_log_name[FN_REFLEN], fname[FN_REFLEN];
+ uint log_name_len= 0, fname_len= 0;
DBUG_ENTER("find_log_pos");
- DBUG_PRINT("enter",("log_name: %s", log_name ? log_name : "NULL"));
+ full_log_name[0]= full_fname[0]= 0;
/*
Mutex needed because we need to make sure the file pointer does not
move from under our feet
*/
if (need_lock)
- pthread_mutex_lock(&LOCK_index);
- safe_mutex_assert_owner(&LOCK_index);
+ mysql_mutex_lock(&LOCK_index);
+ mysql_mutex_assert_owner(&LOCK_index);
+
+ // extend relative paths for log_name to be searched
+ if (log_name)
+ {
+ if(normalize_binlog_name(full_log_name, log_name, is_relay_log))
+ {
+ error= LOG_INFO_EOF;
+ goto end;
+ }
+ }
+
+ log_name_len= log_name ? (uint) strlen(full_log_name) : 0;
+ DBUG_PRINT("enter", ("log_name: %s, full_log_name: %s",
+ log_name ? log_name : "NULL", full_log_name));
/* As the file is flushed, we can't get an error here */
(void) reinit_io_cache(&index_file, READ_CACHE, (my_off_t) 0, 0, 0);
@@ -3016,8 +3379,10 @@ int MYSQL_BIN_LOG::find_log_pos(LOG_INFO *linfo, const char *log_name,
{
uint length;
my_off_t offset= my_b_tell(&index_file);
- /* If we get 0 or 1 characters, this is the end of the file */
+ DBUG_EXECUTE_IF("simulate_find_log_pos_error",
+ error= LOG_INFO_EOF; break;);
+ /* If we get 0 or 1 characters, this is the end of the file */
if ((length= my_b_gets(&index_file, fname, FN_REFLEN)) <= 1)
{
/* Did not find the given entry; Return not found or error */
@@ -3025,21 +3390,30 @@ int MYSQL_BIN_LOG::find_log_pos(LOG_INFO *linfo, const char *log_name,
break;
}
+ // extend relative paths and match against full path
+ if (normalize_binlog_name(full_fname, fname, is_relay_log))
+ {
+ error= LOG_INFO_EOF;
+ break;
+ }
+ fname_len= (uint) strlen(full_fname);
+
// if the log entry matches, null string matching anything
if (!log_name ||
- (log_name_len == length-1 && fname[log_name_len] == '\n' &&
- !memcmp(fname, log_name, log_name_len)))
+ (log_name_len == fname_len-1 && full_fname[log_name_len] == '\n' &&
+ !memcmp(full_fname, full_log_name, log_name_len)))
{
- DBUG_PRINT("info",("Found log file entry"));
- fname[length-1]=0; // remove last \n
+ DBUG_PRINT("info", ("Found log file entry"));
+ full_fname[fname_len-1]= 0; // remove last \n
linfo->index_file_start_offset= offset;
linfo->index_file_offset = my_b_tell(&index_file);
break;
}
}
+end:
if (need_lock)
- pthread_mutex_unlock(&LOCK_index);
+ mysql_mutex_unlock(&LOCK_index);
DBUG_RETURN(error);
}
@@ -3072,11 +3446,12 @@ int MYSQL_BIN_LOG::find_next_log(LOG_INFO* linfo, bool need_lock)
{
int error= 0;
uint length;
- char *fname= linfo->log_file_name;
+ char fname[FN_REFLEN];
+ char *full_fname= linfo->log_file_name;
if (need_lock)
- pthread_mutex_lock(&LOCK_index);
- safe_mutex_assert_owner(&LOCK_index);
+ mysql_mutex_lock(&LOCK_index);
+ mysql_mutex_assert_owner(&LOCK_index);
/* As the file is flushed, we can't get an error here */
(void) reinit_io_cache(&index_file, READ_CACHE, linfo->index_file_offset, 0,
@@ -3088,12 +3463,23 @@ int MYSQL_BIN_LOG::find_next_log(LOG_INFO* linfo, bool need_lock)
error = !index_file.error ? LOG_INFO_EOF : LOG_INFO_IO;
goto err;
}
- fname[length-1]=0; // kill \n
- linfo->index_file_offset = my_b_tell(&index_file);
+
+ if (fname[0] != 0)
+ {
+ if(normalize_binlog_name(full_fname, fname, is_relay_log))
+ {
+ error= LOG_INFO_EOF;
+ goto err;
+ }
+ length= strlen(full_fname);
+ }
+
+ full_fname[length-1]= 0; // kill \n
+ linfo->index_file_offset= my_b_tell(&index_file);
err:
if (need_lock)
- pthread_mutex_unlock(&LOCK_index);
+ mysql_mutex_unlock(&LOCK_index);
return error;
}
@@ -3119,6 +3505,7 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
{
LOG_INFO linfo;
bool error=0;
+ int err;
const char* save_name;
DBUG_ENTER("reset_logs");
@@ -3127,8 +3514,8 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
We need to get both locks to be sure that no one is trying to
write to the index log file.
*/
- pthread_mutex_lock(&LOCK_log);
- pthread_mutex_lock(&LOCK_index);
+ mysql_mutex_lock(&LOCK_log);
+ mysql_mutex_lock(&LOCK_index);
/*
The following mutex is needed to ensure that no threads call
@@ -3136,7 +3523,7 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
thread. If the transaction involved MyISAM tables, it should go
into binlog even on rollback.
*/
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
/* Save variables so that we can reopen the log */
save_name=name;
@@ -3152,15 +3539,19 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
We need to invert the steps and use the purge_index_file methods
in order to make the operation safe.
*/
- if (find_log_pos(&linfo, NullS, 0))
+
+ if ((err= find_log_pos(&linfo, NullS, 0)) != 0)
{
- error=1;
+ uint errcode= purge_log_get_error_code(err);
+ sql_print_error("Failed to locate old binlog or relay log files");
+ my_message(errcode, ER(errcode), MYF(0));
+ error= 1;
goto err;
}
for (;;)
{
- if ((error= my_delete_allow_opened(linfo.log_file_name, MYF(0))) != 0)
+ if ((error= my_delete(linfo.log_file_name, MYF(0))) != 0)
{
if (my_errno == ENOENT)
{
@@ -3191,7 +3582,7 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
/* Start logging with a new file */
close(LOG_CLOSE_INDEX | LOG_CLOSE_TO_BE_OPENED);
- if ((error= my_delete_allow_opened(index_file_name, MYF(0)))) // Reset (open will update)
+ if ((error= my_delete(index_file_name, MYF(0)))) // Reset (open will update)
{
if (my_errno == ENOENT)
{
@@ -3221,12 +3612,14 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
if (!open_index_file(index_file_name, 0, FALSE))
if ((error= open(save_name, log_type, 0, io_cache_type, no_auto_events, max_size, 0, FALSE)))
goto err;
- my_free((uchar*) save_name, MYF(0));
+ my_free((void *) save_name);
err:
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
- pthread_mutex_unlock(&LOCK_index);
- pthread_mutex_unlock(&LOCK_log);
+ if (error == 1)
+ name= const_cast<char*>(save_name);
+ mysql_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_index);
+ mysql_mutex_unlock(&LOCK_log);
DBUG_RETURN(error);
}
@@ -3280,7 +3673,7 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included)
DBUG_ASSERT(rli->slave_running == 1);
DBUG_ASSERT(!strcmp(rli->linfo.log_file_name,rli->event_relay_log_name));
- pthread_mutex_lock(&LOCK_index);
+ mysql_mutex_lock(&LOCK_index);
to_purge_if_included= my_strdup(rli->group_relay_log_name, MYF(0));
/*
@@ -3303,8 +3696,7 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included)
Reset rli's coordinates to the current log.
*/
rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE;
- strmake(rli->event_relay_log_name,rli->linfo.log_file_name,
- sizeof(rli->event_relay_log_name)-1);
+ strmake_buf(rli->event_relay_log_name,rli->linfo.log_file_name);
/*
If we removed the rli->group_relay_log_name file,
@@ -3314,8 +3706,7 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included)
if (included)
{
rli->group_relay_log_pos = BIN_LOG_HEADER_SIZE;
- strmake(rli->group_relay_log_name,rli->linfo.log_file_name,
- sizeof(rli->group_relay_log_name)-1);
+ strmake_buf(rli->group_relay_log_name,rli->linfo.log_file_name);
rli->notify_group_relay_log_name_update();
}
@@ -3324,17 +3715,17 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included)
DBUG_EXECUTE_IF("crash_before_purge_logs", DBUG_SUICIDE(););
- pthread_mutex_lock(&rli->log_space_lock);
+ mysql_mutex_lock(&rli->log_space_lock);
rli->relay_log.purge_logs(to_purge_if_included, included,
0, 0, &rli->log_space_total);
- pthread_mutex_unlock(&rli->log_space_lock);
+ mysql_mutex_unlock(&rli->log_space_lock);
/*
Ok to broadcast after the critical region as there is no risk of
the mutex being destroyed by this thread later - this helps save
context switches
*/
- pthread_cond_broadcast(&rli->log_space_cond);
+ mysql_cond_broadcast(&rli->log_space_cond);
/*
* Need to update the log pos because purge logs has been called
@@ -3355,8 +3746,8 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included)
DBUG_ASSERT(!included || rli->linfo.index_file_start_offset == 0);
err:
- my_free(to_purge_if_included, MYF(0));
- pthread_mutex_unlock(&LOCK_index);
+ my_free(to_purge_if_included);
+ mysql_mutex_unlock(&LOCK_index);
DBUG_RETURN(error);
}
@@ -3396,7 +3787,7 @@ int MYSQL_BIN_LOG::update_log_index(LOG_INFO* log_info, bool need_update_threads
LOG_INFO_EOF to_log not found
LOG_INFO_EMFILE too many files opened
LOG_INFO_FATAL if any other than ENOENT error from
- my_stat() or my_delete()
+ mysql_file_stat() or mysql_file_delete()
*/
int MYSQL_BIN_LOG::purge_logs(const char *to_log,
@@ -3413,7 +3804,7 @@ int MYSQL_BIN_LOG::purge_logs(const char *to_log,
DBUG_PRINT("info",("to_log= %s",to_log));
if (need_mutex)
- pthread_mutex_lock(&LOCK_index);
+ mysql_mutex_lock(&LOCK_index);
if ((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 "
@@ -3476,7 +3867,7 @@ err:
DBUG_EXECUTE_IF("crash_purge_non_critical_after_update_index", DBUG_SUICIDE(););
if (need_mutex)
- pthread_mutex_unlock(&LOCK_index);
+ mysql_mutex_unlock(&LOCK_index);
DBUG_RETURN(error);
}
@@ -3540,8 +3931,7 @@ int MYSQL_BIN_LOG::close_purge_index_file()
bool MYSQL_BIN_LOG::is_inited_purge_index_file()
{
- DBUG_ENTER("MYSQL_BIN_LOG::is_inited_purge_index_file");
- DBUG_RETURN (my_b_inited(&purge_index_file));
+ return my_b_inited(&purge_index_file);
}
int MYSQL_BIN_LOG::sync_purge_index_file()
@@ -3577,13 +3967,12 @@ int MYSQL_BIN_LOG::register_create_index_entry(const char *entry)
int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *decrease_log_space,
bool need_mutex)
{
+ DBUG_ENTER("MYSQL_BIN_LOG:purge_index_entry");
MY_STAT s;
int error= 0;
LOG_INFO log_info;
LOG_INFO check_log_info;
- DBUG_ENTER("MYSQL_BIN_LOG:purge_index_entry");
-
DBUG_ASSERT(my_b_inited(&purge_index_file));
if ((error=reinit_io_cache(&purge_index_file, READ_CACHE, 0, 0, 0)))
@@ -3615,7 +4004,7 @@ int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *decrease_log_space,
/* Get rid of the trailing '\n' */
log_info.log_file_name[length-1]= 0;
- if (!my_stat(log_info.log_file_name, &s, MYF(0)))
+ if (!mysql_file_stat(m_key_file_log, log_info.log_file_name, &s, MYF(0)))
{
if (my_errno == ENOENT)
{
@@ -3629,7 +4018,7 @@ int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *decrease_log_space,
ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
log_info.log_file_name);
}
- sql_print_information("Failed to execute my_stat on file '%s'",
+ sql_print_information("Failed to execute mysql_file_stat on file '%s'",
log_info.log_file_name);
my_errno= 0;
}
@@ -3767,7 +4156,7 @@ err:
@retval
LOG_INFO_PURGE_NO_ROTATE Binary file that can't be rotated
LOG_INFO_FATAL if any other than ENOENT error from
- my_stat() or my_delete()
+ mysql_file_stat() or mysql_file_delete()
*/
int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time)
@@ -3780,7 +4169,7 @@ int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time)
DBUG_ENTER("purge_logs_before_date");
- pthread_mutex_lock(&LOCK_index);
+ mysql_mutex_lock(&LOCK_index);
to_log[0]= 0;
if ((error=find_log_pos(&log_info, NullS, 0 /*no mutex*/)))
@@ -3790,7 +4179,8 @@ int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time)
!is_active(log_info.log_file_name) &&
!log_in_use(log_info.log_file_name))
{
- if (!my_stat(log_info.log_file_name, &stat_area, MYF(0)))
+ if (!mysql_file_stat(m_key_file_log,
+ log_info.log_file_name, &stat_area, MYF(0)))
{
if (my_errno == ENOENT)
{
@@ -3826,9 +4216,7 @@ int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time)
else
{
if (stat_area.st_mtime < purge_time)
- strmake(to_log,
- log_info.log_file_name,
- sizeof(log_info.log_file_name) - 1);
+ strmake_buf(to_log, log_info.log_file_name);
else
break;
}
@@ -3839,7 +4227,7 @@ int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time)
error= (to_log[0] ? purge_logs(to_log, 1, 0, 1, (ulonglong *) 0) : 0);
err:
- pthread_mutex_unlock(&LOCK_index);
+ mysql_mutex_unlock(&LOCK_index);
DBUG_RETURN(error);
}
#endif /* HAVE_REPLICATION */
@@ -3916,6 +4304,10 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
{
int error= 0, close_on_error= FALSE;
char new_name[FN_REFLEN], *new_name_ptr, *old_name, *file_to_open;
+ uint close_flag;
+ bool delay_close= false;
+ File old_file;
+ LINT_INIT(old_file);
DBUG_ENTER("MYSQL_BIN_LOG::new_file_impl");
if (!is_open())
@@ -3925,11 +4317,11 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
}
if (need_lock)
- pthread_mutex_lock(&LOCK_log);
- pthread_mutex_lock(&LOCK_index);
+ mysql_mutex_lock(&LOCK_log);
+ mysql_mutex_lock(&LOCK_index);
- safe_mutex_assert_owner(&LOCK_log);
- safe_mutex_assert_owner(&LOCK_index);
+ mysql_mutex_assert_owner(&LOCK_log);
+ mysql_mutex_assert_owner(&LOCK_index);
/*
if binlog is used as tc log, be sure all xids are "unlogged",
@@ -3943,12 +4335,12 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
if (prepared_xids)
{
tc_log_page_waits++;
- pthread_mutex_lock(&LOCK_prep_xids);
+ mysql_mutex_lock(&LOCK_prep_xids);
while (prepared_xids) {
DBUG_PRINT("info", ("prepared_xids=%lu", prepared_xids));
- pthread_cond_wait(&COND_prep_xids, &LOCK_prep_xids);
+ mysql_cond_wait(&COND_prep_xids, &LOCK_prep_xids);
}
- pthread_mutex_unlock(&LOCK_prep_xids);
+ mysql_mutex_unlock(&LOCK_prep_xids);
}
/* Reuse old name if not binlog and not update log */
@@ -3979,7 +4371,6 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
*/
if (is_relay_log)
r.checksum_alg= relay_log_checksum_alg;
- r.pre_55_writing_direct();
DBUG_ASSERT(!is_relay_log || relay_log_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF);
if(DBUG_EVALUATE_IF("fault_injection_new_file_rotate_event", (error=close_on_error=TRUE), FALSE) ||
(error= r.write(&log_file)))
@@ -4000,7 +4391,20 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
}
old_name=name;
name=0; // Don't free name
- close(LOG_CLOSE_TO_BE_OPENED | LOG_CLOSE_INDEX);
+ close_flag= LOG_CLOSE_TO_BE_OPENED | LOG_CLOSE_INDEX;
+ if (!is_relay_log)
+ {
+ /*
+ We need to keep the old binlog file open (and marked as in-use) until
+ the new one is fully created and synced to disk and index. Otherwise we
+ leave a window where if we crash, there is no binlog file marked as
+ crashed for server restart to detect the need for recovery.
+ */
+ old_file= log_file.file;
+ close_flag|= LOG_CLOSE_DELAYED_CLOSE;
+ delay_close= true;
+ }
+ close(close_flag);
if (log_type == LOG_BIN && checksum_alg_reset != BINLOG_CHECKSUM_ALG_UNDEF)
{
DBUG_ASSERT(!is_relay_log);
@@ -4039,10 +4443,16 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
close_on_error= TRUE;
}
- my_free(old_name,MYF(0));
+ my_free(old_name);
end:
+ if (delay_close)
+ {
+ clear_inuse_flag_when_closing(old_file);
+ mysql_file_close(old_file, MYF(MY_WME));
+ }
+
if (error && close_on_error /* rotate or reopen failed */)
{
/*
@@ -4067,8 +4477,8 @@ end:
}
if (need_lock)
- pthread_mutex_unlock(&LOCK_log);
- pthread_mutex_unlock(&LOCK_index);
+ mysql_mutex_unlock(&LOCK_log);
+ mysql_mutex_unlock(&LOCK_index);
DBUG_RETURN(error);
}
@@ -4077,7 +4487,7 @@ end:
bool MYSQL_BIN_LOG::append(Log_event* ev)
{
bool error = 0;
- pthread_mutex_lock(&LOCK_log);
+ mysql_mutex_lock(&LOCK_log);
DBUG_ENTER("MYSQL_BIN_LOG::append");
DBUG_ASSERT(log_file.type == SEQ_READ_APPEND);
@@ -4085,7 +4495,6 @@ bool MYSQL_BIN_LOG::append(Log_event* ev)
Log_event::write() is smart enough to use my_b_write() or
my_b_append() depending on the kind of cache we have.
*/
- ev->pre_55_writing_direct();
if (ev->write(&log_file))
{
error=1;
@@ -4093,10 +4502,12 @@ bool MYSQL_BIN_LOG::append(Log_event* ev)
}
bytes_written+= ev->data_written;
DBUG_PRINT("info",("max_size: %lu",max_size));
+ if (flush_and_sync(0))
+ goto err;
if ((uint) my_b_append_tell(&log_file) > max_size)
error= new_file_without_locking();
err:
- pthread_mutex_unlock(&LOCK_log);
+ mysql_mutex_unlock(&LOCK_log);
signal_update(); // Safe as we don't call close
DBUG_RETURN(error);
}
@@ -4111,7 +4522,7 @@ bool MYSQL_BIN_LOG::appendv(const char* buf, uint len,...)
DBUG_ASSERT(log_file.type == SEQ_READ_APPEND);
- safe_mutex_assert_owner(&LOCK_log);
+ mysql_mutex_assert_owner(&LOCK_log);
do
{
if (my_b_append(&log_file,(uchar*) buf,len))
@@ -4122,6 +4533,8 @@ bool MYSQL_BIN_LOG::appendv(const char* buf, uint len,...)
bytes_written += len;
} while ((buf=va_arg(args,const char*)) && (len=va_arg(args,uint)));
DBUG_PRINT("info",("max_size: %lu",max_size));
+ if (flush_and_sync(0))
+ goto err;
if ((uint) my_b_append_tell(&log_file) > max_size)
error= new_file_without_locking();
err:
@@ -4130,17 +4543,21 @@ err:
DBUG_RETURN(error);
}
-
-bool MYSQL_BIN_LOG::flush_and_sync()
+bool MYSQL_BIN_LOG::flush_and_sync(bool *synced)
{
int err=0, fd=log_file.file;
- safe_mutex_assert_owner(&LOCK_log);
+ if (synced)
+ *synced= 0;
+ mysql_mutex_assert_owner(&LOCK_log);
if (flush_io_cache(&log_file))
return 1;
- if (++sync_binlog_counter >= sync_binlog_period && sync_binlog_period)
+ uint sync_period= get_sync_period();
+ if (sync_period && ++sync_counter >= sync_period)
{
- sync_binlog_counter= 0;
- err=my_sync(fd, MYF(MY_WME|MY_SYNC_FILESIZE));
+ sync_counter= 0;
+ err= mysql_file_sync(fd, MYF(MY_WME|MY_SYNC_FILESIZE));
+ if (synced)
+ *synced= 1;
#ifndef DBUG_OFF
if (opt_binlog_dbug_fsync_sleep > 0)
my_sleep(opt_binlog_dbug_fsync_sleep);
@@ -4170,6 +4587,72 @@ bool MYSQL_BIN_LOG::is_query_in_union(THD *thd, query_id_t query_id_param)
query_id_param >= thd->binlog_evt_union.first_query_id);
}
+/**
+ This function checks if a transactional table was updated by the
+ current transaction.
+
+ @param thd The client thread that executed the current statement.
+ @return
+ @c true if a transactional table was updated, @c false otherwise.
+*/
+bool
+trans_has_updated_trans_table(const THD* thd)
+{
+ binlog_cache_mngr *const cache_mngr=
+ (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
+
+ return (cache_mngr ? !cache_mngr->trx_cache.empty() : 0);
+}
+
+/**
+ This function checks if a transactional table was updated by the
+ current statement.
+
+ @param thd The client thread that executed the current statement.
+ @return
+ @c true if a transactional table was updated, @c false otherwise.
+*/
+bool
+stmt_has_updated_trans_table(const THD *thd)
+{
+ Ha_trx_info *ha_info;
+
+ for (ha_info= thd->transaction.stmt.ha_list; ha_info;
+ ha_info= ha_info->next())
+ {
+ if (ha_info->is_trx_read_write() && ha_info->ht() != binlog_hton)
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/**
+ This function checks if either a trx-cache or a non-trx-cache should
+ be used. If @c bin_log_direct_non_trans_update is active or the format
+ is either MIXED or ROW, the cache to be used depends on the flag @c
+ is_transactional.
+
+ On the other hand, if binlog_format is STMT or direct option is
+ OFF, the trx-cache should be used if and only if the statement is
+ transactional or the trx-cache is not empty. Otherwise, the
+ non-trx-cache should be used.
+
+ @param thd The client thread.
+ @param is_transactional The changes are related to a trx-table.
+ @return
+ @c true if a trx-cache should be used, @c false otherwise.
+*/
+bool use_trans_cache(const THD* thd, bool is_transactional)
+{
+ 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()));
+}
+
/**
This function checks if a transaction, either a multi-statement
or a single statement transaction is about to commit or not.
@@ -4180,43 +4663,40 @@ bool MYSQL_BIN_LOG::is_query_in_union(THD *thd, query_id_t query_id_param)
@return
@c true if committing a transaction, otherwise @c false.
*/
-bool ending_trans(const THD* thd, const bool all)
+bool ending_trans(THD* thd, const bool all)
{
- return (all || (!all && !(thd->options &
- (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT))));
+ return (all || ending_single_stmt_trans(thd, all));
}
/**
- This function checks if a non-transactional table was updated by
- the current transaction.
+ This function checks if a single statement transaction is about
+ to commit or not.
@param thd The client thread that executed the current statement.
+ @param all Committing a transaction (i.e. TRUE) or a statement
+ (i.e. FALSE).
@return
- @c true if a non-transactional table was updated, @c false
- otherwise.
+ @c true if committing a single statement transaction, otherwise
+ @c false.
*/
-bool trans_has_updated_non_trans_table(const THD* thd)
+bool ending_single_stmt_trans(THD* thd, const bool all)
{
- return (thd->transaction.all.modified_non_trans_table ||
- thd->transaction.stmt.modified_non_trans_table);
+ return (!all && !thd->in_multi_stmt_transaction_mode());
}
/**
- This function checks if any statement was committed and cached.
+ This function checks if a non-transactional table was updated by
+ the current transaction.
@param thd The client thread that executed the current statement.
- @param all Committing a transaction (i.e. TRUE) or a statement
- (i.e. FALSE).
@return
- @c true if at a statement was committed and cached, @c false
+ @c true if a non-transactional table was updated, @c false
otherwise.
*/
-bool trans_has_no_stmt_committed(const THD* thd, bool all)
+bool trans_has_updated_non_trans_table(const THD* thd)
{
- binlog_trx_data *const trx_data=
- (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
-
- return (!all && !trx_data->at_least_one_stmt_committed);
+ return (thd->transaction.all.modified_non_trans_table ||
+ thd->transaction.stmt.modified_non_trans_table);
}
/**
@@ -4237,28 +4717,35 @@ bool stmt_has_updated_non_trans_table(const THD* thd)
binlog_hton, which has internal linkage.
*/
-int THD::binlog_setup_trx_data()
+binlog_cache_mngr *THD::binlog_setup_trx_data()
{
DBUG_ENTER("THD::binlog_setup_trx_data");
- binlog_trx_data *trx_data=
- (binlog_trx_data*) thd_get_ha_data(this, binlog_hton);
+ binlog_cache_mngr *cache_mngr=
+ (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
- if (trx_data)
- DBUG_RETURN(0); // Already set up
+ if (cache_mngr)
+ DBUG_RETURN(cache_mngr); // Already set up
- trx_data= (binlog_trx_data*) my_malloc(sizeof(binlog_trx_data), MYF(MY_ZEROFILL));
- if (!trx_data ||
- open_cached_file(&trx_data->trans_log, mysql_tmpdir,
+ cache_mngr= (binlog_cache_mngr*) my_malloc(sizeof(binlog_cache_mngr), MYF(MY_ZEROFILL));
+ if (!cache_mngr ||
+ open_cached_file(&cache_mngr->stmt_cache.cache_log, mysql_tmpdir,
+ LOG_PREFIX, binlog_stmt_cache_size, MYF(MY_WME)) ||
+ open_cached_file(&cache_mngr->trx_cache.cache_log, mysql_tmpdir,
LOG_PREFIX, binlog_cache_size, MYF(MY_WME)))
{
- my_free((uchar*)trx_data, MYF(MY_ALLOW_ZERO_PTR));
- DBUG_RETURN(1); // Didn't manage to set it up
+ my_free(cache_mngr);
+ DBUG_RETURN(0); // Didn't manage to set it up
}
- thd_set_ha_data(this, binlog_hton, trx_data);
-
- trx_data= new (thd_get_ha_data(this, binlog_hton)) binlog_trx_data;
+ thd_set_ha_data(this, binlog_hton, cache_mngr);
- DBUG_RETURN(0);
+ cache_mngr= new (cache_mngr)
+ binlog_cache_mngr(max_binlog_stmt_cache_size,
+ max_binlog_cache_size,
+ &binlog_stmt_cache_use,
+ &binlog_stmt_cache_disk_use,
+ &binlog_cache_use,
+ &binlog_cache_disk_use);
+ DBUG_RETURN(cache_mngr);
}
/*
@@ -4274,11 +4761,10 @@ int THD::binlog_setup_trx_data()
- Start a transaction if not in autocommit mode or if a BEGIN
statement has been seen.
- - Start a statement transaction to allow us to truncate the binary
- log.
+ - Start a statement transaction to allow us to truncate the cache.
- Save the currrent binlog position so that we can roll back the
- statement by truncating the transaction log.
+ statement by truncating the cache.
We only update the saved position if the old one was undefined,
the reason is that there are some cases (e.g., for CREATE-SELECT)
@@ -4292,18 +4778,18 @@ int THD::binlog_setup_trx_data()
void
THD::binlog_start_trans_and_stmt()
{
- binlog_trx_data *trx_data= (binlog_trx_data*) thd_get_ha_data(this, binlog_hton);
+ binlog_cache_mngr *cache_mngr= (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
DBUG_ENTER("binlog_start_trans_and_stmt");
- DBUG_PRINT("enter", ("trx_data: 0x%lx trx_data->before_stmt_pos: %lu",
- (long) trx_data,
- (trx_data ? (ulong) trx_data->before_stmt_pos :
+ DBUG_PRINT("enter", ("cache_mngr: %p cache_mngr->trx_cache.get_prev_position(): %lu",
+ cache_mngr,
+ (cache_mngr ? (ulong) cache_mngr->trx_cache.get_prev_position() :
(ulong) 0)));
- if (trx_data == NULL ||
- trx_data->before_stmt_pos == MY_OFF_T_UNDEF)
+ if (cache_mngr == NULL ||
+ cache_mngr->trx_cache.get_prev_position() == MY_OFF_T_UNDEF)
{
this->binlog_set_stmt_begin();
- if (options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
+ if (in_multi_stmt_transaction_mode())
trans_register_ha(this, TRUE, binlog_hton);
trans_register_ha(this, FALSE, binlog_hton);
/*
@@ -4321,47 +4807,54 @@ THD::binlog_start_trans_and_stmt()
}
void THD::binlog_set_stmt_begin() {
- binlog_trx_data *trx_data=
- (binlog_trx_data*) thd_get_ha_data(this, binlog_hton);
+ binlog_cache_mngr *cache_mngr=
+ (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
/*
- The call to binlog_trans_log_savepos() might create the trx_data
+ The call to binlog_trans_log_savepos() might create the cache_mngr
structure, if it didn't exist before, so we save the position
into an auto variable and then write it into the transaction
- data for the binary log (i.e., trx_data).
+ data for the binary log (i.e., cache_mngr).
*/
my_off_t pos= 0;
binlog_trans_log_savepos(this, &pos);
- trx_data= (binlog_trx_data*) thd_get_ha_data(this, binlog_hton);
- trx_data->before_stmt_pos= pos;
+ cache_mngr= (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
+ cache_mngr->trx_cache.set_prev_position(pos);
}
static int
binlog_start_consistent_snapshot(handlerton *hton, THD *thd)
{
int err= 0;
- binlog_trx_data *trx_data;
DBUG_ENTER("binlog_start_consistent_snapshot");
- thd->binlog_setup_trx_data();
- trx_data= (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
+ binlog_cache_mngr *const cache_mngr= thd->binlog_setup_trx_data();
/* Server layer calls us with LOCK_commit_ordered locked, so this is safe. */
- strmake(trx_data->last_commit_pos_file, mysql_bin_log.last_commit_pos_file,
- sizeof(trx_data->last_commit_pos_file)-1);
- trx_data->last_commit_pos_offset= mysql_bin_log.last_commit_pos_offset;
+ strmake_buf(cache_mngr->last_commit_pos_file, mysql_bin_log.last_commit_pos_file);
+ cache_mngr->last_commit_pos_offset= mysql_bin_log.last_commit_pos_offset;
trans_register_ha(thd, TRUE, hton);
DBUG_RETURN(err);
}
-/*
- Write a table map to the binary log. If with_annotate != NULL and
- *with_annotate = TRUE write also Annotate_rows before the table map.
- */
+/**
+ This function writes a table map to the binary log.
+ Note that in order to keep the signature uniform with related methods,
+ we use a redundant parameter to indicate whether a transactional table
+ was changed or not.
-int THD::binlog_write_table_map(TABLE *table, bool is_trans,
+ If with_annotate != NULL and
+ *with_annotate = TRUE write also Annotate_rows before the table map.
+
+ @param table a pointer to the table.
+ @param is_transactional @c true indicates a transactional table,
+ otherwise @c false a non-transactional.
+ @return
+ nonzero if an error pops up when writing the table map event.
+*/
+int THD::binlog_write_table_map(TABLE *table, bool is_transactional,
my_bool *with_annotate)
{
int error;
@@ -4371,149 +4864,179 @@ int THD::binlog_write_table_map(TABLE *table, bool is_trans,
table->s->table_map_id));
/* Pre-conditions */
- DBUG_ASSERT(current_stmt_binlog_row_based && mysql_bin_log.is_open());
+ DBUG_ASSERT(is_current_stmt_binlog_format_row() && mysql_bin_log.is_open());
DBUG_ASSERT(table->s->table_map_id != ULONG_MAX);
Table_map_log_event
- the_event(this, table, table->s->table_map_id, is_trans);
+ the_event(this, table, table->s->table_map_id, is_transactional);
- if (is_trans && binlog_table_maps == 0)
+ if (binlog_table_maps == 0)
binlog_start_trans_and_stmt();
- if ((error= mysql_bin_log.write(&the_event, with_annotate)))
+ 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));
+ if (with_annotate && *with_annotate)
+ {
+ Annotate_rows_log_event anno(current_thd, is_transactional, false);
+ /* Annotate event should be written not more than once */
+ *with_annotate= 0;
+ if ((error= anno.write(file)))
+ DBUG_RETURN(error);
+ }
+ if ((error= the_event.write(file)))
DBUG_RETURN(error);
binlog_table_maps++;
DBUG_RETURN(0);
}
+/**
+ This function retrieves a pending row event from a cache which is
+ specified through the parameter @c is_transactional. Respectively, when it
+ is @c true, the pending event is returned from the transactional cache.
+ Otherwise from the non-transactional cache.
+
+ @param is_transactional @c true indicates a transactional cache,
+ otherwise @c false a non-transactional.
+ @return
+ The row event if any.
+*/
Rows_log_event*
-THD::binlog_get_pending_rows_event() const
+THD::binlog_get_pending_rows_event(bool is_transactional) const
{
- binlog_trx_data *const trx_data=
- (binlog_trx_data*) thd_get_ha_data(this, binlog_hton);
+ Rows_log_event* rows= NULL;
+ binlog_cache_mngr *const cache_mngr=
+ (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
+
/*
- This is less than ideal, but here's the story: If there is no
- trx_data, prepare_pending_rows_event() has never been called
- (since the trx_data is set up there). In that case, we just return
- NULL.
+ This is less than ideal, but here's the story: If there is no cache_mngr,
+ prepare_pending_rows_event() has never been called (since the cache_mngr
+ is set up there). In that case, we just return NULL.
*/
- return trx_data ? trx_data->pending() : NULL;
+ if (cache_mngr)
+ {
+ binlog_cache_data *cache_data=
+ cache_mngr->get_binlog_cache_data(use_trans_cache(this, is_transactional));
+
+ rows= cache_data->pending();
+ }
+ return (rows);
}
+/**
+ This function stores a pending row event into a cache which is specified
+ through the parameter @c is_transactional. Respectively, when it is @c
+ true, the pending event is stored into the transactional cache. Otherwise
+ into the non-transactional cache.
+
+ @param evt a pointer to the row event.
+ @param is_transactional @c true indicates a transactional cache,
+ otherwise @c false a non-transactional.
+*/
void
-THD::binlog_set_pending_rows_event(Rows_log_event* ev)
+THD::binlog_set_pending_rows_event(Rows_log_event* ev, bool is_transactional)
{
- if (thd_get_ha_data(this, binlog_hton) == NULL)
- binlog_setup_trx_data();
+ binlog_cache_mngr *const cache_mngr= binlog_setup_trx_data();
- binlog_trx_data *const trx_data=
- (binlog_trx_data*) thd_get_ha_data(this, binlog_hton);
+ DBUG_ASSERT(cache_mngr);
- DBUG_ASSERT(trx_data);
- trx_data->set_pending(ev);
+ binlog_cache_data *cache_data=
+ cache_mngr->get_binlog_cache_data(use_trans_cache(this, is_transactional));
+
+ cache_data->set_pending(ev);
}
/**
- Remove the pending rows event, discarding any outstanding rows.
-
- If there is no pending rows event available, this is effectively a
+ This function removes the pending rows event, discarding any outstanding
+ rows. If there is no pending rows event available, this is effectively a
no-op.
- */
+
+ @param thd a pointer to the user thread.
+ @param is_transactional @c true indicates a transactional cache,
+ otherwise @c false a non-transactional.
+*/
int
-MYSQL_BIN_LOG::remove_pending_rows_event(THD *thd)
+MYSQL_BIN_LOG::remove_pending_rows_event(THD *thd, bool is_transactional)
{
DBUG_ENTER("MYSQL_BIN_LOG::remove_pending_rows_event");
- binlog_trx_data *const trx_data=
- (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
+ binlog_cache_mngr *const cache_mngr=
+ (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
- DBUG_ASSERT(trx_data);
+ DBUG_ASSERT(cache_mngr);
- if (Rows_log_event* pending= trx_data->pending())
+ binlog_cache_data *cache_data=
+ cache_mngr->get_binlog_cache_data(use_trans_cache(thd, is_transactional));
+
+ if (Rows_log_event* pending= cache_data->pending())
{
delete pending;
- trx_data->set_pending(NULL);
+ cache_data->set_pending(NULL);
}
DBUG_RETURN(0);
}
/*
- Moves the last bunch of rows from the pending Rows event to the binlog
- (either cached binlog if transaction, or disk binlog). Sets a new pending
- event.
+ Moves the last bunch of rows from the pending Rows event to a cache (either
+ transactional cache if is_transaction is @c true, or the non-transactional
+ cache otherwise. Sets a new pending event.
+
+ @param thd a pointer to the user thread.
+ @param evt a pointer to the row event.
+ @param is_transactional @c true indicates a transactional cache,
+ otherwise @c false a non-transactional.
*/
int
MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd,
- Rows_log_event* event)
+ Rows_log_event* event,
+ bool is_transactional)
{
DBUG_ENTER("MYSQL_BIN_LOG::flush_and_set_pending_rows_event(event)");
DBUG_ASSERT(mysql_bin_log.is_open());
DBUG_PRINT("enter", ("event: 0x%lx", (long) event));
int error= 0;
+ binlog_cache_mngr *const cache_mngr=
+ (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
- binlog_trx_data *const trx_data=
- (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
+ DBUG_ASSERT(cache_mngr);
- DBUG_ASSERT(trx_data);
+ binlog_cache_data *cache_data=
+ cache_mngr->get_binlog_cache_data(use_trans_cache(thd, is_transactional));
- DBUG_PRINT("info", ("trx_data->pending(): 0x%lx", (long) trx_data->pending()));
+ DBUG_PRINT("info", ("cache_mngr->pending(): 0x%lx", (long) cache_data->pending()));
- if (Rows_log_event* pending= trx_data->pending())
+ if (Rows_log_event* pending= cache_data->pending())
{
+ IO_CACHE *file= &cache_data->cache_log;
+
/*
- Decide if we should write to the log file directly or to the
- transaction log.
+ Write pending event to the cache.
*/
- if (pending->get_cache_stmt() || my_b_tell(&trx_data->trans_log))
- {
- /* Write to transaction log/cache. */
- if (pending->write(&trx_data->trans_log))
- {
- set_write_error(thd);
- delete pending;
- trx_data->set_pending(NULL);
- DBUG_RETURN(1);
- }
- }
- else
+ DBUG_EXECUTE_IF("simulate_disk_full_at_flush_pending",
+ {DBUG_SET("+d,simulate_file_write_error");});
+ if (pending->write(file))
{
- /* Write directly to log file. */
- pthread_mutex_lock(&LOCK_log);
- pending->pre_55_writing_direct();
- if (pending->write(&log_file))
- {
- pthread_mutex_unlock(&LOCK_log);
- set_write_error(thd);
- delete pending;
- trx_data->set_pending(NULL);
- DBUG_RETURN(1);
- }
-
- error= flush_and_sync();
- if (!error)
- {
- signal_update();
- error= rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED);
- }
-
- /*
- Take mutex to protect against a reader seeing partial writes of 64-bit
- offset on 32-bit CPUs.
- */
- pthread_mutex_lock(&LOCK_commit_ordered);
- last_commit_pos_offset= my_b_tell(&log_file);
- pthread_mutex_unlock(&LOCK_commit_ordered);
- pthread_mutex_unlock(&LOCK_log);
+ set_write_error(thd, is_transactional);
+ if (check_write_error(thd) && cache_data &&
+ stmt_has_updated_non_trans_table(thd))
+ cache_data->set_incident();
+ delete pending;
+ cache_data->set_pending(NULL);
+ DBUG_EXECUTE_IF("simulate_disk_full_at_flush_pending",
+ {DBUG_SET("-d,simulate_file_write_error");});
+ DBUG_RETURN(1);
}
delete pending;
}
- thd->binlog_set_pending_rows_event(event);
+ thd->binlog_set_pending_rows_event(event, is_transactional);
DBUG_RETURN(error);
}
@@ -4528,8 +5051,11 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
{
THD *thd= event_info->thd;
bool error= 1;
- uint16 cache_type;
DBUG_ENTER("MYSQL_BIN_LOG::write(Log_event *)");
+ binlog_cache_data *cache_data= 0;
+ bool is_trans_cache= FALSE;
+ bool using_trans= event_info->use_trans_cache();
+ bool direct= event_info->use_direct_logging();
if (thd->binlog_evt_union.do_union)
{
@@ -4538,7 +5064,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
We will log the function call to the binary log on function exit
*/
thd->binlog_evt_union.unioned_events= TRUE;
- thd->binlog_evt_union.unioned_events_trans |= event_info->cache_stmt;
+ thd->binlog_evt_union.unioned_events_trans |= using_trans;
DBUG_RETURN(0);
}
@@ -4548,8 +5074,8 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
this will close all tables on the slave.
*/
bool const end_stmt=
- thd->prelocked_mode && thd->lex->requires_prelocking();
- if (thd->binlog_flush_pending_rows_event(end_stmt))
+ thd->locked_tables_mode && thd->lex->requires_prelocking();
+ if (thd->binlog_flush_pending_rows_event(end_stmt, using_trans))
DBUG_RETURN(error);
/*
@@ -4559,8 +5085,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
*/
if (likely(is_open()))
{
- IO_CACHE *file= &log_file;
- my_off_t my_org_b_tell;
+ my_off_t UNINIT_VAR(my_org_b_tell);
#ifdef HAVE_REPLICATION
/*
In the future we need to add to the following if tests like
@@ -4568,110 +5093,69 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
binlog_[wild_]{do|ignore}_table?" (WL#1049)"
*/
const char *local_db= event_info->get_db();
- if ((!(thd->options & OPTION_BIN_LOG)) ||
+ if ((!(thd->variables.option_bits & OPTION_BIN_LOG)) ||
(thd->lex->sql_command != SQLCOM_ROLLBACK_TO_SAVEPOINT &&
thd->lex->sql_command != SQLCOM_SAVEPOINT &&
!binlog_filter->db_ok(local_db)))
- {
DBUG_RETURN(0);
- }
#endif /* HAVE_REPLICATION */
- my_org_b_tell= my_b_tell(file);
+ IO_CACHE *file= NULL;
-#if defined(USING_TRANSACTIONS)
- /*
- Should we write to the binlog cache or to the binlog on disk?
-
- Write to the binlog cache if:
- 1 - a transactional engine/table is updated (stmt_has_updated_trans_table == TRUE);
- 2 - or the event asks for it (cache_stmt == TRUE);
- 3 - or the cache is already not empty (meaning we're in a transaction;
- note that the present event could be about a non-transactional table, but
- still we need to write to the binlog cache in that case to handle updates
- to mixed trans/non-trans table types).
-
- Write to the binlog on disk if only a non-transactional engine is
- updated and:
- 1 - the binlog cache is empty or;
- 2 - --binlog-direct-non-transactional-updates is set and we are about to
- use the statement format. When using the row format (cache_stmt == TRUE).
- */
- if (opt_using_transactions)
+ if (direct)
+ {
+ file= &log_file;
+ my_org_b_tell= my_b_tell(file);
+ mysql_mutex_lock(&LOCK_log);
+ }
+ else
{
- if (thd->binlog_setup_trx_data())
+ binlog_cache_mngr *const cache_mngr= thd->binlog_setup_trx_data();
+ if (!cache_mngr)
goto err;
- binlog_trx_data *const trx_data=
- (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
- IO_CACHE *trans_log= &trx_data->trans_log;
- my_off_t trans_log_pos= my_b_tell(trans_log);
- if (event_info->get_cache_stmt() || stmt_has_updated_trans_table(thd) ||
- (!thd->variables.binlog_direct_non_trans_update &&
- trans_log_pos != 0))
- {
- DBUG_PRINT("info", ("Using trans_log: cache: %d, trans_log_pos: %lu",
- event_info->get_cache_stmt(),
- (ulong) trans_log_pos));
- thd->binlog_start_trans_and_stmt();
- file= trans_log;
- }
+ 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);
+
+ if (thd->lex->stmt_accessed_non_trans_temp_table())
+ cache_data->set_changes_to_non_trans_temp_table();
+
+ thd->binlog_start_trans_and_stmt();
}
-#endif /* USING_TRANSACTIONS */
DBUG_PRINT("info",("event type: %d",event_info->get_type_code()));
- if (file == &log_file)
- {
- pthread_mutex_lock(&LOCK_log);
- /*
- We did not want to take LOCK_log unless really necessary.
- However, now that we hold LOCK_log, we must check is_open() again, lest
- the log was closed just before.
- */
- if (unlikely(!is_open()))
- {
- pthread_mutex_unlock(&LOCK_log);
- DBUG_RETURN(error);
- }
- event_info->pre_55_writing_direct();
- }
-
- cache_type= event_info->cache_type;
/*
- No check for auto events flag here - this write method should
- never be called if auto-events are enabled
- */
+ No check for auto events flag here - this write method should
+ never be called if auto-events are enabled.
- /*
- 1. Write first log events which describe the 'run environment'
- of the SQL command
+ Write first log events which describe the 'run environment'
+ of the SQL command. If row-based binlogging, Insert_id, Rand
+ and other kind of "setting context" events are not needed.
*/
if (with_annotate && *with_annotate)
{
DBUG_ASSERT(event_info->get_type_code() == TABLE_MAP_EVENT);
- Annotate_rows_log_event anno(thd, cache_type);
+ Annotate_rows_log_event anno(thd, using_trans, direct);
/* Annotate event should be written not more than once */
*with_annotate= 0;
if (anno.write(file))
goto err;
}
- /*
- If row-based binlogging, Insert_id, Rand and other kind of "setting
- context" events are not needed.
- */
+ if (thd)
{
- if (!thd->current_stmt_binlog_row_based)
+ 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,
- cache_type);
+ using_trans, direct);
if (e.write(file))
- goto err_unlock;
+ goto err;
}
if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0)
{
@@ -4680,16 +5164,16 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
nb_elements()));
Intvar_log_event e(thd, (uchar) INSERT_ID_EVENT,
thd->auto_inc_intervals_in_cur_stmt_for_binlog.
- minimum(), cache_type);
+ minimum(), using_trans, direct);
if (e.write(file))
- goto err_unlock;
+ goto err;
}
if (thd->rand_used)
{
Rand_log_event e(thd,thd->rand_saved_seed1,thd->rand_saved_seed2,
- cache_type);
+ using_trans, direct);
if (e.write(file))
- goto err_unlock;
+ goto err;
}
if (thd->user_var_events.elements)
{
@@ -4697,56 +5181,85 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
{
BINLOG_USER_VAR_EVENT *user_var_event;
get_dynamic(&thd->user_var_events,(uchar*) &user_var_event, i);
+
+ /* setting flags for user var log event */
+ uchar flags= User_var_log_event::UNDEF_F;
+ if (user_var_event->unsigned_flag)
+ flags|= User_var_log_event::UNSIGNED_F;
+
User_var_log_event e(thd, user_var_event->user_var_event->name.str,
user_var_event->user_var_event->name.length,
user_var_event->value,
user_var_event->length,
user_var_event->type,
user_var_event->charset_number,
- cache_type);
+ flags,
+ using_trans,
+ direct);
if (e.write(file))
- goto err_unlock;
+ goto err;
}
}
}
}
- /* Write the SQL command */
- if (event_info->write(file) ||
+ /*
+ Write the event.
+ */
+ if (event_info->write(file) ||
DBUG_EVALUATE_IF("injecting_fault_writing", 1, 0))
- goto err_unlock;
+ goto err;
- if (file == &log_file) // we are writing to the real log (disk)
+ error= 0;
+err:
+ if (direct)
{
- ulonglong data_written= (my_b_tell(file) - my_org_b_tell);
- status_var_add(thd->status_var.binlog_bytes_written, data_written);
+ my_off_t offset= my_b_tell(file);
+ bool check_purge= false;
- if (flush_and_sync())
- goto err_unlock;
- signal_update();
- if ((error= rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED)))
- goto err_unlock;
-
- }
- error=0;
+ if (!error)
+ {
+ bool synced;
+
+ if ((error= flush_and_sync(&synced)))
+ {
+ }
+ else if ((error= RUN_HOOK(binlog_storage, after_flush,
+ (thd, log_file_name, file->pos_in_file, synced))))
+ {
+ sql_print_error("Failed to run 'after_flush' hooks");
+ }
+ else
+ {
+ signal_update();
+ if ((error= rotate(false, &check_purge)))
+ check_purge= false;
+ }
+ }
+
+ status_var_add(thd->status_var.binlog_bytes_written,
+ offset - my_org_b_tell);
-err_unlock:
- if (file == &log_file)
- {
- my_off_t offset= my_b_tell(&log_file);
/*
Take mutex to protect against a reader seeing partial writes of 64-bit
offset on 32-bit CPUs.
*/
- pthread_mutex_lock(&LOCK_commit_ordered);
+ mysql_mutex_lock(&LOCK_commit_ordered);
last_commit_pos_offset= offset;
- pthread_mutex_unlock(&LOCK_commit_ordered);
- pthread_mutex_unlock(&LOCK_log);
+ mysql_mutex_unlock(&LOCK_commit_ordered);
+ mysql_mutex_unlock(&LOCK_log);
+
+ if (check_purge)
+ purge();
}
-err:
if (error)
- set_write_error(thd);
+ {
+ set_write_error(thd, is_trans_cache);
+ if (check_write_error(thd) && cache_data &&
+ stmt_has_updated_non_trans_table(thd))
+ cache_data->set_incident();
+ }
}
DBUG_RETURN(error);
@@ -4778,7 +5291,7 @@ bool LOGGER::log_command(THD *thd, enum enum_server_command command)
*/
if (*general_log_handler_list && (what_to_log & (1L << (uint) command)))
{
- if ((thd->options & OPTION_LOG_OFF)
+ if ((thd->variables.option_bits & OPTION_LOG_OFF)
#ifndef NO_EMBEDDED_ACCESS_CHECKS
&& (sctx->master_access & SUPER_ACL)
#endif
@@ -4816,32 +5329,36 @@ bool general_log_write(THD *thd, enum enum_server_command command,
const char *query, uint query_length)
{
/* Write the message to the log if we want to log this king of commands */
- if (logger.log_command(thd, command))
+ if (logger.log_command(thd, command) || mysql_audit_general_enabled())
return logger.general_log_write(thd, command, query, query_length);
return FALSE;
}
/**
+ The method executes rotation when LOCK_log is already acquired
+ by the caller.
+
+ @param force_rotate caller can request the log rotation
+ @param check_purge is set to true if rotation took place
+
@note
If rotation fails, for instance the server was unable
to create a new log file, we still try to write an
incident event to the current log.
@retval
- nonzero - error
+ nonzero - error in rotating routine.
*/
-int MYSQL_BIN_LOG::rotate_and_purge(uint flags)
+int MYSQL_BIN_LOG::rotate(bool force_rotate, bool* check_purge)
{
int error= 0;
- DBUG_ENTER("MYSQL_BIN_LOG::rotate_and_purge");
-#ifdef HAVE_REPLICATION
- bool check_purge= false;
-#endif
- if (!(flags & RP_LOCK_LOG_IS_ALREADY_LOCKED))
- pthread_mutex_lock(&LOCK_log);
- if ((flags & RP_FORCE_ROTATE) ||
- (my_b_tell(&log_file) >= (my_off_t) max_size))
+ DBUG_ENTER("MYSQL_BIN_LOG::rotate");
+
+ //todo: fix the macro def and restore safe_mutex_assert_owner(&LOCK_log);
+ *check_purge= false;
+
+ if (force_rotate || (my_b_tell(&log_file) >= (my_off_t) max_size))
{
if ((error= new_file_without_locking()))
/**
@@ -4854,37 +5371,73 @@ int MYSQL_BIN_LOG::rotate_and_purge(uint flags)
to the current log.
*/
if (!write_incident_already_locked(current_thd))
- flush_and_sync();
+ flush_and_sync(0);
-#ifdef HAVE_REPLICATION
- check_purge= true;
-#endif
- if (flags & RP_BINLOG_CHECKSUM_ALG_CHANGE)
- checksum_alg_reset= BINLOG_CHECKSUM_ALG_UNDEF; // done
+ *check_purge= true;
}
- if (!(flags & RP_LOCK_LOG_IS_ALREADY_LOCKED))
- pthread_mutex_unlock(&LOCK_log);
+ DBUG_RETURN(error);
+}
+
+/**
+ The method executes logs purging routine.
+
+ @retval
+ nonzero - error in rotating routine.
+*/
+void MYSQL_BIN_LOG::purge()
+{
+ mysql_mutex_assert_not_owner(&LOCK_log);
#ifdef HAVE_REPLICATION
- /*
- NOTE: Run purge_logs wo/ holding LOCK_log
- as it otherwise will deadlock in ndbcluster_binlog_index_purge_file
- */
- if (!error && check_purge && expire_logs_days)
+ if (expire_logs_days)
{
+ DEBUG_SYNC(current_thd, "at_purge_logs_before_date");
time_t purge_time= my_time(0) - expire_logs_days*24*60*60;
if (purge_time >= 0)
+ {
purge_logs_before_date(purge_time);
+ }
+ DEBUG_SYNC(current_thd, "after_purge_logs_before_date");
}
#endif
+}
+
+/**
+ The method is a shortcut of @c rotate() and @c purge().
+ LOCK_log is acquired prior to rotate and is released after it.
+
+ @param force_rotate caller can request the log rotation
+
+ @retval
+ nonzero - error in rotating routine.
+*/
+int MYSQL_BIN_LOG::rotate_and_purge(bool force_rotate)
+{
+ int error= 0;
+ DBUG_ENTER("MYSQL_BIN_LOG::rotate_and_purge");
+ bool check_purge= false;
+
+ //todo: fix the macro def and restore safe_mutex_assert_not_owner(&LOCK_log);
+ mysql_mutex_lock(&LOCK_log);
+ if ((error= rotate(force_rotate, &check_purge)))
+ check_purge= false;
+ /*
+ NOTE: Run purge_logs wo/ holding LOCK_log because it does not need
+ the mutex. Otherwise causes various deadlocks.
+ */
+ mysql_mutex_unlock(&LOCK_log);
+
+ if (check_purge)
+ purge();
+
DBUG_RETURN(error);
}
uint MYSQL_BIN_LOG::next_file_id()
{
uint res;
- pthread_mutex_lock(&LOCK_log);
+ mysql_mutex_lock(&LOCK_log);
res = file_id++;
- pthread_mutex_unlock(&LOCK_log);
+ mysql_mutex_unlock(&LOCK_log);
return res;
}
@@ -4935,7 +5488,7 @@ uint MYSQL_BIN_LOG::next_file_id()
int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache)
{
- safe_mutex_assert_owner(&LOCK_log);
+ mysql_mutex_assert_owner(&LOCK_log);
if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
return ER_ERROR_ON_WRITE;
uint length= my_b_bytes_in_cache(cache), group, carry, hdr_offs;
@@ -4972,7 +5525,6 @@ int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache)
do
{
-
/*
if we only got a partial header in the last iteration,
get the other half now and process a full header.
@@ -5162,9 +5714,9 @@ int query_error_code(THD *thd, bool not_killed)
if (not_killed || (killed_mask_hard(thd->killed) == KILL_BAD_DATA))
{
- error= thd->is_error() ? thd->main_da.sql_errno() : 0;
+ error= thd->is_error() ? thd->stmt_da->sql_errno() : 0;
- /* thd->main_da.sql_errno() might be ER_SERVER_SHUTDOWN or
+ /* thd->stmt_da->sql_errno() might be ER_SERVER_SHUTDOWN or
ER_QUERY_INTERRUPTED, So here we need to make sure that error
is not set to these errors when specified not_killed by the
caller.
@@ -5193,7 +5745,6 @@ bool MYSQL_BIN_LOG::write_incident_already_locked(THD *thd)
if (likely(is_open()))
{
- ev.pre_55_writing_direct();
error= ev.write(&log_file);
status_var_add(thd->status_var.binlog_bytes_written, ev.data_written);
}
@@ -5206,27 +5757,33 @@ bool MYSQL_BIN_LOG::write_incident(THD *thd)
{
uint error= 0;
my_off_t offset;
+ bool check_purge= false;
DBUG_ENTER("MYSQL_BIN_LOG::write_incident");
- pthread_mutex_lock(&LOCK_log);
+ mysql_mutex_lock(&LOCK_log);
if (likely(is_open()))
{
if (!(error= write_incident_already_locked(thd)) &&
- !(error= flush_and_sync()))
+ !(error= flush_and_sync(0)))
{
signal_update();
- error= rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED);
+ if ((error= rotate(false, &check_purge)))
+ check_purge= false;
}
+
offset= my_b_tell(&log_file);
/*
Take mutex to protect against a reader seeing partial writes of 64-bit
offset on 32-bit CPUs.
*/
- pthread_mutex_lock(&LOCK_commit_ordered);
+ mysql_mutex_lock(&LOCK_commit_ordered);
last_commit_pos_offset= offset;
- pthread_mutex_unlock(&LOCK_commit_ordered);
+ mysql_mutex_unlock(&LOCK_commit_ordered);
+ mysql_mutex_unlock(&LOCK_log);
+
+ if (check_purge)
+ purge();
}
- pthread_mutex_unlock(&LOCK_log);
DBUG_RETURN(error);
}
@@ -5256,16 +5813,21 @@ bool MYSQL_BIN_LOG::write_incident(THD *thd)
*/
bool
-MYSQL_BIN_LOG::write_transaction_to_binlog(THD *thd, binlog_trx_data *trx_data,
- Log_event *end_ev, bool all)
+MYSQL_BIN_LOG::write_transaction_to_binlog(THD *thd,
+ binlog_cache_mngr *cache_mngr,
+ Log_event *end_ev, bool all,
+ bool using_stmt_cache,
+ bool using_trx_cache)
{
group_commit_entry entry;
DBUG_ENTER("MYSQL_BIN_LOG::write_transaction_to_binlog");
entry.thd= thd;
- entry.trx_data= trx_data;
+ entry.cache_mngr= cache_mngr;
entry.error= 0;
entry.all= all;
+ entry.using_stmt_cache= using_stmt_cache;
+ entry.using_trx_cache= using_trx_cache;
/*
Log "BEGIN" at the beginning of every transaction. Here, a transaction is
@@ -5277,10 +5839,12 @@ MYSQL_BIN_LOG::write_transaction_to_binlog(THD *thd, binlog_trx_data *trx_data,
Due to group commit the actual writing to binlog may happen in a different
thread.
*/
- Query_log_event qinfo(thd, STRING_WITH_LEN("BEGIN"), TRUE, TRUE, 0);
+ Query_log_event qinfo(thd, STRING_WITH_LEN("BEGIN"), using_trx_cache, TRUE,
+ TRUE, 0);
entry.begin_event= &qinfo;
entry.end_event= end_ev;
- if (trx_data->has_incident())
+ 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);
entry.incident_event= &inc_ev;
@@ -5305,18 +5869,18 @@ MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry)
*/
entry->thd->clear_wakeup_ready();
- pthread_mutex_lock(&LOCK_prepare_ordered);
+ mysql_mutex_lock(&LOCK_prepare_ordered);
group_commit_entry *orig_queue= group_commit_queue;
entry->next= orig_queue;
group_commit_queue= entry;
- if (entry->trx_data->using_xa)
+ if (entry->cache_mngr->using_xa)
{
DEBUG_SYNC(entry->thd, "commit_before_prepare_ordered");
run_prepare_ordered(entry->thd, entry->all);
DEBUG_SYNC(entry->thd, "commit_after_prepare_ordered");
}
- pthread_mutex_unlock(&LOCK_prepare_ordered);
+ mysql_mutex_unlock(&LOCK_prepare_ordered);
DEBUG_SYNC(entry->thd, "commit_after_release_LOCK_prepare_ordered");
/*
@@ -5332,21 +5896,21 @@ MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry)
{
/* For the leader, trx_group_commit_leader() already took the lock. */
if (orig_queue != NULL)
- pthread_mutex_lock(&LOCK_commit_ordered);
+ mysql_mutex_lock(&LOCK_commit_ordered);
DEBUG_SYNC(entry->thd, "commit_loop_entry_commit_ordered");
++num_commits;
- if (entry->trx_data->using_xa && !entry->error)
+ if (entry->cache_mngr->using_xa && !entry->error)
run_commit_ordered(entry->thd, entry->all);
group_commit_entry *next= entry->next;
if (!next)
{
group_commit_queue_busy= FALSE;
- pthread_cond_signal(&COND_queue_busy);
+ mysql_cond_signal(&COND_queue_busy);
DEBUG_SYNC(entry->thd, "commit_after_group_run_commit_ordered");
}
- pthread_mutex_unlock(&LOCK_commit_ordered);
+ mysql_mutex_unlock(&LOCK_commit_ordered);
if (next)
{
@@ -5364,7 +5928,7 @@ MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry)
break;
case ER_ERROR_ON_READ:
my_error(ER_ERROR_ON_READ, MYF(ME_NOREFRESH),
- entry->trx_data->trans_log.file_name, entry->commit_errno);
+ entry->error_cache->file_name, entry->commit_errno);
break;
default:
/*
@@ -5382,7 +5946,7 @@ MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry)
we need to mark it as not needed for recovery (unlog() is not called
for a transaction if log_xid() fails).
*/
- if (entry->trx_data->using_xa && entry->trx_data->xa_xid)
+ if (entry->cache_mngr->using_xa && entry->cache_mngr->xa_xid)
mark_xid_done();
return 1;
@@ -5391,7 +5955,7 @@ MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry)
/*
Do binlog group commit as the lead thread.
- This must be called when this thread/transaction is queued at the start of
+ This must be called when this statement/transaction is queued at the start of
the group_commit_queue. It will wait to obtain the LOCK_log mutex, then group
commit all the transactions in the queue (more may have entered while waiting
for LOCK_log). After commit is done, all other threads in the queue will be
@@ -5402,42 +5966,42 @@ void
MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
{
uint xid_count= 0;
- uint write_count= 0;
- my_off_t commit_offset;
+ my_off_t UNINIT_VAR(commit_offset);
group_commit_entry *current;
group_commit_entry *last_in_queue;
- DBUG_ENTER("MYSQL_BIN_LOG::trx_group_commit_leader");
- LINT_INIT(commit_offset);
-
- /*
- Lock the LOCK_log(), and once we get it, collect any additional writes
- that queued up while we were waiting.
- */
- VOID(pthread_mutex_lock(&LOCK_log));
- DEBUG_SYNC(leader->thd, "commit_after_get_LOCK_log");
-
- pthread_mutex_lock(&LOCK_prepare_ordered);
- current= group_commit_queue;
- group_commit_queue= NULL;
- pthread_mutex_unlock(&LOCK_prepare_ordered);
-
- /* As the queue is in reverse order of entering, reverse it. */
group_commit_entry *queue= NULL;
- last_in_queue= current;
- while (current)
- {
- group_commit_entry *next= current->next;
- current->next= queue;
- queue= current;
- current= next;
- }
- DBUG_ASSERT(leader == queue /* the leader should be first in queue */);
+ bool check_purge= false;
+ DBUG_ENTER("MYSQL_BIN_LOG::trx_group_commit_leader");
- /* Now we have in queue the list of transactions to be committed in order. */
DBUG_ASSERT(is_open());
if (likely(is_open())) // Should always be true
{
/*
+ Lock the LOCK_log(), and once we get it, collect any additional writes
+ that queued up while we were waiting.
+ */
+ mysql_mutex_lock(&LOCK_log);
+ DEBUG_SYNC(leader->thd, "commit_after_get_LOCK_log");
+
+ mysql_mutex_lock(&LOCK_prepare_ordered);
+ current= group_commit_queue;
+ group_commit_queue= NULL;
+ mysql_mutex_unlock(&LOCK_prepare_ordered);
+
+ /* As the queue is in reverse order of entering, reverse it. */
+ last_in_queue= current;
+ while (current)
+ {
+ group_commit_entry *next= current->next;
+ current->next= queue;
+ queue= current;
+ current= next;
+ }
+ DBUG_ASSERT(leader == queue /* the leader should be first in queue */);
+
+ /* Now we have in queue the list of transactions to be committed in order. */
+
+ /*
Commit every transaction in the queue.
Note that we are doing this in a different thread than the one running
@@ -5449,46 +6013,60 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
*/
for (current= queue; current != NULL; current= current->next)
{
- binlog_trx_data *trx_data= current->trx_data;
- IO_CACHE *cache= &trx_data->trans_log;
+ binlog_cache_mngr *cache_mngr= current->cache_mngr;
/*
- We only bother to write to the binary log if there is anything
- to write.
+ We already checked before that at least one cache is non-empty; if both
+ are empty we would have skipped calling into here.
*/
- if (my_b_tell(cache) > 0)
- {
- if ((current->error= write_transaction(current)))
- current->commit_errno= errno;
+ DBUG_ASSERT(!cache_mngr->stmt_cache.empty() || !cache_mngr->trx_cache.empty());
- write_count++;
- }
+ current->error= write_transaction_or_stmt(current);
- strmake(trx_data->last_commit_pos_file, log_file_name,
- sizeof(trx_data->last_commit_pos_file)-1);
+ strmake_buf(cache_mngr->last_commit_pos_file, log_file_name);
commit_offset= my_b_write_tell(&log_file);
- trx_data->last_commit_pos_offset= commit_offset;
- if (trx_data->using_xa && trx_data->xa_xid)
+ cache_mngr->last_commit_pos_offset= commit_offset;
+ if (cache_mngr->using_xa && cache_mngr->xa_xid)
xid_count++;
}
- if (write_count > 0)
+ bool synced= 0;
+ if (flush_and_sync(&synced))
{
- if (flush_and_sync())
+ for (current= queue; current != NULL; current= current->next)
{
- for (current= queue; current != NULL; current= current->next)
+ if (!current->error)
{
- if (!current->error)
- {
- current->error= ER_ERROR_ON_WRITE;
- current->commit_errno= errno;
- }
+ current->error= ER_ERROR_ON_WRITE;
+ current->commit_errno= errno;
+ current->error_cache= NULL;
}
}
- else
+ }
+ else
+ {
+ bool any_error= false;
+ bool all_error= true;
+ for (current= queue; current != NULL; current= current->next)
{
- signal_update();
+ if (!current->error &&
+ RUN_HOOK(binlog_storage, after_flush,
+ (current->thd, log_file_name,
+ current->cache_mngr->last_commit_pos_offset, synced)))
+ {
+ current->error= ER_ERROR_ON_WRITE;
+ current->commit_errno= -1;
+ current->error_cache= NULL;
+ any_error= true;
+ }
+ else
+ all_error= false;
}
+
+ if (any_error)
+ sql_print_error("Failed to run 'after_flush' hooks");
+ if (!all_error)
+ signal_update();
}
/*
@@ -5505,7 +6083,7 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
}
else
{
- if (rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED))
+ if (rotate(false, &check_purge))
{
/*
If we fail to rotate, which thread should get the error?
@@ -5514,12 +6092,13 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
*/
last_in_queue->error= ER_ERROR_ON_WRITE;
last_in_queue->commit_errno= errno;
+ check_purge= false;
}
}
}
DEBUG_SYNC(leader->thd, "commit_before_get_LOCK_commit_ordered");
- pthread_mutex_lock(&LOCK_commit_ordered);
+ mysql_mutex_lock(&LOCK_commit_ordered);
last_commit_pos_offset= commit_offset;
/*
We cannot unlock LOCK_log until we have locked LOCK_commit_ordered;
@@ -5527,7 +6106,11 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
messing up the order of commit_ordered() calls. But as soon as
LOCK_commit_ordered is obtained, we can let the next group commit start.
*/
- pthread_mutex_unlock(&LOCK_log);
+ mysql_mutex_unlock(&LOCK_log);
+
+ if (check_purge)
+ purge();
+
DEBUG_SYNC(leader->thd, "commit_after_release_LOCK_log");
++num_group_commits;
@@ -5542,7 +6125,7 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
*/
while (group_commit_queue_busy)
- pthread_cond_wait(&COND_queue_busy, &LOCK_commit_ordered);
+ mysql_cond_wait(&COND_queue_busy, &LOCK_commit_ordered);
group_commit_queue_busy= TRUE;
/* Note that we return with LOCK_commit_ordered locked! */
@@ -5560,7 +6143,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->trx_data->using_xa && !current->error)
+ if (current->cache_mngr->using_xa && !current->error)
run_commit_ordered(current->thd, current->all);
/*
@@ -5573,63 +6156,91 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
current= next;
}
DEBUG_SYNC(leader->thd, "commit_after_group_run_commit_ordered");
- pthread_mutex_unlock(&LOCK_commit_ordered);
+ mysql_mutex_unlock(&LOCK_commit_ordered);
DBUG_VOID_RETURN;
}
+
int
-MYSQL_BIN_LOG::write_transaction(group_commit_entry *entry)
+MYSQL_BIN_LOG::write_transaction_or_stmt(group_commit_entry *entry)
{
- binlog_trx_data *trx_data= entry->trx_data;
- IO_CACHE *cache= &trx_data->trans_log;
+ binlog_cache_mngr *mngr= entry->cache_mngr;
- entry->begin_event->pre_55_writing_direct();
if (entry->begin_event->write(&log_file))
return ER_ERROR_ON_WRITE;
status_var_add(entry->thd->status_var.binlog_bytes_written,
entry->begin_event->data_written);
- DBUG_EXECUTE_IF("crash_before_writing_xid",
- {
- if ((write_cache(entry->thd, cache)))
- DBUG_PRINT("info", ("error writing binlog cache"));
- else
- flush_and_sync();
+ if (entry->using_stmt_cache && !mngr->stmt_cache.empty() &&
+ write_cache(entry->thd, mngr->get_binlog_cache_log(FALSE)))
+ {
+ entry->error_cache= &mngr->stmt_cache.cache_log;
+ entry->commit_errno= errno;
+ return ER_ERROR_ON_WRITE;
+ }
- DBUG_PRINT("info", ("crashing before writing xid"));
- DBUG_SUICIDE();
- });
+ if (entry->using_trx_cache && !mngr->trx_cache.empty())
+ {
+ DBUG_EXECUTE_IF("crash_before_writing_xid",
+ {
+ if ((write_cache(entry->thd,
+ mngr->get_binlog_cache_log(TRUE))))
+ DBUG_PRINT("info", ("error writing binlog cache"));
+ else
+ flush_and_sync(0);
- if (write_cache(entry->thd, cache))
- return ER_ERROR_ON_WRITE;
+ DBUG_PRINT("info", ("crashing before writing xid"));
+ DBUG_SUICIDE();
+ });
+
+ if (write_cache(entry->thd, mngr->get_binlog_cache_log(TRUE)))
+ {
+ entry->error_cache= &mngr->trx_cache.cache_log;
+ entry->commit_errno= errno;
+ return ER_ERROR_ON_WRITE;
+ }
+ }
- entry->end_event->pre_55_writing_direct();
if (entry->end_event->write(&log_file))
+ {
+ entry->error_cache= NULL;
+ entry->commit_errno= errno;
return ER_ERROR_ON_WRITE;
+ }
status_var_add(entry->thd->status_var.binlog_bytes_written,
entry->end_event->data_written);
if (entry->incident_event)
{
- entry->incident_event->pre_55_writing_direct();
if (entry->incident_event->write(&log_file))
+ {
+ entry->error_cache= NULL;
+ entry->commit_errno= errno;
return ER_ERROR_ON_WRITE;
+ }
}
- if (cache->error) // Error on read
+ if (mngr->get_binlog_cache_log(FALSE)->error) // Error on read
+ {
+ entry->error_cache= &mngr->stmt_cache.cache_log;
+ entry->commit_errno= errno;
return ER_ERROR_ON_READ;
+ }
+ if (mngr->get_binlog_cache_log(TRUE)->error) // Error on read
+ {
+ entry->error_cache= &mngr->trx_cache.cache_log;
+ entry->commit_errno= errno;
+ return ER_ERROR_ON_READ;
+ }
return 0;
}
/**
- Wait until we get a signal that the binary log has been updated.
+ Wait until we get a signal that the relay log has been updated.
@param thd Thread variable
- @param is_slave If 0, the caller is the Binlog_dump thread from master;
- if 1, the caller is the SQL thread from the slave. This
- influences only thd->proc_info.
@note
One must have a lock on LOCK_log before calling this function.
@@ -5637,22 +6248,50 @@ MYSQL_BIN_LOG::write_transaction(group_commit_entry *entry)
THD::enter_cond() (see NOTES in sql_class.h).
*/
-void MYSQL_BIN_LOG::wait_for_update(THD* thd, bool is_slave)
+void MYSQL_BIN_LOG::wait_for_update_relay_log(THD* thd)
{
const char *old_msg;
- DBUG_ENTER("wait_for_update");
+ DBUG_ENTER("wait_for_update_relay_log");
old_msg= thd->enter_cond(&update_cond, &LOCK_log,
- is_slave ?
- "Has read all relay log; waiting for the slave I/O "
- "thread to update it" :
- "Has sent all binlog to slave; waiting for binlog "
- "to be updated");
- pthread_cond_wait(&update_cond, &LOCK_log);
+ "Slave has read all relay log; "
+ "waiting for the slave I/O "
+ "thread to update it" );
+ mysql_cond_wait(&update_cond, &LOCK_log);
thd->exit_cond(old_msg);
DBUG_VOID_RETURN;
}
+/**
+ Wait until we get a signal that the binary log has been updated.
+ Applies to master only.
+
+ NOTES
+ @param[in] thd a THD struct
+ @param[in] timeout a pointer to a timespec;
+ NULL means to wait w/o timeout.
+ @retval 0 if got signalled on update
+ @retval non-0 if wait timeout elapsed
+ @note
+ LOCK_log must be taken before calling this function.
+ LOCK_log is being released while the thread is waiting.
+ 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");
+
+ if (!timeout)
+ mysql_cond_wait(&update_cond, &LOCK_log);
+ else
+ ret= mysql_cond_timedwait(&update_cond, &LOCK_log,
+ const_cast<struct timespec *>(timeout));
+ DBUG_RETURN(ret);
+}
+
/**
Close the log file.
@@ -5662,6 +6301,8 @@ void MYSQL_BIN_LOG::wait_for_update(THD* thd, bool is_slave)
- LOG_CLOSE_TO_BE_OPENED : if we intend to call open
at once after close.
- LOG_CLOSE_STOP_EVENT : write a 'stop' event to the log
+ - LOG_CLOSE_DELAYED_CLOSE : do not yet close the file and clear the
+ LOG_EVENT_BINLOG_IN_USE_F flag
@note
One can do an open on the object at once after doing a close.
@@ -5684,7 +6325,6 @@ void MYSQL_BIN_LOG::close(uint exiting)
(uint8) relay_log_checksum_alg : (uint8) binlog_checksum_options;
DBUG_ASSERT(!is_relay_log ||
relay_log_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF);
- s.pre_55_writing_direct();
s.write(&log_file);
bytes_written+= s.data_written;
signal_update();
@@ -5692,19 +6332,18 @@ void MYSQL_BIN_LOG::close(uint exiting)
#endif /* HAVE_REPLICATION */
/* don't pwrite in a file opened with O_APPEND - it doesn't work */
- if (log_file.type == WRITE_CACHE && log_type == LOG_BIN)
+ if (log_file.type == WRITE_CACHE && log_type == LOG_BIN
+ && !(exiting & LOG_CLOSE_DELAYED_CLOSE))
{
- my_off_t offset= BIN_LOG_HEADER_SIZE + FLAGS_OFFSET;
- my_off_t org_position= my_tell(log_file.file, MYF(0));
- uchar flags= 0; // clearing LOG_EVENT_BINLOG_IN_USE_F
- my_pwrite(log_file.file, &flags, 1, offset, MYF(0));
+ my_off_t org_position= mysql_file_tell(log_file.file, MYF(0));
+ clear_inuse_flag_when_closing(log_file.file);
/*
Restore position so that anything we have in the IO_cache is written
to the correct position.
- We need the seek here, as my_pwrite() is not guaranteed to keep the
+ We need the seek here, as mysql_file_pwrite() is not guaranteed to keep the
original position on system that doesn't support pwrite().
*/
- my_seek(log_file.file, org_position, MY_SEEK_SET, MYF(0));
+ mysql_file_seek(log_file.file, org_position, MY_SEEK_SET, MYF(0));
}
/* this will cleanup IO_CACHE, sync and close the file */
@@ -5719,18 +6358,31 @@ void MYSQL_BIN_LOG::close(uint exiting)
if ((exiting & LOG_CLOSE_INDEX) && my_b_inited(&index_file))
{
end_io_cache(&index_file);
- if (my_close(index_file.file, MYF(0)) < 0 && ! write_error)
+ if (mysql_file_close(index_file.file, MYF(0)) < 0 && ! write_error)
{
write_error= 1;
sql_print_error(ER(ER_ERROR_ON_WRITE), index_file_name, errno);
}
}
log_state= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED : LOG_CLOSED;
- safeFree(name);
+ my_free(name);
+ name= NULL;
DBUG_VOID_RETURN;
}
+/*
+ Clear the LOG_EVENT_BINLOG_IN_USE_F; this marks the binlog file as cleanly
+ closed and not needing crash recovery.
+*/
+void MYSQL_BIN_LOG::clear_inuse_flag_when_closing(File file)
+{
+ my_off_t offset= BIN_LOG_HEADER_SIZE + FLAGS_OFFSET;
+ uchar flags= 0; // clearing LOG_EVENT_BINLOG_IN_USE_F
+ mysql_file_pwrite(file, &flags, 1, offset, MYF(0));
+}
+
+
void MYSQL_BIN_LOG::set_max_size(ulong max_size_arg)
{
/*
@@ -5741,10 +6393,10 @@ void MYSQL_BIN_LOG::set_max_size(ulong max_size_arg)
it's like if the SET command was never run.
*/
DBUG_ENTER("MYSQL_BIN_LOG::set_max_size");
- pthread_mutex_lock(&LOCK_log);
+ mysql_mutex_lock(&LOCK_log);
if (is_open())
max_size= max_size_arg;
- pthread_mutex_unlock(&LOCK_log);
+ mysql_mutex_unlock(&LOCK_log);
DBUG_VOID_RETURN;
}
@@ -5763,11 +6415,11 @@ void MYSQL_BIN_LOG::set_max_size(ulong max_size_arg)
@retval
1 String is a number
@retval
- 0 Error
+ 0 String is not a number
*/
static bool test_if_number(register const char *str,
- long *res, bool allow_wildcards)
+ ulong *res, bool allow_wildcards)
{
reg2 int flag;
const char *start;
@@ -5863,10 +6515,10 @@ bool flush_error_log()
bool result= 0;
if (opt_error_log)
{
- VOID(pthread_mutex_lock(&LOCK_error_log));
+ mysql_mutex_lock(&LOCK_error_log);
if (redirect_std_streams(log_error_file))
result= 1;
- VOID(pthread_mutex_unlock(&LOCK_error_log));
+ mysql_mutex_unlock(&LOCK_error_log);
}
return result;
}
@@ -5874,11 +6526,12 @@ bool flush_error_log()
void MYSQL_BIN_LOG::signal_update()
{
DBUG_ENTER("MYSQL_BIN_LOG::signal_update");
- pthread_cond_broadcast(&update_cond);
+ signal_cnt++;
+ mysql_cond_broadcast(&update_cond);
DBUG_VOID_RETURN;
}
-#ifdef __NT__
+#ifdef _WIN32
static void print_buffer_to_nt_eventlog(enum loglevel level, char *buff,
size_t length, size_t buffLen)
{
@@ -5911,7 +6564,7 @@ static void print_buffer_to_nt_eventlog(enum loglevel level, char *buff,
DBUG_VOID_RETURN;
}
-#endif /* __NT__ */
+#endif /* _WIN32 */
#ifndef EMBEDDED_LIBRARY
@@ -5924,7 +6577,7 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer,
DBUG_ENTER("print_buffer_to_file");
DBUG_PRINT("enter",("buffer: %s", buffer));
- VOID(pthread_mutex_lock(&LOCK_error_log));
+ mysql_mutex_lock(&LOCK_error_log);
skr= my_time(0);
localtime_r(&skr, &tm_tmp);
@@ -5943,7 +6596,7 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer,
fflush(stderr);
- VOID(pthread_mutex_unlock(&LOCK_error_log));
+ mysql_mutex_unlock(&LOCK_error_log);
DBUG_VOID_RETURN;
}
@@ -5972,7 +6625,7 @@ int vprint_msg_to_log(enum loglevel level, const char *format, va_list args)
length= my_vsnprintf(buff, sizeof(buff), format, args);
print_buffer_to_file(level, buff, length);
-#ifdef __NT__
+#ifdef _WIN32
print_buffer_to_nt_eventlog(level, buff, length, sizeof(buff));
#endif
@@ -6021,35 +6674,12 @@ void sql_print_information(const char *format, ...)
void
-TC_init()
-{
- my_pthread_mutex_init(&LOCK_prepare_ordered, MY_MUTEX_INIT_SLOW,
- "LOCK_prepare_ordered", MYF(0));
- my_pthread_mutex_init(&LOCK_commit_ordered, MY_MUTEX_INIT_SLOW,
- "LOCK_commit_ordered", MYF(0));
- mutexes_inited= TRUE;
-}
-
-
-void
-TC_destroy()
-{
- if (mutexes_inited)
- {
- pthread_mutex_destroy(&LOCK_prepare_ordered);
- pthread_mutex_destroy(&LOCK_commit_ordered);
- mutexes_inited= FALSE;
- }
-}
-
-
-void
TC_LOG::run_prepare_ordered(THD *thd, bool all)
{
Ha_trx_info *ha_info=
all ? thd->transaction.all.ha_list : thd->transaction.stmt.ha_list;
- safe_mutex_assert_owner(&LOCK_prepare_ordered);
+ mysql_mutex_assert_owner(&LOCK_prepare_ordered);
for (; ha_info; ha_info= ha_info->next())
{
handlerton *ht= ha_info->ht();
@@ -6066,7 +6696,7 @@ TC_LOG::run_commit_ordered(THD *thd, bool all)
Ha_trx_info *ha_info=
all ? thd->transaction.all.ha_list : thd->transaction.stmt.ha_list;
- safe_mutex_assert_owner(&LOCK_commit_ordered);
+ mysql_mutex_assert_owner(&LOCK_commit_ordered);
for (; ha_info; ha_info= ha_info->next())
{
handlerton *ht= ha_info->ht();
@@ -6089,7 +6719,7 @@ int TC_LOG_MMAP::log_and_order(THD *thd, my_xid xid, bool all,
if (need_prepare_ordered)
{
- pthread_mutex_lock(&LOCK_prepare_ordered);
+ mysql_mutex_lock(&LOCK_prepare_ordered);
run_prepare_ordered(thd, all);
if (need_commit_ordered)
{
@@ -6104,7 +6734,7 @@ int TC_LOG_MMAP::log_and_order(THD *thd, my_xid xid, bool all,
commit_ordered_queue= &entry;
is_group_commit_leader= (previous_queue == NULL);
}
- pthread_mutex_unlock(&LOCK_prepare_ordered);
+ mysql_mutex_unlock(&LOCK_prepare_ordered);
}
cookie= 0;
@@ -6126,9 +6756,9 @@ int TC_LOG_MMAP::log_and_order(THD *thd, my_xid xid, bool all,
if (is_group_commit_leader)
{
/* The first in queue starts the ball rolling. */
- pthread_mutex_lock(&LOCK_prepare_ordered);
+ mysql_mutex_lock(&LOCK_prepare_ordered);
while (commit_ordered_queue_busy)
- pthread_cond_wait(&COND_queue_busy, &LOCK_prepare_ordered);
+ mysql_cond_wait(&COND_queue_busy, &LOCK_prepare_ordered);
commit_entry *queue= commit_ordered_queue;
commit_ordered_queue= NULL;
/*
@@ -6136,7 +6766,7 @@ int TC_LOG_MMAP::log_and_order(THD *thd, my_xid xid, bool all,
next.
*/
commit_ordered_queue_busy= true;
- pthread_mutex_unlock(&LOCK_prepare_ordered);
+ mysql_mutex_unlock(&LOCK_prepare_ordered);
/* Reverse the queue list so we get correct order. */
commit_entry *prev= NULL;
@@ -6159,9 +6789,9 @@ int TC_LOG_MMAP::log_and_order(THD *thd, my_xid xid, bool all,
/* Only run commit_ordered() if log_xid was successful. */
if (cookie)
{
- pthread_mutex_lock(&LOCK_commit_ordered);
+ mysql_mutex_lock(&LOCK_commit_ordered);
run_commit_ordered(thd, all);
- pthread_mutex_unlock(&LOCK_commit_ordered);
+ mysql_mutex_unlock(&LOCK_commit_ordered);
}
if (need_prepare_ordered)
@@ -6173,10 +6803,10 @@ int TC_LOG_MMAP::log_and_order(THD *thd, my_xid xid, bool all,
}
else
{
- pthread_mutex_lock(&LOCK_prepare_ordered);
+ mysql_mutex_lock(&LOCK_prepare_ordered);
commit_ordered_queue_busy= false;
- pthread_cond_signal(&COND_queue_busy);
- pthread_mutex_unlock(&LOCK_prepare_ordered);
+ mysql_cond_signal(&COND_queue_busy);
+ mysql_mutex_unlock(&LOCK_prepare_ordered);
}
}
}
@@ -6247,17 +6877,18 @@ int TC_LOG_MMAP::open(const char *opt_name)
DBUG_ASSERT(TC_LOG_PAGE_SIZE % tc_log_page_size == 0);
fn_format(logname,opt_name,mysql_data_home,"",MY_UNPACK_FILENAME);
- if ((fd= my_open(logname, O_RDWR, MYF(0))) < 0)
+ if ((fd= mysql_file_open(key_file_tclog, logname, O_RDWR, MYF(0))) < 0)
{
if (my_errno != ENOENT)
goto err;
if (using_heuristic_recover())
return 1;
- if ((fd= my_create(logname, CREATE_MODE, O_RDWR, MYF(MY_WME))) < 0)
+ if ((fd= mysql_file_create(key_file_tclog, logname, CREATE_MODE,
+ O_RDWR, MYF(MY_WME))) < 0)
goto err;
inited=1;
file_length= opt_tc_log_size;
- if (my_chsize(fd, file_length, 0, MYF(MY_WME)))
+ if (mysql_file_chsize(fd, file_length, 0, MYF(MY_WME)))
goto err;
}
else
@@ -6271,7 +6902,7 @@ int TC_LOG_MMAP::open(const char *opt_name)
"--tc-heuristic-recover is used");
goto err;
}
- file_length= my_seek(fd, 0L, MY_SEEK_END, MYF(MY_WME+MY_FAE));
+ file_length= mysql_file_seek(fd, 0L, MY_SEEK_END, MYF(MY_WME+MY_FAE));
if (file_length == MY_FILEPOS_ERROR || file_length % tc_log_page_size)
goto err;
}
@@ -6295,9 +6926,9 @@ int TC_LOG_MMAP::open(const char *opt_name)
{
pg->next=pg+1;
pg->waiters=0;
- pg->state=POOL;
- pthread_mutex_init(&pg->lock, MY_MUTEX_INIT_FAST);
- pthread_cond_init (&pg->cond, 0);
+ pg->state=PS_POOL;
+ mysql_mutex_init(key_PAGE_lock, &pg->lock, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_PAGE_cond, &pg->cond, 0);
pg->ptr= pg->start=(my_xid *)(data + i*tc_log_page_size);
pg->size=pg->free=tc_log_page_size/sizeof(my_xid);
pg->end=pg->start + pg->size;
@@ -6316,12 +6947,12 @@ int TC_LOG_MMAP::open(const char *opt_name)
my_msync(fd, data, tc_log_page_size, MS_SYNC);
inited=5;
- pthread_mutex_init(&LOCK_sync, MY_MUTEX_INIT_FAST);
- pthread_mutex_init(&LOCK_active, MY_MUTEX_INIT_FAST);
- pthread_mutex_init(&LOCK_pool, MY_MUTEX_INIT_FAST);
- pthread_cond_init(&COND_active, 0);
- pthread_cond_init(&COND_pool, 0);
- pthread_cond_init(&COND_queue_busy, 0);
+ mysql_mutex_init(key_LOCK_sync, &LOCK_sync, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_active, &LOCK_active, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_pool, &LOCK_pool, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_COND_active, &COND_active, 0);
+ mysql_cond_init(key_COND_pool, &COND_pool, 0);
+ mysql_cond_init(key_TC_LOG_MMAP_COND_queue_busy, &COND_queue_busy, 0);
inited=6;
@@ -6357,7 +6988,7 @@ void TC_LOG_MMAP::get_active_from_pool()
PAGE **p, **best_p=0;
int best_free;
- pthread_mutex_lock(&LOCK_pool);
+ mysql_mutex_lock(&LOCK_pool);
do
{
@@ -6377,16 +7008,16 @@ void TC_LOG_MMAP::get_active_from_pool()
}
while ((*best_p == 0 || best_free == 0) && overflow());
- safe_mutex_assert_owner(&LOCK_active);
+ mysql_mutex_assert_owner(&LOCK_active);
active=*best_p;
/* Unlink the page from the pool. */
if (!(*best_p)->next)
pool_last_ptr= best_p;
*best_p=(*best_p)->next;
- pthread_mutex_unlock(&LOCK_pool);
+ mysql_mutex_unlock(&LOCK_pool);
- pthread_mutex_lock(&active->lock);
+ mysql_mutex_lock(&active->lock);
if (active->free == active->size) // we've chosen an empty page
{
tc_log_cur_pages_used++;
@@ -6406,7 +7037,7 @@ int TC_LOG_MMAP::overflow()
let's check the behaviour of tc_log_page_waits first
*/
tc_log_page_waits++;
- pthread_cond_wait(&COND_pool, &LOCK_pool);
+ mysql_cond_wait(&COND_pool, &LOCK_pool);
return 1; // always return 1
}
@@ -6443,7 +7074,7 @@ int TC_LOG_MMAP::log_one_transaction(my_xid xid)
PAGE *p;
ulong cookie;
- pthread_mutex_lock(&LOCK_active);
+ mysql_mutex_lock(&LOCK_active);
/*
if the active page is full - just wait...
@@ -6453,13 +7084,13 @@ int TC_LOG_MMAP::log_one_transaction(my_xid xid)
unlog() does not signal COND_active.
*/
while (unlikely(active && active->free == 0))
- pthread_cond_wait(&COND_active, &LOCK_active);
+ mysql_cond_wait(&COND_active, &LOCK_active);
/* no active page ? take one from the pool */
if (active == 0)
get_active_from_pool();
else
- pthread_mutex_lock(&active->lock);
+ mysql_mutex_lock(&active->lock);
p=active;
@@ -6481,48 +7112,48 @@ int TC_LOG_MMAP::log_one_transaction(my_xid xid)
cookie= (ulong)((uchar *)p->ptr - data); // can never be zero
*p->ptr++= xid;
p->free--;
- p->state= DIRTY;
- pthread_mutex_unlock(&p->lock);
+ p->state= PS_DIRTY;
+ mysql_mutex_unlock(&p->lock);
- pthread_mutex_lock(&LOCK_sync);
+ mysql_mutex_lock(&LOCK_sync);
if (syncing)
{ // somebody's syncing. let's wait
- pthread_mutex_unlock(&LOCK_active);
- pthread_mutex_lock(&p->lock);
+ mysql_mutex_unlock(&LOCK_active);
+ mysql_mutex_lock(&p->lock);
p->waiters++;
- while (p->state == DIRTY && syncing)
+ while (p->state == PS_DIRTY && syncing)
{
- pthread_mutex_unlock(&p->lock);
- pthread_cond_wait(&p->cond, &LOCK_sync);
- pthread_mutex_lock(&p->lock);
+ mysql_mutex_unlock(&p->lock);
+ mysql_cond_wait(&p->cond, &LOCK_sync);
+ mysql_mutex_lock(&p->lock);
}
p->waiters--;
- err= p->state == ERROR;
- if (p->state != DIRTY) // page was synced
+ err= p->state == PS_ERROR;
+ if (p->state != PS_DIRTY) // page was synced
{
- pthread_mutex_unlock(&LOCK_sync);
+ mysql_mutex_unlock(&LOCK_sync);
if (p->waiters == 0)
- pthread_cond_signal(&COND_pool); // in case somebody's waiting
- pthread_mutex_unlock(&p->lock);
+ mysql_cond_signal(&COND_pool); // in case somebody's waiting
+ mysql_mutex_unlock(&p->lock);
goto done; // we're done
}
DBUG_ASSERT(!syncing);
- pthread_mutex_unlock(&p->lock);
+ mysql_mutex_unlock(&p->lock);
syncing = p;
- pthread_mutex_unlock(&LOCK_sync);
+ mysql_mutex_unlock(&LOCK_sync);
- pthread_mutex_lock(&LOCK_active);
+ mysql_mutex_lock(&LOCK_active);
active=0; // page is not active anymore
- pthread_cond_broadcast(&COND_active);
- pthread_mutex_unlock(&LOCK_active);
+ mysql_cond_broadcast(&COND_active);
+ mysql_mutex_unlock(&LOCK_active);
}
else
{
syncing = p; // place is vacant - take it
- pthread_mutex_unlock(&LOCK_sync);
+ mysql_mutex_unlock(&LOCK_sync);
active = 0; // page is not active anymore
- pthread_cond_broadcast(&COND_active);
- pthread_mutex_unlock(&LOCK_active);
+ mysql_cond_broadcast(&COND_active);
+ mysql_mutex_unlock(&LOCK_active);
}
err= sync();
@@ -6543,17 +7174,17 @@ int TC_LOG_MMAP::sync()
err= my_msync(fd, syncing->start, syncing->size * sizeof(my_xid), MS_SYNC);
/* page is synced. let's move it to the pool */
- pthread_mutex_lock(&LOCK_pool);
+ mysql_mutex_lock(&LOCK_pool);
(*pool_last_ptr)=syncing;
pool_last_ptr=&(syncing->next);
syncing->next=0;
- syncing->state= err ? ERROR : POOL;
- pthread_cond_signal(&COND_pool); // in case somebody's waiting
- pthread_mutex_unlock(&LOCK_pool);
+ syncing->state= err ? PS_ERROR : PS_POOL;
+ mysql_cond_signal(&COND_pool); // in case somebody's waiting
+ mysql_mutex_unlock(&LOCK_pool);
/* marking 'syncing' slot free */
- pthread_mutex_lock(&LOCK_sync);
- pthread_cond_broadcast(&syncing->cond); // signal "sync done"
+ mysql_mutex_lock(&LOCK_sync);
+ mysql_cond_broadcast(&syncing->cond); // signal "sync done"
syncing=0;
/*
we check the "active" pointer without LOCK_active. Still, it's safe -
@@ -6564,8 +7195,8 @@ int TC_LOG_MMAP::sync()
(the thread that will send a signal below)
*/
if (active)
- pthread_cond_signal(&active->cond); // wake up a new syncer
- pthread_mutex_unlock(&LOCK_sync);
+ mysql_cond_signal(&active->cond); // wake up a new syncer
+ mysql_mutex_unlock(&LOCK_sync);
return err;
}
@@ -6582,7 +7213,7 @@ int TC_LOG_MMAP::unlog(ulong cookie, my_xid xid)
DBUG_ASSERT(*x == xid);
DBUG_ASSERT(x >= p->start && x < p->end);
- pthread_mutex_lock(&p->lock);
+ mysql_mutex_lock(&p->lock);
*x=0;
p->free++;
DBUG_ASSERT(p->free <= p->size);
@@ -6590,8 +7221,8 @@ int TC_LOG_MMAP::unlog(ulong cookie, my_xid xid)
if (p->free == p->size) // the page is completely empty
statistic_decrement(tc_log_cur_pages_used, &LOCK_status);
if (p->waiters == 0) // the page is in pool and ready to rock
- pthread_cond_signal(&COND_pool); // ping ... for overflow()
- pthread_mutex_unlock(&p->lock);
+ mysql_cond_signal(&COND_pool); // ping ... for overflow()
+ mysql_mutex_unlock(&p->lock);
return 0;
}
@@ -6600,31 +7231,31 @@ void TC_LOG_MMAP::close()
uint i;
switch (inited) {
case 6:
- pthread_mutex_destroy(&LOCK_sync);
- pthread_mutex_destroy(&LOCK_active);
- pthread_mutex_destroy(&LOCK_pool);
- pthread_cond_destroy(&COND_pool);
- pthread_cond_destroy(&COND_active);
- pthread_cond_destroy(&COND_queue_busy);
+ mysql_mutex_destroy(&LOCK_sync);
+ mysql_mutex_destroy(&LOCK_active);
+ mysql_mutex_destroy(&LOCK_pool);
+ mysql_cond_destroy(&COND_pool);
+ mysql_cond_destroy(&COND_active);
+ mysql_cond_destroy(&COND_queue_busy);
case 5:
- data[0]='A'; // garble the first (signature) byte, in case my_delete fails
+ data[0]='A'; // garble the first (signature) byte, in case mysql_file_delete fails
case 4:
for (i=0; i < npages; i++)
{
if (pages[i].ptr == 0)
break;
- pthread_mutex_destroy(&pages[i].lock);
- pthread_cond_destroy(&pages[i].cond);
+ mysql_mutex_destroy(&pages[i].lock);
+ mysql_cond_destroy(&pages[i].cond);
}
case 3:
- my_free((uchar*)pages, MYF(0));
+ my_free(pages);
case 2:
my_munmap((char*)data, (size_t)file_length);
case 1:
- my_close(fd, MYF(0));
+ mysql_file_close(fd, MYF(0));
}
if (inited>=5) // cannot do in the switch because of Windows
- my_delete(logname, MYF(MY_WME));
+ mysql_file_delete(key_file_tclog, logname, MYF(MY_WME));
inited=0;
}
@@ -6652,8 +7283,8 @@ int TC_LOG_MMAP::recover()
goto err1;
}
- if (hash_init(&xids, &my_charset_bin, tc_log_page_size/3, 0,
- sizeof(my_xid), 0, 0, MYF(0)))
+ if (my_hash_init(&xids, &my_charset_bin, tc_log_page_size/3, 0,
+ sizeof(my_xid), 0, 0, MYF(0)))
goto err1;
for ( ; p < end_p ; p++)
@@ -6666,12 +7297,12 @@ int TC_LOG_MMAP::recover()
if (ha_recover(&xids))
goto err2;
- hash_free(&xids);
+ my_hash_free(&xids);
bzero(data, (size_t)file_length);
return 0;
err2:
- hash_free(&xids);
+ my_hash_free(&xids);
err1:
sql_print_error("Crash recovery failed. Either correct the problem "
"(if it's, for example, out of memory error) and restart, "
@@ -6729,8 +7360,9 @@ int TC_LOG_BINLOG::open(const char *opt_name)
DBUG_ASSERT(total_ha_2pc > 1);
DBUG_ASSERT(opt_name && opt_name[0]);
- pthread_mutex_init(&LOCK_prep_xids, MY_MUTEX_INIT_FAST);
- pthread_cond_init (&COND_prep_xids, 0);
+ mysql_mutex_init(key_BINLOG_LOCK_prep_xids,
+ &LOCK_prep_xids, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_BINLOG_COND_prep_xids, &COND_prep_xids, 0);
if (!my_b_inited(&index_file))
{
@@ -6769,7 +7401,7 @@ int TC_LOG_BINLOG::open(const char *opt_name)
do
{
- strmake(log_name, log_info.log_file_name, sizeof(log_name)-1);
+ strmake_buf(log_name, log_info.log_file_name);
} while (!(error= find_next_log(&log_info, 1)));
if (error != LOG_INFO_EOF)
@@ -6797,7 +7429,7 @@ int TC_LOG_BINLOG::open(const char *opt_name)
delete ev;
end_io_cache(&log);
- my_close(file, MYF(MY_WME));
+ mysql_file_close(file, MYF(MY_WME));
if (error)
goto err;
@@ -6811,8 +7443,8 @@ err:
void TC_LOG_BINLOG::close()
{
DBUG_ASSERT(prepared_xids==0);
- pthread_mutex_destroy(&LOCK_prep_xids);
- pthread_cond_destroy (&COND_prep_xids);
+ mysql_mutex_destroy(&LOCK_prep_xids);
+ mysql_cond_destroy(&COND_prep_xids);
}
/*
@@ -6827,27 +7459,13 @@ TC_LOG_BINLOG::log_and_order(THD *thd, my_xid xid, bool all,
int err;
DBUG_ENTER("TC_LOG_BINLOG::log_and_order");
- binlog_trx_data *const trx_data=
- (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
+ binlog_cache_mngr *cache_mngr= thd->binlog_setup_trx_data();
+ if (!cache_mngr)
+ DBUG_RETURN(0);
- trx_data->using_xa= TRUE;
- trx_data->xa_xid= xid;
- if (xid)
- {
- Xid_log_event xid_event(thd, xid);
- err= binlog_flush_trx_cache(thd, trx_data, &xid_event, all);
- }
- else
- {
- /*
- Empty xid occurs in XA COMMIT ... ONE PHASE.
- In this case, we do not have a MySQL xid for the transaction, and the
- external XA transaction coordinator will have to handle recovery if
- needed. So we end the transaction with a plain COMMIT query event.
- */
- Query_log_event end_event(thd, STRING_WITH_LEN("COMMIT"), TRUE, TRUE, 0);
- err= binlog_flush_trx_cache(thd, trx_data, &end_event, all);
- }
+ cache_mngr->using_xa= TRUE;
+ cache_mngr->xa_xid= xid;
+ err= binlog_commit_flush_xid_caches(thd, cache_mngr, all, xid);
DEBUG_SYNC(thd, "binlog_after_log_and_order");
@@ -6870,9 +7488,9 @@ TC_LOG_BINLOG::mark_xids_active(uint xid_count)
{
DBUG_ENTER("TC_LOG_BINLOG::mark_xids_active");
DBUG_PRINT("info", ("xid_count=%u", xid_count));
- pthread_mutex_lock(&LOCK_prep_xids);
+ mysql_mutex_lock(&LOCK_prep_xids);
prepared_xids+= xid_count;
- pthread_mutex_unlock(&LOCK_prep_xids);
+ mysql_mutex_unlock(&LOCK_prep_xids);
DBUG_VOID_RETURN;
}
@@ -6889,13 +7507,16 @@ TC_LOG_BINLOG::mark_xid_done()
my_bool send_signal;
DBUG_ENTER("TC_LOG_BINLOG::mark_xid_done");
- pthread_mutex_lock(&LOCK_prep_xids);
- DBUG_ASSERT(prepared_xids > 0);
- send_signal= !--prepared_xids;
- pthread_mutex_unlock(&LOCK_prep_xids);
+ mysql_mutex_lock(&LOCK_prep_xids);
+ // prepared_xids can be 0 if the transaction had ignorable errors.
+ DBUG_ASSERT(prepared_xids >= 0);
+ if (prepared_xids > 0)
+ prepared_xids--;
+ send_signal= (prepared_xids == 0);
+ mysql_mutex_unlock(&LOCK_prep_xids);
if (send_signal) {
DBUG_PRINT("info", ("prepared_xids=%lu", prepared_xids));
- pthread_cond_signal(&COND_prep_xids);
+ mysql_cond_signal(&COND_prep_xids);
}
DBUG_VOID_RETURN;
}
@@ -6916,8 +7537,8 @@ int TC_LOG_BINLOG::recover(IO_CACHE *log, Format_description_log_event *fdle)
MEM_ROOT mem_root;
if (! fdle->is_valid() ||
- hash_init(&xids, &my_charset_bin, TC_LOG_PAGE_SIZE/3, 0,
- sizeof(my_xid), 0, 0, MYF(0)))
+ my_hash_init(&xids, &my_charset_bin, TC_LOG_PAGE_SIZE/3, 0,
+ sizeof(my_xid), 0, 0, MYF(0)))
goto err1;
init_alloc_root(&mem_root, TC_LOG_PAGE_SIZE, TC_LOG_PAGE_SIZE);
@@ -6943,12 +7564,12 @@ int TC_LOG_BINLOG::recover(IO_CACHE *log, Format_description_log_event *fdle)
goto err2;
free_root(&mem_root, MYF(0));
- hash_free(&xids);
+ my_hash_free(&xids);
return 0;
err2:
free_root(&mem_root, MYF(0));
- hash_free(&xids);
+ my_hash_free(&xids);
err1:
sql_print_error("Crash recovery failed. Either correct the problem "
"(if it's, for example, out of memory error) and restart, "
@@ -6990,12 +7611,12 @@ ulonglong mysql_bin_log_file_pos(void)
void
mysql_bin_log_commit_pos(THD *thd, ulonglong *out_pos, const char **out_file)
{
- binlog_trx_data *const trx_data=
- (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
- if (trx_data)
+ binlog_cache_mngr *cache_mngr;
+ if (opt_bin_log &&
+ (cache_mngr= (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton)))
{
- *out_file= trx_data->last_commit_pos_file;
- *out_pos= (ulonglong)(trx_data->last_commit_pos_offset);
+ *out_file= cache_mngr->last_commit_pos_file;
+ *out_pos= (ulonglong)(cache_mngr->last_commit_pos_offset);
}
else
{
@@ -7011,24 +7632,25 @@ binlog_checksum_update(MYSQL_THD thd, struct st_mysql_sys_var *var,
void *var_ptr, const void *save)
{
ulong value= *((ulong *)save);
+ bool check_purge= false;
- pthread_mutex_lock(mysql_bin_log.get_log_lock());
+ mysql_mutex_lock(mysql_bin_log.get_log_lock());
if(mysql_bin_log.is_open())
{
- uint flags= RP_FORCE_ROTATE | RP_LOCK_LOG_IS_ALREADY_LOCKED |
- (binlog_checksum_options != (uint) value?
- RP_BINLOG_CHECKSUM_ALG_CHANGE : 0);
- if (flags & RP_BINLOG_CHECKSUM_ALG_CHANGE)
+ if (binlog_checksum_options != value)
mysql_bin_log.checksum_alg_reset= (uint8) value;
- mysql_bin_log.rotate_and_purge(flags);
+ if (mysql_bin_log.rotate(true, &check_purge))
+ check_purge= false;
}
else
{
binlog_checksum_options= value;
}
- DBUG_ASSERT((ulong) binlog_checksum_options == value);
- DBUG_ASSERT(mysql_bin_log.checksum_alg_reset == BINLOG_CHECKSUM_ALG_UNDEF);
- pthread_mutex_unlock(mysql_bin_log.get_log_lock());
+ DBUG_ASSERT(binlog_checksum_options == value);
+ mysql_bin_log.checksum_alg_reset= BINLOG_CHECKSUM_ALG_UNDEF;
+ mysql_mutex_unlock(mysql_bin_log.get_log_lock());
+ if (check_purge)
+ mysql_bin_log.purge();
}
@@ -7041,7 +7663,7 @@ static int show_binlog_vars(THD *thd, SHOW_VAR *var, char *buff)
}
static SHOW_VAR binlog_status_vars_top[]= {
- {"binlog", (char *) &show_binlog_vars, SHOW_FUNC},
+ {"Binlog", (char *) &show_binlog_vars, SHOW_FUNC},
{NullS, NullS, SHOW_LONG}
};
@@ -7087,7 +7709,7 @@ static void
set_binlog_snapshot_file(const char *src)
{
int dir_len = dirname_length(src);
- strmake(binlog_snapshot_file, src + dir_len, sizeof(binlog_snapshot_file)-1);
+ strmake_buf(binlog_snapshot_file, src + dir_len);
}
/*
@@ -7099,15 +7721,15 @@ set_binlog_snapshot_file(const char *src)
void
TC_LOG_BINLOG::set_status_variables(THD *thd)
{
- binlog_trx_data *trx_data;
+ binlog_cache_mngr *cache_mngr;
- if (thd)
- trx_data= (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
+ if (thd && opt_bin_log)
+ cache_mngr= (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
else
- trx_data= NULL;
+ cache_mngr= 0;
- bool have_snapshot= (trx_data && trx_data->last_commit_pos_file[0] != 0);
- pthread_mutex_lock(&LOCK_commit_ordered);
+ bool have_snapshot= (cache_mngr && cache_mngr->last_commit_pos_file[0] != 0);
+ mysql_mutex_lock(&LOCK_commit_ordered);
binlog_status_var_num_commits= this->num_commits;
binlog_status_var_num_group_commits= this->num_group_commits;
if (!have_snapshot)
@@ -7115,34 +7737,18 @@ TC_LOG_BINLOG::set_status_variables(THD *thd)
set_binlog_snapshot_file(last_commit_pos_file);
binlog_snapshot_position= last_commit_pos_offset;
}
- pthread_mutex_unlock(&LOCK_commit_ordered);
+ mysql_mutex_unlock(&LOCK_commit_ordered);
if (have_snapshot)
{
- set_binlog_snapshot_file(trx_data->last_commit_pos_file);
- binlog_snapshot_position= trx_data->last_commit_pos_offset;
+ set_binlog_snapshot_file(cache_mngr->last_commit_pos_file);
+ binlog_snapshot_position= cache_mngr->last_commit_pos_offset;
}
}
struct st_mysql_storage_engine binlog_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
-mysql_declare_plugin(binlog)
-{
- MYSQL_STORAGE_ENGINE_PLUGIN,
- &binlog_storage_engine,
- "binlog",
- "MySQL AB",
- "This is a pseudo storage engine to represent the binlog in a transaction",
- PLUGIN_LICENSE_GPL,
- binlog_init, /* Plugin Init */
- NULL, /* Plugin Deinit */
- 0x0100 /* 1.0 */,
- binlog_status_vars_top, /* status variables */
- binlog_sys_vars, /* system variables */
- NULL /* config options */
-}
-mysql_declare_plugin_end;
maria_declare_plugin(binlog)
{
MYSQL_STORAGE_ENGINE_PLUGIN,
diff --git a/sql/log.h b/sql/log.h
index 156a8e6f1b6..4eb23d4878f 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -1,5 +1,5 @@
-/*
- Copyright (c) 2005, 2012, Oracle and/or its affiliates.
+/* Copyright (c) 2005, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2012, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,19 +12,24 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef LOG_H
#define LOG_H
+#include "unireg.h" // REQUIRED: for other includes
+#include "handler.h" /* my_xid */
+
class Relay_log_info;
class Format_description_log_event;
-bool ending_trans(const THD* thd, const bool all);
+bool trans_has_updated_trans_table(const THD* thd);
+bool stmt_has_updated_trans_table(const THD *thd);
+bool use_trans_cache(const THD* thd, bool is_transactional);
+bool ending_trans(THD* thd, const bool all);
+bool ending_single_stmt_trans(THD* thd, const bool all);
bool trans_has_updated_non_trans_table(const THD* thd);
-bool trans_has_no_stmt_committed(const THD* thd, const bool all);
bool stmt_has_updated_non_trans_table(const THD* thd);
/*
@@ -69,11 +74,11 @@ protected:
and TC_LOG::run_commit_ordered(), or any other code that calls handler
prepare_ordered() or commit_ordered() methods.
*/
-extern pthread_mutex_t LOCK_prepare_ordered;
-extern pthread_mutex_t LOCK_commit_ordered;
-
-extern void TC_init();
-extern void TC_destroy();
+extern mysql_mutex_t LOCK_prepare_ordered;
+extern mysql_mutex_t LOCK_commit_ordered;
+#ifdef HAVE_PSI_INTERFACE
+extern PSI_mutex_key key_LOCK_prepare_ordered, key_LOCK_commit_ordered;
+#endif
class TC_LOG_DUMMY: public TC_LOG // use it to disable the logging
{
@@ -100,9 +105,9 @@ class TC_LOG_MMAP: public TC_LOG
{
public: // only to keep Sun Forte on sol9x86 happy
typedef enum {
- POOL, // page is in pool
- ERROR, // last sync failed
- DIRTY // new xids added since last sync
+ PS_POOL, // page is in pool
+ PS_ERROR, // last sync failed
+ PS_DIRTY // new xids added since last sync
} PAGE_STATE;
private:
@@ -113,8 +118,8 @@ class TC_LOG_MMAP: public TC_LOG
int size, free; // max and current number of free xid slots on the page
int waiters; // number of waiters on condition
PAGE_STATE state; // see above
- pthread_mutex_t lock; // to access page data or control structure
- pthread_cond_t cond; // to wait for a sync
+ mysql_mutex_t lock; // to access page data or control structure
+ mysql_cond_t cond; // to wait for a sync
} PAGE;
/* List of THDs for which to invoke commit_ordered(), in order. */
@@ -136,8 +141,8 @@ class TC_LOG_MMAP: public TC_LOG
one has to use active->lock.
Same for LOCK_pool and LOCK_sync
*/
- pthread_mutex_t LOCK_active, LOCK_pool, LOCK_sync;
- pthread_cond_t COND_pool, COND_active;
+ mysql_mutex_t LOCK_active, LOCK_pool, LOCK_sync;
+ mysql_cond_t COND_pool, COND_active;
/*
Queue of threads that need to call commit_ordered().
Access to this queue must be protected by LOCK_prepare_ordered.
@@ -156,8 +161,8 @@ class TC_LOG_MMAP: public TC_LOG
The condition is used together with the LOCK_prepare_ordered mutex.
*/
+ mysql_cond_t COND_queue_busy;
my_bool commit_ordered_queue_busy;
- pthread_cond_t COND_queue_busy;
public:
TC_LOG_MMAP(): inited(0) {}
@@ -197,9 +202,27 @@ extern TC_LOG_DUMMY tc_log_dummy;
#define LOG_CLOSE_INDEX 1
#define LOG_CLOSE_TO_BE_OPENED 2
#define LOG_CLOSE_STOP_EVENT 4
+#define LOG_CLOSE_DELAYED_CLOSE 8
+
+/*
+ Maximum unique log filename extension.
+ Note: setting to 0x7FFFFFFF due to atol windows
+ overflow/truncate.
+ */
+#define MAX_LOG_UNIQUE_FN_EXT 0x7FFFFFFF
+
+/*
+ Number of warnings that will be printed to error log
+ before extension number is exhausted.
+*/
+#define LOG_WARN_UNIQUE_FN_EXT_LEFT 1000
class Relay_log_info;
+#ifdef HAVE_PSI_INTERFACE
+extern PSI_mutex_key key_LOG_INFO_lock;
+#endif
+
/*
Note that we destroy the lock mutex in the desctructor here.
This means that object instances cannot be destroyed/go out of scope,
@@ -211,15 +234,21 @@ typedef struct st_log_info
my_off_t index_file_offset, index_file_start_offset;
my_off_t pos;
bool fatal; // if the purge happens to give us a negative offset
- pthread_mutex_t lock;
- st_log_info()
- : index_file_offset(0), index_file_start_offset(0),
+ mysql_mutex_t lock;
+ st_log_info() : index_file_offset(0), index_file_start_offset(0),
pos(0), fatal(0)
- {
- log_file_name[0] = '\0';
- pthread_mutex_init(&lock, MY_MUTEX_INIT_FAST);
- }
- ~st_log_info() { pthread_mutex_destroy(&lock);}
+ {
+ DBUG_ENTER("LOG_INFO");
+ log_file_name[0] = '\0';
+ mysql_mutex_init(key_LOG_INFO_lock, &lock, MY_MUTEX_INIT_FAST);
+ DBUG_VOID_RETURN;
+ }
+ ~st_log_info()
+ {
+ DBUG_ENTER("~LOG_INFO");
+ mysql_mutex_destroy(&lock);
+ DBUG_VOID_RETURN;
+ }
} LOG_INFO;
/*
@@ -250,7 +279,11 @@ public:
MYSQL_LOG();
void init_pthread_objects();
void cleanup();
- bool open(const char *log_name,
+ bool open(
+#ifdef HAVE_PSI_INTERFACE
+ PSI_file_key log_file_key,
+#endif
+ const char *log_name,
enum_log_type log_type,
const char *new_name,
enum cache_type io_cache_type_arg);
@@ -267,7 +300,7 @@ public:
int generate_new_name(char *new_name, const char *log_name);
protected:
/* LOCK_log is inited by init_pthread_objects() */
- pthread_mutex_t LOCK_log;
+ mysql_mutex_t LOCK_log;
char *name;
char log_file_name[FN_REFLEN];
char time_buff[20], db[NAME_LEN + 1];
@@ -277,6 +310,10 @@ public:
volatile enum_log_state log_state;
enum cache_type io_cache_type;
friend class Log_event;
+#ifdef HAVE_PSI_INTERFACE
+ /** Instrumentation key to use for file io in @c log_file */
+ PSI_file_key m_log_file_key;
+#endif
};
class MYSQL_QUERY_LOG: public MYSQL_LOG
@@ -295,29 +332,52 @@ public:
bool open_slow_log(const char *log_name)
{
char buf[FN_REFLEN];
- return open(generate_name(log_name, "-slow.log", 0, buf), LOG_NORMAL, 0,
- WRITE_CACHE);
+ return open(
+#ifdef HAVE_PSI_INTERFACE
+ key_file_slow_log,
+#endif
+ generate_name(log_name, "-slow.log", 0, buf),
+ LOG_NORMAL, 0, WRITE_CACHE);
}
bool open_query_log(const char *log_name)
{
char buf[FN_REFLEN];
- return open(generate_name(log_name, ".log", 0, buf), LOG_NORMAL, 0,
- WRITE_CACHE);
+ return open(
+#ifdef HAVE_PSI_INTERFACE
+ key_file_query_log,
+#endif
+ generate_name(log_name, ".log", 0, buf),
+ LOG_NORMAL, 0, WRITE_CACHE);
}
private:
time_t last_time;
};
-class binlog_trx_data;
+class binlog_cache_mngr;
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 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;
+#endif
+
struct group_commit_entry
{
struct group_commit_entry *next;
THD *thd;
- binlog_trx_data *trx_data;
+ binlog_cache_mngr *cache_mngr;
+ bool using_stmt_cache;
+ bool using_trx_cache;
/*
Extra events (BEGIN, COMMIT/ROLLBACK/XID, and possibly INCIDENT) to be
written during group commit. The incident_event is only valid if
@@ -329,16 +389,16 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
/* Set during group commit to record any per-thread error. */
int error;
int commit_errno;
+ IO_CACHE *error_cache;
/* This is the `all' parameter for ha_commit_ordered(). */
bool all;
- /* True if we come in through XA log_and_order(), false otherwise. */
};
/* LOCK_log and LOCK_index are inited by init_pthread_objects() */
- pthread_mutex_t LOCK_index;
- pthread_mutex_t LOCK_prep_xids;
- pthread_cond_t COND_prep_xids;
- pthread_cond_t update_cond;
+ mysql_mutex_t LOCK_index;
+ mysql_mutex_t LOCK_prep_xids;
+ mysql_cond_t COND_prep_xids;
+ mysql_cond_t update_cond;
ulonglong bytes_written;
IO_CACHE index_file;
char index_file_name[FN_REFLEN];
@@ -384,12 +444,24 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
Used with the LOCK_commit_ordered mutex.
*/
my_bool group_commit_queue_busy;
- pthread_cond_t COND_queue_busy;
+ mysql_cond_t COND_queue_busy;
/* Total number of committed transactions. */
ulonglong num_commits;
/* Number of group commits done. */
ulonglong num_group_commits;
+ /* pointer to the sync period variable, for binlog this will be
+ sync_binlog_period, for relay log this will be
+ sync_relay_log_period
+ */
+ uint *sync_period_ptr;
+ uint sync_counter;
+
+ inline uint get_sync_period()
+ {
+ return *sync_period_ptr;
+ }
+
int write_to_file(IO_CACHE *cache);
/*
This is used to start writing to a new log file. The difference from
@@ -398,7 +470,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
*/
int new_file_without_locking();
int new_file_impl(bool need_lock);
- int write_transaction(group_commit_entry *entry);
+ int write_transaction_or_stmt(group_commit_entry *entry);
bool write_transaction_to_binlog_events(group_commit_entry *entry);
void trx_group_commit_leader(group_commit_entry *leader);
void mark_xid_done();
@@ -410,6 +482,7 @@ public:
/* This is relay log */
bool is_relay_log;
+ ulong signal_cnt; // update of the counter is checked by heartbeat
uint8 checksum_alg_reset; // to contain a new value when binlog is rotated
/*
Holds the last seen in Relay-Log FD's checksum alg value.
@@ -462,13 +535,28 @@ public:
char last_commit_pos_file[FN_REFLEN];
my_off_t last_commit_pos_offset;
- MYSQL_BIN_LOG();
+ MYSQL_BIN_LOG(uint *sync_period);
/*
note that there's no destructor ~MYSQL_BIN_LOG() !
The reason is that we don't want it to be automatically called
on exit() - but only during the correct shutdown process
*/
+#ifdef HAVE_PSI_INTERFACE
+ void set_psi_keys(PSI_mutex_key key_LOCK_index,
+ PSI_cond_key key_update_cond,
+ PSI_file_key key_file_log,
+ PSI_file_key key_file_log_index,
+ PSI_file_key key_COND_queue_busy)
+ {
+ m_key_LOCK_index= key_LOCK_index;
+ m_key_update_cond= key_update_cond;
+ 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;
+ }
+#endif
+
int open(const char *opt_name);
void close();
int log_and_order(THD *thd, my_xid xid, bool all,
@@ -476,8 +564,10 @@ public:
int unlog(ulong cookie, my_xid xid);
int recover(IO_CACHE *log, Format_description_log_event *fdle);
#if !defined(MYSQL_CLIENT)
- int flush_and_set_pending_rows_event(THD *thd, Rows_log_event* event);
- int remove_pending_rows_event(THD *thd);
+
+ int flush_and_set_pending_rows_event(THD *thd, Rows_log_event* event,
+ bool is_transactional);
+ int remove_pending_rows_event(THD *thd, bool is_transactional);
#endif /* !defined(MYSQL_CLIENT) */
void reset_bytes_written()
@@ -498,7 +588,8 @@ public:
}
void set_max_size(ulong max_size_arg);
void signal_update();
- void wait_for_update(THD* thd, bool master_or_slave);
+ void wait_for_update_relay_log(THD* thd);
+ int wait_for_update_bin_log(THD* thd, const struct timespec * timeout);
void set_need_start_event() { need_start_event = 1; }
void init(bool no_auto_events_arg, ulong max_size);
void init_pthread_objects();
@@ -515,16 +606,16 @@ public:
/* Use this to start writing a new log file */
int new_file();
- void reset_gathered_updates(THD *thd);
bool write(Log_event* event_info,
my_bool *with_annotate= 0); // binary log write
- bool write_transaction_to_binlog(THD *thd, binlog_trx_data *trx_data,
- Log_event *end_ev, bool all);
+ bool write_transaction_to_binlog(THD *thd, binlog_cache_mngr *cache_mngr,
+ Log_event *end_ev, bool all,
+ bool using_stmt_cache, bool using_trx_cache);
bool write_incident_already_locked(THD *thd);
bool write_incident(THD *thd);
int write_cache(THD *thd, IO_CACHE *cache);
- void set_write_error(THD *thd);
+ void set_write_error(THD *thd, bool is_transactional);
bool check_write_error(THD *thd);
void start_union_events(THD *thd, query_id_t query_id_param);
@@ -541,8 +632,23 @@ public:
void make_log_name(char* buf, const char* log_ident);
bool is_active(const char* log_file_name);
int update_log_index(LOG_INFO* linfo, bool need_update_threads);
- int rotate_and_purge(uint flags);
- bool flush_and_sync();
+ int rotate(bool force_rotate, bool* check_purge);
+ void purge();
+ int rotate_and_purge(bool force_rotate);
+ /**
+ Flush binlog cache and synchronize to disk.
+
+ This function flushes events in binlog cache to binary log file,
+ it will do synchronizing according to the setting of system
+ variable 'sync_binlog'. If file is synchronized, @c synced will
+ be set to 1, otherwise 0.
+
+ @param[out] synced if not NULL, set to 1 if file is synchronized, otherwise 0
+
+ @retval 0 Success
+ @retval other Failure
+ */
+ bool flush_and_sync(bool *synced);
int purge_logs(const char *to_log, bool included,
bool need_mutex, bool need_update_threads,
ulonglong *decrease_log_space);
@@ -560,6 +666,7 @@ public:
bool need_mutex);
bool reset_logs(THD* thd);
void close(uint exiting);
+ void clear_inuse_flag_when_closing(File file);
// iterating through the log index file
int find_log_pos(LOG_INFO* linfo, const char* log_name,
@@ -571,11 +678,12 @@ public:
inline char* get_index_fname() { return index_file_name;}
inline char* get_log_fname() { return log_file_name; }
inline char* get_name() { return name; }
- inline pthread_mutex_t* get_log_lock() { return &LOCK_log; }
+ inline mysql_mutex_t* get_log_lock() { return &LOCK_log; }
+ inline mysql_cond_t* get_log_cond() { return &update_cond; }
inline IO_CACHE* get_log_file() { return &log_file; }
- inline void lock_index() { pthread_mutex_lock(&LOCK_index);}
- inline void unlock_index() { pthread_mutex_unlock(&LOCK_index);}
+ inline void lock_index() { mysql_mutex_lock(&LOCK_index);}
+ inline void unlock_index() { mysql_mutex_unlock(&LOCK_index);}
inline IO_CACHE *get_index_file() { return &index_file;}
inline uint32 get_open_count() { return open_count; }
void set_status_variables(THD *thd);
@@ -604,8 +712,8 @@ public:
};
-int check_if_log_table(uint db_len, const char *db, uint table_name_len,
- const char *table_name, uint check_if_opened);
+int check_if_log_table(size_t db_len, const char *db, size_t table_name_len,
+ const char *table_name, bool check_if_opened);
class Log_to_csv_event_handler: public Log_event_handler
{
@@ -662,7 +770,6 @@ public:
const char *sql_text, uint sql_text_len,
CHARSET_INFO *client_cs);
void flush();
- void flush_slow_log();
void init_pthread_objects();
MYSQL_QUERY_LOG *get_mysql_slow_log() { return &mysql_slow_log; }
MYSQL_QUERY_LOG *get_mysql_log() { return &mysql_log; }
@@ -672,7 +779,7 @@ public:
/* Class which manages slow, general and error log event handlers */
class LOGGER
{
- rw_lock_t LOCK_logger;
+ mysql_rwlock_t LOCK_logger;
/* flag to check whether logger mutex is initialized */
uint inited;
@@ -692,9 +799,9 @@ public:
LOGGER() : inited(0), table_log_handler(NULL),
file_log_handler(NULL), is_log_tables_initialized(FALSE)
{}
- void lock_shared() { rw_rdlock(&LOCK_logger); }
- void lock_exclusive() { rw_wrlock(&LOCK_logger); }
- void unlock() { rw_unlock(&LOCK_logger); }
+ void lock_shared() { mysql_rwlock_rdlock(&LOCK_logger); }
+ void lock_exclusive() { mysql_rwlock_wrlock(&LOCK_logger); }
+ void unlock() { mysql_rwlock_unlock(&LOCK_logger); }
bool is_log_table_enabled(uint log_table_type);
bool log_command(THD *thd, enum enum_server_command command);
@@ -707,7 +814,8 @@ public:
void init_base();
void init_log_tables();
bool flush_logs(THD *thd);
- bool flush_slow_log(THD *thd);
+ bool flush_slow_log();
+ bool flush_general_log();
/* Perform basic logger cleanup. this will leave e.g. error log open. */
void cleanup_base();
/* Free memory. Nothing could be logged after this function is called */
@@ -722,21 +830,21 @@ public:
const char *query, uint query_length);
/* we use this function to setup all enabled log event handlers */
- int set_handlers(uint error_log_printer,
- uint slow_log_printer,
- uint general_log_printer);
- void init_error_log(uint error_log_printer);
- void init_slow_log(uint slow_log_printer);
- void init_general_log(uint general_log_printer);
+ int set_handlers(ulonglong error_log_printer,
+ ulonglong slow_log_printer,
+ ulonglong general_log_printer);
+ void init_error_log(ulonglong error_log_printer);
+ void init_slow_log(ulonglong slow_log_printer);
+ void init_general_log(ulonglong general_log_printer);
void deactivate_log_handler(THD* thd, uint log_type);
bool activate_log_handler(THD* thd, uint log_type);
- MYSQL_QUERY_LOG *get_slow_log_file_handler()
+ MYSQL_QUERY_LOG *get_slow_log_file_handler() const
{
if (file_log_handler)
return file_log_handler->get_mysql_slow_log();
return NULL;
}
- MYSQL_QUERY_LOG *get_log_file_handler()
+ MYSQL_QUERY_LOG *get_log_file_handler() const
{
if (file_log_handler)
return file_log_handler->get_mysql_log();
@@ -745,22 +853,114 @@ public:
};
enum enum_binlog_format {
- /*
- statement-based except for cases where only row-based can work (UUID()
- etc):
- */
- BINLOG_FORMAT_MIXED= 0,
- BINLOG_FORMAT_STMT= 1, // statement-based
- BINLOG_FORMAT_ROW= 2, // row_based
-/*
- This value is last, after the end of binlog_format_typelib: it has no
- corresponding cell in this typelib. We use this value to be able to know if
- the user has explicitely specified a binlog format at startup or not.
-*/
- BINLOG_FORMAT_UNSPEC= 3
+ BINLOG_FORMAT_MIXED= 0, ///< statement if safe, otherwise row - autodetected
+ BINLOG_FORMAT_STMT= 1, ///< statement-based
+ BINLOG_FORMAT_ROW= 2, ///< row-based
+ BINLOG_FORMAT_UNSPEC=3 ///< thd_binlog_format() returns it when binlog is closed
};
-extern TYPELIB binlog_format_typelib;
int query_error_code(THD *thd, bool not_killed);
+uint purge_log_get_error_code(int res);
+
+int vprint_msg_to_log(enum loglevel level, const char *format, va_list args);
+void sql_print_error(const char *format, ...);
+void sql_print_warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
+void sql_print_information(const char *format, ...)
+ ATTRIBUTE_FORMAT(printf, 1, 2);
+typedef void (*sql_print_message_func)(const char *format, ...)
+ ATTRIBUTE_FORMAT_FPTR(printf, 1, 2);
+extern sql_print_message_func sql_print_message_handlers[];
+
+int error_log_print(enum loglevel level, const char *format,
+ va_list args);
+
+bool slow_log_print(THD *thd, const char *query, uint query_length,
+ ulonglong current_utime);
+
+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);
+
+void sql_perror(const char *message);
+bool flush_error_log();
+
+File open_binlog(IO_CACHE *log, const char *log_file_name,
+ const char **errmsg);
+
+void make_default_log_name(char **out, const char* log_ext, bool once);
+
+extern MYSQL_PLUGIN_IMPORT MYSQL_BIN_LOG mysql_bin_log;
+extern LOGGER logger;
+
+
+/**
+ Turns a relative log binary log path into a full path, based on the
+ opt_bin_logname or opt_relay_logname.
+
+ @param from The log name we want to make into an absolute path.
+ @param to The buffer where to put the results of the
+ normalization.
+ @param is_relay_log Switch that makes is used inside to choose which
+ option (opt_bin_logname or opt_relay_logname) to
+ use when calculating the base path.
+
+ @returns true if a problem occurs, false otherwise.
+ */
+
+inline bool normalize_binlog_name(char *to, const char *from, bool is_relay_log)
+{
+ DBUG_ENTER("normalize_binlog_name");
+ bool error= false;
+ char buff[FN_REFLEN];
+ char *ptr= (char*) from;
+ char *opt_name= is_relay_log ? opt_relay_logname : opt_bin_logname;
+
+ DBUG_ASSERT(from);
+
+ /* opt_name is not null and not empty and from is a relative path */
+ if (opt_name && opt_name[0] && from && !test_if_hard_path(from))
+ {
+ // take the path from opt_name
+ // take the filename from from
+ char log_dirpart[FN_REFLEN], log_dirname[FN_REFLEN];
+ size_t log_dirpart_len, log_dirname_len;
+ dirname_part(log_dirpart, opt_name, &log_dirpart_len);
+ dirname_part(log_dirname, from, &log_dirname_len);
+
+ /* log may be empty => relay-log or log-bin did not
+ hold paths, just filename pattern */
+ if (log_dirpart_len > 0)
+ {
+ /* create the new path name */
+ if(fn_format(buff, from+log_dirname_len, log_dirpart, "",
+ MYF(MY_UNPACK_FILENAME | MY_SAFE_PATH)) == NULL)
+ {
+ error= true;
+ goto end;
+ }
+
+ ptr= buff;
+ }
+ }
+
+ DBUG_ASSERT(ptr);
+
+ if (ptr)
+ strmake(to, ptr, strlen(ptr));
+
+end:
+ DBUG_RETURN(error);
+}
+
+static inline TC_LOG *get_tc_log_implementation()
+{
+ if (total_ha_2pc <= 1)
+ return &tc_log_dummy;
+ if (opt_bin_log)
+ return &mysql_bin_log;
+ return &tc_log_mmap;
+}
#endif /* LOG_H */
diff --git a/sql/log_event.cc b/sql/log_event.cc
index e6ae1bbb732..6b21a303a50 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2000, 2014, Oracle and/or its affiliates.
Copyright (c) 2009, 2014, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
@@ -13,13 +13,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-1301 USA
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifdef MYSQL_CLIENT
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "mysqld_error.h"
#else
@@ -27,13 +27,25 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "my_global.h" // REQUIRED by log_event.h > m_string.h > my_bitmap.h
+#include "log_event.h"
+#include "sql_base.h" // close_thread_tables
+#include "sql_cache.h" // QUERY_CACHE_FLAGS_SIZE
+#include "sql_locale.h" // MY_LOCALE, my_locale_by_number, my_locale_en_US
+#include "key.h" // key_copy
+#include "lock.h" // mysql_unlock_tables
+#include "sql_parse.h" // mysql_test_parse_for_slave
+#include "tztime.h" // struct Time_zone
+#include "sql_load.h" // mysql_load
+#include "sql_db.h" // load_db_opt_by_name
#include "slave.h"
#include "rpl_rli.h"
#include "rpl_mi.h"
#include "rpl_filter.h"
-#include "rpl_utility.h"
#include "rpl_record.h"
+#include "transaction.h"
#include <my_dir.h>
#include "sql_show.h" // append_identifier
@@ -41,6 +53,7 @@
#include <base64.h>
#include <my_bitmap.h>
+#include "rpl_utility.h"
/**
@@ -203,7 +216,7 @@ static void inline slave_rows_error_report(enum loglevel level, int ha_error,
char buff[MAX_SLAVE_ERRMSG], *slider;
const char *buff_end= buff + sizeof(buff);
uint len;
- List_iterator_fast<MYSQL_ERROR> it(thd->warn_list);
+ List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
MYSQL_ERROR *err;
buff[0]= 0;
@@ -211,11 +224,12 @@ static void inline slave_rows_error_report(enum loglevel level, int ha_error,
slider += len, err= it++)
{
len= my_snprintf(slider, buff_end - slider,
- " %s, Error_code: %d;", err->msg, err->code);
+ " %s, Error_code: %d;", err->get_message_text(),
+ err->get_sql_errno());
}
if (ha_error != 0)
- rli->report(level, thd->is_error() ? thd->main_da.sql_errno() : 0,
+ rli->report(level, thd->is_error() ? thd->stmt_da->sql_errno() : 0,
"Could not execute %s event on table %s.%s;"
"%s handler error %s; "
"the event's master log %s, end_log_pos %lu",
@@ -223,7 +237,7 @@ static void inline slave_rows_error_report(enum loglevel level, int ha_error,
buff, handler_error == NULL ? "<unknown>" : handler_error,
log_name, pos);
else
- rli->report(level, thd->is_error() ? thd->main_da.sql_errno() : 0,
+ rli->report(level, thd->is_error() ? thd->stmt_da->sql_errno() : 0,
"Could not execute %s event on table %s.%s;"
"%s the event's master log %s, end_log_pos %lu",
type, table->s->db.str, table->s->table_name.str,
@@ -427,13 +441,13 @@ inline int ignored_error_code(int err_code)
*/
int convert_handler_error(int error, THD* thd, TABLE *table)
{
- uint actual_error= (thd->is_error() ? thd->main_da.sql_errno() :
+ uint actual_error= (thd->is_error() ? thd->stmt_da->sql_errno() :
0);
if (actual_error == 0)
{
table->file->print_error(error, MYF(0));
- actual_error= (thd->is_error() ? thd->main_da.sql_errno() :
+ actual_error= (thd->is_error() ? thd->stmt_da->sql_errno() :
ER_UNKNOWN_ERROR);
if (actual_error == ER_UNKNOWN_ERROR)
if (global_system_variables.log_warnings)
@@ -570,7 +584,7 @@ static void cleanup_load_tmpdir()
if (is_prefix(file->name, prefbuf))
{
fn_format(fname,file->name,slave_load_tmpdir,"",MY_UNPACK_FILENAME);
- my_delete(fname, MYF(0));
+ mysql_file_delete(key_file_misc, fname, MYF(0));
}
}
@@ -609,16 +623,18 @@ static inline int read_str(const char **buf, const char *buf_end,
/**
- Transforms a string into "" or its expression in 0x... form.
+ Transforms a string into "" or its expression in X'HHHH' form.
*/
char *str_to_hex(char *to, const char *from, uint len)
{
if (len)
{
- *to++= '0';
- *to++= 'x';
+ *to++= 'X';
+ *to++= '\'';
to= octet2hex(to, from, len);
+ *to++= '\'';
+ *to= '\0';
}
else
to= strmov(to, "\"\"");
@@ -639,7 +655,7 @@ append_query_string(THD *thd, CHARSET_INFO *csinfo,
{
char *beg, *ptr;
uint32 const orig_len= to->length();
- if (to->reserve(orig_len + from->length()*2+3))
+ if (to->reserve(orig_len + from->length() * 2 + 4))
return 1;
beg= (char*) to->ptr() + to->length();
@@ -751,16 +767,22 @@ const char* Log_event::get_type_str()
#ifndef MYSQL_CLIENT
Log_event::Log_event(THD* thd_arg, uint16 flags_arg, bool using_trans)
- :log_pos(0), temp_buf(0), exec_time(0), flags(flags_arg),
- cache_type(EVENT_INVALID_CACHE), crc(0), thd(thd_arg),
+ :log_pos(0), temp_buf(0), exec_time(0),
+ crc(0), thd(thd_arg),
checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF)
{
server_id= thd->server_id;
when= thd->start_time;
when_sec_part=thd->start_time_sec_part;
- cache_stmt= using_trans;
-}
+ if (using_trans)
+ cache_type= Log_event::EVENT_TRANSACTIONAL_CACHE;
+ else
+ cache_type= Log_event::EVENT_STMT_CACHE;
+ flags= flags_arg |
+ (thd->variables.option_bits & OPTION_SKIP_REPLICATION ?
+ LOG_EVENT_SKIP_REPLICATION_F : 0);
+}
/**
This minimal constructor is for when you are not even sure that there
@@ -770,8 +792,8 @@ Log_event::Log_event(THD* thd_arg, uint16 flags_arg, bool using_trans)
*/
Log_event::Log_event()
- :temp_buf(0), exec_time(0), flags(0), cache_stmt(0),
- cache_type(EVENT_INVALID_CACHE), crc(0),
+ :temp_buf(0), exec_time(0), flags(0),
+ cache_type(Log_event::EVENT_INVALID_CACHE), crc(0),
thd(0), checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF)
{
server_id= ::server_id;
@@ -792,7 +814,7 @@ Log_event::Log_event()
Log_event::Log_event(const char* buf,
const Format_description_log_event* description_event)
- :temp_buf(0), cache_stmt(0), cache_type(EVENT_INVALID_CACHE),
+ :temp_buf(0), cache_type(Log_event::EVENT_INVALID_CACHE),
crc(0), checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF)
{
#ifndef MYSQL_CLIENT
@@ -889,8 +911,8 @@ int Log_event::do_update_pos(Relay_log_info *rli)
if (debug_not_change_ts_if_art_event == 1
&& is_artificial_event())
debug_not_change_ts_if_art_event= 0; );
- rli->stmt_done(log_pos, is_artificial_event()
- IF_DBUG(&& debug_not_change_ts_if_art_event > 0) ?
+ rli->stmt_done(log_pos, is_artificial_event() &&
+ IF_DBUG(debug_not_change_ts_if_art_event > 0, 1) ?
0 : when);
DBUG_EXECUTE_IF("let_first_flush_log_change_timestamp",
if (debug_not_change_ts_if_art_event == 0)
@@ -910,7 +932,9 @@ Log_event::do_shall_skip(Relay_log_info *rli)
rli->replicate_same_server_id,
rli->slave_skip_counter));
if ((server_id == ::server_id && !rli->replicate_same_server_id) ||
- (rli->slave_skip_counter == 1 && rli->is_in_group()))
+ (rli->slave_skip_counter == 1 && rli->is_in_group()) ||
+ (flags & LOG_EVENT_SKIP_REPLICATION_F &&
+ opt_replicate_events_marked_for_skip != RPL_SKIP_REPLICATE))
return EVENT_SKIP_IGNORE;
if (rli->slave_skip_counter > 0)
return EVENT_SKIP_COUNT;
@@ -1082,6 +1106,9 @@ bool Log_event::write_header(IO_CACHE* file, ulong event_data_length)
ulong now;
bool ret;
DBUG_ENTER("Log_event::write_header");
+ DBUG_PRINT("enter", ("filepos: %lld length: %lu type: %d",
+ (longlong) my_b_tell(file), event_data_length,
+ (int) get_type_code()));
/* Store number of bytes that will be written by this event */
data_written= event_data_length + sizeof(header);
@@ -1189,7 +1216,7 @@ bool Log_event::write_header(IO_CACHE* file, ulong event_data_length)
*/
int Log_event::read_log_event(IO_CACHE* file, String* packet,
- pthread_mutex_t* log_lock,
+ mysql_mutex_t* log_lock,
uint8 checksum_alg_arg,
const char *log_file_name_arg,
bool* is_binlog_active)
@@ -1201,22 +1228,11 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet,
DBUG_ENTER("Log_event::read_log_event");
if (log_lock)
- pthread_mutex_lock(log_lock);
+ mysql_mutex_lock(log_lock);
if (log_file_name_arg)
*is_binlog_active= mysql_bin_log.is_active(log_file_name_arg);
- DBUG_EXECUTE_IF("dump_fake_io_error",
- {
- if (log_lock)
- {
- pthread_mutex_unlock(log_lock);
-
- DBUG_SET("-d,dump_fake_io_error");
- DBUG_RETURN(LOG_READ_IO);
- }
- });
-
if (my_b_read(file, (uchar*) buf, sizeof(buf)))
{
/*
@@ -1299,14 +1315,14 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet,
end:
if (log_lock)
- pthread_mutex_unlock(log_lock);
+ mysql_mutex_unlock(log_lock);
DBUG_RETURN(result);
}
#endif /* !MYSQL_CLIENT */
#ifndef MYSQL_CLIENT
-#define UNLOCK_MUTEX if (log_lock) pthread_mutex_unlock(log_lock);
-#define LOCK_MUTEX if (log_lock) pthread_mutex_lock(log_lock);
+#define UNLOCK_MUTEX if (log_lock) mysql_mutex_unlock(log_lock);
+#define LOCK_MUTEX if (log_lock) mysql_mutex_lock(log_lock);
#else
#define UNLOCK_MUTEX
#define LOCK_MUTEX
@@ -1318,7 +1334,7 @@ end:
Allocates memory; The caller is responsible for clean-up.
*/
Log_event* Log_event::read_log_event(IO_CACHE* file,
- pthread_mutex_t* log_lock,
+ mysql_mutex_t* log_lock,
const Format_description_log_event
*description_event,
my_bool crc_check)
@@ -1362,7 +1378,7 @@ failed my_b_read"));
Log_event *res= 0;
#ifndef max_allowed_packet
THD *thd=current_thd;
- uint max_allowed_packet= thd ? slave_max_allowed_packet:~(ulong)0;
+ uint max_allowed_packet= thd ? slave_max_allowed_packet:~(uint)0;
#endif
if (data_len > max_allowed_packet)
@@ -1401,7 +1417,7 @@ err:
sql_print_error("Error in Log_event::read_log_event(): "
"'%s', data_len: %d, event_type: %d",
error,data_len,head[EVENT_TYPE_OFFSET]);
- my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(buf);
/*
The SQL slave thread will check if file->error<0 to know
if there was an I/O error. Even if there is no "low-level" I/O errors
@@ -1524,15 +1540,11 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
*/
if (description_event->event_type_permutation)
{
- IF_DBUG({
- int new_event_type=
- description_event->event_type_permutation[event_type];
- DBUG_PRINT("info",
- ("converting event type %d to %d (%s)",
- event_type, new_event_type,
- get_type_str((Log_event_type)new_event_type)));
- });
- event_type= description_event->event_type_permutation[event_type];
+ int new_event_type= description_event->event_type_permutation[event_type];
+ DBUG_PRINT("info", ("converting event type %d to %d (%s)",
+ event_type, new_event_type,
+ get_type_str((Log_event_type)new_event_type)));
+ event_type= new_event_type;
}
if (alg != BINLOG_CHECKSUM_ALG_UNDEF &&
@@ -1801,6 +1813,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
@@ -1816,6 +1829,10 @@ my_b_write_quoted(IO_CACHE *file, const uchar *ptr, uint length)
{
if (*s > 0x1F)
my_b_write(file, s, 1);
+ else if (*s == '\'')
+ my_b_write(file, "\\'", 2);
+ else if (*s == '\\')
+ my_b_write(file, "\\\\", 2);
else
{
uchar hex[10];
@@ -1925,37 +1942,14 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr,
/* a long CHAR() field: see #37426 */
length= byte1 | (((byte0 & 0x30) ^ 0x30) << 4);
type= byte0 | 0x30;
- goto beg;
- }
-
- switch (byte0)
- {
- case MYSQL_TYPE_SET:
- case MYSQL_TYPE_ENUM:
- case MYSQL_TYPE_STRING:
- type= byte0;
- length= byte1;
- 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);
- return 0;
- }
}
+ else
+ length = meta & 0xFF;
}
else
length= meta;
}
-
-beg:
-
switch (type) {
case MYSQL_TYPE_LONG:
{
@@ -2093,6 +2087,33 @@ beg:
return 3;
}
+ case MYSQL_TYPE_NEWDATE:
+ {
+ uint32 tmp= uint3korr(ptr);
+ int part;
+ char buf[11];
+ char *pos= &buf[10]; // start from '\0' to the beginning
+
+ /* Copied from field.cc */
+ *pos--=0; // End NULL
+ part=(int) (tmp & 31);
+ *pos--= (char) ('0'+part%10);
+ *pos--= (char) ('0'+part/10);
+ *pos--= ':';
+ part=(int) (tmp >> 5 & 15);
+ *pos--= (char) ('0'+part%10);
+ *pos--= (char) ('0'+part/10);
+ *pos--= ':';
+ part=(int) (tmp >> 9);
+ *pos--= (char) ('0'+part%10); part/=10;
+ *pos--= (char) ('0'+part%10); part/=10;
+ *pos--= (char) ('0'+part%10); part/=10;
+ *pos= (char) ('0'+part);
+ my_b_printf(file , "'%s'", buf);
+ my_snprintf(typestr, typestr_length, "DATE");
+ return 3;
+ }
+
case MYSQL_TYPE_DATE:
{
uint i32= uint3korr(ptr);
@@ -2111,7 +2132,7 @@ beg:
}
case MYSQL_TYPE_ENUM:
- switch (length) {
+ switch (meta & 0xFF) {
case 1:
my_b_printf(file, "%d", (int) *ptr);
my_snprintf(typestr, typestr_length, "ENUM(1 byte)");
@@ -2124,15 +2145,15 @@ beg:
return 2;
}
default:
- my_b_printf(file, "!! Unknown ENUM packlen=%d", length);
+ my_b_printf(file, "!! Unknown ENUM packlen=%d", meta & 0xFF);
return 0;
}
break;
case MYSQL_TYPE_SET:
- my_b_write_bit(file, ptr , length * 8);
- my_snprintf(typestr, typestr_length, "SET(%d bytes)", length);
- return length;
+ my_b_write_bit(file, ptr , (meta & 0xFF) * 8);
+ my_snprintf(typestr, typestr_length, "SET(%d bytes)", meta & 0xFF);
+ return meta & 0xFF;
case MYSQL_TYPE_BLOB:
switch (meta) {
@@ -2230,6 +2251,14 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
else
{
my_b_printf(file, "### @%d=", i + 1);
+ size_t fsize= td->calc_field_size((uint)i, (uchar*) value);
+ if (value + fsize > m_rows_end)
+ {
+ my_b_printf(file, "***Corrupted replication event was detected."
+ " Not printing the value***\n");
+ value+= fsize;
+ return 0;
+ }
size_t size= log_event_print_value(file, value,
td->type(i), td->field_metadata(i),
typestr, sizeof(typestr));
@@ -2406,7 +2435,7 @@ void Log_event::print_base64(IO_CACHE* file,
}
}
- my_free(tmp_str, MYF(0));
+ my_free(tmp_str);
DBUG_VOID_RETURN;
}
@@ -2421,9 +2450,7 @@ void Log_event::print_timestamp(IO_CACHE* file, time_t* ts)
time_t my_when= when;
DBUG_ENTER("Log_event::print_timestamp");
if (!ts)
- {
ts = &my_when;
- }
res=localtime(ts);
my_b_printf(file,"%02d%02d%02d %2d:%02d:%02d",
@@ -2703,7 +2730,6 @@ bool Query_log_event::write(IO_CACHE* file)
memcpy(start, host.str, host.length);
start+= host.length;
}
-
}
if (thd && thd->query_start_sec_part_used)
@@ -2777,7 +2803,7 @@ Query_log_event::Query_log_event()
*/
Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
ulong query_length, bool using_trans,
- bool suppress_use, int errcode)
+ bool direct, bool suppress_use, int errcode)
:Log_event(thd_arg,
(thd_arg->thread_specific_used ? LOG_EVENT_THREAD_SPECIFIC_F :
@@ -2839,7 +2865,7 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
the autocommit flag as written by the master to the binlog. This
behavior may change after WL#4162 has been implemented.
*/
- flags2= (uint32) (thd_arg->options &
+ flags2= (uint32) (thd_arg->variables.option_bits &
(OPTIONS_WRITTEN_TO_BIN_LOG & ~OPTION_NOT_AUTOCOMMIT));
DBUG_ASSERT(thd_arg->variables.character_set_client->number < 256*256);
DBUG_ASSERT(thd_arg->variables.collation_connection->number < 256*256);
@@ -2860,7 +2886,64 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
}
else
time_zone_len= 0;
- DBUG_PRINT("info",("Query_log_event has flags2: %lu sql_mode: %lu",
+
+ LEX *lex= thd->lex;
+ /*
+ Defines that the statement will be written directly to the binary log
+ without being wrapped by a BEGIN...COMMIT. Otherwise, the statement
+ will be written to either the trx-cache or stmt-cache.
+
+ Note that a cache will not be used if the parameter direct is TRUE.
+ */
+ bool use_cache= FALSE;
+ /*
+ TRUE defines that the trx-cache must be used and by consequence the
+ use_cache is TRUE.
+
+ Note that a cache will not be used if the parameter direct is TRUE.
+ */
+ bool trx_cache= FALSE;
+ cache_type= Log_event::EVENT_INVALID_CACHE;
+
+ switch (lex->sql_command)
+ {
+ case SQLCOM_DROP_TABLE:
+ use_cache= (lex->drop_temporary && thd->in_multi_stmt_transaction_mode());
+ break;
+
+ case SQLCOM_CREATE_TABLE:
+ trx_cache= (lex->select_lex.item_list.elements &&
+ thd->is_current_stmt_binlog_format_row());
+ use_cache= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&
+ thd->in_multi_stmt_transaction_mode()) || trx_cache;
+ break;
+ case SQLCOM_SET_OPTION:
+ use_cache= trx_cache= (lex->autocommit ? FALSE : TRUE);
+ break;
+ case SQLCOM_RELEASE_SAVEPOINT:
+ case SQLCOM_ROLLBACK_TO_SAVEPOINT:
+ case SQLCOM_SAVEPOINT:
+ use_cache= trx_cache= TRUE;
+ break;
+ default:
+ use_cache= sqlcom_can_generate_row_events(thd);
+ break;
+ }
+
+ if (!use_cache || direct)
+ {
+ cache_type= Log_event::EVENT_NO_CACHE;
+ }
+ else if (using_trans || trx_cache || stmt_has_updated_trans_table(thd) ||
+ thd->lex->is_mixed_stmt_unsafe(thd->in_multi_stmt_transaction_mode(),
+ thd->variables.binlog_direct_non_trans_update,
+ trans_has_updated_trans_table(thd),
+ thd->tx_isolation))
+ cache_type= Log_event::EVENT_TRANSACTIONAL_CACHE;
+ else
+ cache_type= Log_event::EVENT_STMT_CACHE;
+ DBUG_ASSERT(cache_type != Log_event::EVENT_INVALID_CACHE);
+ DBUG_PRINT("info",("Query_log_event has flags2: %lu sql_mode: %llu",
(ulong) flags2, sql_mode));
}
#endif /* MYSQL_CLIENT */
@@ -3465,6 +3548,34 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli)
return do_apply_event(rli, query, q_len);
}
+/**
+ Compare if two errors should be regarded as equal.
+ This is to handle the case when you can get slightly different errors
+ on master and slave for the same thing.
+ @param
+ expected_error Error we got on master
+ actual_error Error we got on slave
+
+ @return
+ 1 Errors are equal
+ 0 Errors are different
+*/
+
+bool test_if_equal_repl_errors(int expected_error, int actual_error)
+{
+ if (expected_error == actual_error)
+ return 1;
+ switch (expected_error) {
+ case ER_DUP_ENTRY:
+ case ER_AUTOINC_READ_FAILED:
+ return (actual_error == ER_AUTOINC_READ_FAILED ||
+ actual_error == HA_ERR_AUTOINC_ERANGE);
+ default:
+ break;
+ }
+ return 0;
+}
+
/**
@todo
@@ -3489,6 +3600,7 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
LEX_STRING new_db;
int expected_error,actual_error= 0;
HA_CREATE_INFO db_options;
+ DBUG_ENTER("Query_log_event::do_apply_event");
/*
Colleagues: please never free(thd->catalog) in MySQL. This would
@@ -3537,7 +3649,7 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
if ((error= rows_event_stmt_cleanup(const_cast<Relay_log_info*>(rli), thd)))
{
const_cast<Relay_log_info*>(rli)->report(ERROR_LEVEL, error,
- "Error in cleaning up after an event preceeding the commit; "
+ "Error in cleaning up after an event preceding the commit; "
"the group log file/position: %s %s",
const_cast<Relay_log_info*>(rli)->group_master_log_name,
llstr(const_cast<Relay_log_info*>(rli)->group_master_log_pos,
@@ -3554,7 +3666,7 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
}
else
{
- const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
+ const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
}
/*
@@ -3570,10 +3682,8 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
if (is_trans_keyword() || rpl_filter->db_ok(thd->db))
{
thd->set_time(when, when_sec_part);
- thd->set_query((char*)query_arg, q_len_arg);
- VOID(pthread_mutex_lock(&LOCK_thread_count));
- thd->query_id = next_query_id();
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ thd->set_query_and_id((char*)query_arg, q_len_arg,
+ thd->charset(), next_query_id());
thd->variables.pseudo_thread_id= thread_id; // for temp tables
DBUG_PRINT("query",("%s", thd->query()));
@@ -3582,13 +3692,13 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
{
if (flags2_inited)
/*
- all bits of thd->options which are 1 in OPTIONS_WRITTEN_TO_BIN_LOG
+ all bits of thd->variables.option_bits which are 1 in OPTIONS_WRITTEN_TO_BIN_LOG
must take their value from flags2.
*/
- thd->options= flags2|(thd->options & ~OPTIONS_WRITTEN_TO_BIN_LOG);
+ thd->variables.option_bits= flags2|(thd->variables.option_bits & ~OPTIONS_WRITTEN_TO_BIN_LOG);
/*
else, we are in a 3.23/4.0 binlog; we previously received a
- Rotate_log_event which reset thd->options and sql_mode etc, so
+ Rotate_log_event which reset thd->variables.option_bits and sql_mode etc, so
nothing to do.
*/
/*
@@ -3625,6 +3735,18 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
goto compare_errors;
}
thd->update_charset(); // for the charset change to take effect
+ /*
+ Reset thd->query_string.cs to the newly set value.
+ Note, there is a small flaw here. For a very short time frame
+ if the new charset is different from the old charset and
+ if another thread executes "SHOW PROCESSLIST" after
+ the above thd->set_query_and_id() and before this thd->set_query(),
+ and if the current query has some non-ASCII characters,
+ the another thread may see some '?' marks in the PROCESSLIST
+ result. This should be acceptable now. This is a reminder
+ to fix this if any refactoring happens here sometime.
+ */
+ thd->set_query((char*) query_arg, q_len_arg, thd->charset());
}
}
if (time_zone_len)
@@ -3667,10 +3789,30 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
thd->table_map_for_update= (table_map)table_map_for_update;
thd->set_invoker(&user, &host);
+ /*
+ Flag if we need to rollback the statement transaction on
+ slave if it by chance succeeds.
+ If we expected a non-zero error code and get nothing and,
+ it is a concurrency issue or ignorable issue, effects
+ of the statement should be rolled back.
+ */
+ if (expected_error &&
+ (ignored_error_code(expected_error) ||
+ concurrency_error_code(expected_error)))
+ {
+ thd->variables.option_bits|= OPTION_MASTER_SQL_ERROR;
+ }
/* Execute the query (note that we bypass dispatch_command()) */
- const char* found_semicolon= NULL;
- mysql_parse(thd, thd->query(), thd->query_length(), &found_semicolon);
- log_slow_statement(thd);
+ Parser_state parser_state;
+ if (!parser_state.init(thd, thd->query(), thd->query_length()))
+ {
+ mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);
+ /* Finalize server status flags after executing a statement. */
+ thd->update_server_status();
+ log_slow_statement(thd);
+ }
+
+ thd->variables.option_bits&= ~OPTION_MASTER_SQL_ERROR;
/*
Resetting the enable_slow_log thd variable.
@@ -3710,7 +3852,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() || thd->main_da.sql_errno() != ER_SLAVE_IGNORED_TABLE)
+ if (!thd->is_error() || thd->stmt_da->sql_errno() != ER_SLAVE_IGNORED_TABLE)
general_log_write(thd, COM_QUERY, thd->query(), thd->query_length());
else
{
@@ -3727,7 +3869,6 @@ START SLAVE; . Query: '%s'", expected_error, thd->query());
}
compare_errors:
-
/*
In the slave thread, we may sometimes execute some DROP / * 40005
TEMPORARY * / TABLE that come from parts of binlogs (likely if we
@@ -3736,20 +3877,22 @@ compare_errors:
not exist errors", we silently clear the error if TEMPORARY was used.
*/
if (thd->lex->sql_command == SQLCOM_DROP_TABLE && thd->lex->drop_temporary &&
- thd->is_error() && thd->main_da.sql_errno() == ER_BAD_TABLE_ERROR &&
+ thd->is_error() && thd->stmt_da->sql_errno() == ER_BAD_TABLE_ERROR &&
!expected_error)
- thd->main_da.reset_diagnostics_area();
+ thd->stmt_da->reset_diagnostics_area();
/*
If we expected a non-zero error code, and we don't get the same error
code, and it should be ignored or is related to a concurrency issue.
*/
- actual_error= thd->is_error() ? thd->main_da.sql_errno() : 0;
+ actual_error= thd->is_error() ? thd->stmt_da->sql_errno() : 0;
DBUG_PRINT("info",("expected_error: %d sql_errno: %d",
- expected_error, actual_error));
- if ((expected_error && expected_error != actual_error &&
+ expected_error, actual_error));
+
+ if ((expected_error &&
+ !test_if_equal_repl_errors(expected_error, actual_error) &&
!concurrency_error_code(expected_error)) &&
- !ignored_error_code(actual_error) &&
- !ignored_error_code(expected_error))
+ !ignored_error_code(actual_error) &&
+ !ignored_error_code(expected_error))
{
rli->report(ERROR_LEVEL, 0,
"\
@@ -3759,7 +3902,7 @@ Error on slave: actual message='%s', error code=%d. \
Default database: '%s'. Query: '%s'",
ER_SAFE(expected_error),
expected_error,
- actual_error ? thd->main_da.message() : "no error",
+ actual_error ? thd->stmt_da->message() : "no error",
actual_error,
print_slave_db_safe(db), query_arg);
thd->is_slave_error= 1;
@@ -3768,39 +3911,22 @@ Default database: '%s'. Query: '%s'",
If we get the same error code as expected and it is not a concurrency
issue, or should be ignored.
*/
- else if ((expected_error == actual_error &&
+ else if ((test_if_equal_repl_errors(expected_error, actual_error) &&
!concurrency_error_code(expected_error)) ||
- ignored_error_code(actual_error))
+ ignored_error_code(actual_error))
{
DBUG_PRINT("info",("error ignored"));
clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
- thd->killed= NOT_KILLED;
- /*
- When an error is expected and matches the actual error the
- slave does not report any error and by consequence changes
- on transactional tables are not rolled back in the function
- close_thread_tables(). For that reason, we explicitly roll
- them back here.
- */
- if (expected_error && expected_error == actual_error)
- ha_autocommit_or_rollback(thd, TRUE);
+ thd->reset_killed();
}
/*
- If we expected a non-zero error code and get nothing and, it is a concurrency
- issue or should be ignored.
- */
- else if (expected_error && !actual_error &&
- (concurrency_error_code(expected_error) ||
- ignored_error_code(expected_error)))
- ha_autocommit_or_rollback(thd, TRUE);
- /*
Other cases: mostly we expected no error and get one.
*/
else if (thd->is_slave_error || thd->is_fatal_error)
{
rli->report(ERROR_LEVEL, actual_error,
"Error '%s' on query. Default database: '%s'. Query: '%s'",
- (actual_error ? thd->main_da.message() :
+ (actual_error ? thd->stmt_da->message() :
"unexpected success or fatal error"),
print_slave_db_safe(thd->db), query_arg);
thd->is_slave_error= 1;
@@ -3829,12 +3955,28 @@ Default database: '%s'. Query: '%s'",
*/
} /* End of if (db_ok(... */
+ {
+ /**
+ The following failure injecion works in cooperation with tests
+ setting @@global.debug= 'd,stop_slave_middle_group'.
+ The sql thread receives the killed status and will proceed
+ to shutdown trying to finish incomplete events group.
+ */
+ DBUG_EXECUTE_IF("stop_slave_middle_group",
+ if (strcmp("COMMIT", query) != 0 &&
+ strcmp("BEGIN", query) != 0)
+ {
+ if (thd->transaction.all.modified_non_trans_table)
+ const_cast<Relay_log_info*>(rli)->abort_slave= 1;
+ };);
+ }
+
end:
/*
Probably we have set thd->query, thd->db, thd->catalog to point to places
in the data_buf of this event. Now the event is going to be deleted
probably, so data_buf will be freed, so the thd->... listed above will be
- pointers to freed memory.
+ pointers to freed memory.
So we must set them to 0, so that those bad pointers values are not later
used. Note that "cleanup" queries like automatic DROP TEMPORARY TABLE
don't suffer from these assignments to 0 as DROP TEMPORARY
@@ -3842,9 +3984,8 @@ end:
*/
thd->catalog= 0;
thd->set_db(NULL, 0); /* will free the current database */
- thd->set_query(NULL, 0);
+ thd->reset_query();
DBUG_PRINT("info", ("end: query= 0"));
- close_thread_tables(thd);
/*
As a disk space optimization, future masters will not log an event for
LAST_INSERT_ID() if that function returned 0 (and thus they will be able
@@ -3856,7 +3997,7 @@ end:
thd->first_successful_insert_id_in_prev_stmt= 0;
thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
- return thd->is_slave_error;
+ DBUG_RETURN(thd->is_slave_error);
}
int Query_log_event::do_update_pos(Relay_log_info *rli)
@@ -3883,17 +4024,25 @@ Query_log_event::do_shall_skip(Relay_log_info *rli)
DBUG_PRINT("debug", ("query: %s; q_len: %d", query, q_len));
DBUG_ASSERT(query && q_len > 0);
+ /*
+ An event skipped due to @@skip_replication must not be counted towards the
+ number of events to be skipped due to @@sql_slave_skip_counter.
+ */
+ if (flags & LOG_EVENT_SKIP_REPLICATION_F &&
+ opt_replicate_events_marked_for_skip != RPL_SKIP_REPLICATE)
+ DBUG_RETURN(Log_event::EVENT_SKIP_IGNORE);
+
if (rli->slave_skip_counter > 0)
{
if (strcmp("BEGIN", query) == 0)
{
- thd->options|= OPTION_BEGIN;
+ thd->variables.option_bits|= OPTION_BEGIN;
DBUG_RETURN(Log_event::continue_group(rli));
}
if (strcmp("COMMIT", query) == 0 || strcmp("ROLLBACK", query) == 0)
{
- thd->options&= ~OPTION_BEGIN;
+ thd->variables.option_bits&= ~OPTION_BEGIN;
DBUG_RETURN(Log_event::EVENT_SKIP_COUNT);
}
}
@@ -4048,6 +4197,7 @@ bool Start_log_event_v3::write(IO_CACHE* file)
int Start_log_event_v3::do_apply_event(Relay_log_info const *rli)
{
DBUG_ENTER("Start_log_event_v3::do_apply_event");
+ int error= 0;
switch (binlog_version)
{
case 3:
@@ -4060,7 +4210,7 @@ int Start_log_event_v3::do_apply_event(Relay_log_info const *rli)
*/
if (created)
{
- close_temporary_tables(thd);
+ error= close_temporary_tables(thd);
cleanup_load_tmpdir();
}
else
@@ -4088,7 +4238,7 @@ int Start_log_event_v3::do_apply_event(Relay_log_info const *rli)
Can distinguish, based on the value of 'created': this event was
generated at master startup.
*/
- close_temporary_tables(thd);
+ error= close_temporary_tables(thd);
}
/*
Otherwise, can't distinguish a Start_log_event generated at
@@ -4100,7 +4250,7 @@ int Start_log_event_v3::do_apply_event(Relay_log_info const *rli)
/* this case is impossible */
DBUG_RETURN(1);
}
- DBUG_RETURN(0);
+ DBUG_RETURN(error);
}
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
@@ -4148,10 +4298,11 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
*/
if (post_header_len)
{
+#ifndef DBUG_OFF
// Allows us to sanity-check that all events initialized their
// events (see the end of this 'if' block).
- IF_DBUG(memset(post_header_len, 255,
- number_of_event_types*sizeof(uint8)););
+ memset(post_header_len, 255, number_of_event_types*sizeof(uint8));
+#endif
/* Note: all event types must explicitly fill in their lengths here. */
post_header_len[START_EVENT_V3-1]= START_V3_HEADER_LEN;
@@ -4202,6 +4353,7 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
post_header_len[UPDATE_ROWS_EVENT-1]=
post_header_len[DELETE_ROWS_EVENT-1]= 6;);
post_header_len[INCIDENT_EVENT-1]= INCIDENT_HEADER_LEN;
+ post_header_len[HEARTBEAT_LOG_EVENT-1]= 0;
// Set header length of the reserved events to 0
memset(post_header_len + MYSQL_EVENTS_END - 1, 0,
@@ -4211,11 +4363,9 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
post_header_len[ANNOTATE_ROWS_EVENT-1]= ANNOTATE_ROWS_HEADER_LEN;
// Sanity-check that all post header lengths are initialized.
- IF_DBUG({
- int i;
- for (i=0; i<number_of_event_types; i++)
- assert(post_header_len[i] != 255);
- });
+ int i;
+ for (i=0; i<number_of_event_types; i++)
+ DBUG_ASSERT(post_header_len[i] != 255);
}
break;
@@ -4392,7 +4542,7 @@ Format_description_log_event(const char* buf,
DBUG_PRINT("info", (" number_of_event_types=%d",
number_of_event_types));
/* this makes is_valid() return false. */
- my_free(post_header_len, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(post_header_len);
post_header_len= NULL;
DBUG_VOID_RETURN;
}
@@ -4491,7 +4641,6 @@ int Format_description_log_event::do_apply_event(Relay_log_info const *rli)
int ret= 0;
DBUG_ENTER("Format_description_log_event::do_apply_event");
-#ifdef USING_TRANSACTIONS
/*
As a transaction NEVER spans on 2 or more binlogs:
if we have an active transaction at this point, the master died
@@ -4513,7 +4662,7 @@ int Format_description_log_event::do_apply_event(Relay_log_info const *rli)
"its binary log, thus rolled back too.");
const_cast<Relay_log_info*>(rli)->cleanup_context(thd, 1);
}
-#endif
+
/*
If this event comes from ourselves, there is no cleaning task to
perform, we don't call Start_log_event_v3::do_apply_event()
@@ -4604,7 +4753,7 @@ do_server_version_split(char* version,
if (*r == '.')
p++; // skip the dot
}
- if (strinstr(p, "MariaDB") != 0 || strinstr(p, "-maria-") != 0)
+ if (strstr(p, "MariaDB") != 0 || strstr(p, "-maria-") != 0)
split_versions->kind=
Format_description_log_event::master_version_split::KIND_MARIADB;
else
@@ -4643,7 +4792,7 @@ version_product(const Format_description_log_event::master_version_split* versio
the replication event checksum. FALSE otherwise.
*/
bool
-Format_description_log_event::is_version_before_checksum(master_version_split
+Format_description_log_event::is_version_before_checksum(const master_version_split
*version_split)
{
return version_product(version_split) <
@@ -4720,7 +4869,7 @@ void Load_log_event::print_query(THD *thd, bool need_db, const char *cs,
buf->append(STRING_WITH_LEN("LOAD DATA "));
- if (thd->lex->lock_option == TL_WRITE_CONCURRENT_INSERT)
+ if (is_concurrent)
buf->append(STRING_WITH_LEN("CONCURRENT "));
if (fn_start)
@@ -4777,10 +4926,8 @@ void Load_log_event::print_query(THD *thd, bool need_db, const char *cs,
if ((long) skip_lines > 0)
{
- char skipbuf[22];
buf->append(STRING_WITH_LEN(" IGNORE "));
- longlong10_to_str((longlong) skip_lines, skipbuf, 10);
- buf->append(skipbuf);
+ buf->append_ulonglong(skip_lines);
buf->append(STRING_WITH_LEN(" LINES "));
}
@@ -4866,6 +5013,7 @@ bool Load_log_event::write_data_body(IO_CACHE* file)
Load_log_event::Load_log_event(THD *thd_arg, sql_exchange *ex,
const char *db_arg, const char *table_name_arg,
List<Item> &fields_arg,
+ bool is_concurrent_arg,
enum enum_duplicates handle_dup,
bool ignore, bool using_trans)
:Log_event(thd_arg,
@@ -4876,7 +5024,8 @@ Load_log_event::Load_log_event(THD *thd_arg, sql_exchange *ex,
num_fields(0),fields(0),
field_lens(0),field_block_len(0),
table_name(table_name_arg ? table_name_arg : ""),
- db(db_arg), fname(ex->file_name), local_fname(FALSE)
+ db(db_arg), fname(ex->file_name), local_fname(FALSE),
+ is_concurrent(is_concurrent_arg)
{
time_t end_time;
time(&end_time);
@@ -4957,7 +5106,13 @@ Load_log_event::Load_log_event(const char *buf, uint event_len,
const Format_description_log_event *description_event)
:Log_event(buf, description_event), num_fields(0), fields(0),
field_lens(0),field_block_len(0),
- table_name(0), db(0), fname(0), local_fname(FALSE)
+ table_name(0), db(0), fname(0), local_fname(FALSE),
+ /*
+ Load_log_event which comes from the binary log does not contain
+ information about the type of insert which was used on the master.
+ Assume that it was an ordinary, non-concurrent LOAD DATA.
+ */
+ is_concurrent(FALSE)
{
DBUG_ENTER("Load_log_event");
/*
@@ -5016,11 +5171,22 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len,
fields = (char*)field_lens + num_fields;
table_name = fields + field_block_len;
db = table_name + table_name_len + 1;
+ DBUG_EXECUTE_IF ("simulate_invalid_address",
+ db_len = data_len;);
fname = db + db_len + 1;
+ if ((db_len > data_len) || (fname > buf_end))
+ goto err;
fname_len = (uint) strlen(fname);
+ if ((fname_len > data_len) || (fname + fname_len > buf_end))
+ goto err;
// null termination is accomplished by the caller doing buf[event_len]=0
DBUG_RETURN(0);
+
+err:
+ // Invalid event.
+ table_name = 0;
+ DBUG_RETURN(1);
}
@@ -5082,7 +5248,7 @@ void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info,
else if (sql_ex.opt_flags & IGNORE_FLAG)
my_b_printf(&cache,"IGNORE ");
- my_b_printf(&cache, "INTO TABLE %`s", table_name);
+ my_b_printf(&cache, "INTO TABLE `%s`", table_name);
my_b_printf(&cache, " FIELDS TERMINATED BY ");
pretty_print_str(&cache, sql_ex.field_term, sql_ex.field_term_len);
@@ -5114,9 +5280,9 @@ void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info,
for (i = 0; i < num_fields; i++)
{
if (i)
- my_b_printf(&cache, ",");
+ my_b_printf(&cache, ",");
my_b_printf(&cache, "%`s", field);
-
+
field += field_lens[i] + 1;
}
my_b_printf(&cache, ")");
@@ -5189,11 +5355,13 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
bool use_rli_only_for_errors)
{
LEX_STRING new_db;
+ DBUG_ENTER("Load_log_event::do_apply_event");
+
new_db.length= db_len;
new_db.str= (char *) rpl_filter->get_rewrite_db(db, &new_db.length);
thd->set_db(new_db.str, new_db.length);
DBUG_ASSERT(thd->query() == 0);
- thd->set_query_inner(NULL, 0); // Should not be needed
+ thd->reset_query_inner(); // Should not be needed
thd->is_slave_error= 0;
clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
@@ -5205,7 +5373,7 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
*/
lex_start(thd);
thd->lex->local_file= local_fname;
- mysql_reset_thd_for_next_command(thd, 0);
+ mysql_reset_thd_for_next_command(thd);
if (!use_rli_only_for_errors)
{
@@ -5243,22 +5411,14 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
if (rpl_filter->db_ok(thd->db))
{
thd->set_time(when, when_sec_part);
- VOID(pthread_mutex_lock(&LOCK_thread_count));
- thd->query_id = next_query_id();
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
- /*
- Initing thd->row_count is not necessary in theory as this variable has no
- influence in the case of the slave SQL thread (it is used to generate a
- "data truncated" warning but which is absorbed and never gets to the
- error log); still we init it to avoid a Valgrind message.
- */
- mysql_reset_errors(thd, 0);
+ thd->set_query_id(next_query_id());
+ thd->warning_info->opt_clear_warning_info(thd->query_id);
TABLE_LIST tables;
- bzero((char*) &tables,sizeof(tables));
- tables.db= thd->strmake(thd->db, thd->db_length);
- tables.alias = tables.table_name = (char*) table_name;
- tables.lock_type = TL_WRITE;
+ tables.init_one_table(thd->strmake(thd->db, thd->db_length),
+ thd->db_length,
+ table_name, strlen(table_name),
+ table_name, TL_WRITE);
tables.updating= 1;
// the table will be opened in mysql_load
@@ -5296,9 +5456,7 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
thd->set_query(load_data_query, (uint) (query_str.length()));
if (sql_ex.opt_flags & REPLACE_FLAG)
- {
- handle_dup= DUP_REPLACE;
- }
+ handle_dup= DUP_REPLACE;
else if (sql_ex.opt_flags & IGNORE_FLAG)
{
ignore= 1;
@@ -5307,14 +5465,14 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
else
{
/*
- When replication is running fine, if it was DUP_ERROR on the
+ When replication is running fine, if it was DUP_ERROR on the
master then we could choose IGNORE here, because if DUP_ERROR
suceeded on master, and data is identical on the master and slave,
then there should be no uniqueness errors on slave, so IGNORE is
the same as DUP_ERROR. But in the unlikely case of uniqueness errors
(because the data on the master and slave happen to be different
- (user error or bug), we want LOAD DATA to print an error message on
- the slave to discover the problem.
+ (user error or bug), we want LOAD DATA to print an error message on
+ the slave to discover the problem.
If reading from net (a 3.23 master), mysql_load() will change this
to IGNORE.
@@ -5346,7 +5504,7 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
ex.opt_enclosed = (sql_ex.opt_flags & OPT_ENCLOSED_FLAG);
if (sql_ex.empty_flags & FIELD_TERM_EMPTY)
- ex.field_term->length(0);
+ ex.field_term->length(0);
ex.skip_lines = skip_lines;
List<Item> field_list;
@@ -5355,12 +5513,10 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
thd->variables.pseudo_thread_id= thread_id;
if (net)
{
- // mysql_load will use thd->net to read the file
- thd->net.vio = net->vio;
- /*
- Make sure the client does not get confused about the packet sequence
- */
- thd->net.pkt_nr = net->pkt_nr;
+ // mysql_load will use thd->net to read the file
+ thd->net.vio = net->vio;
+ // Make sure the client does not get confused about the packet sequence
+ thd->net.pkt_nr = net->pkt_nr;
}
/*
It is safe to use tmp_list twice because we are not going to
@@ -5372,7 +5528,7 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
thd->is_slave_error= 1;
if (thd->cuted_fields)
{
- /* log_pos is the position of the LOAD event in the master log */
+ /* log_pos is the position of the LOAD event in the master log */
sql_print_warning("Slave: load data infile on table '%s' at "
"log position %s in log '%s' produced %ld "
"warning(s). Default database: '%s'",
@@ -5401,8 +5557,32 @@ error:
const char *remember_db= thd->db;
thd->catalog= 0;
thd->set_db(NULL, 0); /* will free the current database */
- thd->set_query(NULL, 0);
+ thd->reset_query();
+ thd->stmt_da->can_overwrite_status= TRUE;
+ thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
+ thd->stmt_da->can_overwrite_status= FALSE;
close_thread_tables(thd);
+ /*
+ - If transaction rollback was requested due to deadlock
+ perform it and release metadata locks.
+ - If inside a multi-statement transaction,
+ defer the release of metadata locks until the current
+ transaction is either committed or rolled back. This prevents
+ other statements from modifying the table for the entire
+ duration of this transaction. This provides commit ordering
+ and guarantees serializability across multiple transactions.
+ - If in autocommit mode, or outside a transactional context,
+ automatically release metadata locks of the current statement.
+ */
+ if (thd->transaction_rollback_request)
+ {
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+ else if (! thd->in_multi_stmt_transaction_mode())
+ thd->mdl_context.release_transactional_locks();
+ else
+ thd->mdl_context.release_statement_locks();
DBUG_EXECUTE_IF("LOAD_DATA_INFILE_has_fatal_error",
thd->is_slave_error= 0; thd->is_fatal_error= 1;);
@@ -5414,8 +5594,8 @@ error:
int sql_errno;
if (thd->is_error())
{
- err= thd->main_da.message();
- sql_errno= thd->main_da.sql_errno();
+ err= thd->stmt_da->message();
+ sql_errno= thd->stmt_da->sql_errno();
}
else
{
@@ -5426,7 +5606,7 @@ error:
Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'",
err, (char*)table_name, print_slave_db_safe(remember_db));
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
- return 1;
+ DBUG_RETURN(1);
}
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
@@ -5441,10 +5621,10 @@ Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'",
rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
ER(ER_SLAVE_FATAL_ERROR), buf);
- return 1;
+ DBUG_RETURN(1);
}
- return ( use_rli_only_for_errors ? 0 : Log_event::do_apply_event(rli) );
+ DBUG_RETURN( use_rli_only_for_errors ? 0 : Log_event::do_apply_event(rli) );
}
#endif
@@ -5611,7 +5791,7 @@ int Rotate_log_event::do_update_pos(Relay_log_info *rli)
!is_relay_log_event() &&
!rli->is_in_group())
{
- pthread_mutex_lock(&rli->data_lock);
+ mysql_mutex_lock(&rli->data_lock);
DBUG_PRINT("info", ("old group_master_log_name: '%s' "
"old group_master_log_pos: %lu",
rli->group_master_log_name,
@@ -5623,11 +5803,11 @@ int Rotate_log_event::do_update_pos(Relay_log_info *rli)
"new group_master_log_pos: %lu",
rli->group_master_log_name,
(ulong) rli->group_master_log_pos));
- pthread_mutex_unlock(&rli->data_lock);
+ mysql_mutex_unlock(&rli->data_lock);
flush_relay_log_info(rli);
/*
- Reset thd->options and sql_mode etc, because this could be the signal of
+ Reset thd->variables.option_bits and sql_mode etc, because this could be the signal of
a master's downgrade from 5.0 to 4.0.
However, no need to reset description_event_for_exec: indeed, if the next
master is 5.0 (even 5.0.1) we will soon get a Format_desc; if the next
@@ -5780,6 +5960,7 @@ void Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
int Intvar_log_event::do_apply_event(Relay_log_info const *rli)
{
+ DBUG_ENTER("Intvar_log_event::do_apply_event");
/*
We are now in a statement until the associated query log event has
been processed.
@@ -5787,17 +5968,21 @@ int Intvar_log_event::do_apply_event(Relay_log_info const *rli)
const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
if (rli->deferred_events_collecting)
- return rli->deferred_events->add(this);
+ {
+ DBUG_PRINT("info",("deferring event"));
+ DBUG_RETURN(rli->deferred_events->add(this));
+ }
switch (type) {
case LAST_INSERT_ID_EVENT:
thd->first_successful_insert_id_in_prev_stmt= val;
+ DBUG_PRINT("info",("last_insert_id_event: %ld", (long) val));
break;
case INSERT_ID_EVENT:
thd->force_one_auto_inc_interval(val);
break;
}
- return 0;
+ DBUG_RETURN(0);
}
int Intvar_log_event::do_update_pos(Relay_log_info *rli)
@@ -6018,10 +6203,19 @@ void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
int Xid_log_event::do_apply_event(Relay_log_info const *rli)
{
+ bool res;
/* For a slave Xid_log_event is COMMIT */
general_log_print(thd, COM_QUERY,
"COMMIT /* implicit, from Xid_log_event */");
- return end_trans(thd, COMMIT);
+ res= trans_commit(thd); /* Automatically rolls back on error. */
+ thd->mdl_context.release_transactional_locks();
+
+ /*
+ Increment the global status commit count variable
+ */
+ status_var_increment(thd->status_var.com_stat[SQLCOM_COMMIT]);
+
+ return res;
}
Log_event::enum_skip_reason
@@ -6029,7 +6223,7 @@ Xid_log_event::do_shall_skip(Relay_log_info *rli)
{
DBUG_ENTER("Xid_log_event::do_shall_skip");
if (rli->slave_skip_counter > 0) {
- thd->options&= ~OPTION_BEGIN;
+ thd->variables.option_bits&= ~OPTION_BEGIN;
DBUG_RETURN(Log_event::EVENT_SKIP_COUNT);
}
DBUG_RETURN(Log_event::do_shall_skip(rli));
@@ -6069,15 +6263,15 @@ void User_var_log_event::pack_info(THD *thd, Protocol* protocol)
case REAL_RESULT:
{
double real_val;
- char buf2[FLOATING_POINT_BUFFER];
- char buf_mem[FN_REFLEN + FLOATING_POINT_BUFFER];
+ char buf2[MY_GCVT_MAX_FIELD_WIDTH+1];
+ char buf_mem[FN_REFLEN + MY_GCVT_MAX_FIELD_WIDTH + 1];
String buf(buf_mem, sizeof(buf_mem), system_charset_info);
float8get(real_val, val);
buf.length(0);
- my_sprintf(buf2, (buf2, "%.14g", real_val));
if (user_var_append_name_part(thd, &buf, name, name_len) ||
- buf.append(buf2))
- return;
+ buf.append(buf2, my_gcvt(real_val, MY_GCVT_ARG_DOUBLE,
+ MY_GCVT_MAX_FIELD_WIDTH, buf2, NULL)))
+ return;
protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
break;
}
@@ -6088,7 +6282,9 @@ void User_var_log_event::pack_info(THD *thd, Protocol* protocol)
String buf(buf_mem, sizeof(buf_mem), system_charset_info);
buf.length(0);
if (user_var_append_name_part(thd, &buf, name, name_len) ||
- buf.append(buf2, longlong10_to_str(uint8korr(val), buf2, -10)-buf2))
+ buf.append(buf2,
+ longlong10_to_str(uint8korr(val), buf2,
+ ((flags & User_var_log_event::UNSIGNED_F) ? 10 : -10))-buf2))
return;
protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
break;
@@ -6109,7 +6305,7 @@ void User_var_log_event::pack_info(THD *thd, Protocol* protocol)
return;
protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
break;
- }
+ }
case STRING_RESULT:
{
/* 15 is for 'COLLATE' and other chars */
@@ -6132,7 +6328,7 @@ void User_var_log_event::pack_info(THD *thd, Protocol* protocol)
buf.append(" "))
return;
old_len= buf.length();
- if (buf.reserve(old_len + val_len*2 + 2 + sizeof(" COLLATE ") +
+ if (buf.reserve(old_len + val_len * 2 + 3 + sizeof(" COLLATE ") +
MY_CS_NAME_SIZE))
return;
beg= const_cast<char *>(buf.ptr()) + old_len;
@@ -6146,7 +6342,6 @@ void User_var_log_event::pack_info(THD *thd, Protocol* protocol)
break;
}
case ROW_RESULT:
- case IMPOSSIBLE_RESULT:
default:
DBUG_ASSERT(0);
return;
@@ -6167,6 +6362,7 @@ User_var_log_event(const char* buf, uint event_len,
bool error= false;
const char* buf_start= buf;
/* The Post-Header is empty. The Variable Data part begins immediately. */
+ const char *start= buf;
buf+= description_event->common_header_len +
description_event->post_header_len[USER_VAR_EVENT-1];
name_len= uint4korr(buf);
@@ -6186,6 +6382,7 @@ User_var_log_event(const char* buf, uint event_len,
buf+= UV_NAME_LEN_SIZE + name_len;
is_null= (bool) *buf;
+ flags= User_var_log_event::UNDEF_F; // defaults to UNDEF_F
if (is_null)
{
type= STRING_RESULT;
@@ -6205,16 +6402,50 @@ User_var_log_event(const char* buf, uint event_len,
type= (Item_result) buf[UV_VAL_IS_NULL];
charset_number= uint4korr(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE);
- val_len= uint4korr(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
- UV_CHARSET_NUMBER_SIZE);
+ val_len= uint4korr(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
+ UV_CHARSET_NUMBER_SIZE);
val= (char *) (buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
- UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE);
+ UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE);
if (!valid_buffer_range<uint>(val_len, buf_start, val, event_len))
{
error= true;
goto err;
}
+
+ /**
+ We need to check if this is from an old server
+ that did not pack information for flags.
+ We do this by checking if there are extra bytes
+ after the packed value. If there are we take the
+ extra byte and it's value is assumed to contain
+ the flags value.
+
+ Old events will not have this extra byte, thence,
+ we keep the flags set to UNDEF_F.
+ */
+ uint bytes_read= ((val + val_len) - start);
+#ifndef DBUG_OFF
+ bool old_pre_checksum_fd= description_event->is_version_before_checksum(
+ &description_event->server_version_split);
+#endif
+ DBUG_ASSERT((bytes_read == data_written -
+ (old_pre_checksum_fd ||
+ (description_event->checksum_alg ==
+ BINLOG_CHECKSUM_ALG_OFF)) ?
+ 0 : BINLOG_CHECKSUM_LEN)
+ ||
+ (bytes_read == data_written -1 -
+ (old_pre_checksum_fd ||
+ (description_event->checksum_alg ==
+ BINLOG_CHECKSUM_ALG_OFF)) ?
+ 0 : BINLOG_CHECKSUM_LEN));
+ if ((data_written - bytes_read) > 0)
+ {
+ flags= (uint) *(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
+ UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE +
+ val_len);
+ }
}
err:
@@ -6230,6 +6461,7 @@ bool User_var_log_event::write(IO_CACHE* file)
char buf1[UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE];
uchar buf2[max(8, DECIMAL_MAX_FIELD_SIZE + 2)], *pos= buf2;
+ uint unsigned_len= 0;
uint buf1_length;
ulong event_length;
@@ -6251,6 +6483,7 @@ bool User_var_log_event::write(IO_CACHE* file)
break;
case INT_RESULT:
int8store(buf2, *(longlong*) val);
+ unsigned_len= 1;
break;
case DECIMAL_RESULT:
{
@@ -6266,7 +6499,6 @@ bool User_var_log_event::write(IO_CACHE* file)
pos= (uchar*) val;
break;
case ROW_RESULT:
- case IMPOSSIBLE_RESULT:
default:
DBUG_ASSERT(0);
return 0;
@@ -6276,13 +6508,14 @@ bool User_var_log_event::write(IO_CACHE* file)
}
/* Length of the whole event */
- event_length= sizeof(buf)+ name_len + buf1_length + val_len;
+ event_length= sizeof(buf)+ name_len + buf1_length + val_len + unsigned_len;
return (write_header(file, event_length) ||
wrapper_my_b_safe_write(file, (uchar*) buf, sizeof(buf)) ||
wrapper_my_b_safe_write(file, (uchar*) name, name_len) ||
wrapper_my_b_safe_write(file, (uchar*) buf1, buf1_length) ||
wrapper_my_b_safe_write(file, pos, val_len) ||
+ wrapper_my_b_safe_write(file, &flags, unsigned_len) ||
write_footer(file));
}
#endif
@@ -6323,7 +6556,8 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
break;
case INT_RESULT:
char int_buf[22];
- longlong10_to_str(uint8korr(val), int_buf, -10);
+ 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);
break;
case DECIMAL_RESULT:
@@ -6362,7 +6596,8 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
char *hex_str;
CHARSET_INFO *cs;
- hex_str= (char *)my_malloc(2*val_len+1+2,MYF(MY_WME)); // 2 hex digits / byte
+ // 2 hex digits / byte
+ hex_str= (char *) my_malloc(2 * val_len + 1 + 3, MYF(MY_WME));
if (!hex_str)
return;
str_to_hex(hex_str, val, val_len);
@@ -6379,10 +6614,10 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
*/
my_b_printf(&cache, ":=???%s\n", print_event_info->delimiter);
else
- my_b_printf(&cache, ":=_%s %s COLLATE %`s%s\n",
+ my_b_printf(&cache, ":=_%s %s COLLATE `%s`%s\n",
cs->csname, hex_str, cs->name,
print_event_info->delimiter);
- my_free(hex_str, MYF(MY_WME));
+ my_free(hex_str);
}
break;
case ROW_RESULT:
@@ -6404,20 +6639,22 @@ int User_var_log_event::do_apply_event(Relay_log_info const *rli)
{
Item *it= 0;
CHARSET_INFO *charset;
+ DBUG_ENTER("User_var_log_event::do_apply_event");
query_id_t sav_query_id= 0; /* memorize orig id when deferred applying */
if (rli->deferred_events_collecting)
{
set_deferred(current_thd->query_id);
- return rli->deferred_events->add(this);
- } else if (is_deferred())
+ DBUG_RETURN(rli->deferred_events->add(this));
+ }
+ else if (is_deferred())
{
sav_query_id= current_thd->query_id;
current_thd->query_id= query_id; /* recreating original time context */
}
if (!(charset= get_charset(charset_number, MYF(MY_WME))))
- return 1;
+ DBUG_RETURN(1);
LEX_STRING user_var_name;
user_var_name.str= name;
user_var_name.length= name_len;
@@ -6461,10 +6698,9 @@ int User_var_log_event::do_apply_event(Relay_log_info const *rli)
it= new Item_string(val, val_len, charset);
break;
case ROW_RESULT:
- case IMPOSSIBLE_RESULT:
default:
DBUG_ASSERT(0);
- return 0;
+ DBUG_RETURN(0);
}
}
@@ -6478,20 +6714,21 @@ int User_var_log_event::do_apply_event(Relay_log_info const *rli)
error.
*/
if (e->fix_fields(thd, 0))
- return 1;
+ DBUG_RETURN(1);
/*
A variable can just be considered as a table with
a single record and with a single column. Thus, like
a column value, it could always have IMPLICIT derivation.
*/
- e->update_hash(val, val_len, type, charset, DERIVATION_IMPLICIT, 0);
+ e->update_hash(val, val_len, type, charset, DERIVATION_IMPLICIT,
+ (flags & User_var_log_event::UNSIGNED_F));
if (!is_deferred())
- free_root(thd->mem_root,0);
+ free_root(thd->mem_root, 0);
else
current_thd->query_id= sav_query_id; /* restore current query's context */
- return 0;
+ DBUG_RETURN(0);
}
int User_var_log_event::do_update_pos(Relay_log_info *rli)
@@ -6565,13 +6802,13 @@ Slave_log_event::Slave_log_event(THD* thd_arg,
Master_info* mi = rli->mi;
// TODO: re-write this better without holding both locks at the same time
- pthread_mutex_lock(&mi->data_lock);
- pthread_mutex_lock(&rli->data_lock);
+ mysql_mutex_lock(&mi->data_lock);
+ mysql_mutex_lock(&rli->data_lock);
master_host_len = strlen(mi->host);
master_log_len = strlen(rli->group_master_log_name);
// on OOM, just do not initialize the structure and print the error
if ((mem_pool = (char*)my_malloc(get_data_size() + 1,
- MYF(MY_WME))))
+ MYF(MY_WME))))
{
master_host = mem_pool + SL_MASTER_HOST_OFFSET ;
memcpy(master_host, mi->host, master_host_len + 1);
@@ -6580,12 +6817,12 @@ Slave_log_event::Slave_log_event(THD* thd_arg,
master_port = mi->port;
master_pos = rli->group_master_log_pos;
DBUG_PRINT("info", ("master_log: %s pos: %lu", master_log,
- (ulong) master_pos));
+ (ulong) master_pos));
}
else
sql_print_error("Out of memory while recording slave event");
- pthread_mutex_unlock(&rli->data_lock);
- pthread_mutex_unlock(&mi->data_lock);
+ mysql_mutex_unlock(&rli->data_lock);
+ mysql_mutex_unlock(&mi->data_lock);
DBUG_VOID_RETURN;
}
#endif /* !MYSQL_CLIENT */
@@ -6593,7 +6830,7 @@ Slave_log_event::Slave_log_event(THD* thd_arg,
Slave_log_event::~Slave_log_event()
{
- my_free(mem_pool, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(mem_pool);
}
@@ -6723,7 +6960,7 @@ int Stop_log_event::do_update_pos(Relay_log_info *rli)
could give false triggers in MASTER_POS_WAIT() that we have reached
the target position when in fact we have not.
*/
- if (thd->options & OPTION_BEGIN)
+ if (thd->variables.option_bits & OPTION_BEGIN)
rli->inc_event_relay_log_pos();
else
{
@@ -6749,11 +6986,14 @@ int Stop_log_event::do_update_pos(Relay_log_info *rli)
Create_file_log_event::
Create_file_log_event(THD* thd_arg, sql_exchange* ex,
const char* db_arg, const char* table_name_arg,
- List<Item>& fields_arg, enum enum_duplicates handle_dup,
+ List<Item>& fields_arg,
+ bool is_concurrent_arg,
+ enum enum_duplicates handle_dup,
bool ignore,
uchar* block_arg, uint block_len_arg, bool using_trans)
- :Load_log_event(thd_arg,ex,db_arg,table_name_arg,fields_arg,handle_dup, ignore,
- using_trans),
+ :Load_log_event(thd_arg, ex, db_arg, table_name_arg, fields_arg,
+ is_concurrent_arg,
+ handle_dup, ignore, using_trans),
fake_base(0), block(block_arg), event_buf(0), block_len(block_len_arg),
file_id(thd_arg->file_id = mysql_bin_log.next_file_id())
{
@@ -6953,10 +7193,12 @@ int Create_file_log_event::do_apply_event(Relay_log_info const *rli)
fname_buf= strmov(proc_info, "Making temp file ");
ext= slave_load_file_stem(fname_buf, file_id, server_id, ".info");
thd_proc_info(thd, proc_info);
- my_delete(fname_buf, MYF(0)); // old copy may exist already
- if ((fd= my_create(fname_buf, CREATE_MODE,
- O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
- MYF(MY_WME))) < 0 ||
+ /* old copy may exist already */
+ mysql_file_delete(key_file_log_event_info, fname_buf, MYF(0));
+ if ((fd= mysql_file_create(key_file_log_event_info,
+ fname_buf, CREATE_MODE,
+ O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
+ MYF(MY_WME))) < 0 ||
init_io_cache(&file, fd, IO_SIZE, WRITE_CACHE, (my_off_t)0, 0,
MYF(MY_WME|MY_NABP)))
{
@@ -6978,20 +7220,22 @@ int Create_file_log_event::do_apply_event(Relay_log_info const *rli)
goto err;
}
end_io_cache(&file);
- my_close(fd, MYF(0));
+ mysql_file_close(fd, MYF(0));
// fname_buf now already has .data, not .info, because we did our trick
- my_delete(fname_buf, MYF(0)); // old copy may exist already
- if ((fd= my_create(fname_buf, CREATE_MODE,
- O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
- MYF(MY_WME))) < 0)
+ /* old copy may exist already */
+ mysql_file_delete(key_file_log_event_data, fname_buf, MYF(0));
+ if ((fd= mysql_file_create(key_file_log_event_data,
+ fname_buf, CREATE_MODE,
+ O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
+ MYF(MY_WME))) < 0)
{
rli->report(ERROR_LEVEL, my_errno,
"Error in Create_file event: could not open file '%s'",
fname_buf);
goto err;
}
- if (my_write(fd, (uchar*) block, block_len, MYF(MY_WME+MY_NABP)))
+ if (mysql_file_write(fd, (uchar*) block, block_len, MYF(MY_WME+MY_NABP)))
{
rli->report(ERROR_LEVEL, my_errno,
"Error in Create_file event: write to '%s' failed",
@@ -7004,7 +7248,7 @@ err:
if (error)
end_io_cache(&file);
if (fd >= 0)
- my_close(fd, MYF(0));
+ mysql_file_close(fd, MYF(0));
thd_proc_info(thd, 0);
return error != 0;
}
@@ -7099,9 +7343,7 @@ void Append_block_log_event::pack_info(THD *thd, Protocol *protocol)
{
char buf[256];
uint length;
- length= (uint) my_sprintf(buf,
- (buf, ";file_id=%u;block_len=%u", file_id,
- block_len));
+ length= (uint) sprintf(buf, ";file_id=%u;block_len=%u", file_id, block_len);
protocol->store(buf, length, &my_charset_bin);
}
@@ -7136,11 +7378,13 @@ int Append_block_log_event::do_apply_event(Relay_log_info const *rli)
as the present method does not call mysql_parse().
*/
lex_start(thd);
- mysql_reset_thd_for_next_command(thd, 0);
- my_delete(fname, MYF(0)); // old copy may exist already
- if ((fd= my_create(fname, CREATE_MODE,
- O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
- MYF(MY_WME))) < 0)
+ mysql_reset_thd_for_next_command(thd);
+ /* old copy may exist already */
+ mysql_file_delete(key_file_log_event_data, fname, MYF(0));
+ if ((fd= mysql_file_create(key_file_log_event_data,
+ fname, CREATE_MODE,
+ O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
+ MYF(MY_WME))) < 0)
{
rli->report(ERROR_LEVEL, my_errno,
"Error in %s event: could not create file '%s'",
@@ -7148,8 +7392,10 @@ int Append_block_log_event::do_apply_event(Relay_log_info const *rli)
goto err;
}
}
- else if ((fd = my_open(fname, O_WRONLY | O_APPEND | O_BINARY | O_NOFOLLOW,
- MYF(MY_WME))) < 0)
+ else if ((fd= mysql_file_open(key_file_log_event_data,
+ fname,
+ O_WRONLY | O_APPEND | O_BINARY | O_NOFOLLOW,
+ MYF(MY_WME))) < 0)
{
rli->report(ERROR_LEVEL, my_errno,
"Error in %s event: could not open file '%s'",
@@ -7157,10 +7403,12 @@ int Append_block_log_event::do_apply_event(Relay_log_info const *rli)
goto err;
}
- DBUG_EXECUTE_IF("remove_slave_load_file_before_write",
- my_close(fd,MYF(0)); fd= -1; my_delete(fname, MYF(0)););
+ DBUG_EXECUTE_IF("remove_slave_load_file_before_write",
+ {
+ my_delete(fname, MYF(0));
+ });
- if (my_write(fd, (uchar*) block, block_len, MYF(MY_WME+MY_NABP)))
+ if (mysql_file_write(fd, (uchar*) block, block_len, MYF(MY_WME+MY_NABP)))
{
rli->report(ERROR_LEVEL, my_errno,
"Error in %s event: write to '%s' failed",
@@ -7171,7 +7419,7 @@ int Append_block_log_event::do_apply_event(Relay_log_info const *rli)
err:
if (fd >= 0)
- my_close(fd, MYF(0));
+ mysql_file_close(fd, MYF(0));
thd_proc_info(thd, 0);
DBUG_RETURN(error);
}
@@ -7252,7 +7500,7 @@ void Delete_file_log_event::pack_info(THD *thd, Protocol *protocol)
{
char buf[64];
uint length;
- length= (uint) my_sprintf(buf, (buf, ";file_id=%u", (uint) file_id));
+ length= (uint) sprintf(buf, ";file_id=%u", (uint) file_id);
protocol->store(buf, (int32) length, &my_charset_bin);
}
#endif
@@ -7266,9 +7514,9 @@ int Delete_file_log_event::do_apply_event(Relay_log_info const *rli)
{
char fname[FN_REFLEN+10];
char *ext= slave_load_file_stem(fname, file_id, server_id, ".data");
- (void) my_delete(fname, MYF(MY_WME));
+ mysql_file_delete(key_file_log_event_data, fname, MYF(MY_WME));
strmov(ext, ".info");
- (void) my_delete(fname, MYF(MY_WME));
+ mysql_file_delete(key_file_log_event_info, fname, MYF(MY_WME));
return 0;
}
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
@@ -7351,7 +7599,7 @@ void Execute_load_log_event::pack_info(THD *thd, Protocol *protocol)
{
char buf[64];
uint length;
- length= (uint) my_sprintf(buf, (buf, ";file_id=%u", (uint) file_id));
+ length= (uint) sprintf(buf, ";file_id=%u", (uint) file_id);
protocol->store(buf, (int32) length, &my_charset_bin);
}
@@ -7370,8 +7618,9 @@ int Execute_load_log_event::do_apply_event(Relay_log_info const *rli)
Load_log_event *lev= 0;
ext= slave_load_file_stem(fname, file_id, server_id, ".info");
- if ((fd = my_open(fname, O_RDONLY | O_BINARY | O_NOFOLLOW,
- MYF(MY_WME))) < 0 ||
+ if ((fd= mysql_file_open(key_file_log_event_info,
+ fname, O_RDONLY | O_BINARY | O_NOFOLLOW,
+ MYF(MY_WME))) < 0 ||
init_io_cache(&file, fd, IO_SIZE, READ_CACHE, (my_off_t)0, 0,
MYF(MY_WME|MY_NABP)))
{
@@ -7382,7 +7631,7 @@ int Execute_load_log_event::do_apply_event(Relay_log_info const *rli)
}
if (!(lev= (Load_log_event*)
Log_event::read_log_event(&file,
- (pthread_mutex_t*)0,
+ (mysql_mutex_t*)0,
rli->relay_log.description_event_for_exec,
opt_slave_sql_verify_checksum)) ||
lev->get_type_code() != NEW_LOAD_EVENT)
@@ -7416,30 +7665,30 @@ int Execute_load_log_event::do_apply_event(Relay_log_info const *rli)
{
rli->report(ERROR_LEVEL, rli->last_error().number,
"%s. Failed executing load from '%s'", tmp, fname);
- my_free(tmp,MYF(0));
+ my_free(tmp);
}
goto err;
}
/*
We have an open file descriptor to the .info file; we need to close it
- or Windows will refuse to delete the file in my_delete().
+ or Windows will refuse to delete the file in mysql_file_delete().
*/
if (fd >= 0)
{
- my_close(fd, MYF(0));
+ mysql_file_close(fd, MYF(0));
end_io_cache(&file);
fd= -1;
}
- (void) my_delete(fname, MYF(MY_WME));
+ mysql_file_delete(key_file_log_event_info, fname, MYF(MY_WME));
memcpy(ext, ".data", 6);
- (void) my_delete(fname, MYF(MY_WME));
+ mysql_file_delete(key_file_log_event_data, fname, MYF(MY_WME));
error = 0;
err:
delete lev;
if (fd >= 0)
{
- my_close(fd, MYF(0));
+ mysql_file_close(fd, MYF(0));
end_io_cache(&file);
}
return error;
@@ -7504,9 +7753,9 @@ Execute_load_query_log_event(THD *thd_arg, const char* query_arg,
ulong query_length_arg, uint fn_pos_start_arg,
uint fn_pos_end_arg,
enum_load_dup_handling dup_handling_arg,
- bool using_trans, bool suppress_use,
+ bool using_trans, bool direct, bool suppress_use,
int errcode):
- Query_log_event(thd_arg, query_arg, query_length_arg, using_trans,
+ Query_log_event(thd_arg, query_arg, query_length_arg, using_trans, direct,
suppress_use, errcode),
file_id(thd_arg->file_id), fn_pos_start(fn_pos_start_arg),
fn_pos_end(fn_pos_end_arg), dup_handling(dup_handling_arg)
@@ -7586,9 +7835,9 @@ void Execute_load_query_log_event::print(FILE* file,
if (local_fname)
{
my_b_write(&cache, (uchar*) query, fn_pos_start);
- my_b_printf(&cache, " LOCAL INFILE \'");
- my_b_printf(&cache, "%s", local_fname);
- my_b_printf(&cache, "\'");
+ my_b_printf(&cache, " LOCAL INFILE ");
+ pretty_print_str(&cache, local_fname, strlen(local_fname));
+
if (dup_handling == LOAD_DUP_REPLACE)
my_b_printf(&cache, " REPLACE");
my_b_printf(&cache, " INTO");
@@ -7611,7 +7860,6 @@ void Execute_load_query_log_event::print(FILE* file,
void Execute_load_query_log_event::pack_info(THD *thd, Protocol *protocol)
{
char buf_mem[1024];
- char file_id_buf[22];
String buf(buf_mem, sizeof(buf_mem), system_charset_info);
buf.real_alloc(9 + db_len + q_len + 10 + 21);
if (db && db_len)
@@ -7623,9 +7871,8 @@ void Execute_load_query_log_event::pack_info(THD *thd, Protocol *protocol)
}
if (query && q_len && buf.append(query, q_len))
return;
- int10_to_str((long) file_id, file_id_buf, 10);
if (buf.append(" ;file_id=") ||
- buf.append(file_id_buf))
+ buf.append_ulonglong(file_id))
return;
protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
}
@@ -7643,7 +7890,7 @@ Execute_load_query_log_event::do_apply_event(Relay_log_info const *rli)
buf= (char*) my_malloc(q_len + 1 - (fn_pos_end - fn_pos_start) +
(FN_REFLEN + 10) + 10 + 8 + 5, MYF(MY_WME));
- DBUG_EXECUTE_IF("LOAD_DATA_INFILE_has_fatal_error", my_free(buf, MYF(0)); buf= NULL;);
+ DBUG_EXECUTE_IF("LOAD_DATA_INFILE_has_fatal_error", my_free(buf); buf= NULL;);
/* Replace filename and LOCAL keyword in query before executing it */
if (buf == NULL)
@@ -7684,9 +7931,9 @@ Execute_load_query_log_event::do_apply_event(Relay_log_info const *rli)
file so that we can re-execute this event at START SLAVE.
*/
if (!error)
- (void) my_delete(fname, MYF(MY_WME));
+ mysql_file_delete(key_file_log_event_data, fname, MYF(MY_WME));
- my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(buf);
return error;
}
#endif
@@ -7808,9 +8055,9 @@ Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
DBUG_ASSERT((tbl_arg && tbl_arg->s && tid != ~0UL) ||
(!tbl_arg && !cols && tid == ~0UL));
- if (thd_arg->options & OPTION_NO_FOREIGN_KEY_CHECKS)
+ if (thd_arg->variables.option_bits & OPTION_NO_FOREIGN_KEY_CHECKS)
set_flags(NO_FOREIGN_KEY_CHECKS_F);
- if (thd_arg->options & OPTION_RELAXED_UNIQUE_CHECKS)
+ if (thd_arg->variables.option_bits & OPTION_RELAXED_UNIQUE_CHECKS)
set_flags(RELAXED_UNIQUE_CHECKS_F);
/* if bitmap_init fails, caught in is_valid() */
if (likely(!bitmap_init(&m_cols,
@@ -7953,7 +8200,7 @@ Rows_log_event::~Rows_log_event()
if (m_cols.bitmap == m_bitbuf) // no my_malloc happened
m_cols.bitmap= 0; // so no my_free in bitmap_free
bitmap_free(&m_cols); // To pair with bitmap_init().
- my_free((uchar*)m_rows_buf, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(m_rows_buf);
}
int Rows_log_event::get_data_size()
@@ -8006,8 +8253,31 @@ int Rows_log_event::do_add_row_data(uchar *row_data, size_t length)
if (static_cast<size_t>(m_rows_end - m_rows_cur) <= length)
{
size_t const block_size= 1024;
- my_ptrdiff_t const cur_size= m_rows_cur - m_rows_buf;
- my_ptrdiff_t const new_alloc=
+ ulong cur_size= m_rows_cur - m_rows_buf;
+ DBUG_EXECUTE_IF("simulate_too_big_row_case1",
+ cur_size= UINT_MAX32 - (block_size * 10);
+ length= UINT_MAX32 - (block_size * 10););
+ DBUG_EXECUTE_IF("simulate_too_big_row_case2",
+ cur_size= UINT_MAX32 - (block_size * 10);
+ length= block_size * 10;);
+ DBUG_EXECUTE_IF("simulate_too_big_row_case3",
+ cur_size= block_size * 10;
+ length= UINT_MAX32 - (block_size * 10););
+ DBUG_EXECUTE_IF("simulate_too_big_row_case4",
+ cur_size= UINT_MAX32 - (block_size * 10);
+ length= (block_size * 10) - block_size + 1;);
+ ulong remaining_space= UINT_MAX32 - cur_size;
+ /* Check that the new data fits within remaining space and we can add
+ block_size without wrapping.
+ */
+ if (length > remaining_space ||
+ ((length + block_size) > remaining_space))
+ {
+ sql_print_error("The row data is greater than 4GB, which is too big to "
+ "write to the binary log.");
+ DBUG_RETURN(ER_BINLOG_ROW_LOGGING_FAILED);
+ }
+ ulong const new_alloc=
block_size * ((cur_size + length + block_size - 1) / block_size);
uchar* const new_buf= (uchar*)my_realloc((uchar*)m_rows_buf, (uint) new_alloc,
@@ -8056,8 +8326,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
*/
DBUG_ASSERT(get_flags(STMT_END_F));
- const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
- close_thread_tables(thd);
+ const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
thd->clear_error();
DBUG_RETURN(0);
}
@@ -8083,11 +8352,11 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
We also call the mysql_reset_thd_for_next_command(), since this
is the logical start of the next "statement". Note that this
- call might reset the value of current_stmt_binlog_row_based, so
+ call might reset the value of current_stmt_binlog_format, so
we need to do any changes to that value after this function.
*/
lex_start(thd);
- mysql_reset_thd_for_next_command(thd, 0);
+ mysql_reset_thd_for_next_command(thd);
/*
The current statement is just about to begin and
has not yet modified anything. Note, all.modified is reset
@@ -8095,16 +8364,12 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
*/
thd->transaction.stmt.modified_non_trans_table= FALSE;
/*
- Check if the slave is set to use SBR. If so, it should switch
- to using RBR until the end of the "statement", i.e., next
- STMT_END_F or next error.
+ This is a row injection, so we flag the "statement" as
+ such. Note that this code is called both when the slave does row
+ injections and when the BINLOG statement is used to do row
+ injections.
*/
- if (!thd->current_stmt_binlog_row_based &&
- mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG))
- {
- thd->set_current_stmt_binlog_row_based();
- }
-
+ thd->lex->set_stmt_row_injection();
/*
There are a few flags that are replicated with each row event.
@@ -8112,20 +8377,20 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
the event.
*/
if (get_flags(NO_FOREIGN_KEY_CHECKS_F))
- thd->options|= OPTION_NO_FOREIGN_KEY_CHECKS;
+ thd->variables.option_bits|= OPTION_NO_FOREIGN_KEY_CHECKS;
else
- thd->options&= ~OPTION_NO_FOREIGN_KEY_CHECKS;
+ thd->variables.option_bits&= ~OPTION_NO_FOREIGN_KEY_CHECKS;
if (get_flags(RELAXED_UNIQUE_CHECKS_F))
- thd->options|= OPTION_RELAXED_UNIQUE_CHECKS;
+ thd->variables.option_bits|= OPTION_RELAXED_UNIQUE_CHECKS;
else
- thd->options&= ~OPTION_RELAXED_UNIQUE_CHECKS;
+ thd->variables.option_bits&= ~OPTION_RELAXED_UNIQUE_CHECKS;
/* A small test to verify that objects have consistent types */
- DBUG_ASSERT(sizeof(thd->options) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS));
+ DBUG_ASSERT(sizeof(thd->variables.option_bits) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS));
- if (simple_open_n_lock_tables(thd, rli->tables_to_lock))
+ if (open_and_lock_tables(thd, rli->tables_to_lock, FALSE, 0))
{
- uint actual_error= thd->main_da.sql_errno();
+ uint actual_error= thd->stmt_da->sql_errno();
if (thd->is_slave_error || thd->is_fatal_error)
{
/*
@@ -8135,12 +8400,12 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
having severe errors which should not be skiped.
*/
rli->report(ERROR_LEVEL, actual_error,
- "Error '%s' on opening tables",
- (actual_error ? thd->main_da.message() :
+ "Error executing row event: '%s'",
+ (actual_error ? thd->stmt_da->message() :
"unexpected success or fatal error"));
thd->is_slave_error= 1;
}
- const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
+ const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
DBUG_RETURN(actual_error);
}
@@ -8153,27 +8418,52 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
*/
{
+ DBUG_PRINT("debug", ("Checking compability of tables to lock - tables_to_lock: %p",
+ rli->tables_to_lock));
+
+ /**
+ When using RBR and MyISAM MERGE tables the base tables that make
+ up the MERGE table can be appended to the list of tables to lock.
+
+ Thus, we just check compatibility for those that tables that have
+ a correspondent table map event (ie, those that are actually going
+ to be accessed while applying the event). That's why the loop stops
+ at rli->tables_to_lock_count .
+
+ NOTE: The base tables are added here are removed when
+ close_thread_tables is called.
+ */
RPL_TABLE_LIST *ptr= rli->tables_to_lock;
- for ( ; ptr ; ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_global))
+ for (uint i= 0 ; ptr && (i < rli->tables_to_lock_count);
+ ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_global), i++)
{
- if (ptr->m_tabledef.compatible_with(rli, ptr->table))
+ DBUG_ASSERT(ptr->m_tabledef_valid);
+ TABLE *conv_table;
+ if (!ptr->m_tabledef.compatible_with(thd, const_cast<Relay_log_info*>(rli),
+ ptr->table, &conv_table))
{
+ DBUG_PRINT("debug", ("Table: %s.%s is not compatible with master",
+ ptr->table->s->db.str,
+ ptr->table->s->table_name.str));
/*
We should not honour --slave-skip-errors at this point as we are
having severe errors which should not be skiped.
*/
- mysql_unlock_tables(thd, thd->lock);
- thd->lock= 0;
thd->is_slave_error= 1;
- const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
+ const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
DBUG_RETURN(ERR_BAD_TABLE_DEF);
}
+ DBUG_PRINT("debug", ("Table: %s.%s is compatible with master"
+ " - conv_table: %p",
+ ptr->table->s->db.str,
+ ptr->table->s->table_name.str, conv_table));
+ ptr->m_conv_table= conv_table;
}
}
/*
- ... and then we add all the tables to the table map and remove
- them from tables to lock.
+ ... and then we add all the tables to the table map and but keep
+ them in the tables to lock list.
We also invalidate the query cache for all the tables, since
they will now be changed.
@@ -8185,10 +8475,10 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
Rows_log_event, we can invalidate the query cache for the
associated table.
*/
- for (TABLE_LIST *ptr= rli->tables_to_lock ; ptr ; ptr= ptr->next_global)
- {
+ TABLE_LIST *ptr= rli->tables_to_lock;
+ for (uint i=0 ; ptr && (i < rli->tables_to_lock_count); ptr= ptr->next_global, i++)
const_cast<Relay_log_info*>(rli)->m_table_map.set_table(ptr->table_id, ptr->table);
- }
+
#ifdef HAVE_QUERY_CACHE
query_cache.invalidate_locked_for_write(thd, rli->tables_to_lock);
#endif
@@ -8258,13 +8548,23 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
/*
Bug#56662 Assertion failed: next_insert_id == 0, file handler.cc
Don't allow generation of auto_increment value when processing
- rows event by setting 'MODE_NO_AUTO_VALUE_ON_ZERO'.
+ rows event by setting 'MODE_NO_AUTO_VALUE_ON_ZERO'. The exception
+ to this rule happens when the auto_inc column exists on some
+ extra columns on the slave. In that case, do not force
+ MODE_NO_AUTO_VALUE_ON_ZERO.
*/
- ulong saved_sql_mode= thd->variables.sql_mode;
- thd->variables.sql_mode= MODE_NO_AUTO_VALUE_ON_ZERO;
+ ulonglong saved_sql_mode= thd->variables.sql_mode;
+ if (!is_auto_inc_in_extra_columns())
+ thd->variables.sql_mode= MODE_NO_AUTO_VALUE_ON_ZERO;
// row processing loop
+ /*
+ set the initial time of this ROWS statement if it was not done
+ before in some other ROWS event.
+ */
+ const_cast<Relay_log_info*>(rli)->set_row_stmt_start_timestamp();
+
while (error == 0 && m_curr_row < m_rows_end)
{
/* in_use can have been set to NULL in close_tables_for_reopen */
@@ -8284,7 +8584,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
{
int actual_error= convert_handler_error(error, thd, table);
bool idempotent_error= (idempotent_error_code(error) &&
- (slave_exec_mode & SLAVE_EXEC_MODE_IDEMPOTENT));
+ (slave_exec_mode == SLAVE_EXEC_MODE_IDEMPOTENT));
bool ignored_error= (idempotent_error == 0 ?
ignored_error_code(actual_error) : 0);
@@ -8331,8 +8631,16 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
*/
thd->variables.sql_mode= saved_sql_mode;
- DBUG_EXECUTE_IF("STOP_SLAVE_after_first_Rows_event",
- const_cast<Relay_log_info*>(rli)->abort_slave= 1;);
+ {/**
+ The following failure injecion works in cooperation with tests
+ setting @@global.debug= 'd,stop_slave_middle_group'.
+ The sql thread receives the killed status and will proceed
+ to shutdown trying to finish incomplete events group.
+ */
+ DBUG_EXECUTE_IF("stop_slave_middle_group",
+ if (thd->transaction.all.modified_non_trans_table)
+ const_cast<Relay_log_info*>(rli)->abort_slave= 1;);
+ }
if ((error= do_after_row_operations(rli, error)) &&
ignored_error_code(convert_handler_error(error, thd, table)))
@@ -8347,47 +8655,22 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
}
} // if (table)
- /*
- We need to delay this clear until here bacause unpack_current_row() uses
- master-side table definitions stored in rli.
- */
- if (rli->tables_to_lock && get_flags(STMT_END_F))
- const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
if (error)
{
slave_rows_error_report(ERROR_LEVEL, error, rli, thd, table,
get_type_str(),
RPL_LOG_NAME, (ulong) log_pos);
- thd->reset_current_stmt_binlog_row_based();
- thd->is_slave_error= 1;
- DBUG_RETURN(error);
- }
- /*
- This code would ideally be placed in do_update_pos() instead, but
- since we have no access to table there, we do the setting of
- last_event_start_time here instead.
- */
- else if (table && (table->s->primary_key == MAX_KEY) &&
- !cache_stmt && get_flags(STMT_END_F) == RLE_NO_FLAGS)
- {
/*
- ------------ Temporary fix until WL#2975 is implemented ---------
-
- This event is not the last one (no STMT_END_F). If we stop now
- (in case of terminate_slave_thread()), how will we restart? We
- have to restart from Table_map_log_event, but as this table is
- not transactional, the rows already inserted will still be
- present, and idempotency is not guaranteed (no PK) so we risk
- that repeating leads to double insert. So we desperately try to
- continue, hope we'll eventually leave this buggy situation (by
- executing the final Rows_log_event). If we are in a hopeless
- wait (reached end of last relay log and nothing gets appended
- there), we timeout after one minute, and notify DBA about the
- problem. When WL#2975 is implemented, just remove the member
- Relay_log_info::last_event_start_time and all its occurrences.
+ @todo We should probably not call
+ reset_current_stmt_binlog_format_row() from here.
+
+ Note: this applies to log_event_old.cc too.
+ /Sven
*/
- const_cast<Relay_log_info*>(rli)->last_event_start_time= my_time(0);
+ thd->reset_current_stmt_binlog_format_row();
+ thd->is_slave_error= 1;
+ DBUG_RETURN(error);
}
if (get_flags(STMT_END_F) && (error= rows_event_stmt_cleanup(rli, thd)))
@@ -8443,7 +8726,7 @@ static int rows_event_stmt_cleanup(Relay_log_info const *rli, THD * thd)
(assume the last master's transaction is ignored by the slave because of
replicate-ignore rules).
*/
- error= thd->binlog_flush_pending_rows_event(true);
+ error= thd->binlog_flush_pending_rows_event(TRUE);
/*
If this event is not in a transaction, the call below will, if some
@@ -8453,8 +8736,11 @@ static int rows_event_stmt_cleanup(Relay_log_info const *rli, THD * thd)
Xid_log_event will come next which will, if some transactional engines
are involved, commit the transaction and flush the pending event to the
binlog.
+ If there was a deadlock the transaction should have been rolled back
+ already. So there should be no need to rollback the transaction.
*/
- error|= ha_autocommit_or_rollback(thd, error);
+ DBUG_ASSERT(! thd->transaction_rollback_request);
+ error|= (error ? trans_rollback_stmt(thd) : trans_commit_stmt(thd));
/*
Now what if this is not a transactional engine? we still need to
@@ -8466,7 +8752,17 @@ static int rows_event_stmt_cleanup(Relay_log_info const *rli, THD * thd)
event flushed.
*/
- thd->reset_current_stmt_binlog_row_based();
+ /*
+ @todo We should probably not call
+ reset_current_stmt_binlog_format_row() from here.
+
+ Note: this applies to log_event_old.cc too
+
+ Btw, the previous comment about transactional engines does not
+ seem related to anything that happens here.
+ /Sven
+ */
+ thd->reset_current_stmt_binlog_format_row();
const_cast<Relay_log_info*>(rli)->cleanup_context(thd, 0);
}
@@ -8613,14 +8909,16 @@ void Rows_log_event::print_helper(FILE *file,
#ifndef MYSQL_CLIENT
Annotate_rows_log_event::Annotate_rows_log_event(THD *thd,
- uint16 cache_type_arg)
- : Log_event(thd, 0, true),
+ bool using_trans,
+ bool direct)
+ : Log_event(thd, 0, using_trans),
m_save_thd_query_txt(0),
m_save_thd_query_len(0)
{
m_query_txt= thd->query();
m_query_len= thd->query_length();
- cache_type= cache_type_arg;
+ if (direct)
+ cache_type= Log_event::EVENT_NO_CACHE;
}
#endif
@@ -8811,7 +9109,10 @@ int Table_map_log_event::save_field_metadata()
DBUG_ENTER("Table_map_log_event::save_field_metadata");
int index= 0;
for (unsigned int i= 0 ; i < m_table->s->fields ; i++)
+ {
+ DBUG_PRINT("debug", ("field_type: %d", m_coltype[i]));
index+= m_table->s->field[i]->save_field_metadata(&m_field_metadata[index]);
+ }
DBUG_RETURN(index);
}
#endif /* !defined(MYSQL_CLIENT) */
@@ -8824,7 +9125,7 @@ int Table_map_log_event::save_field_metadata()
#if !defined(MYSQL_CLIENT)
Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
bool is_transactional)
- : Log_event(thd, 0, true),
+ : Log_event(thd, 0, is_transactional),
m_table(tbl),
m_dbnam(tbl->s->db.str),
m_dblen(m_dbnam ? tbl->s->db.length : 0),
@@ -9023,8 +9324,8 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len,
Table_map_log_event::~Table_map_log_event()
{
- my_free(m_meta_memory, MYF(MY_ALLOW_ZERO_PTR));
- my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(m_meta_memory);
+ my_free(m_memory);
}
@@ -9126,7 +9427,7 @@ int Table_map_log_event::rewrite_db(const char* new_db, size_t new_len,
memcpy((void*)m_tblnam, tblnam, m_tbllen + 1);
memcpy(m_coltype, coltype, m_colcnt);
- my_free(memory, MYF(MY_WME));
+ my_free(memory);
DBUG_RETURN(0);
}
#endif /* MYSQL_CLIENT */
@@ -9211,9 +9512,9 @@ check_table_map(Relay_log_info const *rli, RPL_TABLE_LIST *table_list)
res= FILTERED_OUT;
else
{
- for(RPL_TABLE_LIST *ptr= static_cast<RPL_TABLE_LIST*>(rli->tables_to_lock);
- ptr;
- ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_local))
+ RPL_TABLE_LIST *ptr= static_cast<RPL_TABLE_LIST*>(rli->tables_to_lock);
+ for(uint i=0 ; ptr && (i< rli->tables_to_lock_count);
+ ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_local), i++)
{
if (ptr->table_id == table_list->table_id)
{
@@ -9245,9 +9546,7 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
DBUG_ASSERT(rli->sql_thd == thd);
/* Step the query id to mark what columns that are actually used. */
- pthread_mutex_lock(&LOCK_thread_count);
- thd->query_id= next_query_id();
- pthread_mutex_unlock(&LOCK_thread_count);
+ thd->set_query_id(next_query_id());
if (!(memory= my_multi_malloc(MYF(MY_WME),
&table_list, (uint) sizeof(RPL_TABLE_LIST),
@@ -9256,16 +9555,16 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
NullS)))
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
- bzero(table_list, sizeof(*table_list));
- table_list->db = db_mem;
- table_list->alias= table_list->table_name = tname_mem;
- table_list->lock_type= TL_WRITE;
- table_list->next_global= table_list->next_local= 0;
+ strmov(db_mem, rpl_filter->get_rewrite_db(m_dbnam, &dummy_len));
+ strmov(tname_mem, m_tblnam);
+
+ table_list->init_one_table(db_mem, strlen(db_mem),
+ tname_mem, strlen(tname_mem),
+ tname_mem, 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;
- strmov(table_list->db, rpl_filter->get_rewrite_db(m_dbnam, &dummy_len));
- strmov(table_list->table_name, m_tblnam);
DBUG_PRINT("debug", ("table: %s is mapped to %u", table_list->table_name, table_list->table_id));
enum_tbl_map_status tblmap_status= check_table_map(rli, table_list);
if (tblmap_status == OK_TO_PROCESS)
@@ -9286,7 +9585,8 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
m_field_metadata, m_field_metadata_size,
m_null_bits, m_flags);
table_list->m_tabledef_valid= TRUE;
- table_list->skip_temporary= 1;
+ table_list->m_conv_table= NULL;
+ table_list->open_type= OT_BASE_ONLY;
/*
We record in the slave's information that the table should be
@@ -9335,7 +9635,7 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
MYF(0), buf);
}
- my_free(memory, MYF(0));
+ my_free(memory);
}
DBUG_RETURN(tblmap_status == SAME_ID_MAPPING_DIFFERENT_TABLE);
@@ -9479,12 +9779,18 @@ Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability
{
int error= 0;
+ /*
+ Increment the global status insert count variable
+ */
+ if (get_flags(STMT_END_F))
+ status_var_increment(thd->status_var.com_stat[SQLCOM_INSERT]);
+
/**
todo: to introduce a property for the event (handler?) which forces
applying the event in the replace (idempotent) fashion.
*/
- if ((slave_exec_mode & SLAVE_EXEC_MODE_IDEMPOTENT) ||
- m_table->s->db_type()->db_type == DB_TYPE_NDBCLUSTER)
+ if ((slave_exec_mode == SLAVE_EXEC_MODE_IDEMPOTENT) ||
+ (m_table->s->db_type()->db_type == DB_TYPE_NDBCLUSTER))
{
/*
We are using REPLACE semantics and not INSERT IGNORE semantics
@@ -9549,9 +9855,28 @@ Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability
* table->auto_increment_field_not_null and SQL_MODE(if includes
* MODE_NO_AUTO_VALUE_ON_ZERO) in update_auto_increment function.
* SQL_MODE of slave sql thread is always consistency with master's.
- * In RBR, auto_increment fields never are NULL.
+ * In RBR, auto_increment fields never are NULL, except if the auto_inc
+ * column exists only on the slave side (i.e., in an extra column
+ * on the slave's table).
*/
- m_table->auto_increment_field_not_null= TRUE;
+ if (!is_auto_inc_in_extra_columns())
+ m_table->auto_increment_field_not_null= TRUE;
+ else
+ {
+ /*
+ Here we have checked that there is an extra field
+ on this server's table that has an auto_inc column.
+
+ Mark that the auto_increment field is null and mark
+ the read and write set bits.
+
+ (There can only be one AUTO_INC column, it is always
+ indexed and it cannot have a DEFAULT value).
+ */
+ m_table->auto_increment_field_not_null= FALSE;
+ m_table->mark_auto_increment_column();
+ }
+
return error;
}
@@ -9560,9 +9885,22 @@ Write_rows_log_event::do_after_row_operations(const Slave_reporting_capability *
int error)
{
int local_error= 0;
+
+ /**
+ Clear the write_set bit for auto_inc field that only
+ existed on the destination table as an extra column.
+ */
+ if (is_auto_inc_in_extra_columns())
+ {
+ bitmap_clear_bit(m_table->write_set, m_table->next_number_field->field_index);
+ bitmap_clear_bit( m_table->read_set, m_table->next_number_field->field_index);
+
+ if (get_flags(STMT_END_F))
+ m_table->file->ha_release_auto_increment();
+ }
m_table->next_number_field=0;
m_table->auto_increment_field_not_null= FALSE;
- if ((slave_exec_mode & SLAVE_EXEC_MODE_IDEMPOTENT) ||
+ if ((slave_exec_mode == SLAVE_EXEC_MODE_IDEMPOTENT) ||
m_table->s->db_type()->db_type == DB_TYPE_NDBCLUSTER)
{
m_table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
@@ -9683,7 +10021,13 @@ Rows_log_event::write_row(const Relay_log_info *const rli,
ulong estimated_rows= (m_rows_end - m_curr_row) / (m_curr_row_end - m_curr_row);
m_table->file->ha_start_bulk_insert(estimated_rows);
}
-
+
+ /*
+ Explicitly set the auto_inc to null to make sure that
+ it gets an auto_generated value.
+ */
+ if (is_auto_inc_in_extra_columns())
+ m_table->next_number_field->set_null();
#ifndef DBUG_OFF
DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
@@ -9854,19 +10198,19 @@ Rows_log_event::write_row(const Relay_log_info *const rli,
#endif
-int
+int
Write_rows_log_event::do_exec_row(const Relay_log_info *const rli)
{
DBUG_ASSERT(m_table != NULL);
- int error= write_row(rli, (slave_exec_mode & SLAVE_EXEC_MODE_IDEMPOTENT));
+ int error= write_row(rli, slave_exec_mode == SLAVE_EXEC_MODE_IDEMPOTENT);
if (error && !thd->is_error())
{
DBUG_ASSERT(0);
my_error(ER_UNKNOWN_ERROR, MYF(0));
}
-
- return error;
+
+ return error;
}
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
@@ -10087,6 +10431,52 @@ int Rows_log_event::find_key()
}
+/*
+ Check if we are already spending too much time on this statement.
+ if we are, warn user that it might be because table does not have
+ a PK, but only if the warning was not printed before for this STMT.
+
+ @param type The event type code.
+ @param table_name The name of the table that the slave is
+ operating.
+ @param is_index_scan States whether the slave is doing an index scan
+ or not.
+ @param rli The relay metadata info.
+*/
+static inline
+void issue_long_find_row_warning(Log_event_type type,
+ const char *table_name,
+ bool is_index_scan,
+ const Relay_log_info *rli)
+{
+ if ((global_system_variables.log_warnings > 1 &&
+ !const_cast<Relay_log_info*>(rli)->is_long_find_row_note_printed()))
+ {
+ time_t now= my_time(0);
+ time_t stmt_ts= const_cast<Relay_log_info*>(rli)->get_row_stmt_start_timestamp();
+
+ DBUG_EXECUTE_IF("inject_long_find_row_note",
+ stmt_ts-=(LONG_FIND_ROW_THRESHOLD*2););
+
+ long delta= (long) (now - stmt_ts);
+
+ if (delta > LONG_FIND_ROW_THRESHOLD)
+ {
+ const_cast<Relay_log_info*>(rli)->set_long_find_row_note_printed();
+ const char* evt_type= type == DELETE_ROWS_EVENT ? " DELETE" : "n UPDATE";
+ const char* scan_type= is_index_scan ? "scanning an index" : "scanning the table";
+
+ 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 "
+ "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);
+ }
+ }
+}
+
+
/**
Locate the current row in event's table.
@@ -10112,7 +10502,11 @@ int Rows_log_event::find_key()
@note If the engine allows random access of the records, a combination of
@c position() and @c rnd_pos() will be used.
- */
+
+ Note that one MUST call ha_index_or_rnd_end() after this function if
+ it returns 0 as we must leave the row position in the handler intact
+ for any following update/delete command.
+*/
int Rows_log_event::find_row(const Relay_log_info *rli)
{
@@ -10122,6 +10516,7 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
TABLE *table= m_table;
int error= 0;
+ bool is_table_scan= false, is_index_scan= false;
/*
rpl_row_tabledefs.test specifies that
@@ -10200,7 +10595,7 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
{
DBUG_PRINT("info",("ha_index_init returns error %d",error));
table->file->print_error(error, MYF(0));
- goto err;
+ goto end;
}
/* Fill key data for the row */
@@ -10235,7 +10630,7 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
error= HA_ERR_KEY_NOT_FOUND;
table->file->print_error(error, MYF(0));
table->file->ha_index_end();
- goto err;
+ goto end;
}
/*
@@ -10265,15 +10660,15 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
/* Unique does not have non nullable part */
if (!(table->key_info->flags & (HA_NULL_PART_KEY)))
{
- table->file->ha_index_end();
- goto ok;
+ error= 0;
+ goto end;
}
else
{
KEY *keyinfo= table->key_info;
/*
- Unique has nullable part. We need to check if there is any field in the
- BI image that is null and part of UNNI.
+ Unique has nullable part. We need to check if there is any
+ field in the BI image that is null and part of UNNI.
*/
bool null_found= FALSE;
for (uint i=0; i < keyinfo->key_parts && !null_found; i++)
@@ -10285,14 +10680,16 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
if (!null_found)
{
- table->file->ha_index_end();
- goto ok;
+ error= 0;
+ goto end;
}
/* else fall through to index scan */
}
}
+ is_index_scan=true;
+
/*
In case key is not unique, we still have to iterate over records found
and find the one which is identical to the row given. A copy of the
@@ -10327,14 +10724,9 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
DBUG_PRINT("info",("no record matching the given row found"));
table->file->print_error(error, MYF(0));
table->file->ha_index_end();
- goto err;
+ goto end;
}
}
-
- /*
- Have to restart the scan to be able to fetch the next row.
- */
- table->file->ha_index_end();
}
else
{
@@ -10342,16 +10734,16 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
/* We use this to test that the correct key is used in test cases. */
DBUG_EXECUTE_IF("slave_crash_if_table_scan", abort(););
- 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)))
{
DBUG_PRINT("info",("error initializing table scan"
" (ha_rnd_init returns %d)",error));
- goto err;
+ goto end;
}
+ is_table_scan= true;
+
/* Continue until we find the right record or have made a full loop */
do
{
@@ -10363,8 +10755,14 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
switch (error) {
case 0:
+ DBUG_DUMP("record found", table->record[0], table->s->reclength);
break;
+ case HA_ERR_END_OF_FILE:
+ DBUG_PRINT("info", ("Record not found"));
+ table->file->ha_rnd_end();
+ goto end;
+
/*
If the record was deleted, we pick the next one without doing
any comparisons.
@@ -10372,50 +10770,28 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
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)))
- {
- error= error2;
- goto err;
- }
- }
- break;
-
default:
DBUG_PRINT("info", ("Failed to get next record"
" (rnd_next returns %d)",error));
table->file->print_error(error, MYF(0));
table->file->ha_rnd_end();
- goto err;
+ goto end;
}
}
- while (restart_count < 2 && record_compare(table));
+ while (record_compare(table));
/*
Note: above record_compare will take into accout all record fields
which might be incorrect in case a partial row was given in the event
*/
- /*
- Have to restart the scan to be able to fetch the next row.
- */
- if (restart_count == 2)
- DBUG_PRINT("info", ("Record not found"));
- else
- DBUG_DUMP("record found", table->record[0], table->s->reclength);
- table->file->ha_rnd_end();
-
DBUG_ASSERT(error == HA_ERR_END_OF_FILE || error == 0);
- goto err;
}
-ok:
- table->default_column_bitmaps();
- DBUG_RETURN(0);
-err:
+end:
+ if (is_table_scan || is_index_scan)
+ issue_long_find_row_warning(get_type_code(), m_table->alias.c_ptr(),
+ is_index_scan, rli);
table->default_column_bitmaps();
DBUG_RETURN(error);
}
@@ -10452,6 +10828,12 @@ Delete_rows_log_event::Delete_rows_log_event(const char *buf, uint event_len,
int
Delete_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const)
{
+ /*
+ Increment the global status delete count variable
+ */
+ if (get_flags(STMT_END_F))
+ status_var_increment(thd->status_var.com_stat[SQLCOM_DELETE]);
+
if ((m_table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
m_table->s->primary_key < MAX_KEY)
{
@@ -10470,7 +10852,7 @@ Delete_rows_log_event::do_after_row_operations(const Slave_reporting_capability
{
/*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, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(m_key);
m_key= NULL;
m_key_info= NULL;
@@ -10488,6 +10870,7 @@ int Delete_rows_log_event::do_exec_row(const Relay_log_info *const rli)
Delete the record found, located in record[0]
*/
error= m_table->file->ha_delete_row(m_table->record[0]);
+ m_table->file->ha_index_or_rnd_end();
}
return error;
}
@@ -10575,6 +10958,12 @@ Update_rows_log_event::Update_rows_log_event(const char *buf, uint event_len,
int
Update_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const)
{
+ /*
+ Increment the global status update count variable
+ */
+ if (get_flags(STMT_END_F))
+ status_var_increment(thd->status_var.com_stat[SQLCOM_UPDATE]);
+
int err;
if ((err= find_key()))
return err;
@@ -10590,7 +10979,7 @@ Update_rows_log_event::do_after_row_operations(const Slave_reporting_capability
{
/*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, MYF(MY_ALLOW_ZERO_PTR)); // Free for multi_malloc
+ my_free(m_key); // Free for multi_malloc
m_key= NULL;
m_key_info= NULL;
@@ -10630,7 +11019,7 @@ Update_rows_log_event::do_exec_row(const Relay_log_info *const rli)
m_curr_row= m_curr_row_end;
/* this also updates m_curr_row_end */
if ((error= unpack_current_row(rli)))
- return error;
+ goto err;
/*
Now we have the right row to update. The old row (the one we're
@@ -10650,6 +11039,8 @@ Update_rows_log_event::do_exec_row(const Relay_log_info *const rli)
if (error == HA_ERR_RECORD_IS_THE_SAME)
error= 0;
+err:
+ m_table->file->ha_index_or_rnd_end();
return error;
}
@@ -10802,7 +11193,7 @@ st_print_event_info::st_print_event_info()
auto_increment_increment(0),auto_increment_offset(0), charset_inited(0),
lc_time_names_number(~0),
charset_database_number(ILLEGAL_CHARSET_INFO_NUMBER),
- thread_id(0), thread_id_printed(false),
+ thread_id(0), thread_id_printed(false), skip_replication(0),
base64_output_mode(BASE64_OUTPUT_UNSPEC), printed_fd_event(FALSE)
{
/*
@@ -10821,6 +11212,18 @@ st_print_event_info::st_print_event_info()
}
#endif
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+Heartbeat_log_event::Heartbeat_log_event(const char* buf, uint event_len,
+ const Format_description_log_event* description_event)
+ :Log_event(buf, description_event)
+{
+ uint8 header_size= description_event->common_header_len;
+ ident_len = event_len - header_size;
+ set_if_smaller(ident_len,FN_REFLEN-1);
+ log_ident= buf + header_size;
+}
+#endif
+
#if defined(MYSQL_SERVER)
/*
Access to the current replication position.
diff --git a/sql/log_event.h b/sql/log_event.h
index e86a5b1a3e4..c10380618a8 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -1,5 +1,4 @@
-/*
- Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +11,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@addtogroup Replication
@@ -30,7 +28,7 @@
#ifndef _log_event_h
#define _log_event_h
-#if defined(USE_PRAGMA_INTERFACE) && !defined(MYSQL_CLIENT)
+#if defined(USE_PRAGMA_INTERFACE) && defined(MYSQL_SERVER)
#pragma interface /* gcc class implementation */
#endif
@@ -38,18 +36,23 @@
#include "rpl_constants.h"
#ifdef MYSQL_CLIENT
+#include "sql_const.h"
#include "rpl_utility.h"
#include "hash.h"
#include "rpl_tblmap.h"
-#include "rpl_tblmap.cc"
#endif
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
#include "rpl_record.h"
#include "rpl_reporting.h"
+#include "sql_class.h" /* THD */
#endif
+/* Forward declarations */
+class String;
+
#define PREFIX_SQL_LOAD "SQL_LOAD-"
+#define LONG_FIND_ROW_THRESHOLD 60 /* seconds */
/**
Either assert or return an error.
@@ -253,7 +256,9 @@ struct sql_ex_info
#define EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN (4 + 4 + 4 + 1)
#define EXECUTE_LOAD_QUERY_HEADER_LEN (QUERY_HEADER_LEN + EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN)
#define INCIDENT_HEADER_LEN 2
+#define HEARTBEAT_HEADER_LEN 0
#define ANNOTATE_ROWS_HEADER_LEN 0
+
/*
Max number of possible extra bytes in a replication event compared to a
packet (i.e. a query) sent from client to master;
@@ -505,6 +510,19 @@ struct sql_ex_info
#define LOG_EVENT_RELAY_LOG_F 0x40
/**
+ @def LOG_EVENT_SKIP_REPLICATION_F
+
+ Flag set by application creating the event (with @@skip_replication); the
+ slave will skip replication of such events if
+ --replicate-events-marked-for-skip is not set to REPLICATE.
+
+ This is a MariaDB flag; we allocate it from the end of the available
+ values to reduce risk of conflict with new MySQL flags.
+*/
+#define LOG_EVENT_SKIP_REPLICATION_F 0x8000
+
+
+/**
@def OPTIONS_WRITTEN_TO_BIN_LOG
OPTIONS_WRITTEN_TO_BIN_LOG are the bits of thd->options which must
@@ -612,6 +630,17 @@ enum Log_event_type
*/
INCIDENT_EVENT= 26,
+ /*
+ Heartbeat event to be send by master at its idle time
+ to ensure master's online status to slave
+ */
+ HEARTBEAT_LOG_EVENT= 27,
+
+ /*
+ Add new events here - right above this comment!
+ Existing events (except ENUM_END_EVENT) should never change their numbers
+ */
+
/* New MySQL/Sun events are to be added right above this comment */
MYSQL_EVENTS_END,
@@ -619,9 +648,7 @@ enum Log_event_type
/* New Maria event numbers start from here */
ANNOTATE_ROWS_EVENT= 160,
- /*
- Existing events (except ENUM_END_EVENT) should never change their numbers
- */
+ /* Add new MariaDB events here - right above this comment! */
ENUM_END_EVENT /* end marker */
};
@@ -639,7 +666,7 @@ enum Int_event_type
};
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
class String;
class MYSQL_BIN_LOG;
class THD;
@@ -683,7 +710,7 @@ typedef struct st_print_event_info
bool flags2_inited;
uint32 flags2;
bool sql_mode_inited;
- ulong sql_mode; /* must be same as THD.variables.sql_mode */
+ ulonglong 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
@@ -692,6 +719,11 @@ typedef struct st_print_event_info
uint charset_database_number;
uint thread_id;
bool thread_id_printed;
+ /*
+ Track when @@skip_replication changes so we need to output a SET
+ statement for it.
+ */
+ int skip_replication;
st_print_event_info();
@@ -717,11 +749,9 @@ typedef struct st_print_event_info
uint8 common_header_len;
char delimiter[16];
-#ifdef MYSQL_CLIENT
uint verbose;
table_mapping m_table_map;
table_mapping m_table_map_ignored;
-#endif
/*
These two caches are used by the row-based replication events to
@@ -733,6 +763,20 @@ typedef struct st_print_event_info
} PRINT_EVENT_INFO;
#endif
+/**
+ the struct aggregates two paramenters that identify an event
+ uniquely in scope of communication of a particular master and slave couple.
+ I.e there can not be 2 events from the same staying connected master which
+ have the same coordinates.
+ @note
+ Such identifier is not yet unique generally as the event originating master
+ is resetable. Also the crashed master can be replaced with some other.
+*/
+typedef struct event_coordinates
+{
+ char * file_name; // binlog file name (directories stripped)
+ my_off_t pos; // event's position in the binlog file
+} LOG_POS_COORD;
/**
@class Log_event
@@ -893,6 +937,31 @@ public:
EVENT_SKIP_COUNT
};
+ enum enum_event_cache_type
+ {
+ EVENT_INVALID_CACHE,
+ /*
+ If possible the event should use a non-transactional cache before
+ being flushed to the binary log. This means that it must be flushed
+ right after its correspondent statement is completed.
+ */
+ EVENT_STMT_CACHE,
+ /*
+ The event should use a transactional cache before being flushed to
+ the binary log. This means that it must be flushed upon commit or
+ rollback.
+ */
+ EVENT_TRANSACTIONAL_CACHE,
+ /*
+ The event must be written directly to the binary log without going
+ through a cache.
+ */
+ EVENT_NO_CACHE,
+ /**
+ If there is a need for different types, introduce them before this.
+ */
+ EVENT_CACHE_COUNT
+ };
/*
The following type definition is to be used whenever data is placed
@@ -947,33 +1016,12 @@ public:
/**
Some 16 flags. See the definitions above for LOG_EVENT_TIME_F,
- LOG_EVENT_FORCED_ROTATE_F, LOG_EVENT_THREAD_SPECIFIC_F, and
- LOG_EVENT_SUPPRESS_USE_F for notes.
+ LOG_EVENT_FORCED_ROTATE_F, LOG_EVENT_THREAD_SPECIFIC_F,
+ LOG_EVENT_SUPPRESS_USE_F, and LOG_EVENT_SKIP_REPLICATION_F for notes.
*/
uint16 flags;
- bool cache_stmt;
- /*
- The revid:alfranio.correia@sun.com-20091103190256-637o8qxlveikrt3i commit
- ("WL#2687 WL#5072 BUG#40278 BUG#47175") in MySQL 5.5 changes the bool
- cache_stmt into an enum cache_type. For the backport of WL#2540 binlog
- event checksum, we need this event_type member to know if we are writing
- directly to the log, or into a transaction cache.
-
- Until the cache_type stuff is merged, we temporarily partially backport
- the cache_type member, only enough to be able to check if we are writing
- directly to log or not. Once MySQL 5.5 is merged, this can be removed, and
- replaced with the MySQL 5.5 code.
-
- Similarly, in MySQL 5.5 the decision on whether to write directly to log
- or indirectly through cache is decided differently, and the
- pre_55_writing_direct() (and all calls to it) are not needed and can be
- removed once 5.5 is merged.
- */
- enum enum_event_cache_type { EVENT_INVALID_CACHE, EVENT_STMT_CACHE,
- EVENT_TRANSACTIONAL_CACHE, EVENT_NO_CACHE };
uint16 cache_type;
- void pre_55_writing_direct() { cache_type= EVENT_NO_CACHE; }
/**
A storage to cache the global system variable's value.
@@ -985,11 +1033,12 @@ public:
Placeholder for event checksum while writing to binlog.
*/
ha_checksum crc;
-#ifndef MYSQL_CLIENT
+
+#ifdef MYSQL_SERVER
THD* thd;
Log_event();
- Log_event(THD* thd_arg, uint16 flags_arg, bool cache_stmt);
+ Log_event(THD* thd_arg, uint16 flags_arg, bool is_transactional);
/*
read_log_event() functions read an event from a binlog or relay
log; used by SHOW BINLOG EVENTS, the binlog_dump thread on the
@@ -1002,7 +1051,7 @@ public:
constructor and pass description_event as an argument.
*/
static Log_event* read_log_event(IO_CACHE* file,
- pthread_mutex_t* log_lock,
+ mysql_mutex_t* log_lock,
const Format_description_log_event
*description_event,
my_bool crc_check);
@@ -1031,9 +1080,10 @@ public:
@retval LOG_READ_TOO_LARGE event too large
*/
static int read_log_event(IO_CACHE* file, String* packet,
- pthread_mutex_t* log_lock, uint8 checksum_alg_arg,
- const char *log_file_name_arg= NULL,
- bool* is_binlog_active= NULL);
+ mysql_mutex_t* log_lock,
+ uint8 checksum_alg_arg,
+ const char *log_file_name_arg = NULL,
+ bool* is_binlog_active = NULL);
/*
init_show_field_list() prepares the column names and types for the
output of SHOW BINLOG EVENTS; it is used only by SHOW BINLOG
@@ -1087,7 +1137,7 @@ public:
static void operator delete(void *ptr, size_t)
{
- my_free(ptr, MYF(MY_WME|MY_ALLOW_ZERO_PTR));
+ my_free(ptr);
}
/* Placement version of the above operators */
@@ -1095,7 +1145,7 @@ public:
static void operator delete(void*, void*) { }
bool wrapper_my_b_safe_write(IO_CACHE* file, const uchar* buf, ulong data_length);
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
bool write_header(IO_CACHE* file, ulong data_length);
bool write_footer(IO_CACHE* file);
my_bool need_checksum();
@@ -1141,7 +1191,18 @@ public:
void set_relay_log_event() { flags |= LOG_EVENT_RELAY_LOG_F; }
bool is_artificial_event() const { return flags & LOG_EVENT_ARTIFICIAL_F; }
bool is_relay_log_event() const { return flags & LOG_EVENT_RELAY_LOG_F; }
- inline bool get_cache_stmt() const { return cache_stmt; }
+ inline bool use_trans_cache() const
+ {
+ return (cache_type == Log_event::EVENT_TRANSACTIONAL_CACHE);
+ }
+ inline void set_direct_logging()
+ {
+ cache_type = Log_event::EVENT_NO_CACHE;
+ }
+ inline bool use_direct_logging()
+ {
+ return (cache_type == Log_event::EVENT_NO_CACHE);
+ }
Log_event(const char* buf, const Format_description_log_event
*description_event);
virtual ~Log_event() { free_temp_buf();}
@@ -1155,7 +1216,7 @@ public:
if (temp_buf)
{
if (event_owns_temp_buf)
- my_free(temp_buf, MYF(0));
+ my_free(temp_buf);
temp_buf = 0;
}
}
@@ -1179,7 +1240,7 @@ public:
/* Return start of query time or current time */
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
public:
/**
@@ -1483,7 +1544,7 @@ protected:
<td>Q_SQL_MODE_CODE == 1</td>
<td>8 byte bitfield</td>
<td>The @c sql_mode variable. See the section "SQL Modes" in the
- MySQL manual, and see mysql_priv.h for a list of the possible
+ MySQL manual, and see sql_priv.h for a list of the possible
flags. Currently (2007-10-04), the following flags are available:
<pre>
MODE_REAL_AS_FLOAT==0x1
@@ -1746,7 +1807,7 @@ public:
uint32 flags2;
/* In connections sql_mode is 32 bits now but will be 64 bits soon */
- ulong sql_mode;
+ ulonglong sql_mode;
ulong auto_increment_increment, auto_increment_offset;
char charset[6];
uint time_zone_len; /* 0 means uninited */
@@ -1769,10 +1830,10 @@ public:
*/
uint32 master_data_written;
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
Query_log_event(THD* thd_arg, const char* query_arg, ulong query_length,
- bool using_trans, bool suppress_use, int error);
+ bool using_trans, bool direct, bool suppress_use, int error);
const char* get_db() { return db; }
#ifdef HAVE_REPLICATION
void pack_info(THD *thd, Protocol* protocol);
@@ -1789,10 +1850,10 @@ public:
~Query_log_event()
{
if (data_buf)
- my_free((uchar*) data_buf, MYF(0));
+ my_free(data_buf);
}
Log_event_type get_type_code() { return QUERY_EVENT; }
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
bool write(IO_CACHE* file);
virtual bool write_post_header_for_derived(IO_CACHE* file) { return FALSE; }
#endif
@@ -1806,7 +1867,7 @@ public:
/* Writes derived event-specific part of post header. */
public: /* !!! Public in this patch to allow old usage */
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
virtual int do_apply_event(Relay_log_info const *rli);
virtual int do_update_pos(Relay_log_info *rli);
@@ -1903,7 +1964,7 @@ public:
int master_log_len;
uint16 master_port;
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
Slave_log_event(THD* thd_arg, Relay_log_info* rli);
void pack_info(THD *thd, Protocol* protocol);
#else
@@ -1917,12 +1978,12 @@ public:
int get_data_size();
bool is_valid() const { return master_host != 0; }
Log_event_type get_type_code() { return SLAVE_EVENT; }
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
bool write(IO_CACHE* file);
#endif
private:
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_apply_event(Relay_log_info const* rli);
#endif
};
@@ -2138,11 +2199,9 @@ protected:
const Format_description_log_event* description_event);
public:
-#ifndef MYSQL_CLIENT
void 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);
-#endif
ulong thread_id;
ulong slave_proxy_id;
uint32 table_name_len;
@@ -2164,6 +2223,17 @@ public:
uint32 skip_lines;
sql_ex_info sql_ex;
bool local_fname;
+ /**
+ Indicates that this event corresponds to LOAD DATA CONCURRENT,
+
+ @note Since Load_log_event event coming from the binary log
+ lacks information whether LOAD DATA on master was concurrent
+ or not, this flag is only set to TRUE for an auxiliary
+ Load_log_event object which is used in mysql_load() to
+ re-construct LOAD DATA statement from function parameters,
+ for logging.
+ */
+ 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)
@@ -2178,13 +2248,15 @@ public:
return local_fname;
}
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
String field_lens_buf;
String fields_buf;
Load_log_event(THD* thd, sql_exchange* ex, const char* db_arg,
const char* table_name_arg,
- List<Item>& fields_arg, enum enum_duplicates handle_dup, bool ignore,
+ List<Item>& fields_arg,
+ bool is_concurrent_arg,
+ enum enum_duplicates handle_dup, bool ignore,
bool using_trans);
void set_fields(const char* db, List<Item> &fields_arg,
Name_resolution_context *context);
@@ -2211,7 +2283,7 @@ public:
{
return sql_ex.new_format() ? NEW_LOAD_EVENT: LOAD_EVENT;
}
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
bool write_data_header(IO_CACHE* file);
bool write_data_body(IO_CACHE* file);
#endif
@@ -2224,7 +2296,7 @@ public:
}
public: /* !!! Public in this patch to allow old usage */
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_apply_event(Relay_log_info const* rli)
{
return do_apply_event(thd->slave_net,rli,0);
@@ -2235,8 +2307,6 @@ public: /* !!! Public in this patch to allow old usage */
#endif
};
-extern char server_version[SERVER_VERSION_LENGTH];
-
/**
@class Start_log_event_v3
@@ -2286,7 +2356,7 @@ public:
*/
bool dont_set_created;
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
Start_log_event_v3();
#ifdef HAVE_REPLICATION
void pack_info(THD *thd, Protocol* protocol);
@@ -2300,7 +2370,7 @@ public:
const Format_description_log_event* description_event);
~Start_log_event_v3() {}
Log_event_type get_type_code() { return START_EVENT_V3;}
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
bool write(IO_CACHE* file);
#endif
bool is_valid() const { return 1; }
@@ -2310,7 +2380,7 @@ public:
}
protected:
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_apply_event(Relay_log_info const *rli);
virtual enum_skip_reason do_shall_skip(Relay_log_info*)
{
@@ -2367,10 +2437,10 @@ public:
*description_event);
~Format_description_log_event()
{
- my_free((uchar*)post_header_len, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(post_header_len);
}
Log_event_type get_type_code() { return FORMAT_DESCRIPTION_EVENT;}
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
bool write(IO_CACHE* file);
#endif
bool header_is_valid() const
@@ -2404,9 +2474,9 @@ public:
}
void calc_server_version_split();
- static bool is_version_before_checksum(master_version_split *version_split);
+ static bool is_version_before_checksum(const master_version_split *version_split);
protected:
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_apply_event(Relay_log_info const *rli);
virtual int do_update_pos(Relay_log_info *rli);
virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
@@ -2458,11 +2528,14 @@ public:
ulonglong val;
uchar type;
-#ifndef MYSQL_CLIENT
- Intvar_log_event(THD* thd_arg,uchar type_arg, ulonglong val_arg,
- uint16 cache_type_arg)
- :Log_event(thd_arg,0,0), val(val_arg), type(type_arg)
- { cache_type= cache_type_arg; }
+#ifdef MYSQL_SERVER
+Intvar_log_event(THD* thd_arg,uchar type_arg, ulonglong val_arg,
+ bool using_trans, bool direct)
+ :Log_event(thd_arg,0,using_trans),val(val_arg),type(type_arg)
+ {
+ if (direct)
+ cache_type= Log_event::EVENT_NO_CACHE;
+ }
#ifdef HAVE_REPLICATION
void pack_info(THD *thd, Protocol* protocol);
#endif /* HAVE_REPLICATION */
@@ -2476,13 +2549,13 @@ public:
Log_event_type get_type_code() { return INTVAR_EVENT;}
const char* get_var_type_name();
int get_data_size() { return 9; /* sizeof(type) + sizeof(val) */;}
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
bool write(IO_CACHE* file);
#endif
bool is_valid() const { return 1; }
private:
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_apply_event(Relay_log_info const *rli);
virtual int do_update_pos(Relay_log_info *rli);
virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
@@ -2535,11 +2608,14 @@ class Rand_log_event: public Log_event
ulonglong seed1;
ulonglong seed2;
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
Rand_log_event(THD* thd_arg, ulonglong seed1_arg, ulonglong seed2_arg,
- uint16 cache_type_arg)
- :Log_event(thd_arg, 0, 0), seed1(seed1_arg), seed2(seed2_arg)
- { cache_type= cache_type_arg; }
+ bool using_trans, bool direct)
+ :Log_event(thd_arg,0,using_trans),seed1(seed1_arg),seed2(seed2_arg)
+ {
+ if (direct)
+ cache_type= Log_event::EVENT_NO_CACHE;
+ }
#ifdef HAVE_REPLICATION
void pack_info(THD *thd, Protocol* protocol);
#endif /* HAVE_REPLICATION */
@@ -2552,13 +2628,13 @@ class Rand_log_event: public Log_event
~Rand_log_event() {}
Log_event_type get_type_code() { return RAND_EVENT;}
int get_data_size() { return 16; /* sizeof(ulonglong) * 2*/ }
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
bool write(IO_CACHE* file);
#endif
bool is_valid() const { return 1; }
private:
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_apply_event(Relay_log_info const *rli);
virtual int do_update_pos(Relay_log_info *rli);
virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
@@ -2582,9 +2658,13 @@ class Xid_log_event: public Log_event
public:
my_xid xid;
-#ifndef MYSQL_CLIENT
- Xid_log_event(THD* thd_arg, my_xid x): Log_event(thd_arg, 0, 0), xid(x)
- { cache_type= EVENT_NO_CACHE; }
+#ifdef MYSQL_SERVER
+ Xid_log_event(THD* thd_arg, my_xid x, bool direct):
+ Log_event(thd_arg, 0, TRUE), xid(x)
+ {
+ if (direct)
+ cache_type= Log_event::EVENT_NO_CACHE;
+ }
#ifdef HAVE_REPLICATION
void pack_info(THD *thd, Protocol* protocol);
#endif /* HAVE_REPLICATION */
@@ -2597,13 +2677,13 @@ class Xid_log_event: public Log_event
~Xid_log_event() {}
Log_event_type get_type_code() { return XID_EVENT;}
int get_data_size() { return sizeof(xid); }
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
bool write(IO_CACHE* file);
#endif
bool is_valid() const { return 1; }
private:
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_apply_event(Relay_log_info const *rli);
enum_skip_reason do_shall_skip(Relay_log_info *rli);
#endif
@@ -2621,6 +2701,10 @@ private:
class User_var_log_event: public Log_event
{
public:
+ enum {
+ UNDEF_F= 0,
+ UNSIGNED_F= 1
+ };
char *name;
uint name_len;
char *val;
@@ -2628,17 +2712,23 @@ public:
Item_result type;
uint charset_number;
bool is_null;
-#ifndef MYSQL_CLIENT
+ uchar flags;
+#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,
- uint charset_number_arg,
- uint16 cache_type_arg)
- :Log_event(thd_arg, 0, 0), name(name_arg), name_len(name_len_arg), val(val_arg),
- val_len(val_len_arg), type(type_arg), charset_number(charset_number_arg),
- deferred(false)
- { is_null= !val; cache_type= cache_type_arg; }
+ uint charset_number_arg, uchar flags_arg,
+ bool using_trans, bool direct)
+ :Log_event(thd_arg, 0, using_trans),
+ name(name_arg), name_len(name_len_arg), val(val_arg),
+ val_len(val_len_arg), type(type_arg), charset_number(charset_number_arg),
+ flags(flags_arg), deferred(false)
+ {
+ is_null= !val;
+ if (direct)
+ cache_type= Log_event::EVENT_NO_CACHE;
+ }
void pack_info(THD *thd, Protocol* protocol);
#else
void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
@@ -2648,7 +2738,7 @@ public:
const Format_description_log_event *description_event);
~User_var_log_event() {}
Log_event_type get_type_code() { return USER_VAR_EVENT;}
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
bool write(IO_CACHE* file);
/*
Getter and setter for deferred User-event.
@@ -2665,7 +2755,7 @@ public:
bool is_valid() const { return name != 0; }
private:
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_apply_event(Relay_log_info const *rli);
virtual int do_update_pos(Relay_log_info *rli);
virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
@@ -2684,7 +2774,7 @@ private:
class Stop_log_event: public Log_event
{
public:
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
Stop_log_event() :Log_event()
{}
#else
@@ -2700,7 +2790,7 @@ public:
bool is_valid() const { return 1; }
private:
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_update_pos(Relay_log_info *rli);
virtual enum_skip_reason do_shall_skip(Relay_log_info *rli)
{
@@ -2776,7 +2866,7 @@ public:
ulonglong pos;
uint ident_len;
uint flags;
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
Rotate_log_event(const char* new_log_ident_arg,
uint ident_len_arg,
ulonglong pos_arg, uint flags);
@@ -2792,17 +2882,17 @@ public:
~Rotate_log_event()
{
if (flags & DUP_NAME)
- my_free((uchar*) new_log_ident, MYF(MY_ALLOW_ZERO_PTR));
+ my_free((void*) new_log_ident);
}
Log_event_type get_type_code() { return ROTATE_EVENT;}
int get_data_size() { return ident_len + ROTATE_HEADER_LEN;}
bool is_valid() const { return new_log_ident != 0; }
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
bool write(IO_CACHE* file);
#endif
private:
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_update_pos(Relay_log_info *rli);
virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
#endif
@@ -2833,10 +2923,11 @@ public:
uint file_id;
bool inited_from_old;
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
Create_file_log_event(THD* thd, sql_exchange* ex, const char* db_arg,
const char* table_name_arg,
List<Item>& fields_arg,
+ bool is_concurrent_arg,
enum enum_duplicates handle_dup, bool ignore,
uchar* block_arg, uint block_len_arg,
bool using_trans);
@@ -2853,7 +2944,7 @@ public:
const Format_description_log_event* description_event);
~Create_file_log_event()
{
- my_free((char*) event_buf, MYF(MY_ALLOW_ZERO_PTR));
+ my_free((void*) event_buf);
}
Log_event_type get_type_code()
@@ -2867,7 +2958,7 @@ public:
4 + 1 + block_len);
}
bool is_valid() const { return inited_from_old || block != 0; }
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
bool write_data_header(IO_CACHE* file);
bool write_data_body(IO_CACHE* file);
/*
@@ -2878,7 +2969,7 @@ public:
#endif
private:
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_apply_event(Relay_log_info const *rli);
#endif
};
@@ -2909,7 +3000,7 @@ public:
*/
const char* db;
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
Append_block_log_event(THD* thd, const char* db_arg, uchar* block_arg,
uint block_len_arg, bool using_trans);
#ifdef HAVE_REPLICATION
@@ -2927,13 +3018,13 @@ public:
Log_event_type get_type_code() { return APPEND_BLOCK_EVENT;}
int get_data_size() { return block_len + APPEND_BLOCK_HEADER_LEN ;}
bool is_valid() const { return block != 0; }
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
bool write(IO_CACHE* file);
const char* get_db() { return db; }
#endif
private:
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_apply_event(Relay_log_info const *rli);
#endif
};
@@ -2951,7 +3042,7 @@ public:
uint file_id;
const char* db; /* see comment in Append_block_log_event */
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
Delete_file_log_event(THD* thd, const char* db_arg, bool using_trans);
#ifdef HAVE_REPLICATION
void pack_info(THD *thd, Protocol* protocol);
@@ -2968,13 +3059,13 @@ public:
Log_event_type get_type_code() { return DELETE_FILE_EVENT;}
int get_data_size() { return DELETE_FILE_HEADER_LEN ;}
bool is_valid() const { return file_id != 0; }
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
bool write(IO_CACHE* file);
const char* get_db() { return db; }
#endif
private:
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_apply_event(Relay_log_info const *rli);
#endif
};
@@ -2992,7 +3083,7 @@ public:
uint file_id;
const char* db; /* see comment in Append_block_log_event */
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
Execute_load_log_event(THD* thd, const char* db_arg, bool using_trans);
#ifdef HAVE_REPLICATION
void pack_info(THD *thd, Protocol* protocol);
@@ -3008,13 +3099,13 @@ public:
Log_event_type get_type_code() { return EXEC_LOAD_EVENT;}
int get_data_size() { return EXEC_LOAD_HEADER_LEN ;}
bool is_valid() const { return file_id != 0; }
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
bool write(IO_CACHE* file);
const char* get_db() { return db; }
#endif
private:
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_apply_event(Relay_log_info const *rli);
#endif
};
@@ -3032,7 +3123,7 @@ private:
class Begin_load_query_log_event: public Append_block_log_event
{
public:
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
Begin_load_query_log_event(THD* thd_arg, const char *db_arg,
uchar* block_arg, uint block_len_arg,
bool using_trans);
@@ -3047,7 +3138,7 @@ public:
~Begin_load_query_log_event() {}
Log_event_type get_type_code() { return BEGIN_LOAD_QUERY_EVENT; }
private:
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
#endif
};
@@ -3083,13 +3174,13 @@ public:
*/
enum_load_dup_handling dup_handling;
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
Execute_load_query_log_event(THD* thd, const char* query_arg,
ulong query_length, uint fn_pos_start_arg,
uint fn_pos_end_arg,
enum_load_dup_handling dup_handling_arg,
- bool using_trans, bool suppress_use,
- int errcode);
+ bool using_trans, bool direct,
+ bool suppress_use, int errcode);
#ifdef HAVE_REPLICATION
void pack_info(THD *thd, Protocol* protocol);
#endif /* HAVE_REPLICATION */
@@ -3108,12 +3199,12 @@ public:
bool is_valid() const { return Query_log_event::is_valid() && file_id != 0; }
ulong get_post_header_size_for_derived();
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
bool write_post_header_for_derived(IO_CACHE* file);
#endif
private:
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_apply_event(Relay_log_info const *rli);
#endif
};
@@ -3161,7 +3252,7 @@ class Annotate_rows_log_event: public Log_event
{
public:
#ifndef MYSQL_CLIENT
- Annotate_rows_log_event(THD*, uint16 cache_type_arg);
+ Annotate_rows_log_event(THD*, bool using_trans, bool direct);
#endif
Annotate_rows_log_event(const char *buf, uint event_len,
const Format_description_log_event*);
@@ -3556,7 +3647,7 @@ public:
flag_set get_flags(flag_set flag) const { return m_flags & flag; }
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
Table_map_log_event(THD *thd, TABLE *tbl, ulong tid, bool is_transactional);
#endif
#ifdef HAVE_REPLICATION
@@ -3572,25 +3663,25 @@ public:
return new table_def(m_coltype, m_colcnt, m_field_metadata,
m_field_metadata_size, m_null_bits, m_flags);
}
- ulong get_table_id() const { return m_table_id; }
- const char *get_table_name() const { return m_tblnam; }
- const char *get_db_name() const { return m_dbnam; }
int rewrite_db(const char* new_name, size_t new_name_len,
const Format_description_log_event*);
#endif
+ ulong get_table_id() const { return m_table_id; }
+ const char *get_table_name() const { return m_tblnam; }
+ const char *get_db_name() const { return m_dbnam; }
virtual Log_event_type get_type_code() { return TABLE_MAP_EVENT; }
virtual bool is_valid() const { return m_memory != NULL; /* we check malloc */ }
virtual int get_data_size() { return (uint) m_data_size; }
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
virtual int save_field_metadata();
virtual bool write_data_header(IO_CACHE *file);
virtual bool write_data_body(IO_CACHE *file);
virtual const char *get_db() { return m_dbnam; }
#endif
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual void pack_info(THD *thd, Protocol *protocol);
#endif
@@ -3600,13 +3691,13 @@ public:
private:
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_apply_event(Relay_log_info const *rli);
virtual int do_update_pos(Relay_log_info *rli);
virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
#endif
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
TABLE *m_table;
#endif
char const *m_dbnam;
@@ -3702,7 +3793,7 @@ public:
void clear_flags(flag_set flags_arg) { m_flags &= ~flags_arg; }
flag_set get_flags(flag_set flags_arg) const { return m_flags & flags_arg; }
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual void pack_info(THD *thd, Protocol *protocol);
#endif
@@ -3717,7 +3808,7 @@ public:
const uchar *ptr, const uchar *prefix);
#endif
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
int add_row_data(uchar *data, size_t length)
{
return do_add_row_data(data,length);
@@ -3731,7 +3822,7 @@ public:
size_t get_width() const { return m_width; }
ulong get_table_id() const { return m_table_id; }
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
virtual bool write_data_header(IO_CACHE *file);
virtual bool write_data_body(IO_CACHE *file);
virtual const char *get_db() { return m_table->s->db.str; }
@@ -3754,7 +3845,7 @@ protected:
The constructors are protected since you're supposed to inherit
this class, not create instances of this class.
*/
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
Rows_log_event(THD*, TABLE*, ulong table_id,
MY_BITMAP const *cols, bool is_transactional);
#endif
@@ -3766,11 +3857,11 @@ protected:
void print_helper(FILE *, PRINT_EVENT_INFO *, char const *const name);
#endif
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
virtual int do_add_row_data(uchar *data, size_t length);
#endif
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
TABLE *m_table; /* The table the rows belong to */
#endif
ulong m_table_id; /* Table ID */
@@ -3799,7 +3890,7 @@ protected:
/* helper functions */
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
const uchar *m_curr_row; /* Start of the row being processed */
const uchar *m_curr_row_end; /* One-after the end of the current row */
uchar *m_key; /* Buffer to keep key value during searches */
@@ -3816,19 +3907,28 @@ protected:
DBUG_ASSERT(m_table);
ASSERT_OR_RETURN_ERROR(m_curr_row < m_rows_end, HA_ERR_CORRUPT_EVENT);
- int const result= ::unpack_row(rli, m_table, m_width, m_curr_row,
- m_rows_end, &m_cols,
- &m_curr_row_end, &m_master_reclength);
- if (m_curr_row_end > m_rows_end)
- my_error(ER_SLAVE_CORRUPT_EVENT, MYF(0));
- ASSERT_OR_RETURN_ERROR(m_curr_row_end <= m_rows_end, HA_ERR_CORRUPT_EVENT);
- return result;
+ return ::unpack_row(rli, m_table, m_width, m_curr_row, &m_cols,
+ &m_curr_row_end, &m_master_reclength, m_rows_end);
+ }
+
+ /**
+ Helper function to check whether there is an auto increment
+ column on the table where the event is to be applied.
+
+ @return true if there is an autoincrement field on the extra
+ columns, false otherwise.
+ */
+ inline bool is_auto_inc_in_extra_columns()
+ {
+ DBUG_ASSERT(m_table);
+ return (m_table->next_number_field &&
+ m_table->next_number_field->field_index >= m_width);
}
#endif
private:
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_apply_event(Relay_log_info const *rli);
virtual int do_update_pos(Relay_log_info *rli);
virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
@@ -3883,7 +3983,7 @@ private:
*/
virtual int do_exec_row(const Relay_log_info *const rli) = 0;
-#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+#endif /* defined(MYSQL_SERVER) && defined(HAVE_REPLICATION) */
friend class Old_rows_log_event;
};
@@ -3906,7 +4006,7 @@ public:
TYPE_CODE = WRITE_ROWS_EVENT
};
-#if !defined(MYSQL_CLIENT)
+#if defined(MYSQL_SERVER)
Write_rows_log_event(THD*, TABLE*, ulong table_id,
MY_BITMAP const *cols, bool is_transactional);
#endif
@@ -3914,7 +4014,7 @@ public:
Write_rows_log_event(const char *buf, uint event_len,
const Format_description_log_event *description_event);
#endif
-#if !defined(MYSQL_CLIENT)
+#if defined(MYSQL_SERVER)
static bool binlog_row_logging_function(THD *thd, TABLE *table,
bool is_transactional,
MY_BITMAP *cols,
@@ -3935,7 +4035,7 @@ private:
void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
#endif
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_before_row_operations(const Slave_reporting_capability *const);
virtual int do_after_row_operations(const Slave_reporting_capability *const,int);
virtual int do_exec_row(const Relay_log_info *const);
@@ -3964,7 +4064,7 @@ public:
TYPE_CODE = UPDATE_ROWS_EVENT
};
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
Update_rows_log_event(THD*, TABLE*, ulong table_id,
MY_BITMAP const *cols_bi,
MY_BITMAP const *cols_ai,
@@ -3984,7 +4084,7 @@ public:
const Format_description_log_event *description_event);
#endif
-#if !defined(MYSQL_CLIENT)
+#ifdef MYSQL_SERVER
static bool binlog_row_logging_function(THD *thd, TABLE *table,
bool is_transactional,
MY_BITMAP *cols,
@@ -4009,11 +4109,11 @@ protected:
void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
#endif
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_before_row_operations(const Slave_reporting_capability *const);
virtual int do_after_row_operations(const Slave_reporting_capability *const,int);
virtual int do_exec_row(const Relay_log_info *const);
-#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+#endif /* defined(MYSQL_SERVER) && defined(HAVE_REPLICATION) */
};
/**
@@ -4045,7 +4145,7 @@ public:
TYPE_CODE = DELETE_ROWS_EVENT
};
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
Delete_rows_log_event(THD*, TABLE*, ulong,
MY_BITMAP const *cols, bool is_transactional);
#endif
@@ -4053,7 +4153,7 @@ public:
Delete_rows_log_event(const char *buf, uint event_len,
const Format_description_log_event *description_event);
#endif
-#if !defined(MYSQL_CLIENT)
+#ifdef MYSQL_SERVER
static bool binlog_row_logging_function(THD *thd, TABLE *table,
bool is_transactional,
MY_BITMAP *cols,
@@ -4074,7 +4174,7 @@ protected:
void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
#endif
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_before_row_operations(const Slave_reporting_capability *const);
virtual int do_after_row_operations(const Slave_reporting_capability *const,int);
virtual int do_exec_row(const Relay_log_info *const);
@@ -4122,7 +4222,7 @@ protected:
*/
class Incident_log_event : public Log_event {
public:
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
Incident_log_event(THD *thd_arg, Incident incident)
: Log_event(thd_arg, 0, FALSE), m_incident(incident)
{
@@ -4130,6 +4230,9 @@ public:
DBUG_PRINT("enter", ("m_incident: %d", m_incident));
m_message.str= NULL; /* Just as a precaution */
m_message.length= 0;
+ set_direct_logging();
+ /* Replicate the incident irregardless of @@skip_replication. */
+ flags&= ~LOG_EVENT_SKIP_REPLICATION_F;
DBUG_VOID_RETURN;
}
@@ -4139,11 +4242,14 @@ public:
DBUG_ENTER("Incident_log_event::Incident_log_event");
DBUG_PRINT("enter", ("m_incident: %d", m_incident));
m_message= msg;
+ set_direct_logging();
+ /* Replicate the incident irregardless of @@skip_replication. */
+ flags&= ~LOG_EVENT_SKIP_REPLICATION_F;
DBUG_VOID_RETURN;
}
#endif
-#ifndef MYSQL_CLIENT
+#ifdef MYSQL_SERVER
void pack_info(THD *thd, Protocol*);
#endif
@@ -4156,7 +4262,7 @@ public:
virtual void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
#endif
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_apply_event(Relay_log_info const *rli);
#endif
@@ -4188,6 +4294,53 @@ static inline bool copy_event_cache_to_file_and_reinit(IO_CACHE *cache,
reinit_io_cache(cache, WRITE_CACHE, 0, FALSE, TRUE);
}
+#ifdef MYSQL_SERVER
+/*****************************************************************************
+
+ Heartbeat Log Event class
+
+ Replication event to ensure to slave that master is alive.
+ The event is originated by master's dump thread and sent straight to
+ slave without being logged. Slave itself does not store it in relay log
+ but rather uses a data for immediate checks and throws away the event.
+
+ Two members of the class log_ident and Log_event::log_pos comprise
+ @see the event_coordinates instance. The coordinates that a heartbeat
+ instance carries correspond to the last event master has sent from
+ its binlog.
+
+ ****************************************************************************/
+class Heartbeat_log_event: public Log_event
+{
+public:
+ Heartbeat_log_event(const char* buf, uint event_len,
+ const Format_description_log_event* description_event);
+ Log_event_type get_type_code() { return HEARTBEAT_LOG_EVENT; }
+ bool is_valid() const
+ {
+ return (log_ident != NULL &&
+ log_pos >= BIN_LOG_HEADER_SIZE);
+ }
+ const char * get_log_ident() { return log_ident; }
+ uint get_ident_len() { return ident_len; }
+
+private:
+ const char* log_ident;
+ uint ident_len;
+};
+
+/**
+ The function is called by slave applier in case there are
+ active table filtering rules to force gathering events associated
+ with Query-log-event into an array to execute
+ them once the fate of the Query is determined for execution.
+*/
+bool slave_execute_deferred_events(THD *thd);
+#endif
+
+int append_query_string(THD *thd, CHARSET_INFO *csinfo,
+ String const *from, String *to);
+
bool rpl_get_position_info(const char **log_file_name, ulonglong *log_pos,
const char **group_relay_log_name,
ulonglong *relay_log_pos);
diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc
index f29a2ed9ad6..70b3ec12356 100644
--- a/sql/log_event_old.cc
+++ b/sql/log_event_old.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007, 2010, Oracle and/or its affiliates.
+/* Copyright (c) 2007, 2013, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,13 +13,24 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
+#include "sql_priv.h"
#ifndef MYSQL_CLIENT
+#include "unireg.h"
+#endif
+#include "my_global.h" // REQUIRED by log_event.h > m_string.h > my_bitmap.h
+#include "log_event.h"
+#ifndef MYSQL_CLIENT
+#include "sql_cache.h" // QUERY_CACHE_FLAGS_SIZE
+#include "sql_base.h" // close_tables_for_reopen
+#include "key.h" // key_copy
+#include "lock.h" // mysql_unlock_tables
+#include "sql_parse.h" // mysql_reset_thd_for_next_command
#include "rpl_rli.h"
#include "rpl_utility.h"
#endif
#include "log_event_old.h"
#include "rpl_record_old.h"
+#include "transaction.h"
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
@@ -46,8 +57,7 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
*/
DBUG_ASSERT(ev->get_flags(Old_rows_log_event::STMT_END_F));
- const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
- close_thread_tables(ev_thd);
+ const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(ev_thd);
ev_thd->clear_error();
DBUG_RETURN(0);
}
@@ -73,26 +83,23 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
We also call the mysql_reset_thd_for_next_command(), since this
is the logical start of the next "statement". Note that this
- call might reset the value of current_stmt_binlog_row_based, so
+ call might reset the value of current_stmt_binlog_format, so
we need to do any changes to that value after this function.
*/
lex_start(ev_thd);
- mysql_reset_thd_for_next_command(ev_thd, 0);
+ mysql_reset_thd_for_next_command(ev_thd);
/*
- Check if the slave is set to use SBR. If so, it should switch
- to using RBR until the end of the "statement", i.e., next
- STMT_END_F or next error.
+ This is a row injection, so we flag the "statement" as
+ such. Note that this code is called both when the slave does row
+ injections and when the BINLOG statement is used to do row
+ injections.
*/
- if (!ev_thd->current_stmt_binlog_row_based &&
- mysql_bin_log.is_open() && (ev_thd->options & OPTION_BIN_LOG))
- {
- ev_thd->set_current_stmt_binlog_row_based();
- }
+ ev_thd->lex->set_stmt_row_injection();
- if (simple_open_n_lock_tables(ev_thd, rli->tables_to_lock))
+ if (open_and_lock_tables(ev_thd, rli->tables_to_lock, FALSE, 0))
{
- uint actual_error= ev_thd->main_da.sql_errno();
+ uint actual_error= ev_thd->stmt_da->sql_errno();
if (ev_thd->is_slave_error || ev_thd->is_fatal_error)
{
/*
@@ -101,11 +108,11 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
*/
rli->report(ERROR_LEVEL, actual_error,
"Error '%s' on opening tables",
- (actual_error ? ev_thd->main_da.message() :
+ (actual_error ? ev_thd->stmt_da->message() :
"unexpected success or fatal error"));
ev_thd->is_slave_error= 1;
}
- const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
+ const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
DBUG_RETURN(actual_error);
}
@@ -119,16 +126,23 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
{
RPL_TABLE_LIST *ptr= rli->tables_to_lock;
- for ( ; ptr ; ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_global))
+ for (uint i= 0 ; ptr&& (i< rli->tables_to_lock_count);
+ ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_global), i++)
{
- if (ptr->m_tabledef.compatible_with(rli, ptr->table))
+ DBUG_ASSERT(ptr->m_tabledef_valid);
+ TABLE *conv_table;
+ if (!ptr->m_tabledef.compatible_with(thd, const_cast<Relay_log_info*>(rli),
+ ptr->table, &conv_table))
{
- mysql_unlock_tables(ev_thd, ev_thd->lock);
- ev_thd->lock= 0;
ev_thd->is_slave_error= 1;
- const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
+ const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(ev_thd);
DBUG_RETURN(Old_rows_log_event::ERR_BAD_TABLE_DEF);
}
+ DBUG_PRINT("debug", ("Table: %s.%s is compatible with master"
+ " - conv_table: %p",
+ ptr->table->s->db.str,
+ ptr->table->s->table_name.str, conv_table));
+ ptr->m_conv_table= conv_table;
}
}
@@ -146,10 +160,9 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
Old_rows_log_event, we can invalidate the query cache for the
associated table.
*/
- for (TABLE_LIST *ptr= rli->tables_to_lock ; ptr ; ptr= ptr->next_global)
- {
+ TABLE_LIST *ptr= rli->tables_to_lock;
+ for (uint i=0; ptr && (i < rli->tables_to_lock_count); ptr= ptr->next_global, i++)
const_cast<Relay_log_info*>(rli)->m_table_map.set_table(ptr->table_id, ptr->table);
- }
#ifdef HAVE_QUERY_CACHE
query_cache.invalidate_locked_for_write(thd, rli->tables_to_lock);
#endif
@@ -180,16 +193,16 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
the event.
*/
if (ev->get_flags(Old_rows_log_event::NO_FOREIGN_KEY_CHECKS_F))
- ev_thd->options|= OPTION_NO_FOREIGN_KEY_CHECKS;
+ ev_thd->variables.option_bits|= OPTION_NO_FOREIGN_KEY_CHECKS;
else
- ev_thd->options&= ~OPTION_NO_FOREIGN_KEY_CHECKS;
+ ev_thd->variables.option_bits&= ~OPTION_NO_FOREIGN_KEY_CHECKS;
if (ev->get_flags(Old_rows_log_event::RELAXED_UNIQUE_CHECKS_F))
- ev_thd->options|= OPTION_RELAXED_UNIQUE_CHECKS;
+ ev_thd->variables.option_bits|= OPTION_RELAXED_UNIQUE_CHECKS;
else
- ev_thd->options&= ~OPTION_RELAXED_UNIQUE_CHECKS;
+ ev_thd->variables.option_bits&= ~OPTION_RELAXED_UNIQUE_CHECKS;
/* A small test to verify that objects have consistent types */
- DBUG_ASSERT(sizeof(ev_thd->options) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS));
+ DBUG_ASSERT(sizeof(ev_thd->variables.option_bits) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS));
/*
Now we are in a statement and will stay in a statement until we
@@ -230,36 +243,29 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
break;
default:
- rli->report(ERROR_LEVEL, ev_thd->main_da.sql_errno(),
+ rli->report(ERROR_LEVEL, ev_thd->stmt_da->sql_errno(),
"Error in %s event: row application failed. %s",
ev->get_type_str(),
- ev_thd->is_error() ? ev_thd->main_da.message() : "");
- ev_thd->is_slave_error= 1;
+ ev_thd->is_error() ? ev_thd->stmt_da->message() : "");
+ thd->is_slave_error= 1;
break;
}
row_start= row_end;
}
- DBUG_EXECUTE_IF("STOP_SLAVE_after_first_Rows_event",
+ DBUG_EXECUTE_IF("stop_slave_middle_group",
const_cast<Relay_log_info*>(rli)->abort_slave= 1;);
error= do_after_row_operations(table, error);
}
- /*
- We need to delay this clear until the table def is no longer needed.
- The table def is needed in unpack_row().
- */
- if (rli->tables_to_lock && ev->get_flags(Old_rows_log_event::STMT_END_F))
- const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
-
if (error)
{ /* error has occured during the transaction */
- rli->report(ERROR_LEVEL, ev_thd->main_da.sql_errno(),
+ rli->report(ERROR_LEVEL, ev_thd->stmt_da->sql_errno(),
"Error in %s event: error during transaction execution "
"on table %s.%s. %s",
ev->get_type_str(), table->s->db.str,
table->s->table_name.str,
- ev_thd->is_error() ? ev_thd->main_da.message() : "");
+ ev_thd->is_error() ? ev_thd->stmt_da->message() : "");
/*
If one day we honour --skip-slave-errors in row-based replication, and
@@ -272,40 +278,12 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
thread is certainly going to stop.
rollback at the caller along with sbr.
*/
- ev_thd->reset_current_stmt_binlog_row_based();
+ ev_thd->reset_current_stmt_binlog_format_row();
const_cast<Relay_log_info*>(rli)->cleanup_context(ev_thd, error);
ev_thd->is_slave_error= 1;
DBUG_RETURN(error);
}
- /*
- This code would ideally be placed in do_update_pos() instead, but
- since we have no access to table there, we do the setting of
- last_event_start_time here instead.
- */
- if (table && (table->s->primary_key == MAX_KEY) &&
- !ev->cache_stmt &&
- ev->get_flags(Old_rows_log_event::STMT_END_F) == Old_rows_log_event::RLE_NO_FLAGS)
- {
- /*
- ------------ Temporary fix until WL#2975 is implemented ---------
-
- This event is not the last one (no STMT_END_F). If we stop now
- (in case of terminate_slave_thread()), how will we restart? We
- have to restart from Table_map_log_event, but as this table is
- not transactional, the rows already inserted will still be
- present, and idempotency is not guaranteed (no PK) so we risk
- that repeating leads to double insert. So we desperately try to
- continue, hope we'll eventually leave this buggy situation (by
- executing the final Old_rows_log_event). If we are in a hopeless
- wait (reached end of last relay log and nothing gets appended
- there), we timeout after one minute, and notify DBA about the
- problem. When WL#2975 is implemented, just remove the member
- st_relay_log_info::last_event_start_time and all its occurences.
- */
- const_cast<Relay_log_info*>(rli)->last_event_start_time= my_time(0);
- }
-
DBUG_RETURN(0);
}
#endif
@@ -747,7 +725,10 @@ 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)))
+ {
+ table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
+ }
/*
Don't print debug messages when running valgrind since they can
@@ -880,7 +861,7 @@ static int find_and_fetch_row(TABLE *table, uchar *key)
default:
table->file->print_error(error, MYF(0));
DBUG_PRINT("info", ("Record not found"));
- table->file->ha_rnd_end();
+ (void) table->file->ha_rnd_end();
DBUG_RETURN(error);
}
}
@@ -1060,7 +1041,7 @@ int Delete_rows_log_event_old::do_after_row_operations(TABLE *table, int error)
{
/*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/
table->file->ha_index_or_rnd_end();
- my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR)); // Free for multi_malloc
+ my_free(m_memory); // Free for multi_malloc
m_memory= NULL;
m_after_image= NULL;
m_key= NULL;
@@ -1160,7 +1141,7 @@ int Update_rows_log_event_old::do_after_row_operations(TABLE *table, int error)
{
/*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/
table->file->ha_index_or_rnd_end();
- my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(m_memory);
m_memory= NULL;
m_after_image= NULL;
m_key= NULL;
@@ -1286,9 +1267,9 @@ Old_rows_log_event::Old_rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
DBUG_ASSERT((tbl_arg && tbl_arg->s && tid != ~0UL) ||
(!tbl_arg && !cols && tid == ~0UL));
- if (thd_arg->options & OPTION_NO_FOREIGN_KEY_CHECKS)
+ if (thd_arg->variables.option_bits & OPTION_NO_FOREIGN_KEY_CHECKS)
set_flags(NO_FOREIGN_KEY_CHECKS_F);
- if (thd_arg->options & OPTION_RELAXED_UNIQUE_CHECKS)
+ if (thd_arg->variables.option_bits & OPTION_RELAXED_UNIQUE_CHECKS)
set_flags(RELAXED_UNIQUE_CHECKS_F);
/* if bitmap_init fails, caught in is_valid() */
if (likely(!bitmap_init(&m_cols,
@@ -1406,7 +1387,7 @@ Old_rows_log_event::~Old_rows_log_event()
if (m_cols.bitmap == m_bitbuf) // no my_malloc happened
m_cols.bitmap= 0; // so no my_free in bitmap_free
bitmap_free(&m_cols); // To pair with bitmap_init().
- my_free((uchar*)m_rows_buf, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(m_rows_buf);
}
@@ -1506,8 +1487,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
*/
DBUG_ASSERT(get_flags(STMT_END_F));
- const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
- close_thread_tables(thd);
+ const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
thd->clear_error();
DBUG_RETURN(0);
}
@@ -1527,8 +1507,6 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
*/
if (!thd->lock)
{
- bool need_reopen= 1; /* To execute the first lap of the loop below */
-
/*
lock_tables() reads the contents of thd->lex, so they must be
initialized. Contrary to in
@@ -1537,79 +1515,31 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
*/
lex_start(thd);
- while ((error= lock_tables(thd, rli->tables_to_lock,
- rli->tables_to_lock_count, &need_reopen)))
+ if ((error= lock_tables(thd, rli->tables_to_lock,
+ rli->tables_to_lock_count, 0)))
{
- if (!need_reopen)
+ if (thd->is_slave_error || thd->is_fatal_error)
{
- if (thd->is_slave_error || thd->is_fatal_error)
- {
- /*
- Error reporting borrowed from Query_log_event with many excessive
- simplifications (we don't honour --slave-skip-errors)
- */
- uint actual_error= thd->net.last_errno;
- rli->report(ERROR_LEVEL, actual_error,
- "Error '%s' in %s event: when locking tables",
- (actual_error ? thd->net.last_error :
- "unexpected success or fatal error"),
- get_type_str());
- thd->is_fatal_error= 1;
- }
- else
- {
- rli->report(ERROR_LEVEL, error,
- "Error in %s event: when locking tables",
- get_type_str());
- }
- const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
- DBUG_RETURN(error);
- }
-
- /*
- So we need to reopen the tables.
-
- We need to flush the pending RBR event, since it keeps a
- pointer to an open table.
-
- ALTERNATIVE SOLUTION (not implemented): Extract a pointer to
- the pending RBR event and reset the table pointer after the
- tables has been reopened.
-
- NOTE: For this new scheme there should be no pending event:
- need to add code to assert that is the case.
- */
- error= thd->binlog_flush_pending_rows_event(false);
- if (error)
- {
- rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
- ER(ER_SLAVE_FATAL_ERROR),
- "call to binlog_flush_pending_rows_event() failed");
- thd->is_slave_error= 1;
- DBUG_RETURN(error);
+ /*
+ Error reporting borrowed from Query_log_event with many excessive
+ simplifications (we don't honour --slave-skip-errors)
+ */
+ uint actual_error= thd->net.last_errno;
+ rli->report(ERROR_LEVEL, actual_error,
+ "Error '%s' in %s event: when locking tables",
+ (actual_error ? thd->net.last_error :
+ "unexpected success or fatal error"),
+ get_type_str());
+ thd->is_fatal_error= 1;
}
- TABLE_LIST *tables= rli->tables_to_lock;
- close_tables_for_reopen(thd, &tables);
-
- uint tables_count= rli->tables_to_lock_count;
- if ((error= open_tables(thd, &tables, &tables_count, 0)))
+ else
{
- if (thd->is_slave_error || thd->is_fatal_error)
- {
- /*
- Error reporting borrowed from Query_log_event with many excessive
- simplifications (we don't honour --slave-skip-errors)
- */
- uint actual_error= thd->net.last_errno;
- rli->report(ERROR_LEVEL, actual_error,
- "Error '%s' on reopening tables",
- (actual_error ? thd->net.last_error :
- "unexpected success or fatal error"));
- thd->is_slave_error= 1;
- }
- const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
- DBUG_RETURN(error);
+ rli->report(ERROR_LEVEL, error,
+ "Error in %s event: when locking tables",
+ get_type_str());
}
+ const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
+ DBUG_RETURN(error);
}
/*
@@ -1622,22 +1552,25 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
{
RPL_TABLE_LIST *ptr= rli->tables_to_lock;
- for ( ; ptr ; ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_global))
+ for (uint i= 0 ; ptr&& (i< rli->tables_to_lock_count);
+ ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_global), i++)
{
- if (ptr->m_tabledef.compatible_with(rli, ptr->table))
+ TABLE *conv_table;
+ if (ptr->m_tabledef.compatible_with(thd, const_cast<Relay_log_info*>(rli),
+ ptr->table, &conv_table))
{
- mysql_unlock_tables(thd, thd->lock);
- thd->lock= 0;
thd->is_slave_error= 1;
- const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
+ const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
DBUG_RETURN(ERR_BAD_TABLE_DEF);
}
+ ptr->m_conv_table= conv_table;
}
}
/*
- ... and then we add all the tables to the table map and remove
- them from tables to lock.
+ ... and then we add all the tables to the table map but keep
+ them in the tables to lock list.
+
We also invalidate the query cache for all the tables, since
they will now be changed.
@@ -1685,16 +1618,16 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
the event.
*/
if (get_flags(NO_FOREIGN_KEY_CHECKS_F))
- thd->options|= OPTION_NO_FOREIGN_KEY_CHECKS;
+ thd->variables.option_bits|= OPTION_NO_FOREIGN_KEY_CHECKS;
else
- thd->options&= ~OPTION_NO_FOREIGN_KEY_CHECKS;
+ thd->variables.option_bits&= ~OPTION_NO_FOREIGN_KEY_CHECKS;
if (get_flags(RELAXED_UNIQUE_CHECKS_F))
- thd->options|= OPTION_RELAXED_UNIQUE_CHECKS;
+ thd->variables.option_bits|= OPTION_RELAXED_UNIQUE_CHECKS;
else
- thd->options&= ~OPTION_RELAXED_UNIQUE_CHECKS;
+ thd->variables.option_bits&= ~OPTION_RELAXED_UNIQUE_CHECKS;
/* A small test to verify that objects have consistent types */
- DBUG_ASSERT(sizeof(thd->options) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS));
+ DBUG_ASSERT(sizeof(thd->variables.option_bits) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS));
/*
Now we are in a statement and will stay in a statement until we
@@ -1790,18 +1723,11 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
} // row processing loop
- DBUG_EXECUTE_IF("STOP_SLAVE_after_first_Rows_event",
+ DBUG_EXECUTE_IF("stop_slave_middle_group",
const_cast<Relay_log_info*>(rli)->abort_slave= 1;);
error= do_after_row_operations(rli, error);
} // if (table)
- /*
- We need to delay this clear until here bacause unpack_current_row() uses
- master-side table definitions stored in rli.
- */
- if (rli->tables_to_lock && get_flags(STMT_END_F))
- const_cast<Relay_log_info*>(rli)->clear_tables_to_lock();
-
if (error)
{ /* error has occured during the transaction */
rli->report(ERROR_LEVEL, thd->net.last_errno,
@@ -1822,7 +1748,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
thread is certainly going to stop.
rollback at the caller along with sbr.
*/
- thd->reset_current_stmt_binlog_row_based();
+ thd->reset_current_stmt_binlog_format_row();
const_cast<Relay_log_info*>(rli)->cleanup_context(thd, error);
thd->is_slave_error= 1;
DBUG_RETURN(error);
@@ -1834,7 +1760,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
last_event_start_time here instead.
*/
if (table && (table->s->primary_key == MAX_KEY) &&
- !cache_stmt && get_flags(STMT_END_F) == RLE_NO_FLAGS)
+ !use_trans_cache() && get_flags(STMT_END_F) == RLE_NO_FLAGS)
{
/*
------------ Temporary fix until WL#2975 is implemented ---------
@@ -1872,7 +1798,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
(assume the last master's transaction is ignored by the slave because of
replicate-ignore rules).
*/
- int binlog_error= thd->binlog_flush_pending_rows_event(true);
+ int binlog_error= thd->binlog_flush_pending_rows_event(TRUE);
/*
If this event is not in a transaction, the call below will, if some
@@ -1882,8 +1808,11 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
Xid_log_event will come next which will, if some transactional engines
are involved, commit the transaction and flush the pending event to the
binlog.
+ If there was a deadlock the transaction should have been rolled back
+ already. So there should be no need to rollback the transaction.
*/
- if ((error= ha_autocommit_or_rollback(thd, binlog_error)))
+ DBUG_ASSERT(! thd->transaction_rollback_request);
+ if ((error= (binlog_error ? trans_rollback_stmt(thd) : trans_commit_stmt(thd))))
rli->report(ERROR_LEVEL, error,
"Error in %s event: commit of row events failed, "
"table `%s`.`%s`",
@@ -1901,7 +1830,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
event flushed.
*/
- thd->reset_current_stmt_binlog_row_based();
+ thd->reset_current_stmt_binlog_format_row();
const_cast<Relay_log_info*>(rli)->cleanup_context(thd, 0);
}
@@ -2301,7 +2230,11 @@ Old_rows_log_event::write_row(const Relay_log_info *const rli,
@note If the engine allows random access of the records, a combination of
@c position() and @c rnd_pos() will be used.
- */
+
+ Note that one MUST call ha_index_or_rnd_end() after this function if
+ it returns 0 as we must leave the row position in the handler intact
+ for any following update/delete command.
+*/
int Old_rows_log_event::find_row(const Relay_log_info *rli)
{
@@ -2444,15 +2377,14 @@ int Old_rows_log_event::find_row(const Relay_log_info *rli)
/* Unique does not have non nullable part */
if (!(table->key_info->flags & (HA_NULL_PART_KEY)))
{
- table->file->ha_index_end();
DBUG_RETURN(0);
}
else
{
KEY *keyinfo= table->key_info;
/*
- Unique has nullable part. We need to check if there is any field in the
- BI image that is null and part of UNNI.
+ Unique has nullable part. We need to check if there is any
+ field in the BI image that is null and part of UNNI.
*/
bool null_found= FALSE;
for (uint i=0; i < keyinfo->key_parts && !null_found; i++)
@@ -2464,7 +2396,6 @@ int Old_rows_log_event::find_row(const Relay_log_info *rli)
if (!null_found)
{
- table->file->ha_index_end();
DBUG_RETURN(0);
}
@@ -2503,15 +2434,10 @@ int Old_rows_log_event::find_row(const Relay_log_info *rli)
continue;
DBUG_PRINT("info",("no record matching the given row found"));
table->file->print_error(error, MYF(0));
- table->file->ha_index_end();
+ (void) table->file->ha_index_end();
DBUG_RETURN(error);
}
}
-
- /*
- Have to restart the scan to be able to fetch the next row.
- */
- table->file->ha_index_end();
}
else
{
@@ -2545,8 +2471,10 @@ int Old_rows_log_event::find_row(const Relay_log_info *rli)
if (++restart_count < 2)
{
int error2;
+ table->file->ha_rnd_end();
if ((error2= table->file->ha_rnd_init_with_error(1)))
DBUG_RETURN(error2);
+ goto restart_rnd_next;
}
break;
@@ -2572,7 +2500,8 @@ int Old_rows_log_event::find_row(const Relay_log_info *rli)
DBUG_PRINT("info", ("Record not found"));
else
DBUG_DUMP("record found", table->record[0], table->s->reclength);
- table->file->ha_rnd_end();
+ if (error)
+ table->file->ha_rnd_end();
DBUG_ASSERT(error == HA_ERR_END_OF_FILE || error == 0);
DBUG_RETURN(error);
@@ -2803,7 +2732,7 @@ Delete_rows_log_event_old::do_after_row_operations(const Slave_reporting_capabil
{
/*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, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(m_key);
m_key= NULL;
return error;
@@ -2821,6 +2750,7 @@ int Delete_rows_log_event_old::do_exec_row(const Relay_log_info *const rli)
Delete the record found, located in record[0]
*/
error= m_table->file->ha_delete_row(m_table->record[0]);
+ m_table->file->ha_index_or_rnd_end();
}
return error;
}
@@ -2902,7 +2832,7 @@ Update_rows_log_event_old::do_after_row_operations(const Slave_reporting_capabil
{
/*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, MYF(MY_ALLOW_ZERO_PTR)); // Free for multi_malloc
+ my_free(m_key); // Free for multi_malloc
m_key= NULL;
return error;
@@ -2957,6 +2887,8 @@ Update_rows_log_event_old::do_exec_row(const Relay_log_info *const rli)
#endif
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)
error= 0;
diff --git a/sql/log_event_old.h b/sql/log_event_old.h
index c28d974c6f2..d9d9f25737b 100644
--- a/sql/log_event_old.h
+++ b/sql/log_event_old.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007 MySQL AB
+/* Copyright (c) 2007, 2013, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -203,11 +203,8 @@ protected:
{
DBUG_ASSERT(m_table);
ASSERT_OR_RETURN_ERROR(m_curr_row < m_rows_end, HA_ERR_CORRUPT_EVENT);
- int const result= ::unpack_row(rli, m_table, m_width, m_curr_row,
- m_rows_end, &m_cols,
- &m_curr_row_end, &m_master_reclength);
- ASSERT_OR_RETURN_ERROR(m_curr_row_end <= m_rows_end, HA_ERR_CORRUPT_EVENT);
- return result;
+ return ::unpack_row(rli, m_table, m_width, m_curr_row, &m_cols,
+ &m_curr_row_end, &m_master_reclength, m_rows_end);
}
#endif
diff --git a/sql/log_slow.h b/sql/log_slow.h
index 5559c002fde..92a2d1bf4f6 100644
--- a/sql/log_slow.h
+++ b/sql/log_slow.h
@@ -19,36 +19,7 @@
#define LOG_SLOW_VERBOSITY_INNODB 1 << 0
#define LOG_SLOW_VERBOSITY_QUERY_PLAN 1 << 1
-#ifdef DEFINE_VARIABLES_LOG_SLOW
-
-/* Names here must be in same order as the bit's above */
-static const char *log_slow_verbosity_names[]=
-{
- "innodb","query_plan",
- NullS
-};
-
-static const unsigned int log_slow_verbosity_names_len[]=
-{
- sizeof("innodb") -1,
- sizeof("query_plan")-1
-};
-
-TYPELIB log_slow_verbosity_typelib=
-{ array_elements(log_slow_verbosity_names)-1,"", log_slow_verbosity_names,
- (unsigned int *) log_slow_verbosity_names_len };
-
-#else
-extern TYPELIB log_slow_verbosity_typelib;
-#endif /* DEFINE_VARIABLES_LOG_SLOW */
-
-/* Defines for what kind of query plan was used and what to log */
-
-/*
- We init the used query plan with a bit that is alwyas set and all 'no' bits
- to enable easy testing of what to log in sql_log.cc
-*/
-#define QPLAN_INIT (QPLAN_ALWAYS_SET | QPLAN_QC_NO)
+#define QPLAN_INIT QPLAN_QC_NO
#define QPLAN_ADMIN 1 << 0
#define QPLAN_FILESORT 1 << 1
@@ -61,47 +32,4 @@ extern TYPELIB log_slow_verbosity_typelib;
#define QPLAN_TMP_TABLE 1 << 8
/* ... */
#define QPLAN_MAX ((ulong) 1) << 31 /* reserved as placeholder */
-#define QPLAN_ALWAYS_SET QPLAN_MAX
-#define QPLAN_VISIBLE_MASK (~(QPLAN_ALWAYS_SET))
-
-#ifdef DEFINE_VARIABLES_LOG_SLOW
-/* Names here must be in same order as the bit's above */
-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",
- NullS
-};
-
-static const unsigned int log_slow_filter_names_len[]=
-{
- sizeof("admin")-1,
- sizeof("filesort")-1,
- sizeof("filesort_on_disk")-1,
- sizeof("full_join")-1,
- sizeof("full_scan")-1,
- sizeof("query_cache")-1,
- sizeof("query_cache_miss")-1,
- sizeof("tmp_table")-1,
- sizeof("tmp_table_on_disk")-1
-};
-
-TYPELIB log_slow_filter_typelib=
-{ array_elements(log_slow_filter_names)-1,"", log_slow_filter_names,
- (unsigned int *) log_slow_filter_names_len };
-
-#else
-extern TYPELIB log_slow_filter_typelib;
-#endif /* DEFINE_VARIABLES_LOG_SLOW */
-static inline ulong fix_log_slow_filter(ulong org_filter)
-{
- return org_filter ? org_filter : QPLAN_ALWAYS_SET;
-}
diff --git a/sql/main.cc b/sql/main.cc
new file mode 100644
index 00000000000..10141c132a6
--- /dev/null
+++ b/sql/main.cc
@@ -0,0 +1,26 @@
+/* Copyright (c) 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 */
+
+/*
+ main() for mysqld.
+ Calls mysqld_main() entry point exported by sql library.
+*/
+extern int mysqld_main(int argc, char **argv);
+
+int main(int argc, char **argv)
+{
+ return mysqld_main(argc, argv);
+}
diff --git a/sql/mdl.cc b/sql/mdl.cc
new file mode 100644
index 00000000000..415d56887d5
--- /dev/null
+++ b/sql/mdl.cc
@@ -0,0 +1,2919 @@
+/* Copyright (c) 2007, 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-1301 USA */
+
+
+#include "sql_class.h"
+#include "debug_sync.h"
+#include <hash.h>
+#include <mysqld_error.h>
+#include <mysql/plugin.h>
+#include <mysql/service_thd_wait.h>
+
+#ifdef HAVE_PSI_INTERFACE
+static PSI_mutex_key key_MDL_map_mutex;
+static PSI_mutex_key key_MDL_wait_LOCK_wait_status;
+
+static PSI_mutex_info all_mdl_mutexes[]=
+{
+ { &key_MDL_map_mutex, "MDL_map::mutex", PSI_FLAG_GLOBAL},
+ { &key_MDL_wait_LOCK_wait_status, "MDL_wait::LOCK_wait_status", 0}
+};
+
+static PSI_rwlock_key key_MDL_lock_rwlock;
+static PSI_rwlock_key key_MDL_context_LOCK_waiting_for;
+
+static PSI_rwlock_info all_mdl_rwlocks[]=
+{
+ { &key_MDL_lock_rwlock, "MDL_lock::rwlock", 0},
+ { &key_MDL_context_LOCK_waiting_for, "MDL_context::LOCK_waiting_for", 0}
+};
+
+static PSI_cond_key key_MDL_wait_COND_wait_status;
+
+static PSI_cond_info all_mdl_conds[]=
+{
+ { &key_MDL_wait_COND_wait_status, "MDL_context::COND_wait_status", 0}
+};
+
+/**
+ Initialise all the performance schema instrumentation points
+ used by the MDL subsystem.
+*/
+static void init_mdl_psi_keys(void)
+{
+ const char *category= "sql";
+ int count;
+
+ if (PSI_server == NULL)
+ return;
+
+ count= array_elements(all_mdl_mutexes);
+ PSI_server->register_mutex(category, all_mdl_mutexes, count);
+
+ count= array_elements(all_mdl_rwlocks);
+ PSI_server->register_rwlock(category, all_mdl_rwlocks, count);
+
+ count= array_elements(all_mdl_conds);
+ PSI_server->register_cond(category, all_mdl_conds, count);
+}
+#endif /* HAVE_PSI_INTERFACE */
+
+
+/**
+ Thread state names to be used in case when we have to wait on resource
+ belonging to certain namespace.
+*/
+
+const char *MDL_key::m_namespace_to_wait_state_name[NAMESPACE_END]=
+{
+ "Waiting for global read lock",
+ "Waiting for schema metadata lock",
+ "Waiting for table metadata lock",
+ "Waiting for stored function metadata lock",
+ "Waiting for stored procedure metadata lock",
+ "Waiting for trigger metadata lock",
+ "Waiting for event metadata lock",
+ "Waiting for commit lock"
+};
+
+static bool mdl_initialized= 0;
+
+
+class MDL_object_lock;
+class MDL_object_lock_cache_adapter;
+
+
+/**
+ A collection of all MDL locks. A singleton,
+ there is only one instance of the map in the server.
+ Maps MDL_key to MDL_lock instances.
+*/
+
+class MDL_map
+{
+public:
+ void init();
+ void destroy();
+ MDL_lock *find_or_insert(const MDL_key *key);
+ void remove(MDL_lock *lock);
+private:
+ bool move_from_hash_to_lock_mutex(MDL_lock *lock);
+private:
+ /** All acquired locks in the server. */
+ HASH m_locks;
+ /* Protects access to m_locks hash. */
+ mysql_mutex_t m_mutex;
+ /**
+ Cache of (unused) MDL_lock objects available for re-use.
+
+ On some systems (e.g. Windows XP) constructing/destructing
+ MDL_lock objects can be fairly expensive. We use this cache
+ to avoid these costs in scenarios in which they can have
+ significant negative effect on performance. For example, when
+ there is only one thread constantly executing statements in
+ auto-commit mode and thus constantly causing creation/
+ destruction of MDL_lock objects for the tables it uses.
+
+ Note that this cache contains only MDL_object_lock objects.
+
+ Protected by m_mutex mutex.
+ */
+ typedef I_P_List<MDL_object_lock, MDL_object_lock_cache_adapter,
+ I_P_List_counter>
+ Lock_cache;
+ Lock_cache m_unused_locks_cache;
+ /** Pre-allocated MDL_lock object for GLOBAL namespace. */
+ MDL_lock *m_global_lock;
+ /** Pre-allocated MDL_lock object for COMMIT namespace. */
+ MDL_lock *m_commit_lock;
+};
+
+
+/**
+ A context of the recursive traversal through all contexts
+ in all sessions in search for deadlock.
+*/
+
+class Deadlock_detection_visitor: public MDL_wait_for_graph_visitor
+{
+public:
+ Deadlock_detection_visitor(MDL_context *start_node_arg)
+ : m_start_node(start_node_arg),
+ m_victim(NULL),
+ m_current_search_depth(0),
+ m_found_deadlock(FALSE)
+ {}
+ virtual bool enter_node(MDL_context *node);
+ virtual void leave_node(MDL_context *node);
+
+ virtual bool inspect_edge(MDL_context *dest);
+
+ MDL_context *get_victim() const { return m_victim; }
+private:
+ /**
+ Change the deadlock victim to a new one if it has lower deadlock
+ weight.
+ */
+ void opt_change_victim_to(MDL_context *new_victim);
+private:
+ /**
+ The context which has initiated the search. There
+ can be multiple searches happening in parallel at the same time.
+ */
+ MDL_context *m_start_node;
+ /** If a deadlock is found, the context that identifies the victim. */
+ MDL_context *m_victim;
+ /** Set to the 0 at start. Increased whenever
+ we descend into another MDL context (aka traverse to the next
+ wait-for graph node). When MAX_SEARCH_DEPTH is reached, we
+ assume that a deadlock is found, even if we have not found a
+ loop.
+ */
+ uint m_current_search_depth;
+ /** TRUE if we found a deadlock. */
+ bool m_found_deadlock;
+ /**
+ Maximum depth for deadlock searches. After this depth is
+ achieved we will unconditionally declare that there is a
+ deadlock.
+
+ @note This depth should be small enough to avoid stack
+ being exhausted by recursive search algorithm.
+
+ TODO: Find out what is the optimal value for this parameter.
+ Current value is safe, but probably sub-optimal,
+ as there is an anecdotal evidence that real-life
+ deadlocks are even shorter typically.
+ */
+ static const uint MAX_SEARCH_DEPTH= 32;
+};
+
+
+/**
+ Enter a node of a wait-for graph. After
+ a node is entered, inspect_edge() will be called
+ for all wait-for destinations of this node. Then
+ leave_node() will be called.
+ We call "enter_node()" for all nodes we inspect,
+ including the starting node.
+
+ @retval TRUE Maximum search depth exceeded.
+ @retval FALSE OK.
+*/
+
+bool Deadlock_detection_visitor::enter_node(MDL_context *node)
+{
+ m_found_deadlock= ++m_current_search_depth >= MAX_SEARCH_DEPTH;
+ if (m_found_deadlock)
+ {
+ DBUG_ASSERT(! m_victim);
+ opt_change_victim_to(node);
+ }
+ return m_found_deadlock;
+}
+
+
+/**
+ Done inspecting this node. Decrease the search
+ depth. If a deadlock is found, and we are
+ backtracking to the start node, optionally
+ change the deadlock victim to one with lower
+ deadlock weight.
+*/
+
+void Deadlock_detection_visitor::leave_node(MDL_context *node)
+{
+ --m_current_search_depth;
+ if (m_found_deadlock)
+ opt_change_victim_to(node);
+}
+
+
+/**
+ Inspect a wait-for graph edge from one MDL context to another.
+
+ @retval TRUE A loop is found.
+ @retval FALSE No loop is found.
+*/
+
+bool Deadlock_detection_visitor::inspect_edge(MDL_context *node)
+{
+ m_found_deadlock= node == m_start_node;
+ return m_found_deadlock;
+}
+
+
+/**
+ Change the deadlock victim to a new one if it has lower deadlock
+ weight.
+
+ @retval new_victim Victim is not changed.
+ @retval !new_victim New victim became the current.
+*/
+
+void
+Deadlock_detection_visitor::opt_change_victim_to(MDL_context *new_victim)
+{
+ if (m_victim == NULL ||
+ m_victim->get_deadlock_weight() >= new_victim->get_deadlock_weight())
+ {
+ /* Swap victims, unlock the old one. */
+ MDL_context *tmp= m_victim;
+ m_victim= new_victim;
+ m_victim->lock_deadlock_victim();
+ if (tmp)
+ tmp->unlock_deadlock_victim();
+ }
+}
+
+
+/**
+ Get a bit corresponding to enum_mdl_type value in a granted/waiting bitmaps
+ and compatibility matrices.
+*/
+
+#define MDL_BIT(A) static_cast<MDL_lock::bitmap_t>(1U << A)
+
+/**
+ The lock context. Created internally for an acquired lock.
+ For a given name, there exists only one MDL_lock instance,
+ and it exists only when the lock has been granted.
+ Can be seen as an MDL subsystem's version of TABLE_SHARE.
+
+ This is an abstract class which lacks information about
+ compatibility rules for lock types. They should be specified
+ in its descendants.
+*/
+
+class MDL_lock
+{
+public:
+ typedef uchar bitmap_t;
+
+ class Ticket_list
+ {
+ public:
+ typedef I_P_List<MDL_ticket,
+ I_P_List_adapter<MDL_ticket,
+ &MDL_ticket::next_in_lock,
+ &MDL_ticket::prev_in_lock>,
+ I_P_List_null_counter,
+ I_P_List_fast_push_back<MDL_ticket> >
+ List;
+ operator const List &() const { return m_list; }
+ Ticket_list() :m_bitmap(0) {}
+
+ void add_ticket(MDL_ticket *ticket);
+ void remove_ticket(MDL_ticket *ticket);
+ bool is_empty() const { return m_list.is_empty(); }
+ bitmap_t bitmap() const { return m_bitmap; }
+ private:
+ void clear_bit_if_not_in_list(enum_mdl_type type);
+ private:
+ /** List of tickets. */
+ List m_list;
+ /** Bitmap of types of tickets in this list. */
+ bitmap_t m_bitmap;
+ };
+
+ typedef Ticket_list::List::Iterator Ticket_iterator;
+
+public:
+ /** The key of the object (data) being protected. */
+ MDL_key key;
+ /**
+ Read-write lock protecting this lock context.
+
+ @note The fact that we use read-write lock prefers readers here is
+ important as deadlock detector won't work correctly otherwise.
+
+ For example, imagine that we have following waiters graph:
+
+ ctxA -> obj1 -> ctxB -> obj1 -|
+ ^ |
+ |----------------------------|
+
+ and both ctxA and ctxB start deadlock detection process:
+
+ ctxA read-locks obj1 ctxB read-locks obj2
+ ctxA goes deeper ctxB goes deeper
+
+ Now ctxC comes in who wants to start waiting on obj1, also
+ ctxD comes in who wants to start waiting on obj2.
+
+ ctxC tries to write-lock obj1 ctxD tries to write-lock obj2
+ ctxC is blocked ctxD is blocked
+
+ Now ctxA and ctxB resume their search:
+
+ ctxA tries to read-lock obj2 ctxB tries to read-lock obj1
+
+ If m_rwlock prefers writes (or fair) both ctxA and ctxB would be
+ blocked because of pending write locks from ctxD and ctxC
+ correspondingly. Thus we will get a deadlock in deadlock detector.
+ If m_wrlock prefers readers (actually ignoring pending writers is
+ enough) ctxA and ctxB will continue and no deadlock will occur.
+ */
+ mysql_prlock_t m_rwlock;
+
+ bool is_empty() const
+ {
+ return (m_granted.is_empty() && m_waiting.is_empty());
+ }
+
+ virtual const bitmap_t *incompatible_granted_types_bitmap() const = 0;
+ virtual const bitmap_t *incompatible_waiting_types_bitmap() const = 0;
+
+ bool has_pending_conflicting_lock(enum_mdl_type type);
+
+ bool can_grant_lock(enum_mdl_type type, MDL_context *requstor_ctx,
+ bool ignore_lock_priority) const;
+
+ inline static MDL_lock *create(const MDL_key *key);
+
+ void reschedule_waiters();
+
+ void remove_ticket(Ticket_list MDL_lock::*queue, MDL_ticket *ticket);
+
+ bool visit_subgraph(MDL_ticket *waiting_ticket,
+ MDL_wait_for_graph_visitor *gvisitor);
+
+ virtual bool needs_notification(const MDL_ticket *ticket) const = 0;
+ virtual void notify_conflicting_locks(MDL_context *ctx) = 0;
+
+ virtual bitmap_t hog_lock_types_bitmap() const = 0;
+
+ /** List of granted tickets for this lock. */
+ Ticket_list m_granted;
+ /** Tickets for contexts waiting to acquire a lock. */
+ Ticket_list m_waiting;
+
+ /**
+ Number of times high priority lock requests have been granted while
+ low priority lock requests were waiting.
+ */
+ ulong m_hog_lock_count;
+
+public:
+
+ MDL_lock(const MDL_key *key_arg)
+ : key(key_arg),
+ m_hog_lock_count(0),
+ m_ref_usage(0),
+ m_ref_release(0),
+ m_is_destroyed(FALSE),
+ m_version(0)
+ {
+ mysql_prlock_init(key_MDL_lock_rwlock, &m_rwlock);
+ }
+
+ virtual ~MDL_lock()
+ {
+ mysql_prlock_destroy(&m_rwlock);
+ }
+ inline static void destroy(MDL_lock *lock);
+public:
+ /**
+ These three members are used to make it possible to separate
+ the mdl_locks.m_mutex mutex and MDL_lock::m_rwlock in
+ MDL_map::find_or_insert() for increased scalability.
+ The 'm_is_destroyed' member is only set by destroyers that
+ have both the mdl_locks.m_mutex and MDL_lock::m_rwlock, thus
+ holding any of the mutexes is sufficient to read it.
+ The 'm_ref_usage; is incremented under protection by
+ mdl_locks.m_mutex, but when 'm_is_destroyed' is set to TRUE, this
+ member is moved to be protected by the MDL_lock::m_rwlock.
+ This means that the MDL_map::find_or_insert() which only
+ holds the MDL_lock::m_rwlock can compare it to 'm_ref_release'
+ without acquiring mdl_locks.m_mutex again and if equal it can also
+ destroy the lock object safely.
+ The 'm_ref_release' is incremented under protection by
+ MDL_lock::m_rwlock.
+ Note since we are only interested in equality of these two
+ counters we don't have to worry about overflows as long as
+ their size is big enough to hold maximum number of concurrent
+ threads on the system.
+ */
+ uint m_ref_usage;
+ uint m_ref_release;
+ bool m_is_destroyed;
+ /**
+ We use the same idea and an additional version counter to support
+ caching of unused MDL_lock object for further re-use.
+ This counter is incremented while holding both MDL_map::m_mutex and
+ MDL_lock::m_rwlock locks each time when a MDL_lock is moved from
+ the hash to the unused objects list (or destroyed).
+ A thread, which has found a MDL_lock object for the key in the hash
+ and then released the MDL_map::m_mutex before acquiring the
+ MDL_lock::m_rwlock, can determine that this object was moved to the
+ unused objects list (or destroyed) while it held no locks by comparing
+ the version value which it read while holding the MDL_map::m_mutex
+ with the value read after acquiring the MDL_lock::m_rwlock.
+ Note that since it takes several years to overflow this counter such
+ theoretically possible overflows should not have any practical effects.
+ */
+ ulonglong m_version;
+};
+
+
+/**
+ An implementation of the scoped metadata lock. The only locking modes
+ which are supported at the moment are SHARED and INTENTION EXCLUSIVE
+ and EXCLUSIVE
+*/
+
+class MDL_scoped_lock : public MDL_lock
+{
+public:
+ MDL_scoped_lock(const MDL_key *key_arg)
+ : MDL_lock(key_arg)
+ { }
+
+ virtual const bitmap_t *incompatible_granted_types_bitmap() const
+ {
+ return m_granted_incompatible;
+ }
+ virtual const bitmap_t *incompatible_waiting_types_bitmap() const
+ {
+ return m_waiting_incompatible;
+ }
+ virtual bool needs_notification(const MDL_ticket *ticket) const
+ {
+ return (ticket->get_type() == MDL_SHARED);
+ }
+ virtual void notify_conflicting_locks(MDL_context *ctx);
+
+ /*
+ In scoped locks, only IX lock request would starve because of X/S. But that
+ is practically very rare case. So just return 0 from this function.
+ */
+ virtual bitmap_t hog_lock_types_bitmap() const
+ {
+ return 0;
+ }
+
+private:
+ static const bitmap_t m_granted_incompatible[MDL_TYPE_END];
+ static const bitmap_t m_waiting_incompatible[MDL_TYPE_END];
+};
+
+
+/**
+ An implementation of a per-object lock. Supports SHARED, SHARED_UPGRADABLE,
+ SHARED HIGH PRIORITY and EXCLUSIVE locks.
+*/
+
+class MDL_object_lock : public MDL_lock
+{
+public:
+ MDL_object_lock(const MDL_key *key_arg)
+ : MDL_lock(key_arg)
+ { }
+
+ /**
+ Reset unused MDL_object_lock object to represent the lock context for a
+ different object.
+ */
+ void reset(const MDL_key *new_key)
+ {
+ /* We need to change only object's key. */
+ key.mdl_key_init(new_key);
+ /* m_granted and m_waiting should be already in the empty/initial state. */
+ DBUG_ASSERT(is_empty());
+ /* Object should not be marked as destroyed. */
+ DBUG_ASSERT(! m_is_destroyed);
+ /*
+ Values of the rest of the fields should be preserved between old and
+ new versions of the object. E.g., m_version and m_ref_usage/release
+ should be kept intact to properly handle possible remaining references
+ to the old version of the object.
+ */
+ }
+
+ virtual const bitmap_t *incompatible_granted_types_bitmap() const
+ {
+ return m_granted_incompatible;
+ }
+ virtual const bitmap_t *incompatible_waiting_types_bitmap() const
+ {
+ return m_waiting_incompatible;
+ }
+ virtual bool needs_notification(const MDL_ticket *ticket) const
+ {
+ return ticket->is_upgradable_or_exclusive();
+ }
+ virtual void notify_conflicting_locks(MDL_context *ctx);
+
+ /*
+ To prevent starvation, these lock types that are only granted
+ max_write_lock_count times in a row while other lock types are
+ waiting.
+ */
+ virtual bitmap_t hog_lock_types_bitmap() const
+ {
+ return (MDL_BIT(MDL_SHARED_NO_WRITE) |
+ MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
+ MDL_BIT(MDL_EXCLUSIVE));
+ }
+
+private:
+ static const bitmap_t m_granted_incompatible[MDL_TYPE_END];
+ static const bitmap_t m_waiting_incompatible[MDL_TYPE_END];
+
+public:
+ /** Members for linking the object into the list of unused objects. */
+ MDL_object_lock *next_in_cache, **prev_in_cache;
+};
+
+
+/**
+ Helper class for linking MDL_object_lock objects into the unused objects list.
+*/
+class MDL_object_lock_cache_adapter :
+ public I_P_List_adapter<MDL_object_lock, &MDL_object_lock::next_in_cache,
+ &MDL_object_lock::prev_in_cache>
+{
+};
+
+
+static MDL_map mdl_locks;
+/**
+ Start-up parameter for the maximum size of the unused MDL_lock objects cache.
+*/
+ulong mdl_locks_cache_size;
+
+
+extern "C"
+{
+static uchar *
+mdl_locks_key(const uchar *record, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ MDL_lock *lock=(MDL_lock*) record;
+ *length= lock->key.length();
+ return (uchar*) lock->key.ptr();
+}
+} /* extern "C" */
+
+
+/**
+ Initialize the metadata locking subsystem.
+
+ This function is called at server startup.
+
+ In particular, initializes the new global mutex and
+ the associated condition variable: LOCK_mdl and COND_mdl.
+ These locking primitives are implementation details of the MDL
+ subsystem and are private to it.
+*/
+
+void mdl_init()
+{
+ DBUG_ASSERT(! mdl_initialized);
+ mdl_initialized= TRUE;
+
+#ifdef HAVE_PSI_INTERFACE
+ init_mdl_psi_keys();
+#endif
+
+ mdl_locks.init();
+}
+
+
+/**
+ Release resources of metadata locking subsystem.
+
+ Destroys the global mutex and the condition variable.
+ Called at server shutdown.
+*/
+
+void mdl_destroy()
+{
+ if (mdl_initialized)
+ {
+ mdl_initialized= FALSE;
+ mdl_locks.destroy();
+ }
+}
+
+
+/** Initialize the global hash containing all MDL locks. */
+
+void MDL_map::init()
+{
+ MDL_key global_lock_key(MDL_key::GLOBAL, "", "");
+ MDL_key commit_lock_key(MDL_key::COMMIT, "", "");
+
+ mysql_mutex_init(key_MDL_map_mutex, &m_mutex, NULL);
+ my_hash_init(&m_locks, &my_charset_bin, 16 /* FIXME */, 0, 0,
+ mdl_locks_key, 0, 0);
+ m_global_lock= MDL_lock::create(&global_lock_key);
+ m_commit_lock= MDL_lock::create(&commit_lock_key);
+}
+
+
+/**
+ Destroy the global hash containing all MDL locks.
+ @pre It must be empty.
+*/
+
+void MDL_map::destroy()
+{
+ DBUG_ASSERT(!m_locks.records);
+ mysql_mutex_destroy(&m_mutex);
+ my_hash_free(&m_locks);
+ MDL_lock::destroy(m_global_lock);
+ MDL_lock::destroy(m_commit_lock);
+
+ MDL_object_lock *lock;
+ while ((lock= m_unused_locks_cache.pop_front()))
+ MDL_lock::destroy(lock);
+}
+
+
+/**
+ Find MDL_lock object corresponding to the key, create it
+ if it does not exist.
+
+ @retval non-NULL - Success. MDL_lock instance for the key with
+ locked MDL_lock::m_rwlock.
+ @retval NULL - Failure (OOM).
+*/
+
+MDL_lock* MDL_map::find_or_insert(const MDL_key *mdl_key)
+{
+ MDL_lock *lock;
+ my_hash_value_type hash_value;
+
+ if (mdl_key->mdl_namespace() == MDL_key::GLOBAL ||
+ mdl_key->mdl_namespace() == MDL_key::COMMIT)
+ {
+ /*
+ Avoid locking m_mutex when lock for GLOBAL or COMMIT namespace is
+ requested. Return pointer to pre-allocated MDL_lock instance instead.
+ Such an optimization allows to save one mutex lock/unlock for any
+ statement changing data.
+
+ It works since these namespaces contain only one element so keys
+ for them look like '<namespace-id>\0\0'.
+ */
+ DBUG_ASSERT(mdl_key->length() == 3);
+
+ lock= (mdl_key->mdl_namespace() == MDL_key::GLOBAL) ? m_global_lock :
+ m_commit_lock;
+
+ mysql_prlock_wrlock(&lock->m_rwlock);
+
+ return lock;
+ }
+
+
+ hash_value= my_calc_hash(&m_locks, mdl_key->ptr(), mdl_key->length());
+
+retry:
+ mysql_mutex_lock(&m_mutex);
+ if (!(lock= (MDL_lock*) my_hash_search_using_hash_value(&m_locks,
+ hash_value,
+ mdl_key->ptr(),
+ mdl_key->length())))
+ {
+ MDL_object_lock *unused_lock= NULL;
+
+ /*
+ No lock object found so we need to create a new one
+ or reuse an existing unused object.
+ */
+ if (mdl_key->mdl_namespace() != MDL_key::SCHEMA &&
+ m_unused_locks_cache.elements())
+ {
+ /*
+ We need a MDL_object_lock type of object and the unused objects
+ cache has some. Get the first object from the cache and set a new
+ key for it.
+ */
+ DBUG_ASSERT(mdl_key->mdl_namespace() != MDL_key::GLOBAL &&
+ mdl_key->mdl_namespace() != MDL_key::COMMIT);
+
+ unused_lock= m_unused_locks_cache.pop_front();
+ unused_lock->reset(mdl_key);
+
+ lock= unused_lock;
+ }
+ else
+ {
+ lock= MDL_lock::create(mdl_key);
+ }
+
+ if (!lock || my_hash_insert(&m_locks, (uchar*)lock))
+ {
+ if (unused_lock)
+ {
+ /*
+ Note that we can't easily destroy an object from cache here as it
+ still might be referenced by other threads. So we simply put it
+ back into the cache.
+ */
+ m_unused_locks_cache.push_front(unused_lock);
+ }
+ else
+ {
+ MDL_lock::destroy(lock);
+ }
+ mysql_mutex_unlock(&m_mutex);
+ return NULL;
+ }
+ }
+
+ if (move_from_hash_to_lock_mutex(lock))
+ goto retry;
+
+ return lock;
+}
+
+
+/**
+ Release mdl_locks.m_mutex mutex and lock MDL_lock::m_rwlock for lock
+ object from the hash. Handle situation when object was released
+ while we held no locks.
+
+ @retval FALSE - Success.
+ @retval TRUE - Object was released while we held no mutex, caller
+ should re-try looking up MDL_lock object in the hash.
+*/
+
+bool MDL_map::move_from_hash_to_lock_mutex(MDL_lock *lock)
+{
+ ulonglong version;
+
+ DBUG_ASSERT(! lock->m_is_destroyed);
+ mysql_mutex_assert_owner(&m_mutex);
+
+ /*
+ We increment m_ref_usage which is a reference counter protected by
+ mdl_locks.m_mutex under the condition it is present in the hash and
+ m_is_destroyed is FALSE.
+ */
+ lock->m_ref_usage++;
+ /* Read value of the version counter under protection of m_mutex lock. */
+ version= lock->m_version;
+ mysql_mutex_unlock(&m_mutex);
+
+ mysql_prlock_wrlock(&lock->m_rwlock);
+ lock->m_ref_release++;
+
+ if (unlikely(lock->m_version != version))
+ {
+ /*
+ If the current value of version differs from one that was read while
+ we held m_mutex mutex, this MDL_lock object was moved to the unused
+ objects list or destroyed while we held no locks.
+ We should retry our search. But first we should destroy the MDL_lock
+ object if necessary.
+ */
+ if (unlikely(lock->m_is_destroyed))
+ {
+ /*
+ Object was released while we held no locks, we need to
+ release it if no others hold references to it, while our own
+ reference count ensured that the object as such haven't got
+ its memory released yet. We can also safely compare
+ m_ref_usage and m_ref_release since the object is no longer
+ present in the hash (or unused objects list) so no one will
+ be able to find it and increment m_ref_usage anymore.
+ */
+ uint ref_usage= lock->m_ref_usage;
+ uint ref_release= lock->m_ref_release;
+ mysql_prlock_unlock(&lock->m_rwlock);
+ if (ref_usage == ref_release)
+ MDL_lock::destroy(lock);
+ }
+ else
+ {
+ /*
+ Object was not destroyed but its version has changed.
+ This means that it was moved to the unused objects list
+ (and even might be already re-used). So now it might
+ correspond to a different key, therefore we should simply
+ retry our search.
+ */
+ mysql_prlock_unlock(&lock->m_rwlock);
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/**
+ Destroy MDL_lock object or delegate this responsibility to
+ whatever thread that holds the last outstanding reference to
+ it.
+*/
+
+void MDL_map::remove(MDL_lock *lock)
+{
+ if (lock->key.mdl_namespace() == MDL_key::GLOBAL ||
+ lock->key.mdl_namespace() == MDL_key::COMMIT)
+ {
+ /*
+ Never destroy pre-allocated MDL_lock objects for GLOBAL and
+ COMMIT namespaces.
+ */
+ mysql_prlock_unlock(&lock->m_rwlock);
+ return;
+ }
+
+ mysql_mutex_lock(&m_mutex);
+ my_hash_delete(&m_locks, (uchar*) lock);
+ /*
+ To let threads holding references to the MDL_lock object know that it was
+ moved to the list of unused objects or destroyed, we increment the version
+ counter under protection of both MDL_map::m_mutex and MDL_lock::m_rwlock
+ locks. This allows us to read the version value while having either one
+ of those locks.
+ */
+ lock->m_version++;
+
+ if ((lock->key.mdl_namespace() != MDL_key::SCHEMA) &&
+ (m_unused_locks_cache.elements() < mdl_locks_cache_size))
+ {
+ /*
+ This is an object of MDL_object_lock type and the cache of unused
+ objects has not reached its maximum size yet. So instead of destroying
+ object we move it to the list of unused objects to allow its later
+ re-use with possibly different key. Any threads holding references to
+ this object (owning MDL_map::m_mutex or MDL_lock::m_rwlock) will notice
+ this thanks to the fact that we have changed the MDL_lock::m_version
+ counter.
+ */
+ DBUG_ASSERT(lock->key.mdl_namespace() != MDL_key::GLOBAL &&
+ lock->key.mdl_namespace() != MDL_key::COMMIT);
+
+ m_unused_locks_cache.push_front((MDL_object_lock*)lock);
+ mysql_mutex_unlock(&m_mutex);
+ mysql_prlock_unlock(&lock->m_rwlock);
+ }
+ else
+ {
+ /*
+ Destroy the MDL_lock object, but ensure that anyone that is
+ holding a reference to the object is not remaining, if so he
+ has the responsibility to release it.
+
+ Setting of m_is_destroyed to TRUE while holding _both_
+ mdl_locks.m_mutex and MDL_lock::m_rwlock mutexes transfers the
+ protection of m_ref_usage from mdl_locks.m_mutex to
+ MDL_lock::m_rwlock while removal of the object from the hash
+ (and cache of unused objects) makes it read-only. Therefore
+ whoever acquires MDL_lock::m_rwlock next will see the most up
+ to date version of m_ref_usage.
+
+ This means that when m_is_destroyed is TRUE and we hold the
+ MDL_lock::m_rwlock we can safely read the m_ref_usage
+ member.
+ */
+ uint ref_usage, ref_release;
+
+ lock->m_is_destroyed= TRUE;
+ ref_usage= lock->m_ref_usage;
+ ref_release= lock->m_ref_release;
+ mysql_mutex_unlock(&m_mutex);
+ mysql_prlock_unlock(&lock->m_rwlock);
+ if (ref_usage == ref_release)
+ MDL_lock::destroy(lock);
+ }
+}
+
+
+/**
+ Initialize a metadata locking context.
+
+ This is to be called when a new server connection is created.
+*/
+
+MDL_context::MDL_context()
+ : m_thd(NULL),
+ m_needs_thr_lock_abort(FALSE),
+ m_waiting_for(NULL)
+{
+ mysql_prlock_init(key_MDL_context_LOCK_waiting_for, &m_LOCK_waiting_for);
+}
+
+
+/**
+ Destroy metadata locking context.
+
+ Assumes and asserts that there are no active or pending locks
+ associated with this context at the time of the destruction.
+
+ Currently does nothing. Asserts that there are no pending
+ or satisfied lock requests. The pending locks must be released
+ prior to destruction. This is a new way to express the assertion
+ that all tables are closed before a connection is destroyed.
+*/
+
+void MDL_context::destroy()
+{
+ DBUG_ASSERT(m_tickets[MDL_STATEMENT].is_empty() &&
+ m_tickets[MDL_TRANSACTION].is_empty() &&
+ m_tickets[MDL_EXPLICIT].is_empty());
+
+ mysql_prlock_destroy(&m_LOCK_waiting_for);
+}
+
+
+/**
+ Initialize a lock request.
+
+ This is to be used for every lock request.
+
+ Note that initialization and allocation are split into two
+ calls. This is to allow flexible memory management of lock
+ requests. Normally a lock request is stored in statement memory
+ (e.g. is a member of struct TABLE_LIST), but we would also like
+ to allow allocation of lock requests in other memory roots,
+ for example in the grant subsystem, to lock privilege tables.
+
+ The MDL subsystem does not own or manage memory of lock requests.
+
+ @param mdl_namespace Id of namespace of object to be locked
+ @param db Name of database to which the object belongs
+ @param name Name of of the object
+ @param mdl_type The MDL lock type for the request.
+*/
+
+void MDL_request::init(MDL_key::enum_mdl_namespace mdl_namespace,
+ const char *db_arg,
+ const char *name_arg,
+ enum_mdl_type mdl_type_arg,
+ enum_mdl_duration mdl_duration_arg)
+{
+ key.mdl_key_init(mdl_namespace, db_arg, name_arg);
+ type= mdl_type_arg;
+ duration= mdl_duration_arg;
+ ticket= NULL;
+}
+
+
+/**
+ Initialize a lock request using pre-built MDL_key.
+
+ @sa MDL_request::init(namespace, db, name, type).
+
+ @param key_arg The pre-built MDL key for the request.
+ @param mdl_type_arg The MDL lock type for the request.
+*/
+
+void MDL_request::init(const MDL_key *key_arg,
+ enum_mdl_type mdl_type_arg,
+ enum_mdl_duration mdl_duration_arg)
+{
+ key.mdl_key_init(key_arg);
+ type= mdl_type_arg;
+ duration= mdl_duration_arg;
+ ticket= NULL;
+}
+
+
+/**
+ Auxiliary functions needed for creation/destruction of MDL_lock objects.
+
+ @note Also chooses an MDL_lock descendant appropriate for object namespace.
+*/
+
+inline MDL_lock *MDL_lock::create(const MDL_key *mdl_key)
+{
+ switch (mdl_key->mdl_namespace())
+ {
+ case MDL_key::GLOBAL:
+ case MDL_key::SCHEMA:
+ case MDL_key::COMMIT:
+ return new MDL_scoped_lock(mdl_key);
+ default:
+ return new MDL_object_lock(mdl_key);
+ }
+}
+
+
+void MDL_lock::destroy(MDL_lock *lock)
+{
+ delete lock;
+}
+
+
+/**
+ Auxiliary functions needed for creation/destruction of MDL_ticket
+ objects.
+
+ @todo This naive implementation should be replaced with one that saves
+ on memory allocation by reusing released objects.
+*/
+
+MDL_ticket *MDL_ticket::create(MDL_context *ctx_arg, enum_mdl_type type_arg
+#ifndef DBUG_OFF
+ , enum_mdl_duration duration_arg
+#endif
+ )
+{
+ return new MDL_ticket(ctx_arg, type_arg
+#ifndef DBUG_OFF
+ , duration_arg
+#endif
+ );
+}
+
+
+void MDL_ticket::destroy(MDL_ticket *ticket)
+{
+ delete ticket;
+}
+
+
+/**
+ Return the 'weight' of this ticket for the
+ victim selection algorithm. Requests with
+ lower weight are preferred to requests
+ with higher weight when choosing a victim.
+*/
+
+uint MDL_ticket::get_deadlock_weight() const
+{
+ return (m_lock->key.mdl_namespace() == MDL_key::GLOBAL ||
+ m_type >= MDL_SHARED_NO_WRITE ?
+ DEADLOCK_WEIGHT_DDL : DEADLOCK_WEIGHT_DML);
+}
+
+
+/** Construct an empty wait slot. */
+
+MDL_wait::MDL_wait()
+ :m_wait_status(EMPTY)
+{
+ mysql_mutex_init(key_MDL_wait_LOCK_wait_status, &m_LOCK_wait_status, NULL);
+ mysql_cond_init(key_MDL_wait_COND_wait_status, &m_COND_wait_status, NULL);
+}
+
+
+/** Destroy system resources. */
+
+MDL_wait::~MDL_wait()
+{
+ mysql_mutex_destroy(&m_LOCK_wait_status);
+ mysql_cond_destroy(&m_COND_wait_status);
+}
+
+
+/**
+ Set the status unless it's already set. Return FALSE if set,
+ TRUE otherwise.
+*/
+
+bool MDL_wait::set_status(enum_wait_status status_arg)
+{
+ bool was_occupied= TRUE;
+ mysql_mutex_lock(&m_LOCK_wait_status);
+ if (m_wait_status == EMPTY)
+ {
+ was_occupied= FALSE;
+ m_wait_status= status_arg;
+ mysql_cond_signal(&m_COND_wait_status);
+ }
+ mysql_mutex_unlock(&m_LOCK_wait_status);
+ return was_occupied;
+}
+
+
+/** Query the current value of the wait slot. */
+
+MDL_wait::enum_wait_status MDL_wait::get_status()
+{
+ enum_wait_status result;
+ mysql_mutex_lock(&m_LOCK_wait_status);
+ result= m_wait_status;
+ mysql_mutex_unlock(&m_LOCK_wait_status);
+ return result;
+}
+
+
+/** Clear the current value of the wait slot. */
+
+void MDL_wait::reset_status()
+{
+ mysql_mutex_lock(&m_LOCK_wait_status);
+ m_wait_status= EMPTY;
+ mysql_mutex_unlock(&m_LOCK_wait_status);
+}
+
+
+/**
+ Wait for the status to be assigned to this wait slot.
+
+ @param abs_timeout Absolute time after which waiting should stop.
+ @param set_status_on_timeout TRUE - If in case of timeout waiting
+ context should close the wait slot by
+ sending TIMEOUT to itself.
+ FALSE - Otherwise.
+ @param wait_state_name Thread state name to be set for duration of wait.
+
+ @returns Signal posted.
+*/
+
+MDL_wait::enum_wait_status
+MDL_wait::timed_wait(THD *thd, struct timespec *abs_timeout,
+ bool set_status_on_timeout, const char *wait_state_name)
+{
+ const char *old_msg;
+ enum_wait_status result;
+ int wait_result= 0;
+ DBUG_ENTER("MDL_wait::timed_wait");
+
+ mysql_mutex_lock(&m_LOCK_wait_status);
+
+ old_msg= thd_enter_cond(thd, &m_COND_wait_status, &m_LOCK_wait_status,
+ wait_state_name);
+
+ thd_wait_begin(thd, THD_WAIT_META_DATA_LOCK);
+ while (!m_wait_status && !thd->killed &&
+ wait_result != ETIMEDOUT && wait_result != ETIME)
+ {
+ wait_result= mysql_cond_timedwait(&m_COND_wait_status, &m_LOCK_wait_status,
+ abs_timeout);
+ }
+ thd_wait_end(thd);
+
+ if (m_wait_status == EMPTY)
+ {
+ /*
+ Wait has ended not due to a status being set from another
+ thread but due to this connection/statement being killed or a
+ time out.
+ To avoid races, which may occur if another thread sets
+ GRANTED status before the code which calls this method
+ processes the abort/timeout, we assign the status under
+ protection of the m_LOCK_wait_status, within the critical
+ section. An exception is when set_status_on_timeout is
+ false, which means that the caller intends to restart the
+ wait.
+ */
+ if (thd->killed)
+ m_wait_status= KILLED;
+ else if (set_status_on_timeout)
+ m_wait_status= TIMEOUT;
+ }
+ result= m_wait_status;
+
+ thd_exit_cond(thd, old_msg);
+
+ DBUG_RETURN(result);
+}
+
+
+/**
+ Clear bit corresponding to the type of metadata lock in bitmap representing
+ set of such types if list of tickets does not contain ticket with such type.
+
+ @param[in,out] bitmap Bitmap representing set of types of locks.
+ @param[in] list List to inspect.
+ @param[in] type Type of metadata lock to look up in the list.
+*/
+
+void MDL_lock::Ticket_list::clear_bit_if_not_in_list(enum_mdl_type type)
+{
+ MDL_lock::Ticket_iterator it(m_list);
+ const MDL_ticket *ticket;
+
+ while ((ticket= it++))
+ if (ticket->get_type() == type)
+ return;
+ m_bitmap&= ~ MDL_BIT(type);
+}
+
+
+/**
+ Add ticket to MDL_lock's list of waiting requests and
+ update corresponding bitmap of lock types.
+*/
+
+void MDL_lock::Ticket_list::add_ticket(MDL_ticket *ticket)
+{
+ /*
+ Ticket being added to the list must have MDL_ticket::m_lock set,
+ since for such tickets methods accessing this member might be
+ called by other threads.
+ */
+ DBUG_ASSERT(ticket->get_lock());
+ /*
+ Add ticket to the *back* of the queue to ensure fairness
+ among requests with the same priority.
+ */
+ m_list.push_back(ticket);
+ m_bitmap|= MDL_BIT(ticket->get_type());
+}
+
+
+/**
+ Remove ticket from MDL_lock's list of requests and
+ update corresponding bitmap of lock types.
+*/
+
+void MDL_lock::Ticket_list::remove_ticket(MDL_ticket *ticket)
+{
+ m_list.remove(ticket);
+ /*
+ Check if waiting queue has another ticket with the same type as
+ one which was removed. If there is no such ticket, i.e. we have
+ removed last ticket of particular type, then we need to update
+ bitmap of waiting ticket's types.
+ Note that in most common case, i.e. when shared lock is removed
+ from waiting queue, we are likely to find ticket of the same
+ type early without performing full iteration through the list.
+ So this method should not be too expensive.
+ */
+ clear_bit_if_not_in_list(ticket->get_type());
+}
+
+
+/**
+ Determine waiting contexts which requests for the lock can be
+ satisfied, grant lock to them and wake them up.
+
+ @note Together with MDL_lock::add_ticket() this method implements
+ fair scheduling among requests with the same priority.
+ It tries to grant lock from the head of waiters list, while
+ add_ticket() adds new requests to the back of this list.
+
+*/
+
+void MDL_lock::reschedule_waiters()
+{
+ MDL_lock::Ticket_iterator it(m_waiting);
+ MDL_ticket *ticket;
+ bool skip_high_priority= false;
+ bitmap_t hog_lock_types= hog_lock_types_bitmap();
+
+ if (m_hog_lock_count >= max_write_lock_count)
+ {
+ /*
+ If number of successively granted high-prio, strong locks has exceeded
+ max_write_lock_count give a way to low-prio, weak locks to avoid their
+ starvation.
+ */
+
+ if ((m_waiting.bitmap() & ~hog_lock_types) != 0)
+ {
+ /*
+ Even though normally when m_hog_lock_count is non-0 there is
+ some pending low-prio lock, we still can encounter situation
+ when m_hog_lock_count is non-0 and there are no pending low-prio
+ locks. This, for example, can happen when a ticket for pending
+ low-prio lock was removed from waiters list due to timeout,
+ and reschedule_waiters() is called after that to update the
+ waiters queue. m_hog_lock_count will be reset to 0 at the
+ end of this call in such case.
+
+ Note that it is not an issue if we fail to wake up any pending
+ waiters for weak locks in the loop below. This would mean that
+ all of them are either killed, timed out or chosen as a victim
+ by deadlock resolver, but have not managed to remove ticket
+ from the waiters list yet. After tickets will be removed from
+ the waiters queue there will be another call to
+ reschedule_waiters() with pending bitmap updated to reflect new
+ state of waiters queue.
+ */
+ skip_high_priority= true;
+ }
+ }
+
+ /*
+ Find the first (and hence the oldest) waiting request which
+ can be satisfied (taking into account priority). Grant lock to it.
+ Repeat the process for the remainder of waiters.
+ Note we don't need to re-start iteration from the head of the
+ list after satisfying the first suitable request as in our case
+ all compatible types of requests have the same priority.
+
+ TODO/FIXME: We should:
+ - Either switch to scheduling without priorities
+ which will allow to stop iteration through the
+ list of waiters once we found the first ticket
+ which can't be satisfied
+ - Or implement some check using bitmaps which will
+ allow to stop iteration in cases when, e.g., we
+ grant SNRW lock and there are no pending S or
+ SH locks.
+ */
+ while ((ticket= it++))
+ {
+ /*
+ Skip high-prio, strong locks if earlier we have decided to give way to
+ low-prio, weaker locks.
+ */
+ if (skip_high_priority &&
+ ((MDL_BIT(ticket->get_type()) & hog_lock_types) != 0))
+ continue;
+
+ if (can_grant_lock(ticket->get_type(), ticket->get_ctx(),
+ skip_high_priority))
+ {
+ if (! ticket->get_ctx()->m_wait.set_status(MDL_wait::GRANTED))
+ {
+ /*
+ Satisfy the found request by updating lock structures.
+ It is OK to do so even after waking up the waiter since any
+ session which tries to get any information about the state of
+ this lock has to acquire MDL_lock::m_rwlock first and thus,
+ when manages to do so, already sees an updated state of the
+ MDL_lock object.
+ */
+ m_waiting.remove_ticket(ticket);
+ m_granted.add_ticket(ticket);
+
+ /*
+ Increase counter of successively granted high-priority strong locks,
+ if we have granted one.
+ */
+ if ((MDL_BIT(ticket->get_type()) & hog_lock_types) != 0)
+ m_hog_lock_count++;
+ }
+ /*
+ If we could not update the wait slot of the waiter,
+ it can be due to fact that its connection/statement was
+ killed or it has timed out (i.e. the slot is not empty).
+ Since in all such cases the waiter assumes that the lock was
+ not been granted, we should keep the request in the waiting
+ queue and look for another request to reschedule.
+ */
+ }
+ }
+
+ if ((m_waiting.bitmap() & ~hog_lock_types) == 0)
+ {
+ /*
+ Reset number of successively granted high-prio, strong locks
+ if there are no pending low-prio, weak locks.
+ This ensures:
+ - That m_hog_lock_count is correctly reset after strong lock
+ is released and weak locks are granted (or there are no
+ other lock requests).
+ - That situation when SNW lock is granted along with some SR
+ locks, but SW locks are still blocked are handled correctly.
+ - That m_hog_lock_count is zero in most cases when there are no pending
+ weak locks (see comment at the start of this method for example of
+ exception). This allows to save on checks at the start of this method.
+ */
+ m_hog_lock_count= 0;
+ }
+}
+
+
+/**
+ Compatibility (or rather "incompatibility") matrices for scoped metadata
+ lock. Arrays of bitmaps which elements specify which granted/waiting locks
+ are incompatible with type of lock being requested.
+
+ Here is how types of individual locks are translated to type of scoped lock:
+
+ ----------------+-------------+
+ Type of request | Correspond. |
+ for indiv. lock | scoped lock |
+ ----------------+-------------+
+ S, SH, SR, SW | IS |
+ SNW, SNRW, X | IX |
+ SNW, SNRW -> X | IX (*) |
+
+ The first array specifies if particular type of request can be satisfied
+ if there is granted scoped lock of certain type.
+
+ | Type of active |
+ Request | scoped lock |
+ type | IS(**) IX S X |
+ ---------+------------------+
+ IS | + + + + |
+ IX | + + - - |
+ S | + - + - |
+ X | + - - - |
+
+ The second array specifies if particular type of request can be satisfied
+ if there is already waiting request for the scoped lock of certain type.
+ I.e. it specifies what is the priority of different lock types.
+
+ | Pending |
+ Request | scoped lock |
+ type | IS(**) IX S X |
+ ---------+-----------------+
+ IS | + + + + |
+ IX | + + - - |
+ S | + + + - |
+ X | + + + + |
+
+ Here: "+" -- means that request can be satisfied
+ "-" -- means that request can't be satisfied and should wait
+
+ (*) Since for upgradable locks we always take intention exclusive scoped
+ lock at the same time when obtaining the shared lock, there is no
+ need to obtain such lock during the upgrade itself.
+ (**) Since intention shared scoped locks are compatible with all other
+ type of locks we don't even have any accounting for them.
+*/
+
+const MDL_lock::bitmap_t MDL_scoped_lock::m_granted_incompatible[MDL_TYPE_END] =
+{
+ MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED),
+ MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_INTENTION_EXCLUSIVE), 0, 0, 0, 0, 0,
+ MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED) | MDL_BIT(MDL_INTENTION_EXCLUSIVE)
+};
+
+const MDL_lock::bitmap_t MDL_scoped_lock::m_waiting_incompatible[MDL_TYPE_END] =
+{
+ MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED),
+ MDL_BIT(MDL_EXCLUSIVE), 0, 0, 0, 0, 0, 0
+};
+
+
+/**
+ Compatibility (or rather "incompatibility") matrices for per-object
+ metadata lock. Arrays of bitmaps which elements specify which granted/
+ waiting locks are incompatible with type of lock being requested.
+
+ The first array specifies if particular type of request can be satisfied
+ if there is granted lock of certain type.
+
+ Request | Granted requests for lock |
+ type | S SH SR SW SNW SNRW X |
+ ----------+------------------------------+
+ S | + + + + + + - |
+ SH | + + + + + + - |
+ SR | + + + + + - - |
+ SW | + + + + - - - |
+ SNW | + + + - - - - |
+ SNRW | + + - - - - - |
+ X | - - - - - - - |
+ SNW -> X | - - - 0 0 0 0 |
+ SNRW -> X | - - 0 0 0 0 0 |
+
+ The second array specifies if particular type of request can be satisfied
+ if there is waiting request for the same lock of certain type. In other
+ words it specifies what is the priority of different lock types.
+
+ Request | Pending requests for lock |
+ type | S SH SR SW SNW SNRW X |
+ ----------+-----------------------------+
+ S | + + + + + + - |
+ SH | + + + + + + + |
+ SR | + + + + + - - |
+ SW | + + + + - - - |
+ SNW | + + + + + + - |
+ SNRW | + + + + + + - |
+ X | + + + + + + + |
+ SNW -> X | + + + + + + + |
+ SNRW -> X | + + + + + + + |
+
+ Here: "+" -- means that request can be satisfied
+ "-" -- means that request can't be satisfied and should wait
+ "0" -- means impossible situation which will trigger assert
+
+ @note In cases then current context already has "stronger" type
+ of lock on the object it will be automatically granted
+ thanks to usage of the MDL_context::find_ticket() method.
+*/
+
+const MDL_lock::bitmap_t
+MDL_object_lock::m_granted_incompatible[MDL_TYPE_END] =
+{
+ 0,
+ MDL_BIT(MDL_EXCLUSIVE),
+ MDL_BIT(MDL_EXCLUSIVE),
+ MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE),
+ MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
+ MDL_BIT(MDL_SHARED_NO_WRITE),
+ MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
+ MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_WRITE),
+ MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
+ MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_WRITE) |
+ MDL_BIT(MDL_SHARED_READ),
+ MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
+ MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_WRITE) |
+ MDL_BIT(MDL_SHARED_READ) | MDL_BIT(MDL_SHARED_HIGH_PRIO) |
+ MDL_BIT(MDL_SHARED)
+};
+
+
+const MDL_lock::bitmap_t
+MDL_object_lock::m_waiting_incompatible[MDL_TYPE_END] =
+{
+ 0,
+ MDL_BIT(MDL_EXCLUSIVE),
+ 0,
+ MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE),
+ MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
+ MDL_BIT(MDL_SHARED_NO_WRITE),
+ MDL_BIT(MDL_EXCLUSIVE),
+ MDL_BIT(MDL_EXCLUSIVE),
+ 0
+};
+
+
+/**
+ Check if request for the metadata lock can be satisfied given its
+ current state.
+
+ @param type_arg The requested lock type.
+ @param requestor_ctx The MDL context of the requestor.
+ @param ignore_lock_priority Ignore lock priority.
+
+ @retval TRUE Lock request can be satisfied
+ @retval FALSE There is some conflicting lock.
+
+ @note In cases then current context already has "stronger" type
+ of lock on the object it will be automatically granted
+ thanks to usage of the MDL_context::find_ticket() method.
+*/
+
+bool
+MDL_lock::can_grant_lock(enum_mdl_type type_arg,
+ MDL_context *requestor_ctx,
+ bool ignore_lock_priority) const
+{
+ bool can_grant= FALSE;
+ bitmap_t waiting_incompat_map= incompatible_waiting_types_bitmap()[type_arg];
+ bitmap_t granted_incompat_map= incompatible_granted_types_bitmap()[type_arg];
+
+ /*
+ New lock request can be satisfied iff:
+ - There are no incompatible types of satisfied requests
+ in other contexts
+ - There are no waiting requests which have higher priority
+ than this request when priority was not ignored.
+ */
+ if (ignore_lock_priority || !(m_waiting.bitmap() & waiting_incompat_map))
+ {
+ if (! (m_granted.bitmap() & granted_incompat_map))
+ can_grant= TRUE;
+ else
+ {
+ Ticket_iterator it(m_granted);
+ MDL_ticket *ticket;
+
+ /* Check that the incompatible lock belongs to some other context. */
+ while ((ticket= it++))
+ {
+ if (ticket->get_ctx() != requestor_ctx &&
+ ticket->is_incompatible_when_granted(type_arg))
+ break;
+ }
+ if (ticket == NULL) /* Incompatible locks are our own. */
+ can_grant= TRUE;
+ }
+ }
+ return can_grant;
+}
+
+
+/** Remove a ticket from waiting or pending queue and wakeup up waiters. */
+
+void MDL_lock::remove_ticket(Ticket_list MDL_lock::*list, MDL_ticket *ticket)
+{
+ mysql_prlock_wrlock(&m_rwlock);
+ (this->*list).remove_ticket(ticket);
+ if (is_empty())
+ mdl_locks.remove(this);
+ else
+ {
+ /*
+ There can be some contexts waiting to acquire a lock
+ which now might be able to do it. Grant the lock to
+ them and wake them up!
+
+ We always try to reschedule locks, since there is no easy way
+ (i.e. by looking at the bitmaps) to find out whether it is
+ required or not.
+ In a general case, even when the queue's bitmap is not changed
+ after removal of the ticket, there is a chance that some request
+ can be satisfied (due to the fact that a granted request
+ reflected in the bitmap might belong to the same context as a
+ pending request).
+ */
+ reschedule_waiters();
+ mysql_prlock_unlock(&m_rwlock);
+ }
+}
+
+
+/**
+ Check if we have any pending locks which conflict with existing
+ shared lock.
+
+ @pre The ticket must match an acquired lock.
+
+ @return TRUE if there is a conflicting lock request, FALSE otherwise.
+*/
+
+bool MDL_lock::has_pending_conflicting_lock(enum_mdl_type type)
+{
+ bool result;
+
+ mysql_mutex_assert_not_owner(&LOCK_open);
+
+ mysql_prlock_rdlock(&m_rwlock);
+ result= (m_waiting.bitmap() & incompatible_granted_types_bitmap()[type]);
+ mysql_prlock_unlock(&m_rwlock);
+ return result;
+}
+
+
+MDL_wait_for_graph_visitor::~MDL_wait_for_graph_visitor()
+{
+}
+
+
+MDL_wait_for_subgraph::~MDL_wait_for_subgraph()
+{
+}
+
+/**
+ Check if ticket represents metadata lock of "stronger" or equal type
+ than specified one. I.e. if metadata lock represented by ticket won't
+ allow any of locks which are not allowed by specified type of lock.
+
+ @return TRUE if ticket has stronger or equal type
+ FALSE otherwise.
+*/
+
+bool MDL_ticket::has_stronger_or_equal_type(enum_mdl_type type) const
+{
+ const MDL_lock::bitmap_t *
+ granted_incompat_map= m_lock->incompatible_granted_types_bitmap();
+
+ return ! (granted_incompat_map[type] & ~(granted_incompat_map[m_type]));
+}
+
+
+bool MDL_ticket::is_incompatible_when_granted(enum_mdl_type type) const
+{
+ return (MDL_BIT(m_type) &
+ m_lock->incompatible_granted_types_bitmap()[type]);
+}
+
+
+bool MDL_ticket::is_incompatible_when_waiting(enum_mdl_type type) const
+{
+ return (MDL_BIT(m_type) &
+ m_lock->incompatible_waiting_types_bitmap()[type]);
+}
+
+
+/**
+ Check whether the context already holds a compatible lock ticket
+ on an object.
+ Start searching from list of locks for the same duration as lock
+ being requested. If not look at lists for other durations.
+
+ @param mdl_request Lock request object for lock to be acquired
+ @param[out] result_duration Duration of lock which was found.
+
+ @note Tickets which correspond to lock types "stronger" than one
+ being requested are also considered compatible.
+
+ @return A pointer to the lock ticket for the object or NULL otherwise.
+*/
+
+MDL_ticket *
+MDL_context::find_ticket(MDL_request *mdl_request,
+ enum_mdl_duration *result_duration)
+{
+ MDL_ticket *ticket;
+ int i;
+
+ for (i= 0; i < MDL_DURATION_END; i++)
+ {
+ enum_mdl_duration duration= (enum_mdl_duration)((mdl_request->duration+i) %
+ MDL_DURATION_END);
+ Ticket_iterator it(m_tickets[duration]);
+
+ while ((ticket= it++))
+ {
+ if (mdl_request->key.is_equal(&ticket->m_lock->key) &&
+ ticket->has_stronger_or_equal_type(mdl_request->type))
+ {
+ *result_duration= duration;
+ return ticket;
+ }
+ }
+ }
+ return NULL;
+}
+
+
+/**
+ Try to acquire one lock.
+
+ Unlike exclusive locks, shared locks are acquired one by
+ one. This is interface is chosen to simplify introduction of
+ the new locking API to the system. MDL_context::try_acquire_lock()
+ is currently used from open_table(), and there we have only one
+ table to work with.
+
+ This function may also be used to try to acquire an exclusive
+ lock on a destination table, by ALTER TABLE ... RENAME.
+
+ Returns immediately without any side effect if encounters a lock
+ conflict. Otherwise takes the lock.
+
+ FIXME: Compared to lock_table_name_if_not_cached() (from 5.1)
+ it gives slightly more false negatives.
+
+ @param mdl_request [in/out] Lock request object for lock to be acquired
+
+ @retval FALSE Success. The lock may have not been acquired.
+ Check the ticket, if it's NULL, a conflicting lock
+ exists.
+ @retval TRUE Out of resources, an error has been reported.
+*/
+
+bool
+MDL_context::try_acquire_lock(MDL_request *mdl_request)
+{
+ MDL_ticket *ticket;
+
+ if (try_acquire_lock_impl(mdl_request, &ticket))
+ return TRUE;
+
+ if (! mdl_request->ticket)
+ {
+ /*
+ Our attempt to acquire lock without waiting has failed.
+ Let us release resources which were acquired in the process.
+ We can't get here if we allocated a new lock object so there
+ is no need to release it.
+ */
+ DBUG_ASSERT(! ticket->m_lock->is_empty());
+ mysql_prlock_unlock(&ticket->m_lock->m_rwlock);
+ MDL_ticket::destroy(ticket);
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Auxiliary method for acquiring lock without waiting.
+
+ @param mdl_request [in/out] Lock request object for lock to be acquired
+ @param out_ticket [out] Ticket for the request in case when lock
+ has not been acquired.
+
+ @retval FALSE Success. The lock may have not been acquired.
+ Check MDL_request::ticket, if it's NULL, a conflicting
+ lock exists. In this case "out_ticket" out parameter
+ points to ticket which was constructed for the request.
+ MDL_ticket::m_lock points to the corresponding MDL_lock
+ object and MDL_lock::m_rwlock write-locked.
+ @retval TRUE Out of resources, an error has been reported.
+*/
+
+bool
+MDL_context::try_acquire_lock_impl(MDL_request *mdl_request,
+ MDL_ticket **out_ticket)
+{
+ MDL_lock *lock;
+ MDL_key *key= &mdl_request->key;
+ MDL_ticket *ticket;
+ enum_mdl_duration found_duration;
+
+ DBUG_ASSERT(mdl_request->type != MDL_EXCLUSIVE ||
+ is_lock_owner(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE));
+ DBUG_ASSERT(mdl_request->ticket == NULL);
+
+ /* Don't take chances in production. */
+ mdl_request->ticket= NULL;
+ mysql_mutex_assert_not_owner(&LOCK_open);
+
+ /*
+ Check whether the context already holds a shared lock on the object,
+ and if so, grant the request.
+ */
+ if ((ticket= find_ticket(mdl_request, &found_duration)))
+ {
+ DBUG_ASSERT(ticket->m_lock);
+ DBUG_ASSERT(ticket->has_stronger_or_equal_type(mdl_request->type));
+ /*
+ If the request is for a transactional lock, and we found
+ a transactional lock, just reuse the found ticket.
+
+ It's possible that we found a transactional lock,
+ but the request is for a HANDLER lock. In that case HANDLER
+ code will clone the ticket (see below why it's needed).
+
+ If the request is for a transactional lock, and we found
+ a HANDLER lock, create a copy, to make sure that when user
+ does HANDLER CLOSE, the transactional lock is not released.
+
+ If the request is for a handler lock, and we found a
+ HANDLER lock, also do the clone. HANDLER CLOSE for one alias
+ should not release the lock on the table HANDLER opened through
+ a different alias.
+ */
+ mdl_request->ticket= ticket;
+ if ((found_duration != mdl_request->duration ||
+ mdl_request->duration == MDL_EXPLICIT) &&
+ clone_ticket(mdl_request))
+ {
+ /* Clone failed. */
+ mdl_request->ticket= NULL;
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ if (!(ticket= MDL_ticket::create(this, mdl_request->type
+#ifndef DBUG_OFF
+ , mdl_request->duration
+#endif
+ )))
+ return TRUE;
+
+ /* The below call implicitly locks MDL_lock::m_rwlock on success. */
+ if (!(lock= mdl_locks.find_or_insert(key)))
+ {
+ MDL_ticket::destroy(ticket);
+ return TRUE;
+ }
+
+ ticket->m_lock= lock;
+
+ if (lock->can_grant_lock(mdl_request->type, this, false))
+ {
+ lock->m_granted.add_ticket(ticket);
+
+ mysql_prlock_unlock(&lock->m_rwlock);
+
+ m_tickets[mdl_request->duration].push_front(ticket);
+
+ mdl_request->ticket= ticket;
+ }
+ else
+ *out_ticket= ticket;
+
+ return FALSE;
+}
+
+
+/**
+ Create a copy of a granted ticket.
+ This is used to make sure that HANDLER ticket
+ is never shared with a ticket that belongs to
+ a transaction, so that when we HANDLER CLOSE,
+ we don't release a transactional ticket, and
+ vice versa -- when we COMMIT, we don't mistakenly
+ release a ticket for an open HANDLER.
+
+ @retval TRUE Out of memory.
+ @retval FALSE Success.
+*/
+
+bool
+MDL_context::clone_ticket(MDL_request *mdl_request)
+{
+ MDL_ticket *ticket;
+
+ mysql_mutex_assert_not_owner(&LOCK_open);
+ /*
+ By submitting mdl_request->type to MDL_ticket::create()
+ we effectively downgrade the cloned lock to the level of
+ the request.
+ */
+ if (!(ticket= MDL_ticket::create(this, mdl_request->type
+#ifndef DBUG_OFF
+ , mdl_request->duration
+#endif
+ )))
+ return TRUE;
+
+ /* clone() is not supposed to be used to get a stronger lock. */
+ DBUG_ASSERT(mdl_request->ticket->has_stronger_or_equal_type(ticket->m_type));
+
+ ticket->m_lock= mdl_request->ticket->m_lock;
+ mdl_request->ticket= ticket;
+
+ mysql_prlock_wrlock(&ticket->m_lock->m_rwlock);
+ ticket->m_lock->m_granted.add_ticket(ticket);
+ mysql_prlock_unlock(&ticket->m_lock->m_rwlock);
+
+ m_tickets[mdl_request->duration].push_front(ticket);
+
+ return FALSE;
+}
+
+
+/**
+ Notify threads holding a shared metadata locks on object which
+ conflict with a pending X, SNW or SNRW lock.
+
+ @param ctx MDL_context for current thread.
+*/
+
+void MDL_object_lock::notify_conflicting_locks(MDL_context *ctx)
+{
+ Ticket_iterator it(m_granted);
+ MDL_ticket *conflicting_ticket;
+
+ while ((conflicting_ticket= it++))
+ {
+ /* Only try to abort locks on which we back off. */
+ if (conflicting_ticket->get_ctx() != ctx &&
+ conflicting_ticket->get_type() < MDL_SHARED_NO_WRITE)
+
+ {
+ MDL_context *conflicting_ctx= conflicting_ticket->get_ctx();
+
+ /*
+ If thread which holds conflicting lock is waiting on table-level
+ lock or some other non-MDL resource we might need to wake it up
+ by calling code outside of MDL.
+ */
+ mysql_notify_thread_having_shared_lock(ctx->get_thd(),
+ conflicting_ctx->get_thd(),
+ conflicting_ctx->get_needs_thr_lock_abort());
+ }
+ }
+}
+
+
+/**
+ Notify threads holding scoped IX locks which conflict with a pending S lock.
+
+ @param ctx MDL_context for current thread.
+*/
+
+void MDL_scoped_lock::notify_conflicting_locks(MDL_context *ctx)
+{
+ Ticket_iterator it(m_granted);
+ MDL_ticket *conflicting_ticket;
+
+ while ((conflicting_ticket= it++))
+ {
+ if (conflicting_ticket->get_ctx() != ctx &&
+ conflicting_ticket->get_type() == MDL_INTENTION_EXCLUSIVE)
+
+ {
+ MDL_context *conflicting_ctx= conflicting_ticket->get_ctx();
+
+ /*
+ Thread which holds global IX lock can be a handler thread for
+ insert delayed. We need to kill such threads in order to get
+ global shared lock. We do this my calling code outside of MDL.
+ */
+ mysql_notify_thread_having_shared_lock(ctx->get_thd(),
+ conflicting_ctx->get_thd(),
+ conflicting_ctx->get_needs_thr_lock_abort());
+ }
+ }
+}
+
+
+/**
+ Acquire one lock with waiting for conflicting locks to go away if needed.
+
+ @param mdl_request [in/out] Lock request object for lock to be acquired
+
+ @param lock_wait_timeout [in] Seconds to wait before timeout.
+
+ @retval FALSE Success. MDL_request::ticket points to the ticket
+ for the lock.
+ @retval TRUE Failure (Out of resources or waiting is aborted),
+*/
+
+bool
+MDL_context::acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout)
+{
+ MDL_lock *lock;
+ MDL_ticket *ticket;
+ struct timespec abs_timeout;
+ MDL_wait::enum_wait_status wait_status;
+ DBUG_ENTER("MDL_context::acquire_lock");
+
+ /* Do some work outside the critical section. */
+ set_timespec(abs_timeout, lock_wait_timeout);
+
+ if (try_acquire_lock_impl(mdl_request, &ticket))
+ DBUG_RETURN(TRUE);
+
+ if (mdl_request->ticket)
+ {
+ /*
+ We have managed to acquire lock without waiting.
+ MDL_lock, MDL_context and MDL_request were updated
+ accordingly, so we can simply return success.
+ */
+ DBUG_RETURN(FALSE);
+ }
+
+ /*
+ Our attempt to acquire lock without waiting has failed.
+ As a result of this attempt we got MDL_ticket with m_lock
+ member pointing to the corresponding MDL_lock object which
+ has MDL_lock::m_rwlock write-locked.
+ */
+ lock= ticket->m_lock;
+
+ lock->m_waiting.add_ticket(ticket);
+
+ /*
+ Once we added a pending ticket to the waiting queue,
+ we must ensure that our wait slot is empty, so
+ that our lock request can be scheduled. Do that in the
+ critical section formed by the acquired write lock on MDL_lock.
+ */
+ m_wait.reset_status();
+
+ /*
+ Don't break conflicting locks if timeout is 0 as 0 is used
+ To check if there is any conflicting locks...
+ */
+ if (lock->needs_notification(ticket) && lock_wait_timeout)
+ lock->notify_conflicting_locks(this);
+
+ mysql_prlock_unlock(&lock->m_rwlock);
+
+ will_wait_for(ticket);
+
+ /* There is a shared or exclusive lock on the object. */
+ DEBUG_SYNC(m_thd, "mdl_acquire_lock_wait");
+
+ find_deadlock();
+
+ if (lock->needs_notification(ticket))
+ {
+ struct timespec abs_shortwait;
+ set_timespec(abs_shortwait, 1);
+ wait_status= MDL_wait::EMPTY;
+
+ while (cmp_timespec(abs_shortwait, abs_timeout) <= 0)
+ {
+ /* abs_timeout is far away. Wait a short while and notify locks. */
+ wait_status= m_wait.timed_wait(m_thd, &abs_shortwait, FALSE,
+ mdl_request->key.get_wait_state_name());
+
+ if (wait_status != MDL_wait::EMPTY)
+ break;
+
+ mysql_prlock_wrlock(&lock->m_rwlock);
+ lock->notify_conflicting_locks(this);
+ mysql_prlock_unlock(&lock->m_rwlock);
+ set_timespec(abs_shortwait, 1);
+ }
+ if (wait_status == MDL_wait::EMPTY)
+ wait_status= m_wait.timed_wait(m_thd, &abs_timeout, TRUE,
+ mdl_request->key.get_wait_state_name());
+ }
+ else
+ wait_status= m_wait.timed_wait(m_thd, &abs_timeout, TRUE,
+ mdl_request->key.get_wait_state_name());
+
+ done_waiting_for();
+
+ if (wait_status != MDL_wait::GRANTED)
+ {
+ lock->remove_ticket(&MDL_lock::m_waiting, ticket);
+ MDL_ticket::destroy(ticket);
+ switch (wait_status)
+ {
+ case MDL_wait::VICTIM:
+ my_error(ER_LOCK_DEADLOCK, MYF(0));
+ break;
+ case MDL_wait::TIMEOUT:
+ my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
+ break;
+ case MDL_wait::KILLED:
+ break;
+ default:
+ DBUG_ASSERT(0);
+ break;
+ }
+ DBUG_RETURN(TRUE);
+ }
+
+ /*
+ We have been granted our request.
+ State of MDL_lock object is already being appropriately updated by a
+ concurrent thread (@sa MDL_lock:reschedule_waiters()).
+ So all we need to do is to update MDL_context and MDL_request objects.
+ */
+ DBUG_ASSERT(wait_status == MDL_wait::GRANTED);
+
+ m_tickets[mdl_request->duration].push_front(ticket);
+
+ mdl_request->ticket= ticket;
+
+ DBUG_RETURN(FALSE);
+}
+
+
+extern "C" int mdl_request_ptr_cmp(const void* ptr1, const void* ptr2)
+{
+ MDL_request *req1= *(MDL_request**)ptr1;
+ MDL_request *req2= *(MDL_request**)ptr2;
+ return req1->key.cmp(&req2->key);
+}
+
+
+/**
+ Acquire exclusive locks. There must be no granted locks in the
+ context.
+
+ This is a replacement of lock_table_names(). It is used in
+ RENAME, DROP and other DDL SQL statements.
+
+ @param mdl_requests List of requests for locks to be acquired.
+
+ @param lock_wait_timeout Seconds to wait before timeout.
+
+ @note The list of requests should not contain non-exclusive lock requests.
+ There should not be any acquired locks in the context.
+
+ @note Assumes that one already owns scoped intention exclusive lock.
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool MDL_context::acquire_locks(MDL_request_list *mdl_requests,
+ ulong lock_wait_timeout)
+{
+ MDL_request_list::Iterator it(*mdl_requests);
+ MDL_request **sort_buf, **p_req;
+ MDL_savepoint mdl_svp= mdl_savepoint();
+ ssize_t req_count= static_cast<ssize_t>(mdl_requests->elements());
+ DBUG_ENTER("MDL_context::acquire_locks");
+
+ if (req_count == 0)
+ DBUG_RETURN(FALSE);
+
+ /* Sort requests according to MDL_key. */
+ if (! (sort_buf= (MDL_request **)my_malloc(req_count *
+ sizeof(MDL_request*),
+ MYF(MY_WME))))
+ DBUG_RETURN(TRUE);
+
+ for (p_req= sort_buf; p_req < sort_buf + req_count; p_req++)
+ *p_req= it++;
+
+ my_qsort(sort_buf, req_count, sizeof(MDL_request*),
+ mdl_request_ptr_cmp);
+
+ for (p_req= sort_buf; p_req < sort_buf + req_count; p_req++)
+ {
+ if (acquire_lock(*p_req, lock_wait_timeout))
+ goto err;
+ }
+ my_free(sort_buf);
+ DBUG_RETURN(FALSE);
+
+err:
+ /*
+ Release locks we have managed to acquire so far.
+ Use rollback_to_savepoint() since there may be duplicate
+ requests that got assigned the same ticket.
+ */
+ rollback_to_savepoint(mdl_svp);
+ /* Reset lock requests back to its initial state. */
+ for (req_count= p_req - sort_buf, p_req= sort_buf;
+ p_req < sort_buf + req_count; p_req++)
+ {
+ (*p_req)->ticket= NULL;
+ }
+ my_free(sort_buf);
+ DBUG_RETURN(TRUE);
+}
+
+
+/**
+ Upgrade a shared metadata lock to exclusive.
+
+ Used in ALTER TABLE, when a copy of the table with the
+ new definition has been constructed.
+
+ @param lock_wait_timeout Seconds to wait before timeout.
+
+ @note In case of failure to upgrade lock (e.g. because upgrader
+ was killed) leaves lock in its original state (locked in
+ shared mode).
+
+ @note There can be only one upgrader for a lock or we will have deadlock.
+ This invariant is ensured by the fact that upgradeable locks SNW
+ and SNRW are not compatible with each other and themselves.
+
+ @retval FALSE Success
+ @retval TRUE Failure (thread was killed)
+*/
+
+bool
+MDL_context::upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket,
+ ulong lock_wait_timeout)
+{
+ MDL_request mdl_xlock_request;
+ MDL_savepoint mdl_svp= mdl_savepoint();
+ bool is_new_ticket;
+
+ DBUG_ENTER("MDL_ticket::upgrade_shared_lock_to_exclusive");
+ DEBUG_SYNC(get_thd(), "mdl_upgrade_shared_lock_to_exclusive");
+
+ /*
+ Do nothing if already upgraded. Used when we FLUSH TABLE under
+ LOCK TABLES and a table is listed twice in LOCK TABLES list.
+ */
+ if (mdl_ticket->m_type == MDL_EXCLUSIVE)
+ DBUG_RETURN(FALSE);
+
+ /* Only allow upgrades from MDL_SHARED_NO_WRITE/NO_READ_WRITE */
+ DBUG_ASSERT(mdl_ticket->m_type == MDL_SHARED_NO_WRITE ||
+ mdl_ticket->m_type == MDL_SHARED_NO_READ_WRITE);
+
+ mdl_xlock_request.init(&mdl_ticket->m_lock->key, MDL_EXCLUSIVE,
+ MDL_TRANSACTION);
+
+ if (acquire_lock(&mdl_xlock_request, lock_wait_timeout))
+ DBUG_RETURN(TRUE);
+
+ is_new_ticket= ! has_lock(mdl_svp, mdl_xlock_request.ticket);
+
+ /* Merge the acquired and the original lock. @todo: move to a method. */
+ mysql_prlock_wrlock(&mdl_ticket->m_lock->m_rwlock);
+ if (is_new_ticket)
+ mdl_ticket->m_lock->m_granted.remove_ticket(mdl_xlock_request.ticket);
+ /*
+ Set the new type of lock in the ticket. To update state of
+ MDL_lock object correctly we need to temporarily exclude
+ ticket from the granted queue and then include it back.
+ */
+ mdl_ticket->m_lock->m_granted.remove_ticket(mdl_ticket);
+ mdl_ticket->m_type= MDL_EXCLUSIVE;
+ mdl_ticket->m_lock->m_granted.add_ticket(mdl_ticket);
+
+ mysql_prlock_unlock(&mdl_ticket->m_lock->m_rwlock);
+
+ if (is_new_ticket)
+ {
+ m_tickets[MDL_TRANSACTION].remove(mdl_xlock_request.ticket);
+ MDL_ticket::destroy(mdl_xlock_request.ticket);
+ }
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ A fragment of recursive traversal of the wait-for graph
+ in search for deadlocks. Direct the deadlock visitor to all
+ contexts that own the lock the current node in the wait-for
+ graph is waiting for.
+ As long as the initial node is remembered in the visitor,
+ a deadlock is found when the same node is seen twice.
+*/
+
+bool MDL_lock::visit_subgraph(MDL_ticket *waiting_ticket,
+ MDL_wait_for_graph_visitor *gvisitor)
+{
+ MDL_ticket *ticket;
+ MDL_context *src_ctx= waiting_ticket->get_ctx();
+ bool result= TRUE;
+
+ mysql_prlock_rdlock(&m_rwlock);
+
+ /* Must be initialized after taking a read lock. */
+ Ticket_iterator granted_it(m_granted);
+ Ticket_iterator waiting_it(m_waiting);
+
+ /*
+ MDL_lock's waiting and granted queues and MDL_context::m_waiting_for
+ member are updated by different threads when the lock is granted
+ (see MDL_context::acquire_lock() and MDL_lock::reschedule_waiters()).
+ As a result, here we may encounter a situation when MDL_lock data
+ already reflects the fact that the lock was granted but
+ m_waiting_for member has not been updated yet.
+
+ For example, imagine that:
+
+ thread1: Owns SNW lock on table t1.
+ thread2: Attempts to acquire SW lock on t1,
+ but sees an active SNW lock.
+ Thus adds the ticket to the waiting queue and
+ sets m_waiting_for to point to the ticket.
+ thread1: Releases SNW lock, updates MDL_lock object to
+ grant SW lock to thread2 (moves the ticket for
+ SW from waiting to the active queue).
+ Attempts to acquire a new SNW lock on t1,
+ sees an active SW lock (since it is present in the
+ active queue), adds ticket for SNW lock to the waiting
+ queue, sets m_waiting_for to point to this ticket.
+
+ At this point deadlock detection algorithm run by thread1 will see that:
+ - Thread1 waits for SNW lock on t1 (since m_waiting_for is set).
+ - SNW lock is not granted, because it conflicts with active SW lock
+ owned by thread 2 (since ticket for SW is present in granted queue).
+ - Thread2 waits for SW lock (since its m_waiting_for has not been
+ updated yet!).
+ - SW lock is not granted because there is pending SNW lock from thread1.
+ Therefore deadlock should exist [sic!].
+
+ To avoid detection of such false deadlocks we need to check the "actual"
+ status of the ticket being waited for, before analyzing its blockers.
+ We do this by checking the wait status of the context which is waiting
+ for it. To avoid races this has to be done under protection of
+ MDL_lock::m_rwlock lock.
+ */
+ if (src_ctx->m_wait.get_status() != MDL_wait::EMPTY)
+ {
+ result= FALSE;
+ goto end;
+ }
+
+ /*
+ To avoid visiting nodes which were already marked as victims of
+ deadlock detection (or whose requests were already satisfied) we
+ enter the node only after peeking at its wait status.
+ This is necessary to avoid active waiting in a situation
+ when previous searches for a deadlock already selected the
+ node we're about to enter as a victim (see the comment
+ in MDL_context::find_deadlock() for explanation why several searches
+ can be performed for the same wait).
+ There is no guarantee that the node isn't chosen a victim while we
+ are visiting it but this is OK: in the worst case we might do some
+ extra work and one more context might be chosen as a victim.
+ */
+ if (gvisitor->enter_node(src_ctx))
+ goto end;
+
+ /*
+ We do a breadth-first search first -- that is, inspect all
+ edges of the current node, and only then follow up to the next
+ node. In workloads that involve wait-for graph loops this
+ has proven to be a more efficient strategy [citation missing].
+ */
+ while ((ticket= granted_it++))
+ {
+ /* Filter out edges that point to the same node. */
+ if (ticket->get_ctx() != src_ctx &&
+ ticket->is_incompatible_when_granted(waiting_ticket->get_type()) &&
+ gvisitor->inspect_edge(ticket->get_ctx()))
+ {
+ goto end_leave_node;
+ }
+ }
+
+ while ((ticket= waiting_it++))
+ {
+ /* Filter out edges that point to the same node. */
+ if (ticket->get_ctx() != src_ctx &&
+ ticket->is_incompatible_when_waiting(waiting_ticket->get_type()) &&
+ gvisitor->inspect_edge(ticket->get_ctx()))
+ {
+ goto end_leave_node;
+ }
+ }
+
+ /* Recurse and inspect all adjacent nodes. */
+ granted_it.rewind();
+ while ((ticket= granted_it++))
+ {
+ if (ticket->get_ctx() != src_ctx &&
+ ticket->is_incompatible_when_granted(waiting_ticket->get_type()) &&
+ ticket->get_ctx()->visit_subgraph(gvisitor))
+ {
+ goto end_leave_node;
+ }
+ }
+
+ waiting_it.rewind();
+ while ((ticket= waiting_it++))
+ {
+ if (ticket->get_ctx() != src_ctx &&
+ ticket->is_incompatible_when_waiting(waiting_ticket->get_type()) &&
+ ticket->get_ctx()->visit_subgraph(gvisitor))
+ {
+ goto end_leave_node;
+ }
+ }
+
+ result= FALSE;
+
+end_leave_node:
+ gvisitor->leave_node(src_ctx);
+
+end:
+ mysql_prlock_unlock(&m_rwlock);
+ return result;
+}
+
+
+/**
+ Traverse a portion of wait-for graph which is reachable
+ through the edge represented by this ticket and search
+ for deadlocks.
+
+ @retval TRUE A deadlock is found. A pointer to deadlock
+ victim is saved in the visitor.
+ @retval FALSE
+*/
+
+bool MDL_ticket::accept_visitor(MDL_wait_for_graph_visitor *gvisitor)
+{
+ return m_lock->visit_subgraph(this, gvisitor);
+}
+
+
+/**
+ A fragment of recursive traversal of the wait-for graph of
+ MDL contexts in the server in search for deadlocks.
+ Assume this MDL context is a node in the wait-for graph,
+ and direct the visitor to all adjacent nodes. As long
+ as the starting node is remembered in the visitor, a
+ deadlock is found when the same node is visited twice.
+ One MDL context is connected to another in the wait-for
+ graph if it waits on a resource that is held by the other
+ context.
+
+ @retval TRUE A deadlock is found. A pointer to deadlock
+ victim is saved in the visitor.
+ @retval FALSE
+*/
+
+bool MDL_context::visit_subgraph(MDL_wait_for_graph_visitor *gvisitor)
+{
+ bool result= FALSE;
+
+ mysql_prlock_rdlock(&m_LOCK_waiting_for);
+
+ if (m_waiting_for)
+ result= m_waiting_for->accept_visitor(gvisitor);
+
+ mysql_prlock_unlock(&m_LOCK_waiting_for);
+
+ return result;
+}
+
+
+/**
+ Try to find a deadlock. This function produces no errors.
+
+ @note If during deadlock resolution context which performs deadlock
+ detection is chosen as a victim it will be informed about the
+ fact by setting VICTIM status to its wait slot.
+*/
+
+void MDL_context::find_deadlock()
+{
+ while (1)
+ {
+ /*
+ The fact that we use fresh instance of gvisitor for each
+ search performed by find_deadlock() below is important,
+ the code responsible for victim selection relies on this.
+ */
+ Deadlock_detection_visitor dvisitor(this);
+ MDL_context *victim;
+
+ if (! visit_subgraph(&dvisitor))
+ {
+ /* No deadlocks are found! */
+ break;
+ }
+
+ victim= dvisitor.get_victim();
+
+ /*
+ Failure to change status of the victim is OK as it means
+ that the victim has received some other message and is
+ about to stop its waiting/to break deadlock loop.
+ Even when the initiator of the deadlock search is
+ chosen the victim, we need to set the respective wait
+ result in order to "close" it for any attempt to
+ schedule the request.
+ This is needed to avoid a possible race during
+ cleanup in case when the lock request on which the
+ context was waiting is concurrently satisfied.
+ */
+ (void) victim->m_wait.set_status(MDL_wait::VICTIM);
+ victim->unlock_deadlock_victim();
+
+ if (victim == this)
+ break;
+ /*
+ After adding a new edge to the waiting graph we found that it
+ creates a loop (i.e. there is a deadlock). We decided to destroy
+ this loop by removing an edge, but not the one that we added.
+ Since this doesn't guarantee that all loops created by addition
+ of the new edge are destroyed, we have to repeat the search.
+ */
+ }
+}
+
+
+/**
+ Release lock.
+
+ @param duration Lock duration.
+ @param ticket Ticket for lock to be released.
+
+*/
+
+void MDL_context::release_lock(enum_mdl_duration duration, MDL_ticket *ticket)
+{
+ MDL_lock *lock= ticket->m_lock;
+ DBUG_ENTER("MDL_context::release_lock");
+ DBUG_PRINT("enter", ("db=%s name=%s", lock->key.db_name(),
+ lock->key.name()));
+
+ DBUG_ASSERT(this == ticket->get_ctx());
+ mysql_mutex_assert_not_owner(&LOCK_open);
+
+ lock->remove_ticket(&MDL_lock::m_granted, ticket);
+
+ m_tickets[duration].remove(ticket);
+ MDL_ticket::destroy(ticket);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Release lock with explicit duration.
+
+ @param ticket Ticket for lock to be released.
+
+*/
+
+void MDL_context::release_lock(MDL_ticket *ticket)
+{
+ DBUG_ASSERT(ticket->m_duration == MDL_EXPLICIT);
+
+ release_lock(MDL_EXPLICIT, ticket);
+}
+
+
+/**
+ Release all locks associated with the context. If the sentinel
+ is not NULL, do not release locks stored in the list after and
+ including the sentinel.
+
+ Statement and transactional locks are added to the beginning of
+ the corresponding lists, i.e. stored in reverse temporal order.
+ This allows to employ this function to:
+ - back off in case of a lock conflict.
+ - release all locks in the end of a statment or transaction
+ - rollback to a savepoint.
+*/
+
+void MDL_context::release_locks_stored_before(enum_mdl_duration duration,
+ MDL_ticket *sentinel)
+{
+ MDL_ticket *ticket;
+ Ticket_iterator it(m_tickets[duration]);
+ DBUG_ENTER("MDL_context::release_locks_stored_before");
+
+ if (m_tickets[duration].is_empty())
+ DBUG_VOID_RETURN;
+
+ while ((ticket= it++) && ticket != sentinel)
+ {
+ DBUG_PRINT("info", ("found lock to release ticket=%p", ticket));
+ release_lock(duration, ticket);
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Release all explicit locks in the context which correspond to the
+ same name/object as this lock request.
+
+ @param ticket One of the locks for the name/object for which all
+ locks should be released.
+*/
+
+void MDL_context::release_all_locks_for_name(MDL_ticket *name)
+{
+ /* Use MDL_ticket::m_lock to identify other locks for the same object. */
+ MDL_lock *lock= name->m_lock;
+
+ /* Remove matching lock tickets from the context. */
+ MDL_ticket *ticket;
+ Ticket_iterator it_ticket(m_tickets[MDL_EXPLICIT]);
+
+ while ((ticket= it_ticket++))
+ {
+ DBUG_ASSERT(ticket->m_lock);
+ if (ticket->m_lock == lock)
+ release_lock(MDL_EXPLICIT, ticket);
+ }
+}
+
+
+/**
+ Downgrade an exclusive lock to shared metadata lock.
+
+ @param type Type of lock to which exclusive lock should be downgraded.
+*/
+
+void MDL_ticket::downgrade_exclusive_lock(enum_mdl_type type)
+{
+ mysql_mutex_assert_not_owner(&LOCK_open);
+
+ /*
+ Do nothing if already downgraded. Used when we FLUSH TABLE under
+ LOCK TABLES and a table is listed twice in LOCK TABLES list.
+ */
+ if (m_type != MDL_EXCLUSIVE)
+ return;
+
+ mysql_prlock_wrlock(&m_lock->m_rwlock);
+ /*
+ To update state of MDL_lock object correctly we need to temporarily
+ exclude ticket from the granted queue and then include it back.
+ */
+ m_lock->m_granted.remove_ticket(this);
+ m_type= type;
+ m_lock->m_granted.add_ticket(this);
+ m_lock->reschedule_waiters();
+ mysql_prlock_unlock(&m_lock->m_rwlock);
+}
+
+
+/**
+ Auxiliary function which allows to check if we have some kind of lock on
+ a object. Returns TRUE if we have a lock of a given or stronger type.
+
+ @param mdl_namespace Id of object namespace
+ @param db Name of the database
+ @param name Name of the object
+ @param mdl_type Lock type. Pass in the weakest type to find
+ out if there is at least some lock.
+
+ @return TRUE if current context contains satisfied lock for the object,
+ FALSE otherwise.
+*/
+
+bool
+MDL_context::is_lock_owner(MDL_key::enum_mdl_namespace mdl_namespace,
+ const char *db, const char *name,
+ enum_mdl_type mdl_type)
+{
+ MDL_request mdl_request;
+ enum_mdl_duration not_unused;
+ /* We don't care about exact duration of lock here. */
+ mdl_request.init(mdl_namespace, db, name, mdl_type, MDL_TRANSACTION);
+ MDL_ticket *ticket= find_ticket(&mdl_request, &not_unused);
+
+ DBUG_ASSERT(ticket == NULL || ticket->m_lock);
+
+ return ticket;
+}
+
+
+/**
+ Check if we have any pending locks which conflict with existing shared lock.
+
+ @pre The ticket must match an acquired lock.
+
+ @return TRUE if there is a conflicting lock request, FALSE otherwise.
+*/
+
+bool MDL_ticket::has_pending_conflicting_lock() const
+{
+ return m_lock->has_pending_conflicting_lock(m_type);
+}
+
+
+/**
+ Releases metadata locks that were acquired after a specific savepoint.
+
+ @note Used to release tickets acquired during a savepoint unit.
+ @note It's safe to iterate and unlock any locks after taken after this
+ savepoint because other statements that take other special locks
+ cause a implicit commit (ie LOCK TABLES).
+*/
+
+void MDL_context::rollback_to_savepoint(const MDL_savepoint &mdl_savepoint)
+{
+ DBUG_ENTER("MDL_context::rollback_to_savepoint");
+
+ /* If savepoint is NULL, it is from the start of the transaction. */
+ release_locks_stored_before(MDL_STATEMENT, mdl_savepoint.m_stmt_ticket);
+ release_locks_stored_before(MDL_TRANSACTION, mdl_savepoint.m_trans_ticket);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Release locks acquired by normal statements (SELECT, UPDATE,
+ DELETE, etc) in the course of a transaction. Do not release
+ HANDLER locks, if there are any.
+
+ This method is used at the end of a transaction, in
+ implementation of COMMIT (implicit or explicit) and ROLLBACK.
+*/
+
+void MDL_context::release_transactional_locks()
+{
+ DBUG_ENTER("MDL_context::release_transactional_locks");
+ release_locks_stored_before(MDL_STATEMENT, NULL);
+ release_locks_stored_before(MDL_TRANSACTION, NULL);
+ DBUG_VOID_RETURN;
+}
+
+
+void MDL_context::release_statement_locks()
+{
+ DBUG_ENTER("MDL_context::release_transactional_locks");
+ release_locks_stored_before(MDL_STATEMENT, NULL);
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Does this savepoint have this lock?
+
+ @retval TRUE The ticket is older than the savepoint or
+ is an LT, HA or GLR ticket. Thus it belongs
+ to the savepoint or has explicit duration.
+ @retval FALSE The ticket is newer than the savepoint.
+ and is not an LT, HA or GLR ticket.
+*/
+
+bool MDL_context::has_lock(const MDL_savepoint &mdl_savepoint,
+ MDL_ticket *mdl_ticket)
+{
+ MDL_ticket *ticket;
+ /* Start from the beginning, most likely mdl_ticket's been just acquired. */
+ MDL_context::Ticket_iterator s_it(m_tickets[MDL_STATEMENT]);
+ MDL_context::Ticket_iterator t_it(m_tickets[MDL_TRANSACTION]);
+
+ while ((ticket= s_it++) && ticket != mdl_savepoint.m_stmt_ticket)
+ {
+ if (ticket == mdl_ticket)
+ return FALSE;
+ }
+
+ while ((ticket= t_it++) && ticket != mdl_savepoint.m_trans_ticket)
+ {
+ if (ticket == mdl_ticket)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/**
+ Change lock duration for transactional lock.
+
+ @param ticket Ticket representing lock.
+ @param duration Lock duration to be set.
+
+ @note This method only supports changing duration of
+ transactional lock to some other duration.
+*/
+
+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);
+
+ m_tickets[MDL_TRANSACTION].remove(mdl_ticket);
+ m_tickets[duration].push_front(mdl_ticket);
+#ifndef DBUG_OFF
+ mdl_ticket->m_duration= duration;
+#endif
+}
+
+
+/**
+ Set explicit duration for all locks in the context.
+*/
+
+void MDL_context::set_explicit_duration_for_all_locks()
+{
+ int i;
+ MDL_ticket *ticket;
+
+ /*
+ In the most common case when this function is called list
+ of transactional locks is bigger than list of locks with
+ explicit duration. So we start by swapping these two lists
+ and then move elements from new list of transactional
+ locks and list of statement locks to list of locks with
+ explicit duration.
+ */
+
+ m_tickets[MDL_EXPLICIT].swap(m_tickets[MDL_TRANSACTION]);
+
+ for (i= 0; i < MDL_EXPLICIT; i++)
+ {
+ Ticket_iterator it_ticket(m_tickets[i]);
+
+ while ((ticket= it_ticket++))
+ {
+ m_tickets[i].remove(ticket);
+ m_tickets[MDL_EXPLICIT].push_front(ticket);
+ }
+ }
+
+#ifndef DBUG_OFF
+ Ticket_iterator exp_it(m_tickets[MDL_EXPLICIT]);
+
+ while ((ticket= exp_it++))
+ ticket->m_duration= MDL_EXPLICIT;
+#endif
+}
+
+
+/**
+ Set transactional duration for all locks in the context.
+*/
+
+void MDL_context::set_transaction_duration_for_all_locks()
+{
+ MDL_ticket *ticket;
+
+ /*
+ In the most common case when this function is called list
+ of explicit locks is bigger than two other lists (in fact,
+ list of statement locks is always empty). So we start by
+ swapping list of explicit and transactional locks and then
+ move contents of new list of explicit locks to list of
+ locks with transactional duration.
+ */
+
+ DBUG_ASSERT(m_tickets[MDL_STATEMENT].is_empty());
+
+ m_tickets[MDL_TRANSACTION].swap(m_tickets[MDL_EXPLICIT]);
+
+ Ticket_iterator it_ticket(m_tickets[MDL_EXPLICIT]);
+
+ while ((ticket= it_ticket++))
+ {
+ m_tickets[MDL_EXPLICIT].remove(ticket);
+ m_tickets[MDL_TRANSACTION].push_front(ticket);
+ }
+
+#ifndef DBUG_OFF
+ Ticket_iterator trans_it(m_tickets[MDL_TRANSACTION]);
+
+ while ((ticket= trans_it++))
+ ticket->m_duration= MDL_TRANSACTION;
+#endif
+}
diff --git a/sql/mdl.h b/sql/mdl.h
new file mode 100644
index 00000000000..68f24a7a0e8
--- /dev/null
+++ b/sql/mdl.h
@@ -0,0 +1,875 @@
+#ifndef MDL_H
+#define MDL_H
+/* Copyright (c) 2009, 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-1301 USA */
+
+#if defined(__IBMC__) || defined(__IBMCPP__)
+/* Further down, "next_in_lock" and "next_in_context" have the same type,
+ and in "sql_plist.h" this leads to an identical signature, which causes
+ problems in function overloading.
+*/
+#pragma namemangling(v5)
+#endif
+
+
+#include "sql_plist.h"
+#include <my_sys.h>
+#include <my_pthread.h>
+#include <m_string.h>
+#include <mysql_com.h>
+
+class THD;
+
+class MDL_context;
+class MDL_lock;
+class MDL_ticket;
+
+/**
+ Type of metadata lock request.
+
+ @sa Comments for MDL_object_lock::can_grant_lock() and
+ MDL_scoped_lock::can_grant_lock() for details.
+*/
+
+enum enum_mdl_type {
+ /*
+ An intention exclusive metadata lock. Used only for scoped locks.
+ Owner of this type of lock can acquire upgradable exclusive locks on
+ individual objects.
+ Compatible with other IX locks, but is incompatible with scoped S and
+ X locks.
+ */
+ MDL_INTENTION_EXCLUSIVE= 0,
+ /*
+ A shared metadata lock.
+ To be used in cases when we are interested in object metadata only
+ and there is no intention to access object data (e.g. for stored
+ routines or during preparing prepared statements).
+ We also mis-use this type of lock for open HANDLERs, since lock
+ acquired by this statement has to be compatible with lock acquired
+ by LOCK TABLES ... WRITE statement, i.e. SNRW (We can't get by by
+ acquiring S lock at HANDLER ... OPEN time and upgrading it to SR
+ lock for HANDLER ... READ as it doesn't solve problem with need
+ to abort DML statements which wait on table level lock while having
+ open HANDLER in the same connection).
+ To avoid deadlock which may occur when SNRW lock is being upgraded to
+ X lock for table on which there is an active S lock which is owned by
+ thread which waits in its turn for table-level lock owned by thread
+ performing upgrade we have to use thr_abort_locks_for_thread()
+ facility in such situation.
+ This problem does not arise for locks on stored routines as we don't
+ use SNRW locks for them. It also does not arise when S locks are used
+ during PREPARE calls as table-level locks are not acquired in this
+ case.
+ */
+ MDL_SHARED,
+ /*
+ A high priority shared metadata lock.
+ Used for cases when there is no intention to access object data (i.e.
+ data in the table).
+ "High priority" means that, unlike other shared locks, it is granted
+ ignoring pending requests for exclusive locks. Intended for use in
+ cases when we only need to access metadata and not data, e.g. when
+ filling an INFORMATION_SCHEMA table.
+ Since SH lock is compatible with SNRW lock, the connection that
+ holds SH lock lock should not try to acquire any kind of table-level
+ or row-level lock, as this can lead to a deadlock. Moreover, after
+ acquiring SH lock, the connection should not wait for any other
+ resource, as it might cause starvation for X locks and a potential
+ deadlock during upgrade of SNW or SNRW to X lock (e.g. if the
+ upgrading connection holds the resource that is being waited for).
+ */
+ MDL_SHARED_HIGH_PRIO,
+ /*
+ A shared metadata lock for cases when there is an intention to read data
+ from table.
+ A connection holding this kind of lock can read table metadata and read
+ table data (after acquiring appropriate table and row-level locks).
+ This means that one can only acquire TL_READ, TL_READ_NO_INSERT, and
+ similar table-level locks on table if one holds SR MDL lock on it.
+ To be used for tables in SELECTs, subqueries, and LOCK TABLE ... READ
+ statements.
+ */
+ MDL_SHARED_READ,
+ /*
+ A shared metadata lock for cases when there is an intention to modify
+ (and not just read) data in the table.
+ A connection holding SW lock can read table metadata and modify or read
+ table data (after acquiring appropriate table and row-level locks).
+ To be used for tables to be modified by INSERT, UPDATE, DELETE
+ statements, but not LOCK TABLE ... WRITE or DDL). Also taken by
+ SELECT ... FOR UPDATE.
+ */
+ MDL_SHARED_WRITE,
+ /*
+ An upgradable shared metadata lock which blocks all attempts to update
+ table data, allowing reads.
+ A connection holding this kind of lock can read table metadata and read
+ table data.
+ Can be upgraded to X metadata lock.
+ Note, that since this type of lock is not compatible with SNRW or SW
+ lock types, acquiring appropriate engine-level locks for reading
+ (TL_READ* for MyISAM, shared row locks in InnoDB) should be
+ contention-free.
+ To be used for the first phase of ALTER TABLE, when copying data between
+ tables, to allow concurrent SELECTs from the table, but not UPDATEs.
+ */
+ MDL_SHARED_NO_WRITE,
+ /*
+ An upgradable shared metadata lock which allows other connections
+ to access table metadata, but not data.
+ It blocks all attempts to read or update table data, while allowing
+ INFORMATION_SCHEMA and SHOW queries.
+ A connection holding this kind of lock can read table metadata modify and
+ read table data.
+ Can be upgraded to X metadata lock.
+ To be used for LOCK TABLES WRITE statement.
+ Not compatible with any other lock type except S and SH.
+ */
+ MDL_SHARED_NO_READ_WRITE,
+ /*
+ An exclusive metadata lock.
+ A connection holding this lock can modify both table's metadata and data.
+ No other type of metadata lock can be granted while this lock is held.
+ To be used for CREATE/DROP/RENAME TABLE statements and for execution of
+ certain phases of other DDL statements.
+ */
+ MDL_EXCLUSIVE,
+ /* This should be the last !!! */
+ MDL_TYPE_END};
+
+
+/** Duration of metadata lock. */
+
+enum enum_mdl_duration {
+ /**
+ Locks with statement duration are automatically released at the end
+ of statement or transaction.
+ */
+ MDL_STATEMENT= 0,
+ /**
+ Locks with transaction duration are automatically released at the end
+ of transaction.
+ */
+ MDL_TRANSACTION,
+ /**
+ Locks with explicit duration survive the end of statement and transaction.
+ They have to be released explicitly by calling MDL_context::release_lock().
+ */
+ MDL_EXPLICIT,
+ /* This should be the last ! */
+ MDL_DURATION_END };
+
+
+/** Maximal length of key for metadata locking subsystem. */
+#define MAX_MDLKEY_LENGTH (1 + NAME_LEN + 1 + NAME_LEN + 1)
+
+
+/**
+ Metadata lock object key.
+
+ A lock is requested or granted based on a fully qualified name and type.
+ E.g. They key for a table consists of <0 (=table)>+<database>+<table name>.
+ Elsewhere in the comments this triple will be referred to simply as "key"
+ or "name".
+*/
+
+class MDL_key
+{
+public:
+ /**
+ Object namespaces.
+ Sic: when adding a new member to this enum make sure to
+ update m_namespace_to_wait_state_name array in mdl.cc!
+
+ Different types of objects exist in different namespaces
+ - TABLE is for tables and views.
+ - FUNCTION is for stored functions.
+ - PROCEDURE is for stored procedures.
+ - TRIGGER is for triggers.
+ - EVENT is for event scheduler events
+ Note that although there isn't metadata locking on triggers,
+ it's necessary to have a separate namespace for them since
+ MDL_key is also used outside of the MDL subsystem.
+ */
+ enum enum_mdl_namespace { GLOBAL=0,
+ SCHEMA,
+ TABLE,
+ FUNCTION,
+ PROCEDURE,
+ TRIGGER,
+ EVENT,
+ COMMIT,
+ /* This should be the last ! */
+ NAMESPACE_END };
+
+ const uchar *ptr() const { return (uchar*) m_ptr; }
+ uint length() const { return m_length; }
+
+ const char *db_name() const { return m_ptr + 1; }
+ uint db_name_length() const { return m_db_name_length; }
+
+ const char *name() const { return m_ptr + m_db_name_length + 2; }
+ uint name_length() const { return m_length - m_db_name_length - 3; }
+
+ enum_mdl_namespace mdl_namespace() const
+ { return (enum_mdl_namespace)(m_ptr[0]); }
+
+ /**
+ Construct a metadata lock key from a triplet (mdl_namespace,
+ database and name).
+
+ @remark The key for a table is <mdl_namespace>+<database name>+<table name>
+
+ @param mdl_namespace Id of namespace of object to be locked
+ @param db Name of database to which the object belongs
+ @param name Name of of the object
+ @param key Where to store the the MDL key.
+ */
+ void mdl_key_init(enum_mdl_namespace mdl_namespace,
+ const char *db, const char *name)
+ {
+ m_ptr[0]= (char) mdl_namespace;
+ /*
+ It is responsibility of caller to ensure that db and object names
+ are not longer than NAME_LEN. Still we play safe and try to avoid
+ buffer overruns.
+ */
+ m_db_name_length= (uint16) (strmake(m_ptr + 1, db, NAME_LEN) - m_ptr - 1);
+ m_length= (uint16) (strmake(m_ptr + m_db_name_length + 2, name, NAME_LEN) -
+ m_ptr + 1);
+ }
+ void mdl_key_init(const MDL_key *rhs)
+ {
+ memcpy(m_ptr, rhs->m_ptr, rhs->m_length);
+ m_length= rhs->m_length;
+ m_db_name_length= rhs->m_db_name_length;
+ }
+ bool is_equal(const MDL_key *rhs) const
+ {
+ return (m_length == rhs->m_length &&
+ memcmp(m_ptr, rhs->m_ptr, m_length) == 0);
+ }
+ /**
+ Compare two MDL keys lexicographically.
+ */
+ int cmp(const MDL_key *rhs) const
+ {
+ /*
+ The key buffer is always '\0'-terminated. Since key
+ character set is utf-8, we can safely assume that no
+ character starts with a zero byte.
+ */
+ return memcmp(m_ptr, rhs->m_ptr, min(m_length, rhs->m_length));
+ }
+
+ MDL_key(const MDL_key *rhs)
+ {
+ mdl_key_init(rhs);
+ }
+ MDL_key(enum_mdl_namespace namespace_arg,
+ const char *db_arg, const char *name_arg)
+ {
+ mdl_key_init(namespace_arg, db_arg, name_arg);
+ }
+ MDL_key() {} /* To use when part of MDL_request. */
+
+ /**
+ Get thread state name to be used in case when we have to
+ wait on resource identified by key.
+ */
+ const char * get_wait_state_name() const
+ {
+ return m_namespace_to_wait_state_name[(int)mdl_namespace()];
+ }
+
+private:
+ uint16 m_length;
+ uint16 m_db_name_length;
+ char m_ptr[MAX_MDLKEY_LENGTH];
+ static const char * m_namespace_to_wait_state_name[NAMESPACE_END];
+private:
+ MDL_key(const MDL_key &); /* not implemented */
+ MDL_key &operator=(const MDL_key &); /* not implemented */
+};
+
+
+/**
+ A pending metadata lock request.
+
+ A lock request and a granted metadata lock are represented by
+ different classes because they have different allocation
+ sites and hence different lifetimes. The allocation of lock requests is
+ controlled from outside of the MDL subsystem, while allocation of granted
+ locks (tickets) is controlled within the MDL subsystem.
+
+ MDL_request is a C structure, you don't need to call a constructor
+ or destructor for it.
+*/
+
+class MDL_request
+{
+public:
+ /** Type of metadata lock. */
+ enum enum_mdl_type type;
+ /** Duration for requested lock. */
+ enum enum_mdl_duration duration;
+
+ /**
+ Pointers for participating in the list of lock requests for this context.
+ */
+ MDL_request *next_in_list;
+ MDL_request **prev_in_list;
+ /**
+ Pointer to the lock ticket object for this lock request.
+ Valid only if this lock request is satisfied.
+ */
+ MDL_ticket *ticket;
+
+ /** A lock is requested based on a fully qualified name and type. */
+ MDL_key key;
+
+public:
+ static void *operator new(size_t size, MEM_ROOT *mem_root) throw ()
+ { return alloc_root(mem_root, size); }
+ static void operator delete(void *ptr, MEM_ROOT *mem_root) {}
+
+ void init(MDL_key::enum_mdl_namespace namespace_arg,
+ const char *db_arg, const char *name_arg,
+ enum_mdl_type mdl_type_arg,
+ enum_mdl_duration mdl_duration_arg);
+ void init(const MDL_key *key_arg, enum_mdl_type mdl_type_arg,
+ enum_mdl_duration mdl_duration_arg);
+ /** Set type of lock request. Can be only applied to pending locks. */
+ inline void set_type(enum_mdl_type type_arg)
+ {
+ DBUG_ASSERT(ticket == NULL);
+ type= type_arg;
+ }
+
+ /*
+ This is to work around the ugliness of TABLE_LIST
+ compiler-generated assignment operator. It is currently used
+ in several places to quickly copy "most" of the members of the
+ table list. These places currently never assume that the mdl
+ request is carried over to the new TABLE_LIST, or shared
+ between lists.
+
+ This method does not initialize the instance being assigned!
+ Use of init() for initialization after this assignment operator
+ is mandatory. Can only be used before the request has been
+ granted.
+ */
+ MDL_request& operator=(const MDL_request &rhs)
+ {
+ ticket= NULL;
+ /* Do nothing, in particular, don't try to copy the key. */
+ return *this;
+ }
+ /* Another piece of ugliness for TABLE_LIST constructor */
+ MDL_request() {}
+
+ MDL_request(const MDL_request *rhs)
+ :type(rhs->type),
+ duration(rhs->duration),
+ ticket(NULL),
+ key(&rhs->key)
+ {}
+};
+
+
+typedef void (*mdl_cached_object_release_hook)(void *);
+
+
+/**
+ An abstract class for inspection of a connected
+ subgraph of the wait-for graph.
+*/
+
+class MDL_wait_for_graph_visitor
+{
+public:
+ virtual bool enter_node(MDL_context *node) = 0;
+ virtual void leave_node(MDL_context *node) = 0;
+
+ virtual bool inspect_edge(MDL_context *dest) = 0;
+ virtual ~MDL_wait_for_graph_visitor();
+ MDL_wait_for_graph_visitor() :m_lock_open_count(0) {}
+public:
+ /**
+ XXX, hack: During deadlock search, we may need to
+ inspect TABLE_SHAREs and acquire LOCK_open. Since
+ LOCK_open is not a recursive mutex, count here how many
+ times we "took" it (but only take and release once).
+ Not using a native recursive mutex or rwlock in 5.5 for
+ LOCK_open since it has significant performance impacts.
+ */
+ uint m_lock_open_count;
+};
+
+/**
+ Abstract class representing an edge in the waiters graph
+ to be traversed by deadlock detection algorithm.
+*/
+
+class MDL_wait_for_subgraph
+{
+public:
+ virtual ~MDL_wait_for_subgraph();
+
+ /**
+ Accept a wait-for graph visitor to inspect the node
+ this edge is leading to.
+ */
+ virtual bool accept_visitor(MDL_wait_for_graph_visitor *gvisitor) = 0;
+
+ enum enum_deadlock_weight
+ {
+ DEADLOCK_WEIGHT_DML= 0,
+ DEADLOCK_WEIGHT_DDL= 100
+ };
+ /* A helper used to determine which lock request should be aborted. */
+ virtual uint get_deadlock_weight() const = 0;
+};
+
+
+/**
+ A granted metadata lock.
+
+ @warning MDL_ticket members are private to the MDL subsystem.
+
+ @note Multiple shared locks on a same object are represented by a
+ single ticket. The same does not apply for other lock types.
+
+ @note There are two groups of MDL_ticket members:
+ - "Externally accessible". These members can be accessed from
+ threads/contexts different than ticket owner in cases when
+ ticket participates in some list of granted or waiting tickets
+ for a lock. Therefore one should change these members before
+ including then to waiting/granted lists or while holding lock
+ protecting those lists.
+ - "Context private". Such members are private to thread/context
+ owning this ticket. I.e. they should not be accessed from other
+ threads/contexts.
+*/
+
+class MDL_ticket : public MDL_wait_for_subgraph
+{
+public:
+ /**
+ Pointers for participating in the list of lock requests for this context.
+ Context private.
+ */
+ MDL_ticket *next_in_context;
+ MDL_ticket **prev_in_context;
+ /**
+ Pointers for participating in the list of satisfied/pending requests
+ for the lock. Externally accessible.
+ */
+ MDL_ticket *next_in_lock;
+ MDL_ticket **prev_in_lock;
+public:
+ bool has_pending_conflicting_lock() const;
+
+ MDL_context *get_ctx() const { return m_ctx; }
+ bool is_upgradable_or_exclusive() const
+ {
+ return m_type == MDL_SHARED_NO_WRITE ||
+ m_type == MDL_SHARED_NO_READ_WRITE ||
+ m_type == MDL_EXCLUSIVE;
+ }
+ enum_mdl_type get_type() const { return m_type; }
+ MDL_lock *get_lock() const { return m_lock; }
+ void downgrade_exclusive_lock(enum_mdl_type type);
+
+ bool has_stronger_or_equal_type(enum_mdl_type type) const;
+
+ bool is_incompatible_when_granted(enum_mdl_type type) const;
+ bool is_incompatible_when_waiting(enum_mdl_type type) const;
+
+ /** Implement MDL_wait_for_subgraph interface. */
+ virtual bool accept_visitor(MDL_wait_for_graph_visitor *dvisitor);
+ virtual uint get_deadlock_weight() const;
+private:
+ friend class MDL_context;
+
+ MDL_ticket(MDL_context *ctx_arg, enum_mdl_type type_arg
+#ifndef DBUG_OFF
+ , enum_mdl_duration duration_arg
+#endif
+ )
+ : m_type(type_arg),
+#ifndef DBUG_OFF
+ m_duration(duration_arg),
+#endif
+ m_ctx(ctx_arg),
+ m_lock(NULL)
+ {}
+
+ static MDL_ticket *create(MDL_context *ctx_arg, enum_mdl_type type_arg
+#ifndef DBUG_OFF
+ , enum_mdl_duration duration_arg
+#endif
+ );
+ static void destroy(MDL_ticket *ticket);
+private:
+ /** Type of metadata lock. Externally accessible. */
+ enum enum_mdl_type m_type;
+#ifndef DBUG_OFF
+ /**
+ Duration of lock represented by this ticket.
+ Context private. Debug-only.
+ */
+ enum_mdl_duration m_duration;
+#endif
+ /**
+ Context of the owner of the metadata lock ticket. Externally accessible.
+ */
+ MDL_context *m_ctx;
+
+ /**
+ Pointer to the lock object for this lock ticket. Externally accessible.
+ */
+ MDL_lock *m_lock;
+
+private:
+ MDL_ticket(const MDL_ticket &); /* not implemented */
+ MDL_ticket &operator=(const MDL_ticket &); /* not implemented */
+};
+
+
+/**
+ Savepoint for MDL context.
+
+ Doesn't include metadata locks with explicit duration as
+ they are not released during rollback to savepoint.
+*/
+
+class MDL_savepoint
+{
+public:
+ MDL_savepoint() {};
+
+private:
+ MDL_savepoint(MDL_ticket *stmt_ticket, MDL_ticket *trans_ticket)
+ : m_stmt_ticket(stmt_ticket), m_trans_ticket(trans_ticket)
+ {}
+
+ friend class MDL_context;
+
+private:
+ /**
+ Pointer to last lock with statement duration which was taken
+ before creation of savepoint.
+ */
+ MDL_ticket *m_stmt_ticket;
+ /**
+ Pointer to last lock with transaction duration which was taken
+ before creation of savepoint.
+ */
+ MDL_ticket *m_trans_ticket;
+};
+
+
+/**
+ A reliable way to wait on an MDL lock.
+*/
+
+class MDL_wait
+{
+public:
+ MDL_wait();
+ ~MDL_wait();
+
+ enum enum_wait_status { EMPTY = 0, GRANTED, VICTIM, TIMEOUT, KILLED };
+
+ bool set_status(enum_wait_status result_arg);
+ enum_wait_status get_status();
+ void reset_status();
+ enum_wait_status timed_wait(THD *thd, struct timespec *abs_timeout,
+ bool signal_timeout, const char *wait_state_name);
+private:
+ /**
+ Condvar which is used for waiting until this context's pending
+ request can be satisfied or this thread has to perform actions
+ to resolve a potential deadlock (we subscribe to such
+ notification by adding a ticket corresponding to the request
+ to an appropriate queue of waiters).
+ */
+ mysql_mutex_t m_LOCK_wait_status;
+ mysql_cond_t m_COND_wait_status;
+ enum_wait_status m_wait_status;
+};
+
+
+typedef I_P_List<MDL_request, I_P_List_adapter<MDL_request,
+ &MDL_request::next_in_list,
+ &MDL_request::prev_in_list>,
+ I_P_List_counter>
+ MDL_request_list;
+
+/**
+ Context of the owner of metadata locks. I.e. each server
+ connection has such a context.
+*/
+
+class MDL_context
+{
+public:
+ typedef I_P_List<MDL_ticket,
+ I_P_List_adapter<MDL_ticket,
+ &MDL_ticket::next_in_context,
+ &MDL_ticket::prev_in_context> >
+ Ticket_list;
+
+ typedef Ticket_list::Iterator Ticket_iterator;
+
+ MDL_context();
+ void destroy();
+
+ bool try_acquire_lock(MDL_request *mdl_request);
+ bool acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout);
+ bool acquire_locks(MDL_request_list *requests, ulong lock_wait_timeout);
+ bool upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket,
+ ulong lock_wait_timeout);
+
+ bool clone_ticket(MDL_request *mdl_request);
+
+ void release_all_locks_for_name(MDL_ticket *ticket);
+ void release_lock(MDL_ticket *ticket);
+
+ bool is_lock_owner(MDL_key::enum_mdl_namespace mdl_namespace,
+ const char *db, const char *name,
+ enum_mdl_type mdl_type);
+
+ bool has_lock(const MDL_savepoint &mdl_savepoint, MDL_ticket *mdl_ticket);
+
+ inline bool has_locks() const
+ {
+ return !(m_tickets[MDL_STATEMENT].is_empty() &&
+ m_tickets[MDL_TRANSACTION].is_empty() &&
+ m_tickets[MDL_EXPLICIT].is_empty());
+ }
+
+ MDL_savepoint mdl_savepoint()
+ {
+ return MDL_savepoint(m_tickets[MDL_STATEMENT].front(),
+ m_tickets[MDL_TRANSACTION].front());
+ }
+
+ void set_explicit_duration_for_all_locks();
+ void set_transaction_duration_for_all_locks();
+ void set_lock_duration(MDL_ticket *mdl_ticket, enum_mdl_duration duration);
+
+ void release_statement_locks();
+ void release_transactional_locks();
+ void rollback_to_savepoint(const MDL_savepoint &mdl_savepoint);
+
+ inline THD *get_thd() const { return m_thd; }
+
+ /** @pre Only valid if we started waiting for lock. */
+ inline uint get_deadlock_weight() const
+ { return m_waiting_for->get_deadlock_weight(); }
+ /**
+ Post signal to the context (and wake it up if necessary).
+
+ @retval FALSE - Success, signal was posted.
+ @retval TRUE - Failure, signal was not posted since context
+ already has received some signal or closed
+ signal slot.
+ */
+ void init(THD *thd_arg) { m_thd= thd_arg; }
+
+ void set_needs_thr_lock_abort(bool needs_thr_lock_abort)
+ {
+ /*
+ @note In theory, this member should be modified under protection
+ of some lock since it can be accessed from different threads.
+ In practice, this is not necessary as code which reads this
+ value and so might miss the fact that value was changed will
+ always re-try reading it after small timeout and therefore
+ will see the new value eventually.
+ */
+ m_needs_thr_lock_abort= needs_thr_lock_abort;
+ }
+ bool get_needs_thr_lock_abort() const
+ {
+ return m_needs_thr_lock_abort;
+ }
+public:
+ /**
+ If our request for a lock is scheduled, or aborted by the deadlock
+ detector, the result is recorded in this class.
+ */
+ MDL_wait m_wait;
+private:
+ /**
+ Lists of all MDL tickets acquired by this connection.
+
+ Lists of MDL tickets:
+ ---------------------
+ The entire set of locks acquired by a connection can be separated
+ in three subsets according to their: locks released at the end of
+ statement, at the end of transaction and locks are released
+ explicitly.
+
+ Statement and transactional locks are locks with automatic scope.
+ They are accumulated in the course of a transaction, and released
+ either at the end of uppermost statement (for statement locks) or
+ on COMMIT, ROLLBACK or ROLLBACK TO SAVEPOINT (for transactional
+ locks). They must not be (and never are) released manually,
+ i.e. with release_lock() call.
+
+ Locks with explicit duration are taken for locks that span
+ multiple transactions or savepoints.
+ These are: HANDLER SQL locks (HANDLER SQL is
+ transaction-agnostic), LOCK TABLES locks (you can COMMIT/etc
+ under LOCK TABLES, and the locked tables stay locked), and
+ locks implementing "global read lock".
+
+ Statement/transactional locks are always prepended to the
+ beginning of the appropriate list. In other words, they are
+ stored in reverse temporal order. Thus, when we rollback to
+ a savepoint, we start popping and releasing tickets from the
+ front until we reach the last ticket acquired after the savepoint.
+
+ Locks with explicit duration stored are not stored in any
+ particular order, and among each other can be split into
+ three sets:
+
+ [LOCK TABLES locks] [HANDLER locks] [GLOBAL READ LOCK locks]
+
+ The following is known about these sets:
+
+ * GLOBAL READ LOCK locks are always stored after LOCK TABLES
+ locks and after HANDLER locks. This is because one can't say
+ SET GLOBAL read_only=1 or FLUSH TABLES WITH READ LOCK
+ if one has locked tables. One can, however, LOCK TABLES
+ after having entered the read only mode. Note, that
+ subsequent LOCK TABLES statement will unlock the previous
+ set of tables, but not the GRL!
+ There are no HANDLER locks after GRL locks because
+ SET GLOBAL read_only performs a FLUSH TABLES WITH
+ READ LOCK internally, and FLUSH TABLES, in turn, implicitly
+ closes all open HANDLERs.
+ However, one can open a few HANDLERs after entering the
+ read only mode.
+ * LOCK TABLES locks include intention exclusive locks on
+ involved schemas and global intention exclusive lock.
+ */
+ Ticket_list m_tickets[MDL_DURATION_END];
+ THD *m_thd;
+ /**
+ TRUE - if for this context we will break protocol and try to
+ acquire table-level locks while having only S lock on
+ some table.
+ To avoid deadlocks which might occur during concurrent
+ upgrade of SNRW lock on such object to X lock we have to
+ abort waits for table-level locks for such connections.
+ FALSE - Otherwise.
+ */
+ bool m_needs_thr_lock_abort;
+
+ /**
+ Read-write lock protecting m_waiting_for member.
+
+ @note The fact that this read-write lock prefers readers is
+ important as deadlock detector won't work correctly
+ otherwise. @sa Comment for MDL_lock::m_rwlock.
+ */
+ mysql_prlock_t m_LOCK_waiting_for;
+ /**
+ Tell the deadlock detector what metadata lock or table
+ definition cache entry this session is waiting for.
+ In principle, this is redundant, as information can be found
+ by inspecting waiting queues, but we'd very much like it to be
+ readily available to the wait-for graph iterator.
+ */
+ MDL_wait_for_subgraph *m_waiting_for;
+private:
+ MDL_ticket *find_ticket(MDL_request *mdl_req,
+ enum_mdl_duration *duration);
+ void release_locks_stored_before(enum_mdl_duration duration, MDL_ticket *sentinel);
+ void release_lock(enum_mdl_duration duration, MDL_ticket *ticket);
+ bool try_acquire_lock_impl(MDL_request *mdl_request,
+ MDL_ticket **out_ticket);
+
+public:
+ void find_deadlock();
+
+ bool visit_subgraph(MDL_wait_for_graph_visitor *dvisitor);
+
+ /** Inform the deadlock detector there is an edge in the wait-for graph. */
+ void will_wait_for(MDL_wait_for_subgraph *waiting_for_arg)
+ {
+ mysql_prlock_wrlock(&m_LOCK_waiting_for);
+ m_waiting_for= waiting_for_arg;
+ mysql_prlock_unlock(&m_LOCK_waiting_for);
+ }
+
+ /** Remove the wait-for edge from the graph after we're done waiting. */
+ void done_waiting_for()
+ {
+ mysql_prlock_wrlock(&m_LOCK_waiting_for);
+ m_waiting_for= NULL;
+ mysql_prlock_unlock(&m_LOCK_waiting_for);
+ }
+ void lock_deadlock_victim()
+ {
+ mysql_prlock_rdlock(&m_LOCK_waiting_for);
+ }
+ void unlock_deadlock_victim()
+ {
+ mysql_prlock_unlock(&m_LOCK_waiting_for);
+ }
+private:
+ MDL_context(const MDL_context &rhs); /* not implemented */
+ MDL_context &operator=(MDL_context &rhs); /* not implemented */
+};
+
+
+void mdl_init();
+void mdl_destroy();
+
+
+/*
+ Functions in the server's kernel used by metadata locking subsystem.
+*/
+
+extern bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use,
+ bool needs_thr_lock_abort);
+extern "C" const char* thd_enter_cond(MYSQL_THD thd, mysql_cond_t *cond,
+ mysql_mutex_t *mutex, const char *msg);
+extern "C" void thd_exit_cond(MYSQL_THD thd, const char *old_msg);
+
+#ifndef DBUG_OFF
+extern mysql_mutex_t LOCK_open;
+#endif
+
+
+/*
+ Start-up parameter for the maximum size of the unused MDL_lock objects cache
+ and a constant for its default value.
+*/
+extern ulong mdl_locks_cache_size;
+static const ulong MDL_LOCKS_CACHE_SIZE_DEFAULT = 1024;
+
+/*
+ Metadata locking subsystem tries not to grant more than
+ max_write_lock_count high-prio, strong locks successively,
+ to avoid starving out weak, low-prio locks.
+*/
+extern "C" ulong max_write_lock_count;
+#endif
diff --git a/sql/message.h b/sql/message.h
index f0acc03b9bb..dac0576d0c4 100644
--- a/sql/message.h
+++ b/sql/message.h
@@ -1,4 +1,6 @@
-/* Copyright (c) 2008 Sun Microsystems, Inc.
+#ifndef MESSAGE_INCLUDED
+#define MESSAGE_INCLUDED
+/* Copyright (c) 2008, 2009 Sun Microsystems, Inc.
Use is subject to license terms.
This program is free software; you can redistribute it and/or modify
@@ -22,6 +24,8 @@
mc.exe can be installed with Windows SDK, some Visual Studio distributions
do not include it.
*/
+
+
//
// Values are 32 bit values layed out as follows:
//
@@ -69,3 +73,5 @@
//
#define MSG_DEFAULT 0xC0000064L
+#endif /* MESSAGE_INCLUDED */
+
diff --git a/sql/mf_iocache.cc b/sql/mf_iocache.cc
index 22aae59ca08..d8848c1ee35 100644
--- a/sql/mf_iocache.cc
+++ b/sql/mf_iocache.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000-2004, 2006, 2007 MySQL AB
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,7 +11,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@file
@@ -32,7 +32,8 @@
flush_io_cache().
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "sql_class.h" // THD
#ifdef HAVE_REPLICATION
extern "C" {
diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc
index eb1de09e056..2479bc3258c 100644
--- a/sql/multi_range_read.cc
+++ b/sql/multi_range_read.cc
@@ -1,6 +1,22 @@
-#include "mysql_priv.h"
+/* Copyright (C) 2010, 2011 Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "sql_parse.h"
#include <my_bit.h>
#include "sql_select.h"
+#include "key.h"
/****************************************************************************
* Default MRR implementation (MRR to non-MRR converter)
@@ -1115,6 +1131,7 @@ void DsMrr_impl::close_second_handler()
{
if (secondary_file)
{
+ secondary_file->extra(HA_EXTRA_NO_KEYREAD);
secondary_file->ha_index_or_rnd_end();
secondary_file->ha_external_lock(current_thd, F_UNLCK);
secondary_file->ha_close();
@@ -1405,7 +1422,7 @@ ha_rows DsMrr_impl::dsmrr_info(uint keyno, uint n_ranges, uint rows,
uint key_parts,
uint *bufsz, uint *flags, COST_VECT *cost)
{
- ha_rows res;
+ ha_rows res __attribute__((unused));
uint def_flags= *flags;
uint def_bufsz= *bufsz;
@@ -1494,10 +1511,10 @@ ha_rows DsMrr_impl::dsmrr_info_const(uint keyno, RANGE_SEQ_IF *seq,
@retval FALSE No
*/
-bool key_uses_partial_cols(TABLE *table, uint keyno)
+bool key_uses_partial_cols(TABLE_SHARE *share, uint keyno)
{
- KEY_PART_INFO *kp= table->key_info[keyno].key_part;
- KEY_PART_INFO *kp_end= kp + table->key_info[keyno].key_parts;
+ KEY_PART_INFO *kp= share->key_info[keyno].key_part;
+ KEY_PART_INFO *kp_end= kp + share->key_info[keyno].key_parts;
for (; kp != kp_end; kp++)
{
if (!kp->field->part_of_key.is_set(keyno))
@@ -1518,10 +1535,11 @@ bool key_uses_partial_cols(TABLE *table, uint keyno)
@retval FALSE Otherwise
*/
-bool DsMrr_impl::check_cpk_scan(THD *thd, uint keyno, uint mrr_flags)
+bool DsMrr_impl::check_cpk_scan(THD *thd, TABLE_SHARE *share, uint keyno,
+ uint mrr_flags)
{
return test((mrr_flags & HA_MRR_SINGLE_POINT) &&
- keyno == table->s->primary_key &&
+ keyno == share->primary_key &&
primary_file->primary_key_is_clustered() &&
optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_SORT_KEYS));
}
@@ -1557,14 +1575,15 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags,
COST_VECT dsmrr_cost;
bool res;
THD *thd= current_thd;
+ TABLE_SHARE *share= primary_file->get_table_share();
- bool doing_cpk_scan= check_cpk_scan(thd, keyno, *flags);
- bool using_cpk= test(keyno == table->s->primary_key &&
+ bool doing_cpk_scan= check_cpk_scan(thd, share, keyno, *flags);
+ bool using_cpk= test(keyno == share->primary_key &&
primary_file->primary_key_is_clustered());
*flags &= ~HA_MRR_IMPLEMENTATION_FLAGS;
if (!optimizer_flag(thd, OPTIMIZER_SWITCH_MRR) ||
*flags & HA_MRR_INDEX_ONLY ||
- (using_cpk && !doing_cpk_scan) || key_uses_partial_cols(table, keyno))
+ (using_cpk && !doing_cpk_scan) || key_uses_partial_cols(share, keyno))
{
/* Use the default implementation */
*flags |= HA_MRR_USE_DEFAULT_IMPL;
@@ -1572,7 +1591,7 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags,
return TRUE;
}
- uint add_len= table->key_info[keyno].key_length + primary_file->ref_length;
+ 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))
return TRUE;
diff --git a/sql/multi_range_read.h b/sql/multi_range_read.h
index 0ad61142b12..0819b6fe948 100644
--- a/sql/multi_range_read.h
+++ b/sql/multi_range_read.h
@@ -1,3 +1,19 @@
+/*
+ Copyright (c) 2009, 2011, Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
/**
@defgroup DS-MRR declarations
@{
@@ -617,7 +633,7 @@ private:
COST_VECT *cost);
bool get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags,
uint *buffer_size, COST_VECT *cost);
- bool check_cpk_scan(THD *thd, uint keyno, uint mrr_flags);
+ 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_decimal.cc b/sql/my_decimal.cc
index 44207b9319b..21611afd87b 100644
--- a/sql/my_decimal.cc
+++ b/sql/my_decimal.cc
@@ -12,17 +12,20 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
+#include <my_global.h>
+#include "sql_priv.h"
#include <time.h>
+#ifndef MYSQL_CLIENT
+#include "sql_class.h" // THD
+#endif
+
#define DIG_BASE 1000000000
#define DIG_PER_DEC1 9
#define ROUND_UP(X) (((X)+DIG_PER_DEC1-1)/DIG_PER_DEC1)
-
#ifndef MYSQL_CLIENT
/**
report result of decimal operation.
@@ -47,16 +50,16 @@ int decimal_operation_results(int result, const char *value, const char *type)
value, type);
break;
case E_DEC_OVERFLOW:
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_DATA_OVERFLOW, ER(ER_DATA_OVERFLOW),
value, type);
break;
case E_DEC_DIV_ZERO:
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_DIVISION_BY_ZERO, ER(ER_DIVISION_BY_ZERO));
break;
case E_DEC_BAD_NUM:
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_BAD_DATA, ER(ER_BAD_DATA),
value, type);
break;
@@ -114,12 +117,64 @@ int my_decimal2string(uint mask, const my_decimal *d,
result= decimal2string((decimal_t*) d, (char*) str->ptr(),
&length, (int)fixed_prec, fixed_dec,
filler);
- str->set_charset(&my_charset_bin);
str->length(length);
+ str->set_charset(&my_charset_numeric);
return check_result(mask, result);
}
+/**
+ @brief Converting decimal to string with character set conversion
+
+ @details Convert given my_decimal to String; allocate buffer as needed.
+
+ @param[in] mask what problems to warn on (mask of E_DEC_* values)
+ @param[in] val the decimal to print
+ @param[in] fixed_prec overall number of digits if ZEROFILL, 0 otherwise
+ @param[in] fixed_dec number of decimal places (if fixed_prec != 0)
+ @param[in] filler what char to pad with (ZEROFILL et al.)
+ @param[out] *str where to store the resulting string
+ @param[in] cs character set
+
+ @return error coce
+ @retval E_DEC_OK
+ @retval E_DEC_TRUNCATED
+ @retval E_DEC_OVERFLOW
+ @retval E_DEC_OOM
+
+ Would be great to make it a method of the String class,
+ but this would need to include
+ my_decimal.h from sql_string.h and sql_string.cc, which is not desirable.
+*/
+bool
+str_set_decimal(uint mask, const my_decimal *val,
+ uint fixed_prec, uint fixed_dec, char filler,
+ String *str, CHARSET_INFO *cs)
+{
+ if (!(cs->state & MY_CS_NONASCII))
+ {
+ /* For ASCII-compatible character sets we can use my_decimal2string */
+ my_decimal2string(mask, val, fixed_prec, fixed_dec, filler, str);
+ str->set_charset(cs);
+ return FALSE;
+ }
+ else
+ {
+ /*
+ For ASCII-incompatible character sets (like UCS2) we
+ call my_decimal2string() on a temporary buffer first,
+ and then convert the result to the target character
+ with help of str->copy().
+ */
+ uint errors;
+ char buf[DECIMAL_MAX_STR_LENGTH];
+ String tmp(buf, sizeof(buf), &my_charset_latin1);
+ my_decimal2string(mask, val, fixed_prec, fixed_dec, filler, &tmp);
+ return str->copy(tmp.ptr(), tmp.length(), &my_charset_latin1, cs, &errors);
+ }
+}
+
+
/*
Convert from decimal to binary representation
@@ -329,12 +384,12 @@ print_decimal(const my_decimal *dec)
int i, end;
char buff[512], *pos;
pos= buff;
- pos+= my_sprintf(buff, (buff, "Decimal: sign: %d intg: %d frac: %d { ",
- dec->sign(), dec->intg, dec->frac));
+ pos+= sprintf(buff, "Decimal: sign: %d intg: %d frac: %d { ",
+ dec->sign(), dec->intg, dec->frac);
end= ROUND_UP(dec->frac)+ROUND_UP(dec->intg)-1;
for (i=0; i < end; i++)
- pos+= my_sprintf(pos, (pos, "%09d, ", dec->buf[i]));
- pos+= my_sprintf(pos, (pos, "%09d }\n", dec->buf[i]));
+ pos+= sprintf(pos, "%09d, ", dec->buf[i]);
+ pos+= sprintf(pos, "%09d }\n", dec->buf[i]);
fputs(buff, DBUG_FILE);
}
diff --git a/sql/my_decimal.h b/sql/my_decimal.h
index 0e9526b067a..22ae38bef41 100644
--- a/sql/my_decimal.h
+++ b/sql/my_decimal.h
@@ -1,5 +1,5 @@
-/*
- Copyright (c) 2005, 2013, Oracle and/or its affiliates.
+/* Copyright (c) 2005, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2011, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +12,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
/**
@file
@@ -30,11 +29,17 @@
#ifndef my_decimal_h
#define my_decimal_h
+#if defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY)
+#include "sql_string.h" /* String */
+#endif
+
C_MODE_START
#include <decimal.h>
+#include <my_decimal_limits.h>
C_MODE_END
-#include <my_decimal_limits.h>
+class String;
+typedef struct st_mysql_time MYSQL_TIME;
/**
maximum size of packet length.
@@ -86,23 +91,16 @@ class my_decimal :public decimal_t
#endif
public:
+
my_decimal(const my_decimal &rhs) : decimal_t(rhs)
{
-#if !defined(DBUG_OFF)
- foo1= test_value;
- foo2= test_value;
-#endif
+ init();
for (uint i= 0; i < DECIMAL_BUFF_LENGTH; i++)
buffer[i]= rhs.buffer[i];
- fix_buffer_pointer();
}
my_decimal& operator=(const my_decimal &rhs)
{
-#if !defined(DBUG_OFF)
- foo1= test_value;
- foo2= test_value;
-#endif
if (this == &rhs)
return *this;
decimal_t::operator=(rhs);
@@ -120,7 +118,7 @@ public:
#endif
len= DECIMAL_BUFF_LENGTH;
buf= buffer;
- TRASH(buffer, sizeof(buffer));
+ TRASH_ALLOC(buffer, sizeof(buffer));
}
my_decimal()
@@ -160,6 +158,12 @@ const char *dbug_decimal_as_string(char *buff, const my_decimal *val);
#define dbug_decimal_as_string(A) NULL
#endif
+bool str_set_decimal(uint mask, const my_decimal *val, uint fixed_prec,
+ uint fixed_dec, char filler, String *str,
+ CHARSET_INFO *cs);
+
+extern my_decimal decimal_zero;
+
#ifndef MYSQL_CLIENT
int decimal_operation_results(int result, const char *value, const char *type);
#else
@@ -175,7 +179,7 @@ void max_my_decimal(my_decimal *to, int precision, int frac)
{
DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION)&&
(frac <= DECIMAL_MAX_SCALE));
- max_decimal(precision, frac, (decimal_t*) to);
+ max_decimal(precision, frac, to);
}
inline void max_internal_decimal(my_decimal *to)
@@ -264,7 +268,6 @@ inline
void my_decimal2decimal(const my_decimal *from, my_decimal *to)
{
*to= *from;
- to->fix_buffer_pointer();
}
@@ -276,14 +279,19 @@ inline
int binary2my_decimal(uint mask, const uchar *bin, my_decimal *d, int prec,
int scale)
{
- return check_result(mask, bin2decimal(bin, (decimal_t*) d, prec, scale));
+ return check_result(mask, bin2decimal(bin, d, prec, scale));
}
inline
int my_decimal_set_zero(my_decimal *d)
{
- decimal_make_zero(((decimal_t*) d));
+ /*
+ We need the up-cast here, since my_decimal has sign() member functions,
+ which conflicts with decimal_t::size
+ (and decimal_make_zero is a macro, rather than a funcion).
+ */
+ decimal_make_zero(static_cast<decimal_t*>(d));
return 0;
}
@@ -291,7 +299,7 @@ int my_decimal_set_zero(my_decimal *d)
inline
bool my_decimal_is_zero(const my_decimal *decimal_value)
{
- return decimal_is_zero((decimal_t*) decimal_value);
+ return decimal_is_zero(decimal_value);
}
@@ -299,7 +307,7 @@ inline
int my_decimal_round(uint mask, const my_decimal *from, int scale,
bool truncate, my_decimal *to)
{
- return check_result(mask, decimal_round((decimal_t*) from, to, scale,
+ return check_result(mask, decimal_round(from, to, scale,
(truncate ? TRUNCATE : HALF_UP)));
}
@@ -307,18 +315,25 @@ int my_decimal_round(uint mask, const my_decimal *from, int scale,
inline
int my_decimal_floor(uint mask, const my_decimal *from, my_decimal *to)
{
- return check_result(mask, decimal_round((decimal_t*) from, to, 0, FLOOR));
+ return check_result(mask, decimal_round(from, to, 0, FLOOR));
}
inline
int my_decimal_ceiling(uint mask, const my_decimal *from, my_decimal *to)
{
- return check_result(mask, decimal_round((decimal_t*) from, to, 0, CEILING));
+ return check_result(mask, decimal_round(from, to, 0, CEILING));
}
+inline bool str_set_decimal(const my_decimal *val, String *str,
+ CHARSET_INFO *cs)
+{
+ return str_set_decimal(E_DEC_FATAL_ERROR, val, 0, 0, 0, str, cs);
+}
+
#ifndef MYSQL_CLIENT
+class String;
int my_decimal2string(uint mask, const my_decimal *d, uint fixed_prec,
uint fixed_dec, char filler, String *str);
#endif
@@ -342,8 +357,7 @@ int my_decimal2double(uint, const decimal_t *d, double *result)
inline
int str2my_decimal(uint mask, const char *str, my_decimal *d, char **end)
{
- return check_result_and_overflow(mask, string2decimal(str,(decimal_t*)d,end),
- d);
+ return check_result_and_overflow(mask, string2decimal(str, d, end), d);
}
@@ -366,7 +380,7 @@ my_decimal *date2my_decimal(MYSQL_TIME *ltime, my_decimal *dec);
inline
int double2my_decimal(uint mask, double val, my_decimal *d)
{
- return check_result_and_overflow(mask, double2decimal(val, (decimal_t*)d), d);
+ return check_result_and_overflow(mask, double2decimal(val, d), d);
}
@@ -406,7 +420,7 @@ int my_decimal_add(uint mask, my_decimal *res, const my_decimal *a,
const my_decimal *b)
{
return check_result_and_overflow(mask,
- decimal_add((decimal_t*)a,(decimal_t*)b,res),
+ decimal_add(a, b, res),
res);
}
@@ -416,7 +430,7 @@ int my_decimal_sub(uint mask, my_decimal *res, const my_decimal *a,
const my_decimal *b)
{
return check_result_and_overflow(mask,
- decimal_sub((decimal_t*)a,(decimal_t*)b,res),
+ decimal_sub(a, b, res),
res);
}
@@ -426,7 +440,7 @@ int my_decimal_mul(uint mask, my_decimal *res, const my_decimal *a,
const my_decimal *b)
{
return check_result_and_overflow(mask,
- decimal_mul((decimal_t*)a,(decimal_t*)b,res),
+ decimal_mul(a, b, res),
res);
}
@@ -436,8 +450,7 @@ int my_decimal_div(uint mask, my_decimal *res, const my_decimal *a,
const my_decimal *b, int div_scale_inc)
{
return check_result_and_overflow(mask,
- decimal_div((decimal_t*)a,(decimal_t*)b,res,
- div_scale_inc),
+ decimal_div(a, b, res, div_scale_inc),
res);
}
@@ -447,7 +460,7 @@ int my_decimal_mod(uint mask, my_decimal *res, const my_decimal *a,
const my_decimal *b)
{
return check_result_and_overflow(mask,
- decimal_mod((decimal_t*)a,(decimal_t*)b,res),
+ decimal_mod(a, b, res),
res);
}
@@ -458,14 +471,14 @@ int my_decimal_mod(uint mask, my_decimal *res, const my_decimal *a,
inline
int my_decimal_cmp(const my_decimal *a, const my_decimal *b)
{
- return decimal_cmp((decimal_t*) a, (decimal_t*) b);
+ return decimal_cmp(a, b);
}
inline
int my_decimal_intg(const my_decimal *a)
{
- return decimal_intg((decimal_t*) a);
+ return decimal_intg(a);
}
diff --git a/sql/my_lock.c b/sql/my_lock.c
deleted file mode 100644
index 420308aa53e..00000000000
--- a/sql/my_lock.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/* Copyright (c) 2000-2003, 2006 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 Street, Fifth Floor, Boston, MA 02110-1301, USA */
-
-#if defined(__NETWARE__)
-#include "../mysys/my_lock.c"
-#else
-
-#undef MAP_TO_USE_RAID /* Avoid RAID mappings */
-#include <my_global.h>
-#include <my_sys.h>
-#include <mysys_err.h>
-#include <my_pthread.h>
-#include <thr_alarm.h>
-#include <errno.h>
-
-/**
- @breif Lock a part of a file
-
- @note
- This works like mysys/my_lock.c, with the exception that this function
- uses the thr_alarm() to break long lock statements.
- (mysys can't use thr_alarm() as by default the alarm handling doesn't
- exists)
-*/
-
-int my_lock(File fd,int locktype,my_off_t start,my_off_t length,myf MyFlags)
-{
- thr_alarm_t alarmed;
- ALARM alarm_buff;
- uint wait_for_alarm;
- struct flock m_lock;
- DBUG_ENTER("my_lock");
- DBUG_PRINT("my",("Fd: %d Op: %d start: %ld Length: %ld MyFlags: %d",
- fd,locktype,(ulong) start,(ulong) length,MyFlags));
- if (my_disable_locking && ! (MyFlags & MY_FORCE_LOCK))
- DBUG_RETURN(0); /* purecov: inspected */
-
- m_lock.l_type=(short) locktype;
- m_lock.l_whence=0L;
- m_lock.l_start=(long) start;
- m_lock.l_len=(long) length;
- if (fcntl(fd,F_SETLK,&m_lock) != -1) /* Check if we can lock */
- DBUG_RETURN(0); /* Ok, file locked */
-
- if (!(MyFlags & MY_NO_WAIT))
- {
- wait_for_alarm= (MyFlags & MY_SHORT_WAIT ? MY_HOW_OFTEN_TO_ALARM :
- (uint) 12*60*60);
- DBUG_PRINT("info",("Was locked, trying with alarm"));
- if (!thr_alarm(&alarmed,wait_for_alarm,&alarm_buff))
- {
- int value;
- while ((value=fcntl(fd,F_SETLKW,&m_lock)) && !thr_got_alarm(&alarmed) &&
- errno == EINTR) ;
- thr_end_alarm(&alarmed);
- if (value != -1)
- DBUG_RETURN(0);
- }
- else
- {
- errno=EINTR;
- }
- }
- if (errno == EINTR || errno == EACCES)
- my_errno=EAGAIN; /* Easier to check for this */
- else
- my_errno=errno;
-
- if (MyFlags & MY_WME)
- {
- if (locktype == F_UNLCK)
- my_error(EE_CANTUNLOCK,MYF(ME_BELL+ME_WAITTANG),errno);
- else
- my_error(EE_CANTLOCK,MYF(ME_BELL+ME_WAITTANG),errno);
- }
- DBUG_PRINT("error",("errno: %d",errno));
- DBUG_RETURN(-1);
-}
-#endif
diff --git a/sql/mysql_install_db.cc b/sql/mysql_install_db.cc
index 364dca9120a..9c1a234241f 100644
--- a/sql/mysql_install_db.cc
+++ b/sql/mysql_install_db.cc
@@ -54,6 +54,7 @@ static char *opt_os_password;
static my_bool opt_default_user;
static my_bool opt_allow_remote_root_access;
static my_bool opt_skip_networking;
+static my_bool opt_verbose_bootstrap;
static my_bool verbose_errors;
@@ -83,6 +84,8 @@ static struct my_option my_long_options[]=
0, 0},
{"silent", 's', "Print less information", &opt_silent,
&opt_silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"verbose-bootstrap", 'o', "Include mysqld bootstrap output",&opt_verbose_bootstrap,
+ &opt_verbose_bootstrap, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
@@ -119,8 +122,8 @@ static void die(const char *fmt, ...)
fprintf(stderr,
"http://kb.askmonty.org/v/installation-issues-on-windows contains some help\n"
"for solving the most common problems. If this doesn't help you, please\n"
- "leave a comment in the knowledge base or file a bug report at\n"
- "https://bugs.launchpad.net/maria");
+ "leave a comment in the Knowledgebase or file a bug report at\n"
+ "http://mariadb.org/jira");
}
fflush(stderr);
va_end(args);
@@ -244,11 +247,12 @@ static char *init_bootstrap_command_line(char *cmdline, size_t size)
get_basedir(basedir, sizeof(basedir), mysqld_path);
my_snprintf(cmdline, size-1,
- "\"\"%s\" --no-defaults --bootstrap"
- " \"--language=%s\\share\\english\""
+ "\"\"%s\" --no-defaults %s --bootstrap"
+ " \"--lc-messages-dir=%s/share\""
" --basedir=. --datadir=. --default-storage-engine=myisam"
- " --max_allowed_packet=9M --loose-skip-innodb --loose-skip-pbxt"
- " --net-buffer-length=16k\"", mysqld_path, basedir);
+ " --max_allowed_packet=9M --loose-skip-innodb"
+ " --net-buffer-length=16k\"", mysqld_path,
+ opt_verbose_bootstrap?"--console":"", basedir );
return cmdline;
}
@@ -377,7 +381,7 @@ static int register_service()
static void clean_directory(const char *dir)
{
char dir2[MAX_PATH+2];
- *(strmake(dir2, dir, MAX_PATH+1)+1)= 0;
+ *(strmake_buf(dir2, dir)+1)= 0;
SHFILEOPSTRUCT fileop;
fileop.hwnd= NULL; /* no status display */
@@ -552,7 +556,9 @@ static int create_db_instance()
/* Do mysqld --bootstrap. */
init_bootstrap_command_line(cmdline, sizeof(cmdline));
- /* verbose("Executing %s", cmdline); */
+
+ if(opt_verbose_bootstrap)
+ printf("Executing %s\n", cmdline);
in= popen(cmdline, "wt");
if (!in)
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
deleted file mode 100644
index 0a64f654863..00000000000
--- a/sql/mysql_priv.h
+++ /dev/null
@@ -1,2942 +0,0 @@
-/*
- Copyright (c) 2000, 2013, Oracle and/or its affiliates.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-*/
-
-/**
- @file
-
- @details
- Mostly this file is used in the server. But a little part of it is used in
- mysqlbinlog too (definition of SELECT_DISTINCT and others).
- The consequence is that 90% of the file is wrapped in \#ifndef MYSQL_CLIENT,
- except the part which must be in the server and in the client.
-*/
-
-#ifndef MYSQL_PRIV_H
-#define MYSQL_PRIV_H
-
-#ifndef MYSQL_CLIENT
-
-/*
- the following #define adds server-only members to enum_mysql_show_type,
- that is defined in mysql/plugin.h
- it has to be before mysql/plugin.h is included.
-*/
-#define SHOW_always_last SHOW_LONG_STATUS, SHOW_DOUBLE_STATUS, \
- SHOW_HAVE, SHOW_MY_BOOL, SHOW_HA_ROWS, SHOW_SYS, \
- SHOW_LONG_NOFLUSH, SHOW_LONGLONG_STATUS
-
-#include <my_global.h>
-#include <mysql_version.h>
-#include <mysql_embed.h>
-#include <my_sys.h>
-#include <my_time.h>
-#include <m_string.h>
-#include <hash.h>
-#include <signal.h>
-#include <thr_lock.h>
-#include <my_base.h> /* Needed by field.h */
-#include <queues.h>
-#include "sql_bitmap.h"
-#include "sql_array.h"
-#include "sql_plugin.h"
-#include "scheduler.h"
-#include "log_slow.h"
-
-class Parser_state;
-
-/**
- Query type constants.
-
- QT_ORDINARY -- ordinary SQL query.
- QT_IS -- SQL query to be shown in INFORMATION_SCHEMA (in utf8 and without
- character set introducers).
- QT_VIEW_INTERNAL -- view internal representation (like QT_ORDINARY except
- ORDER BY clause)
-*/
-enum enum_query_type
-{
- QT_ORDINARY,
- QT_IS,
- QT_VIEW_INTERNAL
-};
-
-/* TODO convert all these three maps to Bitmap classes */
-typedef ulonglong table_map; /* Used for table bits in join */
-#if MAX_INDEXES <= 64
-typedef Bitmap<64> key_map; /* Used for finding keys */
-#else
-typedef Bitmap<((MAX_INDEXES+7)/8*8)> key_map; /* Used for finding keys */
-#endif
-typedef ulong nesting_map; /* Used for flags of nesting constructs */
-/*
- Used to identify NESTED_JOIN structures within a join (applicable only to
- structures that have not been simplified away and embed more the one
- element)
-*/
-typedef ulonglong nested_join_map;
-
-/* query_id */
-typedef ulonglong query_id_t;
-extern query_id_t global_query_id;
-
-/* increment query_id and return it. */
-inline query_id_t next_query_id() { return global_query_id++; }
-
-/* useful constants */
-extern MYSQL_PLUGIN_IMPORT const key_map key_map_empty;
-extern MYSQL_PLUGIN_IMPORT key_map key_map_full; /* Should be threaded as const */
-extern MYSQL_PLUGIN_IMPORT const char *primary_key_name;
-
-#include "mysql_com.h"
-#include <violite.h>
-#include "unireg.h"
-
-void init_sql_alloc(MEM_ROOT *root, uint block_size, uint pre_alloc_size);
-#endif // MYSQL_CLIENT
-
-void *sql_alloc(size_t);
-void *sql_calloc(size_t);
-char *sql_strdup(const char *str);
-char *sql_strmake(const char *str, size_t len);
-void *sql_memdup(const void * ptr, size_t size);
-void sql_element_free(void *ptr);
-
-#ifndef MYSQL_CLIENT
-char *sql_strmake_with_convert(const char *str, size_t arg_length,
- CHARSET_INFO *from_cs,
- size_t max_res_length,
- CHARSET_INFO *to_cs, size_t *result_length);
-bool net_request_file(NET* net, const char* fname);
-char* query_table_status(THD *thd,const char *db,const char *table_name);
-
-#define x_free(A) { my_free((uchar*) (A),MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR)); }
-#define safeFree(x) { if(x) { my_free((uchar*) x,MYF(0)); x = NULL; } }
-#define PREV_BITS(type,A) ((type) (((type) 1 << (A)) -1))
-#define all_bits_set(A,B) ((A) & (B) != (B))
-
-/* Version numbers for deprecation messages */
-#define VER_BETONY "5.5"
-#define VER_CELOSIA "5.6"
-
-#define WARN_DEPRECATED(Thd,Ver,Old,New) \
- do { \
- DBUG_ASSERT(strncmp(Ver, MYSQL_SERVER_VERSION, sizeof(Ver)-1) > 0); \
- if (((uchar*)Thd) != NULL) \
- push_warning_printf(((THD *)Thd), MYSQL_ERROR::WARN_LEVEL_WARN, \
- ER_WARN_DEPRECATED_SYNTAX, \
- ER(ER_WARN_DEPRECATED_SYNTAX), \
- (Old), (New)); \
- else \
- sql_print_warning("'%s' is deprecated and will be removed " \
- "in a future release. Please use '%s' instead.", \
- (Old), (New)); \
- } while(0)
-
-extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *system_charset_info;
-extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *files_charset_info ;
-extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *national_charset_info;
-extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *table_alias_charset;
-
-
-enum Derivation
-{
- DERIVATION_IGNORABLE= 5,
- DERIVATION_COERCIBLE= 4,
- DERIVATION_SYSCONST= 3,
- DERIVATION_IMPLICIT= 2,
- DERIVATION_NONE= 1,
- DERIVATION_EXPLICIT= 0
-};
-
-
-typedef struct my_locale_st
-{
- uint number;
- const char *name;
- const char *description;
- const bool is_ascii;
- TYPELIB *month_names;
- TYPELIB *ab_month_names;
- TYPELIB *day_names;
- TYPELIB *ab_day_names;
- uint max_month_name_length;
- uint max_day_name_length;
-#ifdef __cplusplus
- my_locale_st(uint number_par,
- const char *name_par, const char *descr_par, bool is_ascii_par,
- TYPELIB *month_names_par, TYPELIB *ab_month_names_par,
- TYPELIB *day_names_par, TYPELIB *ab_day_names_par,
- uint max_month_name_length_par, uint max_day_name_length_par) :
- number(number_par),
- name(name_par), description(descr_par), is_ascii(is_ascii_par),
- month_names(month_names_par), ab_month_names(ab_month_names_par),
- day_names(day_names_par), ab_day_names(ab_day_names_par),
- max_month_name_length(max_month_name_length_par),
- max_day_name_length(max_day_name_length_par)
- {}
-#endif
-} MY_LOCALE;
-
-extern MY_LOCALE my_locale_en_US;
-extern MY_LOCALE *my_locales[];
-extern MY_LOCALE *my_default_lc_time_names;
-
-MY_LOCALE *my_locale_by_name(const char *name);
-MY_LOCALE *my_locale_by_number(uint number);
-
-/*************************************************************************/
-
-/**
- Object_creation_ctx -- interface for creation context of database objects
- (views, stored routines, events, triggers). Creation context -- is a set
- of attributes, that should be fixed at the creation time and then be used
- each time the object is parsed or executed.
-*/
-
-class Object_creation_ctx
-{
-public:
- Object_creation_ctx *set_n_backup(THD *thd);
-
- void restore_env(THD *thd, Object_creation_ctx *backup_ctx);
-
-protected:
- Object_creation_ctx() {}
- virtual Object_creation_ctx *create_backup_ctx(THD *thd) const = 0;
-
- virtual void change_env(THD *thd) const = 0;
-
-public:
- virtual ~Object_creation_ctx()
- { }
-};
-
-/*************************************************************************/
-
-/**
- Default_object_creation_ctx -- default implementation of
- Object_creation_ctx.
-*/
-
-class Default_object_creation_ctx : public Object_creation_ctx
-{
-public:
- CHARSET_INFO *get_client_cs()
- {
- return m_client_cs;
- }
-
- CHARSET_INFO *get_connection_cl()
- {
- return m_connection_cl;
- }
-
-protected:
- Default_object_creation_ctx(THD *thd);
-
- Default_object_creation_ctx(CHARSET_INFO *client_cs,
- CHARSET_INFO *connection_cl);
-
-protected:
- virtual Object_creation_ctx *create_backup_ctx(THD *thd) const;
-
- virtual void change_env(THD *thd) const;
-
-protected:
- /**
- client_cs stores the value of character_set_client session variable.
- The only character set attribute is used.
-
- Client character set is included into query context, because we save
- query in the original character set, which is client character set. So,
- in order to parse the query properly we have to switch client character
- set on parsing.
- */
- CHARSET_INFO *m_client_cs;
-
- /**
- connection_cl stores the value of collation_connection session
- variable. Both character set and collation attributes are used.
-
- Connection collation is included into query context, becase it defines
- the character set and collation of text literals in internal
- representation of query (item-objects).
- */
- CHARSET_INFO *m_connection_cl;
-};
-
-/***************************************************************************
- Configuration parameters
-****************************************************************************/
-
-#define ACL_CACHE_SIZE 256
-#define MAX_PASSWORD_LENGTH 32
-#define HOST_CACHE_SIZE 128
-#define MAX_ACCEPT_RETRY 10 // Test accept this many times
-#define MAX_FIELDS_BEFORE_HASH 32
-#define USER_VARS_HASH_SIZE 16
-#define TABLE_OPEN_CACHE_MIN 64
-#define TABLE_OPEN_CACHE_DEFAULT 64
-#define TABLE_DEF_CACHE_DEFAULT 256
-/**
- We must have room for at least 256 table definitions in the table
- cache, since otherwise there is no chance prepared
- statements that use these many tables can work.
- Prepared statements use table definition cache ids (table_map_id)
- as table version identifiers. If the table definition
- cache size is less than the number of tables used in a statement,
- the contents of the table definition cache is guaranteed to rotate
- between a prepare and execute. This leads to stable validation
- errors. In future we shall use more stable version identifiers,
- for now the only solution is to ensure that the table definition
- cache can contain at least all tables of a given statement.
-*/
-#define TABLE_DEF_CACHE_MIN 256
-
-/*
- Stack reservation.
- Feel free to raise this by the smallest amount you can to get the
- "execution_constants" test to pass.
-*/
-#define STACK_MIN_SIZE 16000 // Abort if less stack during eval.
-
-#define STACK_MIN_SIZE_FOR_OPEN 1024*80
-#define STACK_BUFF_ALLOC 352 ///< For stack overrun checks
-#ifndef MYSQLD_NET_RETRY_COUNT
-#define MYSQLD_NET_RETRY_COUNT 10 ///< Abort read after this many int.
-#endif
-#define TEMP_POOL_SIZE 128
-
-#define QUERY_ALLOC_BLOCK_SIZE 8192
-#define QUERY_ALLOC_PREALLOC_SIZE 8192
-#define TRANS_ALLOC_BLOCK_SIZE 4096
-#define TRANS_ALLOC_PREALLOC_SIZE 4096
-#define RANGE_ALLOC_BLOCK_SIZE 4096
-#define ACL_ALLOC_BLOCK_SIZE 1024
-#define UDF_ALLOC_BLOCK_SIZE 1024
-#define TABLE_ALLOC_BLOCK_SIZE 1024
-#define BDB_LOG_ALLOC_BLOCK_SIZE 1024
-#define WARN_ALLOC_BLOCK_SIZE 2048
-#define WARN_ALLOC_PREALLOC_SIZE 1024
-#define PROFILE_ALLOC_BLOCK_SIZE 2048
-#define PROFILE_ALLOC_PREALLOC_SIZE 1024
-
-/*
- The following parameters is to decide when to use an extra cache to
- optimise seeks when reading a big table in sorted order
-*/
-#define MIN_FILE_LENGTH_TO_USE_ROW_CACHE (10L*1024*1024)
-#define MIN_ROWS_TO_USE_TABLE_CACHE 100
-#define MIN_ROWS_TO_USE_BULK_INSERT 100
-
-/**
- The following is used to decide if MySQL should use table scanning
- instead of reading with keys. The number says how many evaluation of the
- WHERE clause is comparable to reading one extra row from a table.
-*/
-#define TIME_FOR_COMPARE 5 // 5 compares == one read
-
-/**
- Number of comparisons of table rowids equivalent to reading one row from a
- table.
-*/
-#define TIME_FOR_COMPARE_ROWID (TIME_FOR_COMPARE*100)
-
-/* cost1 is better that cost2 only if cost1 + COST_EPS < cost2 */
-#define COST_EPS 0.001
-
-/*
- For sequential disk seeks the cost formula is:
- DISK_SEEK_BASE_COST + DISK_SEEK_PROP_COST * #blocks_to_skip
-
- The cost of average seek
- DISK_SEEK_BASE_COST + DISK_SEEK_PROP_COST*BLOCKS_IN_AVG_SEEK =1.0.
-*/
-#define DISK_SEEK_BASE_COST ((double)0.9)
-
-#define BLOCKS_IN_AVG_SEEK 128
-
-#define DISK_SEEK_PROP_COST ((double)0.1/BLOCKS_IN_AVG_SEEK)
-
-
-/**
- Number of rows in a reference table when refereed through a not unique key.
- This value is only used when we don't know anything about the key
- distribution.
-*/
-#define MATCHING_ROWS_IN_OTHER_TABLE 10
-
-/*
- Subquery materialization-related constants
-*/
-#define HEAP_TEMPTABLE_LOOKUP_COST 0.05
-#define DISK_TEMPTABLE_LOOKUP_COST 1.0
-
-/** Don't pack string keys shorter than this (if PACK_KEYS=1 isn't used). */
-#define KEY_DEFAULT_PACK_LENGTH 8
-
-/** Characters shown for the command in 'show processlist'. */
-#define PROCESS_LIST_WIDTH 100
-/* Characters shown for the command in 'information_schema.processlist' */
-#define PROCESS_LIST_INFO_WIDTH 65535
-
-#define PRECISION_FOR_DOUBLE 53
-#define PRECISION_FOR_FLOAT 24
-
-/* -[digits].E+## */
-#define MAX_FLOAT_STR_LENGTH (FLT_DIG + 6)
-/* -[digits].E+### */
-#define MAX_DOUBLE_STR_LENGTH (DBL_DIG + 7)
-
-/*
- Default time to wait before aborting a new client connection
- that does not respond to "initial server greeting" timely
-*/
-#define CONNECT_TIMEOUT 10
-
-/* The following can also be changed from the command line */
-#define DEFAULT_CONCURRENCY 10
-#define DELAYED_LIMIT 100 /**< pause after xxx inserts */
-#define DELAYED_QUEUE_SIZE 1000
-#define DELAYED_WAIT_TIMEOUT 5*60 /**< Wait for delayed insert */
-#define MAX_CONNECT_ERRORS 10 ///< errors before disabling host
-
-#ifdef __NETWARE__
-#define IF_NETWARE(A,B) A
-#else
-#define IF_NETWARE(A,B) B
-#endif
-
-#if defined(__WIN__)
-
-#define INTERRUPT_PRIOR -2
-#define CONNECT_PRIOR -1
-#define WAIT_PRIOR 0
-#define QUERY_PRIOR 2
-#else
-#define INTERRUPT_PRIOR 10
-#define CONNECT_PRIOR 9
-#define WAIT_PRIOR 8
-#define QUERY_PRIOR 6
-#endif /* __WIN92__ */
-
- /* Bits from testflag */
-#define TEST_PRINT_CACHED_TABLES 1
-#define TEST_NO_KEY_GROUP 2
-#define TEST_MIT_THREAD 4
-#define TEST_BLOCKING 8
-#define TEST_KEEP_TMP_TABLES 16
-#define TEST_READCHECK 64 /**< Force use of readcheck */
-#define TEST_NO_EXTRA 128
-#define TEST_CORE_ON_SIGNAL 256 /**< Give core if signal */
-#define TEST_NO_STACKTRACE 512
-#define TEST_SIGINT 1024 /**< Allow sigint on threads */
-#define TEST_SYNCHRONIZATION 2048 /**< get server to do sleep in
- some places */
-#endif
-
-/*
- This is included in the server and in the client.
- Options for select set by the yacc parser (stored in lex->options).
-
- XXX:
- log_event.h defines OPTIONS_WRITTEN_TO_BIN_LOG to specify what THD
- options list are written into binlog. These options can NOT change their
- values, or it will break replication between version.
-
- context is encoded as following:
- SELECT - SELECT_LEX_NODE::options
- THD - THD::options
- intern - neither. used only as
- func(..., select_node->options | thd->options | OPTION_XXX, ...)
-
- TODO: separate three contexts above, move them to separate bitfields.
-*/
-
-#define SELECT_DISTINCT (ULL(1) << 0) // SELECT, user
-#define SELECT_STRAIGHT_JOIN (ULL(1) << 1) // SELECT, user
-#define SELECT_DESCRIBE (ULL(1) << 2) // SELECT, user
-#define SELECT_SMALL_RESULT (ULL(1) << 3) // SELECT, user
-#define SELECT_BIG_RESULT (ULL(1) << 4) // SELECT, user
-#define OPTION_FOUND_ROWS (ULL(1) << 5) // SELECT, user
-#define OPTION_TO_QUERY_CACHE (ULL(1) << 6) // SELECT, user
-#define SELECT_NO_JOIN_CACHE (ULL(1) << 7) // intern
-#define OPTION_BIG_TABLES (ULL(1) << 8) // THD, user
-#define OPTION_BIG_SELECTS (ULL(1) << 9) // THD, user
-#define OPTION_LOG_OFF (ULL(1) << 10) // THD, user
-#define OPTION_QUOTE_SHOW_CREATE (ULL(1) << 11) // THD, user, unused
-#define TMP_TABLE_ALL_COLUMNS (ULL(1) << 12) // SELECT, intern
-#define OPTION_WARNINGS (ULL(1) << 13) // THD, user
-#define OPTION_AUTO_IS_NULL (ULL(1) << 14) // THD, user, binlog
-#define OPTION_FOUND_COMMENT (ULL(1) << 15) // SELECT, intern, parser
-#define OPTION_SAFE_UPDATES (ULL(1) << 16) // THD, user
-#define OPTION_BUFFER_RESULT (ULL(1) << 17) // SELECT, user
-#define OPTION_BIN_LOG (ULL(1) << 18) // THD, user
-#define OPTION_NOT_AUTOCOMMIT (ULL(1) << 19) // THD, user
-#define OPTION_BEGIN (ULL(1) << 20) // THD, intern
-#define OPTION_TABLE_LOCK (ULL(1) << 21) // THD, intern
-#define OPTION_QUICK (ULL(1) << 22) // SELECT (for DELETE)
-#define OPTION_KEEP_LOG (ULL(1) << 23) // THD, user
-
-/* The following is used to detect a conflict with DISTINCT */
-#define SELECT_ALL (ULL(1) << 24) // SELECT, user, parser
-
-/** The following can be set when importing tables in a 'wrong order'
- to suppress foreign key checks */
-#define OPTION_NO_FOREIGN_KEY_CHECKS (ULL(1) << 26) // THD, user, binlog
-/** The following speeds up inserts to InnoDB tables by suppressing unique
- key checks in some cases */
-#define OPTION_RELAXED_UNIQUE_CHECKS (ULL(1) << 27) // THD, user, binlog
-#define SELECT_NO_UNLOCK (ULL(1) << 28) // SELECT, intern
-#define OPTION_SCHEMA_TABLE (ULL(1) << 29) // SELECT, intern
-/** Flag set if setup_tables already done */
-#define OPTION_SETUP_TABLES_DONE (ULL(1) << 30) // intern
-/** If not set then the thread will ignore all warnings with level notes. */
-#define OPTION_SQL_NOTES (ULL(1) << 31) // THD, user
-/**
- Force the used temporary table to be a MyISAM table (because we will use
- fulltext functions when reading from it.
-*/
-#define TMP_TABLE_FORCE_MYISAM (ULL(1) << 32)
-#define OPTION_PROFILING (ULL(1) << 33)
-
-
-/**
- Maximum length of time zone name that we support
- (Time zone name is char(64) in db). mysqlbinlog needs it.
-*/
-#define MAX_TIME_ZONE_NAME_LENGTH (NAME_LEN + 1)
-
-/*
- Check how many bytes are available on buffer.
-
- @param buf_start Pointer to buffer start.
- @param buf_current Pointer to the current position on buffer.
- @param buf_len Buffer length.
-
- @return Number of bytes available on event buffer.
-*/
-template <class T> T available_buffer(const char* buf_start,
- const char* buf_current,
- T buf_len)
-{
- return buf_len - (buf_current - buf_start);
-}
-
-/*
- Check if jump value is within buffer limits.
-
- @param jump Number of positions we want to advance.
- @param buf_start Pointer to buffer start
- @param buf_current Pointer to the current position on buffer.
- @param buf_len Buffer length.
-
- @return True If jump value is within buffer limits.
- False Otherwise.
-*/
-template <class T> bool valid_buffer_range(T jump,
- const char* buf_start,
- const char* buf_current,
- T buf_len)
-{
- return (jump <= available_buffer(buf_start, buf_current, buf_len));
-}
-
-/* The rest of the file is included in the server only */
-#ifndef MYSQL_CLIENT
-
-/* Bits for different SQL modes modes (including ANSI mode) */
-#define MODE_REAL_AS_FLOAT 1
-#define MODE_PIPES_AS_CONCAT 2
-#define MODE_ANSI_QUOTES 4
-#define MODE_IGNORE_SPACE 8
-#define MODE_IGNORE_BAD_TABLE_OPTIONS 16
-#define MODE_ONLY_FULL_GROUP_BY 32
-#define MODE_NO_UNSIGNED_SUBTRACTION 64
-#define MODE_NO_DIR_IN_CREATE 128
-#define MODE_POSTGRESQL 256
-#define MODE_ORACLE 512
-#define MODE_MSSQL 1024
-#define MODE_DB2 2048
-#define MODE_MAXDB 4096
-#define MODE_NO_KEY_OPTIONS 8192
-#define MODE_NO_TABLE_OPTIONS 16384
-#define MODE_NO_FIELD_OPTIONS 32768
-#define MODE_MYSQL323 65536L
-#define MODE_MYSQL40 (MODE_MYSQL323*2)
-#define MODE_ANSI (MODE_MYSQL40*2)
-#define MODE_NO_AUTO_VALUE_ON_ZERO (MODE_ANSI*2)
-#define MODE_NO_BACKSLASH_ESCAPES (MODE_NO_AUTO_VALUE_ON_ZERO*2)
-#define MODE_STRICT_TRANS_TABLES (MODE_NO_BACKSLASH_ESCAPES*2)
-#define MODE_STRICT_ALL_TABLES (MODE_STRICT_TRANS_TABLES*2)
-#define MODE_NO_ZERO_IN_DATE (MODE_STRICT_ALL_TABLES*2)
-#define MODE_NO_ZERO_DATE (MODE_NO_ZERO_IN_DATE*2)
-#define MODE_INVALID_DATES (MODE_NO_ZERO_DATE*2)
-#define MODE_ERROR_FOR_DIVISION_BY_ZERO (MODE_INVALID_DATES*2)
-#define MODE_TRADITIONAL (MODE_ERROR_FOR_DIVISION_BY_ZERO*2)
-#define MODE_NO_AUTO_CREATE_USER (MODE_TRADITIONAL*2)
-#define MODE_HIGH_NOT_PRECEDENCE (MODE_NO_AUTO_CREATE_USER*2)
-#define MODE_NO_ENGINE_SUBSTITUTION (MODE_HIGH_NOT_PRECEDENCE*2)
-#define MODE_PAD_CHAR_TO_FULL_LENGTH (ULL(1) << 31)
-
-/* @@optimizer_switch flags. These must be in sync with optimizer_switch_typelib */
-#define OPTIMIZER_SWITCH_INDEX_MERGE 1
-#define OPTIMIZER_SWITCH_INDEX_MERGE_UNION 2
-#define OPTIMIZER_SWITCH_INDEX_MERGE_SORT_UNION 4
-#define OPTIMIZER_SWITCH_INDEX_MERGE_INTERSECT 8
-#define OPTIMIZER_SWITCH_INDEX_MERGE_SORT_INTERSECT 16
-#define OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN 32
-#define OPTIMIZER_SWITCH_DERIVED_MERGE 64
-#define OPTIMIZER_SWITCH_DERIVED_WITH_KEYS 128
-#define OPTIMIZER_SWITCH_FIRSTMATCH 256
-#define OPTIMIZER_SWITCH_LOOSE_SCAN 512
-#define OPTIMIZER_SWITCH_MATERIALIZATION 1024
-#define OPTIMIZER_SWITCH_IN_TO_EXISTS (1<<11)
-#define OPTIMIZER_SWITCH_SEMIJOIN (1<<12)
-#define OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE (1<<13)
-#define OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN (1<<14)
-#define OPTIMIZER_SWITCH_SUBQUERY_CACHE (1<<15)
-/** If this is off, MRR is never used. */
-#define OPTIMIZER_SWITCH_MRR (1ULL << 16)
-/**
- If OPTIMIZER_SWITCH_MRR is on and this is on, MRR is used depending on a
- cost-based choice ("automatic"). If OPTIMIZER_SWITCH_MRR is on and this is
- off, MRR is "forced" (i.e. used as long as the storage engine is capable of
- doing it).
-*/
-#define OPTIMIZER_SWITCH_MRR_COST_BASED (1ULL << 17)
-#define OPTIMIZER_SWITCH_MRR_SORT_KEYS (1ULL << 18)
-#define OPTIMIZER_SWITCH_OUTER_JOIN_WITH_CACHE (1ULL << 19)
-#define OPTIMIZER_SWITCH_SEMIJOIN_WITH_CACHE (1ULL << 20)
-#define OPTIMIZER_SWITCH_JOIN_CACHE_INCREMENTAL (1ULL << 21)
-#define OPTIMIZER_SWITCH_JOIN_CACHE_HASHED (1ULL << 22)
-#define OPTIMIZER_SWITCH_JOIN_CACHE_BKA (1ULL << 23)
-#define OPTIMIZER_SWITCH_OPTIMIZE_JOIN_BUFFER_SIZE (1ULL << 24)
-#define OPTIMIZER_SWITCH_TABLE_ELIMINATION (1ULL << 25)
-#define OPTIMIZER_SWITCH_LAST (1ULL << 26)
-
-/* The following must be kept in sync with optimizer_switch_str in mysqld.cc */
-#define OPTIMIZER_SWITCH_DEFAULT (OPTIMIZER_SWITCH_INDEX_MERGE | \
- OPTIMIZER_SWITCH_INDEX_MERGE_UNION | \
- OPTIMIZER_SWITCH_INDEX_MERGE_SORT_UNION | \
- OPTIMIZER_SWITCH_INDEX_MERGE_INTERSECT | \
- OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN | \
- OPTIMIZER_SWITCH_DERIVED_MERGE | \
- OPTIMIZER_SWITCH_DERIVED_WITH_KEYS | \
- OPTIMIZER_SWITCH_TABLE_ELIMINATION | \
- OPTIMIZER_SWITCH_IN_TO_EXISTS | \
- OPTIMIZER_SWITCH_MATERIALIZATION | \
- OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE|\
- OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN|\
- OPTIMIZER_SWITCH_OUTER_JOIN_WITH_CACHE | \
- OPTIMIZER_SWITCH_SEMIJOIN_WITH_CACHE | \
- OPTIMIZER_SWITCH_JOIN_CACHE_INCREMENTAL | \
- OPTIMIZER_SWITCH_JOIN_CACHE_HASHED | \
- OPTIMIZER_SWITCH_JOIN_CACHE_BKA | \
- OPTIMIZER_SWITCH_SUBQUERY_CACHE | \
- OPTIMIZER_SWITCH_SEMIJOIN | \
- OPTIMIZER_SWITCH_FIRSTMATCH | \
- OPTIMIZER_SWITCH_LOOSE_SCAN )
-
-/*
- 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
- contact the replication team because the replication code should then be
- updated (to store more bytes on disk).
-
- NOTE: When adding new SQL_MODE types, make sure to also add them to
- the scripts used for creating the MySQL system tables
- in scripts/mysql_system_tables.sql and scripts/mysql_system_tables_fix.sql
-
-*/
-
-#define RAID_BLOCK_SIZE 1024
-
-#define MY_CHARSET_BIN_MB_MAXLEN 1
-
-/*
- Flags below are set when we perform
- context analysis of the statement and make
- subqueries non-const. It prevents subquery
- evaluation at context analysis stage.
-*/
-
-/*
- Don't evaluate this subquery during statement prepare even if
- it's a constant one. The flag is switched off in the end of
- mysqld_stmt_prepare.
-*/
-#define CONTEXT_ANALYSIS_ONLY_PREPARE 1
-/*
- Special JOIN::prepare mode: changing of query is prohibited.
- When creating a view, we need to just check its syntax omitting
- any optimizations: afterwards definition of the view will be
- reconstructed by means of ::print() methods and written to
- to an .frm file. We need this definition to stay untouched.
-*/
-#define CONTEXT_ANALYSIS_ONLY_VIEW 2
-/*
- Don't evaluate this subquery during derived table prepare even if
- it's a constant one.
-*/
-#define CONTEXT_ANALYSIS_ONLY_DERIVED 4
-
-/*
- Don't evaluate constant sub-expressions of virtual column
- expressions when opening tables
-*/
-#define CONTEXT_ANALYSIS_ONLY_VCOL_EXPR 8
-
-/*
- Uncachable causes:
-
-This subquery has fields from outer query (put by user)
-*/
-#define UNCACHEABLE_DEPENDENT_GENERATED 1
-/* This subquery contains functions with random result */
-#define UNCACHEABLE_RAND 2
-/* This subquery contains functions with side effect */
-#define UNCACHEABLE_SIDEEFFECT 4
-/* Forcing to save JOIN tables for explain */
-#define UNCACHEABLE_EXPLAIN 8
-/* For uncorrelated SELECT in an UNION with some correlated SELECTs */
-#define UNCACHEABLE_UNITED 16
-#define UNCACHEABLE_CHECKOPTION 32
-/*
- This subquery has fields from outer query injected during
- transformation process
-*/
-#define UNCACHEABLE_DEPENDENT_INJECTED 64
-
-/* This subquery has fields from outer query (any nature) */
-#define UNCACHEABLE_DEPENDENT (UNCACHEABLE_DEPENDENT_GENERATED | \
- UNCACHEABLE_DEPENDENT_INJECTED)
-
-/* Used to check GROUP BY list in the MODE_ONLY_FULL_GROUP_BY mode */
-#define UNDEF_POS (-1)
-
-/* BINLOG_DUMP options */
-
-#define BINLOG_DUMP_NON_BLOCK 1
-#endif /* !MYSQL_CLIENT */
-
-#define BINLOG_SEND_ANNOTATE_ROWS_EVENT 2
-
-#ifndef MYSQL_CLIENT
-/* sql_show.cc:show_log_files() */
-#define SHOW_LOG_STATUS_FREE "FREE"
-#define SHOW_LOG_STATUS_INUSE "IN USE"
-
-struct TABLE_LIST;
-class String;
-void view_store_options(THD *thd, TABLE_LIST *table, String *buff);
-
-/* Options to add_table_to_list() */
-#define TL_OPTION_UPDATING 1
-#define TL_OPTION_FORCE_INDEX 2
-#define TL_OPTION_IGNORE_LEAVES 4
-#define TL_OPTION_ALIAS 8
-
-/* Some portable defines */
-
-#define portable_sizeof_char_ptr 8
-
-#define tmp_file_prefix "#sql" /**< Prefix for tmp tables */
-#define tmp_file_prefix_length 4
-
-/* Flags for calc_week() function. */
-#define WEEK_MONDAY_FIRST 1
-#define WEEK_YEAR 2
-#define WEEK_FIRST_WEEKDAY 4
-
-#define STRING_BUFFER_USUAL_SIZE 80
-
-/*
- Some defines for exit codes for ::is_equal class functions.
-*/
-#define IS_EQUAL_NO 0
-#define IS_EQUAL_YES 1
-#define IS_EQUAL_PACK_LENGTH 2
-
-enum enum_parsing_place
-{
- NO_MATTER,
- IN_HAVING,
- SELECT_LIST,
- IN_WHERE,
- IN_ON,
- IN_GROUP_BY,
- PARSING_PLACE_SIZE /* always should be the last */
-};
-
-struct st_table;
-
-#define thd_proc_info(thd, msg) set_thd_proc_info(thd, msg, __func__, __FILE__, __LINE__)
-class THD;
-
-enum enum_check_fields
-{
- CHECK_FIELD_IGNORE,
- CHECK_FIELD_WARN,
- CHECK_FIELD_ERROR_FOR_NULL
-};
-
-#if defined(MYSQL_DYNAMIC_PLUGIN) && defined(_WIN32)
-extern "C" THD *_current_thd_noinline();
-#define _current_thd() _current_thd_noinline()
-#else
-/*
- THR_THD is a key which will be used to set/get THD* for a thread,
- using my_pthread_setspecific_ptr()/my_thread_getspecific_ptr().
-*/
-extern pthread_key(THD*, THR_THD);
-inline THD *_current_thd(void)
-{
- return my_pthread_getspecific_ptr(THD*,THR_THD);
-}
-#endif
-#define current_thd _current_thd()
-
-/**
- Enumerate possible types of a table from re-execution
- standpoint.
- TABLE_LIST class has a member of this type.
- At prepared statement prepare, this member is assigned a value
- as of the current state of the database. Before (re-)execution
- of a prepared statement, we check that the value recorded at
- prepare matches the type of the object we obtained from the
- table definition cache.
-
- @sa check_and_update_table_version()
- @sa Execute_observer
- @sa Prepared_statement::reprepare()
-*/
-
-enum enum_table_ref_type
-{
- /** Initial value set by the parser */
- TABLE_REF_NULL= 0,
- TABLE_REF_VIEW,
- TABLE_REF_BASE_TABLE,
- TABLE_REF_I_S_TABLE,
- TABLE_REF_TMP_TABLE
-};
-
-/*
- External variables
-*/
-extern ulong server_id, concurrency;
-
-
-typedef my_bool (*qc_engine_callback)(THD *thd, char *table_key,
- uint key_length,
- ulonglong *engine_data);
-#include "sql_string.h"
-#include "my_decimal.h"
-
-/*
- to unify the code that differs only in the argument passed to the
- error message (string vs. number)
-
- We pass this container around, and only convert the number
- to a string when necessary.
-*/
-class Lazy_string
-{
-public:
- Lazy_string() {}
- virtual ~Lazy_string() {}
- virtual void copy_to(String *str) const = 0;
-};
-
-class Lazy_string_str : public Lazy_string
-{
- const char *str;
- size_t len;
-public:
- Lazy_string_str(const char *str_arg, size_t len_arg)
- : Lazy_string(), str(str_arg), len(len_arg) {}
- void copy_to(String *dst) const
- { dst->copy(str, (uint32)len, system_charset_info); }
-};
-
-class Lazy_string_num : public Lazy_string
-{
- longlong m_value;
- bool m_unsigned;
-public:
- Lazy_string_num(longlong num_arg, bool unsigned_flag= false) :
- Lazy_string(), m_value(num_arg), m_unsigned(unsigned_flag) {}
- void copy_to(String *dst) const
- { dst->set_int(m_value, m_unsigned, &my_charset_bin); }
-};
-
-class Lazy_string_decimal: public Lazy_string
-{
- const my_decimal *d;
-public:
- Lazy_string_decimal(const my_decimal *d_arg)
- : Lazy_string(), d(d_arg) {}
- void copy_to(String *dst) const {
- my_decimal2string(E_DEC_FATAL_ERROR, d, 0, 0, ' ', dst);
- }
-};
-
-class Lazy_string_double: public Lazy_string
-{
- double num;
-public:
- Lazy_string_double(double num_arg) : Lazy_string(), num(num_arg) {}
- void copy_to(String *dst) const
- { dst->set_real(num, NOT_FIXED_DEC, &my_charset_bin); }
-};
-
-class Lazy_string_time : public Lazy_string
-{
- const MYSQL_TIME *ltime;
-public:
- Lazy_string_time(const MYSQL_TIME *ltime_arg)
- : Lazy_string(), ltime(ltime_arg) {}
- void copy_to(String *dst) const
- {
- dst->alloc(MAX_DATETIME_FULL_WIDTH);
- dst->length((uint) my_TIME_to_str(ltime, (char*) dst->ptr(),
- AUTO_SEC_PART_DIGITS));
- dst->set_charset(&my_charset_bin);
- }
-};
-
-static inline enum enum_mysql_timestamp_type
-mysql_type_to_time_type(enum enum_field_types mysql_type)
-{
- switch (mysql_type) {
- case MYSQL_TYPE_TIME: return MYSQL_TIMESTAMP_TIME;
- case MYSQL_TYPE_TIMESTAMP:
- 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;
- }
-}
-
-
-static inline uint
-mysql_temporal_int_part_length(enum enum_field_types mysql_type)
-{
- 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];
-}
-
-
-#include "sql_list.h"
-#include "sql_map.h"
-#include "handler.h"
-#include "parse_file.h"
-#include "table.h"
-#include "sql_error.h"
-#include "field.h" /* Field definitions */
-#include "protocol.h"
-#include "sql_udf.h"
-#include "sql_profile.h"
-#include "sql_partition.h"
-
-class user_var_entry;
-class Security_context;
-enum enum_var_type
-{
- OPT_DEFAULT= 0, OPT_SESSION, OPT_GLOBAL
-};
-class sys_var;
-#ifdef MYSQL_SERVER
-class Comp_creator;
-typedef Comp_creator* (*chooser_compare_func_creator)(bool invert);
-#endif
-#include "item.h"
-extern my_decimal decimal_zero;
-extern my_decimal max_seconds_for_time_type, time_second_part_factor;
-
-/* sql_parse.cc */
-void free_items(Item *item);
-void cleanup_items(Item *item);
-class THD;
-void close_thread_tables(THD *thd);
-
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
-bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables);
-bool check_single_table_access(THD *thd, ulong privilege,
- TABLE_LIST *tables, bool no_errors);
-bool check_routine_access(THD *thd,ulong want_access,char *db,char *name,
- bool is_proc, bool no_errors);
-bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table);
-bool check_some_routine_access(THD *thd, const char *db, const char *name, bool is_proc);
-#else
-inline bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables)
-{ return false; }
-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)
-{ return false; }
-inline bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
-{ return false; }
-inline bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *table_list)
-{ return false; }
-inline bool check_some_routine_access(THD *thd, const char *db,
- const char *name, bool is_proc)
-{ return false; }
-#define decrease_user_connections(X) do { } while(0) /* nothing */
-#endif /*NO_EMBEDDED_ACCESS_CHECKS*/
-
-bool multi_update_precheck(THD *thd, TABLE_LIST *tables);
-bool multi_delete_precheck(THD *thd, TABLE_LIST *tables);
-int mysql_multi_update_prepare(THD *thd);
-int mysql_multi_delete_prepare(THD *thd);
-bool mysql_insert_select_prepare(THD *thd);
-bool update_precheck(THD *thd, TABLE_LIST *tables);
-bool delete_precheck(THD *thd, TABLE_LIST *tables);
-bool insert_precheck(THD *thd, TABLE_LIST *tables);
-bool create_table_precheck(THD *thd, TABLE_LIST *tables,
- TABLE_LIST *create_table);
-int append_query_string(THD *thd, CHARSET_INFO *csinfo,
- String const *from, String *to);
-
-void get_default_definer(THD *thd, LEX_USER *definer);
-LEX_USER *create_default_definer(THD *thd);
-LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name);
-LEX_USER *get_current_user(THD *thd, LEX_USER *user);
-bool check_string_byte_length(LEX_STRING *str, const char *err_msg,
- uint max_byte_length);
-bool check_string_char_length(LEX_STRING *str, const char *err_msg,
- uint max_char_length, CHARSET_INFO *cs,
- bool no_error);
-bool check_host_name(LEX_STRING *str);
-
-bool parse_sql(THD *thd,
- Parser_state *parser_state,
- Object_creation_ctx *creation_ctx);
-
-enum enum_mysql_completiontype {
- ROLLBACK_RELEASE=-2, ROLLBACK=1, ROLLBACK_AND_CHAIN=7,
- COMMIT_RELEASE=-1, COMMIT=0, COMMIT_AND_CHAIN=6
-};
-
-bool begin_trans(THD *thd);
-bool end_active_trans(THD *thd);
-int end_trans(THD *thd, enum enum_mysql_completiontype completion);
-
-Item *negate_expression(THD *thd, Item *expr);
-
-/* log.cc */
-int vprint_msg_to_log(enum loglevel level, const char *format, va_list args);
-void sql_print_error(const char *format, ...);
-void sql_print_warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
-void sql_print_information(const char *format, ...)
- ATTRIBUTE_FORMAT(printf, 1, 2);
-typedef void (*sql_print_message_func)(const char *format, ...)
- ATTRIBUTE_FORMAT_FPTR(printf, 1, 2);
-extern sql_print_message_func sql_print_message_handlers[];
-
-int error_log_print(enum loglevel level, const char *format,
- va_list args);
-
-bool slow_log_print(THD *thd, const char *query, uint query_length,
- ulonglong current_utime);
-
-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);
-
-#include "sql_class.h"
-#include "sql_acl.h"
-#include "tztime.h"
-#ifdef MYSQL_SERVER
-#include "sql_servers.h"
-#include "opt_range.h"
-#include "sql_expression_cache.h"
-
-#ifdef HAVE_QUERY_CACHE
-struct Query_cache_query_flags
-{
- unsigned int client_long_flag:1;
- unsigned int client_protocol_41:1;
- unsigned int result_in_binary_protocol:1;
- unsigned int more_results_exists:1;
- unsigned int in_trans:1;
- unsigned int autocommit:1;
- unsigned int pkt_nr;
- uint character_set_client_num;
- uint character_set_results_num;
- uint collation_connection_num;
- ha_rows limit;
- Time_zone *time_zone;
- ulong sql_mode;
- ulong max_sort_length;
- ulong group_concat_max_len;
- ulong default_week_format;
- ulong div_precision_increment;
- MY_LOCALE *lc_time_names;
-};
-#define QUERY_CACHE_FLAGS_SIZE sizeof(Query_cache_query_flags)
-#define QUERY_CACHE_DB_LENGTH_SIZE 2
-#include "sql_cache.h"
-#define query_cache_store_query(A, B) query_cache.store_query(A, B)
-#define query_cache_destroy() query_cache.destroy()
-#define query_cache_result_size_limit(A) query_cache.result_size_limit(A)
-#define query_cache_init() query_cache.init()
-#define query_cache_resize(A) query_cache.resize(A)
-#define query_cache_set_min_res_unit(A) query_cache.set_min_res_unit(A)
-#define query_cache_invalidate3(A, B, C) query_cache.invalidate(A, B, C)
-#define query_cache_invalidate1(A,B) query_cache.invalidate(A,B)
-#define query_cache_send_result_to_client(A, B, C) \
- query_cache.send_result_to_client(A, B, C)
-#define query_cache_invalidate_by_MyISAM_filename_ref \
- &query_cache_invalidate_by_MyISAM_filename
-/* note the "maybe": it's a read without mutex */
-#define query_cache_maybe_disabled(T) \
- (T->variables.query_cache_type == 0 || query_cache.query_cache_size == 0)
-#define query_cache_is_cacheable_query(L) \
- (((L)->sql_command == SQLCOM_SELECT) && (L)->safe_to_cache_query)
-#else
-#define QUERY_CACHE_FLAGS_SIZE 0
-#define QUERY_CACHE_DB_LENGTH_SIZE 0
-#define query_cache_store_query(A, B) do { } while(0)
-#define query_cache_destroy() do { } while(0)
-#define query_cache_result_size_limit(A) do { } while(0)
-#define query_cache_init() do { } while(0)
-#define query_cache_resize(A) do { } while(0)
-#define query_cache_set_min_res_unit(A) do { } while(0)
-#define query_cache_invalidate3(A, B, C) do { } while(0)
-#define query_cache_invalidate1(A,B) do { } while(0)
-#define query_cache_send_result_to_client(A, B, C) 0
-#define query_cache_invalidate_by_MyISAM_filename_ref NULL
-#define query_cache_abort(A) do { } while(0)
-#define query_cache_end_of_result(A) do { } while(0)
-#define query_cache_maybe_disabled(T) 1
-#define query_cache_is_cacheable_query(L) 0
-#endif /*HAVE_QUERY_CACHE*/
-
-uint kill_one_thread(THD *thd, ulong id, killed_state kill_signal);
-void sql_kill(THD *thd, ulong id, killed_state kill_signal);
-void sql_kill_user(THD *thd, LEX_USER *str, killed_state kill_signal);
-
-/*
- Error injector Macros to enable easy testing of recovery after failures
- in various error cases.
-*/
-#ifndef ERROR_INJECT_SUPPORT
-
-#define ERROR_INJECT(x) 0
-#define ERROR_INJECT_ACTION(x,action) 0
-#define ERROR_INJECT_CRASH(x) 0
-#define ERROR_INJECT_VALUE(x) 0
-#define ERROR_INJECT_VALUE_ACTION(x,action) 0
-#define ERROR_INJECT_VALUE_CRASH(x) 0
-#define SET_ERROR_INJECT_VALUE(x)
-
-#else
-
-inline bool check_and_unset_keyword(const char *dbug_str)
-{
- const char *extra_str= "-d,";
- char total_str[200];
- if (_db_keyword_ (0, dbug_str, 1))
- {
- strxmov(total_str, extra_str, dbug_str, NullS);
- DBUG_SET(total_str);
- return 1;
- }
- return 0;
-}
-
-
-inline bool
-check_and_unset_inject_value(int value)
-{
- THD *thd= current_thd;
- if (thd->error_inject_value == (uint)value)
- {
- thd->error_inject_value= 0;
- return 1;
- }
- return 0;
-}
-
-/*
- ERROR INJECT MODULE:
- --------------------
- These macros are used to insert macros from the application code.
- The event that activates those error injections can be activated
- from SQL by using:
- SET SESSION dbug=+d,code;
-
- After the error has been injected, the macros will automatically
- remove the debug code, thus similar to using:
- SET SESSION dbug=-d,code
- from SQL.
-
- ERROR_INJECT_CRASH will inject a crash of the MySQL Server if code
- is set when macro is called. ERROR_INJECT_CRASH can be used in
- if-statements, it will always return FALSE unless of course it
- crashes in which case it doesn't return at all.
-
- ERROR_INJECT_ACTION will inject the action specified in the action
- parameter of the macro, before performing the action the code will
- be removed such that no more events occur. ERROR_INJECT_ACTION
- can also be used in if-statements and always returns FALSE.
- ERROR_INJECT can be used in a normal if-statement, where the action
- part is performed in the if-block. The macro returns TRUE if the
- error was activated and otherwise returns FALSE. If activated the
- code is removed.
-
- Sometimes it is necessary to perform error inject actions as a serie
- of events. In this case one can use one variable on the THD object.
- Thus one sets this value by using e.g. SET_ERROR_INJECT_VALUE(100).
- Then one can later test for it by using ERROR_INJECT_CRASH_VALUE,
- ERROR_INJECT_ACTION_VALUE and ERROR_INJECT_VALUE. This have the same
- behaviour as the above described macros except that they use the
- error inject value instead of a code used by DBUG macros.
-*/
-#define SET_ERROR_INJECT_VALUE(x) \
- current_thd->error_inject_value= (x)
-#define ERROR_INJECT_CRASH(code) \
- DBUG_EVALUATE_IF(code, (DBUG_ABORT(), 0), 0)
-#define ERROR_INJECT_ACTION(code, action) \
- (check_and_unset_keyword(code) ? ((action), 0) : 0)
-#define ERROR_INJECT(code) \
- check_and_unset_keyword(code)
-#define ERROR_INJECT_VALUE(value) \
- check_and_unset_inject_value(value)
-#define ERROR_INJECT_VALUE_ACTION(value,action) \
- (check_and_unset_inject_value(value) ? (action) : 0)
-#define ERROR_INJECT_VALUE_CRASH(value) \
- ERROR_INJECT_VALUE_ACTION(value, (DBUG_ABORT(), 0))
-
-#endif
-
-int write_bin_log(THD *thd, bool clear_error,
- char const *query, ulong query_length);
-
-/* sql_connect.cc */
-pthread_handler_t handle_one_connection(void *arg);
-bool init_new_connection_handler_thread();
-void reset_mqh(LEX_USER *lu, bool get_them);
-bool check_mqh(THD *thd, uint check_command);
-void time_out_user_resource_limits(THD *thd, USER_CONN *uc);
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
-void decrease_user_connections(USER_CONN *uc);
-#endif
-bool thd_init_client_charset(THD *thd, uint cs_number);
-inline bool is_supported_parser_charset(CHARSET_INFO *cs)
-{
- return test(cs->mbminlen == 1);
-}
-bool setup_connection_thread_globals(THD *thd);
-bool login_connection(THD *thd);
-void end_connection(THD *thd);
-void prepare_new_connection_state(THD* thd);
-void update_global_user_stats(THD* thd, bool create_user, time_t now);
-int get_or_create_user_conn(THD *thd, const char *user,
- const char *host, USER_RESOURCES *mqh);
-int check_for_max_user_connections(THD *thd, USER_CONN *uc);
-
-int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create, bool silent);
-bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create);
-bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent);
-bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db);
-void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, ushort flags);
-void mysql_client_binlog_statement(THD *thd);
-bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
- my_bool drop_temporary);
-int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
- bool drop_temporary, bool drop_view, bool log_query);
-bool quick_rm_table(handlerton *base,const char *db,
- const char *table_name, uint flags);
-void close_cached_table(THD *thd, TABLE *table);
-bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent);
-bool do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db,
- char *new_table_name, char *new_table_alias,
- bool skip_error);
-
-bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name,
- bool force_switch);
-
-bool mysql_opt_change_db(THD *thd,
- const LEX_STRING *new_db_name,
- LEX_STRING *saved_db_name,
- bool force_switch,
- bool *cur_db_changed);
-
-void mysql_parse(THD *thd, char *rawbuf, uint length,
- const char ** semicolon);
-
-bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length);
-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);
-void mysql_init_select(LEX *lex);
-void mysql_reset_thd_for_next_command(THD *thd, my_bool calculate_userstat);
-bool mysql_new_select(LEX *lex, bool move_down);
-void create_select_for_variable(const char *var_name);
-void mysql_init_multi_delete(LEX *lex);
-bool multi_delete_set_locks_and_link_aux_tables(LEX *lex);
-void init_max_user_conn(void);
-void init_update_queries(void);
-void init_global_user_stats(void);
-void init_global_table_stats(void);
-void init_global_index_stats(void);
-void init_global_client_stats(void);
-void free_max_user_conn(void);
-void free_global_user_stats(void);
-void free_global_table_stats(void);
-void free_global_index_stats(void);
-void free_global_client_stats(void);
-pthread_handler_t handle_bootstrap(void *arg);
-int mysql_execute_command(THD *thd);
-bool do_command(THD *thd);
-bool dispatch_command(enum enum_server_command command, THD *thd,
- char* packet, uint packet_length);
-void log_slow_statement(THD *thd);
-bool check_dup(const char *db, const char *name, TABLE_LIST *tables);
-bool records_are_comparable(const TABLE *table);
-bool compare_record(const TABLE *table);
-bool append_file_to_dir(THD *thd, const char **filename_ptr,
- const char *table_name);
-void wait_while_table_is_used(THD *thd, TABLE *table,
- enum ha_extra_function function);
-bool table_cache_init(void);
-void table_cache_free(void);
-bool table_def_init(void);
-void table_def_free(void);
-void assign_new_table_id(TABLE_SHARE *share);
-uint cached_open_tables(void);
-uint cached_table_definitions(void);
-void kill_mysql(void);
-void close_connection(THD *thd, uint errcode, bool lock);
-bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
- int *write_to_binlog);
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
-bool check_access(THD *thd, ulong access, const char *db, ulong *save_priv,
- bool no_grant, bool no_errors, bool schema_db);
-bool check_table_access(THD *thd, ulong want_access, TABLE_LIST *tables,
- uint number, bool no_errors);
-#else
-inline bool check_access(THD *thd, ulong access, const char *db,
- ulong *save_priv, bool no_grant, bool no_errors,
- bool schema_db)
-{
- if (save_priv)
- *save_priv= GLOBAL_ACLS;
- return false;
-}
-inline bool check_table_access(THD *thd, ulong want_access, TABLE_LIST *tables,
- uint number, bool no_errors)
-{ return false; }
-#endif /*NO_EMBEDDED_ACCESS_CHECKS*/
-
-#endif /* MYSQL_SERVER */
-#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
-bool check_global_access(THD *thd, ulong want_access);
-#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
-#ifdef MYSQL_SERVER
-
-/*
- Support routine for SQL parser on partitioning syntax
-*/
-my_bool is_partition_management(LEX *lex);
-/*
- General routine to change field->ptr of a NULL-terminated array of Field
- objects. Useful when needed to call val_int, val_str or similar and the
- field data is not in table->record[0] but in some other structure.
- set_key_field_ptr changes all fields of an index using a key_info object.
- All methods presume that there is at least one field to change.
-*/
-
-void set_field_ptr(Field **ptr, const uchar *new_buf, const uchar *old_buf);
-void set_key_field_ptr(KEY *key_info, const uchar *new_buf,
- const uchar *old_buf);
-
-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_check_table(THD* thd, TABLE_LIST* table_list,
- HA_CHECK_OPT* check_opt);
-bool mysql_repair_table(THD* thd, TABLE_LIST* table_list,
- HA_CHECK_OPT* check_opt);
-bool mysql_analyze_table(THD* thd, TABLE_LIST* table_list,
- HA_CHECK_OPT* check_opt);
-bool mysql_optimize_table(THD* thd, TABLE_LIST* table_list,
- HA_CHECK_OPT* check_opt);
-bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* table_list,
- LEX_STRING *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);
-TABLE *create_virtual_tmp_table(THD *thd, List<Create_field> &field_list);
-
-bool mysql_xa_recover(THD *thd);
-
-bool check_simple_select();
-int mysql_alter_tablespace(THD* thd, st_alter_tablespace *ts_info);
-
-SORT_FIELD * make_unireg_sortorder(ORDER *order, uint *length,
- SORT_FIELD *sortorder);
-int setup_order(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
- List<Item> &fields, List <Item> &all_fields, ORDER *order);
-int setup_group(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
- List<Item> &fields, List<Item> &all_fields, ORDER *order,
- bool *hidden_group_fields);
-bool fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
- Item **ref_pointer_array);
-
-bool handle_select(THD *thd, LEX *lex, select_result *result,
- ulong setup_tables_done_option);
-bool mysql_select(THD *thd, Item ***rref_pointer_array,
- TABLE_LIST *tables, uint wild_num, List<Item> &list,
- COND *conds, uint og_num, ORDER *order, ORDER *group,
- Item *having, ORDER *proc_param, ulonglong select_type,
- select_result *result, SELECT_LEX_UNIT *unit,
- SELECT_LEX *select_lex);
-
-int join_read_key2(THD *thd, struct st_join_table *tab, TABLE *table,
- struct st_table_ref *table_ref);
-void free_underlaid_joins(THD *thd, SELECT_LEX *select);
-bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit,
- select_result *result);
-int mysql_explain_select(THD *thd, SELECT_LEX *sl, char const *type,
- select_result *result);
-bool mysql_union(THD *thd, LEX *lex, select_result *result,
- SELECT_LEX_UNIT *unit, ulong setup_tables_done_option);
-bool mysql_handle_derived(LEX *lex, uint phases);
-bool mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived, uint phases);
-bool mysql_handle_list_of_derived(LEX *lex, TABLE_LIST *dt_list, uint phases);
-bool check_table_file_presence(char *old_path, char *path,
- const char *db,
- const char *table_name,
- const char *alias,
- bool issue_error);
-Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
- Item ***copy_func, Field **from_field,
- Field **def_field,
- bool group, bool modify_item,
- bool table_cant_handle_bit_fields,
- bool make_copy_field,
- uint convert_blob_length);
-bool open_tmp_table(TABLE *table);
-bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
- ENGINE_COLUMNDEF *start_recinfo,
- ENGINE_COLUMNDEF **recinfo,
- ulonglong options);
-
-void sp_prepare_create_field(THD *thd, Create_field *sql_field);
-int prepare_create_field(Create_field *sql_field,
- uint *blob_columns,
- int *timestamps, int *timestamps_with_niladic,
- longlong table_flags);
-bool mysql_create_table(THD *thd,const char *db, const char *table_name,
- HA_CREATE_INFO *create_info,
- Alter_info *alter_info,
- bool tmp_table, uint select_field_count);
-bool mysql_create_table_no_lock(THD *thd, const char *db,
- const char *table_name,
- HA_CREATE_INFO *create_info,
- Alter_info *alter_info,
- bool tmp_table, uint select_field_count);
-
-bool mysql_alter_table(THD *thd, char *new_db, char *new_name,
- HA_CREATE_INFO *create_info,
- TABLE_LIST *table_list,
- Alter_info *alter_info,
- uint order_num, ORDER *order, bool ignore,
- bool require_online);
-bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list);
-bool mysql_create_like_table(THD *thd, TABLE_LIST *table,
- TABLE_LIST *src_table,
- HA_CREATE_INFO *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_prepare_update(THD *thd, TABLE_LIST *table_list,
- Item **conds, uint order_num, ORDER *order);
-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);
-bool mysql_multi_update(THD *thd, TABLE_LIST *table_list,
- List<Item> *fields, List<Item> *values,
- COND *conds, ulonglong options,
- enum enum_duplicates handle_duplicates, bool ignore,
- SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex);
-bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table,
- List<Item> &fields, List_item *values,
- List<Item> &update_fields,
- List<Item> &update_values, enum_duplicates duplic,
- COND **where, bool select_insert,
- bool check_fields, bool abort_on_warning);
-bool mysql_insert(THD *thd,TABLE_LIST *table,List<Item> &fields,
- List<List_item> &values, List<Item> &update_fields,
- List<Item> &update_values, enum_duplicates flag,
- bool ignore);
-void upgrade_lock_type_for_insert(THD *thd, thr_lock_type *lock_type,
- enum_duplicates duplic,
- bool is_multi_insert);
-int check_that_all_fields_are_given_values(THD *thd, TABLE *entry,
- TABLE_LIST *table_list);
-void prepare_triggers_for_insert_stmt(TABLE *table);
-int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds);
-bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
- SQL_I_List<ORDER> *order, ha_rows rows, ulonglong options,
- bool reset_auto_increment);
-bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok);
-bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create);
-uint create_table_def_key(THD *thd, char *key, TABLE_LIST *table_list,
- bool tmp_table);
-
-/**
- Create a table cache key for non-temporary table.
-
- @param key Buffer for key (must be at least NAME_LEN*2+2 bytes).
- @param db Database name.
- @param table_name Table name.
-
- @return Length of key.
-
- @sa create_table_def_key(thd, char *, table_list, bool)
-*/
-
-inline uint
-create_table_def_key(char *key, const char *db, const char *table_name)
-{
- /*
- In theory caller should ensure that both db and table_name are
- not longer than NAME_LEN bytes. In practice we play safe to avoid
- buffer overruns.
- */
- return (uint)(strmake(strmake(key, db, NAME_LEN) + 1, table_name,
- NAME_LEN) - key + 1);
-}
-
-TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key,
- uint key_length, uint db_flags, int *error);
-void release_table_share(TABLE_SHARE *share, enum release_type type);
-TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name);
-TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update,
- uint lock_flags);
-TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem,
- bool *refresh, uint flags);
-bool name_lock_locked_table(THD *thd, TABLE_LIST *tables);
-bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in);
-TABLE *table_cache_insert_placeholder(THD *thd, const char *key,
- uint key_length);
-bool lock_table_name_if_not_cached(THD *thd, const char *db,
- const char *table_name, TABLE **table);
-TABLE *find_locked_table(THD *thd, const char *db,const char *table_name);
-void detach_merge_children(TABLE *table, bool clear_refs);
-bool fix_merge_after_open(TABLE_LIST *old_child_list, TABLE_LIST **old_last,
- TABLE_LIST *new_child_list, TABLE_LIST **new_last);
-bool reopen_table(TABLE *table);
-bool reopen_tables(THD *thd,bool get_locks,bool in_refresh);
-thr_lock_type read_lock_type_for_table(THD *thd, LEX *lex,
- TABLE_LIST *table_list);
-void close_data_files_and_morph_locks(THD *thd, const char *db,
- const char *table_name);
-void close_handle_and_leave_table_as_lock(TABLE *table);
-bool wait_for_tables(THD *thd);
-bool table_is_used(TABLE *table, bool wait_for_name_lock);
-TABLE *drop_locked_tables(THD *thd,const char *db, const char *table_name);
-void abort_locked_tables(THD *thd,const char *db, const char *table_name);
-void execute_init_command(THD *thd, sys_var_str *init_command_var,
- rw_lock_t *var_mutex);
-extern Field *not_found_field;
-extern Field *view_ref_found;
-
-enum find_item_error_report_type {REPORT_ALL_ERRORS, REPORT_EXCEPT_NOT_FOUND,
- IGNORE_ERRORS, REPORT_EXCEPT_NON_UNIQUE,
- IGNORE_EXCEPT_NON_UNIQUE};
-Field *
-find_field_in_tables(THD *thd, Item_ident *item,
- TABLE_LIST *first_table, TABLE_LIST *last_table,
- Item **ref, find_item_error_report_type report_error,
- 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 *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,
- bool allow_rowid, uint *cached_field_index_ptr);
-Field *
-find_field_in_table_sef(TABLE *table, const char *name);
-int update_virtual_fields(THD *thd, TABLE *table,
- enum enum_vcol_update_mode vcol_update_mode= VCOL_UPDATE_FOR_READ);
-int dynamic_column_error_message(enum_dyncol_func_result rc);
-
-#endif /* MYSQL_SERVER */
-
-#ifdef HAVE_OPENSSL
-#include <openssl/des.h>
-struct st_des_keyblock
-{
- DES_cblock key1, key2, key3;
-};
-struct st_des_keyschedule
-{
- DES_key_schedule ks1, ks2, ks3;
-};
-extern char *des_key_file;
-extern struct st_des_keyschedule des_keyschedule[10];
-extern uint des_default_key;
-extern pthread_mutex_t LOCK_des_key_file;
-bool load_des_key_file(const char *file_name);
-#endif /* HAVE_OPENSSL */
-
-#ifdef MYSQL_SERVER
-/* sql_do.cc */
-bool mysql_do(THD *thd, List<Item> &values);
-
-/* sql_analyse.h */
-bool append_escaped(String *to_str, String *from_str);
-
-/* sql_show.cc */
-bool mysqld_show_open_tables(THD *thd,const char *wild);
-bool mysqld_show_logs(THD *thd);
-bool append_identifier(THD *thd, String *packet, const char *name,
- uint length);
-#endif /* MYSQL_SERVER */
-#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
-int get_quote_char_for_identifier(THD *thd, const char *name, uint length);
-#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
-#ifdef MYSQL_SERVER
-void mysqld_list_fields(THD *thd,TABLE_LIST *table, const char *wild);
-int mysqld_dump_create_info(THD *thd, TABLE_LIST *table_list, int fd);
-bool mysqld_show_create(THD *thd, TABLE_LIST *table_list);
-bool mysqld_show_create_db(THD *thd, char *dbname, HA_CREATE_INFO *create);
-
-void mysqld_list_processes(THD *thd,const char *user,bool verbose);
-int mysqld_show_status(THD *thd);
-int mysqld_show_variables(THD *thd,const char *wild);
-bool mysqld_show_storage_engines(THD *thd);
-bool mysqld_show_authors(THD *thd);
-bool mysqld_show_contributors(THD *thd);
-bool mysqld_show_privileges(THD *thd);
-bool mysqld_show_column_types(THD *thd);
-bool mysqld_help (THD *thd, const char *text);
-void 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);
-
-int add_status_vars(SHOW_VAR *list);
-void remove_status_vars(SHOW_VAR *list);
-void init_status_vars();
-void free_status_vars();
-void reset_status_vars();
-
-/* information schema */
-extern LEX_STRING INFORMATION_SCHEMA_NAME;
-/* log tables */
-extern LEX_STRING MYSQL_SCHEMA_NAME;
-extern LEX_STRING GENERAL_LOG_NAME;
-extern LEX_STRING SLOW_LOG_NAME;
-
-extern const LEX_STRING partition_keywords[];
-ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name);
-ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx);
-int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
- enum enum_schema_tables schema_table_idx);
-int make_schema_select(THD *thd, SELECT_LEX *sel,
- enum enum_schema_tables schema_table_idx);
-int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list);
-int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
-int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
-int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
-int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
-bool get_schema_tables_result(JOIN *join,
- enum enum_schema_table_state executed_place);
-enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table);
-
-inline bool is_schema_db(const char *name, size_t len)
-{
- return (INFORMATION_SCHEMA_NAME.length == len &&
- !my_strcasecmp(system_charset_info,
- INFORMATION_SCHEMA_NAME.str, name));
-}
-
-inline bool is_schema_db(const char *name)
-{
- return !my_strcasecmp(system_charset_info,
- INFORMATION_SCHEMA_NAME.str, name);
-}
-
-/* sql_prepare.cc */
-
-void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length);
-void mysqld_stmt_execute(THD *thd, char *packet, uint packet_length);
-void mysqld_stmt_close(THD *thd, char *packet);
-void mysql_sql_stmt_prepare(THD *thd);
-void mysql_sql_stmt_execute(THD *thd);
-void mysql_sql_stmt_close(THD *thd);
-void mysqld_stmt_fetch(THD *thd, char *packet, uint packet_length);
-void mysqld_stmt_reset(THD *thd, char *packet);
-void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
-void reinit_stmt_before_use(THD *thd, LEX *lex);
-
-/* sql_base.cc */
-#define TMP_TABLE_KEY_EXTRA 8
-void set_item_name(Item *item,char *pos,uint length);
-bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum enum_field_types type,
- char *length, char *decimal,
- uint type_modifier,
- Item *default_value, Item *on_update_value,
- LEX_STRING *comment,
- char *change, List<String> *interval_list,
- CHARSET_INFO *cs,
- uint uint_geom_type,
- Virtual_column_info *vcol_info,
- engine_option_value *create_options);
-Create_field * new_create_field(THD *thd, char *field_name, enum_field_types type,
- char *length, char *decimals,
- uint type_modifier,
- Item *default_value, Item *on_update_value,
- LEX_STRING *comment, char *change,
- List<String> *interval_list, CHARSET_INFO *cs,
- uint uint_geom_type,
- Virtual_column_info *vcol_info);
-void store_position_for_column(const char *name);
-bool add_to_list(THD *thd, SQL_I_List<ORDER> &list, Item *group,bool asc);
-bool push_new_name_resolution_context(THD *thd,
- TABLE_LIST *left_op,
- TABLE_LIST *right_op);
-Item *normalize_cond(Item *cond);
-void add_join_on(TABLE_LIST *b,Item *expr);
-void add_join_natural(TABLE_LIST *a,TABLE_LIST *b,List<String> *using_fields,
- SELECT_LEX *lex);
-bool add_proc_to_list(THD *thd, Item *item);
-void unlink_open_table(THD *thd, TABLE *find, bool unlock);
-void drop_open_table(THD *thd, TABLE *table, const char *db_name,
- const char *table_name);
-void update_non_unique_table_error(TABLE_LIST *update,
- const char *operation,
- TABLE_LIST *duplicate);
-
-SQL_SELECT *make_select(TABLE *head, table_map const_tables,
- table_map read_tables, COND *conds,
- bool allow_null_cond, int *error);
-extern Item **not_found_item;
-
-/*
- This enumeration type is used only by the function find_item_in_list
- to return the info on how an item has been resolved against a list
- of possibly aliased items.
- The item can be resolved:
- - against an alias name of the list's element (RESOLVED_AGAINST_ALIAS)
- - against non-aliased field name of the list (RESOLVED_WITH_NO_ALIAS)
- - against an aliased field name of the list (RESOLVED_BEHIND_ALIAS)
- - ignoring the alias name in cases when SQL requires to ignore aliases
- (e.g. when the resolved field reference contains a table name or
- when the resolved item is an expression) (RESOLVED_IGNORING_ALIAS)
-*/
-enum enum_resolution_type {
- NOT_RESOLVED=0,
- RESOLVED_IGNORING_ALIAS,
- RESOLVED_BEHIND_ALIAS,
- RESOLVED_WITH_NO_ALIAS,
- RESOLVED_AGAINST_ALIAS
-};
-Item ** find_item_in_list(Item *item, List<Item> &items, uint *counter,
- find_item_error_report_type report_error,
- enum_resolution_type *resolution);
-bool get_key_map_from_key_list(key_map *map, TABLE *table,
- List<String> *index_list);
-bool insert_fields(THD *thd, Name_resolution_context *context,
- const char *db_name, const char *table_name,
- List_iterator<Item> *it, bool any_privileges);
-void make_leaves_list(List<TABLE_LIST> &list, TABLE_LIST *tables,
- bool full_table_list, TABLE_LIST *boundary);
-bool setup_tables(THD *thd, Name_resolution_context *context,
- List<TABLE_LIST> *from_clause, TABLE_LIST *tables,
- List<TABLE_LIST> &leaves, bool select_insert,
- bool full_table_list);
-bool setup_tables_and_check_access(THD *thd,
- Name_resolution_context *context,
- List<TABLE_LIST> *from_clause,
- TABLE_LIST *tables,
- List<TABLE_LIST> &leaves,
- bool select_insert,
- ulong want_access_first,
- ulong want_access,
- bool full_table_list);
-int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
- List<Item> *sum_func_list, uint wild_num);
-bool setup_fields(THD *thd, Item** ref_pointer_array,
- List<Item> &item, enum_mark_columns mark_used_columns,
- List<Item> *sum_func_list, bool allow_sum_func);
-inline bool setup_fields_with_no_wrap(THD *thd, Item **ref_pointer_array,
- List<Item> &item,
- enum_mark_columns mark_used_columns,
- 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, sum_func_list,
- allow_sum_func);
- thd->lex->select_lex.no_wrap_view_item= FALSE;
- return res;
-}
-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);
-int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order);
-void wait_for_condition(THD *thd, pthread_mutex_t *mutex,
- pthread_cond_t *cond);
-int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags);
-/* open_and_lock_tables with optional derived handling */
-int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived);
-/* simple open_and_lock_tables without derived handling */
-inline int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables)
-{
- return open_and_lock_tables_derived(thd, tables, FALSE);
-}
-/* open_and_lock_tables with derived handling */
-inline int open_and_lock_tables(THD *thd, TABLE_LIST *tables)
-{
- return open_and_lock_tables_derived(thd, tables, TRUE);
-}
-/* simple open_and_lock_tables without derived handling for single table */
-TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l,
- thr_lock_type lock_type);
-bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags,
- uint dt_phases);
-int lock_tables(THD *thd, TABLE_LIST *tables, uint counter, bool *need_reopen);
-int decide_logging_format(THD *thd, TABLE_LIST *tables);
-TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
- const char *table_name, bool link_in_list);
-bool rm_temporary_table(handlerton *base, const char *path);
-void free_io_cache(TABLE *entry);
-void intern_close_table(TABLE *entry);
-bool close_thread_table(THD *thd, TABLE **table_ptr);
-void close_temporary_tables(THD *thd);
-void close_tables_for_reopen(THD *thd, TABLE_LIST **tables);
-TABLE_LIST *find_table_in_list(TABLE_LIST *table,
- TABLE_LIST *TABLE_LIST::*link,
- const char *db_name,
- const char *table_name);
-TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
- bool check_alias);
-TABLE *find_temporary_table(THD *thd, const char *db, const char *table_name);
-TABLE *find_temporary_table(THD *thd, TABLE_LIST *table_list);
-int drop_temporary_table(THD *thd, TABLE_LIST *table_list);
-void close_temporary_table(THD *thd, TABLE *table, bool free_share,
- bool delete_table);
-void close_temporary(TABLE *table, bool free_share, bool delete_table);
-bool rename_temporary_table(THD* thd, TABLE *table, const char *new_db,
- const char *table_name);
-void remove_db_from_cache(const char *db);
-void flush_tables();
-bool is_equal(const LEX_STRING *a, const LEX_STRING *b);
-char *make_default_log_name(char *buff,const char* log_ext);
-char *make_once_alloced_filename(const char *basename, const char *ext);
-void unfix_fields(List<Item> &items);
-
-#ifdef WITH_PARTITION_STORAGE_ENGINE
-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,
- uint fast_alter_partition);
-uint set_part_state(Alter_info *alter_info, partition_info *tab_part_info,
- enum partition_state part_state);
-uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
- HA_CREATE_INFO *create_info,
- handlerton *old_db_type,
- bool *partition_changed,
- uint *fast_alter_partition);
-#endif
-
-/* bits for last argument to remove_table_from_cache() */
-#define RTFC_NO_FLAG 0x0000
-#define RTFC_OWNED_BY_THD_FLAG 0x0001
-#define RTFC_WAIT_OTHER_THREAD_FLAG 0x0002
-#define RTFC_CHECK_KILLED_FLAG 0x0004
-bool remove_table_from_cache(THD *thd, const char *db, const char *table,
- uint flags, my_bool deleting);
-
-#define NORMAL_PART_NAME 0
-#define TEMP_PART_NAME 1
-#define RENAMED_PART_NAME 2
-void create_partition_name(char *out, const char *in1,
- const char *in2, uint name_variant,
- bool translate);
-void create_subpartition_name(char *out, const char *in1,
- const char *in2, const char *in3,
- uint name_variant);
-
-typedef struct st_lock_param_type
-{
- TABLE_LIST table_list;
- ulonglong copied;
- ulonglong deleted;
- THD *thd;
- HA_CREATE_INFO *create_info;
- Alter_info *alter_info;
- TABLE *table;
- KEY *key_info_buffer;
- const char *db;
- const char *table_name;
- uchar *pack_frm_data;
- enum thr_lock_type old_lock_type;
- uint key_count;
- uint db_options;
- size_t pack_frm_len;
- partition_info *part_info;
-} ALTER_PARTITION_PARAM_TYPE;
-
-void mem_alloc_error(size_t size);
-
-enum ddl_log_entry_code
-{
- /*
- DDL_LOG_EXECUTE_CODE:
- This is a code that indicates that this is a log entry to
- be executed, from this entry a linked list of log entries
- can be found and executed.
- DDL_LOG_ENTRY_CODE:
- An entry to be executed in a linked list from an execute log
- entry.
- DDL_IGNORE_LOG_ENTRY_CODE:
- An entry that is to be ignored
- */
- DDL_LOG_EXECUTE_CODE = 'e',
- DDL_LOG_ENTRY_CODE = 'l',
- DDL_IGNORE_LOG_ENTRY_CODE = 'i'
-};
-
-enum ddl_log_action_code
-{
- /*
- The type of action that a DDL_LOG_ENTRY_CODE entry is to
- perform.
- DDL_LOG_DELETE_ACTION:
- Delete an entity
- DDL_LOG_RENAME_ACTION:
- Rename an entity
- DDL_LOG_REPLACE_ACTION:
- Rename an entity after removing the previous entry with the
- new name, that is replace this entry.
- */
- DDL_LOG_DELETE_ACTION = 'd',
- DDL_LOG_RENAME_ACTION = 'r',
- DDL_LOG_REPLACE_ACTION = 's'
-};
-
-
-typedef struct st_ddl_log_entry
-{
- const char *name;
- const char *from_name;
- const char *handler_name;
- uint next_entry;
- uint entry_pos;
- enum ddl_log_entry_code entry_type;
- enum ddl_log_action_code action_type;
- /*
- Most actions have only one phase. REPLACE does however have two
- phases. The first phase removes the file with the new name if
- there was one there before and the second phase renames the
- old name to the new name.
- */
- char phase;
-} DDL_LOG_ENTRY;
-
-typedef struct st_ddl_log_memory_entry
-{
- uint entry_pos;
- struct st_ddl_log_memory_entry *next_log_entry;
- struct st_ddl_log_memory_entry *prev_log_entry;
- struct st_ddl_log_memory_entry *next_active_log_entry;
-} DDL_LOG_MEMORY_ENTRY;
-
-
-bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
- DDL_LOG_MEMORY_ENTRY **active_entry);
-bool write_execute_ddl_log_entry(uint first_entry,
- bool complete,
- DDL_LOG_MEMORY_ENTRY **active_entry);
-bool deactivate_ddl_log_entry(uint entry_no);
-void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry);
-bool sync_ddl_log();
-void release_ddl_log();
-void execute_ddl_log_recovery();
-bool execute_ddl_log_entry(THD *thd, uint first_entry);
-
-extern pthread_mutex_t LOCK_gdl;
-
-#define WFRM_WRITE_SHADOW 1
-#define WFRM_INSTALL_SHADOW 2
-#define WFRM_PACK_FRM 4
-#define WFRM_KEEP_SHARE 8
-bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags);
-int abort_and_upgrade_lock_and_close_table(ALTER_PARTITION_PARAM_TYPE *lpt);
-void close_open_tables_and_downgrade(ALTER_PARTITION_PARAM_TYPE *lpt);
-void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table);
-
-/* Functions to work with system tables. */
-bool open_system_tables_for_read(THD *thd, TABLE_LIST *table_list,
- Open_tables_state *backup);
-void close_system_tables(THD *thd, Open_tables_state *backup);
-TABLE *open_system_table_for_update(THD *thd, TABLE_LIST *one_table);
-
-TABLE *open_performance_schema_table(THD *thd, TABLE_LIST *one_table,
- Open_tables_state *backup);
-void close_performance_schema_table(THD *thd, Open_tables_state *backup);
-
-bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock,
- bool wait_for_refresh, bool wait_for_placeholders);
-bool close_cached_tables_set_readonly(THD *thd);
-bool close_cached_connection_tables(THD *thd, bool wait_for_refresh,
- LEX_STRING *connect_string,
- bool have_lock = FALSE);
-void copy_field_from_tmp_record(Field *field,int offset);
-bool fill_record(THD *thd, Field **field, List<Item> &values,
- bool ignore_errors, bool use_value);
-bool fill_record_n_invoke_before_triggers(THD *thd, List<Item> &fields,
- List<Item> &values,
- bool ignore_errors,
- Table_triggers_list *triggers,
- enum trg_event_type event);
-bool fill_record_n_invoke_before_triggers(THD *thd, Field **field,
- List<Item> &values,
- bool ignore_errors,
- Table_triggers_list *triggers,
- enum trg_event_type event);
-OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild);
-
-inline TABLE_LIST *find_table_in_global_list(TABLE_LIST *table,
- const char *db_name,
- const char *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);
-}
-
-
-/* sql_calc.cc */
-bool eval_const_cond(COND *cond);
-
-/* sql_load.cc */
-int mysql_load(THD *thd, sql_exchange *ex, TABLE_LIST *table_list,
- List<Item> &fields_vars, List<Item> &set_fields,
- List<Item> &set_values_list,
- enum enum_duplicates handle_duplicates, bool ignore,
- bool local_file);
-int write_record(THD *thd, TABLE *table, COPY_INFO *info);
-
-/* sql_manager.cc */
-extern bool volatile mqh_used;
-void start_handle_manager();
-void stop_handle_manager();
-bool mysql_manager_submit(void (*action)());
-
-
-/* sql_test.cc */
-#ifndef DBUG_OFF
-void print_where(COND *cond,const char *info, enum_query_type query_type);
-void print_cached_tables(void);
-void TEST_filesort(SORT_FIELD *sortorder,uint s_length);
-void print_plan(JOIN* join,uint idx, double record_count, double read_time,
- double current_read_time, const char *info);
-void print_keyuse_array(DYNAMIC_ARRAY *keyuse_array);
-void print_sjm(SJ_MATERIALIZATION_INFO *sjm);
-#endif
-void mysql_print_status();
-/* key.cc */
-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_restore(uchar *to_record, uchar *from_key, KEY *key_info,
- uint key_length);
-bool key_cmp_if_same(TABLE *form,const uchar *key,uint index,uint key_length);
-void key_unpack(String *to,TABLE *form,uint index);
-bool is_key_used(TABLE *table, uint idx, const MY_BITMAP *fields);
-int key_cmp(KEY_PART_INFO *key_part, const uchar *key, uint key_length);
-ulong key_hashnr(KEY *key_info, uint used_key_parts, const uchar *key);
-bool key_buf_cmp(KEY *key_info, uint used_key_parts,
- const uchar *key1, const uchar *key2);
-extern "C" int key_rec_cmp(void *key_info, uchar *a, uchar *b);
-int key_tuple_cmp(KEY_PART_INFO *part, uchar *key1, uchar *key2, uint tuple_length);
-
-bool init_errmessage(void);
-#endif /* MYSQL_SERVER */
-void sql_perror(const char *message);
-
-bool fn_format_relative_to_data_home(char * to, const char *name,
- const char *dir, const char *extension);
-void set_server_version(void);
-/**
- Test a file path to determine if the path is compatible with the secure file
- path restriction.
-*/
-bool is_secure_file_path(char *path);
-
-#ifdef MYSQL_SERVER
-File open_binlog(IO_CACHE *log, const char *log_file_name,
- const char **errmsg);
-
-/* mysqld.cc */
-extern void MYSQLerror(const char*);
-void refresh_status(THD *thd);
-my_bool mysql_rm_tmp_tables(void);
-void handle_connection_in_main_thread(THD *thd);
-void create_thread_to_handle_connection(THD *thd);
-void unlink_thd(THD *thd);
-bool one_thread_per_connection_end(THD *thd, bool put_in_cache);
-void flush_thread_cache();
-
-/* item_func.cc */
-extern bool check_reserved_words(LEX_STRING *name);
-extern enum_field_types agg_field_type(Item **items, uint nitems);
-Item *find_date_time_item(Item **args, uint nargs, uint col);
-
-/* strfunc.cc */
-ulonglong find_set(TYPELIB *lib, const char *x, uint 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,
- bool part_match);
-uint find_type2(const TYPELIB *lib, const char *find, uint 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,
- CHARSET_INFO * const cs);
-
-
-bool is_keyword(const char *name, uint len);
-
-#define MY_DB_OPT_FILE "db.opt"
-bool my_database_names_init(void);
-void my_database_names_free(void);
-bool check_db_dir_existence(const char *db_name);
-bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create);
-bool load_db_opt_by_name(THD *thd, const char *db_name,
- HA_CREATE_INFO *db_create_info);
-CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name);
-bool my_dbopt_init(void);
-void my_dbopt_cleanup(void);
-extern int creating_database; // How many database locks are made
-extern int creating_table; // How many mysql_create_table() are running
-
-/*
- External variables
-*/
-
-extern time_t server_start_time, flush_status_time;
-#endif /* MYSQL_SERVER */
-#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
-extern uint mysql_data_home_len;
-
-extern MYSQL_PLUGIN_IMPORT char *mysql_data_home;
-extern char server_version[SERVER_VERSION_LENGTH];
-extern MYSQL_PLUGIN_IMPORT char mysql_real_data_home[];
-extern char mysql_unpacked_real_data_home[];
-
-extern CHARSET_INFO *character_set_filesystem;
-#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
-#ifdef MYSQL_SERVER
-extern char *opt_mysql_tmpdir, mysql_charsets_dir[],
- def_ft_boolean_syntax[sizeof(ft_boolean_syntax)];
-extern int mysql_unpacked_real_data_home_len;
-#define mysql_tmpdir (my_tmpdir(&mysql_tmpdir_list))
-extern MYSQL_PLUGIN_IMPORT MY_TMPDIR mysql_tmpdir_list;
-extern const LEX_STRING command_name[];
-
-extern const char *first_keyword, *delayed_user, *binary_keyword;
-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 char * const TRG_EXT;
-extern const char * const TRN_EXT;
-extern Eq_creator eq_creator;
-extern Ne_creator ne_creator;
-extern Gt_creator gt_creator;
-extern Lt_creator lt_creator;
-extern Ge_creator ge_creator;
-extern Le_creator le_creator;
-extern char language[FN_REFLEN];
-#endif /* MYSQL_SERVER */
-#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
-extern MYSQL_PLUGIN_IMPORT char reg_ext[FN_EXTLEN];
-extern MYSQL_PLUGIN_IMPORT uint reg_ext_length;
-#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
-#ifdef MYSQL_SERVER
-extern char glob_hostname[FN_REFLEN], mysql_home[FN_REFLEN];
-extern char pidfile_name[FN_REFLEN], system_time_zone[30], *opt_init_file;
-extern char log_error_file[FN_REFLEN], *opt_tc_log_file, *opt_log_basename;
-extern ulonglong log_10_int[20];
-extern ulonglong keybuff_size;
-extern ulonglong thd_startup_options;
-extern ulong thread_id;
-extern ulong binlog_cache_use, binlog_cache_disk_use;
-extern ulong aborted_threads,aborted_connects;
-extern ulong opt_progress_report_time;
-extern ulong delayed_insert_timeout;
-extern ulong delayed_insert_limit, delayed_queue_size;
-extern ulong delayed_insert_threads, delayed_insert_writes;
-extern ulong delayed_rows_in_use,delayed_insert_errors;
-extern ulong slave_open_temp_tables;
-extern ulong query_cache_size, query_cache_min_res_unit;
-extern ulong slow_launch_threads, slow_launch_time;
-extern ulong table_cache_size, table_def_size;
-extern MYSQL_PLUGIN_IMPORT ulong max_connections;
-extern ulong max_connect_errors, connect_timeout;
-extern ulong extra_max_connections;
-extern ulong slave_net_timeout, slave_trans_retries;
-extern int max_user_connections;
-extern bool max_user_connections_checking;
-extern ulonglong denied_connections;
-extern ulong what_to_log,flush_time;
-extern ulong query_buff_size;
-extern ulong slave_max_allowed_packet;
-extern ulong max_prepared_stmt_count, prepared_stmt_count;
-extern ulong binlog_cache_size, open_files_limit;
-extern ulonglong max_binlog_cache_size;
-extern ulong max_binlog_size, max_relay_log_size, opt_binlog_dbug_fsync_sleep;
-extern ulong opt_binlog_rows_event_max_size;
-extern my_bool opt_master_verify_checksum;
-extern my_bool opt_slave_sql_verify_checksum;
-extern ulong rpl_recovery_rank, thread_cache_size, thread_pool_size;
-extern ulong back_log;
-#endif /* MYSQL_SERVER */
-#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
-extern ulong MYSQL_PLUGIN_IMPORT specialflag;
-#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
-#ifdef MYSQL_SERVER
-extern ulong current_pid;
-extern ulong expire_logs_days, sync_binlog_period, sync_binlog_counter;
-extern ulong binlog_checksum_options;
-extern ulong opt_tc_log_size, tc_log_max_pages_used, tc_log_page_size;
-extern ulong tc_log_page_waits;
-extern my_bool relay_log_purge, opt_innodb_safe_binlog, opt_innodb;
-extern my_bool opt_expect_abort, opt_stack_trace;
-extern uint test_flags,select_errors,ha_open_options;
-extern uint protocol_version, mysqld_port, mysqld_extra_port, dropping_tables;
-extern uint delay_key_write_options;
-extern ulong max_long_data_size, max_used_connections;
-extern uint internal_tmp_table_max_key_length;
-extern uint internal_tmp_table_max_key_segments;
-#endif /* MYSQL_SERVER */
-#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
-extern MYSQL_PLUGIN_IMPORT uint lower_case_table_names;
-#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
-#ifdef MYSQL_SERVER
-extern bool opt_endinfo, using_udf_functions;
-extern my_bool locked_in_memory;
-extern bool opt_using_transactions;
-#endif /* MYSQL_SERVER */
-#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
-extern MYSQL_PLUGIN_IMPORT bool mysqld_embedded;
-#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
-#ifdef MYSQL_SERVER
-extern bool opt_large_files, server_id_supplied;
-extern bool opt_update_log, opt_bin_log, opt_error_log;
-extern my_bool opt_log, opt_slow_log;
-extern ulong log_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;
-extern my_bool opt_character_set_client_handshake;
-extern bool volatile abort_loop, shutdown_in_progress;
-extern bool in_bootstrap;
-extern uint volatile thread_count, thread_running, global_read_lock, global_disable_checkpoint;
-extern ulong thread_created;
-extern uint thread_handling;
-extern uint connection_count, extra_connection_count;
-extern my_bool opt_sql_bin_update, opt_safe_user_create, opt_no_mix_types;
-extern my_bool opt_safe_show_db, opt_local_infile, opt_myisam_use_mmap;
-extern my_bool opt_slave_compressed_protocol, use_temp_pool, opt_help;
-extern ulong slave_exec_mode_options;
-extern my_bool opt_readonly, lower_case_file_system;
-extern my_bool opt_userstat_running;
-extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs;
-extern my_bool opt_secure_auth, debug_assert_if_crashed_table;
-extern char* opt_secure_file_priv;
-extern my_bool opt_log_slow_admin_statements, opt_log_slow_slave_statements;
-extern my_bool opt_query_cache_strip_comments;
-extern my_bool sp_automatic_privileges, opt_noacl;
-extern my_bool opt_old_style_user_limits, trust_function_creators;
-extern uint opt_crash_binlog_innodb;
-extern char *shared_memory_base_name, *mysqld_unix_port;
-extern my_bool opt_enable_shared_memory;
-extern char *default_tz_name;
-#endif /* MYSQL_SERVER */
-#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
-extern my_bool opt_large_pages;
-extern uint opt_large_page_size;
-#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
-#ifdef MYSQL_SERVER
-extern char *opt_logname, *opt_slow_logname;
-extern const char *log_output_str;
-
-extern MYSQL_PLUGIN_IMPORT MYSQL_BIN_LOG mysql_bin_log;
-extern LOGGER logger;
-extern TABLE_LIST general_log, slow_log;
-extern FILE *bootstrap_file;
-extern int bootstrap_error;
-extern FILE *stderror_file;
-/*
- THR_MALLOC is a key which will be used to set/get MEM_ROOT** for a thread,
- using my_pthread_setspecific_ptr()/my_thread_getspecific_ptr().
-*/
-extern pthread_key(MEM_ROOT**,THR_MALLOC);
-extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open, LOCK_lock_db,
- LOCK_mapped_file,LOCK_user_locks, LOCK_status,
- LOCK_error_log, LOCK_delayed_insert, LOCK_short_uuid_generator,
- LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
- LOCK_slave_list, LOCK_active_mi, LOCK_global_read_lock,
- LOCK_global_system_variables, LOCK_user_conn,
- LOCK_prepared_stmt_count,
- LOCK_bytes_sent, LOCK_bytes_received, LOCK_connection_count;
-extern MYSQL_PLUGIN_IMPORT pthread_mutex_t LOCK_thread_count;
-#ifdef HAVE_OPENSSL
-extern pthread_mutex_t LOCK_des_key_file;
-#endif
-extern pthread_mutex_t LOCK_server_started;
-extern pthread_cond_t COND_server_started;
-extern pthread_mutex_t LOCK_global_user_client_stats;
-extern pthread_mutex_t LOCK_global_table_stats;
-extern pthread_mutex_t LOCK_global_index_stats;
-extern pthread_mutex_t LOCK_stats;
-
-extern int mysqld_server_started;
-extern rw_lock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
-extern rw_lock_t LOCK_system_variables_hash;
-extern pthread_cond_t COND_refresh, COND_thread_count;
-extern pthread_cond_t COND_global_read_lock;
-extern pthread_attr_t connection_attrib;
-extern I_List<THD> threads;
-extern I_List<NAMED_LIST> key_caches;
-extern MY_BITMAP temp_pool;
-extern const String my_null_string;
-extern SHOW_VAR status_vars[];
-#endif /* MYSQL_SERVER */
-#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
-extern MYSQL_PLUGIN_IMPORT struct system_variables global_system_variables;
-#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
-#ifdef MYSQL_SERVER
-extern struct system_variables max_system_variables;
-extern struct system_status_var global_status_var;
-extern struct my_rnd_struct sql_rand;
-
-extern const char *opt_date_time_formats[];
-extern KNOWN_DATE_TIME_FORMAT known_date_time_formats[];
-
-extern String null_string;
-extern HASH open_cache, lock_db_cache;
-extern HASH global_user_stats;
-extern HASH global_client_stats;
-extern HASH global_table_stats;
-extern HASH global_index_stats;
-
-extern TABLE *unused_tables;
-extern const char* any_db;
-extern struct my_option my_long_options[];
-extern const LEX_STRING view_type;
-extern scheduler_functions thread_scheduler;
-extern TYPELIB thread_handling_typelib;
-extern uint8 uc_update_queries[SQLCOM_END+1];
-extern uint sql_command_flags[];
-extern TYPELIB log_output_typelib;
-
-/* optional things, have_* variables */
-extern SHOW_COMP_OPTION have_community_features;
-
-extern handlerton *partition_hton;
-extern handlerton *myisam_hton;
-/*
- @todo remove, make it static in ha_maria.cc
- currently it's needed for sql_select.cc
-*/
-extern handlerton *maria_hton;
-extern handlerton *heap_hton;
-
-extern SHOW_COMP_OPTION have_ssl, have_symlink, have_dlopen;
-extern SHOW_COMP_OPTION have_query_cache;
-extern SHOW_COMP_OPTION have_geometry, have_rtree_keys;
-extern SHOW_COMP_OPTION have_crypt;
-extern SHOW_COMP_OPTION have_compress;
-
-extern int orig_argc;
-extern char **orig_argv;
-extern const char *load_default_groups[];
-
-#ifndef __WIN__
-extern pthread_t signal_thread;
-#endif
-
-#ifdef HAVE_OPENSSL
-extern struct st_VioSSLFd * ssl_acceptor_fd;
-#endif /* HAVE_OPENSSL */
-
-MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count,
- uint flags, bool *need_reopen);
-int mysql_lock_tables(THD *thd, MYSQL_LOCK *sql_lock,
- bool write_lock_used,
- uint flags, bool *need_reopen);
-
-/* mysql_lock_tables() and open_table() flags bits */
-#define MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK 0x0001
-#define MYSQL_LOCK_IGNORE_FLUSH 0x0002
-#define MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN 0x0004
-#define MYSQL_OPEN_TEMPORARY_ONLY 0x0008
-#define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0010
-#define MYSQL_LOCK_PERF_SCHEMA 0x0020
-#define MYSQL_LOCK_NOT_TEMPORARY 0x0040
-/* flags for get_lock_data */
-#define GET_LOCK_UNLOCK 1
-#define GET_LOCK_STORE_LOCKS 2
-
-void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock= 1);
-void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);
-void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count);
-void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table,
- bool always_unlock);
-void mysql_lock_abort(THD *thd, TABLE *table, bool upgrade_lock);
-void mysql_lock_downgrade_write(THD *thd, TABLE *table,
- thr_lock_type new_lock_type);
-bool mysql_lock_abort_for_thread(THD *thd, TABLE *table);
-MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b);
-TABLE_LIST *mysql_lock_have_duplicate(THD *thd, TABLE_LIST *needle,
- TABLE_LIST *haystack);
-void reset_lock_data(MYSQL_LOCK *sql_lock, bool unlock);
-bool lock_global_read_lock(THD *thd);
-void unlock_global_read_lock(THD *thd);
-bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh,
- bool is_not_commit);
-void start_waiting_global_read_lock(THD *thd);
-bool make_global_read_lock_block_commit(THD *thd);
-void disable_checkpoints(THD *thd);
-bool set_protect_against_global_read_lock(void);
-void unset_protect_against_global_read_lock(void);
-void broadcast_refresh(void);
-MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
- uint flags, TABLE **write_lock_used);
-
-/* Lock based on name */
-int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list);
-int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use);
-void unlock_table_name(THD *thd, TABLE_LIST *table_list);
-bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list);
-bool lock_table_names(THD *thd, TABLE_LIST *table_list);
-void unlock_table_names(THD *thd, TABLE_LIST *table_list,
- TABLE_LIST *last_table);
-bool lock_table_names_exclusively(THD *thd, TABLE_LIST *table_list);
-bool is_table_name_exclusively_locked_by_this_thread(THD *thd,
- TABLE_LIST *table_list);
-bool is_table_name_exclusively_locked_by_this_thread(THD *thd, uchar *key,
- int key_length);
-
-
-/* old unireg functions */
-
-void unireg_init(ulong options);
-void unireg_end(void) __attribute__((noreturn));
-bool mysql_create_frm(THD *thd, const char *file_name,
- const char *db, const char *table,
- HA_CREATE_INFO *create_info,
- List<Create_field> &create_field,
- uint key_count,KEY *key_info,handler *db_type);
-int rea_create_table(THD *thd, const char *path,
- const char *db, const char *table_name,
- HA_CREATE_INFO *create_info,
- List<Create_field> &create_field,
- uint key_count,KEY *key_info,
- handler *file);
-int format_number(uint inputflag,uint max_length,char * pos,uint length,
- char * *errpos);
-
-/* table.cc */
-TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
- uint key_length);
-void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
- uint key_length,
- const char *table_name, const char *path);
-void free_table_share(TABLE_SHARE *share);
-int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags);
-void open_table_error(TABLE_SHARE *share, int error, int db_errno, int errarg);
-int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
- uint db_stat, uint prgflag, uint ha_open_flags,
- TABLE *outparam, bool is_create_table);
-bool unpack_vcol_info_from_frm(THD *thd, MEM_ROOT *mem_root,
- TABLE *table, Field *field,
- LEX_STRING *vcol_expr, bool *error_reported);
-int readfrm(const char *name, uchar **data, size_t *length);
-int writefrm(const char* name, const uchar* data, size_t len);
-int closefrm(TABLE *table, bool free_share);
-int read_string(File file, uchar* *to, size_t length);
-void free_blobs(TABLE *table);
-void free_field_buffers_larger_than(TABLE *table, uint32 size);
-int set_zone(int nr,int min_zone,int max_zone);
-ulong convert_period_to_month(ulong period);
-ulong convert_month_to_period(ulong month);
-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);
-bool str_to_time_with_warn(const char *str,uint length,MYSQL_TIME *l_time,
- ulong fuzzydate);
-timestamp_type str_to_datetime_with_warn(const char *str, uint length,
- MYSQL_TIME *l_time, ulong flags);
-bool time_to_datetime(MYSQL_TIME *l_time);
-void time_to_daytime_interval(MYSQL_TIME *l_time);
-void localtime_to_TIME(MYSQL_TIME *to, struct tm *from);
-void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds);
-
-void make_truncated_value_warning(THD *thd,
- MYSQL_ERROR::enum_warning_level level,
- const Lazy_string *str_val,
- timestamp_type time_type,
- const char *field_name);
-bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime,
- ulong fuzzydate, const char *field_name);
-bool decimal_to_datetime_with_warn(const my_decimal *value, MYSQL_TIME *ltime,
- ulong fuzzydate, const char *field_name);
-bool int_to_datetime_with_warn(bool neg, ulonglong value, MYSQL_TIME *ltime,
- ulong fuzzydate, const char *field_name);
-
-static inline void make_truncated_value_warning(THD *thd,
- MYSQL_ERROR::enum_warning_level level, const char *str_val,
- uint str_length, timestamp_type time_type,
- const char *field_name)
-{
- const Lazy_string_str str(str_val, str_length);
- make_truncated_value_warning(thd, level, &str, time_type, field_name);
-}
-
-bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL interval);
-bool calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign,
- longlong *seconds_out, long *microseconds_out);
-
-extern LEX_STRING interval_type_to_name[];
-
-extern DATE_TIME_FORMAT *date_time_format_make(timestamp_type format_type,
- const char *format_str,
- uint format_length);
-extern DATE_TIME_FORMAT *date_time_format_copy(THD *thd,
- DATE_TIME_FORMAT *format);
-const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
- timestamp_type type);
-extern bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time,
- timestamp_type type, String *str);
-int my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b);
-longlong get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
- Item *warn_item, bool *is_null);
-static inline bool
-non_zero_date(const MYSQL_TIME *ltime)
-{
- return ltime->year || ltime->month || ltime->day;
-}
-static inline bool
-check_date(const MYSQL_TIME *ltime, ulonglong flags, int *was_cut)
-{
- return check_date(ltime, non_zero_date(ltime), flags, was_cut);
-}
-bool check_date_with_warn(const MYSQL_TIME *ltime, uint fuzzy_date,
- timestamp_type ts_type);
-bool make_date_with_warn(MYSQL_TIME *ltime,
- uint fuzzy_date, timestamp_type ts_type);
-bool adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec);
-int test_if_number(char *str,int *res,bool allow_wildcards);
-void change_byte(uchar *,uint,char,char);
-bool init_read_record(READ_RECORD *info, THD *thd, TABLE *reg_form,
- SQL_SELECT *select, int use_record_cache,
- bool print_errors, bool disable_rr_cache);
-void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
- bool print_error, uint idx);
-void end_read_record(READ_RECORD *info);
-ha_rows filesort(THD *thd, TABLE *form,struct st_sort_field *sortorder,
- uint s_length, SQL_SELECT *select,
- ha_rows max_rows, bool sort_positions,
- ha_rows *examined_rows);
-void filesort_free_buffers(TABLE *table, bool full);
-double get_merge_many_buffs_cost(uint *buffer, uint last_n_elems,
- int elem_size);
-void change_double_for_sort(double nr,uchar *to);
-double my_double_round(double value, longlong dec, bool dec_unsigned,
- bool truncate);
-int get_quick_record(SQL_SELECT *select);
-
-int calc_weekday(long daynr,bool sunday_first_day_of_week);
-uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year);
-void find_date(char *pos,uint *vek,uint flag);
-TYPELIB *convert_strings_to_array_type(char * *typelibs, char * *end);
-TYPELIB *typelib(MEM_ROOT *mem_root, List<String> &strings);
-ulong get_form_pos(File file, uchar *head, TYPELIB *save_names);
-ulong make_new_entry(File file,uchar *fileinfo,TYPELIB *formnames,
- const char *newname);
-ulong next_io_size(ulong pos);
-void append_unescaped(String *res, const char *pos, uint length);
-int create_frm(THD *thd, const char *name, const char *db, const char *table,
- uint reclength, uchar *fileinfo,
- HA_CREATE_INFO *create_info, uint keys);
-void update_create_info_from_table(HA_CREATE_INFO *info, TABLE *form);
-int rename_file_ext(const char * from,const char * to,const char * ext);
-bool check_db_name(LEX_STRING *db);
-bool check_column_name(const char *name);
-bool check_table_name(const char *name, uint length, bool check_for_path_chars);
-char *get_field(MEM_ROOT *mem, Field *field);
-bool get_field(MEM_ROOT *mem, Field *field, class String *res);
-int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr);
-char *fn_rext(char *name);
-
-/* Conversion functions */
-#endif /* MYSQL_SERVER */
-
-#if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS
-uint strconvert(CHARSET_INFO *from_cs, const char *from,
- CHARSET_INFO *to_cs, char *to, uint to_length, uint *errors);
-/* depends on errmsg.txt Database `db`, Table `t` ... */
-#define EXPLAIN_FILENAME_MAX_EXTRA_LENGTH 63
-enum enum_explain_filename_mode
-{
- EXPLAIN_ALL_VERBOSE= 0,
- EXPLAIN_PARTITIONS_VERBOSE,
- EXPLAIN_PARTITIONS_AS_COMMENT
-};
-uint explain_filename(THD* thd, const char *from, char *to, uint to_length,
- enum_explain_filename_mode explain_mode);
-uint filename_to_tablename(const char *from, char *to, uint to_length);
-uint tablename_to_filename(const char *from, char *to, uint to_length);
-uint check_n_cut_mysql50_prefix(const char *from, char *to, uint to_length);
-bool check_mysql50_prefix(const char *name);
-#endif /* MYSQL_SERVER || INNODB_COMPATIBILITY_HOOKS */
-#ifdef MYSQL_SERVER
-uint build_table_filename(char *buff, size_t bufflen, const char *db,
- const char *table, const char *ext, uint flags);
-const char *get_canonical_filename(handler *file, const char *path,
- char *tmp_path);
-
-uint build_table_shadow_filename(char *buff, size_t bufflen,
- ALTER_PARTITION_PARAM_TYPE *lpt);
-/* Flags for conversion functions. */
-#define FN_FROM_IS_TMP (1 << 0)
-#define FN_TO_IS_TMP (1 << 1)
-#define FN_IS_TMP (FN_FROM_IS_TMP | FN_TO_IS_TMP)
-#define NO_FRM_RENAME (1 << 2)
-#define FRM_ONLY (1 << 3)
-
-/* from hostname.cc */
-struct in_addr;
-char * ip_to_hostname(struct in_addr *in,uint *errors);
-void inc_host_errors(struct in_addr *in);
-void reset_host_errors(struct in_addr *in);
-bool hostname_cache_init();
-void hostname_cache_free();
-void hostname_cache_refresh(void);
-
-/* sql_cache.cc */
-extern bool sql_cache_init();
-extern void sql_cache_free();
-extern int sql_cache_hit(THD *thd, char *inBuf, uint length);
-
-/* item_func.cc */
-Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
- LEX_STRING component);
-int get_var_with_binlog(THD *thd, enum_sql_command sql_command,
- LEX_STRING &name, user_var_entry **out_entry);
-/* log.cc */
-bool flush_error_log(void);
-
-/* sql_list.cc */
-void free_list(I_List <i_string_pair> *list);
-void free_list(I_List <i_string> *list);
-
-/* sql_yacc.cc */
-#ifndef DBUG_OFF
-extern void turn_parser_debug_on();
-#endif
-
-/* frm_crypt.cc */
-#ifdef HAVE_CRYPTED_FRM
-SQL_CRYPT *get_crypt_for_frm(void);
-#endif
-
-/* password.c */
-extern "C" void my_make_scrambled_password_323(char *to, const char *password,
- size_t pass_len);
-extern "C" void my_make_scrambled_password(char *to, const char *password,
- size_t pass_len);
-
-#include "sql_view.h"
-
-/* Some inline functions for more speed */
-
-inline bool add_item_to_list(THD *thd, Item *item)
-{
- return thd->lex->current_select->add_item_to_list(thd, item);
-}
-
-inline bool add_value_to_list(THD *thd, Item *value)
-{
- return thd->lex->value_list.push_back(value);
-}
-
-inline bool add_order_to_list(THD *thd, Item *item, bool asc)
-{
- return thd->lex->current_select->add_order_to_list(thd, item, asc);
-}
-
-inline bool add_gorder_to_list(THD *thd, Item *item, bool asc)
-{
- return thd->lex->current_select->add_gorder_to_list(thd, item, asc);
-}
-
-inline bool add_group_to_list(THD *thd, Item *item, bool asc)
-{
- return thd->lex->current_select->add_group_to_list(thd, item, asc);
-}
-
-inline void mark_as_null_row(TABLE *table)
-{
- table->null_row=1;
- table->status|=STATUS_NULL_ROW;
- bfill(table->null_flags,table->s->null_bytes,255);
-}
-
-inline void table_case_convert(char * name, uint length)
-{
- if (lower_case_table_names)
- files_charset_info->cset->casedn(files_charset_info,
- name, length, name, length);
-}
-
-inline const char *table_case_name(HA_CREATE_INFO *info, const char *name)
-{
- return ((lower_case_table_names == 2 && info->alias) ? info->alias : name);
-}
-
-inline ulong sql_rnd_with_mutex()
-{
- pthread_mutex_lock(&LOCK_thread_count);
- ulong tmp=(ulong) (my_rnd(&sql_rand) * 0xffffffff); /* make all bits random */
- pthread_mutex_unlock(&LOCK_thread_count);
- return tmp;
-}
-
-Comp_creator *comp_eq_creator(bool invert);
-Comp_creator *comp_ge_creator(bool invert);
-Comp_creator *comp_gt_creator(bool invert);
-Comp_creator *comp_le_creator(bool invert);
-Comp_creator *comp_lt_creator(bool invert);
-Comp_creator *comp_ne_creator(bool invert);
-
-Item * all_any_subquery_creator(Item *left_expr,
- chooser_compare_func_creator cmp,
- bool all,
- SELECT_LEX *select_lex);
-
-/**
- clean/setup table fields and map.
-
- @param table TABLE structure pointer (which should be setup)
- @param table_list TABLE_LIST structure pointer (owner of TABLE)
- @param tablenr table number
-*/
-
-
-inline void setup_table_map(TABLE *table, TABLE_LIST *table_list, uint tablenr)
-{
- table->used_fields= 0;
- table_list->reset_const_table();
- table->null_row= 0;
- table->status= STATUS_NO_RECORD;
- table->maybe_null= table_list->outer_join;
- TABLE_LIST *embedding= table_list->embedding;
- while (!table->maybe_null && embedding)
- {
- table->maybe_null= embedding->outer_join;
- embedding= embedding->embedding;
- }
- table->tablenr= tablenr;
- table->map= (table_map) 1 << tablenr;
- table->force_index= table_list->force_index;
- table->force_index_order= table->force_index_group= 0;
- table->covering_keys= table->s->keys_for_keyread;
- table->merge_keys.clear_all();
- TABLE_LIST *orig= table_list->select_lex ?
- table_list->select_lex->master_unit()->derived : 0;
- if (!orig || !orig->is_merged_derived())
- {
- /* Tables merged from derived were set up already.*/
- table->covering_keys= table->s->keys_for_keyread;
- table->merge_keys.clear_all();
- }
-}
-
-
-/**
- convert a hex digit into number.
-*/
-
-inline int hexchar_to_int(char c)
-{
- if (c <= '9' && c >= '0')
- return c-'0';
- c|=32;
- if (c <= 'f' && c >= 'a')
- return c-'a'+10;
- return -1;
-}
-
-/**
- return true if the table was created explicitly.
-*/
-inline bool is_user_table(TABLE * table)
-{
- const char *name= table->s->table_name.str;
- return strncmp(name, tmp_file_prefix, tmp_file_prefix_length);
-}
-
-
-#ifndef HAVE_LOG2
-/*
- This will be slightly slower and perhaps a tiny bit less accurate than
- doing it the IEEE754 way but log2() should be available on C99 systems.
-*/
-inline double log2(double x)
-{
- return (log(x) / M_LN2);
-}
-#endif
-
-
-/*
- Some functions that are different in the embedded library and the normal
- server
-*/
-
-#ifndef EMBEDDED_LIBRARY
-extern "C" void unireg_abort(int exit_code) __attribute__((noreturn));
-void kill_delayed_threads(void);
-bool check_stack_overrun(THD *thd, long margin, uchar *dummy);
-#else
-extern "C" void unireg_clear(int exit_code);
-#define unireg_abort(exit_code) do { unireg_clear(exit_code); DBUG_RETURN(exit_code); } while(0)
-inline void kill_delayed_threads(void) {}
-#define check_stack_overrun(A, B, C) 0
-#endif
-
-/* Used by handlers to store things in schema tables */
-#define IS_FILES_FILE_ID 0
-#define IS_FILES_FILE_NAME 1
-#define IS_FILES_FILE_TYPE 2
-#define IS_FILES_TABLESPACE_NAME 3
-#define IS_FILES_TABLE_CATALOG 4
-#define IS_FILES_TABLE_SCHEMA 5
-#define IS_FILES_TABLE_NAME 6
-#define IS_FILES_LOGFILE_GROUP_NAME 7
-#define IS_FILES_LOGFILE_GROUP_NUMBER 8
-#define IS_FILES_ENGINE 9
-#define IS_FILES_FULLTEXT_KEYS 10
-#define IS_FILES_DELETED_ROWS 11
-#define IS_FILES_UPDATE_COUNT 12
-#define IS_FILES_FREE_EXTENTS 13
-#define IS_FILES_TOTAL_EXTENTS 14
-#define IS_FILES_EXTENT_SIZE 15
-#define IS_FILES_INITIAL_SIZE 16
-#define IS_FILES_MAXIMUM_SIZE 17
-#define IS_FILES_AUTOEXTEND_SIZE 18
-#define IS_FILES_CREATION_TIME 19
-#define IS_FILES_LAST_UPDATE_TIME 20
-#define IS_FILES_LAST_ACCESS_TIME 21
-#define IS_FILES_RECOVER_TIME 22
-#define IS_FILES_TRANSACTION_COUNTER 23
-#define IS_FILES_VERSION 24
-#define IS_FILES_ROW_FORMAT 25
-#define IS_FILES_TABLE_ROWS 26
-#define IS_FILES_AVG_ROW_LENGTH 27
-#define IS_FILES_DATA_LENGTH 28
-#define IS_FILES_MAX_DATA_LENGTH 29
-#define IS_FILES_INDEX_LENGTH 30
-#define IS_FILES_DATA_FREE 31
-#define IS_FILES_CREATE_TIME 32
-#define IS_FILES_UPDATE_TIME 33
-#define IS_FILES_CHECK_TIME 34
-#define IS_FILES_CHECKSUM 35
-#define IS_FILES_STATUS 36
-#define IS_FILES_EXTRA 37
-void init_fill_schema_files_row(TABLE* table);
-bool schema_table_store_record(THD *thd, TABLE *table);
-
-/* sql/item_create.cc */
-int item_create_init();
-void item_create_cleanup();
-
-bool show_create_trigger(THD *thd, const sp_name *trg_name);
-
-inline void lex_string_set(LEX_STRING *lex_str, const char *c_str)
-{
- lex_str->str= (char *) c_str;
- lex_str->length= strlen(c_str);
-}
-
-bool load_charset(MEM_ROOT *mem_root,
- Field *field,
- CHARSET_INFO *dflt_cs,
- CHARSET_INFO **cs);
-
-bool load_collation(MEM_ROOT *mem_root,
- Field *field,
- CHARSET_INFO *dflt_cl,
- CHARSET_INFO **cl);
-
-bool db_name_is_in_ignore_db_dirs_list(const char *dbase);
-
-#endif /* MYSQL_SERVER */
-extern "C" int test_if_data_home_dir(const char *dir);
-
-#endif /* MYSQL_CLIENT */
-
-#endif /* MYSQL_PRIV_H */
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 5d214189c07..52b56f121c5 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2008, 2014, Monty Program Ab
+/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,11 +12,47 @@
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
-*/
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_priv.h"
+#include "unireg.h"
+#include <signal.h>
+#ifndef __WIN__
+#include <netdb.h> // getservbyname, servent
+#endif
+#include "sql_parse.h" // test_if_data_home_dir
+#include "sql_cache.h" // query_cache, query_cache_*
+#include "sql_locale.h" // MY_LOCALES, my_locales, my_locale_by_name
+#include "sql_show.h" // free_status_vars, add_status_vars,
+ // reset_status_vars
+#include "strfunc.h" // find_set_from_flags
+#include "parse_file.h" // File_parser_dummy_hook
+#include "sql_db.h" // my_dboptions_cache_free
+ // my_dboptions_cache_init
+#include "sql_table.h" // release_ddl_log, execute_ddl_log_recovery
+#include "sql_connect.h" // free_max_user_conn, init_max_user_conn,
+ // handle_one_connection
+#include "sql_time.h" // known_date_time_formats,
+ // get_date_time_format_str,
+ // date_time_format_make
+#include "tztime.h" // my_tz_free, my_tz_init, my_tz_SYSTEM
+#include "hostname.h" // hostname_cache_free, hostname_cache_init
+#include "sql_acl.h" // acl_free, grant_free, acl_init,
+ // grant_init
+#include "sql_base.h" // table_def_free, table_def_init,
+ // cached_open_tables,
+ // cached_table_definitions
+#include "sql_test.h" // mysql_print_status
+#include "item_create.h" // item_create_cleanup, item_create_init
+#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
-#define DEFINE_VARIABLES_LOG_SLOW // Declare variables in log_slow.h
-#include "mysql_priv.h"
#include <m_ctype.h>
#include <my_dir.h>
#include <my_bit.h>
@@ -26,53 +62,51 @@
#include "rpl_filter.h"
#include "client_settings.h"
#include "repl_failsafe.h"
+#include <sql_common.h>
#include <my_stacktrace.h>
#include "mysqld_suffix.h"
#include "mysys_err.h"
#include "events.h"
+#include "sql_audit.h"
+#include "probes_mysql.h"
+#include "scheduler.h"
#include <waiting_threads.h>
#include "debug_sync.h"
-#include "log_event.h"
-#include "sql_show.h"
+#include "sql_callback.h"
+#include "threadpool.h"
+#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
+#include "../storage/perfschema/pfs_server.h"
+#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
+
+#include "keycaches.h"
#include "../storage/myisam/ha_myisam.h"
+#include "set_var.h"
#include "rpl_injector.h"
+#include "rpl_handler.h"
+
#ifdef HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
#endif
-#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
-#if defined(NOT_ENOUGH_TESTED) \
- && defined(NDB_SHM_TRANSPORTER) && MYSQL_VERSION_ID >= 50000
-#define OPT_NDB_SHM_DEFAULT 1
-#else
-#define OPT_NDB_SHM_DEFAULT 0
-#endif
-#endif
-
-#ifndef DEFAULT_SKIP_THREAD_PRIORITY
-#define DEFAULT_SKIP_THREAD_PRIORITY 0
-#endif
-
#include <thr_alarm.h>
#include <ft_global.h>
#include <errmsg.h>
#include "sp_rcontext.h"
#include "sp_cache.h"
+#include "sql_reload.h" // reload_acl_and_cache
-#define mysqld_charset &my_charset_latin1
-
-#if SIZEOF_CHARP == 4
-#define MAX_MEM_TABLE_SIZE ~(ulong) 0
-#else
-#define MAX_MEM_TABLE_SIZE ~(ulonglong) 0
+#ifdef HAVE_POLL_H
+#include <poll.h>
#endif
+#define mysqld_charset &my_charset_latin1
+
/* We have HAVE_valgrind below as this speeds up the shutdown of MySQL */
-#if defined(HAVE_DEC_3_2_THREADS) || defined(SIGNALS_DONT_BREAK_READ) || defined(HAVE_valgrind) && defined(__linux__)
+#if defined(SIGNALS_DONT_BREAK_READ) || defined(HAVE_valgrind) && defined(__linux__)
#define HAVE_CLOSE_SERVER_SOCK 1
#endif
@@ -95,15 +129,12 @@ extern "C" { // Because of SCO 3.2V4.2
#include <my_net.h>
#if !defined(__WIN__)
-# ifndef __NETWARE__
#include <sys/resource.h>
-# endif /* __NETWARE__ */
#ifdef HAVE_SYS_UN_H
-# include <sys/un.h>
+#include <sys/un.h>
#endif
-#include <netdb.h>
#ifdef HAVE_SELECT_H
-# include <select.h>
+#include <select.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
@@ -121,45 +152,15 @@ extern "C" { // Because of SCO 3.2V4.2
#include <crtdbg.h>
#endif
-#ifdef __NETWARE__
-#define zVOLSTATE_ACTIVE 6
-#define zVOLSTATE_DEACTIVE 2
-#define zVOLSTATE_MAINTENANCE 3
-
-#undef __event_h__
-#include <../include/event.h>
-/*
- This #undef exists here because both libc of NetWare and MySQL have
- files named event.h which causes compilation errors.
-*/
-
-#include <nks/netware.h>
-#include <nks/vm.h>
-#include <library.h>
-#include <monitor.h>
-#include <zOmni.h> //For NEB
-#include <neb.h> //For NEB
-#include <nebpub.h> //For NEB
-#include <zEvent.h> //For NSS event structures
-#include <zPublics.h>
-
-static void *neb_consumer_id= NULL; //For storing NEB consumer id
-static char datavolname[256]= {0};
-static VolumeID_t datavolid;
-static event_handle_t eh;
-static Report_t ref;
-static void *refneb= NULL;
-my_bool event_flag= FALSE;
-
-static int volumeid= -1;
-
- /* NEB event callback */
-unsigned long neb_event_callback(struct EventBlock *eblock);
-static void registerwithneb();
-static void getvolumename();
-static void getvolumeID(BYTE *volumeName);
-#endif /* __NETWARE__ */
-
+#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);
+extern int getpagesizes2(size_t *, int);
+extern int memcntl(caddr_t, size_t, int, caddr_t, int, int);
+#endif /* __sun__ ... */
+#endif /* HAVE_SOLARIS_LARGE_PAGES */
#ifdef _AIX41
int initgroups(const char *,unsigned int);
@@ -191,6 +192,10 @@ typedef fp_except fp_except_t;
# endif
#endif
+#ifndef HAVE_FCNTL
+#define fcntl(X,Y,Z) 0
+#endif
+
extern "C" my_bool reopen_fstreams(const char *filename,
FILE *outstream, FILE *errstream);
@@ -252,11 +257,7 @@ inline void setup_fpu()
#define MYSQL_KILL_SIGNAL SIGTERM
-#ifdef HAVE_GLIBC2_STYLE_GETHOSTBYNAME_R
-#include <sys/types.h>
-#else
#include <my_pthread.h> // For thr_setconcurency()
-#endif
#ifdef SOLARIS
extern "C" int gethostname(char *name, int namelen);
@@ -275,125 +276,6 @@ extern "C" sig_handler handle_fatal_signal(int sig);
#include <welcome_copyright_notice.h> // ORACLE_WELCOME_COPYRIGHT_NOTICE
const char *show_comp_option_name[]= {"YES", "NO", "DISABLED"};
-/*
- WARNING: When adding new SQL modes don't forget to update the
- tables definitions that stores it's value.
- (ie: mysql.event, mysql.proc)
-*/
-static const char *sql_mode_names[]=
-{
- "REAL_AS_FLOAT", "PIPES_AS_CONCAT", "ANSI_QUOTES", "IGNORE_SPACE",
- "IGNORE_BAD_TABLE_OPTIONS", "ONLY_FULL_GROUP_BY", "NO_UNSIGNED_SUBTRACTION",
- "NO_DIR_IN_CREATE",
- "POSTGRESQL", "ORACLE", "MSSQL", "DB2", "MAXDB", "NO_KEY_OPTIONS",
- "NO_TABLE_OPTIONS", "NO_FIELD_OPTIONS", "MYSQL323", "MYSQL40", "ANSI",
- "NO_AUTO_VALUE_ON_ZERO", "NO_BACKSLASH_ESCAPES", "STRICT_TRANS_TABLES",
- "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",
- NullS
-};
-
-static const unsigned int sql_mode_names_len[]=
-{
- /*REAL_AS_FLOAT*/ 13,
- /*PIPES_AS_CONCAT*/ 15,
- /*ANSI_QUOTES*/ 11,
- /*IGNORE_SPACE*/ 12,
- /*IGNORE_BAD_TABLE_OPTIONS*/ 24,
- /*ONLY_FULL_GROUP_BY*/ 18,
- /*NO_UNSIGNED_SUBTRACTION*/ 23,
- /*NO_DIR_IN_CREATE*/ 16,
- /*POSTGRESQL*/ 10,
- /*ORACLE*/ 6,
- /*MSSQL*/ 5,
- /*DB2*/ 3,
- /*MAXDB*/ 5,
- /*NO_KEY_OPTIONS*/ 14,
- /*NO_TABLE_OPTIONS*/ 16,
- /*NO_FIELD_OPTIONS*/ 16,
- /*MYSQL323*/ 8,
- /*MYSQL40*/ 7,
- /*ANSI*/ 4,
- /*NO_AUTO_VALUE_ON_ZERO*/ 21,
- /*NO_BACKSLASH_ESCAPES*/ 20,
- /*STRICT_TRANS_TABLES*/ 19,
- /*STRICT_ALL_TABLES*/ 17,
- /*NO_ZERO_IN_DATE*/ 15,
- /*NO_ZERO_DATE*/ 12,
- /*ALLOW_INVALID_DATES*/ 19,
- /*ERROR_FOR_DIVISION_BY_ZERO*/ 26,
- /*TRADITIONAL*/ 11,
- /*NO_AUTO_CREATE_USER*/ 19,
- /*HIGH_NOT_PRECEDENCE*/ 19,
- /*NO_ENGINE_SUBSTITUTION*/ 22,
- /*PAD_CHAR_TO_FULL_LENGTH*/ 23
-};
-
-TYPELIB sql_mode_typelib= { array_elements(sql_mode_names)-1,"",
- sql_mode_names,
- (unsigned int *)sql_mode_names_len };
-
-static const char *optimizer_switch_names[]=
-{
- "index_merge","index_merge_union","index_merge_sort_union",
- "index_merge_intersection","index_merge_sort_intersection",
- "index_condition_pushdown",
- "derived_merge", "derived_with_keys",
- "firstmatch","loosescan","materialization","in_to_exists","semijoin",
- "partial_match_rowid_merge",
- "partial_match_table_scan",
- "subquery_cache",
- "mrr",
- "mrr_cost_based",
- "mrr_sort_keys",
- "outer_join_with_cache",
- "semijoin_with_cache",
- "join_cache_incremental",
- "join_cache_hashed",
- "join_cache_bka",
- "optimize_join_buffer_size",
- "table_elimination",
- "default", NullS
-};
-
-/* Corresponding defines are named OPTIMIZER_SWITCH_XXX */
-static const unsigned int optimizer_switch_names_len[]=
-{
- sizeof("index_merge") - 1,
- sizeof("index_merge_union") - 1,
- sizeof("index_merge_sort_union") - 1,
- sizeof("index_merge_intersection") - 1,
- sizeof("index_merge_sort_intersection") - 1,
- sizeof("index_condition_pushdown") - 1,
- sizeof("derived_merge") - 1,
- sizeof("derived_with_keys") - 1,
- sizeof("firstmatch") - 1,
- sizeof("loosescan") - 1,
- sizeof("materialization") - 1,
- sizeof("in_to_exists") - 1,
- sizeof("semijoin") - 1,
- sizeof("partial_match_rowid_merge") - 1,
- sizeof("partial_match_table_scan") - 1,
- sizeof("subquery_cache") - 1,
- sizeof("mrr") - 1,
- sizeof("mrr_cost_based") - 1,
- sizeof("mrr_sort_keys") - 1,
- sizeof("outer_join_with_cache") - 1,
- sizeof("semijoin_with_cache") - 1,
- sizeof("join_cache_incremental") - 1,
- sizeof("join_cache_hashed") - 1,
- sizeof("join_cache_bka") - 1,
- sizeof("optimize_join_buffer_size") - 1,
- sizeof("table_elimination") - 1,
- sizeof("default") - 1
-};
-TYPELIB optimizer_switch_typelib= { array_elements(optimizer_switch_names)-1,"",
- optimizer_switch_names,
- (unsigned int *)optimizer_switch_names_len };
static const char *tc_heuristic_recover_names[]=
{
@@ -405,38 +287,11 @@ static TYPELIB tc_heuristic_recover_typelib=
tc_heuristic_recover_names, NULL
};
-static const char *thread_handling_names[]=
-{ "one-thread-per-connection", "no-threads",
-#if HAVE_POOL_OF_THREADS == 1
- "pool-of-threads",
-#endif
- NullS};
-
-TYPELIB thread_handling_typelib=
-{
- array_elements(thread_handling_names) - 1, "",
- thread_handling_names, NULL
-};
-
-const char *plugin_maturity_names[]=
-{ "unknown", "experimental", "alpha", "beta", "gamma", "stable", 0 };
-
-TYPELIB plugin_maturity_values=
-{
- array_elements(plugin_maturity_names) - 1, "", plugin_maturity_names, 0
-};
-
-const int server_maturity= MariaDB_PLUGIN_MATURITY_UNKNOWN;
-
const char *first_keyword= "first", *binary_keyword= "BINARY";
const char *my_localhost= "localhost", *delayed_user= "DELAYED";
-#if SIZEOF_OFF_T > 4 && defined(BIG_TABLES)
-#define GET_HA_ROWS GET_ULL
-#else
-#define GET_HA_ROWS GET_ULONG
-#endif
bool opt_large_files= sizeof(my_off_t) > 4;
+static my_bool opt_autocommit; ///< for --autocommit command-line option
/*
Used with --help for detailed option
@@ -451,14 +306,31 @@ arg_cmp_func Arg_comparator::comparator_matrix[6][2] =
{&Arg_comparator::compare_decimal, &Arg_comparator::compare_e_decimal},
{&Arg_comparator::compare_datetime, &Arg_comparator::compare_e_datetime}};
-const char *log_output_names[] = { "NONE", "FILE", "TABLE", NullS};
-static const unsigned int log_output_names_len[]= { 4, 4, 5, 0 };
-TYPELIB log_output_typelib= {array_elements(log_output_names)-1,"",
- log_output_names,
- (unsigned int *) log_output_names_len};
-
/* static variables */
+#ifdef HAVE_PSI_INTERFACE
+#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY)
+static PSI_thread_key key_thread_handle_con_namedpipes;
+static PSI_cond_key key_COND_handler_count;
+#endif /* _WIN32 || HAVE_SMEM && !EMBEDDED_LIBRARY */
+
+#if defined(HAVE_SMEM) && !defined(EMBEDDED_LIBRARY)
+static PSI_thread_key key_thread_handle_con_sharedmem;
+#endif /* HAVE_SMEM && !EMBEDDED_LIBRARY */
+
+#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY)
+static PSI_thread_key key_thread_handle_con_sockets;
+#endif /* _WIN32 || HAVE_SMEM && !EMBEDDED_LIBRARY */
+
+#ifdef __WIN__
+static PSI_thread_key key_thread_handle_shutdown;
+#endif /* __WIN__ */
+
+#if defined (HAVE_OPENSSL) && !defined(HAVE_YASSL)
+static PSI_rwlock_key key_rwlock_openssl;
+#endif
+#endif /* HAVE_PSI_INTERFACE */
+
#ifdef HAVE_NPTL
volatile sig_atomic_t ld_assume_kernel_is_set= 0;
#endif
@@ -467,75 +339,34 @@ volatile sig_atomic_t ld_assume_kernel_is_set= 0;
static bool lower_case_table_names_used= 0;
static bool max_long_data_size_used= false;
static bool volatile select_thread_in_use, signal_thread_in_use;
-static bool volatile ready_to_exit;
+static volatile bool ready_to_exit;
static my_bool opt_debugging= 0, opt_external_locking= 0, opt_console= 0;
static my_bool opt_short_log_format= 0;
-static my_bool opt_ignore_wrong_options= 0;
-static my_bool opt_sync= 0, opt_thread_alarm;
-/*
- Set this to 1 if you want to that 'strict mode' should affect all date
- operations. If this is 0, then date checking is only done when storing
- dates into a table.
-*/
-my_bool strict_date_checking= 0;
static uint kill_cached_threads, wake_thread;
-ulong thread_created;
-uint thread_handling;
ulong max_used_connections;
-static ulong my_bind_addr; /**< the address we bind to */
static volatile ulong cached_thread_count= 0;
-static const char *sql_mode_str= "OFF";
-/* Text representation for OPTIMIZER_SWITCH_DEFAULT */
-static const char *optimizer_switch_str="index_merge=on,index_merge_union=on,"
- "index_merge_sort_union=on,"
- "index_merge_intersection=on,"
- "index_merge_sort_intersection=off,"
- "index_condition_pushdown=on,"
- "derived_merge=on,"
- "derived_with_keys=on,"
- "firstmatch=on,"
- "loosescan=on,"
- "materialization=on,"
- "in_to_exists=on,"
- "semijoin=on,"
- "partial_match_rowid_merge=on,"
- "partial_match_table_scan=on,"
- "subquery_cache=on,"
- "mrr=off,"
- "mrr_cost_based=off,"
- "mrr_sort_keys=off,"
- "outer_join_with_cache=on,"
- "semijoin_with_cache=on,"
- "join_cache_incremental=on,"
- "join_cache_hashed=on,"
- "join_cache_bka=on,"
- "optimize_join_buffer_size=off,"
- "table_elimination=on";
- ;
-#ifdef SAFEMALLOC
-my_bool sf_malloc_trough_check= 0;
-#endif
-static char *mysqld_user, *mysqld_chroot, *log_error_file_ptr;
-static char *opt_init_slave, *language_ptr, *opt_init_connect;
+static char *mysqld_user, *mysqld_chroot;
static char *default_character_set_name;
static char *character_set_filesystem_name;
+static char *lc_messages;
static char *lc_time_names_name;
static char *my_bind_addr_str;
static char *default_collation_name;
-static char *default_storage_engine_str;
+char *default_storage_engine;
static char compiled_default_collation_name[]= MYSQL_DEFAULT_COLLATION_NAME;
static I_List<THD> thread_cache;
-static double long_query_time;
-static ulong opt_my_crc_dbug_check;
-
-static pthread_cond_t COND_thread_cache, COND_flush_thread_cache;
+static bool binlog_format_used= false;
+LEX_STRING opt_init_connect, opt_init_slave;
+static mysql_cond_t COND_thread_cache, COND_flush_thread_cache;
+static DYNAMIC_ARRAY all_options;
/* Global variables */
-bool opt_update_log, opt_bin_log, opt_ignore_builtin_innodb= 0;
-my_bool opt_log, opt_slow_log, debug_assert_if_crashed_table, opt_help= 0;
+bool opt_bin_log, opt_bin_log_used=0, opt_ignore_builtin_innodb= 0;
+my_bool opt_log, opt_slow_log, debug_assert_if_crashed_table= 0, opt_help= 0;
+static my_bool opt_abort;
+ulonglong log_output_options;
my_bool opt_userstat_running;
-ulong log_output_options;
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;
@@ -547,6 +378,10 @@ my_bool locked_in_memory;
bool opt_using_transactions;
bool volatile abort_loop;
bool volatile shutdown_in_progress;
+uint volatile global_disable_checkpoint;
+#if defined(_WIN32) && !defined(EMBEDDED_LIBRARY)
+ulong slow_start_timeout;
+#endif
/*
True if the bootstrap thread is running. Protected by LOCK_thread_count,
just like thread_count.
@@ -575,11 +410,11 @@ my_bool opt_skip_slave_start = 0; ///< If set, slave is not autostarted
my_bool opt_reckless_slave = 0;
my_bool opt_enable_named_pipe= 0;
my_bool opt_local_infile, opt_slave_compressed_protocol;
-my_bool opt_safe_user_create = 0, opt_no_mix_types = 0;
-my_bool opt_show_slave_auth_info, opt_sql_bin_update = 0;
+my_bool opt_safe_user_create = 0;
+my_bool opt_show_slave_auth_info;
my_bool opt_log_slave_updates= 0;
my_bool opt_replicate_annotate_row_events= 0;
-bool slave_warning_issued = false;
+char *opt_slave_skip_errors;
/*
Legacy global handlerton. These will be removed (please do not add more).
@@ -588,41 +423,25 @@ handlerton *heap_hton;
handlerton *myisam_hton;
handlerton *partition_hton;
-#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
-const char *opt_ndbcluster_connectstring= 0;
-const char *opt_ndb_connectstring= 0;
-char opt_ndb_constrbuf[1024]= {0};
-unsigned opt_ndb_constrbuf_len= 0;
-my_bool opt_ndb_shm, opt_ndb_optimized_node_selection;
-ulong opt_ndb_cache_check_time;
-const char *opt_ndb_mgmd;
-ulong opt_ndb_nodeid;
-ulong ndb_extra_logging;
-#ifdef HAVE_NDB_BINLOG
-ulong ndb_report_thresh_binlog_epoch_slip;
-ulong ndb_report_thresh_binlog_mem_usage;
-#endif
-
-extern const char *ndb_distribution_names[];
-extern TYPELIB ndb_distribution_typelib;
-extern const char *opt_ndb_distribution;
-extern enum ndb_distribution opt_ndb_distribution_id;
-#endif
-my_bool opt_readonly, use_temp_pool, relay_log_purge;
+my_bool read_only= 0, opt_readonly= 0;
+my_bool use_temp_pool, relay_log_purge;
+my_bool relay_log_recovery;
my_bool opt_sync_frm, opt_allow_suspicious_udfs;
my_bool opt_secure_auth= 0;
-char* opt_secure_file_priv= 0;
+char* opt_secure_file_priv;
my_bool opt_log_slow_admin_statements= 0;
my_bool opt_log_slow_slave_statements= 0;
-my_bool opt_query_cache_strip_comments = 0;
my_bool lower_case_file_system= 0;
my_bool opt_large_pages= 0;
+my_bool opt_super_large_pages= 0;
my_bool opt_myisam_use_mmap= 0;
-uint opt_large_page_size= 0;
+uint opt_large_page_size= 0;
#if defined(ENABLED_DEBUG_SYNC)
-uint opt_debug_sync_timeout= 0;
+MYSQL_PLUGIN_IMPORT uint opt_debug_sync_timeout= 0;
#endif /* defined(ENABLED_DEBUG_SYNC) */
my_bool opt_old_style_user_limits= 0, trust_function_creators= 0;
+ulong opt_replicate_events_marked_for_skip;
+
/*
True if there is at least one per-hour limit for some user, so we should
check them before each query (and possibly reset counters when hour is
@@ -636,45 +455,52 @@ ulong opt_binlog_rows_event_max_size;
my_bool opt_master_verify_checksum= 0;
my_bool opt_slave_sql_verify_checksum= 1;
const char *binlog_format_names[]= {"MIXED", "STATEMENT", "ROW", NullS};
-TYPELIB binlog_format_typelib=
- { array_elements(binlog_format_names) - 1, "",
- binlog_format_names, NULL };
-ulong opt_binlog_format_id= (ulong) BINLOG_FORMAT_UNSPEC;
-const char *opt_binlog_format= binlog_format_names[opt_binlog_format_id];
#ifdef HAVE_INITGROUPS
volatile sig_atomic_t calling_initgroups= 0; /**< Used in SIGSEGV handler. */
#endif
uint mysqld_port, test_flags, select_errors, dropping_tables, ha_open_options;
uint mysqld_extra_port;
uint mysqld_port_timeout;
-uint delay_key_write_options, protocol_version;
+ulong delay_key_write_options;
+uint protocol_version;
uint lower_case_table_names;
-uint tc_heuristic_recover= 0;
-uint volatile thread_count, thread_running;
-ulonglong thd_startup_options;
+ulong tc_heuristic_recover= 0;
+uint volatile thread_count;
+int32 thread_running;
+ulong thread_created;
ulong back_log, connect_timeout, concurrency, server_id;
ulong table_cache_size, table_def_size;
ulong what_to_log;
-ulong query_buff_size, slow_launch_time, slave_open_temp_tables;
+ulong slow_launch_time, slave_open_temp_tables;
ulong open_files_limit, max_binlog_size, max_relay_log_size;
-ulong slave_net_timeout, slave_trans_retries;
+ulong slave_trans_retries;
+uint slave_net_timeout;
ulong slave_exec_mode_options;
-static const char *slave_exec_mode_str= "STRICT";
-ulong thread_cache_size=0, thread_pool_size= 0;
-ulong binlog_cache_size=0;
-ulonglong max_binlog_cache_size=0;
+ulonglong slave_type_conversions_options;
+ulong thread_cache_size=0;
+ulonglong binlog_cache_size=0;
+ulonglong max_binlog_cache_size=0;
ulong slave_max_allowed_packet= 0;
-ulong query_cache_size=0;
+ulonglong binlog_stmt_cache_size=0;
+ulonglong max_binlog_stmt_cache_size=0;
+ulonglong query_cache_size=0;
ulong refresh_version; /* Increments on each reload */
+ulong executed_events=0;
query_id_t global_query_id;
+my_atomic_rwlock_t global_query_id_lock;
+my_atomic_rwlock_t thread_running_lock;
ulong aborted_threads, aborted_connects;
ulong delayed_insert_timeout, delayed_insert_limit, delayed_queue_size;
ulong delayed_insert_threads, delayed_insert_writes, delayed_rows_in_use;
ulong delayed_insert_errors,flush_time;
ulong specialflag=0;
ulong binlog_cache_use= 0, binlog_cache_disk_use= 0;
+ulong binlog_stmt_cache_use= 0, binlog_stmt_cache_disk_use= 0;
ulong max_connections, max_connect_errors;
ulong extra_max_connections;
+ulonglong denied_connections;
+my_decimal decimal_zero;
+
/*
Maximum length of parameter value which can be set through
mysql_send_long_data() call.
@@ -685,9 +511,7 @@ ulong max_long_data_size;
uint internal_tmp_table_max_key_length;
uint internal_tmp_table_max_key_segments;
-int max_user_connections= 0;
bool max_user_connections_checking=0;
-ulonglong denied_connections;
/**
Limit of the total number of prepared statements in the server.
Is necessary to protect the server against out-of-memory attacks.
@@ -705,10 +529,50 @@ ulong max_prepared_stmt_count;
*/
ulong prepared_stmt_count=0;
ulong thread_id=1L,current_pid;
-ulong slow_launch_threads = 0, sync_binlog_period;
+ulong slow_launch_threads = 0;
+uint sync_binlog_period= 0, sync_relaylog_period= 0,
+ sync_relayloginfo_period= 0, sync_masterinfo_period= 0;
ulong expire_logs_days = 0;
ulong rpl_recovery_rank=0;
-const char *log_output_str= "FILE";
+/**
+ Soft upper limit for number of sp_head objects that can be stored
+ in the sp_cache for one connection.
+*/
+ulong stored_program_cache_size= 0;
+
+const double log_10[] = {
+ 1e000, 1e001, 1e002, 1e003, 1e004, 1e005, 1e006, 1e007, 1e008, 1e009,
+ 1e010, 1e011, 1e012, 1e013, 1e014, 1e015, 1e016, 1e017, 1e018, 1e019,
+ 1e020, 1e021, 1e022, 1e023, 1e024, 1e025, 1e026, 1e027, 1e028, 1e029,
+ 1e030, 1e031, 1e032, 1e033, 1e034, 1e035, 1e036, 1e037, 1e038, 1e039,
+ 1e040, 1e041, 1e042, 1e043, 1e044, 1e045, 1e046, 1e047, 1e048, 1e049,
+ 1e050, 1e051, 1e052, 1e053, 1e054, 1e055, 1e056, 1e057, 1e058, 1e059,
+ 1e060, 1e061, 1e062, 1e063, 1e064, 1e065, 1e066, 1e067, 1e068, 1e069,
+ 1e070, 1e071, 1e072, 1e073, 1e074, 1e075, 1e076, 1e077, 1e078, 1e079,
+ 1e080, 1e081, 1e082, 1e083, 1e084, 1e085, 1e086, 1e087, 1e088, 1e089,
+ 1e090, 1e091, 1e092, 1e093, 1e094, 1e095, 1e096, 1e097, 1e098, 1e099,
+ 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109,
+ 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119,
+ 1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129,
+ 1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139,
+ 1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149,
+ 1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159,
+ 1e160, 1e161, 1e162, 1e163, 1e164, 1e165, 1e166, 1e167, 1e168, 1e169,
+ 1e170, 1e171, 1e172, 1e173, 1e174, 1e175, 1e176, 1e177, 1e178, 1e179,
+ 1e180, 1e181, 1e182, 1e183, 1e184, 1e185, 1e186, 1e187, 1e188, 1e189,
+ 1e190, 1e191, 1e192, 1e193, 1e194, 1e195, 1e196, 1e197, 1e198, 1e199,
+ 1e200, 1e201, 1e202, 1e203, 1e204, 1e205, 1e206, 1e207, 1e208, 1e209,
+ 1e210, 1e211, 1e212, 1e213, 1e214, 1e215, 1e216, 1e217, 1e218, 1e219,
+ 1e220, 1e221, 1e222, 1e223, 1e224, 1e225, 1e226, 1e227, 1e228, 1e229,
+ 1e230, 1e231, 1e232, 1e233, 1e234, 1e235, 1e236, 1e237, 1e238, 1e239,
+ 1e240, 1e241, 1e242, 1e243, 1e244, 1e245, 1e246, 1e247, 1e248, 1e249,
+ 1e250, 1e251, 1e252, 1e253, 1e254, 1e255, 1e256, 1e257, 1e258, 1e259,
+ 1e260, 1e261, 1e262, 1e263, 1e264, 1e265, 1e266, 1e267, 1e268, 1e269,
+ 1e270, 1e271, 1e272, 1e273, 1e274, 1e275, 1e276, 1e277, 1e278, 1e279,
+ 1e280, 1e281, 1e282, 1e283, 1e284, 1e285, 1e286, 1e287, 1e288, 1e289,
+ 1e290, 1e291, 1e292, 1e293, 1e294, 1e295, 1e296, 1e297, 1e298, 1e299,
+ 1e300, 1e301, 1e302, 1e303, 1e304, 1e305, 1e306, 1e307, 1e308
+};
time_t server_start_time, flush_status_time;
@@ -716,36 +580,31 @@ char mysql_home[FN_REFLEN], pidfile_name[FN_REFLEN], system_time_zone[30];
char *default_tz_name;
char log_error_file[FN_REFLEN], glob_hostname[FN_REFLEN], *opt_log_basename;
char mysql_real_data_home[FN_REFLEN],
- language[FN_REFLEN], reg_ext[FN_EXTLEN], mysql_charsets_dir[FN_REFLEN],
- *opt_init_file, *opt_tc_log_file,
- def_ft_boolean_syntax[sizeof(ft_boolean_syntax)];
-const char *opt_basename;
+ lc_messages_dir[FN_REFLEN], reg_ext[FN_EXTLEN],
+ mysql_charsets_dir[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;
+uint mysql_real_data_home_len, mysql_data_home_len= 1;
uint reg_ext_length;
const key_map key_map_empty(0);
key_map key_map_full(0); // Will be initialized later
-const char *opt_date_time_formats[3];
+DATE_TIME_FORMAT global_date_format, global_datetime_format, global_time_format;
+Time_zone *default_tz;
-uint mysql_data_home_len;
-char mysql_data_home_buff[2], *mysql_data_home=mysql_real_data_home;
+const char *mysql_real_data_home_ptr= mysql_real_data_home;
char server_version[SERVER_VERSION_LENGTH];
char *mysqld_unix_port, *opt_mysql_tmpdir;
-const char **errmesg; /**< Error messages */
-const char *myisam_recover_options_str="OFF";
-const char *myisam_stats_method_str="nulls_unequal";
-const char *opt_thread_handling= thread_handling_typelib.type_names[0];
+ulong thread_handling;
-/** name of reference on left espression in rewritten IN subquery */
+/** name of reference on left expression in rewritten IN subquery */
const char *in_left_expr_name= "<left expr>";
/** name of additional condition */
const char *in_additional_cond= "<IN COND>";
const char *in_having_cond= "<IN HAVING>";
-my_decimal decimal_zero;
-my_decimal max_seconds_for_time_type, time_second_part_factor;
-
/* classes for comparation parsing/processing */
Eq_creator eq_creator;
Ne_creator ne_creator;
@@ -754,15 +613,28 @@ Lt_creator lt_creator;
Ge_creator ge_creator;
Le_creator le_creator;
-FILE *bootstrap_file;
+MYSQL_FILE *bootstrap_file;
int bootstrap_error;
-FILE *stderror_file=0;
I_List<THD> threads;
-I_List<NAMED_LIST> key_caches;
Rpl_filter* rpl_filter;
Rpl_filter* binlog_filter;
+THD *first_global_thread()
+{
+ if (threads.is_empty())
+ return NULL;
+ return threads.head();
+}
+
+THD *next_global_thread(THD *thd)
+{
+ if (threads.is_last(thd))
+ return NULL;
+ struct ilink *next= thd->next;
+ return static_cast<THD*>(next);
+}
+
struct system_variables global_system_variables;
struct system_variables max_system_variables;
struct system_status_var global_status_var;
@@ -773,28 +645,31 @@ MY_BITMAP temp_pool;
CHARSET_INFO *system_charset_info, *files_charset_info ;
CHARSET_INFO *national_charset_info, *table_alias_charset;
CHARSET_INFO *character_set_filesystem;
+CHARSET_INFO *error_message_charset_info;
+MY_LOCALE *my_default_lc_messages;
MY_LOCALE *my_default_lc_time_names;
SHOW_COMP_OPTION have_ssl, have_symlink, have_dlopen, have_query_cache;
SHOW_COMP_OPTION have_geometry, have_rtree_keys;
SHOW_COMP_OPTION have_crypt, have_compress;
-SHOW_COMP_OPTION have_community_features;
+SHOW_COMP_OPTION have_profiling;
/* Thread specific variables */
pthread_key(MEM_ROOT**,THR_MALLOC);
pthread_key(THD*, THR_THD);
-pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count,
- LOCK_mapped_file, LOCK_status, LOCK_global_read_lock,
- LOCK_error_log,
- LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create,
- LOCK_crypt, LOCK_bytes_sent, LOCK_bytes_received,
- LOCK_global_system_variables,
- LOCK_user_conn, LOCK_slave_list, LOCK_active_mi,
- LOCK_connection_count, LOCK_short_uuid_generator;
-pthread_mutex_t LOCK_stats, LOCK_global_user_client_stats;
-pthread_mutex_t LOCK_global_table_stats, LOCK_global_index_stats;
+mysql_mutex_t LOCK_thread_count;
+mysql_mutex_t
+ LOCK_status, LOCK_error_log, LOCK_short_uuid_generator,
+ LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create,
+ LOCK_crypt,
+ LOCK_global_system_variables,
+ LOCK_user_conn, LOCK_slave_list, LOCK_active_mi,
+ LOCK_connection_count, LOCK_error_messages;
+
+mysql_mutex_t LOCK_stats, LOCK_global_user_client_stats,
+ LOCK_global_table_stats, LOCK_global_index_stats;
/**
The below lock protects access to two global server variables:
@@ -803,53 +678,499 @@ pthread_mutex_t LOCK_global_table_stats, LOCK_global_index_stats;
in the server, respectively. As PREPARE/DEALLOCATE rate in a loaded
server may be fairly high, we need a dedicated lock.
*/
-pthread_mutex_t LOCK_prepared_stmt_count;
+mysql_mutex_t LOCK_prepared_stmt_count;
#ifdef HAVE_OPENSSL
-pthread_mutex_t LOCK_des_key_file;
+mysql_mutex_t LOCK_des_key_file;
#endif
-rw_lock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
-rw_lock_t LOCK_system_variables_hash;
-pthread_cond_t COND_refresh, COND_thread_count, COND_global_read_lock;
+mysql_rwlock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
+mysql_rwlock_t LOCK_system_variables_hash;
+mysql_cond_t COND_thread_count;
pthread_t signal_thread;
pthread_attr_t connection_attrib;
-pthread_mutex_t LOCK_server_started;
-pthread_cond_t COND_server_started;
+mysql_mutex_t LOCK_server_started;
+mysql_cond_t COND_server_started;
int mysqld_server_started= 0;
File_parser_dummy_hook file_parser_dummy_hook;
/* replication parameters, if master_host is not NULL, we are a slave */
-uint master_port= MYSQL_PORT, master_connect_retry = 60;
-uint report_port= MYSQL_PORT;
+uint report_port= 0;
ulong master_retry_count=0;
-char *master_user, *master_password, *master_host, *master_info_file;
-char *relay_log_info_file;
-char *report_user, *report_password, *report_host;
+char *master_info_file;
+char *relay_log_info_file, *report_user, *report_password, *report_host;
char *opt_relay_logname = 0, *opt_relaylog_index_name=0;
-my_bool master_ssl;
-char *master_ssl_key, *master_ssl_cert;
-char *master_ssl_ca, *master_ssl_capath, *master_ssl_cipher;
-char *opt_logname, *opt_slow_logname;
+char *opt_logname, *opt_slow_logname, *opt_bin_logname;
/* Static variables */
static volatile sig_atomic_t kill_in_progress;
my_bool opt_stack_trace;
-my_bool opt_expect_abort= 0;
-static my_bool opt_bootstrap, opt_myisam_log;
+my_bool opt_expect_abort= 0, opt_bootstrap= 0;
+static my_bool opt_myisam_log;
static int cleanup_done;
-static ulong opt_specialflag, opt_myisam_block_size;
-static char *opt_update_logname, *opt_binlog_index_name;
-static char *opt_tc_heuristic_recover;
-static char *mysql_home_ptr, *pidfile_name_ptr;
+static ulong opt_specialflag;
+static char *opt_binlog_index_name;
+char *mysql_home_ptr, *pidfile_name_ptr;
+/** Initial command line arguments (count), after load_defaults().*/
static int defaults_argc;
+/**
+ Initial command line arguments (arguments), after load_defaults().
+ This memory is allocated by @c load_defaults() and should be freed
+ using @c free_defaults().
+ Do not modify defaults_argc / defaults_argv,
+ use remaining_argc / remaining_argv instead to parse the command
+ line arguments in multiple steps.
+*/
static char **defaults_argv;
-static char *opt_bin_logname;
+/** Remaining command line arguments (count), filtered by handle_options().*/
+static int remaining_argc;
+/** Remaining command line arguments (arguments), filtered by handle_options().*/
+static char **remaining_argv;
int orig_argc;
char **orig_argv;
+#ifdef HAVE_PSI_INTERFACE
+#ifdef HAVE_MMAP
+PSI_mutex_key key_PAGE_lock, key_LOCK_sync, key_LOCK_active, key_LOCK_pool;
+#endif /* HAVE_MMAP */
+
+#ifdef HAVE_OPENSSL
+PSI_mutex_key key_LOCK_des_key_file;
+#endif /* HAVE_OPENSSL */
+
+PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_prep_xids,
+ key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi,
+ key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create,
+ key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
+ key_LOCK_gdl, key_LOCK_global_system_variables,
+ key_LOCK_manager,
+ key_LOCK_prepared_stmt_count,
+ key_LOCK_rpl_status, key_LOCK_server_started, key_LOCK_status,
+ key_LOCK_system_variables_hash, key_LOCK_table_share, key_LOCK_thd_data,
+ key_LOCK_user_conn, key_LOCK_uuid_short_generator, key_LOG_LOCK_log,
+ key_master_info_data_lock, key_master_info_run_lock,
+ key_master_info_sleep_lock,
+ key_mutex_slave_reporting_capability_err_lock, key_relay_log_info_data_lock,
+ key_relay_log_info_log_space_lock, key_relay_log_info_run_lock,
+ key_relay_log_info_sleep_lock,
+ key_structure_guard_mutex, key_TABLE_SHARE_LOCK_ha_data,
+ key_LOCK_error_messages, key_LOG_INFO_lock, key_LOCK_thread_count,
+ key_PARTITION_LOCK_auto_inc;
+PSI_mutex_key key_RELAYLOG_LOCK_index;
+
+PSI_mutex_key key_LOCK_stats,
+ key_LOCK_global_user_client_stats, key_LOCK_global_table_stats,
+ key_LOCK_global_index_stats,
+ key_LOCK_wakeup_ready;
+
+PSI_mutex_key key_LOCK_prepare_ordered, key_LOCK_commit_ordered;
+
+static PSI_mutex_info all_server_mutexes[]=
+{
+#ifdef HAVE_MMAP
+ { &key_PAGE_lock, "PAGE::lock", 0},
+ { &key_LOCK_sync, "TC_LOG_MMAP::LOCK_sync", 0},
+ { &key_LOCK_active, "TC_LOG_MMAP::LOCK_active", 0},
+ { &key_LOCK_pool, "TC_LOG_MMAP::LOCK_pool", 0},
+#endif /* HAVE_MMAP */
+
+#ifdef HAVE_OPENSSL
+ { &key_LOCK_des_key_file, "LOCK_des_key_file", PSI_FLAG_GLOBAL},
+#endif /* HAVE_OPENSSL */
+
+ { &key_BINLOG_LOCK_index, "MYSQL_BIN_LOG::LOCK_index", 0},
+ { &key_BINLOG_LOCK_prep_xids, "MYSQL_BIN_LOG::LOCK_prep_xids", 0},
+ { &key_RELAYLOG_LOCK_index, "MYSQL_RELAY_LOG::LOCK_index", 0},
+ { &key_delayed_insert_mutex, "Delayed_insert::mutex", 0},
+ { &key_hash_filo_lock, "hash_filo::lock", 0},
+ { &key_LOCK_active_mi, "LOCK_active_mi", PSI_FLAG_GLOBAL},
+ { &key_LOCK_connection_count, "LOCK_connection_count", PSI_FLAG_GLOBAL},
+ { &key_LOCK_crypt, "LOCK_crypt", PSI_FLAG_GLOBAL},
+ { &key_LOCK_delayed_create, "LOCK_delayed_create", PSI_FLAG_GLOBAL},
+ { &key_LOCK_delayed_insert, "LOCK_delayed_insert", PSI_FLAG_GLOBAL},
+ { &key_LOCK_delayed_status, "LOCK_delayed_status", PSI_FLAG_GLOBAL},
+ { &key_LOCK_error_log, "LOCK_error_log", PSI_FLAG_GLOBAL},
+ { &key_LOCK_gdl, "LOCK_gdl", PSI_FLAG_GLOBAL},
+ { &key_LOCK_global_system_variables, "LOCK_global_system_variables", PSI_FLAG_GLOBAL},
+ { &key_LOCK_manager, "LOCK_manager", PSI_FLAG_GLOBAL},
+ { &key_LOCK_prepared_stmt_count, "LOCK_prepared_stmt_count", PSI_FLAG_GLOBAL},
+ { &key_LOCK_rpl_status, "LOCK_rpl_status", PSI_FLAG_GLOBAL},
+ { &key_LOCK_server_started, "LOCK_server_started", PSI_FLAG_GLOBAL},
+ { &key_LOCK_status, "LOCK_status", PSI_FLAG_GLOBAL},
+ { &key_LOCK_system_variables_hash, "LOCK_system_variables_hash", PSI_FLAG_GLOBAL},
+ { &key_LOCK_table_share, "LOCK_table_share", PSI_FLAG_GLOBAL},
+ { &key_LOCK_stats, "LOCK_stats", PSI_FLAG_GLOBAL},
+ { &key_LOCK_global_user_client_stats, "LOCK_global_user_client_stats", PSI_FLAG_GLOBAL},
+ { &key_LOCK_global_table_stats, "LOCK_global_table_stats", PSI_FLAG_GLOBAL},
+ { &key_LOCK_global_index_stats, "LOCK_global_index_stats", PSI_FLAG_GLOBAL},
+ { &key_LOCK_wakeup_ready, "THD::LOCK_wakeup_ready", 0},
+ { &key_LOCK_thd_data, "THD::LOCK_thd_data", 0},
+ { &key_LOCK_user_conn, "LOCK_user_conn", PSI_FLAG_GLOBAL},
+ { &key_LOCK_uuid_short_generator, "LOCK_uuid_short_generator", PSI_FLAG_GLOBAL},
+ { &key_LOG_LOCK_log, "LOG::LOCK_log", 0},
+ { &key_master_info_data_lock, "Master_info::data_lock", 0},
+ { &key_master_info_run_lock, "Master_info::run_lock", 0},
+ { &key_master_info_sleep_lock, "Master_info::sleep_lock", 0},
+ { &key_mutex_slave_reporting_capability_err_lock, "Slave_reporting_capability::err_lock", 0},
+ { &key_relay_log_info_data_lock, "Relay_log_info::data_lock", 0},
+ { &key_relay_log_info_log_space_lock, "Relay_log_info::log_space_lock", 0},
+ { &key_relay_log_info_run_lock, "Relay_log_info::run_lock", 0},
+ { &key_relay_log_info_sleep_lock, "Relay_log_info::sleep_lock", 0},
+ { &key_structure_guard_mutex, "Query_cache::structure_guard_mutex", 0},
+ { &key_TABLE_SHARE_LOCK_ha_data, "TABLE_SHARE::LOCK_ha_data", 0},
+ { &key_LOCK_error_messages, "LOCK_error_messages", PSI_FLAG_GLOBAL},
+ { &key_LOCK_prepare_ordered, "LOCK_prepare_ordered", PSI_FLAG_GLOBAL},
+ { &key_LOCK_commit_ordered, "LOCK_commit_ordered", PSI_FLAG_GLOBAL},
+ { &key_LOG_INFO_lock, "LOG_INFO::lock", 0},
+ { &key_LOCK_thread_count, "LOCK_thread_count", PSI_FLAG_GLOBAL},
+ { &key_PARTITION_LOCK_auto_inc, "HA_DATA_PARTITION::LOCK_auto_inc", 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;
+
+static PSI_rwlock_info all_server_rwlocks[]=
+{
+#if defined (HAVE_OPENSSL) && !defined(HAVE_YASSL)
+ { &key_rwlock_openssl, "CRYPTO_dynlock_value::lock", 0},
+#endif
+ { &key_rwlock_LOCK_grant, "LOCK_grant", PSI_FLAG_GLOBAL},
+ { &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_rwlock_LOCK_system_variables_hash, "LOCK_system_variables_hash", PSI_FLAG_GLOBAL},
+ { &key_rwlock_query_cache_query_lock, "Query_cache_query::lock", 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_prep_xids, key_BINLOG_update_cond,
+ key_COND_cache_status_changed, key_COND_manager,
+ key_COND_rpl_status, key_COND_server_started,
+ key_delayed_insert_cond, key_delayed_insert_cond_client,
+ key_item_func_sleep_cond, key_master_info_data_cond,
+ key_master_info_start_cond, key_master_info_stop_cond,
+ key_master_info_sleep_cond,
+ key_relay_log_info_data_cond, key_relay_log_info_log_space_cond,
+ key_relay_log_info_start_cond, key_relay_log_info_stop_cond,
+ key_relay_log_info_sleep_cond,
+ key_TABLE_SHARE_cond, key_user_level_lock_cond,
+ key_COND_thread_count, key_COND_thread_cache, key_COND_flush_thread_cache,
+ key_BINLOG_COND_queue_busy;
+PSI_cond_key key_RELAYLOG_update_cond, key_COND_wakeup_ready;
+PSI_cond_key key_RELAYLOG_COND_queue_busy;
+PSI_cond_key key_TC_LOG_MMAP_COND_queue_busy;
+
+static PSI_cond_info all_server_conds[]=
+{
+#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY)
+ { &key_COND_handler_count, "COND_handler_count", PSI_FLAG_GLOBAL},
+#endif /* _WIN32 || HAVE_SMEM && !EMBEDDED_LIBRARY */
+#ifdef HAVE_MMAP
+ { &key_PAGE_cond, "PAGE::cond", 0},
+ { &key_COND_active, "TC_LOG_MMAP::COND_active", 0},
+ { &key_COND_pool, "TC_LOG_MMAP::COND_pool", 0},
+ { &key_TC_LOG_MMAP_COND_queue_busy, "TC_LOG_MMAP::COND_queue_busy", 0},
+#endif /* HAVE_MMAP */
+ { &key_BINLOG_COND_prep_xids, "MYSQL_BIN_LOG::COND_prep_xids", 0},
+ { &key_BINLOG_update_cond, "MYSQL_BIN_LOG::update_cond", 0},
+ { &key_BINLOG_COND_queue_busy, "MYSQL_BIN_LOG::COND_queue_busy", 0},
+ { &key_RELAYLOG_update_cond, "MYSQL_RELAY_LOG::update_cond", 0},
+ { &key_RELAYLOG_COND_queue_busy, "MYSQL_RELAY_LOG::COND_queue_busy", 0},
+ { &key_COND_wakeup_ready, "THD::COND_wakeup_ready", 0},
+ { &key_COND_cache_status_changed, "Query_cache::COND_cache_status_changed", 0},
+ { &key_COND_manager, "COND_manager", PSI_FLAG_GLOBAL},
+ { &key_COND_rpl_status, "COND_rpl_status", PSI_FLAG_GLOBAL},
+ { &key_COND_server_started, "COND_server_started", PSI_FLAG_GLOBAL},
+ { &key_delayed_insert_cond, "Delayed_insert::cond", 0},
+ { &key_delayed_insert_cond_client, "Delayed_insert::cond_client", 0},
+ { &key_item_func_sleep_cond, "Item_func_sleep::cond", 0},
+ { &key_master_info_data_cond, "Master_info::data_cond", 0},
+ { &key_master_info_start_cond, "Master_info::start_cond", 0},
+ { &key_master_info_stop_cond, "Master_info::stop_cond", 0},
+ { &key_master_info_sleep_cond, "Master_info::sleep_cond", 0},
+ { &key_relay_log_info_data_cond, "Relay_log_info::data_cond", 0},
+ { &key_relay_log_info_log_space_cond, "Relay_log_info::log_space_cond", 0},
+ { &key_relay_log_info_start_cond, "Relay_log_info::start_cond", 0},
+ { &key_relay_log_info_stop_cond, "Relay_log_info::stop_cond", 0},
+ { &key_relay_log_info_sleep_cond, "Relay_log_info::sleep_cond", 0},
+ { &key_TABLE_SHARE_cond, "TABLE_SHARE::cond", 0},
+ { &key_user_level_lock_cond, "User_level_lock::cond", 0},
+ { &key_COND_thread_count, "COND_thread_count", PSI_FLAG_GLOBAL},
+ { &key_COND_thread_cache, "COND_thread_cache", PSI_FLAG_GLOBAL},
+ { &key_COND_flush_thread_cache, "COND_flush_thread_cache", PSI_FLAG_GLOBAL}
+};
+
+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;
+
+static PSI_thread_info all_server_threads[]=
+{
+#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY)
+ { &key_thread_handle_con_namedpipes, "con_named_pipes", PSI_FLAG_GLOBAL},
+#endif /* _WIN32 || HAVE_SMEM && !EMBEDDED_LIBRARY */
+
+#if defined(HAVE_SMEM) && !defined(EMBEDDED_LIBRARY)
+ { &key_thread_handle_con_sharedmem, "con_shared_mem", PSI_FLAG_GLOBAL},
+#endif /* HAVE_SMEM && !EMBEDDED_LIBRARY */
+
+#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY)
+ { &key_thread_handle_con_sockets, "con_sockets", PSI_FLAG_GLOBAL},
+#endif /* _WIN32 || HAVE_SMEM && !EMBEDDED_LIBRARY */
+
+#ifdef __WIN__
+ { &key_thread_handle_shutdown, "shutdown", PSI_FLAG_GLOBAL},
+#endif /* __WIN__ */
+
+ { &key_thread_bootstrap, "bootstrap", PSI_FLAG_GLOBAL},
+ { &key_thread_delayed_insert, "delayed_insert", 0},
+ { &key_thread_handle_manager, "manager", PSI_FLAG_GLOBAL},
+ { &key_thread_main, "main", PSI_FLAG_GLOBAL},
+ { &key_thread_one_connection, "one_connection", 0},
+ { &key_thread_signal_hand, "signal_handler", PSI_FLAG_GLOBAL}
+};
+
+PSI_file_key key_file_binlog, key_file_binlog_index, key_file_casetest,
+ key_file_dbopt, key_file_des_key_file, key_file_ERRMSG, key_select_to_file,
+ key_file_fileparser, key_file_frm, key_file_global_ddl_log, key_file_load,
+ key_file_loadfile, key_file_log_event_data, key_file_log_event_info,
+ key_file_master_info, key_file_misc, key_file_partition,
+ key_file_pid, key_file_relay_log_info, key_file_send_file, key_file_tclog,
+ key_file_trg, key_file_trn, key_file_init;
+PSI_file_key key_file_query_log, key_file_slow_log;
+PSI_file_key key_file_relaylog, key_file_relaylog_index;
+
+static PSI_file_info all_server_files[]=
+{
+ { &key_file_binlog, "binlog", 0},
+ { &key_file_binlog_index, "binlog_index", 0},
+ { &key_file_relaylog, "relaylog", 0},
+ { &key_file_relaylog_index, "relaylog_index", 0},
+ { &key_file_casetest, "casetest", 0},
+ { &key_file_dbopt, "dbopt", 0},
+ { &key_file_des_key_file, "des_key_file", 0},
+ { &key_file_ERRMSG, "ERRMSG", 0},
+ { &key_select_to_file, "select_to_file", 0},
+ { &key_file_fileparser, "file_parser", 0},
+ { &key_file_frm, "FRM", 0},
+ { &key_file_global_ddl_log, "global_ddl_log", 0},
+ { &key_file_load, "load", 0},
+ { &key_file_loadfile, "LOAD_FILE", 0},
+ { &key_file_log_event_data, "log_event_data", 0},
+ { &key_file_log_event_info, "log_event_info", 0},
+ { &key_file_master_info, "master_info", 0},
+ { &key_file_misc, "misc", 0},
+ { &key_file_partition, "partition", 0},
+ { &key_file_pid, "pid", 0},
+ { &key_file_query_log, "query_log", 0},
+ { &key_file_relay_log_info, "relay_log_info", 0},
+ { &key_file_send_file, "send_file", 0},
+ { &key_file_slow_log, "slow_log", 0},
+ { &key_file_tclog, "tclog", 0},
+ { &key_file_trg, "trigger_name", 0},
+ { &key_file_trn, "trigger", 0},
+ { &key_file_init, "init", 0}
+};
+
+/**
+ Initialise all the performance schema instrumentation points
+ used by the server.
+*/
+void init_server_psi_keys(void)
+{
+ const char* category= "sql";
+ int count;
+
+ if (PSI_server == NULL)
+ return;
+
+ count= array_elements(all_server_mutexes);
+ PSI_server->register_mutex(category, all_server_mutexes, count);
+
+ count= array_elements(all_server_rwlocks);
+ PSI_server->register_rwlock(category, all_server_rwlocks, count);
+
+ count= array_elements(all_server_conds);
+ PSI_server->register_cond(category, all_server_conds, count);
+
+ count= array_elements(all_server_threads);
+ PSI_server->register_thread(category, all_server_threads, count);
+
+ count= array_elements(all_server_files);
+ PSI_server->register_file(category, all_server_files, count);
+}
+
+#endif /* HAVE_PSI_INTERFACE */
+
+/*
+ Since buffered_option_error_reporter is only used currently
+ for parsing performance schema options, this code is not needed
+ when the performance schema is not compiled in.
+*/
+#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
+/**
+ A log message for the error log, buffered in memory.
+ Log messages are temporarily buffered when generated before the error log
+ is initialized, and then printed once the error log is ready.
+*/
+class Buffered_log : public Sql_alloc
+{
+public:
+ Buffered_log(enum loglevel level, const char *message);
+
+ ~Buffered_log()
+ {}
+
+ void print(void);
+
+private:
+ /** Log message level. */
+ enum loglevel m_level;
+ /** Log message text. */
+ String m_message;
+};
+
+/**
+ Constructor.
+ @param level the message log level
+ @param message the message text
+*/
+Buffered_log::Buffered_log(enum loglevel level, const char *message)
+ : m_level(level), m_message()
+{
+ m_message.copy(message, strlen(message), &my_charset_latin1);
+}
+
+/**
+ Print a buffered log to the real log file.
+*/
+void Buffered_log::print()
+{
+ /*
+ Since messages are buffered, they can be printed out
+ of order with other entries in the log.
+ Add "Buffered xxx" to the message text to prevent confusion.
+ */
+ switch(m_level)
+ {
+ case ERROR_LEVEL:
+ sql_print_error("Buffered error: %s\n", m_message.c_ptr_safe());
+ break;
+ case WARNING_LEVEL:
+ sql_print_warning("Buffered warning: %s\n", m_message.c_ptr_safe());
+ break;
+ case INFORMATION_LEVEL:
+ /*
+ Messages printed as "information" still end up in the mysqld *error* log,
+ but with a [Note] tag instead of an [ERROR] tag.
+ While this is probably fine for a human reading the log,
+ it is upsetting existing automated scripts used to parse logs,
+ because such scripts are likely to not already handle [Note] properly.
+ INFORMATION_LEVEL messages are simply silenced, on purpose,
+ to avoid un needed verbosity.
+ */
+ break;
+ }
+}
+
+/**
+ Collection of all the buffered log messages.
+*/
+class Buffered_logs
+{
+public:
+ Buffered_logs()
+ {}
+
+ ~Buffered_logs()
+ {}
+
+ void init();
+ void cleanup();
+
+ void buffer(enum loglevel m_level, const char *msg);
+ void print();
+private:
+ /**
+ Memory root to use to store buffered logs.
+ This memory root lifespan is between init and cleanup.
+ Once the buffered logs are printed, they are not needed anymore,
+ and all the memory used is reclaimed.
+ */
+ MEM_ROOT m_root;
+ /** List of buffered log messages. */
+ List<Buffered_log> m_list;
+};
+
+void Buffered_logs::init()
+{
+ init_alloc_root(&m_root, 1024, 0);
+}
+
+void Buffered_logs::cleanup()
+{
+ m_list.delete_elements();
+ free_root(&m_root, MYF(0));
+}
+
+/**
+ Add a log message to the buffer.
+*/
+void Buffered_logs::buffer(enum loglevel level, const char *msg)
+{
+ /*
+ Do not let Sql_alloc::operator new(size_t) allocate memory,
+ there is no memory root associated with the main() thread.
+ Give explicitly the proper memory root to use to
+ Sql_alloc::operator new(size_t, MEM_ROOT *) instead.
+ */
+ Buffered_log *log= new (&m_root) Buffered_log(level, msg);
+ if (log)
+ m_list.push_back(log, &m_root);
+}
+
+/**
+ Print buffered log messages.
+*/
+void Buffered_logs::print()
+{
+ Buffered_log *log;
+ List_iterator_fast<Buffered_log> it(m_list);
+ while ((log= it++))
+ log->print();
+}
+
+/** Logs reported before a logger is available. */
+static Buffered_logs buffered_logs;
+
+#ifndef EMBEDDED_LIBRARY
+/**
+ Error reporter that buffer log messages.
+ @param level log message level
+ @param format log message format string
+*/
+C_MODE_START
+static void buffered_option_error_reporter(enum loglevel level,
+ const char *format, ...)
+{
+ va_list args;
+ char buffer[1024];
+
+ va_start(args, format);
+ my_vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+ buffered_logs.buffer(level, buffer);
+}
+C_MODE_END
+#endif /* !EMBEDDED_LIBRARY */
+#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
+
static my_socket unix_sock, base_ip_sock, extra_ip_sock;
struct my_rnd_struct sql_rand; ///< used by sql_class.cc:THD::THD()
@@ -864,7 +1185,7 @@ static pthread_t select_thread;
#undef getpid
#include <process.h>
-static pthread_cond_t COND_handler_count;
+static mysql_cond_t COND_handler_count;
static uint handler_count;
static bool start_mode=0, use_opt_args;
static int opt_argc;
@@ -878,7 +1199,7 @@ static NTService Service; ///< Service object for WinNT
#endif /* EMBEDDED_LIBRARY */
#endif /* __WIN__ */
-#ifdef __NT__
+#ifdef _WIN32
static char pipe_name[512];
static SECURITY_ATTRIBUTES saPipeSecurity;
static SECURITY_DESCRIPTOR sdPipeDescriptor;
@@ -894,15 +1215,15 @@ bool mysqld_embedded=1;
static my_bool plugins_are_initialized= FALSE;
#ifndef DBUG_OFF
-static const char* default_dbug_option, *current_dbug_option;
+static const char* default_dbug_option;
#endif
+static const char *current_dbug_option="disabled";
#ifdef HAVE_LIBWRAP
const char *libwrapName= NULL;
int allow_severity = LOG_INFO;
int deny_severity = LOG_WARNING;
#endif
#ifdef HAVE_QUERY_CACHE
-static ulong query_cache_limit= 0;
ulong query_cache_min_res_unit= QUERY_CACHE_MIN_RESULT_DATA_SIZE;
Query_cache query_cache;
#endif
@@ -912,16 +1233,20 @@ my_bool opt_enable_shared_memory;
HANDLE smem_event_connect_request= 0;
#endif
-scheduler_functions thread_scheduler, extra_thread_scheduler;
+my_bool opt_use_ssl = 0;
+char *opt_ssl_ca= NULL, *opt_ssl_capath= NULL, *opt_ssl_cert= NULL,
+ *opt_ssl_cipher= NULL, *opt_ssl_key= NULL;
+
+static scheduler_functions thread_scheduler_struct, extra_thread_scheduler_struct;
+scheduler_functions *thread_scheduler= &thread_scheduler_struct,
+ *extra_thread_scheduler= &extra_thread_scheduler_struct;
-#define SSL_VARS_NOT_STATIC
-#include "sslopt-vars.h"
#ifdef HAVE_OPENSSL
#include <openssl/crypto.h>
#ifndef HAVE_YASSL
typedef struct CRYPTO_dynlock_value
{
- rw_lock_t lock;
+ mysql_rwlock_t lock;
} openssl_lock_t;
static openssl_lock_t *openssl_stdlocks;
@@ -932,7 +1257,9 @@ static void openssl_lock(int, openssl_lock_t *, const char *, int);
static unsigned long openssl_id_function();
#endif
char *des_key_file;
+#ifndef EMBEDDED_LIBRARY
struct st_VioSSLFd *ssl_acceptor_fd;
+#endif
#endif /* HAVE_OPENSSL */
/**
@@ -945,28 +1272,28 @@ uint connection_count= 0, extra_connection_count= 0;
pthread_handler_t signal_hand(void *arg);
static int mysql_init_variables(void);
-static int get_options(int *argc,char **argv);
+static int get_options(int *argc_ptr, char ***argv_ptr);
+static bool add_terminator(DYNAMIC_ARRAY *options);
extern "C" my_bool mysqld_get_one_option(int, const struct my_option *, char *);
static int init_thread_environment();
static char *get_relative_path(const char *path);
static int fix_paths(void);
-pthread_handler_t handle_connections_sockets(void *arg);
+void handle_connections_sockets();
+#ifdef _WIN32
+pthread_handler_t handle_connections_sockets_thread(void *arg);
+#endif
pthread_handler_t kill_server_thread(void *arg);
-static void bootstrap(FILE *file);
+static void bootstrap(MYSQL_FILE *file);
static bool read_init_file(char *file_name);
-#ifdef __NT__
+#ifdef _WIN32
pthread_handler_t handle_connections_namedpipes(void *arg);
#endif
#ifdef HAVE_SMEM
pthread_handler_t handle_connections_shared_memory(void *arg);
#endif
pthread_handler_t handle_slave(void *arg);
-static ulong find_bit_type(const char *x, TYPELIB *bit_lib);
-static ulong find_bit_type_or_exit(const char *x, TYPELIB *bit_lib,
- const char *option, int *error);
static void clean_up(bool print_message);
static int test_if_case_insensitive(const char *dir_name);
-static void register_mutex_order();
#ifndef EMBEDDED_LIBRARY
static bool pid_file_created= false;
@@ -976,9 +1303,10 @@ static void close_server_sock();
static void clean_up_mutexes(void);
static void wait_for_signal_thread_to_end(void);
static void create_pid_file();
-static void end_ssl();
+static void mysqld_exit(int exit_code) __attribute__((noreturn));
#endif
static void delete_pid_file(myf flags);
+static void end_ssl();
#ifndef EMBEDDED_LIBRARY
@@ -998,10 +1326,10 @@ static void close_connections(void)
flush_thread_cache();
/* kill connection thread */
-#if !defined(__WIN__) && !defined(__NETWARE__)
+#if !defined(__WIN__)
DBUG_PRINT("quit", ("waiting for select thread: 0x%lx",
(ulong) select_thread));
- (void) pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
while (select_thread_in_use)
{
@@ -1017,18 +1345,18 @@ static void close_connections(void)
set_timespec(abstime, 2);
for (uint tmp=0 ; tmp < 10 && select_thread_in_use; tmp++)
{
- error=pthread_cond_timedwait(&COND_thread_count,&LOCK_thread_count,
- &abstime);
+ error= mysql_cond_timedwait(&COND_thread_count, &LOCK_thread_count,
+ &abstime);
if (error != EINTR)
break;
}
#ifdef EXTRA_DEBUG
if (error != 0 && error != ETIMEDOUT && !count++)
- sql_print_error("Got error %d from pthread_cond_timedwait",error);
+ sql_print_error("Got error %d from mysql_cond_timedwait", error);
#endif
close_server_sock();
}
- (void) pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
#endif /* __WIN__ */
@@ -1049,7 +1377,7 @@ static void close_connections(void)
extra_ip_sock= INVALID_SOCKET;
}
}
-#ifdef __NT__
+#ifdef _WIN32
if (hPipe != INVALID_HANDLE_VALUE && opt_enable_named_pipe)
{
HANDLE temp;
@@ -1091,7 +1419,7 @@ static void close_connections(void)
*/
THD *tmp;
- (void) pthread_mutex_lock(&LOCK_thread_count); // For unlink from list
+ mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
I_List_iterator<THD> it(threads);
while ((tmp=it++))
@@ -1103,33 +1431,33 @@ static void close_connections(void)
continue;
tmp->killed= KILL_SERVER_HARD;
- thread_scheduler.post_kill_notification(tmp);
- pthread_mutex_lock(&tmp->LOCK_thd_data);
+ MYSQL_CALLBACK(thread_scheduler, post_kill_notification, (tmp));
+ mysql_mutex_lock(&tmp->LOCK_thd_data);
if (tmp->mysys_var)
{
tmp->mysys_var->abort=1;
- pthread_mutex_lock(&tmp->mysys_var->mutex);
+ mysql_mutex_lock(&tmp->mysys_var->mutex);
if (tmp->mysys_var->current_cond)
{
uint i;
for (i=0; i < 2; i++)
{
- int ret= pthread_mutex_trylock(tmp->mysys_var->current_mutex);
- pthread_cond_broadcast(tmp->mysys_var->current_cond);
+ int ret= mysql_mutex_trylock(tmp->mysys_var->current_mutex);
+ mysql_cond_broadcast(tmp->mysys_var->current_cond);
if (!ret)
{
/* Thread has surely got the signal, unlock and abort */
- pthread_mutex_unlock(tmp->mysys_var->current_mutex);
+ mysql_mutex_unlock(tmp->mysys_var->current_mutex);
break;
}
sleep(1);
}
}
- pthread_mutex_unlock(&tmp->mysys_var->mutex);
+ mysql_mutex_unlock(&tmp->mysys_var->mutex);
}
- pthread_mutex_unlock(&tmp->LOCK_thd_data);
+ mysql_mutex_unlock(&tmp->LOCK_thd_data);
}
- (void) pthread_mutex_unlock(&LOCK_thread_count); // For unlink from list
+ mysql_mutex_unlock(&LOCK_thread_count); // For unlink from list
Events::deinit();
end_slave();
@@ -1147,38 +1475,37 @@ static void close_connections(void)
for (;;)
{
DBUG_PRINT("quit",("Locking LOCK_thread_count"));
- (void) pthread_mutex_lock(&LOCK_thread_count); // For unlink from list
+ mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
if (!(tmp=threads.get()))
{
DBUG_PRINT("quit",("Unlocking LOCK_thread_count"));
- (void) pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
break;
}
#ifndef __bsdi__ // Bug in BSDI kernel
if (tmp->vio_ok())
{
if (global_system_variables.log_warnings)
- sql_print_warning(ER(ER_FORCING_CLOSE),my_progname,
+ sql_print_warning(ER_DEFAULT(ER_FORCING_CLOSE),my_progname,
tmp->thread_id,
(tmp->main_security_ctx.user ?
tmp->main_security_ctx.user : ""));
- close_connection(tmp,ER_SERVER_SHUTDOWN,0);
+ close_connection(tmp,ER_SERVER_SHUTDOWN);
}
#endif
DBUG_PRINT("quit",("Unlocking LOCK_thread_count"));
- (void) pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
}
/* All threads has now been aborted */
DBUG_PRINT("quit",("Waiting for threads to die (count=%u)",thread_count));
- (void) pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
while (thread_count)
{
- (void) pthread_cond_wait(&COND_thread_count,&LOCK_thread_count);
+ mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
DBUG_PRINT("quit",("One thread died (count=%u)",thread_count));
}
- (void) pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
- close_active_mi();
DBUG_PRINT("quit",("close_connections thread"));
DBUG_VOID_RETURN;
}
@@ -1217,8 +1544,9 @@ static void close_server_sock()
close_socket(unix_sock, "unix/IP");
if (unix_sock != INVALID_SOCKET)
- VOID(unlink(mysqld_unix_port));
+ (void) unlink(mysqld_unix_port);
base_ip_sock= extra_ip_sock= unix_sock= INVALID_SOCKET;
+
DBUG_VOID_RETURN;
#endif
}
@@ -1264,10 +1592,12 @@ void kill_mysql(void)
if (!kill_in_progress)
{
pthread_t tmp;
+ int error;
abort_loop=1;
- if (pthread_create(&tmp,&connection_attrib, kill_server_thread,
- (void*) 0))
- sql_print_error("Can't create thread to kill server");
+ 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);
}
#endif
DBUG_VOID_RETURN;
@@ -1284,10 +1614,7 @@ void kill_mysql(void)
or stop, we just want to kill the server.
*/
-#if defined(__NETWARE__)
-extern "C" void kill_server(int sig_ptr)
-#define RETURN_FROM_KILL_SERVER return
-#elif !defined(__WIN__)
+#if !defined(__WIN__)
static void *kill_server(void *sig_ptr)
#define RETURN_FROM_KILL_SERVER return 0
#else
@@ -1309,14 +1636,14 @@ static void __cdecl kill_server(int sig_ptr)
if (sig != 0) // 0 is not a valid signal number
my_sigset(sig, SIG_IGN); /* purify inspected */
if (sig == MYSQL_KILL_SIGNAL || sig == 0)
- sql_print_information(ER(ER_NORMAL_SHUTDOWN),my_progname);
+ sql_print_information(ER_DEFAULT(ER_NORMAL_SHUTDOWN),my_progname);
else
- sql_print_error(ER(ER_GOT_SIGNAL),my_progname,sig); /* purecov: inspected */
+ sql_print_error(ER_DEFAULT(ER_GOT_SIGNAL),my_progname,sig); /* purecov: inspected */
-#if defined(HAVE_SMEM) && defined(__WIN__)
+#ifdef HAVE_SMEM
/*
- Send event to smem_event_connect_request for aborting
- */
+ Send event to smem_event_connect_request for aborting
+ */
if (opt_enable_shared_memory)
{
if (!SetEvent(smem_event_connect_request))
@@ -1336,11 +1663,6 @@ static void __cdecl kill_server(int sig_ptr)
unireg_end();
/* purecov: begin deadcode */
-#ifdef __NETWARE__
- if (!event_flag)
- pthread_join(select_thread, NULL); // wait for main thread
-#endif /* __NETWARE__ */
-
DBUG_LEAVE; // Must match DBUG_ENTER()
my_thread_end();
pthread_exit(0);
@@ -1357,7 +1679,7 @@ static void __cdecl kill_server(int sig_ptr)
}
-#if defined(USE_ONE_SIGNAL_HAND) || (defined(__NETWARE__) && defined(SIGNALS_DONT_BREAK_READ))
+#if defined(USE_ONE_SIGNAL_HAND)
pthread_handler_t kill_server_thread(void *arg __attribute__((unused)))
{
my_thread_init(); // Initialize new thread
@@ -1378,7 +1700,7 @@ extern "C" sig_handler print_signal_warning(int sig)
#ifdef SIGNAL_HANDLER_RESET_ON_DELIVERY
my_sigset(sig,print_signal_warning); /* int. thread system calls */
#endif
-#if !defined(__WIN__) && !defined(__NETWARE__)
+#if !defined(__WIN__)
if (sig == SIGALRM)
alarm(2); /* reschedule alarm */
#endif
@@ -1386,6 +1708,18 @@ extern "C" sig_handler print_signal_warning(int sig)
#ifndef EMBEDDED_LIBRARY
+static void init_error_log_mutex()
+{
+ mysql_mutex_init(key_LOCK_error_log, &LOCK_error_log, MY_MUTEX_INIT_FAST);
+}
+
+
+static void clean_up_error_log_mutex()
+{
+ mysql_mutex_destroy(&LOCK_error_log);
+}
+
+
/**
cleanup all memory and end program nicely.
@@ -1400,13 +1734,14 @@ void unireg_end(void)
{
clean_up(1);
my_thread_end();
-#if defined(SIGNALS_DONT_BREAK_READ) && !defined(__NETWARE__)
+#if defined(SIGNALS_DONT_BREAK_READ)
exit(0);
#else
pthread_exit(0); // Exit is in main thread
#endif
}
+
extern "C" void unireg_abort(int exit_code)
{
DBUG_ENTER("unireg_abort");
@@ -1415,16 +1750,30 @@ extern "C" void unireg_abort(int exit_code)
usage();
if (exit_code)
sql_print_error("Aborting\n");
- clean_up(!opt_help && (exit_code || !opt_bootstrap)); /* purecov: inspected */
+ clean_up(!opt_abort && (exit_code || !opt_bootstrap)); /* purecov: inspected */
DBUG_PRINT("quit",("done with cleanup in unireg_abort"));
+ mysqld_exit(exit_code);
+}
+
+static void mysqld_exit(int exit_code)
+{
+ /*
+ Important note: we wait for the signal thread to end,
+ but if a kill -15 signal was sent, the signal thread did
+ spawn the kill_server_thread thread, which is running concurrently.
+ */
wait_for_signal_thread_to_end();
+ mysql_audit_finalize();
clean_up_mutexes();
- my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
+ clean_up_error_log_mutex();
+ my_end((opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0));
+#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
+ shutdown_performance_schema(); // we do it as late as possible
+#endif
exit(exit_code); /* purecov: inspected */
}
-#endif /*EMBEDDED_LIBRARY*/
-
+#endif /* !EMBEDDED_LIBRARY */
void clean_up(bool print_message)
{
@@ -1432,6 +1781,7 @@ void clean_up(bool print_message)
if (cleanup_done++)
return; /* purecov: inspected */
+ close_active_mi();
stop_handle_manager();
release_ddl_log();
@@ -1451,7 +1801,7 @@ void clean_up(bool print_message)
bitmap_free(&slave_error_mask);
#endif
my_tz_free();
- my_database_names_free();
+ my_dboptions_cache_free();
ignore_db_dirs_free();
#ifndef NO_EMBEDDED_ACCESS_CHECKS
servers_free(1);
@@ -1459,91 +1809,80 @@ void clean_up(bool print_message)
grant_free();
#endif
query_cache_destroy();
- table_cache_free();
- table_def_free();
hostname_cache_free();
item_user_lock_free();
lex_free(); /* Free some memory */
item_create_cleanup();
- set_var_free();
if (!opt_noacl)
{
#ifdef HAVE_DLOPEN
udf_free();
#endif
}
+ table_def_start_shutdown();
plugin_shutdown();
ha_end();
if (tc_log)
tc_log->close();
- TC_destroy();
+ delegates_destroy();
xid_cache_free();
+ table_def_free();
+ mdl_destroy();
+ key_caches.delete_elements((void (*)(const char*, uchar*)) free_key_cache);
wt_end();
- delete_elements(&key_caches, (void (*)(const char*, uchar*)) free_key_cache);
multi_keycache_free();
sp_cache_end();
free_status_vars();
end_thr_alarm(1); /* Free allocated memory */
my_free_open_file_info();
- my_free((char*) global_system_variables.date_format,
- MYF(MY_ALLOW_ZERO_PTR));
- my_free((char*) global_system_variables.time_format,
- MYF(MY_ALLOW_ZERO_PTR));
- my_free((char*) global_system_variables.datetime_format,
- MYF(MY_ALLOW_ZERO_PTR));
if (defaults_argv)
free_defaults(defaults_argv);
- my_free(sys_init_connect.value, MYF(MY_ALLOW_ZERO_PTR));
- my_free(sys_init_slave.value, MYF(MY_ALLOW_ZERO_PTR));
- my_free(sys_var_general_log_path.value, MYF(MY_ALLOW_ZERO_PTR));
- my_free(sys_var_slow_log_path.value, MYF(MY_ALLOW_ZERO_PTR));
free_tmpdir(&mysql_tmpdir_list);
-#ifdef HAVE_REPLICATION
- my_free(slave_load_tmpdir,MYF(MY_ALLOW_ZERO_PTR));
-#endif
- x_free(opt_secure_file_priv);
bitmap_free(&temp_pool);
free_max_user_conn();
free_global_user_stats();
free_global_client_stats();
free_global_table_stats();
free_global_index_stats();
+ delete_dynamic(&all_options);
#ifdef HAVE_REPLICATION
end_slave_list();
#endif
my_uuid_end();
delete binlog_filter;
delete rpl_filter;
-#ifndef EMBEDDED_LIBRARY
end_ssl();
-#endif
+#ifndef EMBEDDED_LIBRARY
vio_end();
-#ifdef USE_REGEX
+#endif /*!EMBEDDED_LIBRARY*/
my_regex_end();
-#endif
- free_charsets();
#if defined(ENABLED_DEBUG_SYNC)
/* End the debug sync facility. See debug_sync.cc. */
debug_sync_end();
#endif /* defined(ENABLED_DEBUG_SYNC) */
delete_pid_file(MYF(0));
- if (print_message && errmesg && server_start_time)
- sql_print_information(ER(ER_SHUTDOWN_COMPLETE),my_progname);
- thread_scheduler.end();
+
+ if (print_message && my_default_lc_messages && server_start_time)
+ sql_print_information(ER_DEFAULT(ER_SHUTDOWN_COMPLETE),my_progname);
+ cleanup_errmsgs();
+ MYSQL_CALLBACK(thread_scheduler, end, ());
mysql_library_end();
finish_client_errs();
- my_free((uchar*) my_error_unregister(ER_ERROR_FIRST, ER_ERROR_LAST),
- MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
+ (void) my_error_unregister(ER_ERROR_FIRST, ER_ERROR_LAST); // finish server errs
DBUG_PRINT("quit", ("Error messages freed"));
/* Tell main we are ready */
logger.cleanup_end();
- (void) pthread_mutex_lock(&LOCK_thread_count);
+ sys_var_end();
+ my_atomic_rwlock_destroy(&global_query_id_lock);
+ my_atomic_rwlock_destroy(&thread_running_lock);
+ free_charsets();
+ mysql_mutex_lock(&LOCK_thread_count);
DBUG_PRINT("quit", ("got thread count lock"));
ready_to_exit=1;
/* do the broadcast inside the lock to ensure that my_end() is not called */
- (void) pthread_cond_broadcast(&COND_thread_count);
- (void) pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_cond_broadcast(&COND_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
/*
The following lines may never be executed as the main thread may have
@@ -1561,7 +1900,6 @@ void clean_up(bool print_message)
*/
static void wait_for_signal_thread_to_end()
{
-#ifndef __NETWARE__
uint i;
/*
Wait up to 10 seconds for signal thread to die. We use this mainly to
@@ -1569,100 +1907,60 @@ static void wait_for_signal_thread_to_end()
*/
for (i= 0 ; i < 100 && signal_thread_in_use; i++)
{
- if (pthread_kill(signal_thread, MYSQL_KILL_SIGNAL) != ESRCH)
+ if (pthread_kill(signal_thread, MYSQL_KILL_SIGNAL) == ESRCH)
break;
my_sleep(100); // Give it time to die
}
-#endif
}
-
#endif /*EMBEDDED_LIBRARY*/
static void clean_up_mutexes()
{
DBUG_ENTER("clean_up_mutexes");
- (void) pthread_mutex_destroy(&LOCK_mysql_create_db);
- (void) pthread_mutex_destroy(&LOCK_lock_db);
- (void) pthread_mutex_destroy(&LOCK_Acl);
- (void) rwlock_destroy(&LOCK_grant);
- (void) pthread_mutex_destroy(&LOCK_open);
- (void) pthread_mutex_destroy(&LOCK_thread_count);
- (void) pthread_mutex_destroy(&LOCK_mapped_file);
- (void) pthread_mutex_destroy(&LOCK_status);
- (void) pthread_mutex_destroy(&LOCK_error_log);
- (void) pthread_mutex_destroy(&LOCK_delayed_insert);
- (void) pthread_mutex_destroy(&LOCK_delayed_status);
- (void) pthread_mutex_destroy(&LOCK_delayed_create);
- (void) pthread_mutex_destroy(&LOCK_crypt);
- (void) pthread_mutex_destroy(&LOCK_bytes_sent);
- (void) pthread_mutex_destroy(&LOCK_bytes_received);
- (void) pthread_mutex_destroy(&LOCK_user_conn);
- (void) pthread_mutex_destroy(&LOCK_connection_count);
- (void) pthread_mutex_destroy(&LOCK_stats);
- (void) pthread_mutex_destroy(&LOCK_global_user_client_stats);
- (void) pthread_mutex_destroy(&LOCK_global_table_stats);
- (void) pthread_mutex_destroy(&LOCK_global_index_stats);
-
-#ifndef EMBEDDED_LIBRARY
- Events::destroy_mutexes();
-#endif /* !EMBEDDED_LIBRARY */
+ mysql_rwlock_destroy(&LOCK_grant);
+ mysql_mutex_destroy(&LOCK_thread_count);
+ mysql_mutex_destroy(&LOCK_status);
+ mysql_mutex_destroy(&LOCK_delayed_insert);
+ mysql_mutex_destroy(&LOCK_delayed_status);
+ mysql_mutex_destroy(&LOCK_delayed_create);
+ mysql_mutex_destroy(&LOCK_crypt);
+ mysql_mutex_destroy(&LOCK_user_conn);
+ mysql_mutex_destroy(&LOCK_connection_count);
+ mysql_mutex_destroy(&LOCK_stats);
+ mysql_mutex_destroy(&LOCK_global_user_client_stats);
+ mysql_mutex_destroy(&LOCK_global_table_stats);
+ mysql_mutex_destroy(&LOCK_global_index_stats);
#ifdef HAVE_OPENSSL
- (void) pthread_mutex_destroy(&LOCK_des_key_file);
+ mysql_mutex_destroy(&LOCK_des_key_file);
#ifndef HAVE_YASSL
for (int i= 0; i < CRYPTO_num_locks(); ++i)
- (void) rwlock_destroy(&openssl_stdlocks[i].lock);
+ mysql_rwlock_destroy(&openssl_stdlocks[i].lock);
OPENSSL_free(openssl_stdlocks);
#endif /* HAVE_YASSL */
#endif /* HAVE_OPENSSL */
#ifdef HAVE_REPLICATION
- (void) pthread_mutex_destroy(&LOCK_rpl_status);
- (void) pthread_cond_destroy(&COND_rpl_status);
+ mysql_mutex_destroy(&LOCK_rpl_status);
+ mysql_cond_destroy(&COND_rpl_status);
#endif /* HAVE_REPLICATION */
- (void) pthread_mutex_destroy(&LOCK_server_started);
- (void) pthread_cond_destroy(&COND_server_started);
- (void) pthread_mutex_destroy(&LOCK_active_mi);
- (void) rwlock_destroy(&LOCK_sys_init_connect);
- (void) rwlock_destroy(&LOCK_sys_init_slave);
- (void) pthread_mutex_destroy(&LOCK_global_system_variables);
- (void) pthread_mutex_destroy(&LOCK_short_uuid_generator);
- (void) rwlock_destroy(&LOCK_system_variables_hash);
- (void) pthread_mutex_destroy(&LOCK_global_read_lock);
- (void) pthread_mutex_destroy(&LOCK_prepared_stmt_count);
- (void) pthread_cond_destroy(&COND_thread_count);
- (void) pthread_cond_destroy(&COND_refresh);
- (void) pthread_cond_destroy(&COND_global_read_lock);
- (void) pthread_cond_destroy(&COND_thread_cache);
- (void) pthread_cond_destroy(&COND_flush_thread_cache);
+ mysql_mutex_destroy(&LOCK_active_mi);
+ mysql_rwlock_destroy(&LOCK_sys_init_connect);
+ mysql_rwlock_destroy(&LOCK_sys_init_slave);
+ mysql_mutex_destroy(&LOCK_global_system_variables);
+ mysql_rwlock_destroy(&LOCK_system_variables_hash);
+ mysql_mutex_destroy(&LOCK_short_uuid_generator);
+ mysql_mutex_destroy(&LOCK_prepared_stmt_count);
+ mysql_mutex_destroy(&LOCK_error_messages);
+ mysql_cond_destroy(&COND_thread_count);
+ mysql_cond_destroy(&COND_thread_cache);
+ mysql_cond_destroy(&COND_flush_thread_cache);
+ mysql_mutex_destroy(&LOCK_server_started);
+ mysql_cond_destroy(&COND_server_started);
+ mysql_mutex_destroy(&LOCK_prepare_ordered);
+ mysql_mutex_destroy(&LOCK_commit_ordered);
DBUG_VOID_RETURN;
}
-/**
- Register order of mutex for wrong mutex deadlock detector
-
- By aquiring all mutex in order here, the mutex order detector in
- mysys/thr_mutex.c, will give a warning on first wrong mutex usage!
-*/
-
-#ifdef SAFE_MUTEX
-#define always_in_that_order(A,B) \
- pthread_mutex_lock(A); pthread_mutex_lock(B); \
- pthread_mutex_unlock(B); pthread_mutex_unlock(A)
-#else
-#define always_in_that_order(A,B)
-#endif
-
-static void register_mutex_order()
-{
- /*
- We must have LOCK_open before LOCK_global_system_variables because
- LOCK_open is hold while sql_plugin.c::intern_sys_var_ptr() is called.
- */
- always_in_that_order(&LOCK_open, &LOCK_global_system_variables);
-}
-#undef always_in_that_order
-
-
/****************************************************************************
** Init IP and UNIX socket
****************************************************************************/
@@ -1709,7 +2007,7 @@ static void set_ports()
static struct passwd *check_user(const char *user)
{
-#if !defined(__WIN__) && !defined(__NETWARE__)
+#if !defined(__WIN__)
struct passwd *tmp_user_info;
uid_t user_id= geteuid();
@@ -1731,7 +2029,7 @@ static struct passwd *check_user(const char *user)
}
if (!user)
{
- if (!opt_bootstrap)
+ if (!opt_bootstrap && !opt_help)
{
sql_print_error("Fatal error: Please read \"Security\" section of the manual to find out how to run mysqld as root!\n");
unireg_abort(1);
@@ -1752,13 +2050,19 @@ static struct passwd *check_user(const char *user)
if (!(tmp_user_info= getpwuid(atoi(user))))
goto err;
}
+
return tmp_user_info;
/* purecov: end */
err:
sql_print_error("Fatal error: Can't change to run as user '%s' ; Please check that the user exists!\n",user);
unireg_abort(1);
+#endif
+ return NULL;
+}
+static inline void allow_coredumps()
+{
#ifdef PR_SET_DUMPABLE
if (test_flags & TEST_CORE_ON_SIGNAL)
{
@@ -1766,15 +2070,13 @@ err:
(void) prctl(PR_SET_DUMPABLE, 1);
}
#endif
-
-#endif
- return NULL;
}
+
static void set_user(const char *user, struct passwd *user_info_arg)
{
/* purecov: begin tested */
-#if !defined(__WIN__) && !defined(__NETWARE__)
+#if !defined(__WIN__)
DBUG_ASSERT(user_info_arg != 0);
#ifdef HAVE_INITGROUPS
/*
@@ -1797,6 +2099,7 @@ static void set_user(const char *user, struct passwd *user_info_arg)
sql_perror("setuid");
unireg_abort(1);
}
+ allow_coredumps();
#endif
/* purecov: end */
}
@@ -1804,7 +2107,7 @@ static void set_user(const char *user, struct passwd *user_info_arg)
static void set_effective_user(struct passwd *user_info_arg)
{
-#if !defined(__WIN__) && !defined(__NETWARE__)
+#if !defined(__WIN__)
DBUG_ASSERT(user_info_arg != 0);
if (setregid((gid_t)-1, user_info_arg->pw_gid) == -1)
{
@@ -1816,6 +2119,7 @@ static void set_effective_user(struct passwd *user_info_arg)
sql_perror("setreuid");
unireg_abort(1);
}
+ allow_coredumps();
#endif
}
@@ -1823,7 +2127,7 @@ static void set_effective_user(struct passwd *user_info_arg)
/** Change root user if started with @c --chroot . */
static void set_root(const char *path)
{
-#if !defined(__WIN__) && !defined(__NETWARE__)
+#if !defined(__WIN__)
if (chroot(path) == -1)
{
sql_perror("chroot");
@@ -1839,36 +2143,89 @@ static void set_root(const char *path)
static my_socket activate_tcp_port(uint port)
{
- struct sockaddr_in IPaddr;
- my_socket ip_sock;
- int arg=1;
- int ret;
- uint waited;
- uint this_wait;
- uint retry;
+ struct addrinfo *ai, *a;
+ struct addrinfo hints;
+ int error;
+ int arg;
+ char port_buf[NI_MAXSERV];
+ my_socket ip_sock= INVALID_SOCKET;
DBUG_ENTER("activate_tcp_port");
- DBUG_PRINT("enter",("port: %u", port));
- LINT_INIT(ret);
+ DBUG_PRINT("general",("IP Socket is %d",port));
+
+ bzero(&hints, sizeof (hints));
+ hints.ai_flags= AI_PASSIVE;
+ hints.ai_socktype= SOCK_STREAM;
+ hints.ai_family= AF_UNSPEC;
+
+ my_snprintf(port_buf, NI_MAXSERV, "%d", port);
+ error= getaddrinfo(my_bind_addr_str, port_buf, &hints, &ai);
+ if (error != 0)
+ {
+ DBUG_PRINT("error",("Got error: %d from getaddrinfo()", error));
+ sql_perror(ER_DEFAULT(ER_IPSOCK_ERROR)); /* purecov: tested */
+ unireg_abort(1); /* purecov: tested */
+ }
+
+ for (a= ai; a != NULL; a= a->ai_next)
+ {
+ ip_sock= socket(a->ai_family, a->ai_socktype, a->ai_protocol);
+
+ char ip_addr[INET6_ADDRSTRLEN];
+
+ if (vio_get_normalized_ip_string(a->ai_addr, a->ai_addrlen,
+ ip_addr, sizeof (ip_addr)))
+ {
+ ip_addr[0]= 0;
+ }
+
+ if (ip_sock == INVALID_SOCKET)
+ {
+ sql_print_error("Failed to create a socket for %s '%s': errno: %d.",
+ (a->ai_family == AF_INET) ? "IPv4" : "IPv6",
+ (const char *) ip_addr,
+ (int) socket_errno);
+ }
+ else
+ {
+ sql_print_information("Server socket created on IP: '%s'.",
+ (const char *) ip_addr);
+ break;
+ }
+ }
- ip_sock = socket(AF_INET, SOCK_STREAM, 0);
if (ip_sock == INVALID_SOCKET)
{
DBUG_PRINT("error",("Got error: %d from socket()",socket_errno));
- sql_perror(ER(ER_IPSOCK_ERROR)); /* purecov: tested */
+ sql_perror(ER_DEFAULT(ER_IPSOCK_ERROR)); /* purecov: tested */
unireg_abort(1); /* purecov: tested */
}
- bzero((char*) &IPaddr, sizeof(IPaddr));
- IPaddr.sin_family = AF_INET;
- IPaddr.sin_addr.s_addr = my_bind_addr;
- IPaddr.sin_port = (unsigned short) htons((unsigned short) port);
#ifndef __WIN__
/*
We should not use SO_REUSEADDR on windows as this would enable a
user to open two mysqld servers with the same TCP/IP port.
*/
+ arg= 1;
(void) setsockopt(ip_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&arg,sizeof(arg));
#endif /* __WIN__ */
+
+#ifdef IPV6_V6ONLY
+ /*
+ For interoperability with older clients, IPv6 socket should
+ listen on both IPv6 and IPv4 wildcard addresses.
+ Turn off IPV6_V6ONLY option.
+
+ NOTE: this will work starting from Windows Vista only.
+ On Windows XP dual stack is not available, so it will not
+ listen on the corresponding IPv4-address.
+ */
+ if (a->ai_family == AF_INET6)
+ {
+ arg= 0;
+ (void) setsockopt(ip_sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg,
+ sizeof(arg));
+ }
+#endif
/*
Sometimes the port is not released fast enough when stopping and
restarting the server. This happens quite often with the test suite
@@ -1877,11 +2234,11 @@ static my_socket activate_tcp_port(uint port)
Retry at second: 1, 3, 7, 13, 22, 35, 52, 74, ...
Limit the sequence by mysqld_port_timeout (set --port-open-timeout=#).
*/
-
+ int ret;
+ uint waited, retry, this_wait;
for (waited= 0, retry= 1; ; retry++, waited+= this_wait)
{
- if (((ret= bind(ip_sock, my_reinterpret_cast(struct sockaddr *) (&IPaddr),
- sizeof(IPaddr))) >= 0) ||
+ if (((ret= bind(ip_sock, a->ai_addr, a->ai_addrlen)) >= 0 ) ||
(socket_errno != SOCKET_EADDRINUSE) ||
(waited >= mysqld_port_timeout))
break;
@@ -1889,6 +2246,7 @@ static my_socket activate_tcp_port(uint port)
this_wait= retry * retry / 3 + 1;
sleep(this_wait);
}
+ freeaddrinfo(ai);
if (ret < 0)
{
char buff[100];
@@ -1909,20 +2267,27 @@ static my_socket activate_tcp_port(uint port)
DBUG_RETURN(ip_sock);
}
-
static void network_init(void)
{
#ifdef HAVE_SYS_UN_H
struct sockaddr_un UNIXaddr;
+ int arg;
#endif
- int arg=1;
DBUG_ENTER("network_init");
- if (thread_scheduler.init())
+ if (MYSQL_CALLBACK_ELSE(thread_scheduler, init, (), 0))
unireg_abort(1); /* purecov: inspected */
set_ports();
+ if (report_port == 0)
+ {
+ report_port= mysqld_port;
+ }
+#ifndef DBUG_OFF
+ if (!opt_disable_networking)
+ DBUG_ASSERT(report_port != 0);
+#endif
if (!opt_disable_networking && !opt_bootstrap)
{
if (mysqld_port)
@@ -1931,7 +2296,7 @@ static void network_init(void)
extra_ip_sock= activate_tcp_port(mysqld_extra_port);
}
-#ifdef __NT__
+#ifdef _WIN32
/* create named pipe */
if (Service.IsNT() && mysqld_unix_port[0] && !opt_bootstrap &&
opt_enable_named_pipe)
@@ -2002,10 +2367,11 @@ static void network_init(void)
UNIXaddr.sun_family = AF_UNIX;
strmov(UNIXaddr.sun_path, mysqld_unix_port);
(void) unlink(mysqld_unix_port);
+ arg= 1;
(void) setsockopt(unix_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&arg,
sizeof(arg));
umask(0);
- if (bind(unix_sock, my_reinterpret_cast(struct sockaddr *) (&UNIXaddr),
+ if (bind(unix_sock, reinterpret_cast<struct sockaddr *>(&UNIXaddr),
sizeof(UNIXaddr)) < 0)
{
sql_perror("Can't start server : Bind on unix socket"); /* purecov: tested */
@@ -2025,50 +2391,35 @@ static void network_init(void)
DBUG_VOID_RETURN;
}
-#endif /*!EMBEDDED_LIBRARY*/
-
-#ifndef EMBEDDED_LIBRARY
/**
Close a connection.
- @param thd Thread handle
- @param errcode Error code to print to console
- @param lock 1 if we have have to lock LOCK_thread_count
+ @param thd Thread handle.
+ @param sql_errno The error code to send before disconnect.
@note
For the connection that is doing shutdown, this is called twice
*/
-void close_connection(THD *thd, uint errcode, bool lock)
+void close_connection(THD *thd, uint sql_errno)
{
- st_vio *vio;
DBUG_ENTER("close_connection");
- DBUG_PRINT("enter",("fd: %s error: '%s'",
- thd->net.vio ? vio_description(thd->net.vio) :
- "(not connected)",
- errcode ? ER(errcode) : ""));
- if (lock)
- (void) pthread_mutex_lock(&LOCK_thread_count);
- thd->killed= KILL_CONNECTION;
- if (global_system_variables.log_warnings > 3)
- {
- Security_context *sctx= &thd->main_security_ctx;
- sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
- thd->thread_id,(thd->db ? thd->db : "unconnected"),
- sctx->user ? sctx->user : "unauthenticated",
- sctx->host_or_ip,
- (errcode ? ER(errcode) : "CLOSE_CONNECTION"));
- }
+ if (sql_errno)
+ net_send_error(thd, sql_errno, ER_DEFAULT(sql_errno), NULL);
+
+ thd->print_aborted_warning(3, sql_errno ? ER_DEFAULT(sql_errno)
+ : "CLOSE_CONNECTION");
+
+ thd->disconnect();
- if ((vio= thd->net.vio) != 0)
+ MYSQL_CONNECTION_DONE((int) sql_errno, thd->thread_id);
+
+ if (MYSQL_CONNECTION_DONE_ENABLED())
{
- if (errcode)
- net_send_error(thd, errcode, ER(errcode)); /* purecov: inspected */
- vio_close(vio); /* vio is freed in delete thd */
+ sleep(0); /* Workaround to avoid tailcall optimisation */
}
- if (lock)
- (void) pthread_mutex_unlock(&LOCK_thread_count);
+ MYSQL_AUDIT_NOTIFY_CONNECTION_DISCONNECT(thd, sql_errno);
DBUG_VOID_RETURN;
}
#endif /* EMBEDDED_LIBRARY */
@@ -2081,12 +2432,40 @@ extern "C" sig_handler end_mysqld_signal(int sig __attribute__((unused)))
DBUG_ENTER("end_mysqld_signal");
/* Don't call kill_mysql() if signal thread is not running */
if (signal_thread_in_use)
- kill_mysql(); // Take down mysqld nicely
+ kill_mysql(); // Take down mysqld nicely
DBUG_VOID_RETURN; /* purecov: deadcode */
}
/*
+ Cleanup THD object
+
+ SYNOPSIS
+ thd_cleanup()
+ thd Thread handler
+*/
+
+void thd_cleanup(THD *thd)
+{
+ thd->cleanup();
+}
+
+/*
+ Decrease number of connections
+
+ SYNOPSIS
+ dec_connection_count()
+*/
+
+void dec_connection_count(THD *thd)
+{
+ mysql_mutex_lock(&LOCK_connection_count);
+ (*thd->scheduler->connection_count)--;
+ mysql_mutex_unlock(&LOCK_connection_count);
+}
+
+
+/*
Unlink thd from global list of available connections and free thd
SYNOPSIS
@@ -2101,20 +2480,23 @@ void unlink_thd(THD *thd)
{
DBUG_ENTER("unlink_thd");
DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
- thd->cleanup();
- pthread_mutex_lock(&LOCK_connection_count);
- (*thd->scheduler->connection_count)--;
- pthread_mutex_unlock(&LOCK_connection_count);
+ thd_cleanup(thd);
+ dec_connection_count(thd);
- (void) pthread_mutex_lock(&LOCK_thread_count);
+ thd->add_status_to_global();
+
+ mysql_mutex_lock(&LOCK_thread_count);
+ thread_count--;
+ thd->unlink();
/*
Used by binlog_reset_master. It would be cleaner to use
DEBUG_SYNC here, but that's not possible because the THD's debug
sync feature has been shut down at this point.
*/
DBUG_EXECUTE_IF("sleep_after_lock_thread_count_before_delete_thd", sleep(5););
- thread_count--;
+ mysql_mutex_unlock(&LOCK_thread_count);
+
delete thd;
DBUG_VOID_RETURN;
}
@@ -2138,18 +2520,28 @@ void unlink_thd(THD *thd)
static bool cache_thread()
{
- safe_mutex_assert_owner(&LOCK_thread_count);
+ mysql_mutex_assert_owner(&LOCK_thread_count);
if (cached_thread_count < thread_cache_size &&
! abort_loop && !kill_cached_threads)
{
/* Don't kill the thread, just put it in cache for reuse */
DBUG_PRINT("info", ("Adding thread to cache"));
cached_thread_count++;
+
+#ifdef HAVE_PSI_INTERFACE
+ /*
+ Delete the instrumentation for the job that just completed,
+ before parking this pthread in the cache (blocked on COND_thread_cache).
+ */
+ if (likely(PSI_server != NULL))
+ PSI_server->delete_current_thread();
+#endif
+
while (!abort_loop && ! wake_thread && ! kill_cached_threads)
- (void) pthread_cond_wait(&COND_thread_cache, &LOCK_thread_count);
+ mysql_cond_wait(&COND_thread_cache, &LOCK_thread_count);
cached_thread_count--;
if (kill_cached_threads)
- pthread_cond_signal(&COND_flush_thread_cache);
+ mysql_cond_signal(&COND_flush_thread_cache);
if (wake_thread)
{
THD *thd;
@@ -2157,6 +2549,21 @@ static bool cache_thread()
thd= thread_cache.get();
thd->thread_stack= (char*) &thd; // For store_globals
(void) thd->store_globals();
+
+#ifdef HAVE_PSI_INTERFACE
+ /*
+ Create new instrumentation for the new THD job,
+ and attach it to this running pthread.
+ */
+ if (likely(PSI_server != NULL))
+ {
+ PSI_thread *psi= PSI_server->new_thread(key_thread_one_connection,
+ thd, thd->thread_id);
+ if (likely(psi != NULL))
+ PSI_server->set_thread(psi);
+ }
+#endif
+
/*
THD::mysys_var::abort is associated with physical thread rather
than with THD object. So we need to reset this flag before using
@@ -2199,10 +2606,13 @@ bool one_thread_per_connection_end(THD *thd, bool put_in_cache)
/* Mark that current_thd is not valid anymore */
my_pthread_setspecific_ptr(THR_THD, 0);
if (put_in_cache)
+ {
+ mysql_mutex_lock(&LOCK_thread_count);
put_in_cache= cache_thread();
- pthread_mutex_unlock(&LOCK_thread_count);
- if (put_in_cache)
- DBUG_RETURN(0); // Thread is reused
+ mysql_mutex_unlock(&LOCK_thread_count);
+ if (put_in_cache)
+ DBUG_RETURN(0); // Thread is reused
+ }
/* It's safe to broadcast outside a lock (COND... is not deleted here) */
DBUG_PRINT("signal", ("Broadcasting COND_thread_count"));
@@ -2211,7 +2621,7 @@ bool one_thread_per_connection_end(THD *thd, bool put_in_cache)
ERR_remove_state(0);
#endif
my_thread_end();
- (void) pthread_cond_broadcast(&COND_thread_count);
+ mysql_cond_broadcast(&COND_thread_count);
pthread_exit(0);
return 0; // Avoid compiler warnings
@@ -2220,15 +2630,15 @@ bool one_thread_per_connection_end(THD *thd, bool put_in_cache)
void flush_thread_cache()
{
- (void) pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
kill_cached_threads++;
while (cached_thread_count)
{
- pthread_cond_broadcast(&COND_thread_cache);
- pthread_cond_wait(&COND_flush_thread_cache,&LOCK_thread_count);
+ mysql_cond_broadcast(&COND_thread_cache);
+ mysql_cond_wait(&COND_flush_thread_cache, &LOCK_thread_count);
}
kill_cached_threads--;
- (void) pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
}
@@ -2275,29 +2685,7 @@ static BOOL WINAPI console_event_handler( DWORD type )
}
-/*
- In Visual Studio 2005 and later, default SIGABRT handler will overwrite
- any unhandled exception filter set by the application and will try to
- call JIT debugger. This is not what we want, this we calling __debugbreak
- to stop in debugger, if process is being debugged or to generate
- EXCEPTION_BREAKPOINT and then handle_segfault will do its magic.
-*/
-
-#if (_MSC_VER >= 1400)
-static void my_sigabrt_handler(int sig)
-{
- __debugbreak();
-}
-#endif /*_MSC_VER >=1400 */
-void win_install_sigabrt_handler(void)
-{
-#if (_MSC_VER >=1400)
- /*abort() should not override our exception filter*/
- _set_abort_behavior(0,_CALL_REPORTFAULT);
- signal(SIGABRT,my_sigabrt_handler);
-#endif /* _MSC_VER >=1400 */
-}
#ifdef DEBUG_UNHANDLED_EXCEPTION_FILTER
#define DEBUGGER_ATTACH_TIMEOUT 120
@@ -2376,7 +2764,6 @@ LONG WINAPI my_unhandler_exception_filter(EXCEPTION_POINTERS *ex_pointers)
static void init_signals(void)
{
- win_install_sigabrt_handler();
if(opt_console)
SetConsoleCtrlHandler(console_event_handler,TRUE);
@@ -2414,261 +2801,95 @@ static void start_signal_handler(void)
static void check_data_home(const char *path)
{}
+#endif /* __WIN__ */
-#elif defined(__NETWARE__)
-/// down server event callback.
-void mysql_down_server_cb(void *, void *)
+#if BACKTRACE_DEMANGLE
+#include <cxxabi.h>
+extern "C" char *my_demangle(const char *mangled_name, int *status)
{
- event_flag= TRUE;
- kill_server(0);
+ return abi::__cxa_demangle(mangled_name, NULL, NULL, status);
}
+#endif
-/// destroy callback resources.
-void mysql_cb_destroy(void *)
-{
- UnRegisterEventNotification(eh); // cleanup down event notification
- NX_UNWRAP_INTERFACE(ref);
- /* Deregister NSS volume deactivation event */
- NX_UNWRAP_INTERFACE(refneb);
- if (neb_consumer_id)
- UnRegisterConsumer(neb_consumer_id, NULL);
-}
+/*
+ pthread_attr_setstacksize() without so much platform-dependency
+ Return: The actual stack size if possible.
+*/
-/// initialize callbacks.
-void mysql_cb_init()
+#ifndef EMBEDDED_LIBRARY
+static size_t my_setstacksize(pthread_attr_t *attr, size_t stacksize)
{
- // register for down server event
- void *handle = getnlmhandle();
- rtag_t rt= AllocateResourceTag(handle, "MySQL Down Server Callback",
- EventSignature);
- NX_WRAP_INTERFACE((void *)mysql_down_server_cb, 2, (void **)&ref);
- eh= RegisterForEventNotification(rt, EVENT_PRE_DOWN_SERVER,
- EVENT_PRIORITY_APPLICATION,
- NULL, ref, NULL);
-
- /*
- Register for volume deactivation event
- Wrap the callback function, as it is called by non-LibC thread
- */
- (void *) NX_WRAP_INTERFACE(neb_event_callback, 1, &refneb);
- registerwithneb();
-
- NXVmRegisterExitHandler(mysql_cb_destroy, NULL); // clean-up
-}
-
+ size_t guard_size __attribute__((unused))= 0;
-/** To get the name of the NetWare volume having MySQL data folder. */
-static void getvolumename()
-{
- char *p;
+#if defined(__ia64__) || defined(__ia64)
/*
- We assume that data path is already set.
- If not it won't come here. Terminate after volume name
+ On IA64, half of the requested stack size is used for "normal stack"
+ and half for "register stack". The space measured by check_stack_overrun
+ is the "normal stack", so double the request to make sure we have the
+ caller-expected amount of normal stack.
+
+ NOTE: there is no guarantee that the register stack can't grow faster
+ than normal stack, so it's very unclear that we won't dump core due to
+ stack overrun despite check_stack_overrun's efforts. Experimentation
+ shows that in the execution_constants test, the register stack grows
+ less than half as fast as normal stack, but perhaps other scenarios are
+ less forgiving. If it turns out that more space is needed for the
+ register stack, that could be forced (rather inefficiently) by using a
+ multiplier higher than 2 here.
*/
- if ((p= strchr(mysql_real_data_home, ':')))
- strmake(datavolname, mysql_real_data_home,
- (uint) (p - mysql_real_data_home));
-}
-
-
-/**
- Registering with NEB for NSS Volume Deactivation event.
-*/
-
-static void registerwithneb()
-{
-
- ConsumerRegistrationInfo reg_info;
-
- /* Clear NEB registration structure */
- bzero((char*) &reg_info, sizeof(struct ConsumerRegistrationInfo));
-
- /* Fill the NEB consumer information structure */
- reg_info.CRIVersion= 1; // NEB version
- /* NEB Consumer name */
- reg_info.CRIConsumerName= (BYTE *) "MySQL Database Server";
- /* Event of interest */
- reg_info.CRIEventName= (BYTE *) "NSS.ChangeVolState.Enter";
- reg_info.CRIUserParameter= NULL; // Consumer Info
- reg_info.CRIEventFlags= 0; // Event flags
- /* Consumer NLM handle */
- reg_info.CRIOwnerID= (LoadDefinitionStructure *)getnlmhandle();
- reg_info.CRIConsumerESR= NULL; // No consumer ESR required
- reg_info.CRISecurityToken= 0; // No security token for the event
- reg_info.CRIConsumerFlags= 0; // SMP_ENABLED_BIT;
- reg_info.CRIFilterName= 0; // No event filtering
- reg_info.CRIFilterDataLength= 0; // No filtering data
- reg_info.CRIFilterData= 0; // No filtering data
- /* Callback function for the event */
- (void *)reg_info.CRIConsumerCallback= (void *) refneb;
- reg_info.CRIOrder= 0; // Event callback order
- reg_info.CRIConsumerType= CHECK_CONSUMER; // Consumer type
-
- /* Register for the event with NEB */
- if (RegisterConsumer(&reg_info))
- {
- consoleprintf("Failed to register for NSS Volume Deactivation event \n");
- return;
- }
- /* This ID is required for deregistration */
- neb_consumer_id= reg_info.CRIConsumerID;
-
- /* Get MySQL data volume name, stored in global variable datavolname */
- getvolumename();
+ stacksize *= 2;
+#endif
/*
- Get the NSS volume ID of the MySQL Data volume.
- Volume ID is stored in a global variable
+ On many machines, the "guard space" is subtracted from the requested
+ stack size, and that space is quite large on some platforms. So add
+ it to our request, if we can find out what it is.
*/
- getvolumeID((BYTE*) datavolname);
-}
-
-
-/**
- Callback for NSS Volume Deactivation event.
-*/
-
-ulong neb_event_callback(struct EventBlock *eblock)
-{
- EventChangeVolStateEnter_s *voldata;
- extern bool nw_panic;
+#ifdef HAVE_PTHREAD_ATTR_GETGUARDSIZE
+ if (pthread_attr_getguardsize(attr, &guard_size))
+ guard_size = 0; /* if can't find it out, treat as 0 */
+#endif
- voldata= (EventChangeVolStateEnter_s *)eblock->EBEventData;
+ pthread_attr_setstacksize(attr, stacksize + guard_size);
- /* Deactivation of a volume */
- if ((voldata->oldState == zVOLSTATE_ACTIVE &&
- voldata->newState == zVOLSTATE_DEACTIVE ||
- voldata->newState == zVOLSTATE_MAINTENANCE))
+ /* Retrieve actual stack size if possible */
+#ifdef HAVE_PTHREAD_ATTR_GETSTACKSIZE
{
- /*
- Ensure that we bring down MySQL server only for MySQL data
- volume deactivation
- */
- if (!memcmp(&voldata->volID, &datavolid, sizeof(VolumeID_t)))
+ size_t real_stack_size= 0;
+ /* We must ignore real_stack_size = 0 as Solaris 2.9 can return 0 here */
+ if (pthread_attr_getstacksize(attr, &real_stack_size) == 0 &&
+ real_stack_size > guard_size)
{
- consoleprintf("MySQL data volume is deactivated, shutting down MySQL Server \n");
- event_flag= TRUE;
- nw_panic = TRUE;
- event_flag= TRUE;
- kill_server(0);
+ real_stack_size -= guard_size;
+ if (real_stack_size < stacksize)
+ {
+ if (global_system_variables.log_warnings)
+ sql_print_warning("Asked for %zu thread stack, but got %zu",
+ stacksize, real_stack_size);
+ stacksize= real_stack_size;
+ }
}
}
- return 0;
-}
-
-
-#define ADMIN_VOL_PATH "_ADMIN:/Volumes/"
-
-/**
- Function to get NSS volume ID of the MySQL data.
-*/
-static void getvolumeID(BYTE *volumeName)
-{
- char path[zMAX_FULL_NAME];
- Key_t rootKey= 0, fileKey= 0;
- QUAD getInfoMask;
- zInfo_s info;
- STATUS status;
-
- /* Get the root key */
- if ((status= zRootKey(0, &rootKey)) != zOK)
- {
- consoleprintf("\nGetNSSVolumeProperties - Failed to get root key, status: %d\n.", (int) status);
- goto exit;
- }
-
- /*
- Get the file key. This is the key to the volume object in the
- NSS admin volumes directory.
- */
-
- strxmov(path, (const char *) ADMIN_VOL_PATH, (const char *) volumeName,
- NullS);
- if ((status= zOpen(rootKey, zNSS_TASK, zNSPACE_LONG|zMODE_UTF8,
- (BYTE *) path, zRR_READ_ACCESS, &fileKey)) != zOK)
- {
- consoleprintf("\nGetNSSVolumeProperties - Failed to get file, status: %d\n.", (int) status);
- goto exit;
- }
-
- getInfoMask= zGET_IDS | zGET_VOLUME_INFO ;
- if ((status= zGetInfo(fileKey, getInfoMask, sizeof(info),
- zINFO_VERSION_A, &info)) != zOK)
- {
- consoleprintf("\nGetNSSVolumeProperties - Failed in zGetInfo, status: %d\n.", (int) status);
- goto exit;
- }
-
- /* Copy the data to global variable */
- datavolid.timeLow= info.vol.volumeID.timeLow;
- datavolid.timeMid= info.vol.volumeID.timeMid;
- datavolid.timeHighAndVersion= info.vol.volumeID.timeHighAndVersion;
- datavolid.clockSeqHighAndReserved= info.vol.volumeID.clockSeqHighAndReserved;
- datavolid.clockSeqLow= info.vol.volumeID.clockSeqLow;
- /* This is guranteed to be 6-byte length (but sizeof() would be better) */
- memcpy(datavolid.node, info.vol.volumeID.node, (unsigned int) 6);
-
-exit:
- if (rootKey)
- zClose(rootKey);
- if (fileKey)
- zClose(fileKey);
-}
-
-
-static void init_signals(void)
-{
- int signals[] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGABRT};
-
- for (uint i=0 ; i < sizeof(signals)/sizeof(int) ; i++)
- signal(signals[i], kill_server);
- mysql_cb_init(); // initialize callbacks
-
-}
-
-
-static void start_signal_handler(void)
-{
- // Save vm id of this process
- if (!opt_bootstrap)
- create_pid_file();
- // no signal handler
-}
-
-
-/**
- Warn if the data is on a Traditional volume.
-
- @note
- Already done by mysqld_safe
-*/
-
-static void check_data_home(const char *path)
-{
-}
-
-#endif /*__WIN__ || __NETWARE */
-
+#endif /* !EMBEDDED_LIBRARY */
-#if BACKTRACE_DEMANGLE
-#include <cxxabi.h>
-extern "C" char *my_demangle(const char *mangled_name, int *status)
-{
- return abi::__cxa_demangle(mangled_name, NULL, NULL, status);
+#if defined(__ia64__) || defined(__ia64)
+ stacksize /= 2;
+#endif
+ return stacksize;
}
#endif
-#if !defined(__WIN__) && !defined(__NETWARE__)
+#if !defined(__WIN__)
#ifndef SA_RESETHAND
#define SA_RESETHAND 0
-#endif
+#endif /* SA_RESETHAND */
#ifndef SA_NODEFER
#define SA_NODEFER 0
-#endif
+#endif /* SA_NODEFER */
#ifndef EMBEDDED_LIBRARY
@@ -2686,9 +2907,7 @@ static void init_signals(void)
sigemptyset(&sa.sa_mask);
sigprocmask(SIG_SETMASK,&sa.sa_mask,NULL);
-#ifdef HAVE_STACKTRACE
my_init_stacktrace();
-#endif
#if defined(__amiga__)
sa.sa_handler=(void(*)())handle_fatal_signal;
#else
@@ -2730,9 +2949,6 @@ static void init_signals(void)
sa.sa_flags = 0;
sa.sa_handler = print_signal_warning;
sigaction(SIGHUP, &sa, (struct sigaction*) 0);
-#ifdef SIGTSTP
- sigaddset(&set,SIGTSTP);
-#endif
if (thd_lib_detected != THD_LIB_LT)
sigaddset(&set,THR_SERVER_ALARM);
if (test_flags & TEST_SIGINT)
@@ -2742,7 +2958,12 @@ static void init_signals(void)
sigdelset(&set, SIGINT);
}
else
+ {
sigaddset(&set,SIGINT);
+#ifdef SIGTSTP
+ sigaddset(&set,SIGTSTP);
+#endif
+ }
sigprocmask(SIG_SETMASK,&set,NULL);
pthread_sigmask(SIG_SETMASK,&set,NULL);
@@ -2757,31 +2978,20 @@ static void start_signal_handler(void)
DBUG_ENTER("start_signal_handler");
(void) pthread_attr_init(&thr_attr);
-#if !defined(HAVE_DEC_3_2_THREADS)
pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_SYSTEM);
(void) pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED);
- if (!(opt_specialflag & SPECIAL_NO_PRIOR))
- my_pthread_attr_setprio(&thr_attr,INTERRUPT_PRIOR);
-#if defined(__ia64__) || defined(__ia64)
- /*
- Peculiar things with ia64 platforms - it seems we only have half the
- stack size in reality, so we have to double it here
- */
- pthread_attr_setstacksize(&thr_attr,my_thread_stack_size*2);
-#else
- pthread_attr_setstacksize(&thr_attr,my_thread_stack_size);
-#endif
-#endif
+ (void) my_setstacksize(&thr_attr,my_thread_stack_size);
- (void) pthread_mutex_lock(&LOCK_thread_count);
- if ((error=pthread_create(&signal_thread,&thr_attr,signal_hand,0)))
+ mysql_mutex_lock(&LOCK_thread_count);
+ if ((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);
exit(1);
}
- (void) pthread_cond_wait(&COND_thread_count,&LOCK_thread_count);
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
(void) pthread_attr_destroy(&thr_attr);
DBUG_VOID_RETURN;
@@ -2803,7 +3013,7 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused)))
This should actually be '+ max_number_of_slaves' instead of +10,
but the +10 should be quite safe.
*/
- init_thr_alarm(thread_scheduler.max_threads + extra_max_connections +
+ init_thr_alarm(thread_scheduler->max_threads + extra_max_connections +
global_system_variables.max_insert_delayed_threads + 10);
if (test_flags & TEST_SIGINT)
{
@@ -2832,11 +3042,11 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused)))
This works by waiting for start_signal_handler to free mutex,
after which we signal it that we are ready.
At this pointer there is no other threads running, so there
- should not be any other pthread_cond_signal() calls.
+ should not be any other mysql_cond_signal() calls.
*/
- (void) pthread_mutex_lock(&LOCK_thread_count);
- (void) pthread_mutex_unlock(&LOCK_thread_count);
- (void) pthread_cond_broadcast(&COND_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
+ mysql_cond_broadcast(&COND_thread_count);
(void) pthread_sigmask(SIG_BLOCK,&set,NULL);
for (;;)
@@ -2853,8 +3063,8 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused)))
{
DBUG_PRINT("quit",("signal_handler: calling my_thread_end()"));
my_thread_end();
- signal_thread_in_use= 0;
DBUG_LEAVE; // Must match DBUG_ENTER()
+ signal_thread_in_use= 0;
pthread_exit(0); // Safety
return 0; // Avoid compiler warnings
}
@@ -2872,13 +3082,19 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused)))
if (!abort_loop)
{
abort_loop=1; // mark abort for threads
+#ifdef HAVE_PSI_INTERFACE
+ /* Delete the instrumentation for the signal thread */
+ if (likely(PSI_server != NULL))
+ PSI_server->delete_current_thread();
+#endif
#ifdef USE_ONE_SIGNAL_HAND
pthread_t tmp;
- if (!(opt_specialflag & SPECIAL_NO_PRIOR))
- my_pthread_attr_setprio(&connection_attrib,INTERRUPT_PRIOR);
- if (pthread_create(&tmp,&connection_attrib, kill_server_thread,
- (void*) &sig))
- sql_print_error("Can't create thread to kill server");
+ if ((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
kill_server((void*) sig); // MIT THREAD has a alarm thread
#endif
@@ -2936,34 +3152,19 @@ static void check_data_home(const char *path)
for the client.
*/
/* ARGSUSED */
-extern "C" int my_message_sql(uint error, const char *str, myf MyFlags);
+extern "C" void my_message_sql(uint error, const char *str, myf MyFlags);
-int my_message_sql(uint error, const char *str, myf MyFlags)
+void my_message_sql(uint error, const char *str, myf MyFlags)
{
- THD *thd;
+ THD *thd= current_thd;
MYSQL_ERROR::enum_warning_level level;
sql_print_message_func func;
+
DBUG_ENTER("my_message_sql");
DBUG_PRINT("error", ("error: %u message: '%s' Flag: %d", error, str, MyFlags));
DBUG_ASSERT(str != NULL);
- /*
- An error should have a valid error number (!= 0), so it can be caught
- in stored procedures by SQL exception handlers.
- Calling my_error() with error == 0 is a bug.
- Remaining known places to fix:
- - storage/myisam/mi_create.c, my_printf_error()
- TODO:
- DBUG_ASSERT(error != 0);
- */
- if (error == 0)
- {
- /* At least, prevent new abuse ... */
- DBUG_ASSERT(strncmp(str, "MyISAM table", 12) == 0 ||
- strncmp(str, "Aria table", 11) == 0 ||
- (MyFlags & ME_JUST_INFO));
- error= ER_UNKNOWN_ERROR;
- }
+ DBUG_ASSERT(error != 0);
if (MyFlags & ME_JUST_INFO)
{
@@ -2981,82 +3182,21 @@ int my_message_sql(uint error, const char *str, myf MyFlags)
func= sql_print_error;
}
- if ((thd= current_thd))
+ if (thd)
{
- /*
- TODO: There are two exceptions mechanism (THD and sp_rcontext),
- this could be improved by having a common stack of handlers.
- */
- if (thd->handle_error(error, str, level))
- {
- DBUG_PRINT("info", ("error handled by handle_error()"));
- DBUG_RETURN(0);
- }
-
- if (level == MYSQL_ERROR::WARN_LEVEL_WARN)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, error, str);
- if (level != MYSQL_ERROR::WARN_LEVEL_ERROR)
- goto to_error_log;
-
- thd->is_slave_error= 1; // needed to catch query errors during replication
-
- if (thd->main_da.is_ok() && !thd->main_da.can_overwrite_status)
- {
- /*
- Client has already got ok packet and we are not in net_flush(), so
- we write a message to error log.
- This could happen if we get an error in implicit commit.
- This should never happen in normal operation, so lets
- assert here in debug builds.
- */
- DBUG_ASSERT(0);
- func= sql_print_error;
- MyFlags|= ME_NOREFRESH;
- }
- else if (! thd->main_da.is_error()) // Return only first message
- {
- thd->main_da.set_error_status(thd, error, str);
- }
- query_cache_abort(&thd->net);
-
- /*
- If a continue handler is found, the error message will be cleared
- by the stored procedures code.
- */
- if (thd->spcont &&
- ! (MyFlags & ME_NO_SP_HANDLER) &&
- thd->spcont->handle_error(error, MYSQL_ERROR::WARN_LEVEL_ERROR, thd))
- {
- /*
- Do not push any warnings, a handled error must be completely
- silenced.
- */
- DBUG_RETURN(0);
- }
-
- /* When simulating OOM, skip writing to error log to avoid mtr errors */
- DBUG_EXECUTE_IF("simulate_out_of_memory", DBUG_RETURN(0););
-
- if (!thd->no_warnings_for_error &&
- !(MyFlags & ME_NO_WARNING_FOR_ERROR))
- {
- /*
- Suppress infinite recursion if there a memory allocation error
- inside push_warning.
- */
- thd->no_warnings_for_error= TRUE;
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, error, str);
- thd->no_warnings_for_error= FALSE;
- }
+ if (MyFlags & ME_FATALERROR)
+ thd->is_fatal_error= 1;
+ (void) thd->raise_condition(error, NULL, level, str);
}
+ else
+ mysql_audit_general(0, MYSQL_AUDIT_GENERAL_ERROR, error, str);
-to_error_log:
/* When simulating OOM, skip writing to error log to avoid mtr errors */
- DBUG_EXECUTE_IF("simulate_out_of_memory", DBUG_RETURN(0););
+ DBUG_EXECUTE_IF("simulate_out_of_memory", DBUG_VOID_RETURN;);
if (!thd || thd->log_all_errors || (MyFlags & ME_NOREFRESH))
(*func)("%s: %s", my_progname_short, str); /* purecov: inspected */
- DBUG_RETURN(0);
+ DBUG_VOID_RETURN;
}
@@ -3072,7 +3212,7 @@ void *my_str_malloc_mysqld(size_t size)
void my_str_free_mysqld(void *ptr)
{
- my_free((uchar*)ptr, MYF(MY_FAE));
+ my_free(ptr);
}
#endif /* EMBEDDED_LIBRARY */
@@ -3094,14 +3234,7 @@ pthread_handler_t handle_shutdown(void *arg)
}
#endif
-const char *load_default_groups[]= {
-#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
-"mysql_cluster",
-#endif
-"mysqld", "server", MYSQL_BASE_VERSION,
-"mariadb", MARIADB_BASE_VERSION,
-"client-server",
-0, 0};
+#include <mysqld_default_groups.h>
#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY)
static const int load_default_groups_sz=
@@ -3139,41 +3272,32 @@ check_enough_stack_size(int recurse_level)
@param format_type What kind of format should be supported
@param var_ptr Pointer to variable that should be updated
- @note
- The default value is taken from either opt_date_time_formats[] or
- the ISO format (ANSI SQL)
-
- @retval 0 ok
- @retval 1 error
+ @retval
+ 0 ok
+ @retval
+ 1 error
*/
static bool init_global_datetime_format(timestamp_type format_type,
- DATE_TIME_FORMAT **var_ptr)
+ DATE_TIME_FORMAT *format)
{
- /* Get command line option */
- const char *str= opt_date_time_formats[format_type];
+ /*
+ Get command line option
+ format->format.str is already set by my_getopt
+ */
+ format->format.length= strlen(format->format.str);
- if (!str) // No specified format
+ if (parse_date_time_format(format_type, format))
{
- str= get_date_time_format_str(&known_date_time_formats[ISO_FORMAT],
- format_type);
- /*
- Set the "command line" option to point to the generated string so
- that we can set global formats back to default
- */
- opt_date_time_formats[format_type]= str;
+ fprintf(stderr, "Wrong date/time format specifier: %s\n",
+ format->format.str);
+ return true;
}
- if (!(*var_ptr= date_time_format_make(format_type, str, strlen(str))))
- {
- fprintf(stderr, "Wrong date/time format specifier: %s\n", str);
- return 1;
- }
- return 0;
+ return false;
}
SHOW_VAR com_status_vars[]= {
{"admin_commands", (char*) offsetof(STATUS_VAR, com_other), SHOW_LONG_STATUS},
- {"assign_to_keycache", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ASSIGN_TO_KEYCACHE]), SHOW_LONG_STATUS},
{"alter_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ALTER_DB]), SHOW_LONG_STATUS},
{"alter_db_upgrade", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ALTER_DB_UPGRADE]), SHOW_LONG_STATUS},
{"alter_event", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ALTER_EVENT]), SHOW_LONG_STATUS},
@@ -3183,7 +3307,7 @@ SHOW_VAR com_status_vars[]= {
{"alter_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ALTER_TABLE]), SHOW_LONG_STATUS},
{"alter_tablespace", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ALTER_TABLESPACE]), SHOW_LONG_STATUS},
{"analyze", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ANALYZE]), SHOW_LONG_STATUS},
- {"backup_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_BACKUP_TABLE]), SHOW_LONG_STATUS},
+ {"assign_to_keycache", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ASSIGN_TO_KEYCACHE]), SHOW_LONG_STATUS},
{"begin", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_BEGIN]), SHOW_LONG_STATUS},
{"binlog", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_BINLOG_BASE64_EVENT]), SHOW_LONG_STATUS},
{"call_procedure", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CALL]), SHOW_LONG_STATUS},
@@ -3230,8 +3354,6 @@ SHOW_VAR com_status_vars[]= {
{"install_plugin", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_INSTALL_PLUGIN]), SHOW_LONG_STATUS},
{"kill", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_KILL]), SHOW_LONG_STATUS},
{"load", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_LOAD]), SHOW_LONG_STATUS},
- {"load_master_data", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_LOAD_MASTER_DATA]), SHOW_LONG_STATUS},
- {"load_master_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_LOAD_MASTER_TABLE]), SHOW_LONG_STATUS},
{"lock_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_LOCK_TABLES]), SHOW_LONG_STATUS},
{"optimize", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_OPTIMIZE]), SHOW_LONG_STATUS},
{"preload_keys", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_PRELOAD_KEYS]), SHOW_LONG_STATUS},
@@ -3245,7 +3367,7 @@ SHOW_VAR com_status_vars[]= {
{"replace", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REPLACE]), SHOW_LONG_STATUS},
{"replace_select", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REPLACE_SELECT]), SHOW_LONG_STATUS},
{"reset", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RESET]), SHOW_LONG_STATUS},
- {"restore_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RESTORE_TABLE]), SHOW_LONG_STATUS},
+ {"resignal", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RESIGNAL]), SHOW_LONG_STATUS},
{"revoke", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REVOKE]), SHOW_LONG_STATUS},
{"revoke_all", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REVOKE_ALL]), SHOW_LONG_STATUS},
{"rollback", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ROLLBACK]), SHOW_LONG_STATUS},
@@ -3259,7 +3381,6 @@ SHOW_VAR com_status_vars[]= {
{"show_charsets", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CHARSETS]), SHOW_LONG_STATUS},
{"show_client_statistics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CLIENT_STATS]), SHOW_LONG_STATUS},
{"show_collations", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLLATIONS]), SHOW_LONG_STATUS},
- {"show_column_types", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLUMN_TYPES]), SHOW_LONG_STATUS},
{"show_contributors", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CONTRIBUTORS]), SHOW_LONG_STATUS},
{"show_create_db", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE_DB]), SHOW_LONG_STATUS},
{"show_create_event", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CREATE_EVENT]), SHOW_LONG_STATUS},
@@ -3271,18 +3392,17 @@ SHOW_VAR com_status_vars[]= {
{"show_engine_logs", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_ENGINE_LOGS]), SHOW_LONG_STATUS},
{"show_engine_mutex", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_ENGINE_MUTEX]), SHOW_LONG_STATUS},
{"show_engine_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_ENGINE_STATUS]), SHOW_LONG_STATUS},
- {"show_events", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_EVENTS]), SHOW_LONG_STATUS},
{"show_errors", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_ERRORS]), SHOW_LONG_STATUS},
+ {"show_events", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_EVENTS]), SHOW_LONG_STATUS},
{"show_fields", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_FIELDS]), SHOW_LONG_STATUS},
#ifndef DBUG_OFF
{"show_function_code", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_FUNC_CODE]), SHOW_LONG_STATUS},
#endif
{"show_function_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS_FUNC]), SHOW_LONG_STATUS},
{"show_grants", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_GRANTS]), SHOW_LONG_STATUS},
- {"show_keys", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_KEYS]), SHOW_LONG_STATUS},
{"show_index_statistics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_INDEX_STATS]), SHOW_LONG_STATUS},
+ {"show_keys", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_KEYS]), SHOW_LONG_STATUS},
{"show_master_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_MASTER_STAT]), SHOW_LONG_STATUS},
- {"show_new_master", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_NEW_MASTER]), SHOW_LONG_STATUS},
{"show_open_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_OPEN_TABLES]), SHOW_LONG_STATUS},
{"show_plugins", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_PLUGINS]), SHOW_LONG_STATUS},
{"show_privileges", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_PRIVILEGES]), SHOW_LONG_STATUS},
@@ -3293,6 +3413,7 @@ SHOW_VAR com_status_vars[]= {
{"show_processlist", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_PROCESSLIST]), SHOW_LONG_STATUS},
{"show_profile", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_PROFILE]), SHOW_LONG_STATUS},
{"show_profiles", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_PROFILES]), SHOW_LONG_STATUS},
+ {"show_relaylog_events", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_RELAYLOG_EVENTS]), SHOW_LONG_STATUS},
{"show_slave_hosts", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_SLAVE_HOSTS]), SHOW_LONG_STATUS},
{"show_slave_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_SLAVE_STAT]), SHOW_LONG_STATUS},
{"show_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS]), SHOW_LONG_STATUS},
@@ -3304,6 +3425,7 @@ SHOW_VAR com_status_vars[]= {
{"show_user_statistics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_USER_STATS]), SHOW_LONG_STATUS},
{"show_variables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_VARIABLES]), SHOW_LONG_STATUS},
{"show_warnings", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_WARNS]), SHOW_LONG_STATUS},
+ {"signal", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SIGNAL]), SHOW_LONG_STATUS},
{"slave_start", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_START]), SHOW_LONG_STATUS},
{"slave_stop", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_STOP]), SHOW_LONG_STATUS},
{"stmt_close", (char*) offsetof(STATUS_VAR, com_stmt_close), SHOW_LONG_STATUS},
@@ -3327,13 +3449,15 @@ SHOW_VAR com_status_vars[]= {
{NullS, NullS, SHOW_LONG}
};
-static int init_common_variables(const char *conf_file_name, int argc,
- char **argv, const char **groups)
+static int init_common_variables()
{
- char buff[FN_REFLEN], *s;
umask(((~my_umask) & 0666));
+ my_decimal_set_zero(&decimal_zero); // set decimal_zero constant;
+
tzset(); // Set tzname
+ sf_leaking_memory= 0; // no memory leaks from now on
+
max_system_variables.pseudo_thread_id= (ulong)~0;
server_start_time= flush_status_time= my_time(0);
@@ -3353,14 +3477,23 @@ static int init_common_variables(const char *conf_file_name, int argc,
return 1;
#ifdef HAVE_TZNAME
- {
- struct tm tm_tmp;
- localtime_r(&server_start_time,&tm_tmp);
- strmake(system_time_zone, tzname[tm_tmp.tm_isdst != 0 ? 1 : 0],
- sizeof(system_time_zone)-1);
+ struct tm tm_tmp;
+ localtime_r(&server_start_time,&tm_tmp);
+ const char *tz_name= tzname[tm_tmp.tm_isdst != 0 ? 1 : 0];
+#ifdef _WIN32
+ /*
+ Time zone name may be localized and contain non-ASCII characters,
+ Convert from ANSI encoding to UTF8.
+ */
+ wchar_t wtz_name[sizeof(system_time_zone)];
+ mbstowcs(wtz_name, tz_name, sizeof(system_time_zone)-1);
+ WideCharToMultiByte(CP_UTF8,0, wtz_name, -1, system_time_zone,
+ sizeof(system_time_zone) - 1, NULL, NULL);
+#else
+ strmake_buf(system_time_zone, tz_name);
+#endif /* _WIN32 */
+#endif /* HAVE_TZNAME */
- }
-#endif
/*
We set SYSTEM time zone as reasonable default and
also for failure of my_tz_init() and bootstrap mode.
@@ -3369,6 +3502,19 @@ static int init_common_variables(const char *conf_file_name, int argc,
*/
global_system_variables.time_zone= my_tz_SYSTEM;
+#ifdef HAVE_PSI_INTERFACE
+ /*
+ Complete the mysql_bin_log initialization.
+ Instrumentation keys are known only after the performance schema initialization,
+ and can not be set in the MYSQL_BIN_LOG constructor (called before main()).
+ */
+ mysql_bin_log.set_psi_keys(key_BINLOG_LOCK_index,
+ key_BINLOG_update_cond,
+ key_file_binlog,
+ key_file_binlog_index,
+ key_BINLOG_COND_queue_busy);
+#endif
+
/*
Init mutexes for the global MYSQL_BIN_LOG objects.
As safe_mutex depends on what MY_INIT() does, we can't init the mutexes of
@@ -3393,14 +3539,34 @@ static int init_common_variables(const char *conf_file_name, int argc,
strmake(glob_hostname, STRING_WITH_LEN("localhost"));
sql_print_warning("gethostname failed, using '%s' as hostname",
glob_hostname);
- opt_basename= "mysql";
+ opt_log_basename= const_cast<char *>("mysql");
}
else
+ opt_log_basename= glob_hostname;
+
+ if (!*pidfile_name)
{
- opt_basename= glob_hostname;
+ strmake(pidfile_name, opt_log_basename, sizeof(pidfile_name)-5);
+ strmov(fn_ext(pidfile_name),".pid"); // Add proper extension
}
- strmake(pidfile_name, opt_basename, sizeof(pidfile_name)-5);
- strmov(fn_ext(pidfile_name),".pid"); // Add proper extension
+
+ /*
+ The default-storage-engine entry in my_long_options should have a
+ non-null default value. It was earlier intialized as
+ (longlong)"MyISAM" in my_long_options but this triggered a
+ compiler error in the Sun Studio 12 compiler. As a work-around we
+ set the def_value member to 0 in my_long_options and initialize it
+ to the correct value here.
+
+ From MySQL 5.5 onwards, the default storage engine is InnoDB
+ (except in the embedded server, where the default continues to
+ be MyISAM)
+ */
+#if defined(WITH_INNOBASE_STORAGE_ENGINE) || defined(WITH_XTRADB_STORAGE_ENGINE)
+ default_storage_engine= const_cast<char *>("InnoDB");
+#else
+ default_storage_engine= const_cast<char *>("MyISAM");
+#endif
/*
Add server status variables to the dynamic list of
@@ -3437,15 +3603,15 @@ static int init_common_variables(const char *conf_file_name, int argc,
SQLCOM_END + 8);
#endif
- orig_argc=argc;
- orig_argv=argv;
- load_defaults(conf_file_name, groups, &argc, &argv);
- defaults_argv=argv;
- defaults_argc=argc;
- if (get_options(&defaults_argc, defaults_argv))
+ if (get_options(&remaining_argc, &remaining_argv))
return 1;
set_server_version();
+#ifndef EMBEDDED_LIBRARY
+ if (opt_abort && !opt_verbose)
+ unireg_abort(0);
+#endif /*!EMBEDDED_LIBRARY*/
+
DBUG_PRINT("info",("%s Ver %s for %s on %s\n",my_progname,
server_version, SYSTEM_TYPE,MACHINE_TYPE));
@@ -3453,10 +3619,70 @@ static int init_common_variables(const char *conf_file_name, int argc,
/* Initialize large page size */
if (opt_large_pages && (opt_large_page_size= my_get_large_page_size()))
{
+ DBUG_PRINT("info", ("Large page set, large_page_size = %d",
+ opt_large_page_size));
my_use_large_pages= 1;
my_large_page_size= opt_large_page_size;
}
+ else
+ {
+ opt_large_pages= 0;
+ /*
+ Either not configured to use large pages or Linux haven't
+ been compiled with large page support
+ */
+ }
#endif /* HAVE_LARGE_PAGES */
+#ifdef HAVE_SOLARIS_LARGE_PAGES
+#define LARGE_PAGESIZE (4*1024*1024) /* 4MB */
+#define SUPER_LARGE_PAGESIZE (256*1024*1024) /* 256MB */
+ if (opt_large_pages)
+ {
+ /*
+ tell the kernel that we want to use 4/256MB page for heap storage
+ and also for the stack. We use 4 MByte as default and if the
+ super-large-page is set we increase it to 256 MByte. 256 MByte
+ is for server installations with GBytes of RAM memory where
+ the MySQL Server will have page caches and other memory regions
+ measured in a number of GBytes.
+ We use as big pages as possible which isn't bigger than the above
+ desired page sizes.
+ */
+ int nelem;
+ size_t max_desired_page_size;
+ if (opt_super_large_pages)
+ max_desired_page_size= SUPER_LARGE_PAGESIZE;
+ else
+ max_desired_page_size= LARGE_PAGESIZE;
+ nelem = getpagesizes(NULL, 0);
+ if (nelem > 0)
+ {
+ size_t *pagesize = (size_t *) malloc(sizeof(size_t) * nelem);
+ if (pagesize != NULL && getpagesizes(pagesize, nelem) > 0)
+ {
+ size_t max_page_size= 0;
+ for (int i= 0; i < nelem; i++)
+ {
+ if (pagesize[i] > max_page_size &&
+ pagesize[i] <= max_desired_page_size)
+ max_page_size= pagesize[i];
+ }
+ free(pagesize);
+ if (max_page_size > 0)
+ {
+ struct memcntl_mha mpss;
+
+ mpss.mha_cmd= MHA_MAPSIZE_BSSBRK;
+ mpss.mha_pagesize= max_page_size;
+ mpss.mha_flags= 0;
+ memcntl(NULL, 0, MC_HAT_ADVISE, (caddr_t)&mpss, 0, 0);
+ mpss.mha_cmd= MHA_MAPSIZE_STACK;
+ memcntl(NULL, 0, MC_HAT_ADVISE, (caddr_t)&mpss, 0, 0);
+ }
+ }
+ }
+ }
+#endif /* HAVE_SOLARIS_LARGE_PAGES */
/* connections and databases needs lots of files */
{
@@ -3512,29 +3738,27 @@ static int init_common_variables(const char *conf_file_name, int argc,
open_files_limit= files;
}
unireg_init(opt_specialflag); /* Set up extern variabels */
+ if (!(my_default_lc_messages=
+ my_locale_by_name(lc_messages)))
+ {
+ sql_print_error("Unknown locale: '%s'", lc_messages);
+ return 1;
+ }
+ global_system_variables.lc_messages= my_default_lc_messages;
if (init_errmessage()) /* Read error messages from file */
return 1;
init_client_errs();
- mysql_library_init(never,never,never); /* for replication */
+ mysql_library_init(unused,unused,unused); /* for replication */
lex_init();
if (item_create_init())
return 1;
item_init();
- if (set_var_init())
- return 1;
-#ifdef HAVE_REPLICATION
- if (init_replication_sys_vars())
- return 1;
-#endif
- mysys_uses_curses=0;
-#ifdef USE_REGEX
#ifndef EMBEDDED_LIBRARY
my_regex_init(&my_charset_latin1, check_enough_stack_size);
my_string_stack_guard= check_enough_stack_size;
#else
my_regex_init(&my_charset_latin1, NULL);
#endif
-#endif
/*
Process a comma-separated character set list and choose
the first available character set. This is mostly for
@@ -3568,12 +3792,12 @@ static int init_common_variables(const char *conf_file_name, int argc,
default_collation= get_charset_by_name(default_collation_name, MYF(0));
if (!default_collation)
{
- sql_print_error(ER(ER_UNKNOWN_COLLATION), default_collation_name);
+ sql_print_error(ER_DEFAULT(ER_UNKNOWN_COLLATION), default_collation_name);
return 1;
}
if (!my_charset_same(default_charset_info, default_collation))
{
- sql_print_error(ER(ER_COLLATION_CHARSET_MISMATCH),
+ sql_print_error(ER_DEFAULT(ER_COLLATION_CHARSET_MISMATCH),
default_collation_name,
default_charset_info->csname);
return 1;
@@ -3585,7 +3809,7 @@ static int init_common_variables(const char *conf_file_name, int argc,
global_system_variables.collation_database= default_charset_info;
global_system_variables.collation_connection= default_charset_info;
global_system_variables.character_set_results= default_charset_info;
- global_system_variables.character_set_client= default_charset_info;
+ global_system_variables.character_set_client= default_charset_info;
if (!(character_set_filesystem=
get_charset_by_csname(character_set_filesystem_name,
@@ -3601,20 +3825,6 @@ static int init_common_variables(const char *conf_file_name, int argc,
}
global_system_variables.lc_time_names= my_default_lc_time_names;
- sys_init_connect.value_length= 0;
- if ((sys_init_connect.value= opt_init_connect))
- sys_init_connect.value_length= strlen(opt_init_connect);
- else
- sys_init_connect.value=my_strdup("",MYF(0));
- sys_init_connect.is_os_charset= TRUE;
-
- sys_init_slave.value_length= 0;
- if ((sys_init_slave.value= opt_init_slave))
- sys_init_slave.value_length= strlen(opt_init_slave);
- else
- sys_init_slave.value=my_strdup("",MYF(0));
- sys_init_slave.is_os_charset= TRUE;
-
/* check log options and issue warnings if needed */
if (opt_log && opt_logname && *opt_logname &&
!(log_output_options & (LOG_FILE | LOG_NONE)))
@@ -3625,16 +3835,13 @@ static int init_common_variables(const char *conf_file_name, int argc,
if (opt_slow_log && opt_slow_logname && *opt_slow_logname &&
!(log_output_options & (LOG_FILE | LOG_NONE)))
sql_print_warning("Although a path was specified for the "
- "--log_slow_queries option, log tables are used. "
+ "--log-slow-queries option, log tables are used. "
"To enable logging to files use the --log-output=file option.");
- s= opt_logname && *opt_logname ? opt_logname : make_default_log_name(buff, ".log");
- sys_var_general_log_path.value= my_strdup(s, MYF(0));
- sys_var_general_log_path.value_length= strlen(s);
-
- s= opt_slow_logname && *opt_slow_logname ? opt_slow_logname : make_default_log_name(buff, "-slow.log");
- sys_var_slow_log_path.value= my_strdup(s, MYF(0));
- sys_var_slow_log_path.value_length= strlen(s);
+ if (!opt_logname || !*opt_logname)
+ make_default_log_name(&opt_logname, ".log", false);
+ if (!opt_slow_logname || !*opt_slow_logname)
+ make_default_log_name(&opt_slow_logname, "-slow.log", false);
#if defined(ENABLED_DEBUG_SYNC)
/* Initialize the debug sync facility. See debug_sync.cc. */
@@ -3649,7 +3856,7 @@ static int init_common_variables(const char *conf_file_name, int argc,
use_temp_pool= 0;
#endif
- if (my_database_names_init())
+ if (my_dboptions_cache_init())
return 1;
/*
@@ -3710,41 +3917,49 @@ You should consider changing lower_case_table_names to 1 or 2",
static int init_thread_environment()
{
- (void) pthread_mutex_init(&LOCK_mysql_create_db,MY_MUTEX_INIT_SLOW);
- (void) pthread_mutex_init(&LOCK_lock_db,MY_MUTEX_INIT_SLOW);
- (void) pthread_mutex_init(&LOCK_Acl,MY_MUTEX_INIT_SLOW);
- (void) pthread_mutex_init(&LOCK_open, MY_MUTEX_INIT_FAST);
- (void) pthread_mutex_init(&LOCK_thread_count,MY_MUTEX_INIT_FAST);
- (void) pthread_mutex_init(&LOCK_mapped_file,MY_MUTEX_INIT_SLOW);
- (void) pthread_mutex_init(&LOCK_status,MY_MUTEX_INIT_FAST);
- (void) pthread_mutex_init(&LOCK_error_log,MY_MUTEX_INIT_FAST);
- (void) pthread_mutex_init(&LOCK_delayed_insert,MY_MUTEX_INIT_FAST);
- (void) pthread_mutex_init(&LOCK_delayed_status,MY_MUTEX_INIT_FAST);
- (void) pthread_mutex_init(&LOCK_delayed_create,MY_MUTEX_INIT_SLOW);
- (void) pthread_mutex_init(&LOCK_crypt,MY_MUTEX_INIT_FAST);
- (void) pthread_mutex_init(&LOCK_bytes_sent,MY_MUTEX_INIT_FAST);
- (void) pthread_mutex_init(&LOCK_bytes_received,MY_MUTEX_INIT_FAST);
- (void) pthread_mutex_init(&LOCK_user_conn, MY_MUTEX_INIT_FAST);
- (void) pthread_mutex_init(&LOCK_active_mi, MY_MUTEX_INIT_FAST);
- (void) pthread_mutex_init(&LOCK_global_system_variables, MY_MUTEX_INIT_FAST);
- (void) my_rwlock_init(&LOCK_system_variables_hash, NULL);
- (void) pthread_mutex_init(&LOCK_global_read_lock, MY_MUTEX_INIT_FAST);
- (void) pthread_mutex_init(&LOCK_prepared_stmt_count, MY_MUTEX_INIT_FAST);
- (void) pthread_mutex_init(&LOCK_short_uuid_generator, MY_MUTEX_INIT_FAST);
- (void) pthread_mutex_init(&LOCK_connection_count, MY_MUTEX_INIT_FAST);
- (void) pthread_mutex_init(&LOCK_stats, MY_MUTEX_INIT_FAST);
- (void) pthread_mutex_init(&LOCK_global_user_client_stats,
- MY_MUTEX_INIT_FAST);
- (void) pthread_mutex_init(&LOCK_global_table_stats, MY_MUTEX_INIT_FAST);
- (void) pthread_mutex_init(&LOCK_global_index_stats, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_thread_count, &LOCK_thread_count, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_status, &LOCK_status, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_delayed_insert,
+ &LOCK_delayed_insert, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_delayed_status,
+ &LOCK_delayed_status, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_delayed_create,
+ &LOCK_delayed_create, MY_MUTEX_INIT_SLOW);
+ mysql_mutex_init(key_LOCK_crypt, &LOCK_crypt, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_user_conn, &LOCK_user_conn, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_active_mi, &LOCK_active_mi, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_global_system_variables,
+ &LOCK_global_system_variables, MY_MUTEX_INIT_FAST);
+ mysql_rwlock_init(key_rwlock_LOCK_system_variables_hash,
+ &LOCK_system_variables_hash);
+ mysql_mutex_init(key_LOCK_prepared_stmt_count,
+ &LOCK_prepared_stmt_count, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_error_messages,
+ &LOCK_error_messages, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_uuid_short_generator,
+ &LOCK_short_uuid_generator, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_connection_count,
+ &LOCK_connection_count, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_stats, &LOCK_stats, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_global_user_client_stats,
+ &LOCK_global_user_client_stats, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_global_table_stats,
+ &LOCK_global_table_stats, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_global_index_stats,
+ &LOCK_global_index_stats, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_prepare_ordered, &LOCK_prepare_ordered,
+ MY_MUTEX_INIT_SLOW);
+ mysql_mutex_init(key_LOCK_commit_ordered, &LOCK_commit_ordered,
+ MY_MUTEX_INIT_SLOW);
#ifdef HAVE_OPENSSL
- (void) pthread_mutex_init(&LOCK_des_key_file,MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_des_key_file,
+ &LOCK_des_key_file, MY_MUTEX_INIT_FAST);
#ifndef HAVE_YASSL
openssl_stdlocks= (openssl_lock_t*) OPENSSL_malloc(CRYPTO_num_locks() *
sizeof(openssl_lock_t));
for (int i= 0; i < CRYPTO_num_locks(); ++i)
- (void) my_rwlock_init(&openssl_stdlocks[i].lock, NULL);
+ mysql_rwlock_init(key_rwlock_openssl, &openssl_stdlocks[i].lock);
CRYPTO_set_dynlock_create_callback(openssl_dynlock_create);
CRYPTO_set_dynlock_destroy_callback(openssl_dynlock_destroy);
CRYPTO_set_dynlock_lock_callback(openssl_lock);
@@ -3752,20 +3967,19 @@ static int init_thread_environment()
CRYPTO_set_id_callback(openssl_id_function);
#endif
#endif
- (void) my_rwlock_init(&LOCK_sys_init_connect, NULL);
- (void) my_rwlock_init(&LOCK_sys_init_slave, NULL);
- (void) my_rwlock_init(&LOCK_grant, NULL);
- (void) pthread_cond_init(&COND_thread_count,NULL);
- (void) pthread_cond_init(&COND_refresh,NULL);
- (void) pthread_cond_init(&COND_global_read_lock,NULL);
- (void) pthread_cond_init(&COND_thread_cache,NULL);
- (void) pthread_cond_init(&COND_flush_thread_cache,NULL);
+ mysql_rwlock_init(key_rwlock_LOCK_sys_init_connect, &LOCK_sys_init_connect);
+ mysql_rwlock_init(key_rwlock_LOCK_sys_init_slave, &LOCK_sys_init_slave);
+ mysql_rwlock_init(key_rwlock_LOCK_grant, &LOCK_grant);
+ mysql_cond_init(key_COND_thread_count, &COND_thread_count, NULL);
+ mysql_cond_init(key_COND_thread_cache, &COND_thread_cache, NULL);
+ mysql_cond_init(key_COND_flush_thread_cache, &COND_flush_thread_cache, NULL);
#ifdef HAVE_REPLICATION
- (void) pthread_mutex_init(&LOCK_rpl_status, MY_MUTEX_INIT_FAST);
- (void) pthread_cond_init(&COND_rpl_status, NULL);
+ mysql_mutex_init(key_LOCK_rpl_status, &LOCK_rpl_status, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_COND_rpl_status, &COND_rpl_status, NULL);
#endif
- (void) pthread_mutex_init(&LOCK_server_started, MY_MUTEX_INIT_FAST);
- (void) pthread_cond_init(&COND_server_started,NULL);
+ mysql_mutex_init(key_LOCK_server_started,
+ &LOCK_server_started, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_COND_server_started, &COND_server_started, NULL);
sp_cache_init();
#ifdef HAVE_EVENT_SCHEDULER
Events::init_mutexes();
@@ -3775,8 +3989,6 @@ static int init_thread_environment()
(void) pthread_attr_setdetachstate(&connection_attrib,
PTHREAD_CREATE_DETACHED);
pthread_attr_setscope(&connection_attrib, PTHREAD_SCOPE_SYSTEM);
- if (!(opt_specialflag & SPECIAL_NO_PRIOR))
- my_pthread_attr_setprio(&connection_attrib,WAIT_PRIOR);
if (pthread_key_create(&THR_THD,NULL) ||
pthread_key_create(&THR_MALLOC,NULL))
@@ -3784,7 +3996,6 @@ static int init_thread_environment()
sql_print_error("Can't create thread-keys");
return 1;
}
- register_mutex_order();
return 0;
}
@@ -3799,7 +4010,7 @@ static unsigned long openssl_id_function()
static openssl_lock_t *openssl_dynlock_create(const char *file, int line)
{
openssl_lock_t *lock= new openssl_lock_t;
- my_rwlock_init(&lock->lock, NULL);
+ mysql_rwlock_init(key_rwlock_openssl, &lock->lock);
return lock;
}
@@ -3807,7 +4018,7 @@ static openssl_lock_t *openssl_dynlock_create(const char *file, int line)
static void openssl_dynlock_destroy(openssl_lock_t *lock, const char *file,
int line)
{
- rwlock_destroy(&lock->lock);
+ mysql_rwlock_destroy(&lock->lock);
delete lock;
}
@@ -3833,16 +4044,16 @@ static void openssl_lock(int mode, openssl_lock_t *lock, const char *file,
switch (mode) {
case CRYPTO_LOCK|CRYPTO_READ:
what = "read lock";
- err = rw_rdlock(&lock->lock);
+ err= mysql_rwlock_rdlock(&lock->lock);
break;
case CRYPTO_LOCK|CRYPTO_WRITE:
what = "write lock";
- err = rw_wrlock(&lock->lock);
+ err= mysql_rwlock_wrlock(&lock->lock);
break;
case CRYPTO_UNLOCK|CRYPTO_READ:
case CRYPTO_UNLOCK|CRYPTO_WRITE:
what = "unlock";
- err = rw_unlock(&lock->lock);
+ err= mysql_rwlock_unlock(&lock->lock);
break;
default:
/* Unknown locking mode. */
@@ -3858,11 +4069,9 @@ static void openssl_lock(int mode, openssl_lock_t *lock, const char *file,
#endif /* HAVE_OPENSSL */
-#ifndef EMBEDDED_LIBRARY
-
static void init_ssl()
{
-#ifdef HAVE_OPENSSL
+#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
if (opt_use_ssl)
{
enum enum_ssl_init_error error= SSL_INITERR_NOERROR;
@@ -3887,23 +4096,23 @@ static void init_ssl()
}
if (des_key_file)
load_des_key_file(des_key_file);
-#endif /* HAVE_OPENSSL */
+#endif /* HAVE_OPENSSL && ! EMBEDDED_LIBRARY */
}
static void end_ssl()
{
#ifdef HAVE_OPENSSL
+#ifndef EMBEDDED_LIBRARY
if (ssl_acceptor_fd)
{
free_vio_ssl_acceptor_fd(ssl_acceptor_fd);
ssl_acceptor_fd= 0;
}
+#endif /* ! EMBEDDED_LIBRARY */
#endif /* HAVE_OPENSSL */
}
-#endif /* EMBEDDED_LIBRARY */
-
#ifdef _WIN32
/**
Registers a file to be collected when Windows Error Reporting creates a crash
@@ -3938,10 +4147,10 @@ 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
*/
- if (table_cache_init() | table_def_init() | hostname_cache_init())
+ mdl_init();
+ if (table_def_init() | hostname_cache_init())
unireg_abort(1);
- query_cache_result_size_limit(query_cache_limit);
query_cache_set_min_res_unit(query_cache_min_res_unit);
query_cache_init();
query_cache_resize(query_cache_size);
@@ -3961,7 +4170,7 @@ static int init_server_components()
help information. Since the implementation of plugin server
variables the help output is now written much later.
*/
- if (opt_error_log && !opt_help)
+ if (opt_error_log && !opt_abort)
{
if (!log_error_file_ptr[0])
fn_format(log_error_file, pidfile_name, mysql_data_home, ".err",
@@ -3969,8 +4178,13 @@ static int init_server_components()
else
fn_format(log_error_file, log_error_file_ptr, mysql_data_home, ".err",
MY_UNPACK_FILENAME | MY_SAFE_PATH);
+ /*
+ _ptr may have been set to my_disabled_option or "" if no argument was
+ passed, but we need to show the real name in SHOW VARIABLES:
+ */
+ log_error_file_ptr= log_error_file;
if (!log_error_file[0])
- opt_error_log= 1; // Too long file name
+ opt_error_log= 0; // Too long file name
else
{
my_bool res;
@@ -3992,9 +4206,19 @@ static int init_server_components()
/* set up the hook before initializing plugins which may use it */
error_handler_hook= my_message_sql;
- proc_info_hook= (const char *(*)(void *, const char *, const char *,
- const char *, const unsigned int))
- set_thd_proc_info;
+ proc_info_hook= set_thd_proc_info;
+
+#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
+ /*
+ Parsing the performance schema command line option may have reported
+ warnings/information messages.
+ Now that the logger is finally available, and redirected
+ to the proper file when the --log--error option is used,
+ print the buffered messages to the log.
+ */
+ buffered_logs.print();
+ buffered_logs.cleanup();
+#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
if (xid_cache_init())
{
@@ -4002,88 +4226,23 @@ static int init_server_components()
unireg_abort(1);
}
- /* need to configure logging before initializing storage engines */
- if (opt_update_log)
- {
- /*
- Update log is removed since 5.0. But we still accept the option.
- The idea is if the user already uses the binlog and the update log,
- we completely ignore any option/variable related to the update log, like
- if the update log did not exist. But if the user uses only the update
- log, then we translate everything into binlog for him (with warnings).
- Implementation of the above :
- - If mysqld is started with --log-update and --log-bin,
- ignore --log-update (print a warning), push a warning when SQL_LOG_UPDATE
- is used, and turn off --sql-bin-update-same.
- This will completely ignore SQL_LOG_UPDATE
- - If mysqld is started with --log-update only,
- change it to --log-bin (with the filename passed to log-update,
- plus '-bin') (print a warning), push a warning when SQL_LOG_UPDATE is
- used, and turn on --sql-bin-update-same.
- This will translate SQL_LOG_UPDATE to SQL_LOG_BIN.
-
- Note that we tell the user that --sql-bin-update-same is deprecated and
- does nothing, and we don't take into account if he used this option or
- not; but internally we give this variable a value to have the behaviour
- we want (i.e. have SQL_LOG_UPDATE influence SQL_LOG_BIN or not).
- As sql-bin-update-same, log-update and log-bin cannot be changed by the
- user after starting the server (they are not variables), the user will
- not later interfere with the settings we do here.
- */
- if (opt_bin_log)
- {
- opt_sql_bin_update= 0;
- sql_print_error("The update log is no longer supported by MySQL in \
-version 5.0 and above. It is replaced by the binary log.");
- }
- else
- {
- opt_sql_bin_update= 1;
- opt_bin_log= 1;
- if (opt_update_logname)
- {
- /* as opt_bin_log==0, no need to free opt_bin_logname */
- if (!(opt_bin_logname= my_once_strdup(opt_update_logname, MYF(MY_WME))))
- {
- sql_print_error("Out of memory");
- return EXIT_OUT_OF_MEMORY;
- }
- sql_print_error("The update log is no longer supported by MySQL in \
-version 5.0 and above. It is replaced by the binary log. Now starting MySQL \
-with --log-bin='%s' instead.",opt_bin_logname);
- }
- else
- sql_print_error("The update log is no longer supported by MySQL in \
-version 5.0 and above. It is replaced by the binary log. Now starting MySQL \
-with --log-bin instead.");
- }
- }
- if (opt_log_slave_updates && !opt_bin_log)
- {
- sql_print_error("You need to use --log-bin to make "
- "--log-slave-updates work.");
+ /*
+ initialize delegates for extension observers, errors have already
+ been reported in the function
+ */
+ if (delegates_init())
unireg_abort(1);
- }
- if (!opt_bin_log)
+
+ /* need to configure logging before initializing storage engines */
+ if (!opt_bin_log_used)
{
- if (opt_binlog_format_id != BINLOG_FORMAT_UNSPEC)
- {
- sql_print_error("You need to use --log-bin to make "
- "--binlog-format work.");
- unireg_abort(1);
- }
- else
- {
- global_system_variables.binlog_format= BINLOG_FORMAT_STMT;
- }
+ if (opt_log_slave_updates)
+ sql_print_warning("You need to use --log-bin to make "
+ "--log-slave-updates work.");
+ if (binlog_format_used)
+ sql_print_warning("You need to use --log-bin to make "
+ "--binlog-format work.");
}
- else
- if (opt_binlog_format_id == BINLOG_FORMAT_UNSPEC)
- global_system_variables.binlog_format= BINLOG_FORMAT_STMT;
- else
- {
- DBUG_ASSERT(global_system_variables.binlog_format != BINLOG_FORMAT_UNSPEC);
- }
/* Check that we have not let the format to unspecified at this point */
DBUG_ASSERT((uint)global_system_variables.binlog_format <=
@@ -4092,19 +4251,27 @@ with --log-bin instead.");
#ifdef HAVE_REPLICATION
if (opt_log_slave_updates && replicate_same_server_id)
{
- sql_print_error("\
-using --replicate-same-server-id in conjunction with \
+ if (opt_bin_log)
+ {
+ sql_print_error("using --replicate-same-server-id in conjunction with \
--log-slave-updates is impossible, it would lead to infinite loops in this \
server.");
- unireg_abort(1);
+ unireg_abort(1);
+ }
+ else
+ sql_print_warning("using --replicate-same-server-id in conjunction with \
+--log-slave-updates would lead to infinite loops in this server. However this \
+will be ignored as the --log-bin option is not defined.");
}
#endif
+ DBUG_ASSERT(!opt_bin_log || opt_bin_logname);
+
if (opt_bin_log)
{
/* Reports an error and aborts, if the --log-bin's path
is a directory.*/
- if (opt_bin_logname &&
+ if (opt_bin_logname[0] &&
opt_bin_logname[strlen(opt_bin_logname) - 1] == FN_LIBCHAR)
{
sql_print_error("Path '%s' is a directory name, please specify \
@@ -4126,7 +4293,7 @@ a file name for --log-bin-index option", opt_binlog_index_name);
char buf[FN_REFLEN];
const char *ln;
ln= mysql_bin_log.generate_name(opt_bin_logname, "-bin", 1, buf);
- if (!opt_bin_logname && !opt_binlog_index_name)
+ if (!opt_bin_logname[0] && !opt_binlog_index_name)
{
/*
User didn't give us info to name the binlog index file.
@@ -4139,9 +4306,9 @@ a file name for --log-bin-index option", opt_binlog_index_name);
"neither --log-basename or --log-bin-index where "
"used; This may cause repliction to break when this "
"server acts as a master and has its hostname "
- "changed!! Please use '--log-basename=%s' or "
+ "changed! Please use '--log-basename=%s' or "
"'--log-bin=%s' to avoid this problem.",
- opt_basename, ln);
+ opt_log_basename, ln);
}
if (ln == buf)
{
@@ -4154,7 +4321,7 @@ a file name for --log-bin-index option", opt_binlog_index_name);
}
/* call ha_init_key_cache() on all key caches to init them */
- process_key_caches(&ha_init_key_cache);
+ process_key_caches(&ha_init_key_cache, 0);
init_global_table_stats();
init_global_index_stats();
@@ -4163,22 +4330,28 @@ a file name for --log-bin-index option", opt_binlog_index_name);
if (ha_init_errors())
DBUG_RETURN(1);
- {
- if (plugin_init(&defaults_argc, defaults_argv,
- (opt_noacl ? PLUGIN_INIT_SKIP_PLUGIN_TABLE : 0) |
- (opt_help ? PLUGIN_INIT_SKIP_INITIALIZATION : 0)))
- {
- sql_print_error("Failed to initialize plugins.");
- unireg_abort(1);
- }
- plugins_are_initialized= TRUE; /* Don't separate from init function */
+ tc_log= 0; // ha_initialize_handlerton() needs that
+
+ if (plugin_init(&remaining_argc, remaining_argv,
+ (opt_noacl ? PLUGIN_INIT_SKIP_PLUGIN_TABLE : 0) |
+ (opt_abort ? PLUGIN_INIT_SKIP_INITIALIZATION : 0)))
+ {
+ sql_print_error("Failed to initialize plugins.");
+ unireg_abort(1);
}
+ plugins_are_initialized= TRUE; /* Don't separate from init function */
+
+ have_csv= plugin_status(STRING_WITH_LEN("csv"),
+ MYSQL_STORAGE_ENGINE_PLUGIN);
+ have_ndbcluster= plugin_status(STRING_WITH_LEN("ndbcluster"),
+ MYSQL_STORAGE_ENGINE_PLUGIN);
+ have_partitioning= plugin_status(STRING_WITH_LEN("partition"),
+ MYSQL_STORAGE_ENGINE_PLUGIN);
/* we do want to exit if there are any other unknown options */
- if (defaults_argc > 1)
+ if (remaining_argc > 1)
{
int ho_error;
- char **tmp_argv= defaults_argv;
struct my_option no_opts[]=
{
{0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
@@ -4186,32 +4359,31 @@ a file name for --log-bin-index option", opt_binlog_index_name);
/*
We need to eat any 'loose' arguments first before we conclude
that there are unprocessed options.
- But we need to preserve defaults_argv pointer intact for
- free_defaults() to work. Thus we use a copy here.
*/
my_getopt_skip_unknown= 0;
- if ((ho_error= handle_options(&defaults_argc, &tmp_argv, no_opts,
+ if ((ho_error= handle_options(&remaining_argc, &remaining_argv, no_opts,
mysqld_get_one_option)))
unireg_abort(ho_error);
+ /* Add back the program name handle_options removes */
+ remaining_argc++;
+ remaining_argv--;
my_getopt_skip_unknown= TRUE;
- if (defaults_argc)
+ if (remaining_argc > 1)
{
fprintf(stderr, "%s: Too many arguments (first extra is '%s').\n",
- my_progname, *tmp_argv);
+ my_progname, remaining_argv[1]);
unireg_abort(1);
}
}
- if (opt_help)
+ if (opt_abort)
unireg_abort(0);
/* if the errmsg.sys is not loaded, terminate to maintain behaviour */
- if (!errmesg[0][0])
- unireg_abort(1);
-
- TC_init();
+ if (!DEFAULT_ERRMSGS[0][0])
+ unireg_abort(1);
/* We have to initialize the storage engines before CSV logging */
if (ha_init())
@@ -4255,46 +4427,41 @@ a file name for --log-bin-index option", opt_binlog_index_name);
}
/*
- Check that the default storage engine is actually available.
+ Set the default storage engine
*/
- if (default_storage_engine_str)
+ LEX_STRING name= { default_storage_engine, strlen(default_storage_engine) };
+ plugin_ref plugin;
+ handlerton *hton;
+ if ((plugin= ha_resolve_by_name(0, &name)))
+ hton= plugin_data(plugin, handlerton*);
+ else
{
- LEX_STRING name= { default_storage_engine_str,
- strlen(default_storage_engine_str) };
- plugin_ref plugin;
- handlerton *hton;
-
- if ((plugin= ha_resolve_by_name(0, &name)))
- hton= plugin_data(plugin, handlerton*);
- else
+ sql_print_error("Unknown/unsupported storage engine: %s",
+ default_storage_engine);
+ unireg_abort(1);
+ }
+ if (!ha_storage_engine_is_enabled(hton))
+ {
+ if (!opt_bootstrap)
{
- sql_print_error("Unknown/unsupported table type: %s",
- default_storage_engine_str);
+ sql_print_error("Default storage engine (%s) is not available",
+ default_storage_engine);
unireg_abort(1);
}
- if (!ha_storage_engine_is_enabled(hton))
- {
- if (!opt_bootstrap)
- {
- sql_print_error("Default storage engine (%s) is not available",
- default_storage_engine_str);
- unireg_abort(1);
- }
- DBUG_ASSERT(global_system_variables.table_plugin);
- }
- else
- {
- /*
- Need to unlock as global_system_variables.table_plugin
- was acquired during plugin_init()
- */
- pthread_mutex_lock(&LOCK_global_system_variables);
- plugin_unlock(0, global_system_variables.table_plugin);
- global_system_variables.table_plugin= plugin;
- pthread_mutex_unlock(&LOCK_global_system_variables);
- }
+ DBUG_ASSERT(global_system_variables.table_plugin);
}
-#if defined(WITH_ARIA_STORAGE_ENGINE) && defined(USE_MARIA_FOR_TMP_TABLES)
+ else
+ {
+ /*
+ Need to unlock as global_system_variables.table_plugin
+ was acquired during plugin_init()
+ */
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ plugin_unlock(0, global_system_variables.table_plugin);
+ global_system_variables.table_plugin= plugin;
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ }
+#if defined(WITH_ARIA_STORAGE_ENGINE) && defined(USE_ARIA_FOR_TMP_TABLES)
if (!ha_storage_engine_is_enabled(maria_hton) && !opt_bootstrap)
{
sql_print_error("Aria engine is not enabled or did not start. The Aria engine must be enabled to continue as mysqld was configured with --with-aria-tmp-tables");
@@ -4307,10 +4474,7 @@ a file name for --log-bin-index option", opt_binlog_index_name);
internal_tmp_table_max_key_segments= myisam_max_key_segments();
#endif
- tc_log= (total_ha_2pc > 1 ? (opt_bin_log ?
- (TC_LOG *) &mysql_bin_log :
- (TC_LOG *) &tc_log_mmap) :
- (TC_LOG *) &tc_log_dummy);
+ tc_log= get_tc_log_implementation();
if (tc_log->open(opt_bin_log ? opt_bin_logname : opt_tc_log_file))
{
@@ -4335,10 +4499,6 @@ a file name for --log-bin-index option", opt_binlog_index_name);
mysql_bin_log.purge_logs_before_date(purge_time);
}
#endif
-#ifdef __NETWARE__
- /* Increasing stacksize of threads on NetWare */
- pthread_attr_setstacksize(&connection_attrib, NW_THD_STACKSIZE);
-#endif
if (opt_myisam_log)
(void) mi_log(1);
@@ -4381,8 +4541,12 @@ static void create_shutdown_thread()
#ifdef __WIN__
hEventShutdown=CreateEvent(0, FALSE, FALSE, shutdown_event_name);
pthread_t hThread;
- if (pthread_create(&hThread,&connection_attrib,handle_shutdown,0))
- sql_print_warning("Can't create thread to handle shutdown requests");
+ int error;
+ if ((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);
// On "Stop Service" we have to do regular shutdown
Service.SetShutdownEvent(hEventShutdown);
@@ -4392,12 +4556,12 @@ static void create_shutdown_thread()
#endif /* EMBEDDED_LIBRARY */
-#if (defined(__NT__) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY)
+#if (defined(_WIN32) || defined(HAVE_SMEM)) && !defined(EMBEDDED_LIBRARY)
static void handle_connections_methods()
{
pthread_t hThread;
+ int error;
DBUG_ENTER("handle_connections_methods");
-#ifdef __NT__
if (hPipe == INVALID_HANDLE_VALUE &&
(!have_tcpip || opt_disable_networking) &&
!opt_enable_shared_memory)
@@ -4405,30 +4569,31 @@ static void handle_connections_methods()
sql_print_error("TCP/IP, --shared-memory, or --named-pipe should be configured on NT OS");
unireg_abort(1); // Will not return
}
-#endif
- pthread_mutex_lock(&LOCK_thread_count);
- (void) pthread_cond_init(&COND_handler_count,NULL);
+ mysql_mutex_lock(&LOCK_thread_count);
+ mysql_cond_init(key_COND_handler_count, &COND_handler_count, NULL);
handler_count=0;
-#ifdef __NT__
if (hPipe != INVALID_HANDLE_VALUE)
{
handler_count++;
- if (pthread_create(&hThread,&connection_attrib,
- handle_connections_namedpipes, 0))
+ if ((error= mysql_thread_create(key_thread_handle_con_namedpipes,
+ &hThread, &connection_attrib,
+ handle_connections_namedpipes, 0)))
{
- sql_print_warning("Can't create thread to handle named pipes");
+ sql_print_warning("Can't create thread to handle named pipes"
+ " (errno= %d)", error);
handler_count--;
}
}
-#endif /* __NT__ */
if (have_tcpip && !opt_disable_networking)
{
handler_count++;
- if (pthread_create(&hThread,&connection_attrib,
- handle_connections_sockets, 0))
+ if ((error= mysql_thread_create(key_thread_handle_con_sockets,
+ &hThread, &connection_attrib,
+ handle_connections_sockets_thread, 0)))
{
- sql_print_warning("Can't create thread to handle TCP/IP");
+ sql_print_warning("Can't create thread to handle TCP/IP",
+ " (errno= %d)", error);
handler_count--;
}
}
@@ -4436,35 +4601,73 @@ static void handle_connections_methods()
if (opt_enable_shared_memory)
{
handler_count++;
- if (pthread_create(&hThread,&connection_attrib,
- handle_connections_shared_memory, 0))
+ if ((error= mysql_thread_create(key_thread_handle_con_sharedmem,
+ &hThread, &connection_attrib,
+ handle_connections_shared_memory, 0)))
{
- sql_print_warning("Can't create thread to handle shared memory");
+ sql_print_warning("Can't create thread to handle shared memory",
+ " (errno= %d)", error);
handler_count--;
}
}
#endif
while (handler_count > 0)
- pthread_cond_wait(&COND_handler_count,&LOCK_thread_count);
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_cond_wait(&COND_handler_count, &LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
DBUG_VOID_RETURN;
}
void decrement_handler_count()
{
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
handler_count--;
- pthread_cond_signal(&COND_handler_count);
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_cond_signal(&COND_handler_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
my_thread_end();
}
#else
#define decrement_handler_count()
-#endif /* defined(__NT__) || defined(HAVE_SMEM) */
+#endif /* defined(_WIN32) || defined(HAVE_SMEM) */
#ifndef EMBEDDED_LIBRARY
+
+LEX_STRING sql_statement_names[(uint) SQLCOM_END + 1];
+
+static void init_sql_statement_names()
+{
+ char *first_com= (char*) offsetof(STATUS_VAR, com_stat[0]);
+ char *last_com= (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_END]);
+ int record_size= (char*) offsetof(STATUS_VAR, com_stat[1])
+ - (char*) offsetof(STATUS_VAR, com_stat[0]);
+ char *ptr;
+ uint i;
+ uint com_index;
+
+ for (i= 0; i < ((uint) SQLCOM_END + 1); i++)
+ sql_statement_names[i]= empty_lex_str;
+
+ SHOW_VAR *var= &com_status_vars[0];
+ while (var->name != NULL)
+ {
+ ptr= var->value;
+ if ((first_com <= ptr) && (ptr <= last_com))
+ {
+ com_index= ((int)(ptr - first_com))/record_size;
+ DBUG_ASSERT(com_index < (uint) SQLCOM_END);
+ sql_statement_names[com_index].str= const_cast<char *>(var->name);
+ sql_statement_names[com_index].length= strlen(var->name);
+ }
+ var++;
+ }
+
+ DBUG_ASSERT(strcmp(sql_statement_names[(uint) SQLCOM_SELECT].str, "select") == 0);
+ DBUG_ASSERT(strcmp(sql_statement_names[(uint) SQLCOM_SIGNAL].str, "signal") == 0);
+
+ sql_statement_names[(uint) SQLCOM_END].str= const_cast<char*>("error");
+}
+
#ifndef DBUG_OFF
/*
Debugging helper function to keep the locale database
@@ -4502,19 +4705,136 @@ static void test_lc_time_sz()
}
#endif//DBUG_OFF
-
#ifdef __WIN__
int win_main(int argc, char **argv)
#else
-int main(int argc, char **argv)
+int mysqld_main(int argc, char **argv)
#endif
{
+ /*
+ Perform basic thread library and malloc initialization,
+ to be able to read defaults files and parse options.
+ */
+ my_progname= argv[0];
+ sf_leaking_memory= 1; // no safemalloc memory leak reports if we exit early
#ifdef HAVE_NPTL
ld_assume_kernel_is_set= (getenv("LD_ASSUME_KERNEL") != 0);
#endif
+#ifndef _WIN32
+ // For windows, my_init() is called from the win specific mysqld_main
+ if (my_init()) // init my_sys library & pthreads
+ {
+ fprintf(stderr, "my_init() failed.");
+ return 1;
+ }
+#endif
+
+ orig_argc= argc;
+ orig_argv= argv;
+ my_getopt_use_args_separator= TRUE;
+ if (load_defaults(MYSQL_CONFIG_NAME, load_default_groups, &argc, &argv))
+ return 1;
+ my_getopt_use_args_separator= FALSE;
+ defaults_argc= argc;
+ defaults_argv= argv;
+ remaining_argc= argc;
+ remaining_argv= argv;
+
+ /* Must be initialized early for comparison of options name */
+ system_charset_info= &my_charset_utf8_general_ci;
- MY_INIT(argv[0]); // init my_sys library & pthreads
- /* nothing should come before this line ^^^ */
+ init_sql_statement_names();
+ sys_var_init();
+
+#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
+ /*
+ The performance schema needs to be initialized as early as possible,
+ before to-be-instrumented objects of the server are initialized.
+ */
+ int ho_error;
+ DYNAMIC_ARRAY all_early_options;
+
+ my_getopt_register_get_addr(NULL);
+ /* Skip unknown options so that they may be processed later */
+ my_getopt_skip_unknown= TRUE;
+
+ /* prepare all_early_options array */
+ my_init_dynamic_array(&all_early_options, sizeof(my_option), 100, 25);
+ sys_var_add_options(&all_early_options, sys_var::PARSE_EARLY);
+ add_terminator(&all_early_options);
+
+ /*
+ Logs generated while parsing the command line
+ options are buffered and printed later.
+ */
+ buffered_logs.init();
+ my_getopt_error_reporter= buffered_option_error_reporter;
+
+ ho_error= handle_options(&remaining_argc, &remaining_argv,
+ (my_option*)(all_early_options.buffer), NULL);
+ delete_dynamic(&all_early_options);
+ if (ho_error == 0)
+ {
+ /* Add back the program name handle_options removes */
+ remaining_argc++;
+ remaining_argv--;
+ if (pfs_param.m_enabled)
+ {
+ PSI_hook= initialize_performance_schema(&pfs_param);
+ if (PSI_hook == NULL)
+ {
+ pfs_param.m_enabled= false;
+ buffered_logs.buffer(WARNING_LEVEL,
+ "Performance schema disabled (reason: init failed).");
+ }
+ }
+ }
+#else
+ /*
+ Other provider of the instrumentation interface should
+ initialize PSI_hook here:
+ - HAVE_PSI_INTERFACE is for the instrumentation interface
+ - WITH_PERFSCHEMA_STORAGE_ENGINE is for one implementation
+ of the interface,
+ but there could be alternate implementations, which is why
+ these two defines are kept separate.
+ */
+#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
+
+#ifdef HAVE_PSI_INTERFACE
+ /*
+ Obtain the current performance schema instrumentation interface,
+ if available.
+ */
+ if (PSI_hook)
+ PSI_server= (PSI*) PSI_hook->get_interface(PSI_CURRENT_VERSION);
+
+ if (PSI_server)
+ {
+ /*
+ Now that we have parsed the command line arguments, and have initialized
+ the performance schema itself, the next step is to register all the
+ server instruments.
+ */
+ init_server_psi_keys();
+ /* Instrument the main thread */
+ PSI_thread *psi= PSI_server->new_thread(key_thread_main, NULL, 0);
+ if (psi)
+ PSI_server->set_thread(psi);
+
+ /*
+ Now that some instrumentation is in place,
+ recreate objects which were initialised early,
+ so that they are instrumented as well.
+ */
+ my_thread_global_reinit();
+ }
+#endif /* HAVE_PSI_INTERFACE */
+
+ init_error_log_mutex();
+
+ /* Initialize audit interface globals. Audit plugins are inited later. */
+ mysql_audit_initialize();
/*
Perform basic logger initialization logger. Should be called after
@@ -4522,6 +4842,28 @@ int main(int argc, char **argv)
*/
logger.init_base();
+#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
+ if (ho_error)
+ {
+ /*
+ Parsing command line option failed,
+ Since we don't have a workable remaining_argc/remaining_argv
+ to continue the server initialization, this is as far as this
+ code can go.
+ This is the best effort to log meaningful messages:
+ - messages will be printed to stderr, which is not redirected yet,
+ - messages will be printed in the NT event log, for windows.
+ */
+ buffered_logs.print();
+ buffered_logs.cleanup();
+ /*
+ Not enough initializations for unireg_abort()
+ Using exit() for windows.
+ */
+ exit (ho_error);
+ }
+#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
+
#ifdef _CUSTOMSTARTUPCONFIG_
if (_cust_check_startup())
{
@@ -4530,64 +4872,13 @@ int main(int argc, char **argv)
}
#endif
-#ifdef __WIN__
- /*
- Before performing any socket operation (like retrieving hostname
- in init_common_variables we have to call WSAStartup
- */
- {
- WSADATA WsaData;
- if (SOCKET_ERROR == WSAStartup (0x0101, &WsaData))
- {
- /* errors are not read yet, so we use english text here */
- my_message(ER_WSAS_FAILED, "WSAStartup Failed", MYF(0));
- unireg_abort(1);
- }
- }
-#endif /* __WIN__ */
-
- if (init_common_variables(MYSQL_CONFIG_NAME,
- argc, argv, load_default_groups))
+ if (init_common_variables())
unireg_abort(1); // Will do exit
init_signals();
- if (!(opt_specialflag & SPECIAL_NO_PRIOR))
- my_pthread_setprio(pthread_self(),CONNECT_PRIOR);
-#if defined(__ia64__) || defined(__ia64)
- /*
- Peculiar things with ia64 platforms - it seems we only have half the
- stack size in reality, so we have to double it here
- */
- pthread_attr_setstacksize(&connection_attrib,my_thread_stack_size*2);
-#else
- pthread_attr_setstacksize(&connection_attrib,my_thread_stack_size);
-#endif
-#ifdef HAVE_PTHREAD_ATTR_GETSTACKSIZE
- {
- /* Retrieve used stack size; Needed for checking stack overflows */
- size_t stack_size= 0;
- pthread_attr_getstacksize(&connection_attrib, &stack_size);
-#if defined(__ia64__) || defined(__ia64)
- stack_size/= 2;
-#endif
- /* We must check if stack_size = 0 as Solaris 2.9 can return 0 here */
- if (stack_size && stack_size < my_thread_stack_size)
- {
- if (global_system_variables.log_warnings)
- sql_print_warning("Asked for %lu thread stack, but got %ld",
- my_thread_stack_size, (long) stack_size);
-#if defined(__ia64__) || defined(__ia64)
- my_thread_stack_size= stack_size*2;
-#else
- my_thread_stack_size= stack_size;
-#endif
- }
- }
-#endif
-#ifdef __NETWARE__
- /* Increasing stacksize of threads on NetWare */
- pthread_attr_setstacksize(&connection_attrib, NW_THD_STACKSIZE);
-#endif
+
+ my_thread_stack_size= my_setstacksize(&connection_attrib,
+ my_thread_stack_size);
(void) thr_setconcurrency(concurrency); // 10 by default
@@ -4608,12 +4899,8 @@ int main(int argc, char **argv)
We have enough space for fiddling with the argv, continue
*/
check_data_home(mysql_real_data_home);
- if (my_setwd(mysql_real_data_home, opt_help ? 0 : MYF(MY_WME)) && !opt_help)
+ if (my_setwd(mysql_real_data_home, opt_abort ? 0 : MYF(MY_WME)) && !opt_abort)
unireg_abort(1); /* purecov: inspected */
- mysql_data_home= mysql_data_home_buff;
- mysql_data_home[0]=FN_CURLIB; // all paths are relative from here
- mysql_data_home[1]=0;
- mysql_data_home_len= 2;
if ((user_info= check_user(mysqld_user)))
{
@@ -4627,24 +4914,23 @@ int main(int argc, char **argv)
if (opt_bin_log && !server_id)
{
- server_id= !master_host ? 1 : 2;
+ server_id= 1;
#ifdef EXTRA_DEBUG
- switch (server_id) {
- case 1:
- sql_print_warning("\
-You have enabled the binary log, but you haven't set server-id to \
-a non-zero value: we force server id to 1; updates will be logged to the \
-binary log, but connections from slaves will not be accepted.");
- break;
- case 2:
- sql_print_warning("\
-You should set server-id to a non-0 value if master_host is set; \
-we force server id to 2, but this MySQL server will not act as a slave.");
- break;
- }
+ sql_print_warning("You have enabled the binary log, but you haven't set "
+ "server-id to a non-zero value: we force server id to 1; "
+ "updates will be logged to the binary log, but "
+ "connections from slaves will not be accepted.");
#endif
}
+ /*
+ The subsequent calls may take a long time : e.g. innodb log read.
+ Thus set the long running service control manager timeout
+ */
+#if defined(_WIN32) && !defined(EMBEDDED_LIBRARY)
+ Service.SetSlowStarting(slow_start_timeout);
+#endif
+
if (init_server_components())
unireg_abort(1);
@@ -4678,15 +4964,16 @@ we force server id to 2, but this MySQL server will not act as a slave.");
{
abort_loop=1;
select_thread_in_use=0;
-#ifndef __NETWARE__
+
(void) pthread_kill(signal_thread, MYSQL_KILL_SIGNAL);
-#endif /* __NETWARE__ */
delete_pid_file(MYF(MY_WME));
+
if (unix_sock != INVALID_SOCKET)
unlink(mysqld_unix_port);
exit(1);
}
+
if (!opt_noacl)
(void) grant_init();
@@ -4703,6 +4990,8 @@ we force server id to 2, but this MySQL server will not act as a slave.");
init_status_vars();
if (opt_bootstrap) /* If running with bootstrap, do not start replication. */
opt_skip_slave_start= 1;
+
+ binlog_unsafe_map_init();
/*
init_slave() must be called after the thread keys are created.
Some parts of the code (e.g. SHOW STATUS LIKE 'slave_running' and other
@@ -4714,6 +5003,20 @@ we force server id to 2, but this MySQL server will not act as a slave.");
unireg_abort(1);
}
+#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
+ initialize_performance_schema_acl(opt_bootstrap);
+ /*
+ Do not check the structure of the performance schema tables
+ during bootstrap:
+ - the tables are not supposed to exist yet, bootstrap will create them
+ - a check would print spurious error messages
+ */
+ if (! opt_bootstrap)
+ check_performance_schema();
+#endif
+
+ initialize_information_schema_acl();
+
execute_ddl_log_recovery();
if (Events::init(opt_noacl || opt_bootstrap))
@@ -4722,7 +5025,7 @@ we force server id to 2, but this MySQL server will not act as a slave.");
if (opt_bootstrap)
{
select_thread_in_use= 0; // Allow 'kill' to work
- bootstrap(stdin);
+ bootstrap(mysql_stdin);
if (!kill_in_progress)
unireg_abort(bootstrap_error ? 1 : 0);
else
@@ -4731,7 +5034,7 @@ we force server id to 2, but this MySQL server will not act as a slave.");
exit(0);
}
}
- if (opt_init_file)
+ if (opt_init_file && *opt_init_file)
{
if (read_init_file(opt_init_file))
unireg_abort(1);
@@ -4740,7 +5043,7 @@ we force server id to 2, but this MySQL server will not act as a slave.");
create_shutdown_thread();
start_handle_manager();
- sql_print_information(ER(ER_STARTUP),my_progname,server_version,
+ sql_print_information(ER_DEFAULT(ER_STARTUP),my_progname,server_version,
((unix_sock == INVALID_SOCKET) ? (char*) ""
: mysqld_unix_port),
mysqld_port,
@@ -4751,23 +5054,16 @@ we force server id to 2, but this MySQL server will not act as a slave.");
/* Signal threads waiting for server to be started */
- pthread_mutex_lock(&LOCK_server_started);
+ mysql_mutex_lock(&LOCK_server_started);
mysqld_server_started= 1;
- pthread_cond_signal(&COND_server_started);
- pthread_mutex_unlock(&LOCK_server_started);
+ mysql_cond_signal(&COND_server_started);
+ mysql_mutex_unlock(&LOCK_server_started);
-#if defined(__NT__) || defined(HAVE_SMEM)
+#if defined(_WIN32) || defined(HAVE_SMEM)
handle_connections_methods();
#else
-#ifdef __WIN__
- if (!have_tcpip || opt_disable_networking)
- {
- sql_print_error("TCP/IP unavailable or disabled with --skip-networking; no available interfaces");
- unireg_abort(1);
- }
-#endif
- handle_connections_sockets(0);
-#endif /* __NT__ */
+ handle_connections_sockets();
+#endif /* _WIN32 || HAVE_SMEM */
/* (void) pthread_attr_destroy(&connection_attrib); */
@@ -4777,21 +5073,30 @@ we force server id to 2, but this MySQL server will not act as a slave.");
#ifdef EXTRA_DEBUG2
sql_print_error("Before Lock_thread_count");
#endif
- (void) pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
DBUG_PRINT("quit", ("Got thread_count mutex"));
select_thread_in_use=0; // For close_connections
- (void) pthread_mutex_unlock(&LOCK_thread_count);
- (void) pthread_cond_broadcast(&COND_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
+ mysql_cond_broadcast(&COND_thread_count);
#ifdef EXTRA_DEBUG2
sql_print_error("After lock_thread_count");
#endif
#endif /* __WIN__ */
+#ifdef HAVE_PSI_INTERFACE
+ /*
+ Disable the main thread instrumentation,
+ to avoid recording events during the shutdown.
+ */
+ if (PSI_server)
+ PSI_server->delete_current_thread();
+#endif
+
/* Wait until cleanup is done */
- (void) pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
while (!ready_to_exit)
- pthread_cond_wait(&COND_thread_count,&LOCK_thread_count);
- (void) pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY)
if (Service.IsNT() && start_mode)
@@ -4803,16 +5108,11 @@ we force server id to 2, but this MySQL server will not act as a slave.");
CloseHandle(hEventShutdown);
}
#endif
- clean_up(1);
- wait_for_signal_thread_to_end();
- clean_up_mutexes();
- my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
-
- exit(0);
- return(0); /* purecov: deadcode */
+ mysqld_exit(0);
+ return 0;
}
-#endif /* EMBEDDED_LIBRARY */
+#endif /* !EMBEDDED_LIBRARY */
/****************************************************************************
@@ -4823,10 +5123,15 @@ we force server id to 2, but this MySQL server will not act as a slave.");
#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY)
int mysql_service(void *p)
{
+ if (my_thread_init())
+ return 1;
+
if (use_opt_args)
win_main(opt_argc, opt_argv);
else
win_main(Service.my_argc, Service.my_argv);
+
+ my_thread_end();
return 0;
}
@@ -4870,7 +5175,7 @@ default_service_handling(char **argv,
/* We have to quote filename if it contains spaces */
pos= add_quoted_string(path_and_service, file_path, end);
- if (*extra_opt)
+ if (extra_opt && *extra_opt)
{
/*
Add option after file_path. There will be zero or one extra option. It's
@@ -4913,8 +5218,10 @@ default_service_handling(char **argv,
}
-int main(int argc, char **argv)
+int mysqld_main(int argc, char **argv)
{
+ my_progname= argv[0];
+
/*
When several instances are running on the same machine, we
need to have an unique named hEventShudown through the
@@ -4926,6 +5233,12 @@ int main(int argc, char **argv)
/* Must be initialized early for comparison of service name */
system_charset_info= &my_charset_utf8_general_ci;
+ if (my_init())
+ {
+ fprintf(stderr, "my_init() failed.");
+ return 1;
+ }
+
if (Service.GetOS()) /* true NT family */
{
char file_path[FN_REFLEN];
@@ -5021,7 +5334,7 @@ int main(int argc, char **argv)
create MySQL privilege tables without having to start a full MySQL server.
*/
-static void bootstrap(FILE *file)
+static void bootstrap(MYSQL_FILE *file)
{
DBUG_ENTER("bootstrap");
@@ -5036,24 +5349,28 @@ static void bootstrap(FILE *file)
bootstrap_file=file;
#ifndef EMBEDDED_LIBRARY // TODO: Enable this
- if (pthread_create(&thd->real_id,&connection_attrib,handle_bootstrap,
- (void*) thd))
+ int error;
+ if ((error= mysql_thread_create(key_thread_bootstrap,
+ &thd->real_id, &connection_attrib,
+ handle_bootstrap,
+ (void*) thd)))
{
- sql_print_warning("Can't create thread to handle bootstrap");
+ sql_print_warning("Can't create thread to handle bootstrap (errno= %d)",
+ error);
bootstrap_error=-1;
DBUG_VOID_RETURN;
}
/* Wait for thread to die */
- (void) pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
while (in_bootstrap)
{
- (void) pthread_cond_wait(&COND_thread_count,&LOCK_thread_count);
+ mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
DBUG_PRINT("quit",("One thread died (count=%u)",thread_count));
}
- (void) pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
#else
thd->mysql= 0;
- handle_bootstrap((void *)thd);
+ do_handle_bootstrap(thd);
#endif
DBUG_VOID_RETURN;
@@ -5062,17 +5379,26 @@ static void bootstrap(FILE *file)
static bool read_init_file(char *file_name)
{
- FILE *file;
+ MYSQL_FILE *file;
DBUG_ENTER("read_init_file");
DBUG_PRINT("enter",("name: %s",file_name));
- if (!(file=my_fopen(file_name,O_RDONLY,MYF(MY_WME))))
+ if (!(file= mysql_file_fopen(key_file_init, file_name,
+ O_RDONLY, MYF(MY_WME))))
DBUG_RETURN(TRUE);
bootstrap(file);
- (void) my_fclose(file,MYF(MY_WME));
+ mysql_file_fclose(file, MYF(MY_WME));
DBUG_RETURN(FALSE);
}
+/**
+ Increment number of created threads
+*/
+void inc_thread_created(void)
+{
+ thread_created++;
+}
+
#ifndef EMBEDDED_LIBRARY
/*
@@ -5087,13 +5413,12 @@ static bool read_init_file(char *file_name)
void handle_connection_in_main_thread(THD *thd)
{
- safe_mutex_assert_owner(&LOCK_thread_count);
+ mysql_mutex_assert_owner(&LOCK_thread_count);
thread_cache_size=0; // Safety
threads.append(thd);
- thd->set_time(my_hrtime());
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
thd->start_utime= microsecond_interval_timer();
- handle_one_connection(thd);
+ do_handle_one_connection(thd);
}
@@ -5108,7 +5433,7 @@ void create_thread_to_handle_connection(THD *thd)
/* Get thread from cache */
thread_cache.push_back(thd);
wake_thread++;
- pthread_cond_signal(&COND_thread_cache);
+ mysql_cond_signal(&COND_thread_cache);
}
else
{
@@ -5119,36 +5444,38 @@ void create_thread_to_handle_connection(THD *thd)
threads.append(thd);
DBUG_PRINT("info",(("creating thread %lu"), thd->thread_id));
thd->prior_thr_create_utime= microsecond_interval_timer();
- if ((error=pthread_create(&thd->real_id,&connection_attrib,
- handle_one_connection,
- (void*) thd)))
+ if ((error= mysql_thread_create(key_thread_one_connection,
+ &thd->real_id, &connection_attrib,
+ handle_one_connection,
+ (void*) thd)))
{
/* purecov: begin inspected */
DBUG_PRINT("error",
("Can't create thread to handle request (error %d)",
error));
+
thread_count--;
thd->killed= KILL_CONNECTION; // Safety
- (void) pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
- pthread_mutex_lock(&LOCK_connection_count);
+ mysql_mutex_lock(&LOCK_connection_count);
(*thd->scheduler->connection_count)--;
- pthread_mutex_unlock(&LOCK_connection_count);
+ mysql_mutex_unlock(&LOCK_connection_count);
statistic_increment(aborted_connects,&LOCK_status);
/* Can't use my_error() since store_globals has not been called. */
my_snprintf(error_message_buff, sizeof(error_message_buff),
- ER(ER_CANT_CREATE_THREAD), error);
- net_send_error(thd, ER_CANT_CREATE_THREAD, error_message_buff);
- (void) pthread_mutex_lock(&LOCK_thread_count);
- close_connection(thd,ER_OUT_OF_RESOURCES,0);
+ ER_THD(thd, ER_CANT_CREATE_THREAD), error);
+ net_send_error(thd, ER_CANT_CREATE_THREAD, error_message_buff, NULL);
+ close_connection(thd, ER_OUT_OF_RESOURCES);
+ mysql_mutex_lock(&LOCK_thread_count);
delete thd;
- (void) pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
return;
/* purecov: end */
}
}
- (void) pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
DBUG_PRINT("info",("Thread created"));
}
@@ -5168,26 +5495,22 @@ void create_thread_to_handle_connection(THD *thd)
static void create_new_thread(THD *thd)
{
- NET *net=&thd->net;
DBUG_ENTER("create_new_thread");
- if (protocol_version > 9)
- net->return_errno=1;
-
/*
Don't allow too many connections. We roughly check here that we allow
only (max_connections + 1) connections.
*/
- pthread_mutex_lock(&LOCK_connection_count);
+ mysql_mutex_lock(&LOCK_connection_count);
if (*thd->scheduler->connection_count >=
*thd->scheduler->max_connections + 1|| abort_loop)
{
- pthread_mutex_unlock(&LOCK_connection_count);
+ mysql_mutex_unlock(&LOCK_connection_count);
DBUG_PRINT("error",("Too many connections"));
- close_connection(thd, ER_CON_COUNT_ERROR, 1);
+ close_connection(thd, ER_CON_COUNT_ERROR);
statistic_increment(denied_connections, &LOCK_status);
delete thd;
DBUG_VOID_RETURN;
@@ -5198,11 +5521,11 @@ static void create_new_thread(THD *thd)
if (connection_count + extra_connection_count > max_used_connections)
max_used_connections= connection_count + extra_connection_count;
- pthread_mutex_unlock(&LOCK_connection_count);
+ mysql_mutex_unlock(&LOCK_connection_count);
/* Start a new thread to handle connection. */
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
/*
The initialization of thread_id is done in create_embedded_thd() for
@@ -5213,7 +5536,7 @@ static void create_new_thread(THD *thd)
thread_count++;
- thd->scheduler->add_connection(thd);
+ MYSQL_CALLBACK(thd->scheduler, add_connection, (thd));
DBUG_VOID_RETURN;
}
@@ -5224,16 +5547,13 @@ static void create_new_thread(THD *thd)
inline void kill_broken_server()
{
/* hack to get around signals ignored in syscalls for problem OS's */
- if (
-#if !defined(__NETWARE__)
- unix_sock == INVALID_SOCKET ||
-#endif
+ if (unix_sock == INVALID_SOCKET ||
(!opt_disable_networking && base_ip_sock == INVALID_SOCKET))
{
select_thread_in_use = 0;
/* The following call will never return */
DBUG_PRINT("general", ("killing server because socket is closed"));
- kill_server(IF_NETWARE(MYSQL_KILL_SIGNAL, (void*) MYSQL_KILL_SIGNAL));
+ kill_server((void*) MYSQL_KILL_SIGNAL);
}
}
#define MAYBE_BROKEN_SYSCALL kill_broken_server();
@@ -5244,59 +5564,63 @@ inline void kill_broken_server()
/* Handle new connections and spawn new process to handle them */
#ifndef EMBEDDED_LIBRARY
-pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused)))
+
+void handle_connections_sockets()
{
- my_socket sock,new_sock;
+ my_socket UNINIT_VAR(sock), UNINIT_VAR(new_sock);
uint error_count=0;
- uint max_used_connection;
- fd_set readFDs,clientFDs;
THD *thd;
- struct sockaddr_in cAddr;
- int base_ip_flags=0, extra_ip_flags= 0, socket_flags=0, flags;
+ struct sockaddr_storage cAddr;
+ int ip_flags __attribute__((unused))=0;
+ int socket_flags __attribute__((unused))= 0;
+ int extra_ip_flags __attribute__((unused))=0;
+ int flags=0,retval;
st_vio *vio_tmp;
- DBUG_ENTER("handle_connections_sockets");
-
- max_used_connection= (uint) (max(base_ip_sock, unix_sock));
- max_used_connection= (uint) (max(extra_ip_sock, (int) max_used_connection));
- max_used_connection++;
-
- LINT_INIT(new_sock);
+#ifdef HAVE_POLL
+ int socket_count= 0;
+ struct pollfd fds[3]; // for ip_sock, unix_sock and extra_ip_sock
+#define setup_fds(X) \
+ fds[socket_count].fd= X; \
+ fds[socket_count].events= POLLIN; \
+ socket_count++
+#else
+ fd_set readFDs,clientFDs;
+ uint max_used_connection= (uint)
+ max(max(base_ip_sock, unix_sock), extra_ip_sock) + 1;
+#define setup_fds(X) FD_SET(X,&clientFDs)
+ FD_ZERO(&clientFDs);
+#endif
- (void) my_pthread_getprio(pthread_self()); // For debugging
+ DBUG_ENTER("handle_connections_sockets");
- FD_ZERO(&clientFDs);
if (base_ip_sock != INVALID_SOCKET)
{
- FD_SET(base_ip_sock, &clientFDs);
-#ifdef HAVE_FCNTL
- base_ip_flags = fcntl(base_ip_sock, F_GETFL, 0);
-#endif
+ setup_fds(base_ip_sock);
+ ip_flags = fcntl(base_ip_sock, F_GETFL, 0);
}
if (extra_ip_sock != INVALID_SOCKET)
{
- FD_SET(extra_ip_sock, &clientFDs);
-#ifdef HAVE_FCNTL
+ setup_fds(extra_ip_sock);
extra_ip_flags = fcntl(extra_ip_sock, F_GETFL, 0);
-#endif
}
-
#ifdef HAVE_SYS_UN_H
- FD_SET(unix_sock,&clientFDs);
-#ifdef HAVE_FCNTL
+ setup_fds(unix_sock);
socket_flags=fcntl(unix_sock, F_GETFL, 0);
#endif
-#endif
DBUG_PRINT("general",("Waiting for connections."));
MAYBE_BROKEN_SYSCALL;
while (!abort_loop)
{
- readFDs=clientFDs;
-#ifdef HPUX10
- if (select(max_used_connection,(int*) &readFDs,0,0,0) < 0)
- continue;
+#ifdef HAVE_POLL
+ retval= poll(fds, socket_count, -1);
#else
- if (select((int) max_used_connection,&readFDs,0,0,0) < 0)
+ readFDs=clientFDs;
+
+ retval= select((int) max_used_connection,&readFDs,0,0,0);
+#endif
+
+ if (retval < 0)
{
if (socket_errno != SOCKET_EINTR)
{
@@ -5306,7 +5630,7 @@ pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused)))
MAYBE_BROKEN_SYSCALL
continue;
}
-#endif /* HPUX10 */
+
if (abort_loop)
{
MAYBE_BROKEN_SYSCALL;
@@ -5314,26 +5638,34 @@ pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused)))
}
/* Is this a new connection request ? */
-#ifdef HAVE_SYS_UN_H
- if (FD_ISSET(unix_sock,&readFDs))
+#ifdef HAVE_POLL
+ for (int i= 0; i < socket_count; ++i)
{
- sock= unix_sock;
- flags= socket_flags;
+ if (fds[i].revents & POLLIN)
+ {
+ sock= fds[i].fd;
+ flags= fcntl(sock, F_GETFL, 0);
+ break;
+ }
+ }
+#else // HAVE_POLL
+ if (FD_ISSET(base_ip_sock,&readFDs))
+ {
+ sock= base_ip_sock;
+ flags= ip_flags;
}
else
-#endif
+ if (FD_ISSET(extra_ip_sock,&readFDs))
{
- if (FD_ISSET(base_ip_sock,&readFDs))
- {
- sock= base_ip_sock;
- flags= base_ip_flags;
- }
- else
- {
- sock= extra_ip_sock;
- flags= extra_ip_flags;
- }
+ sock= extra_ip_sock;
+ flags= extra_ip_flags;
}
+ else
+ {
+ sock = unix_sock;
+ flags= socket_flags;
+ }
+#endif // HAVE_POLL
#if !defined(NO_FCNTL_NONBLOCK)
if (!(test_flags & TEST_BLOCKING))
@@ -5347,16 +5679,9 @@ pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused)))
#endif /* NO_FCNTL_NONBLOCK */
for (uint retry=0; retry < MAX_ACCEPT_RETRY; retry++)
{
- size_socket length=sizeof(struct sockaddr_in);
- new_sock = accept(sock, my_reinterpret_cast(struct sockaddr *) (&cAddr),
- &length);
-#ifdef __NETWARE__
- // TODO: temporary fix, waiting for TCP/IP fix - DEFECT000303149
- if ((new_sock == INVALID_SOCKET) && (socket_errno == EINVAL))
- {
- kill_server(SIGTERM);
- }
-#endif
+ size_socket length= sizeof(struct sockaddr_storage);
+ new_sock= accept(sock, (struct sockaddr *)(&cAddr),
+ &length);
if (new_sock != INVALID_SOCKET ||
(socket_errno != SOCKET_EINTR && socket_errno != SOCKET_EAGAIN))
break;
@@ -5420,9 +5745,10 @@ pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused)))
{
size_socket dummyLen;
- struct sockaddr dummy;
- dummyLen = sizeof(struct sockaddr);
- if (getsockname(new_sock,&dummy, &dummyLen) < 0)
+ struct sockaddr_storage dummy;
+ dummyLen = sizeof(dummy);
+ if ( getsockname(new_sock,(struct sockaddr *)&dummy,
+ (SOCKET_SIZE_TYPE *)&dummyLen) < 0 )
{
sql_perror("Error on new connection socket");
(void) mysql_socket_shutdown(new_sock, SHUT_RDWR);
@@ -5438,7 +5764,7 @@ pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused)))
if (!(thd= new THD))
{
(void) mysql_socket_shutdown(new_sock, SHUT_RDWR);
- VOID(closesocket(new_sock));
+ (void) closesocket(new_sock);
continue;
}
if (!(vio_tmp=vio_new(new_sock,
@@ -5468,17 +5794,23 @@ pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused)))
if (sock == extra_ip_sock)
{
thd->extra_port= 1;
- thd->scheduler= &extra_thread_scheduler;
+ thd->scheduler= extra_thread_scheduler;
}
create_new_thread(thd);
}
- DBUG_LEAVE;
+ DBUG_VOID_RETURN;
+}
+
+
+#ifdef _WIN32
+pthread_handler_t handle_connections_sockets_thread(void *arg)
+{
+ my_thread_init();
+ handle_connections_sockets();
decrement_handler_count();
return 0;
}
-
-#ifdef __NT__
pthread_handler_t handle_connections_namedpipes(void *arg)
{
HANDLE hConnectedPipe;
@@ -5561,7 +5893,7 @@ pthread_handler_t handle_connections_namedpipes(void *arg)
if (!(thd->net.vio= vio_new_win32pipe(hConnectedPipe)) ||
my_net_init(&thd->net, thd->net.vio))
{
- close_connection(thd, ER_OUT_OF_RESOURCES, 1);
+ close_connection(thd, ER_OUT_OF_RESOURCES);
delete thd;
continue;
}
@@ -5574,7 +5906,7 @@ pthread_handler_t handle_connections_namedpipes(void *arg)
decrement_handler_count();
return 0;
}
-#endif /* __NT__ */
+#endif /* _WIN32 */
#ifdef HAVE_SMEM
@@ -5756,7 +6088,7 @@ pthread_handler_t handle_connections_shared_memory(void *arg)
event_conn_closed)) ||
my_net_init(&thd->net, thd->net.vio))
{
- close_connection(thd, ER_OUT_OF_RESOURCES, 1);
+ close_connection(thd, ER_OUT_OF_RESOURCES);
errmsg= 0;
goto errorconn;
}
@@ -5794,7 +6126,7 @@ errorconn:
/* End shared memory handling */
error:
if (tmp)
- my_free(tmp, MYF(0));
+ my_free(tmp);
if (errmsg)
{
@@ -5820,230 +6152,18 @@ error:
Handle start options
******************************************************************************/
-enum options_mysqld
-{
- OPT_ISAM_LOG=256,
- OPT_SKIP_GRANT, OPT_SKIP_LOCK,
- OPT_ENABLE_LOCK, OPT_USE_LOCKING,
- OPT_SOCKET, OPT_UPDATE_LOG,
- OPT_EXTRA_PORT,
- OPT_BIN_LOG, OPT_SKIP_RESOLVE,
- OPT_SKIP_NETWORKING, OPT_BIN_LOG_INDEX,
- OPT_BIND_ADDRESS, OPT_PID_FILE,
- OPT_SKIP_PRIOR, OPT_BIG_TABLES,
- OPT_STANDALONE, OPT_ONE_THREAD,
- OPT_CONSOLE, OPT_LOW_PRIORITY_UPDATES,
- OPT_SKIP_HOST_CACHE, OPT_SHORT_LOG_FORMAT,
- OPT_FLUSH, OPT_SAFE,
- OPT_BOOTSTRAP, OPT_SKIP_SHOW_DB,
- OPT_STORAGE_ENGINE, OPT_INIT_FILE,
- OPT_DELAY_KEY_WRITE_ALL, OPT_SLOW_QUERY_LOG,
- OPT_DELAY_KEY_WRITE, OPT_CHARSETS_DIR,
- OPT_MASTER_HOST, OPT_MASTER_USER,
- OPT_MASTER_PASSWORD, OPT_MASTER_PORT,
- OPT_MASTER_INFO_FILE, OPT_MASTER_CONNECT_RETRY,
- OPT_MASTER_RETRY_COUNT, OPT_LOG_TC, OPT_LOG_TC_SIZE,
- OPT_MASTER_SSL, OPT_MASTER_SSL_KEY,
- OPT_MASTER_SSL_CERT, OPT_MASTER_SSL_CAPATH,
- OPT_MASTER_SSL_CIPHER, OPT_MASTER_SSL_CA,
- OPT_SQL_BIN_UPDATE_SAME, OPT_REPLICATE_DO_DB,
- OPT_REPLICATE_IGNORE_DB, OPT_LOG_SLAVE_UPDATES,
- OPT_BINLOG_DO_DB, OPT_BINLOG_IGNORE_DB,
- OPT_BINLOG_FORMAT, OPT_DEBUG_BINLOG_FSYNC_SLEEP,
- OPT_BINLOG_ANNOTATE_ROWS_EVENTS,
- OPT_REPLICATE_ANNOTATE_ROWS_EVENTS,
-#ifndef DBUG_OFF
- OPT_BINLOG_SHOW_XID,
-#endif
- OPT_BINLOG_ROWS_EVENT_MAX_SIZE,
- OPT_WANT_CORE, OPT_CONCURRENT_INSERT,
- OPT_MEMLOCK, OPT_MYISAM_RECOVER,
- OPT_REPLICATE_REWRITE_DB, OPT_SERVER_ID,
- OPT_SKIP_SLAVE_START, OPT_SAFE_SHOW_DB,
- OPT_SAFEMALLOC_MEM_LIMIT, OPT_REPLICATE_DO_TABLE,
- OPT_REPLICATE_IGNORE_TABLE, OPT_REPLICATE_WILD_DO_TABLE,
- OPT_REPLICATE_WILD_IGNORE_TABLE, OPT_REPLICATE_SAME_SERVER_ID,
- OPT_DISCONNECT_SLAVE_EVENT_COUNT, OPT_TC_HEURISTIC_RECOVER,
- OPT_ABORT_SLAVE_EVENT_COUNT,
- OPT_LOG_BIN_TRUST_FUNCTION_CREATORS,
- OPT_LOG_BIN_TRUST_FUNCTION_CREATORS_OLD,
- OPT_ENGINE_CONDITION_PUSHDOWN, OPT_NDB_CONNECTSTRING,
- OPT_NDB_USE_EXACT_COUNT, OPT_NDB_USE_TRANSACTIONS,
- OPT_NDB_FORCE_SEND, OPT_NDB_AUTOINCREMENT_PREFETCH_SZ,
- OPT_NDB_SHM, OPT_NDB_OPTIMIZED_NODE_SELECTION, OPT_NDB_CACHE_CHECK_TIME,
- OPT_NDB_MGMD, OPT_NDB_NODEID,
- OPT_NDB_DISTRIBUTION,
- OPT_NDB_INDEX_STAT_ENABLE,
- OPT_NDB_EXTRA_LOGGING,
- OPT_NDB_REPORT_THRESH_BINLOG_EPOCH_SLIP,
- OPT_NDB_REPORT_THRESH_BINLOG_MEM_USAGE,
- OPT_NDB_USE_COPYING_ALTER_TABLE,
- OPT_SAFEMALLOC, OPT_MUTEX_DEADLOCK_DETECTOR,
- OPT_TEMP_POOL, OPT_TX_ISOLATION, OPT_COMPLETION_TYPE,
- OPT_SKIP_SYMLINKS,
- OPT_MAX_BINLOG_DUMP_EVENTS, OPT_SPORADIC_BINLOG_DUMP_FAIL,
- OPT_SAFE_USER_CREATE, OPT_SQL_MODE,
- OPT_HAVE_NAMED_PIPE,
- OPT_DO_PSTACK, OPT_EVENT_SCHEDULER, OPT_REPORT_HOST,
- OPT_REPORT_USER, OPT_REPORT_PASSWORD, OPT_REPORT_PORT,
- OPT_SHOW_SLAVE_AUTH_INFO,
- OPT_SLAVE_LOAD_TMPDIR, OPT_NO_MIX_TYPE,
- OPT_RPL_RECOVERY_RANK,OPT_INIT_RPL_ROLE,
- OPT_RELAY_LOG, OPT_RELAY_LOG_INDEX, OPT_RELAY_LOG_INFO_FILE,
- OPT_SLAVE_SKIP_ERRORS, OPT_DES_KEY_FILE, OPT_LOCAL_INFILE,
- OPT_SSL_SSL, OPT_SSL_KEY, OPT_SSL_CERT, OPT_SSL_CA,
- OPT_SSL_CAPATH, OPT_SSL_CIPHER,
- OPT_BACK_LOG, OPT_BINLOG_CACHE_SIZE,
- OPT_CONNECT_TIMEOUT, OPT_DELAYED_INSERT_TIMEOUT,
- OPT_DELAYED_INSERT_LIMIT, OPT_DELAYED_QUEUE_SIZE,
- OPT_FLUSH_TIME, OPT_FT_MIN_WORD_LEN, OPT_FT_BOOLEAN_SYNTAX,
- OPT_FT_MAX_WORD_LEN, OPT_FT_QUERY_EXPANSION_LIMIT, OPT_FT_STOPWORD_FILE,
- OPT_INTERACTIVE_TIMEOUT, OPT_JOIN_BUFF_SIZE,
- OPT_JOIN_BUFF_SPACE_LIMIT, OPT_JOIN_CACHE_LEVEL,
- OPT_KEY_BUFFER_SIZE, OPT_KEY_CACHE_BLOCK_SIZE,
- OPT_KEY_CACHE_DIVISION_LIMIT, OPT_KEY_CACHE_AGE_THRESHOLD,
- OPT_KEY_CACHE_PARTITIONS,
- OPT_LONG_QUERY_TIME,
- OPT_LOWER_CASE_TABLE_NAMES, OPT_MAX_ALLOWED_PACKET,
- OPT_SLAVE_MAX_ALLOWED_PACKET,
- OPT_MAX_BINLOG_CACHE_SIZE, OPT_MAX_BINLOG_SIZE,
- OPT_MAX_CONNECTIONS, OPT_MAX_CONNECT_ERRORS,
- OPT_MAX_DELAYED_THREADS, OPT_MAX_HEP_TABLE_SIZE,
- OPT_MAX_JOIN_SIZE, OPT_MAX_PREPARED_STMT_COUNT,
- OPT_MAX_RELAY_LOG_SIZE, OPT_MAX_SORT_LENGTH,
- OPT_MAX_SEEKS_FOR_KEY, OPT_MAX_TMP_TABLES, OPT_MAX_USER_CONNECTIONS,
- OPT_MAX_LENGTH_FOR_SORT_DATA,
- OPT_MAX_WRITE_LOCK_COUNT, OPT_BULK_INSERT_BUFFER_SIZE,
- OPT_MAX_ERROR_COUNT, OPT_MRR_BUFFER_SIZE, OPT_MYISAM_DATA_POINTER_SIZE,
-
- OPT_MYISAM_BLOCK_SIZE, OPT_MYISAM_MAX_EXTRA_SORT_FILE_SIZE,
- OPT_MYISAM_MAX_SORT_FILE_SIZE, OPT_MYISAM_SORT_BUFFER_SIZE,
- OPT_MYISAM_USE_MMAP, OPT_MYISAM_REPAIR_THREADS,
- OPT_MYISAM_MMAP_SIZE,
- OPT_MYISAM_STATS_METHOD,
-
- OPT_PAGECACHE_BUFFER_SIZE,
- OPT_PAGECACHE_DIVISION_LIMIT, OPT_PAGECACHE_AGE_THRESHOLD,
-
- OPT_NET_BUFFER_LENGTH, OPT_NET_RETRY_COUNT,
- OPT_NET_READ_TIMEOUT, OPT_NET_WRITE_TIMEOUT,
- OPT_OPEN_FILES_LIMIT,
- OPT_PRELOAD_BUFFER_SIZE,
- OPT_QUERY_CACHE_LIMIT, OPT_QUERY_CACHE_MIN_RES_UNIT, OPT_QUERY_CACHE_SIZE,
- OPT_QUERY_CACHE_TYPE, OPT_QUERY_CACHE_WLOCK_INVALIDATE, OPT_RECORD_BUFFER,
- OPT_RECORD_RND_BUFFER, OPT_DIV_PRECINCREMENT, OPT_RELAY_LOG_SPACE_LIMIT,
- OPT_RELAY_LOG_PURGE,
- OPT_SLAVE_NET_TIMEOUT, OPT_SLAVE_COMPRESSED_PROTOCOL, OPT_SLOW_LAUNCH_TIME,
- OPT_SLAVE_TRANS_RETRIES,
- OPT_SUBQUERY_CACHE,
- OPT_READONLY, OPT_ROWID_MERGE_BUFF_SIZE,
- OPT_DEBUGGING, OPT_DEBUG_FLUSH,
- OPT_SORT_BUFFER, OPT_TABLE_OPEN_CACHE, OPT_TABLE_DEF_CACHE,
- OPT_THREAD_CONCURRENCY, OPT_THREAD_CACHE_SIZE,
- OPT_TMP_TABLE_SIZE, OPT_THREAD_STACK,
- OPT_WAIT_TIMEOUT, OPT_PROGRESS_REPORT_TIME,
- OPT_ERROR_LOG_FILE,
- OPT_DEFAULT_WEEK_FORMAT,
- OPT_RANGE_ALLOC_BLOCK_SIZE, OPT_ALLOW_SUSPICIOUS_UDFS,
- OPT_QUERY_ALLOC_BLOCK_SIZE, OPT_QUERY_PREALLOC_SIZE,
- OPT_TRANS_ALLOC_BLOCK_SIZE, OPT_TRANS_PREALLOC_SIZE,
- OPT_SYNC_FRM, OPT_SYNC_BINLOG, OPT_SYNC, OPT_THREAD_ALARM,
- OPT_SYNC_REPLICATION,
- OPT_SYNC_REPLICATION_SLAVE_ID,
- OPT_SYNC_REPLICATION_TIMEOUT,
- OPT_ENABLE_SHARED_MEMORY,
- OPT_SHARED_MEMORY_BASE_NAME,
- OPT_OLD_PASSWORDS,
- OPT_OLD_ALTER_TABLE,
- OPT_EXPIRE_LOGS_DAYS,
- OPT_GROUP_CONCAT_MAX_LEN,
- OPT_DEFAULT_COLLATION,
- OPT_DEFAULT_COLLATION_OLD,
- OPT_CHARACTER_SET_CLIENT_HANDSHAKE,
- OPT_CHARACTER_SET_FILESYSTEM,
- OPT_LC_TIME_NAMES,
- OPT_INIT_CONNECT,
- OPT_INIT_SLAVE,
- OPT_SECURE_AUTH,
- OPT_DATE_FORMAT,
- OPT_TIME_FORMAT,
- OPT_DATETIME_FORMAT,
- OPT_LOG_QUERIES_NOT_USING_INDEXES,
- OPT_DEFAULT_TIME_ZONE,
- OPT_SYSDATE_IS_NOW,
- OPT_OPTIMIZER_SEARCH_DEPTH,
- OPT_OPTIMIZER_PRUNE_LEVEL,
- OPT_OPTIMIZER_SWITCH,
- OPT_UPDATABLE_VIEWS_WITH_LIMIT,
- OPT_SP_AUTOMATIC_PRIVILEGES,
- OPT_MAX_SP_RECURSION_DEPTH,
- OPT_AUTO_INCREMENT, OPT_AUTO_INCREMENT_OFFSET,
- OPT_ENABLE_LARGE_PAGES,
- OPT_TIMED_MUTEXES,
- OPT_OLD_STYLE_USER_LIMITS,
- OPT_LOG_SLOW_ADMIN_STATEMENTS,
- OPT_TABLE_LOCK_WAIT_TIMEOUT,
- OPT_PLUGIN_LOAD,
- OPT_PLUGIN_DIR,
- OPT_PLUGIN_MATURITY,
- OPT_SYMBOLIC_LINKS,
- OPT_WARNINGS,
- OPT_RECORD_BUFFER_OLD,
- OPT_LOG_OUTPUT,
- OPT_PORT_OPEN_TIMEOUT,
- OPT_PROFILING,
- OPT_KEEP_FILES_ON_CREATE,
- OPT_GENERAL_LOG,
- OPT_SLOW_LOG, OPT_LOG_BASENAME,
- OPT_THREAD_HANDLING,
- OPT_INNODB_ROLLBACK_ON_TIMEOUT,
- OPT_SECURE_FILE_PRIV,
- OPT_MIN_EXAMINED_ROW_LIMIT,
- OPT_LOG_SLOW_SLAVE_STATEMENTS,
- OPT_DEBUG_CRC, OPT_DEBUG_ON, OPT_DEBUG_ASSERT_IF_CRASHED_TABLE,
- OPT_DEBUG_ASSERT_ON_ERROR,
- OPT_OLD_MODE,
- OPT_TEST_IGNORE_WRONG_OPTIONS, OPT_TEST_RESTART,
-#if defined(ENABLED_DEBUG_SYNC)
- OPT_DEBUG_SYNC_TIMEOUT,
-#endif /* defined(ENABLED_DEBUG_SYNC) */
- OPT_DEPRECATED_OPTION,
- OPT_SLAVE_EXEC_MODE,
- OPT_DEADLOCK_SEARCH_DEPTH_SHORT,
- OPT_DEADLOCK_SEARCH_DEPTH_LONG,
- OPT_DEADLOCK_TIMEOUT_SHORT,
- OPT_DEADLOCK_TIMEOUT_LONG,
- OPT_LOG_SLOW_RATE_LIMIT,
- OPT_LOG_SLOW_VERBOSITY,
- OPT_LOG_SLOW_FILTER,
- OPT_USERSTAT,
- OPT_GENERAL_LOG_FILE,
- OPT_SLOW_QUERY_LOG_FILE,
- OPT_IGNORE_BUILTIN_INNODB,
- OPT_BINLOG_DIRECT_NON_TRANS_UPDATE,
- OPT_DEFAULT_CHARACTER_SET_OLD,
- OPT_MAX_LONG_DATA_SIZE,
- OPT_MASTER_VERIFY_CHECKSUM,
- OPT_SLAVE_SQL_VERIFY_CHECKSUM,
- OPT_QUERY_CACHE_STRIP_COMMENTS,
- OPT_IGNORE_DB_DIRECTORY
-};
-
-
-#define LONG_TIMEOUT ((ulong) 3600L*24L*365L)
+/**
+ System variables are automatically command-line options (few
+ exceptions are documented in sys_var.h), so don't need
+ to be listed here.
+*/
-struct my_option my_long_options[] =
+struct my_option my_long_options[]=
{
{"help", '?', "Display this help and exit.",
&opt_help, &opt_help, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
0, 0},
-#ifdef HAVE_REPLICATION
- {"abort-slave-event-count", OPT_ABORT_SLAVE_EVENT_COUNT,
- "Option used by mysql-test for debugging and testing of replication.",
- &abort_slave_event_count, &abort_slave_event_count,
- 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
-#endif /* HAVE_REPLICATION */
- {"allow-suspicious-udfs", OPT_ALLOW_SUSPICIOUS_UDFS,
+ {"allow-suspicious-udfs", 0,
"Allows use of UDFs consisting of only one symbol xxx() "
"without corresponding xxx_init() or xxx_deinit(). That also means "
"that one can load any function from any library, for example exit() "
@@ -6053,61 +6173,16 @@ struct my_option my_long_options[] =
{"ansi", 'a', "Use ANSI SQL syntax instead of MySQL syntax. This mode "
"will also set transaction isolation level 'serializable'.", 0, 0, 0,
GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"auto-increment-increment", OPT_AUTO_INCREMENT,
- "Auto-increment columns are incremented by this.",
- &global_system_variables.auto_increment_increment,
- &max_system_variables.auto_increment_increment, 0, GET_ULONG,
- OPT_ARG, 1, 1, 65535, 0, 1, 0 },
- {"auto-increment-offset", OPT_AUTO_INCREMENT_OFFSET,
- "Offset added to Auto-increment columns. Used when auto-increment-increment != 1.",
- &global_system_variables.auto_increment_offset,
- &max_system_variables.auto_increment_offset, 0, GET_ULONG, OPT_ARG,
- 1, 1, 65535, 0, 1, 0 },
- {"automatic-sp-privileges", OPT_SP_AUTOMATIC_PRIVILEGES,
- "Creating and dropping stored procedures alters ACLs. Disable with --skip-automatic-sp-privileges.",
- &sp_automatic_privileges, &sp_automatic_privileges,
- 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
- {"basedir", 'b',
- "Path to installation directory. All paths are usually resolved relative to this.",
- &mysql_home_ptr, &mysql_home_ptr, 0, GET_STR, REQUIRED_ARG,
- 0, 0, 0, 0, 0, 0},
- {"big-tables", OPT_BIG_TABLES,
- "Allow big result sets by saving all temporary sets on file (solves most 'table full' errors).",
- 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ /*
+ Because Sys_var_bit does not support command-line options, we need to
+ explicitely add one for --autocommit
+ */
+ {"autocommit", 0, "Set default value for autocommit (0 or 1)",
+ &opt_autocommit, &opt_autocommit, 0,
+ GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, NULL},
{"bind-address", OPT_BIND_ADDRESS, "IP address to bind to.",
&my_bind_addr_str, &my_bind_addr_str, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
-#ifndef DBUG_OFF
- {"debug-binlog-fsync-sleep", OPT_DEBUG_BINLOG_FSYNC_SLEEP,
- "Extra sleep (in microseconds) to add to binlog fsync(), for debugging",
- &opt_binlog_dbug_fsync_sleep, &opt_binlog_dbug_fsync_sleep,
- 0, GET_ULONG, REQUIRED_ARG, 0, 0, ULONG_MAX, 0, 1, 0},
-#endif
- {"binlog_format", OPT_BINLOG_FORMAT,
- "Does not have any effect without '--log-bin'. "
- "Tell the master the form of binary logging to use: either 'row' for "
- "row-based binary logging, 'statement' for statement-based binary "
- "logging, or 'mixed'. 'mixed' is statement-based binary logging except "
- "for statements where only row-based is correct: Statements that involve "
- "user-defined functions (i.e., UDFs) or the UUID() function."
-#ifdef HAVE_NDB_BINLOG
- "If ndbcluster is enabled and binlog_format is `mixed', the format switches"
- " to 'row' and back implicitly per each query accessing a NDB table."
-#endif
- , &opt_binlog_format, &opt_binlog_format,
- 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"binlog-annotate-row-events", OPT_BINLOG_ANNOTATE_ROWS_EVENTS,
- "Tells the master to annotate RBR events with the statement that "
- "caused these events.",
- (uchar**) &global_system_variables.binlog_annotate_row_events,
- (uchar**) &max_system_variables.binlog_annotate_row_events,
- 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"replicate-annotate-row-events", OPT_REPLICATE_ANNOTATE_ROWS_EVENTS,
- "Tells the slave to write annotate rows events recieved from the master "
- "to its own binary log. Sensible only in pair with log-slave-updates option.",
- (uchar**) &opt_replicate_annotate_row_events,
- (uchar**) &opt_replicate_annotate_row_events,
- 0, GET_BOOL, NO_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.",
@@ -6115,7 +6190,7 @@ struct my_option my_long_options[] =
{"binlog-ignore-db", OPT_BINLOG_IGNORE_DB,
"Tells the master that updates to the given database should not be logged to the binary log.",
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"binlog-row-event-max-size", OPT_BINLOG_ROWS_EVENT_MAX_SIZE,
+ {"binlog-row-event-max-size", 0,
"The maximum size of a row-based binary log event in bytes. Rows will be "
"grouped into events smaller than this size if possible. "
"The value has to be a multiple of 256.",
@@ -6129,12 +6204,12 @@ struct my_option my_long_options[] =
{"bootstrap", OPT_BOOTSTRAP, "Used by mysql installation scripts.", 0, 0, 0,
GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
#endif
- {"character-set-client-handshake", OPT_CHARACTER_SET_CLIENT_HANDSHAKE,
+ {"character-set-client-handshake", 0,
"Don't ignore client side character set value sent during handshake.",
&opt_character_set_client_handshake,
&opt_character_set_client_handshake,
0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
- {"character-set-filesystem", OPT_CHARACTER_SET_FILESYSTEM,
+ {"character-set-filesystem", 0,
"Set the filesystem character set.",
&character_set_filesystem_name,
&character_set_filesystem_name,
@@ -6142,592 +6217,217 @@ struct my_option my_long_options[] =
{"character-set-server", 'C', "Set the default character set.",
&default_character_set_name, &default_character_set_name,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
- {"character-sets-dir", OPT_CHARSETS_DIR,
- "Directory where character sets are.", &charsets_dir,
- &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"chroot", 'r', "Chroot mysqld daemon during startup.",
&mysqld_chroot, &mysqld_chroot, 0, GET_STR, REQUIRED_ARG,
0, 0, 0, 0, 0, 0},
- {"collation-server", OPT_DEFAULT_COLLATION, "Set the default collation.",
+ {"collation-server", 0, "Set the default collation.",
&default_collation_name, &default_collation_name,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
- {"completion-type", OPT_COMPLETION_TYPE, "Default completion type.",
- &global_system_variables.completion_type,
- &max_system_variables.completion_type, 0, GET_ULONG,
- REQUIRED_ARG, 0, 0, 2, 0, 1, 0},
- {"concurrent-insert", OPT_CONCURRENT_INSERT,
- "Use concurrent insert with MyISAM. Disable with --concurrent-insert=0.",
- &myisam_concurrent_insert, &myisam_concurrent_insert,
- 0, GET_ULONG, OPT_ARG, 1, 0, 2, 0, 0, 0},
{"console", OPT_CONSOLE, "Write error output on screen; don't remove the console window on windows.",
&opt_console, &opt_console, 0, GET_BOOL, NO_ARG, 0, 0, 0,
0, 0, 0},
{"core-file", OPT_WANT_CORE, "Write core on errors.", 0, 0, 0, GET_NO_ARG,
NO_ARG, 0, 0, 0, 0, 0, 0},
- {"datadir", 'h', "Path to the database root.", &mysql_data_home,
- &mysql_data_home, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"deadlock-search-depth-short", OPT_DEADLOCK_SEARCH_DEPTH_SHORT,
- "Short search depth for the two-step deadlock detection",
- &global_system_variables.wt_deadlock_search_depth_short,
- &max_system_variables.wt_deadlock_search_depth_short,
- 0, GET_ULONG, REQUIRED_ARG, 4, 0, 32, 0, 0, 0},
- {"deadlock-search-depth-long", OPT_DEADLOCK_SEARCH_DEPTH_LONG,
- "Long search depth for the two-step deadlock detection",
- &global_system_variables.wt_deadlock_search_depth_long,
- &max_system_variables.wt_deadlock_search_depth_long,
- 0, GET_ULONG, REQUIRED_ARG, 15, 0, 33, 0, 0, 0},
- {"deadlock-timeout-short", OPT_DEADLOCK_TIMEOUT_SHORT,
- "Short timeout for the two-step deadlock detection (in microseconds)",
- &global_system_variables.wt_timeout_short,
- &max_system_variables.wt_timeout_short,
- 0, GET_ULONG, REQUIRED_ARG, 10000, 0, ULONG_MAX, 0, 0, 0},
- {"deadlock-timeout-long", OPT_DEADLOCK_TIMEOUT_LONG,
- "Long timeout for the two-step deadlock detection (in microseconds)",
- &global_system_variables.wt_timeout_long,
- &max_system_variables.wt_timeout_long,
- 0, GET_ULONG, REQUIRED_ARG, 50000000, 0, ULONG_MAX, 0, 0, 0},
+ /* default-storage-engine should have "MyISAM" as def_value. Instead
+ of initializing it here it is done in init_common_variables() due
+ to a compiler bug in Sun Studio compiler. */
+#ifdef DBUG_OFF
+ {"debug", '#', "Built in DBUG debugger. Disabled in this build.",
+ &current_dbug_option, &current_dbug_option, 0, GET_STR, OPT_ARG,
+ 0, 0, 0, 0, 0, 0},
+#endif
+#ifdef HAVE_REPLICATION
+ {"debug-abort-slave-event-count", 0,
+ "Option used by mysql-test for debugging and testing of replication.",
+ &abort_slave_event_count, &abort_slave_event_count,
+ 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+#endif /* HAVE_REPLICATION */
#ifndef DBUG_OFF
- {"debug", '#', "Debug log.", &current_dbug_option,
- &current_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
- {"debug-crc-break", OPT_DEBUG_CRC,
- "Call my_debug_put_break_here() if crc matches this number (for debug).",
- &opt_my_crc_dbug_check, &opt_my_crc_dbug_check,
- 0, GET_ULONG, REQUIRED_ARG, 0, 0, ~(ulonglong) 0, 0, 0, 0},
- {"debug-flush", OPT_DEBUG_FLUSH, "Default debug log with flush after write",
- 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"debug-assert-if-crashed-table", OPT_DEBUG_ASSERT_IF_CRASHED_TABLE,
+ {"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,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"debug-assert-if-crashed-table", 0,
"Do an assert in handler::print_error() if we get a crashed table",
&debug_assert_if_crashed_table, &debug_assert_if_crashed_table,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"debug-assert-on-error", OPT_DEBUG_ASSERT_ON_ERROR,
- "Do an assert in various functions if we get a fatal error",
- &my_assert_on_error, &my_assert_on_error,
+#endif
+#ifdef HAVE_REPLICATION
+ {"debug-disconnect-slave-event-count", 0,
+ "Option used by mysql-test for debugging and testing of replication.",
+ &disconnect_slave_event_count, &disconnect_slave_event_count,
+ 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+#endif /* HAVE_REPLICATION */
+ {"debug-exit-info", 'T', "Used for debugging. Use at your own risk.",
+ 0, 0, 0, GET_LONG, OPT_ARG, 0, 0, 0, 0, 0, 0},
+ {"debug-gdb", 0,
+ "Set up signals usable for debugging.",
+ &opt_debugging, &opt_debugging,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+#ifdef HAVE_REPLICATION
+ {"debug-max-binlog-dump-events", 0,
+ "Option used by mysql-test for debugging and testing of replication.",
+ &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
- {"default-character-set", OPT_DEFAULT_CHARACTER_SET_OLD,
- "Set the default character set (deprecated option, use --character-set-server instead).",
- &default_character_set_name, &default_character_set_name,
- 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
- {"default-collation", OPT_DEFAULT_COLLATION_OLD, "Set the default collation "
- "(deprecated option, use --collation-server instead).",
- &default_collation_name, &default_collation_name,
- 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
- {"default-storage-engine", OPT_STORAGE_ENGINE,
- "Set the default storage engine (table type) for tables.",
- &default_storage_engine_str, &default_storage_engine_str,
- 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"default-table-type", OPT_STORAGE_ENGINE,
- "(deprecated) Use --default-storage-engine.",
- &default_storage_engine_str, &default_storage_engine_str,
- 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"default-time-zone", OPT_DEFAULT_TIME_ZONE, "Set the default time zone.",
+ {"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},
+#ifdef HAVE_REPLICATION
+ {"debug-sporadic-binlog-dump-fail", 0,
+ "Option used by mysql-test for debugging and testing of replication.",
+ &opt_sporadic_binlog_dump_fail,
+ &opt_sporadic_binlog_dump_fail, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
+ 0},
+#endif /* HAVE_REPLICATION */
+ {"default-storage-engine", 0, "The default storage engine for new tables",
+ &default_storage_engine, 0, 0, GET_STR, REQUIRED_ARG,
+ 0, 0, 0, 0, 0, 0 },
+ {"default-time-zone", 0, "Set the default time zone.",
&default_tz_name, &default_tz_name,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
- {"delay-key-write", OPT_DELAY_KEY_WRITE, "Type of DELAY_KEY_WRITE.",
- 0,0,0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
- {"delay-key-write-for-all-tables", OPT_DELAY_KEY_WRITE_ALL,
- "Don't flush key buffers between writes for any MyISAM table. "
- "(Deprecated option, use --delay-key-write=all instead.)",
- 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+#if defined(ENABLED_DEBUG_SYNC)
+ {"debug-sync-timeout", OPT_DEBUG_SYNC_TIMEOUT,
+ "Enable the debug sync facility "
+ "and optionally specify a default wait timeout in seconds. "
+ "A zero value keeps the facility disabled.",
+ &opt_debug_sync_timeout, 0,
+ 0, GET_UINT, OPT_ARG, 0, 0, UINT_MAX, 0, 0, 0},
+#endif /* defined(ENABLED_DEBUG_SYNC) */
#ifdef HAVE_OPENSSL
- {"des-key-file", OPT_DES_KEY_FILE,
+ {"des-key-file", 0,
"Load keys for des_encrypt() and des_encrypt from given file.",
&des_key_file, &des_key_file, 0, GET_STR, REQUIRED_ARG,
0, 0, 0, 0, 0, 0},
#endif /* HAVE_OPENSSL */
-#ifdef HAVE_REPLICATION
- {"disconnect-slave-event-count", OPT_DISCONNECT_SLAVE_EVENT_COUNT,
- "Option used by mysql-test for debugging and testing of replication.",
- &disconnect_slave_event_count, &disconnect_slave_event_count,
- 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
-#endif /* HAVE_REPLICATION */
- {"enable-locking", OPT_ENABLE_LOCK,
- "Deprecated option, use --external-locking instead.",
- &opt_external_locking, &opt_external_locking,
- 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
-#ifdef __NT__
- {"enable-named-pipe", OPT_HAVE_NAMED_PIPE, "Enable the named pipe (NT).",
- &opt_enable_named_pipe, &opt_enable_named_pipe, 0, GET_BOOL,
- NO_ARG, 0, 0, 0, 0, 0, 0},
-#endif
#ifdef HAVE_STACKTRACE
- {"stack-trace", OPT_DO_PSTACK, "Print a symbolic stack trace on failure. "
- "On by default. Disable with --disable-stack-trace.",
+ {"stack-trace", 0 , "Print a symbolic stack trace on failure",
&opt_stack_trace, &opt_stack_trace, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
#endif /* HAVE_STACKTRACE */
- {"engine-condition-pushdown",
- OPT_ENGINE_CONDITION_PUSHDOWN,
- "Push supported query conditions to the storage engine.",
- &global_system_variables.engine_condition_pushdown,
- &global_system_variables.engine_condition_pushdown,
- 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
- /* See how it's handled in get_one_option() */
- {"event-scheduler", OPT_EVENT_SCHEDULER, "Enable/disable the event scheduler.",
- NULL, NULL, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
- {"exit-info", 'T', "Used for debugging. Use at your own risk.", 0, 0, 0,
- GET_LONG, OPT_ARG, 0, 0, 0, 0, 0, 0},
- {"external-locking", OPT_USE_LOCKING, "Use system (external) locking "
- "(disabled by default). With this option enabled you can run myisamchk "
- "to test (not repair) tables while the MySQL server is running. "
- "Disable with --skip-external-locking.",
- &opt_external_locking, &opt_external_locking,
+ {"external-locking", 0, "Use system (external) locking (disabled by "
+ "default). With this option enabled you can run myisamchk to test "
+ "(not repair) tables while the MySQL server is running. Disable with "
+ "--skip-external-locking.", &opt_external_locking, &opt_external_locking,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"flush", OPT_FLUSH, "Flush tables to disk between SQL commands.", 0, 0, 0,
- GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
/* We must always support the next option to make scripts like mysqltest
easier to do */
- {"extra-port", OPT_EXTRA_PORT,
- "Extra port number to use for tcp-connections in a one-thread-per-connection manner. 0 means don't use another port",
- &mysqld_extra_port,
- &mysqld_extra_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"extra-max-connections", OPT_MAX_CONNECTIONS,
- "The number of connections on 'extra-port.",
- &extra_max_connections,
- &extra_max_connections, 0, GET_ULONG, REQUIRED_ARG, 1, 1, 100000,
- 0, 1, 0},
- {"gdb", OPT_DEBUGGING,
- "Set up signals usable for debugging.",
+ {"gdb", 0,
+ "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},
- {"general_log", OPT_GENERAL_LOG,
- "Enable/disable general log. Filename can be specified with --general-log-file or --log-basename. Is 'hostname.log' by default.",
- &opt_log, &opt_log, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
-#ifdef HAVE_LARGE_PAGES
- {"large-pages", OPT_ENABLE_LARGE_PAGES, "Enable support for large pages. "
- "Disable with --skip-large-pages.", &opt_large_pages, &opt_large_pages,
- 0, GET_BOOL, NO_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,
+ GET_BOOL, OPT_ARG, 0, 0, 1, 0, 1, 0},
#endif
- {"ignore-builtin-innodb", OPT_IGNORE_BUILTIN_INNODB ,
- "Disable initialization of builtin InnoDB plugin.",
- 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"init-connect", OPT_INIT_CONNECT,
- "Command(s) that are executed for each new connection (but not for SUPER users).",
- &opt_init_connect, &opt_init_connect, 0, GET_STR_ALLOC,
- REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
-#ifndef DISABLE_GRANT_OPTIONS
- {"init-file", OPT_INIT_FILE, "Read SQL commands from this file at startup.",
- &opt_init_file, &opt_init_file, 0, GET_STR, REQUIRED_ARG,
- 0, 0, 0, 0, 0, 0},
-#endif
- {"init-rpl-role", OPT_INIT_RPL_ROLE, "Set the replication role.", 0, 0, 0,
- GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"init-slave", OPT_INIT_SLAVE, "Command(s) that are executed by a slave server \
-each time the SQL thread starts.",
- &opt_init_slave, &opt_init_slave, 0, GET_STR_ALLOC,
- REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"ignore-db-dir", OPT_IGNORE_DB_DIRECTORY,
- "Specifies a directory to add to the ignore list when collecting "
- "database names from the datadir. Put a blank argument to reset "
- "the list accumulated so far.", 0, 0, 0, GET_STR, REQUIRED_ARG,
- 0, 0, 0, 0, 0, 0},
{"language", 'L',
- "Client error messages in given language. May be given as a full path.",
- &language_ptr, &language_ptr, 0, GET_STR, REQUIRED_ARG,
- 0, 0, 0, 0, 0, 0},
- {"lc-time-names", OPT_LC_TIME_NAMES,
+ "Client error messages in given language. May be given as a full path. "
+ "Deprecated. Use --lc-messages-dir instead.",
+ 0, 0, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"lc-messages", 0,
+ "Set the language used for the error messages.",
+ &lc_messages, &lc_messages, 0, GET_STR, REQUIRED_ARG,
+ 0, 0, 0, 0, 0, 0 },
+ {"lc-time-names", 0,
"Set the language used for the month names and the days of the week.",
&lc_time_names_name, &lc_time_names_name,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
- {"local-infile", OPT_LOCAL_INFILE,
- "Enable/disable LOAD DATA LOCAL INFILE (takes values 1 or 0).",
- &opt_local_infile, &opt_local_infile, 0, GET_BOOL, OPT_ARG,
- 1, 0, 0, 0, 0, 0},
{"log", 'l', "Log connections and queries to file (deprecated option, use "
- "--general_log/--general_log_file instead).", &opt_logname,
- &opt_logname, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
- {"general_log_file", OPT_GENERAL_LOG_FILE,
- "Log connections and queries to given file. Defaults to "
- "'datadir'/'log-basename'.log or 'datadir'/'hostname'.log if not "
- "specified.",
- &opt_logname, &opt_logname, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ "--general-log/--general-log-file instead).", &opt_logname, &opt_logname,
+ 0, GET_STR_ALLOC, OPT_ARG, 0, 0, 0, 0, 0, 0},
{"log-basename", OPT_LOG_BASENAME,
"Basename for all log files and the .pid file. This sets all log file "
"names at once (in 'datadir') and is normally the only option you need "
- "for specifying log files. This is especially recommend to be set if you "
- "are using replication as it ensures that your log file names are not "
- "depending on your host name. Sets names for --log-bin, --log-bin-index, "
+ "for specifying log files. Sets names for --log-bin, --log-bin-index, "
"--relay-log, --relay-log-index, --general-log-file, "
- "--log-slow-query-log-file, --log-error-file and --pid-file",
+ "--log-slow-query-log-file, --log-error-file, and --pid-file",
&opt_log_basename, &opt_log_basename, 0, GET_STR, REQUIRED_ARG,
0, 0, 0, 0, 0, 0},
{"log-bin", OPT_BIN_LOG,
"Log update queries in binary format. Optional argument should be name for "
"binary log. If not given "
- "datadir/'log-basename'-bin or 'datadir'/mysql-bin will be used (the later if "
- "--log-basename is not specified). We strongly recommend you to use either "
+ "'datadir'/'log-basename'-bin or 'datadir'/mysql-bin will be used (the later if "
+ "--log-basename is not specified). We strongly recommend to use either "
"--log-basename or specify a filename to ensure that replication doesn't "
- "stop if the real hostname of the computer changes'.",
+ "stop if the real hostname of the computer changes.",
&opt_bin_logname, &opt_bin_logname, 0, GET_STR,
OPT_ARG, 0, 0, 0, 0, 0, 0},
- {"log-bin-index", OPT_BIN_LOG_INDEX,
- "File that holds the names for last binary log files. If not specified, "
- "defaults to 'datadir/log-basename'-bin.index or 'datadir/mysql-bin.index'",
+ {"log-bin-index", 0,
+ "File that holds the names for last binary log files.",
&opt_binlog_index_name, &opt_binlog_index_name, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
-#ifndef TO_BE_REMOVED_IN_5_1_OR_6_0
- /*
- In 5.0.6 we introduced the below option, then in 5.0.16 we renamed it to
- log-bin-trust-function-creators but kept also the old name for
- compatibility; the behaviour was also changed to apply only to functions
- (and triggers). In a future release this old name could be removed.
- */
- {"log-bin-trust-routine-creators", OPT_LOG_BIN_TRUST_FUNCTION_CREATORS_OLD,
- "(deprecated) Use log-bin-trust-function-creators.",
- &trust_function_creators, &trust_function_creators, 0,
- GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
-#endif
- /*
- This option starts with "log-bin" to emphasize that it is specific of
- binary logging.
- */
- {"log-bin-trust-function-creators", OPT_LOG_BIN_TRUST_FUNCTION_CREATORS,
- "If equal to 0 (the default), then when --log-bin is used, creation of "
- "a stored function (or trigger) is allowed only to users having the SUPER "
- "privilege, and only if this stored function (trigger) may not break "
- "binary logging."
- "Note that if ALL connections to this server ALWAYS use row-based binary "
- "logging, the security issues do not exist and the binary logging cannot "
- "break, so you can safely set this to 1."
- ,&trust_function_creators, &trust_function_creators, 0,
- GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"log-error", OPT_ERROR_LOG_FILE,
- "Log errors to file (instead of stdout). If file name is not specified "
- "then 'datadir'/'log-basename'.err or the pid-file path with extension "
- ".err is used.",
- &log_error_file_ptr, &log_error_file_ptr, 0, GET_STR,
- OPT_ARG, 0, 0, 0, 0, 0, 0},
{"log-isam", OPT_ISAM_LOG, "Log all MyISAM changes to file.",
&myisam_log_filename, &myisam_log_filename, 0, GET_STR,
OPT_ARG, 0, 0, 0, 0, 0, 0},
- {"log-long-format", '0',
- "Log some extra information to update log. Please note that this option "
- "is deprecated; see --log-short-format option.",
- 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"log-output", OPT_LOG_OUTPUT,
- "Syntax: log-output[=value[,value...]], where \"value\" could be TABLE, "
- "FILE or NONE.",
- &log_output_str, &log_output_str, 0,
- GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
- {"log-queries-not-using-indexes", OPT_LOG_QUERIES_NOT_USING_INDEXES,
- "Log queries that are executed without benefit of any index to the slow log if it is open.",
- &opt_log_queries_not_using_indexes, &opt_log_queries_not_using_indexes,
- 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"log-short-format", OPT_SHORT_LOG_FORMAT,
+ {"log-short-format", 0,
"Don't log extra information to update and slow-query logs.",
&opt_short_log_format, &opt_short_log_format,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"log-slave-updates", OPT_LOG_SLAVE_UPDATES,
- "Tells the slave to log the updates from the slave thread to the binary log. "
- "You will need to turn it on if you plan to daisy-chain the slaves.",
- &opt_log_slave_updates, &opt_log_slave_updates, 0, GET_BOOL,
- NO_ARG, 0, 0, 0, 0, 0, 0},
- {"log-slow-admin-statements", OPT_LOG_SLOW_ADMIN_STATEMENTS,
- "Log slow OPTIMIZE, ANALYZE, ALTER and other administrative statements "
- "to the slow log if it is open.", &opt_log_slow_admin_statements,
+ {"log-slow-admin-statements", 0,
+ "Log slow OPTIMIZE, ANALYZE, ALTER and other administrative statements to "
+ "the slow log if it is open.", &opt_log_slow_admin_statements,
&opt_log_slow_admin_statements, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"log-slow-slave-statements", OPT_LOG_SLOW_SLAVE_STATEMENTS,
+ {"log-slow-slave-statements", 0,
"Log slow statements executed by slave thread to the slow log if it is open.",
- &opt_log_slow_slave_statements,
- &opt_log_slow_slave_statements,
+ &opt_log_slow_slave_statements, &opt_log_slow_slave_statements,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"log-slow-queries", OPT_SLOW_QUERY_LOG,
- "Enable logging of slow queries (longer than --log-slow-time) to log file "
- "or table. Optional argument is file name for slow log. If not given, "
+ "Enable logging of slow queries (longer than --long-query-time) to log file "
+ "or table. Optional argument is a file name for the slow log. If not given, "
"'log-basename'-slow.log will be used. Use --log-output=TABLE if you want "
- "to have the log in the table 'mysql.slow_log'",
- &opt_slow_logname, &opt_slow_logname, 0, GET_STR, OPT_ARG,
+ "to have the log in the table mysql.slow_log. "
+ "Deprecated option, use --slow-query-log/--slow-query-log-file instead.",
+ &opt_slow_logname, &opt_slow_logname, 0, GET_STR_ALLOC, OPT_ARG,
0, 0, 0, 0, 0, 0},
- {"slow_query_log_file", OPT_SLOW_QUERY_LOG_FILE,
- "Specify name for slow query log. Defaults to 'log-basename'-slow.log if "
- "not given",
- &opt_slow_logname, &opt_slow_logname, 0, GET_STR,
- REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"log-slow-file", OPT_SLOW_QUERY_LOG_FILE,
- "Specify name for slow query log. Defaults to 'log-basename'-slow.log if "
- "not given",
- &opt_slow_logname, &opt_slow_logname, 0, GET_STR,
- REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"log-tc", OPT_LOG_TC,
+ {"log-tc", 0,
"Path to transaction coordinator log (used for transactions that affect "
"more than one storage engine, when binary log is disabled).",
&opt_tc_log_file, &opt_tc_log_file, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#ifdef HAVE_MMAP
- {"log-tc-size", OPT_LOG_TC_SIZE, "Size of transaction coordinator log.",
+ {"log-tc-size", 0, "Size of transaction coordinator log.",
&opt_tc_log_size, &opt_tc_log_size, 0, GET_ULONG,
REQUIRED_ARG, TC_LOG_MIN_SIZE, TC_LOG_MIN_SIZE, (ulonglong) ULONG_MAX, 0,
TC_LOG_PAGE_SIZE, 0},
#endif
- {"log-update", OPT_UPDATE_LOG,
- "The update log is deprecated since version 5.0, is replaced by the binary "
- "log and this option just turns on --log-bin instead.",
- &opt_update_logname, &opt_update_logname, 0, GET_STR,
- OPT_ARG, 0, 0, 0, 0, 0, 0},
- {"log-warnings", 'W', "Log some not critical warnings to the general log file. Value can be between 0-11; The higher value, the more warnings",
- &global_system_variables.log_warnings,
- &max_system_variables.log_warnings, 0, GET_ULONG, OPT_ARG, 1, 0, 0,
- 0, 0, 0},
- {"low-priority-updates", OPT_LOW_PRIORITY_UPDATES,
- "INSERT/DELETE/UPDATE has lower priority than selects.",
- &global_system_variables.low_priority_updates,
- &max_system_variables.low_priority_updates,
- 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"master-connect-retry", OPT_MASTER_CONNECT_RETRY,
- "The number of seconds the slave thread will sleep before retrying to "
- "connect to the master, in case the master goes down or the connection "
- "is lost.",
- &master_connect_retry, &master_connect_retry, 0, GET_UINT,
- REQUIRED_ARG, 60, 0, 0, 0, 0, 0},
- {"master-host", OPT_MASTER_HOST,
- "Master hostname or IP address for replication. If not set, the slave "
- "thread will not be started. Note that the setting of master-host will "
- "be ignored if there exists a valid master.info file.",
- &master_host, &master_host, 0, GET_STR, REQUIRED_ARG, 0, 0,
- 0, 0, 0, 0},
- {"master-info-file", OPT_MASTER_INFO_FILE,
+ {"master-info-file", 0,
"The location and name of the file that remembers the master and where "
"the I/O replication thread is in the master's binlogs. Defaults to "
- "master.info. Is not affected by --log-basename.",
+ "master.info",
&master_info_file, &master_info_file, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"master-password", OPT_MASTER_PASSWORD,
- "The password the slave thread will authenticate with when connecting to "
- "the master. If not set, an empty password is assumed. The value in "
- "master.info will take precedence if it can be read.",
- &master_password, &master_password, 0,
- GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"master-port", OPT_MASTER_PORT,
- "The port the master is listening on. If not set, the compiled setting of "
- "MYSQL_PORT is assumed. If you have not tinkered with configure options, "
- "this should be 3306. The value in master.info will take precedence if it "
- "can be read.", &master_port, &master_port, 0, GET_UINT, REQUIRED_ARG,
- MYSQL_PORT, 0, 0, 0, 0, 0},
- {"master-retry-count", OPT_MASTER_RETRY_COUNT,
+ {"master-retry-count", 0,
"The number of tries the slave will make to connect to the master before giving up.",
&master_retry_count, &master_retry_count, 0, GET_ULONG,
REQUIRED_ARG, 3600*24, 0, 0, 0, 0, 0},
- {"master-ssl", OPT_MASTER_SSL,
- "Enable the slave to connect to the master using SSL.",
- &master_ssl, &master_ssl, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
- 0, 0},
- {"master-ssl-ca", OPT_MASTER_SSL_CA,
- "Master SSL CA file. Only applies if you have enabled master-ssl.",
- &master_ssl_ca, &master_ssl_ca, 0, GET_STR, OPT_ARG,
- 0, 0, 0, 0, 0, 0},
- {"master-ssl-capath", OPT_MASTER_SSL_CAPATH,
- "Master SSL CA path. Only applies if you have enabled master-ssl.",
- &master_ssl_capath, &master_ssl_capath, 0, GET_STR, OPT_ARG,
- 0, 0, 0, 0, 0, 0},
- {"master-ssl-cert", OPT_MASTER_SSL_CERT,
- "Master SSL certificate file name. Only applies if you have enabled "
- "master-ssl.",
- &master_ssl_cert, &master_ssl_cert, 0, GET_STR, OPT_ARG,
- 0, 0, 0, 0, 0, 0},
- {"master-ssl-cipher", OPT_MASTER_SSL_CIPHER,
- "Master SSL cipher. Only applies if you have enabled master-ssl.",
- &master_ssl_cipher, &master_ssl_capath, 0, GET_STR, OPT_ARG,
- 0, 0, 0, 0, 0, 0},
- {"master-ssl-key", OPT_MASTER_SSL_KEY,
- "Master SSL keyfile name. Only applies if you have enabled master-ssl.",
- &master_ssl_key, &master_ssl_key, 0, GET_STR, OPT_ARG,
- 0, 0, 0, 0, 0, 0},
- {"master-user", OPT_MASTER_USER,
- "The username the slave thread will use for authentication when "
- "connecting to the master. The user must have FILE privilege. "
- "If the master user is not set, user test is assumed. The value "
- "in master.info will take precedence if it can be read.",
- &master_user, &master_user, 0, GET_STR, REQUIRED_ARG, 0, 0,
- 0, 0, 0, 0},
#ifdef HAVE_REPLICATION
- {"max-binlog-dump-events", OPT_MAX_BINLOG_DUMP_EVENTS,
- "Option used by mysql-test for debugging and testing of replication.",
- &max_binlog_dump_events, &max_binlog_dump_events, 0,
- GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"init-rpl-role", 0, "Set the replication role.",
+ &rpl_status, &rpl_status, &rpl_role_typelib,
+ GET_ENUM, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#endif /* HAVE_REPLICATION */
- {"memlock", OPT_MEMLOCK, "Lock mysqld in memory.", &locked_in_memory,
+ {"memlock", 0, "Lock mysqld in memory.", &locked_in_memory,
&locked_in_memory, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
-#ifdef SAFE_MUTEX
- {"mutex-deadlock-detector", OPT_MUTEX_DEADLOCK_DETECTOR,
- "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
- {"myisam-recover", OPT_MYISAM_RECOVER,
- "Syntax: myisam-recover=OFF or myisam-recover[=option[,option...]], where option can be DEFAULT, BACKUP, BACKUP_ALL, FORCE or QUICK.",
- &myisam_recover_options_str, &myisam_recover_options_str, 0,
- GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
-#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
- {"ndb-connectstring", OPT_NDB_CONNECTSTRING,
- "Connect string for ndbcluster.",
- &opt_ndb_connectstring, &opt_ndb_connectstring,
- 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"ndb-mgmd-host", OPT_NDB_MGMD,
- "Set host and port for ndb_mgmd. Syntax: hostname[:port]",
- &opt_ndb_mgmd, &opt_ndb_mgmd,
- 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"ndb-nodeid", OPT_NDB_NODEID,
- "Nodeid for this mysqlserver in the cluster.",
- &opt_ndb_nodeid,
- &opt_ndb_nodeid,
- 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"ndb-autoincrement-prefetch-sz", OPT_NDB_AUTOINCREMENT_PREFETCH_SZ,
- "Specify number of autoincrement values that are prefetched.",
- &global_system_variables.ndb_autoincrement_prefetch_sz,
- &max_system_variables.ndb_autoincrement_prefetch_sz,
- 0, GET_ULONG, REQUIRED_ARG, 1, 1, 256, 0, 0, 0},
- {"ndb-force-send", OPT_NDB_FORCE_SEND,
- "Force send of buffers to ndb immediately without waiting for "
- "other threads.",
- &global_system_variables.ndb_force_send,
- &global_system_variables.ndb_force_send,
- 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
- {"ndb_force_send", OPT_NDB_FORCE_SEND,
- "same as --ndb-force-send.",
- &global_system_variables.ndb_force_send,
- &global_system_variables.ndb_force_send,
- 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
- {"ndb-extra-logging", OPT_NDB_EXTRA_LOGGING,
- "Turn on more logging in the error log.",
- &ndb_extra_logging,
- &ndb_extra_logging,
- 0, GET_INT, OPT_ARG, 0, 0, 0, 0, 0, 0},
-#ifdef HAVE_NDB_BINLOG
- {"ndb-report-thresh-binlog-epoch-slip", OPT_NDB_REPORT_THRESH_BINLOG_EPOCH_SLIP,
- "Threshold on number of epochs to be behind before reporting binlog status. "
- "E.g., 3 means that if the difference between what epoch has been received "
- "from the storage nodes and what has been applied to the binlog is 3 or more, "
- "a status message will be sent to the cluster log.",
- &ndb_report_thresh_binlog_epoch_slip,
- &ndb_report_thresh_binlog_epoch_slip,
- 0, GET_ULONG, REQUIRED_ARG, 3, 0, 256, 0, 0, 0},
- {"ndb-report-thresh-binlog-mem-usage", OPT_NDB_REPORT_THRESH_BINLOG_MEM_USAGE,
- "Threshold on percentage of free memory before reporting binlog status. E.g., "
- "10 means that if amount of available memory for receiving binlog data from "
- "the storage nodes goes below 10%, "
- "a status message will be sent to the cluster log.",
- &ndb_report_thresh_binlog_mem_usage,
- &ndb_report_thresh_binlog_mem_usage,
- 0, GET_ULONG, REQUIRED_ARG, 10, 0, 100, 0, 0, 0},
-#endif
- {"ndb-use-exact-count", OPT_NDB_USE_EXACT_COUNT,
- "Use exact records count during query planning and for fast "
- "select count(*), disable for faster queries.",
- &global_system_variables.ndb_use_exact_count,
- &global_system_variables.ndb_use_exact_count,
- 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
- {"ndb_use_exact_count", OPT_NDB_USE_EXACT_COUNT,
- "Same as --ndb-use-exact-count.",
- &global_system_variables.ndb_use_exact_count,
- &global_system_variables.ndb_use_exact_count,
- 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
- {"ndb-use-transactions", OPT_NDB_USE_TRANSACTIONS,
- "Use transactions for large inserts, if enabled then large "
- "inserts will be split into several smaller transactions",
- &global_system_variables.ndb_use_transactions,
- &global_system_variables.ndb_use_transactions,
- 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
- {"ndb_use_transactions", OPT_NDB_USE_TRANSACTIONS,
- "Same as --ndb-use-transactions.",
- &global_system_variables.ndb_use_transactions,
- &global_system_variables.ndb_use_transactions,
- 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
- {"ndb-shm", OPT_NDB_SHM,
- "Use shared memory connections when available.",
- &opt_ndb_shm, &opt_ndb_shm,
- 0, GET_BOOL, OPT_ARG, OPT_NDB_SHM_DEFAULT, 0, 0, 0, 0, 0},
- {"ndb-optimized-node-selection", OPT_NDB_OPTIMIZED_NODE_SELECTION,
- "Select nodes for transactions in a more optimal way.",
- &opt_ndb_optimized_node_selection,
- &opt_ndb_optimized_node_selection,
- 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
- { "ndb-cache-check-time", OPT_NDB_CACHE_CHECK_TIME,
- "A dedicated thread is created to, at the given milliseconds interval, "
- "invalidate the query cache if another MySQL server in the cluster has "
- "changed the data in the database.",
- &opt_ndb_cache_check_time, &opt_ndb_cache_check_time, 0, GET_ULONG, REQUIRED_ARG,
- 0, 0, LONG_TIMEOUT, 0, 1, 0},
- {"ndb-index-stat-enable", OPT_NDB_INDEX_STAT_ENABLE,
- "Use ndb index statistics in query optimization.",
- &global_system_variables.ndb_index_stat_enable,
- &max_system_variables.ndb_index_stat_enable,
- 0, GET_BOOL, OPT_ARG, 0, 0, 1, 0, 0, 0},
-#endif
- {"ndb-use-copying-alter-table",
- OPT_NDB_USE_COPYING_ALTER_TABLE,
- "Force ndbcluster to always copy tables at alter table "
- "(should only be used if on-line alter table fails).",
- &global_system_variables.ndb_use_copying_alter_table,
- &global_system_variables.ndb_use_copying_alter_table,
- 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"new", 'n', "Use very new, possibly 'unsafe', functions.",
- &global_system_variables.new_mode,
- &max_system_variables.new_mode,
- 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
-#ifdef NOT_YET
- {"no-mix-table-types", OPT_NO_MIX_TYPE,
- "Don't allow commands that use two different table types.",
- &opt_no_mix_types, &opt_no_mix_types, 0, GET_BOOL, NO_ARG,
- 0, 0, 0, 0, 0, 0},
-#endif
- {"old-alter-table", OPT_OLD_ALTER_TABLE,
- "Use old, non-optimized alter table.",
- &global_system_variables.old_alter_table,
- &max_system_variables.old_alter_table, 0, GET_BOOL, NO_ARG,
- 0, 0, 0, 0, 0, 0},
- {"old-passwords", OPT_OLD_PASSWORDS, "Use old password "
- "encryption method (needed for 4.0 and older clients).",
- &global_system_variables.old_passwords,
- &max_system_variables.old_passwords, 0, GET_BOOL, NO_ARG,
- 0, 0, 0, 0, 0, 0},
{"one-thread", OPT_ONE_THREAD,
"(Deprecated): Only use one thread (for debugging under Linux). Use "
"thread-handling=no-threads instead.",
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"old-style-user-limits", OPT_OLD_STYLE_USER_LIMITS,
+ {"old-style-user-limits", 0,
"Enable old-style user limits (before 5.0.3, user resources were counted "
"per each user+host vs. per account).",
&opt_old_style_user_limits, &opt_old_style_user_limits,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"pid-file", OPT_PID_FILE,
- "Pid file used by safe_mysqld. If not set, defaults to "
- "'datadir'/'log-basename'.pid or 'datadir'/'hostname'.pid'.",
- &pidfile_name_ptr, &pidfile_name_ptr, 0, GET_STR,
- REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"port", 'P', "Port number to use for connection or 0 for default to, in "
- "order of preference, my.cnf, $MYSQL_TCP_PORT, "
-#if MYSQL_PORT_DEFAULT == 0
- "/etc/services, "
-#endif
- "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").",
- &mysqld_port,
- &mysqld_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"port-open-timeout", OPT_PORT_OPEN_TIMEOUT,
+ {"port-open-timeout", 0,
"Maximum time in seconds to wait for the port to become free. "
- "(Default: No wait).", &mysqld_port_timeout,
- &mysqld_port_timeout, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
- {"profiling_history_size", OPT_PROFILING, "Limit of query profiling memory.",
- &global_system_variables.profiling_history_size,
- &max_system_variables.profiling_history_size,
- 0, GET_ULONG, REQUIRED_ARG, 15, 0, 100, 0, 0, 0},
-#endif
- {"relay-log", OPT_RELAY_LOG,
- "The location and name to use for relay logs. If not specified "
- "'datadir'/'log-basename' will be used.",
- &opt_relay_logname, &opt_relay_logname, 0,
- GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"relay-log-index", OPT_RELAY_LOG_INDEX,
- "The location and name to use for the file that keeps a list of the last "
- "relay logs. If not specified 'datadir'/'log-basename' will be used.",
- &opt_relaylog_index_name, &opt_relaylog_index_name, 0,
- GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"relay-log-info-file", OPT_RELAY_LOG_INFO_FILE,
- "The location and name of the file that remembers where the SQL "
- "replication thread is in the relay logs. Defaults to relay-log.info. "
- "Is not affected by '--log-basename'.",
- &relay_log_info_file, &relay_log_info_file, 0, GET_STR,
- REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ "(Default: No wait).", &mysqld_port_timeout, &mysqld_port_timeout, 0,
+ GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"replicate-do-db", OPT_REPLICATE_DO_DB,
"Tells the slave thread to restrict replication to the specified database. "
"To specify more than one database, use the directive multiple times, "
@@ -6759,7 +6459,7 @@ each time the SQL thread starts.",
"replicate-rewrite-db=master_db_name->slave_db_name.",
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#ifdef HAVE_REPLICATION
- {"replicate-same-server-id", OPT_REPLICATE_SAME_SERVER_ID,
+ {"replicate-same-server-id", 0,
"In replication, if set to 1, do not skip events having our server id. "
"Default value is 0 (to break infinite loops in circular replication). "
"Can't be set to 1 if --log-slave-updates is used.",
@@ -6782,70 +6482,13 @@ each time the SQL thread starts.",
"will not do updates to tables in databases that start with foo and whose "
"table names start with bar.",
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- // In replication, we may need to tell the other servers how to connect
- {"report-host", OPT_REPORT_HOST,
- "Hostname or IP of the slave to be reported to the master during slave "
- "registration. Will appear in the output of SHOW SLAVE HOSTS. Leave unset "
- "if you do not want the slave to register itself with the master. Note that "
- "it is not sufficient for the master to simply read the IP of the slave "
- "from the socket once the slave connects. Due to NAT and other routing "
- "issues, that IP may not be valid for connecting to the slave from the "
- "master or other hosts.",
- &report_host, &report_host, 0, GET_STR, REQUIRED_ARG, 0, 0,
- 0, 0, 0, 0},
- {"report-password", OPT_REPORT_PASSWORD, "Undocumented.",
- &report_password, &report_password, 0, GET_STR,
- REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"report-port", OPT_REPORT_PORT,
- "Port for connecting to slave reported to the master during slave "
- "registration. Set it only if the slave is listening on a non-default "
- "port or if you have a special tunnel from the master or other clients "
- "to the slave. If not sure, leave this option unset.",
- &report_port, &report_port, 0, GET_UINT, REQUIRED_ARG,
- MYSQL_PORT, 0, 0, 0, 0, 0},
- {"report-user", OPT_REPORT_USER, "Undocumented.", &report_user,
- &report_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"rpl-recovery-rank", OPT_RPL_RECOVERY_RANK, "Undocumented.",
- &rpl_recovery_rank, &rpl_recovery_rank, 0, GET_ULONG,
- REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"safe-mode", OPT_SAFE, "Skip some optimize stages (for testing).",
- 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
-#ifndef TO_BE_DELETED
- {"safe-show-database", OPT_SAFE_SHOW_DB,
- "Deprecated option; use GRANT SHOW DATABASES instead.",
+ {"safe-mode", OPT_SAFE, "Skip some optimize stages (for testing). Deprecated.",
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
-#endif
- {"safe-user-create", OPT_SAFE_USER_CREATE,
+ {"safe-user-create", 0,
"Don't allow new user creation by the user who has no write privileges to the mysql.user table.",
&opt_safe_user_create, &opt_safe_user_create, 0, GET_BOOL,
NO_ARG, 0, 0, 0, 0, 0, 0},
- {"safemalloc-mem-limit", OPT_SAFEMALLOC_MEM_LIMIT,
- "Simulate memory shortage when compiled with the --with-debug=full option.",
- 0, 0, 0, GET_ULL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"secure-auth", OPT_SECURE_AUTH, "Disallow authentication for accounts that have old (pre-4.1) passwords.",
- &opt_secure_auth, &opt_secure_auth, 0, GET_BOOL, NO_ARG,
- my_bool(0), 0, 0, 0, 0, 0},
- {"secure-file-priv", OPT_SECURE_FILE_PRIV,
- "Limit LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE() to files within specified directory.",
- &opt_secure_file_priv, &opt_secure_file_priv, 0,
- GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"server-id", OPT_SERVER_ID,
- "Uniquely identifies the server instance in the community of replication partners.",
- &server_id, &server_id, 0, GET_ULONG, REQUIRED_ARG, 0, 0, UINT_MAX32,
- 0, 0, 0},
- {"set-variable", 'O',
- "Change the value of a variable. Please note that this option is deprecated; "
- "you can set variables directly with --variable-name=value.",
- 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
-#ifdef HAVE_SMEM
- {"shared-memory", OPT_ENABLE_SHARED_MEMORY,
- "Enable the shared memory.",&opt_enable_shared_memory, &opt_enable_shared_memory,
- 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"shared-memory-base-name",OPT_SHARED_MEMORY_BASE_NAME,
- "Base name of shared memory.", &shared_memory_base_name, &shared_memory_base_name,
- 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
-#endif
- {"show-slave-auth-info", OPT_SHOW_SLAVE_AUTH_INFO,
+ {"show-slave-auth-info", 0,
"Show user and password in SHOW SLAVE HOSTS on this master.",
&opt_show_slave_auth_info, &opt_show_slave_auth_info, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
@@ -6853,98 +6496,35 @@ each time the SQL thread starts.",
"Deprecated option; Exist only for compatiblity with old my.cnf files",
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
#ifndef DISABLE_GRANT_OPTIONS
- {"skip-grant-tables", OPT_SKIP_GRANT,
+ {"skip-grant-tables", 0,
"Start without grant tables. This gives all users FULL ACCESS to all tables.",
&opt_noacl, &opt_noacl, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
0},
#endif
{"skip-host-cache", OPT_SKIP_HOST_CACHE, "Don't cache host names.", 0, 0, 0,
GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"skip-locking", OPT_SKIP_LOCK,
- "Deprecated option, use --skip-external-locking instead.",
- 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"skip-name-resolve", OPT_SKIP_RESOLVE,
- "Don't resolve hostnames. All hostnames are IP's or 'localhost'.",
- 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"skip-networking", OPT_SKIP_NETWORKING,
- "Don't allow connection with TCP/IP.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0,
- 0, 0, 0},
-#ifdef SAFEMALLOC
- {"safemalloc", OPT_SAFEMALLOC,
- "Check all memory allocation for every malloc/free call.",
- &sf_malloc_trough_check, &sf_malloc_trough_check, 0,
- GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
-#endif
- {"skip-show-database", OPT_SKIP_SHOW_DB,
- "Don't allow 'SHOW DATABASE' commands.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0,
- 0, 0, 0, 0},
- {"skip-slave-start", OPT_SKIP_SLAVE_START,
+ {"skip-slave-start", 0,
"If set, slave is not autostarted.", &opt_skip_slave_start,
&opt_skip_slave_start, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"skip-symlink", OPT_SKIP_SYMLINKS, "Don't allow symlinking of tables. "
- "Deprecated option. Use --skip-symbolic-links instead.",
- 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
{"skip-thread-priority", OPT_SKIP_PRIOR,
- "Don't give threads different priorities. Deprecated option.", 0, 0, 0, GET_NO_ARG, NO_ARG,
- DEFAULT_SKIP_THREAD_PRIORITY, 0, 0, 0, 0, 0},
-#ifdef HAVE_REPLICATION
- {"slave-load-tmpdir", OPT_SLAVE_LOAD_TMPDIR,
- "The location where the slave should put its temporary files when "
- "replicating a LOAD DATA INFILE command.",
- &slave_load_tmpdir, &slave_load_tmpdir, 0, GET_STR_ALLOC,
- REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"slave-skip-errors", OPT_SLAVE_SKIP_ERRORS,
- "Tells the slave thread to continue replication when a query event returns an error from the provided list.",
- 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"slave-exec-mode", OPT_SLAVE_EXEC_MODE,
- "Modes for how replication events should be executed. Legal values are "
- "STRICT (default) and IDEMPOTENT. In IDEMPOTENT mode, replication will "
- "not stop for operations that are idempotent. In STRICT mode, replication "
- "will stop on any unexpected difference between the master and the slave.",
- &slave_exec_mode_str, &slave_exec_mode_str, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"master-verify-checksum", OPT_MASTER_VERIFY_CHECKSUM,
- "Force checksum verification of logged events in binary log before "
- "sending them to slaves or printing them in output of SHOW BINLOG EVENTS. "
- "Disabled by default.",
- &opt_master_verify_checksum, &opt_master_verify_checksum,
- 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
- {"slave-sql-verify-checksum", OPT_SLAVE_SQL_VERIFY_CHECKSUM,
- "Force checksum verification of replication events after reading them "
- "from relay log. Note: Events are always checksum-verified by slave on "
- "receiving them from the network before writing them to the relay "
- "log. Enabled by default.",
- &opt_slave_sql_verify_checksum, &opt_slave_sql_verify_checksum,
- 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
-#endif
- {"slow-query-log", OPT_SLOW_LOG,
- "Enable/disable slow query log. See also '--log-slow-queries'",
- &opt_slow_log,
- &opt_slow_log, 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
- {"socket", OPT_SOCKET, "Socket file to use for connection.",
- &mysqld_unix_port, &mysqld_unix_port, 0, GET_STR,
- REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
-#ifdef HAVE_REPLICATION
- {"sporadic-binlog-dump-fail", OPT_SPORADIC_BINLOG_DUMP_FAIL,
- "Option used by mysql-test for debugging and testing of replication.",
- &opt_sporadic_binlog_dump_fail,
- &opt_sporadic_binlog_dump_fail, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
- 0},
-#endif /* HAVE_REPLICATION */
- {"sql-bin-update-same", OPT_SQL_BIN_UPDATE_SAME,
- "The update log is deprecated since version 5.0, is replaced by the "
- "binary log and this option does nothing anymore.",
- 0, 0, 0, GET_DISABLED, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"sql-mode", OPT_SQL_MODE,
- "Syntax: sql-mode=option[,option[,option...]] where option can be one "
- "of: REAL_AS_FLOAT, PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, "
- "ONLY_FULL_GROUP_BY, NO_UNSIGNED_SUBTRACTION.",
- &sql_mode_str, &sql_mode_str, 0, GET_STR, REQUIRED_ARG, 0,
- 0, 0, 0, 0, 0},
+ "Don't give threads different priorities. This option is deprecated "
+ "because it has no effect; the implied behavior is already the default.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+#if defined(_WIN32) && !defined(EMBEDDED_LIBRARY)
+ {"slow-start-timeout", 0,
+ "Maximum number of milliseconds that the service control manager should wait "
+ "before trying to kill the windows service during startup"
+ "(Default: 15000).", &slow_start_timeout, &slow_start_timeout, 0,
+ GET_ULONG, REQUIRED_ARG, 15000, 0, 0, 0, 0, 0},
+#endif
#ifdef HAVE_OPENSSL
-#include "sslopt-longopts.h"
+ {"ssl", 0,
+ "Enable SSL for connection (automatically enabled with other flags).",
+ &opt_use_ssl, &opt_use_ssl, 0, GET_BOOL, OPT_ARG, 0, 0, 0,
+ 0, 0, 0},
#endif
#ifdef __WIN__
- {"standalone", OPT_STANDALONE,
+ {"standalone", 0,
"Dummy option to start as a standalone program (NT).", 0, 0, 0, GET_NO_ARG,
NO_ARG, 0, 0, 0, 0, 0, 0},
#endif
@@ -6954,27 +6534,21 @@ each time the SQL thread starts.",
The system call realpath() produces warnings under valgrind and
purify. These are not suppressed: instead we disable symlinks
option if compiled with valgrind support.
+ Also disable by default on Windows, due to high overhead for checking .sym
+ files.
*/
- IF_VALGRIND(0,1), 0, 0, 0, 0, 0},
- {"sysdate-is-now", OPT_SYSDATE_IS_NOW,
+ IF_VALGRIND(0,IF_WIN(0,1)), 0, 0, 0, 0, 0},
+ {"sysdate-is-now", 0,
"Non-default option to alias SYSDATE() to NOW() to make it safe-replicable. "
"Since 5.0, SYSDATE() returns a `dynamic' value different for different "
"invocations, even within the same statement.",
&global_system_variables.sysdate_is_now,
0, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 1, 0},
- {"tc-heuristic-recover", OPT_TC_HEURISTIC_RECOVER,
+ {"tc-heuristic-recover", 0,
"Decision to use in heuristic recover process. Possible values are COMMIT "
- "or ROLLBACK.", &opt_tc_heuristic_recover, &opt_tc_heuristic_recover,
- 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
-#if defined(ENABLED_DEBUG_SYNC)
- {"debug-sync-timeout", OPT_DEBUG_SYNC_TIMEOUT,
- "Enable the debug sync facility "
- "and optionally specify a default wait timeout in seconds. "
- "A zero value keeps the facility disabled.",
- &opt_debug_sync_timeout, 0,
- 0, GET_UINT, OPT_ARG, 0, 0, UINT_MAX, 0, 0, 0},
-#endif /* defined(ENABLED_DEBUG_SYNC) */
- {"temp-pool", OPT_TEMP_POOL,
+ "or ROLLBACK.", &tc_heuristic_recover, &tc_heuristic_recover,
+ &tc_heuristic_recover_typelib, GET_ENUM, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"temp-pool", 0,
#if (ENABLE_TEMP_POOL)
"Using this option will cause most temporary files created to use a small "
"set of names, rather than a unique name for each new file.",
@@ -6983,725 +6557,26 @@ each time the SQL thread starts.",
#endif
&use_temp_pool, &use_temp_pool, 0, GET_BOOL, NO_ARG, 1,
0, 0, 0, 0, 0},
- {"test-ignore-wrong-options", OPT_TEST_IGNORE_WRONG_OPTIONS,
- "Ignore wrong enums values in command line arguments. Useful only for test scripts",
- &opt_ignore_wrong_options, &opt_ignore_wrong_options,
- 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"test-expect-abort", OPT_TEST_RESTART,
- "Expect that server aborts with 'abort'; Don't write out server variables on 'abort'. Useful only for test scripts",
- &opt_expect_abort, &opt_expect_abort,
- 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"timed_mutexes", OPT_TIMED_MUTEXES,
- "Specify whether to time mutexes (only InnoDB mutexes are currently supported).",
- &timed_mutexes, &timed_mutexes, 0, GET_BOOL, NO_ARG, 0,
- 0, 0, 0, 0, 0},
- {"tmpdir", 't',
- "Path for temporary files. Several paths may be specified, separated by a "
-#if defined(__WIN__) || defined(__NETWARE__)
- "semicolon (;)"
-#else
- "colon (:)"
-#endif
- ", in this case they are used in a round-robin fashion.",
- &opt_mysql_tmpdir, &opt_mysql_tmpdir, 0, GET_STR, REQUIRED_ARG,
- 0, 0, 0, 0, 0, 0},
- {"transaction-isolation", OPT_TX_ISOLATION,
- "Default transaction isolation level.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0,
- 0, 0, 0, 0, 0},
- {"use-symbolic-links", OPT_SYMBOLIC_LINKS,
- "Enable symbolic link support. "
- "Deprecated option; use --symbolic-links instead.",
- &my_use_symdir, &my_use_symdir, 0, GET_BOOL, NO_ARG,
- IF_VALGRIND(0,1), 0, 0, 0, 0, 0},
+ {"transaction-isolation", 0,
+ "Default transaction isolation level.",
+ &global_system_variables.tx_isolation,
+ &global_system_variables.tx_isolation, &tx_isolation_typelib,
+ GET_ENUM, REQUIRED_ARG, ISO_REPEATABLE_READ, 0, 0, 0, 0, 0},
{"user", 'u', "Run mysqld daemon as user.", 0, 0, 0, GET_STR, REQUIRED_ARG,
0, 0, 0, 0, 0, 0},
{"verbose", 'v', "Used with --help option for detailed help.",
&opt_verbose, &opt_verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG,
NO_ARG, 0, 0, 0, 0, 0, 0},
- {"warnings", OPT_WARNINGS, "Deprecated; use --log-warnings instead.",
- &global_system_variables.log_warnings,
- &max_system_variables.log_warnings, 0, GET_ULONG, OPT_ARG,
- 1, 0, ULONG_MAX, 0, 0, 0},
- {"back_log", OPT_BACK_LOG,
- "The number of outstanding connection requests MySQL can have. This "
- "comes into play when the main MySQL thread gets very many connection "
- "requests in a very short time.", &back_log, &back_log, 0, GET_ULONG,
- REQUIRED_ARG, 50, 1, 65535, 0, 1, 0 },
- {"binlog_cache_size", OPT_BINLOG_CACHE_SIZE,
- "The size of the cache to hold the SQL statements for the binary log "
- "during a transaction. If you often use big, multi-statement "
- "transactions you can increase this to get more performance.",
- &binlog_cache_size, &binlog_cache_size, 0, GET_ULONG,
- REQUIRED_ARG, 32*1024L, IO_SIZE, (ulonglong) ULONG_MAX, 0, IO_SIZE, 0},
- {"bulk_insert_buffer_size", OPT_BULK_INSERT_BUFFER_SIZE,
- "Size of tree cache used in bulk insert optimization. Note that this "
- "is a limit per thread.", &global_system_variables.bulk_insert_buff_size,
- &max_system_variables.bulk_insert_buff_size,
- 0, GET_ULONG, REQUIRED_ARG, 8192*1024, 0, (ulonglong) ULONG_MAX, 0, 1, 0},
- {"connect_timeout", OPT_CONNECT_TIMEOUT,
- "The number of seconds the mysqld server is waiting for a connect packet "
- "before responding with 'Bad handshake'.", &connect_timeout, &connect_timeout,
- 0, GET_ULONG, REQUIRED_ARG, CONNECT_TIMEOUT, 2, LONG_TIMEOUT, 0, 1, 0 },
- { "date_format", OPT_DATE_FORMAT,
- "The DATE format (for future).",
- &opt_date_time_formats[MYSQL_TIMESTAMP_DATE],
- &opt_date_time_formats[MYSQL_TIMESTAMP_DATE],
- 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- { "datetime_format", OPT_DATETIME_FORMAT,
- "The DATETIME/TIMESTAMP format (for future).",
- &opt_date_time_formats[MYSQL_TIMESTAMP_DATETIME],
- &opt_date_time_formats[MYSQL_TIMESTAMP_DATETIME],
- 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- { "default_week_format", OPT_DEFAULT_WEEK_FORMAT,
- "The default week format used by WEEK() functions.",
- &global_system_variables.default_week_format,
- &max_system_variables.default_week_format,
- 0, GET_ULONG, REQUIRED_ARG, 0, 0, 7L, 0, 1, 0},
- {"delayed_insert_limit", OPT_DELAYED_INSERT_LIMIT,
- "After inserting delayed_insert_limit rows, the INSERT DELAYED handler "
- "will check if there are any SELECT statements pending. If so, it allows "
- "these to execute before continuing.",
- &delayed_insert_limit, &delayed_insert_limit, 0, GET_ULONG,
- REQUIRED_ARG, DELAYED_LIMIT, 1, (ulonglong) ULONG_MAX, 0, 1, 0},
- {"delayed_insert_timeout", OPT_DELAYED_INSERT_TIMEOUT,
- "How long a INSERT DELAYED thread should wait for INSERT statements before terminating.",
- &delayed_insert_timeout, &delayed_insert_timeout, 0,
- GET_ULONG, REQUIRED_ARG, DELAYED_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0},
- { "delayed_queue_size", OPT_DELAYED_QUEUE_SIZE,
- "What size queue (in rows) should be allocated for handling INSERT DELAYED. "
- "If the queue becomes full, any client that does INSERT DELAYED will wait "
- "until there is room in the queue again.",
- &delayed_queue_size, &delayed_queue_size, 0, GET_ULONG,
- REQUIRED_ARG, DELAYED_QUEUE_SIZE, 1, (ulonglong) ULONG_MAX, 0, 1, 0},
- {"div_precision_increment", OPT_DIV_PRECINCREMENT,
- "Precision of the result of '/' operator will be increased on that value.",
- &global_system_variables.div_precincrement,
- &max_system_variables.div_precincrement, 0, GET_ULONG,
- REQUIRED_ARG, 4, 0, DECIMAL_MAX_SCALE, 0, 0, 0},
- {"expire_logs_days", OPT_EXPIRE_LOGS_DAYS,
- "If non-zero, binary logs will be purged after expire_logs_days "
- "days; possible purges happen at startup and at binary log rotation.",
- &expire_logs_days, &expire_logs_days, 0, GET_ULONG,
- REQUIRED_ARG, 0, 0, 99, 0, 1, 0},
- { "flush_time", OPT_FLUSH_TIME,
- "A dedicated thread is created to flush all tables at the given interval.",
- &flush_time, &flush_time, 0, GET_ULONG, REQUIRED_ARG,
- 0 , 0, LONG_TIMEOUT, 0, 1, 0},
- { "ft_boolean_syntax", OPT_FT_BOOLEAN_SYNTAX,
- "List of operators for MATCH ... AGAINST ( ... IN BOOLEAN MODE).",
- 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- { "ft_max_word_len", OPT_FT_MAX_WORD_LEN,
- "The maximum length of the word to be included in a FULLTEXT index. "
- "Note: FULLTEXT indexes must be rebuilt after changing this variable.",
- &ft_max_word_len, &ft_max_word_len, 0, GET_ULONG,
- REQUIRED_ARG, HA_FT_MAXCHARLEN, 10, HA_FT_MAXCHARLEN, 0, 1, 0},
- { "ft_min_word_len", OPT_FT_MIN_WORD_LEN,
- "The minimum length of the word to be included in a FULLTEXT index. "
- "Note: FULLTEXT indexes must be rebuilt after changing this variable.",
- &ft_min_word_len, &ft_min_word_len, 0, GET_ULONG,
- REQUIRED_ARG, 4, 1, HA_FT_MAXCHARLEN, 0, 1, 0},
- { "ft_query_expansion_limit", OPT_FT_QUERY_EXPANSION_LIMIT,
- "Number of best matches to use for query expansion.",
- &ft_query_expansion_limit, &ft_query_expansion_limit, 0, GET_ULONG,
- REQUIRED_ARG, 20, 0, 1000, 0, 1, 0},
- { "ft_stopword_file", OPT_FT_STOPWORD_FILE,
- "Use stopwords from this file instead of built-in list.",
- &ft_stopword_file, &ft_stopword_file, 0, GET_STR,
- REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- { "group_concat_max_len", OPT_GROUP_CONCAT_MAX_LEN,
- "The maximum length of the result of function group_concat.",
- &global_system_variables.group_concat_max_len,
- &max_system_variables.group_concat_max_len, 0, GET_ULONG,
- REQUIRED_ARG, 1024, 4, (ulonglong) ULONG_MAX, 0, 1, 0},
- {"interactive_timeout", OPT_INTERACTIVE_TIMEOUT,
- "The number of seconds the server waits for activity on an interactive "
- "connection before closing it.",
- &global_system_variables.net_interactive_timeout,
- &max_system_variables.net_interactive_timeout, 0,
- GET_ULONG, REQUIRED_ARG, NET_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0},
- {"join_buffer_size", OPT_JOIN_BUFF_SIZE,
- "The size of the buffer that is used for joins.",
- &global_system_variables.join_buff_size,
- &max_system_variables.join_buff_size, 0, GET_ULONG,
- REQUIRED_ARG, 128*1024L, 128+MALLOC_OVERHEAD, (ulonglong) ULONG_MAX,
- MALLOC_OVERHEAD, 128, 0},
- {"join_buffer_space_limit", OPT_JOIN_BUFF_SPACE_LIMIT,
- "The limit of the space for all join buffers used by a query.",
- &global_system_variables.join_buff_space_limit,
- &max_system_variables.join_buff_space_limit, 0, GET_ULL,
- REQUIRED_ARG, 16*128*1024L, 2048+MALLOC_OVERHEAD, (ulonglong) ULONGLONG_MAX,
- MALLOC_OVERHEAD, 2048, 0},
- {"join_cache_level", OPT_JOIN_CACHE_LEVEL,
- "Controls what join operations can be executed with join buffers. Odd numbers are used for plain join buffers while even numbers are used for linked buffers",
- &global_system_variables.join_cache_level,
- &max_system_variables.join_cache_level,
- 0, GET_ULONG, REQUIRED_ARG, 2, 0, 8, 0, 1, 0},
- {"keep_files_on_create", OPT_KEEP_FILES_ON_CREATE,
- "Don't overwrite stale .MYD and .MYI even if no directory is specified.",
- &global_system_variables.keep_files_on_create,
- &max_system_variables.keep_files_on_create,
- 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
- {"key_buffer_size", OPT_KEY_BUFFER_SIZE,
- "The size of the buffer used for index blocks for MyISAM tables. Increase "
- "this to get better index handling (for all reads and multiple writes) to "
- "as much as you can afford; 1GB on a 4GB machine that mainly runs MySQL is "
- "quite common.",
- &dflt_key_cache_var.param_buff_size, NULL, NULL, (GET_ULL | GET_ASK_ADDR),
- REQUIRED_ARG, KEY_CACHE_SIZE, 0, SIZE_T_MAX, MALLOC_OVERHEAD,
- IO_SIZE, 0},
- {"key_cache_age_threshold", OPT_KEY_CACHE_AGE_THRESHOLD,
- "This characterizes the number of hits a hot block has to be untouched "
- "until it is considered aged enough to be downgraded to a warm block. "
- "This specifies the percentage ratio of that number of hits to the total "
- "number of blocks in key cache.",
- &dflt_key_cache_var.param_age_threshold, 0, 0,
- (GET_ULONG | GET_ASK_ADDR), REQUIRED_ARG, 300, 100, (ulonglong) ULONG_MAX,
- 0, 100, 0},
- {"key_cache_block_size", OPT_KEY_CACHE_BLOCK_SIZE,
- "The default size of key cache blocks.",
- &dflt_key_cache_var.param_block_size, NULL, NULL, (GET_ULONG | GET_ASK_ADDR),
- REQUIRED_ARG, KEY_CACHE_BLOCK_SIZE, 512, 1024 * 16, 0, 512, 0},
- {"key_cache_division_limit", OPT_KEY_CACHE_DIVISION_LIMIT,
- "The minimum percentage of warm blocks in key cache.",
- &dflt_key_cache_var.param_division_limit, 0, 0,
- (GET_ULONG | GET_ASK_ADDR) , REQUIRED_ARG, 100, 1, 100, 0, 1, 0},
- {"key_cache_segments", OPT_KEY_CACHE_PARTITIONS,
- "The number of segments in a key cache",
- &dflt_key_cache_var.param_partitions, 0,
- 0, (GET_ULONG | GET_ASK_ADDR), REQUIRED_ARG, DEFAULT_KEY_CACHE_PARTITIONS,
- 0, MAX_KEY_CACHE_PARTITIONS, 0, 1, 0},
- {"log-slow-filter", OPT_LOG_SLOW_FILTER,
- "Log only the queries that followed certain execution plan. Multiple flags "
- "allowed in a comma-separated string. [admin, filesort, filesort_on_disk, "
- "full_join, full_scan, query_cache, query_cache_miss, tmp_table, "
- "tmp_table_on_disk]. Sets log-slow-admin-command to ON",
- 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, QPLAN_ALWAYS_SET, 0, 0},
- {"log-slow-rate_limit", OPT_LOG_SLOW_RATE_LIMIT,
- "If set, only write to slow log every 'log_slow_rate_limit' query (use "
- "this to reduce output on slow query log)",
- &global_system_variables.log_slow_rate_limit,
- &max_system_variables.log_slow_rate_limit, 0, GET_ULONG,
- REQUIRED_ARG, 1, 1, ~0ULL, 0, 1L, 0},
- {"log-slow-verbosity", OPT_LOG_SLOW_VERBOSITY,
- "Choose how verbose the messages to your slow log will be. Multiple flags "
- "allowed in a comma-separated string. [query_plan, innodb]",
- 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
- {"log-slow-time", OPT_LONG_QUERY_TIME,
- "Log all queries that have taken more than log-slow-time seconds to "
- "execute to file. The argument will be treated as a decimal value with "
- "microsecond precission. Same as --log-query-time.",
- &long_query_time, &long_query_time, 0, GET_DOUBLE,
- REQUIRED_ARG, 10, 0, LONG_TIMEOUT, 0, 0, 0},
- {"long_query_time", OPT_LONG_QUERY_TIME,
- "Log all queries that have taken more than long-query-time seconds to "
- "execute to file. The argument will be treated as a decimal value with "
- "microsecond precission. Same as --log-slow-time.",
- &long_query_time, &long_query_time, 0, GET_DOUBLE,
- REQUIRED_ARG, 10, 0, LONG_TIMEOUT, 0, 0, 0},
- {"lower_case_table_names", OPT_LOWER_CASE_TABLE_NAMES,
- "If set to 1, table names are stored in lowercase on disk and table names "
- "will be case-insensitive. Should be set to 2 if you are using a case-"
- "insensitive file system.",
- &lower_case_table_names, &lower_case_table_names, 0, GET_UINT, OPT_ARG,
-#ifdef FN_NO_CASE_SENCE
- 1
-#else
- 0
-#endif
- , 0, 2, 0, 1, 0},
- {"max_allowed_packet", OPT_MAX_ALLOWED_PACKET,
- "The maximum packet length to send to or receive from server.",
- &global_system_variables.max_allowed_packet,
- &max_system_variables.max_allowed_packet, 0, GET_ULONG,
- REQUIRED_ARG, 1024*1024L, 1024, 1024L*1024L*1024L, MALLOC_OVERHEAD, 1024, 0},
- {"slave_max_allowed_packet", OPT_SLAVE_MAX_ALLOWED_PACKET,
- "The maximum packet length to sent successfully from the master to slave.",
- &slave_max_allowed_packet, &slave_max_allowed_packet, 0, GET_ULONG,
- REQUIRED_ARG, MAX_MAX_ALLOWED_PACKET, 1024, MAX_MAX_ALLOWED_PACKET, MALLOC_OVERHEAD, 1024, 0},
- {"max_binlog_cache_size", OPT_MAX_BINLOG_CACHE_SIZE,
- "Can be used to restrict the total size used to cache a multi-transaction query.",
- &max_binlog_cache_size, &max_binlog_cache_size, 0,
- GET_ULL, REQUIRED_ARG, (longlong) ULONG_MAX, IO_SIZE, ULONGLONG_MAX, 0, IO_SIZE, 0},
- {"max_binlog_size", OPT_MAX_BINLOG_SIZE,
- "Binary log will be rotated automatically when the size exceeds this "
- "value. Will also apply to relay logs if max_relay_log_size is 0. "
- "The minimum value for this variable is 4096.",
- &max_binlog_size, &max_binlog_size, 0, GET_ULONG,
- REQUIRED_ARG, 1024*1024L*1024L, IO_SIZE, 1024*1024L*1024L, 0, IO_SIZE, 0},
- {"max_connect_errors", OPT_MAX_CONNECT_ERRORS,
- "If there is more than this number of interrupted connections from a host "
- "this host will be blocked from further connections.",
- &max_connect_errors, &max_connect_errors, 0, GET_ULONG,
- REQUIRED_ARG, MAX_CONNECT_ERRORS, 1, (ulonglong) ULONG_MAX, 0, 1, 0},
- // Default max_connections of 151 is larger than Apache's default max
- // children, to avoid "too many connections" error in a common setup
- {"max_connections", OPT_MAX_CONNECTIONS,
- "The number of simultaneous clients allowed.", &max_connections,
- &max_connections, 0, GET_ULONG, REQUIRED_ARG, 151, 1, 100000, 0, 1, 0},
- {"max_delayed_threads", OPT_MAX_DELAYED_THREADS,
- "Don't start more than this number of threads to handle INSERT DELAYED "
- "statements. If set to zero, which means INSERT DELAYED is not used.",
- &global_system_variables.max_insert_delayed_threads,
- &max_system_variables.max_insert_delayed_threads,
- 0, GET_ULONG, REQUIRED_ARG, 20, 0, 16384, 0, 1, 0},
- {"max_error_count", OPT_MAX_ERROR_COUNT,
- "Max number of errors/warnings to store for a statement.",
- &global_system_variables.max_error_count,
- &max_system_variables.max_error_count,
- 0, GET_ULONG, REQUIRED_ARG, DEFAULT_ERROR_COUNT, 0, 65535, 0, 1, 0},
- {"max_heap_table_size", OPT_MAX_HEP_TABLE_SIZE,
- "Don't allow creation of heap tables bigger than this.",
- &global_system_variables.max_heap_table_size,
- &max_system_variables.max_heap_table_size, 0, GET_ULL,
- REQUIRED_ARG, 16*1024*1024L, 16384, MAX_MEM_TABLE_SIZE,
- MALLOC_OVERHEAD, 1024, 0},
- {"max_join_size", OPT_MAX_JOIN_SIZE,
- "Joins that are probably going to read more than max_join_size records return an error.",
- &global_system_variables.max_join_size,
- &max_system_variables.max_join_size, 0, GET_HA_ROWS, REQUIRED_ARG,
- (longlong) HA_POS_ERROR, 1, HA_POS_ERROR, 0, 1, 0},
- {"max_length_for_sort_data", OPT_MAX_LENGTH_FOR_SORT_DATA,
- "Max number of bytes in sorted records.",
- &global_system_variables.max_length_for_sort_data,
- &max_system_variables.max_length_for_sort_data, 0, GET_ULONG,
- REQUIRED_ARG, 1024, 4, 8192*1024L, 0, 1, 0},
- {"max_long_data_size", OPT_MAX_LONG_DATA_SIZE,
- "The maximum size of prepared statement parameter which can be provided "
- "through mysql_send_long_data() API call. To be used when limit of "
- "max_allowed_packet is too small",
- &max_long_data_size, &max_long_data_size, 0, GET_ULONG,
- REQUIRED_ARG, 1024*1024L, 1024, UINT_MAX32, MALLOC_OVERHEAD, 1, 0},
- {"max_prepared_stmt_count", OPT_MAX_PREPARED_STMT_COUNT,
- "Maximum number of prepared statements in the server.",
- &max_prepared_stmt_count, &max_prepared_stmt_count,
- 0, GET_ULONG, REQUIRED_ARG, 16382, 0, 1*1024*1024, 0, 1, 0},
- {"max_relay_log_size", OPT_MAX_RELAY_LOG_SIZE,
- "If non-zero: relay log will be rotated automatically when the size "
- "exceeds this value; if zero (the default): when the size exceeds "
- "max_binlog_size. 0 excepted, the minimum value for this variable is 4096.",
- &max_relay_log_size, &max_relay_log_size, 0, GET_ULONG,
- REQUIRED_ARG, 0L, 0L, 1024*1024L*1024L, 0, IO_SIZE, 0},
- { "max_seeks_for_key", OPT_MAX_SEEKS_FOR_KEY,
- "Limit assumed max number of seeks when looking up rows based on a key.",
- &global_system_variables.max_seeks_for_key,
- &max_system_variables.max_seeks_for_key, 0, GET_ULONG,
- REQUIRED_ARG, (longlong) ULONG_MAX, 1, ULONG_MAX, 0, 1, 0 },
- {"max_sort_length", OPT_MAX_SORT_LENGTH,
- "The number of bytes to use when sorting BLOB or TEXT values (only the "
- "first max_sort_length bytes of each value are used; the rest are ignored).",
- &global_system_variables.max_sort_length,
- &max_system_variables.max_sort_length, 0, GET_ULONG,
- REQUIRED_ARG, 1024, 4, 8192*1024L, 0, 1, 0},
- {"max_sp_recursion_depth", OPT_MAX_SP_RECURSION_DEPTH,
- "Maximum stored procedure recursion depth. (discussed with docs).",
- &global_system_variables.max_sp_recursion_depth,
- &max_system_variables.max_sp_recursion_depth, 0, GET_ULONG,
- OPT_ARG, 0, 0, 255, 0, 1, 0 },
- {"max_tmp_tables", OPT_MAX_TMP_TABLES,
- "Maximum number of temporary tables a client can keep open at a time.",
- &global_system_variables.max_tmp_tables,
- &max_system_variables.max_tmp_tables, 0, GET_ULONG,
- REQUIRED_ARG, 32, 1, (ulonglong) ULONG_MAX, 0, 1, 0},
- {"max_user_connections", OPT_MAX_USER_CONNECTIONS,
- "The maximum number of active connections for a single user (0 = no limit. In addition global max_user_connections counting and checking is permanently disabled).",
- &max_user_connections, &max_user_connections, 0, GET_INT,
- REQUIRED_ARG, 0, 0, INT_MAX, 0, 1, 0},
- {"max_write_lock_count", OPT_MAX_WRITE_LOCK_COUNT,
- "After this many write locks, allow some read locks to run in between.",
- &max_write_lock_count, &max_write_lock_count, 0, GET_ULONG,
- REQUIRED_ARG, (longlong) ULONG_MAX, 1, ULONG_MAX, 0, 1, 0},
- {"min_examined_row_limit", OPT_MIN_EXAMINED_ROW_LIMIT,
- "Don't log queries which examine less than min_examined_row_limit rows to file.",
- &global_system_variables.min_examined_row_limit,
- &max_system_variables.min_examined_row_limit, 0, GET_ULONG,
- REQUIRED_ARG, 0, 0, (ulonglong) ULONG_MAX, 0, 1L, 0},
- {"mrr_buffer_size", OPT_MRR_BUFFER_SIZE,
- "Size of buffer to use when using MRR with range access",
- (uchar**) &global_system_variables.mrr_buff_size,
- (uchar**) &max_system_variables.mrr_buff_size, 0,
- GET_ULONG, REQUIRED_ARG, 256*1024L, IO_SIZE*2+MALLOC_OVERHEAD,
- INT_MAX32, MALLOC_OVERHEAD, 1 /* Small to be able to do tests */ , 0},
- {"myisam_block_size", OPT_MYISAM_BLOCK_SIZE,
- "Block size to be used for MyISAM index pages.",
- &opt_myisam_block_size, &opt_myisam_block_size, 0, GET_ULONG, REQUIRED_ARG,
- MI_KEY_BLOCK_LENGTH, MI_MIN_KEY_BLOCK_LENGTH, MI_MAX_KEY_BLOCK_LENGTH,
- 0, MI_MIN_KEY_BLOCK_LENGTH, 0},
- {"myisam_data_pointer_size", OPT_MYISAM_DATA_POINTER_SIZE,
- "Default pointer size to be used for MyISAM tables.",
- &myisam_data_pointer_size,
- &myisam_data_pointer_size, 0, GET_ULONG, REQUIRED_ARG,
- 6, 2, 7, 0, 1, 0},
- {"myisam_max_extra_sort_file_size", OPT_MYISAM_MAX_EXTRA_SORT_FILE_SIZE,
- "This is a deprecated option that does nothing anymore. "
- "It will be removed in MySQL " VER_CELOSIA,
- &global_system_variables.myisam_max_extra_sort_file_size,
- &max_system_variables.myisam_max_extra_sort_file_size,
- 0, GET_ULL, REQUIRED_ARG, (ulonglong) INT_MAX32,
- 0, MAX_FILE_SIZE, 0, 1, 0},
- {"myisam_max_sort_file_size", OPT_MYISAM_MAX_SORT_FILE_SIZE,
- "Don't use the fast sort index method to created index if the temporary "
- "file would get bigger than this.",
- &global_system_variables.myisam_max_sort_file_size,
- &max_system_variables.myisam_max_sort_file_size, 0,
- GET_ULL, REQUIRED_ARG, (longlong) LONG_MAX, 0, MAX_FILE_SIZE,
- 0, 1024*1024, 0},
- {"myisam_mmap_size", OPT_MYISAM_MMAP_SIZE,
- "Can be used to restrict the total memory used for memory mmaping of myisam files",
- &myisam_mmap_size, &myisam_mmap_size, 0,
- GET_ULL, REQUIRED_ARG, (longlong) SIZE_T_MAX, MEMMAP_EXTRA_MARGIN, SIZE_T_MAX,
- 0, 1, 0},
- {"myisam_repair_threads", OPT_MYISAM_REPAIR_THREADS,
- "Specifies whether several threads should be used when repairing MyISAM "
- "tables. For values > 1, one thread is used per index. The value of 1 "
- "disables parallel repair.",
- &global_system_variables.myisam_repair_threads,
- &max_system_variables.myisam_repair_threads, 0,
- GET_ULONG, REQUIRED_ARG, 1, 1, (ulonglong) ULONG_MAX, 0, 1, 0},
- {"myisam_sort_buffer_size", OPT_MYISAM_SORT_BUFFER_SIZE,
- "The buffer that is allocated when sorting the index when doing a REPAIR "
- "or when creating indexes with CREATE INDEX or ALTER TABLE.",
- &global_system_variables.myisam_sort_buff_size,
- &max_system_variables.myisam_sort_buff_size, 0,
- GET_ULONG, REQUIRED_ARG, 8192 * 1024, 4096, ~0ULL, 0, 1, 0},
- {"myisam_use_mmap", OPT_MYISAM_USE_MMAP,
- "Use memory mapping for reading and writing MyISAM tables.",
- &opt_myisam_use_mmap, &opt_myisam_use_mmap, 0, GET_BOOL, NO_ARG,
- 0, 0, 0, 0, 0, 0},
- {"myisam_stats_method", OPT_MYISAM_STATS_METHOD,
- "Specifies how MyISAM index statistics collection code should threat NULLs. "
- "Possible values of name are \"nulls_unequal\" (default behavior for 4.1/5.0), "
- "\"nulls_equal\" (emulate 4.0 behavior), and \"nulls_ignored\".",
- &myisam_stats_method_str, &myisam_stats_method_str, 0,
- GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"net_buffer_length", OPT_NET_BUFFER_LENGTH,
- "Buffer length for TCP/IP and socket communication.",
- &global_system_variables.net_buffer_length,
- &max_system_variables.net_buffer_length, 0, GET_ULONG,
- REQUIRED_ARG, 16384, 1024, 1024*1024L, 0, 1024, 0},
- {"net_read_timeout", OPT_NET_READ_TIMEOUT,
- "Number of seconds to wait for more data from a connection before aborting the read.",
- &global_system_variables.net_read_timeout,
- &max_system_variables.net_read_timeout, 0, GET_ULONG,
- REQUIRED_ARG, NET_READ_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0},
- {"net_retry_count", OPT_NET_RETRY_COUNT,
- "If a read on a communication port is interrupted, retry this many times before giving up.",
- &global_system_variables.net_retry_count,
- &max_system_variables.net_retry_count,0,
- GET_ULONG, REQUIRED_ARG, MYSQLD_NET_RETRY_COUNT, 1, (ulonglong) ULONG_MAX,
- 0, 1, 0},
- {"net_write_timeout", OPT_NET_WRITE_TIMEOUT,
- "Number of seconds to wait for a block to be written to a connection before "
- "aborting the write.",
- &global_system_variables.net_write_timeout,
- &max_system_variables.net_write_timeout, 0, GET_ULONG,
- REQUIRED_ARG, NET_WRITE_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0},
- { "old", OPT_OLD_MODE, "Use compatible behavior.",
- &global_system_variables.old_mode,
- &max_system_variables.old_mode, 0, GET_BOOL, NO_ARG,
- 0, 0, 0, 0, 0, 0},
- {"open_files_limit", OPT_OPEN_FILES_LIMIT,
- "If this is not 0, then mysqld will use this value to reserve file "
- "descriptors to use with setrlimit(). If this value is 0 then mysqld "
- "will reserve max_connections*5 or max_connections + table_cache*2 "
- "(whichever is larger) number of files.",
- &open_files_limit, &open_files_limit, 0, GET_ULONG,
- REQUIRED_ARG, 0, 0, OS_FILE_LIMIT, 0, 1, 0},
- {"optimizer_prune_level", OPT_OPTIMIZER_PRUNE_LEVEL,
- "Controls the heuristic(s) applied during query optimization to prune "
- "less-promising partial plans from the optimizer search space. Meaning: "
- "0 - do not apply any heuristic, thus perform exhaustive search; 1 - "
- "prune plans based on number of retrieved rows.",
- &global_system_variables.optimizer_prune_level,
- &max_system_variables.optimizer_prune_level,
- 0, GET_ULONG, OPT_ARG, 1, 0, 1, 0, 1, 0},
- {"optimizer_search_depth", OPT_OPTIMIZER_SEARCH_DEPTH,
- "Maximum depth of search performed by the query optimizer. Values larger "
- "than the number of relations in a query result in better query plans, "
- "but take longer to compile a query. Smaller values than the number of "
- "tables in a relation result in faster optimization, but may produce "
- "very bad query plans. If set to 0, the system will automatically pick "
- "a reasonable value; if set to MAX_TABLES+2, the optimizer will switch "
- "to the original find_best (used for testing/comparison).",
- &global_system_variables.optimizer_search_depth,
- &max_system_variables.optimizer_search_depth,
- 0, GET_ULONG, OPT_ARG, MAX_TABLES+1, 0, MAX_TABLES+2, 0, 1, 0},
- {"optimizer_switch", OPT_OPTIMIZER_SWITCH,
- "optimizer_switch=option=val[,option=val...], where option={index_merge, "
- "index_merge_union, index_merge_sort_union, index_merge_intersection, "
- "index_merge_sort_intersection, index_condition_pushdown, "
- "derived_merge, derived_with_keys, "
- "firstmatch, loosescan, materialization, in_to_exists, "
- "semijoin, partial_match_rowid_merge, partial_match_table_scan, "
- "subquery_cache, mrr, mrr_cost_based, mrr_sort_keys, "
- "outer_join_with_cache, semijoin_with_cache, "
- "join_cache_incremental, join_cache_hashed, join_cache_bka, "
- "optimize_join_buffer_size"
- ", table_elimination"
- "} and val={on, off, default}.",
- &optimizer_switch_str, &optimizer_switch_str, 0, GET_STR, REQUIRED_ARG,
- /*OPTIMIZER_SWITCH_DEFAULT*/0, 0, 0, 0, 0, 0},
- {"plugin_dir", OPT_PLUGIN_DIR,
- "Directory for plugins.",
- &opt_plugin_dir_ptr, &opt_plugin_dir_ptr, 0,
- GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"plugin-load", OPT_PLUGIN_LOAD,
- "Optional semicolon-separated list of plugins to load, where each plugin is "
- "identified as name=library, where name is the plugin name and library "
- "is the plugin library in plugin_dir.",
+ {"plugin-load", 0,
+ "Semicolon-separated list of plugins to load, where each plugin is "
+ "specified as ether a plugin_name=library_file pair or only a library_file. "
+ "If the latter case, all plugins from a given library_file will be loaded.",
&opt_plugin_load, &opt_plugin_load, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"plugin-maturity", OPT_PLUGIN_MATURITY,
- "The lowest desirable plugin maturity. Plugins less mature than that will not be installed or loaded.",
- (uchar**) &plugin_maturity, (uchar**) &plugin_maturity, &plugin_maturity_values,
- GET_ENUM, REQUIRED_ARG, server_maturity, 0, 0, 0, 0, 0},
- {"preload_buffer_size", OPT_PRELOAD_BUFFER_SIZE,
- "The size of the buffer that is allocated when preloading indexes.",
- &global_system_variables.preload_buff_size,
- &max_system_variables.preload_buff_size, 0, GET_ULONG,
- REQUIRED_ARG, 32*1024L, 1024, 1024*1024*1024L, 0, 1, 0},
- {"progress_report_time", OPT_PROGRESS_REPORT_TIME,
- "Seconds between sending progress reports to the client for slow commands. Set to 0 to disable progress reporting.",
- &global_system_variables.progress_report_time,
- &max_system_variables.progress_report_time,
- 0, GET_ULONG, REQUIRED_ARG, 5, 0, ULONG_MAX, 0, 1, 0},
- {"query_alloc_block_size", OPT_QUERY_ALLOC_BLOCK_SIZE,
- "Allocation block size for query parsing and execution.",
- &global_system_variables.query_alloc_block_size,
- &max_system_variables.query_alloc_block_size, 0, GET_ULONG,
- REQUIRED_ARG, QUERY_ALLOC_BLOCK_SIZE, 1024, (ulonglong) ULONG_MAX, 0, 1024,
- 0},
-#ifdef HAVE_QUERY_CACHE
- {"query_cache_limit", OPT_QUERY_CACHE_LIMIT,
- "Don't cache results that are bigger than this.",
- &query_cache_limit, &query_cache_limit, 0, GET_ULONG,
- REQUIRED_ARG, 1024*1024L, 0, (ulonglong) ULONG_MAX, 0, 1, 0},
- {"query_cache_min_res_unit", OPT_QUERY_CACHE_MIN_RES_UNIT,
- "Minimal size of unit in which space for results is allocated (last unit "
- "will be trimmed after writing all result data).",
- &query_cache_min_res_unit, &query_cache_min_res_unit,
- 0, GET_ULONG, REQUIRED_ARG, QUERY_CACHE_MIN_RESULT_DATA_SIZE,
- 0, (ulonglong) ULONG_MAX, 0, 1, 0},
-#endif /*HAVE_QUERY_CACHE*/
- {"query_cache_size", OPT_QUERY_CACHE_SIZE,
- "The memory allocated to store results from old queries.",
- &query_cache_size, &query_cache_size, 0, GET_ULONG,
- REQUIRED_ARG, 0, 0, ULONG_MAX, 0, 1024, 0},
-#ifdef HAVE_QUERY_CACHE
- {"query_cache_strip_comments", OPT_QUERY_CACHE_STRIP_COMMENTS,
- "Enable and disable optimisation \"strip comment for query cache\" - "
- "optimisation strip all comments from query while search query result "
- "in query cache",
- (uchar**) &opt_query_cache_strip_comments,
- (uchar**) &opt_query_cache_strip_comments,
- 0, GET_BOOL, REQUIRED_ARG, 0, 0, 1, 0, 1, 0},
- {"query_cache_type", OPT_QUERY_CACHE_TYPE,
- "0 = OFF = Don't cache or retrieve results. 1 = ON = Cache all results "
- "except SELECT SQL_NO_CACHE ... queries. 2 = DEMAND = Cache only SELECT "
- "SQL_CACHE ... queries.", &global_system_variables.query_cache_type,
- &max_system_variables.query_cache_type,
- 0, GET_ULONG, REQUIRED_ARG, 1, 0, 2, 0, 1, 0},
- {"query_cache_wlock_invalidate", OPT_QUERY_CACHE_WLOCK_INVALIDATE,
- "Invalidate queries in query cache on LOCK for write.",
- &global_system_variables.query_cache_wlock_invalidate,
- &max_system_variables.query_cache_wlock_invalidate,
- 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 1, 0},
-#endif /*HAVE_QUERY_CACHE*/
- {"query_prealloc_size", OPT_QUERY_PREALLOC_SIZE,
- "Persistent buffer for query parsing and execution.",
- &global_system_variables.query_prealloc_size,
- &max_system_variables.query_prealloc_size, 0, GET_ULONG,
- REQUIRED_ARG, QUERY_ALLOC_PREALLOC_SIZE, QUERY_ALLOC_PREALLOC_SIZE,
- (ulonglong) ULONG_MAX, 0, 1024, 0},
- {"range_alloc_block_size", OPT_RANGE_ALLOC_BLOCK_SIZE,
- "Allocation block size for storing ranges during optimization.",
- &global_system_variables.range_alloc_block_size,
- &max_system_variables.range_alloc_block_size, 0, GET_ULONG,
- REQUIRED_ARG, RANGE_ALLOC_BLOCK_SIZE, RANGE_ALLOC_BLOCK_SIZE,
- (ulonglong) ULONG_MAX, 0, 1024, 0},
- {"rowid_merge_buff_size", OPT_ROWID_MERGE_BUFF_SIZE,
- "The size of the buffers used [NOT] IN evaluation via partial matching.",
- (uchar**) &global_system_variables.rowid_merge_buff_size,
- (uchar**) &max_system_variables.rowid_merge_buff_size, 0, GET_ULONG,
- REQUIRED_ARG, 8*1024*1024L, 0, MAX_MEM_TABLE_SIZE/2, 0, 1, 0},
- {"read_buffer_size", OPT_RECORD_BUFFER,
- "Each thread that does a sequential scan allocates a buffer of this size "
- "for each table it scans. If you do many sequential scans, you may want "
- "to increase this value.", &global_system_variables.read_buff_size,
- &max_system_variables.read_buff_size,0, GET_ULONG, REQUIRED_ARG,
- 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, INT_MAX32, MALLOC_OVERHEAD, IO_SIZE,
- 0},
- {"read_only", OPT_READONLY,
- "Make all non-temporary tables read-only, with the exception of replication "
- "(slave) threads and users with the SUPER privilege.",
- &opt_readonly,
- &opt_readonly,
- 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 1, 0},
- {"read_rnd_buffer_size", OPT_RECORD_RND_BUFFER,
- "When reading rows in sorted order after a sort, the rows are read through "
- "this buffer to avoid disk seeks. If not set, then it's set to the value of "
- "record_buffer.",
- &global_system_variables.read_rnd_buff_size,
- &max_system_variables.read_rnd_buff_size, 0,
- GET_ULONG, REQUIRED_ARG, 256*1024L, IO_SIZE*2+MALLOC_OVERHEAD,
- INT_MAX32, MALLOC_OVERHEAD, IO_SIZE, 0},
- {"record_buffer", OPT_RECORD_BUFFER_OLD,
- "Alias for read_buffer_size. This variable is deprecated and will be removed in a future release.",
- &global_system_variables.read_buff_size,
- &max_system_variables.read_buff_size,0, GET_ULONG, REQUIRED_ARG,
- 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, INT_MAX32, MALLOC_OVERHEAD, IO_SIZE, 0},
-#ifdef HAVE_REPLICATION
- {"relay_log_purge", OPT_RELAY_LOG_PURGE,
- "0 = do not purge relay logs. 1 = purge them as soon as they are no more needed.",
- &relay_log_purge,
- &relay_log_purge, 0, GET_BOOL, NO_ARG,
- 1, 0, 1, 0, 1, 0},
- {"relay_log_space_limit", OPT_RELAY_LOG_SPACE_LIMIT,
- "Maximum space to use for all relay logs.",
- &relay_log_space_limit,
- &relay_log_space_limit, 0, GET_ULL, REQUIRED_ARG, 0L, 0L,
- ULONG_MAX, 0, 1, 0},
- {"slave_compressed_protocol", OPT_SLAVE_COMPRESSED_PROTOCOL,
- "Use compression on master/slave protocol.",
- &opt_slave_compressed_protocol,
- &opt_slave_compressed_protocol,
- 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 1, 0},
- {"slave_net_timeout", OPT_SLAVE_NET_TIMEOUT,
- "Number of seconds to wait for more data from a master/slave connection before aborting the read.",
- &slave_net_timeout, &slave_net_timeout, 0,
- GET_ULONG, REQUIRED_ARG, SLAVE_NET_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0},
- {"slave_transaction_retries", OPT_SLAVE_TRANS_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.",
- &slave_trans_retries, &slave_trans_retries, 0,
- GET_ULONG, REQUIRED_ARG, 10L, 0L, ULONG_MAX, 0, 1, 0},
-#endif /* HAVE_REPLICATION */
- {"slow_launch_time", OPT_SLOW_LAUNCH_TIME,
- "If creating the thread takes longer than this value (in seconds), "
- "the Slow_launch_threads counter will be incremented.",
- &slow_launch_time, &slow_launch_time, 0, GET_ULONG,
- REQUIRED_ARG, 2L, 0L, LONG_TIMEOUT, 0, 1, 0},
- {"sort_buffer_size", OPT_SORT_BUFFER,
- "Each thread that needs to do a sort allocates a buffer of this size.",
- &global_system_variables.sortbuff_size,
- &max_system_variables.sortbuff_size, 0, GET_ULONG, REQUIRED_ARG,
- MAX_SORT_MEMORY, MIN_SORT_MEMORY+MALLOC_OVERHEAD*2, ~0ULL, MALLOC_OVERHEAD,
- 1, 0},
- {"sync-binlog", OPT_SYNC_BINLOG,
- "Synchronously flush binary log to disk after every #th event. "
- "Use 0 (default) to disable synchronous flushing.",
- &sync_binlog_period, &sync_binlog_period, 0, GET_ULONG,
- REQUIRED_ARG, 0, 0, (ulonglong) ULONG_MAX, 0, 1, 0},
- {"sync-frm", OPT_SYNC_FRM, "Sync .frm to disk on create. Enabled by default.",
- &opt_sync_frm, &opt_sync_frm, 0, GET_BOOL, NO_ARG, 1, 0,
- 0, 0, 0, 0},
- {"sync-sys", OPT_SYNC,
- "Enable/disable system sync calls. Should only be turned off when running "
- "tests or debugging!!",
- &opt_sync, &opt_sync, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
- {"table_cache", OPT_TABLE_OPEN_CACHE,
- "Deprecated; use --table_open_cache instead.",
+ {"table_cache", 0, "Deprecated; use --table-open-cache instead.",
&table_cache_size, &table_cache_size, 0, GET_ULONG,
REQUIRED_ARG, TABLE_OPEN_CACHE_DEFAULT, 1, 512*1024L, 0, 1, 0},
- {"table_definition_cache", OPT_TABLE_DEF_CACHE,
- "The number of cached table definitions.",
- &table_def_size, &table_def_size,
- 0, GET_ULONG, REQUIRED_ARG, TABLE_DEF_CACHE_DEFAULT, TABLE_DEF_CACHE_MIN,
- 512*1024L, 0, 1, 0},
- {"table_open_cache", OPT_TABLE_OPEN_CACHE,
- "The number of cached open tables.",
- &table_cache_size, &table_cache_size, 0, GET_ULONG,
- REQUIRED_ARG, TABLE_OPEN_CACHE_DEFAULT, 1, 512*1024L, 0, 1, 0},
- {"table_lock_wait_timeout", OPT_TABLE_LOCK_WAIT_TIMEOUT,
- "Timeout in seconds to wait for a table level lock before returning an "
- "error. Used only if the connection has active cursors.",
- &table_lock_wait_timeout, &table_lock_wait_timeout,
- 0, GET_ULONG, REQUIRED_ARG, 50, 1, 1024 * 1024 * 1024, 0, 1, 0},
- {"thread-alarm", OPT_THREAD_ALARM,
- "Enable/disable system thread alarm calls. Should only be turned off when running tests or debugging!!",
- &opt_thread_alarm, &opt_thread_alarm, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0,
- 0},
- {"thread_cache_size", OPT_THREAD_CACHE_SIZE,
- "How many threads we should keep in a cache for reuse.",
- &thread_cache_size, &thread_cache_size, 0, GET_ULONG,
- REQUIRED_ARG, 0, 0, 16384, 0, 1, 0},
- {"thread_concurrency", OPT_THREAD_CONCURRENCY,
- "Permits the application to give the threads system a hint for the "
- "desired number of threads that should be run at the same time.",
- &concurrency, &concurrency, 0, GET_ULONG, REQUIRED_ARG,
- DEFAULT_CONCURRENCY, 1, 512, 0, 1, 0},
-#if HAVE_POOL_OF_THREADS == 1
- {"thread_pool_size", OPT_THREAD_CACHE_SIZE,
- "How many threads we should create to handle query requests in case of "
- "'thread_handling=pool-of-threads'.",
- &thread_pool_size, &thread_pool_size, 0, GET_ULONG,
- REQUIRED_ARG, 20, 1, 16384, 0, 1, 0},
-#endif
- {"thread_stack", OPT_THREAD_STACK,
- "The stack size for each thread.", &my_thread_stack_size,
- &my_thread_stack_size, 0, GET_ULONG, REQUIRED_ARG,DEFAULT_THREAD_STACK,
- (sizeof(void*)<=4)?1024L*128L: ((256-16)*1024L), (ulonglong) ULONG_MAX, 0, 1024, 0},
- { "time_format", OPT_TIME_FORMAT,
- "The TIME format (for future).",
- &opt_date_time_formats[MYSQL_TIMESTAMP_TIME],
- &opt_date_time_formats[MYSQL_TIMESTAMP_TIME],
- 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"tmp_table_size", OPT_TMP_TABLE_SIZE,
- "If an internal in-memory temporary table exceeds this size, MySQL will "
- "automatically convert it to an on-disk MyISAM/Aria table.",
- &global_system_variables.tmp_table_size,
- &max_system_variables.tmp_table_size, 0, GET_ULL,
- REQUIRED_ARG, 16*1024*1024L, 1024, MAX_MEM_TABLE_SIZE, 0, 1, 0},
- {"transaction_alloc_block_size", OPT_TRANS_ALLOC_BLOCK_SIZE,
- "Allocation block size for transactions to be stored in binary log.",
- &global_system_variables.trans_alloc_block_size,
- &max_system_variables.trans_alloc_block_size, 0, GET_ULONG,
- REQUIRED_ARG, QUERY_ALLOC_BLOCK_SIZE, 1024, (ulonglong) ULONG_MAX, 0, 1024,
- 0},
- {"transaction_prealloc_size", OPT_TRANS_PREALLOC_SIZE,
- "Persistent buffer for transactions to be stored in binary log.",
- &global_system_variables.trans_prealloc_size,
- &max_system_variables.trans_prealloc_size, 0, GET_ULONG,
- REQUIRED_ARG, TRANS_ALLOC_PREALLOC_SIZE, 1024, (ulonglong) ULONG_MAX, 0,
- 1024, 0},
- {"thread_handling", OPT_THREAD_HANDLING,
- "Define threads usage for handling queries: "
- "one-thread-per-connection"
-#if HAVE_POOL_OF_THREADS == 1
- ", pool-of-threads "
-#endif
- "or no-threads.",
- &opt_thread_handling, &opt_thread_handling,
- 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
- {"updatable_views_with_limit", OPT_UPDATABLE_VIEWS_WITH_LIMIT,
- "1 = YES = Don't issue an error message (warning only) if a VIEW without "
- "presence of a key of the underlying table is used in queries with a "
- "LIMIT clause for updating. 0 = NO = Prohibit update of a VIEW, which "
- "does not contain a key of the underlying table and the query uses a "
- "LIMIT clause (usually get from GUI tools).",
- &global_system_variables.updatable_views_with_limit,
- &max_system_variables.updatable_views_with_limit,
- 0, GET_ULONG, REQUIRED_ARG, 1, 0, 1, 0, 1, 0},
- {"wait_timeout", OPT_WAIT_TIMEOUT,
- "The number of seconds the server waits for activity on a connection before closing it.",
- &global_system_variables.net_wait_timeout,
- &max_system_variables.net_wait_timeout, 0, GET_ULONG,
- REQUIRED_ARG, NET_WAIT_TIMEOUT, 1, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT),
- 0, 1, 0},
- {"userstat", OPT_USERSTAT,
- "Control USER_STATISTICS, CLIENT_STATISTICS, INDEX_STATISTICS and TABLE_STATISTICS running",
- (uchar**) &opt_userstat_running, (uchar**) &opt_userstat_running,
- 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 1, 0},
- {"binlog-direct-non-transactional-updates", OPT_BINLOG_DIRECT_NON_TRANS_UPDATE,
- "Causes updates to non-transactional engines using statement format to be "
- "written directly to binary log. Before using this option, make sure that "
- "there are no dependencies between transactional and non-transactional "
- "tables such as in the statement INSERT INTO t_myisam SELECT * FROM "
- "t_innodb; otherwise, slaves may diverge from the master.",
- &global_system_variables.binlog_direct_non_trans_update,
- &max_system_variables.binlog_direct_non_trans_update,
- 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
@@ -7729,7 +6604,7 @@ static int show_starttime(THD *thd, SHOW_VAR *var, char *buff)
return 0;
}
-#ifdef COMMUNITY_SERVER
+#ifdef ENABLED_PROFILING
static int show_flushstatustime(THD *thd, SHOW_VAR *var, char *buff)
{
var->type= SHOW_LONG;
@@ -7750,12 +6625,12 @@ static int show_rpl_status(THD *thd, SHOW_VAR *var, char *buff)
static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff)
{
var->type= SHOW_MY_BOOL;
- pthread_mutex_lock(&LOCK_active_mi);
+ mysql_mutex_lock(&LOCK_active_mi);
var->value= buff;
*((my_bool *)buff)= (my_bool) (active_mi &&
active_mi->slave_running == MYSQL_SLAVE_RUN_CONNECT &&
active_mi->rli.slave_running);
- pthread_mutex_unlock(&LOCK_active_mi);
+ mysql_mutex_unlock(&LOCK_active_mi);
return 0;
}
@@ -7765,20 +6640,54 @@ static int show_slave_retried_trans(THD *thd, SHOW_VAR *var, char *buff)
TODO: with multimaster, have one such counter per line in
SHOW SLAVE STATUS, and have the sum over all lines here.
*/
- pthread_mutex_lock(&LOCK_active_mi);
+ mysql_mutex_lock(&LOCK_active_mi);
if (active_mi)
{
var->type= SHOW_LONG;
var->value= buff;
- pthread_mutex_lock(&active_mi->rli.data_lock);
+ mysql_mutex_lock(&active_mi->rli.data_lock);
*((long *)buff)= (long)active_mi->rli.retried_trans;
- pthread_mutex_unlock(&active_mi->rli.data_lock);
+ mysql_mutex_unlock(&active_mi->rli.data_lock);
}
else
var->type= SHOW_UNDEF;
- pthread_mutex_unlock(&LOCK_active_mi);
+ mysql_mutex_unlock(&LOCK_active_mi);
return 0;
}
+
+static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, char *buff)
+{
+ mysql_mutex_lock(&LOCK_active_mi);
+ if (active_mi)
+ {
+ var->type= SHOW_LONGLONG;
+ var->value= buff;
+ mysql_mutex_lock(&active_mi->rli.data_lock);
+ *((longlong *)buff)= active_mi->received_heartbeats;
+ mysql_mutex_unlock(&active_mi->rli.data_lock);
+ }
+ else
+ var->type= SHOW_UNDEF;
+ mysql_mutex_unlock(&LOCK_active_mi);
+ return 0;
+}
+
+static int show_heartbeat_period(THD *thd, SHOW_VAR *var, char *buff)
+{
+ mysql_mutex_lock(&LOCK_active_mi);
+ if (active_mi)
+ {
+ var->type= SHOW_CHAR;
+ var->value= buff;
+ sprintf(buff, "%.3f", active_mi->heartbeat_period);
+ }
+ else
+ var->type= SHOW_UNDEF;
+ mysql_mutex_unlock(&LOCK_active_mi);
+ return 0;
+}
+
+
#endif /* HAVE_REPLICATION */
static int show_open_tables(THD *thd, SHOW_VAR *var, char *buff)
@@ -7793,9 +6702,9 @@ static int show_prepared_stmt_count(THD *thd, SHOW_VAR *var, char *buff)
{
var->type= SHOW_LONG;
var->value= buff;
- pthread_mutex_lock(&LOCK_prepared_stmt_count);
+ mysql_mutex_lock(&LOCK_prepared_stmt_count);
*((long *)buff)= (long)prepared_stmt_count;
- pthread_mutex_unlock(&LOCK_prepared_stmt_count);
+ mysql_mutex_unlock(&LOCK_prepared_stmt_count);
return 0;
}
@@ -7807,7 +6716,7 @@ static int show_table_definitions(THD *thd, SHOW_VAR *var, char *buff)
return 0;
}
-#ifdef HAVE_OPENSSL
+#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
/* Functions relying on CTX */
static int show_ssl_ctx_sess_accept(THD *thd, SHOW_VAR *var, char *buff)
{
@@ -8063,7 +6972,7 @@ static int show_ssl_get_cipher_list(THD *thd, SHOW_VAR *var, char *buff)
return 0;
}
-#endif /* HAVE_OPENSSL */
+#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
static int show_default_keycache(THD *thd, SHOW_VAR *var, char *buff)
{
@@ -8105,6 +7014,15 @@ static int show_default_keycache(THD *thd, SHOW_VAR *var, char *buff)
return 0;
}
+#ifdef HAVE_POOL_OF_THREADS
+int show_threadpool_idle_threads(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_INT;
+ var->value= buff;
+ *(int *)buff= tp_get_idle_thread_count();
+ return 0;
+}
+#endif
/*
Variables shown by SHOW STATUS in alphabetical order
@@ -8114,12 +7032,14 @@ SHOW_VAR status_vars[]= {
{"Aborted_clients", (char*) &aborted_threads, SHOW_LONG},
{"Aborted_connects", (char*) &aborted_connects, SHOW_LONG},
{"Access_denied_errors", (char*) offsetof(STATUS_VAR, access_denied_errors), SHOW_LONG_STATUS},
+ {"Binlog_bytes_written", (char*) offsetof(STATUS_VAR, binlog_bytes_written), SHOW_LONGLONG_STATUS},
{"Binlog_cache_disk_use", (char*) &binlog_cache_disk_use, SHOW_LONG},
{"Binlog_cache_use", (char*) &binlog_cache_use, SHOW_LONG},
+ {"Binlog_stmt_cache_disk_use",(char*) &binlog_stmt_cache_disk_use, SHOW_LONG},
+ {"Binlog_stmt_cache_use", (char*) &binlog_stmt_cache_use, SHOW_LONG},
{"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},
- {"Binlog_bytes_written", (char*) offsetof(STATUS_VAR, binlog_bytes_written), SHOW_LONGLONG_STATUS},
{"Com", (char*) com_status_vars, SHOW_ARRAY},
{"Compression", (char*) &show_net_compression, SHOW_FUNC},
{"Connections", (char*) &thread_id, SHOW_LONG_NOFLUSH},
@@ -8131,21 +7051,29 @@ SHOW_VAR status_vars[]= {
{"Delayed_insert_threads", (char*) &delayed_insert_threads, SHOW_LONG_NOFLUSH},
{"Delayed_writes", (char*) &delayed_insert_writes, SHOW_LONG},
{"Empty_queries", (char*) offsetof(STATUS_VAR, empty_queries), SHOW_LONG_STATUS},
+ {"Executed_events", (char*) &executed_events, SHOW_LONG_NOFLUSH },
+ {"Executed_triggers", (char*) offsetof(STATUS_VAR, executed_triggers), SHOW_LONG_STATUS},
+ {"Feature_dynamic_columns", (char*) offsetof(STATUS_VAR, feature_dynamic_columns), SHOW_LONG_STATUS},
+ {"Feature_fulltext", (char*) offsetof(STATUS_VAR, feature_fulltext), SHOW_LONG_STATUS},
+ {"Feature_gis", (char*) offsetof(STATUS_VAR, feature_gis), SHOW_LONG_STATUS},
+ {"Feature_locale", (char*) offsetof(STATUS_VAR, feature_locale), SHOW_LONG_STATUS},
+ {"Feature_subquery", (char*) offsetof(STATUS_VAR, feature_subquery), SHOW_LONG_STATUS},
+ {"Feature_timezone", (char*) offsetof(STATUS_VAR, feature_timezone), SHOW_LONG_STATUS},
+ {"Feature_trigger", (char*) offsetof(STATUS_VAR, feature_trigger), SHOW_LONG_STATUS},
+ {"Feature_xml", (char*) offsetof(STATUS_VAR, feature_xml), SHOW_LONG_STATUS},
{"Flush_commands", (char*) &refresh_version, SHOW_LONG_NOFLUSH},
{"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_icp_attempts ", (char*) offsetof(STATUS_VAR, ha_icp_attempts), 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},
{"Handler_mrr_key_refills", (char*) offsetof(STATUS_VAR, ha_mrr_key_refills_count), SHOW_LONG_STATUS},
{"Handler_mrr_rowid_refills", (char*) offsetof(STATUS_VAR, ha_mrr_rowid_refills_count), SHOW_LONG_STATUS},
-
{"Handler_prepare", (char*) offsetof(STATUS_VAR, ha_prepare_count), SHOW_LONG_STATUS},
{"Handler_read_first", (char*) offsetof(STATUS_VAR, ha_read_first_count), SHOW_LONG_STATUS},
{"Handler_read_key", (char*) offsetof(STATUS_VAR, ha_read_key_count), SHOW_LONG_STATUS},
+ {"Handler_read_last", (char*) offsetof(STATUS_VAR, ha_read_last_count), SHOW_LONG_STATUS},
{"Handler_read_next", (char*) offsetof(STATUS_VAR, ha_read_next_count), SHOW_LONG_STATUS},
{"Handler_read_prev", (char*) offsetof(STATUS_VAR, ha_read_prev_count), SHOW_LONG_STATUS},
{"Handler_read_rnd", (char*) offsetof(STATUS_VAR, ha_read_rnd_count), SHOW_LONG_STATUS},
@@ -8167,11 +7095,12 @@ SHOW_VAR status_vars[]= {
{"Open_table_definitions", (char*) &show_table_definitions, SHOW_FUNC},
{"Open_tables", (char*) &show_open_tables, SHOW_FUNC},
{"Opened_files", (char*) &my_file_total_opened, SHOW_LONG_NOFLUSH},
- {"Opened_tables", (char*) offsetof(STATUS_VAR, opened_tables), SHOW_LONG_STATUS},
{"Opened_table_definitions", (char*) offsetof(STATUS_VAR, opened_shares), SHOW_LONG_STATUS},
+ {"Opened_tables", (char*) offsetof(STATUS_VAR, opened_tables), SHOW_LONG_STATUS},
+ {"Opened_views", (char*) offsetof(STATUS_VAR, opened_views), SHOW_LONG_STATUS},
{"Prepared_stmt_count", (char*) &show_prepared_stmt_count, SHOW_FUNC},
- {"Rows_sent", (char*) offsetof(STATUS_VAR, rows_sent), SHOW_LONGLONG_STATUS},
{"Rows_read", (char*) offsetof(STATUS_VAR, rows_read), SHOW_LONGLONG_STATUS},
+ {"Rows_sent", (char*) offsetof(STATUS_VAR, rows_sent), SHOW_LONGLONG_STATUS},
{"Rows_tmp_read", (char*) offsetof(STATUS_VAR, rows_tmp_read), SHOW_LONGLONG_STATUS},
#ifdef HAVE_QUERY_CACHE
{"Qcache_free_blocks", (char*) &query_cache.free_memory_blocks, SHOW_LONG_NOFLUSH},
@@ -8195,6 +7124,8 @@ SHOW_VAR status_vars[]= {
{"Select_scan", (char*) offsetof(STATUS_VAR, select_scan_count), SHOW_LONG_STATUS},
{"Slave_open_temp_tables", (char*) &slave_open_temp_tables, SHOW_LONG},
#ifdef HAVE_REPLICATION
+ {"Slave_heartbeat_period", (char*) &show_heartbeat_period, SHOW_FUNC},
+ {"Slave_received_heartbeats",(char*) &show_slave_received_heartbeats, SHOW_FUNC},
{"Slave_retried_transactions",(char*) &show_slave_retried_trans, SHOW_FUNC},
{"Slave_running", (char*) &show_slave_running, SHOW_FUNC},
#endif
@@ -8205,6 +7136,7 @@ SHOW_VAR status_vars[]= {
{"Sort_rows", (char*) offsetof(STATUS_VAR, filesort_rows), SHOW_LONG_STATUS},
{"Sort_scan", (char*) offsetof(STATUS_VAR, filesort_scan_count), SHOW_LONG_STATUS},
#ifdef HAVE_OPENSSL
+#ifndef EMBEDDED_LIBRARY
{"Ssl_accept_renegotiates", (char*) &show_ssl_ctx_sess_accept_renegotiate, SHOW_FUNC},
{"Ssl_accepts", (char*) &show_ssl_ctx_sess_accept, SHOW_FUNC},
{"Ssl_callback_cache_hits", (char*) &show_ssl_ctx_sess_cb_hits, SHOW_FUNC},
@@ -8228,46 +7160,98 @@ SHOW_VAR status_vars[]= {
{"Ssl_verify_depth", (char*) &show_ssl_get_verify_depth, SHOW_FUNC},
{"Ssl_verify_mode", (char*) &show_ssl_get_verify_mode, SHOW_FUNC},
{"Ssl_version", (char*) &show_ssl_get_version, SHOW_FUNC},
+#endif
#endif /* HAVE_OPENSSL */
{"Syncs", (char*) &my_sync_count, SHOW_LONG_NOFLUSH},
/*
Expression cache used only for caching subqueries now, so its statistic
variables we call subquery_cache*.
*/
- {"Subquery_cache_hit", (char*) &subquery_cache_hit, SHOW_LONG},
- {"Subquery_cache_miss", (char*) &subquery_cache_miss, SHOW_LONG},
+ {"Subquery_cache_hit", (char*) &subquery_cache_hit, SHOW_LONG},
+ {"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},
#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},
+ {"Tc_log_page_size", (char*) &tc_log_page_size, SHOW_LONG_NOFLUSH},
{"Tc_log_page_waits", (char*) &tc_log_page_waits, SHOW_LONG},
#endif
+#ifdef HAVE_POOL_OF_THREADS
+ {"Threadpool_idle_threads", (char *) &show_threadpool_idle_threads, SHOW_FUNC},
+ {"Threadpool_threads", (char *) &tp_stats.num_worker_threads, SHOW_INT},
+#endif
{"Threads_cached", (char*) &cached_thread_count, SHOW_LONG_NOFLUSH},
- {"Threads_connected", (char*) &thread_count, SHOW_INT},
+ {"Threads_connected", (char*) &connection_count, SHOW_INT},
{"Threads_created", (char*) &thread_created, SHOW_LONG_NOFLUSH},
{"Threads_running", (char*) &thread_running, SHOW_INT},
{"Uptime", (char*) &show_starttime, SHOW_FUNC},
-#ifdef COMMUNITY_SERVER
+#ifdef ENABLED_PROFILING
{"Uptime_since_flush_status",(char*) &show_flushstatustime, SHOW_FUNC},
#endif
{NullS, NullS, SHOW_LONG}
};
+bool add_terminator(DYNAMIC_ARRAY *options)
+{
+ my_option empty_element= {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0};
+ return insert_dynamic(options, (uchar *)&empty_element);
+}
+
#ifndef EMBEDDED_LIBRARY
static void print_version(void)
{
set_server_version();
- /*
- Note: the instance manager keys off the string 'Ver' so it can find the
- version from the output of 'mysqld --version', so don't change it!
- */
+
printf("%s Ver %s for %s on %s (%s)\n",my_progname,
server_version,SYSTEM_TYPE,MACHINE_TYPE, MYSQL_COMPILATION_COMMENT);
}
+/** Compares two options' names, treats - and _ the same */
+static int option_cmp(my_option *a, my_option *b)
+{
+ const char *sa= a->name;
+ const char *sb= b->name;
+ for (; *sa || *sb; sa++, sb++)
+ {
+ if (*sa < *sb)
+ {
+ if (*sa == '-' && *sb == '_')
+ continue;
+ else
+ return -1;
+ }
+ if (*sa > *sb)
+ {
+ if (*sa == '_' && *sb == '-')
+ continue;
+ else
+ return 1;
+ }
+ }
+ DBUG_ASSERT(a->name == b->name);
+ return 0;
+}
+
+static void print_help()
+{
+ MEM_ROOT mem_root;
+ init_alloc_root(&mem_root, 4096, 4096);
+
+ pop_dynamic(&all_options);
+ sys_var_add_options(&all_options, sys_var::PARSE_EARLY);
+ add_plugin_options(&all_options, &mem_root);
+ sort_dynamic(&all_options, (qsort_cmp) option_cmp);
+ add_terminator(&all_options);
+
+ my_print_help((my_option*) all_options.buffer);
+ my_print_variables((my_option*) all_options.buffer);
+
+ free_root(&mem_root, MYF(0));
+}
+
static void usage(void)
{
+ DBUG_ENTER("usage");
if (!(default_charset_info= get_charset_by_csname(default_character_set_name,
MY_CS_PRIMARY,
MYF(MY_WME))))
@@ -8300,7 +7284,7 @@ static void usage(void)
set_ports();
/* Print out all the options including plugin supplied options */
- my_print_help_inc_plugins(my_long_options, sizeof(my_long_options)/sizeof(my_option));
+ print_help();
if (! plugins_are_initialized)
{
@@ -8313,15 +7297,12 @@ because execution stopped before plugins were initialized.");
To see what values a running MySQL server is using, type\n\
'mysqladmin variables' instead of 'mysqld --verbose --help'.");
}
+ DBUG_VOID_RETURN;
}
#endif /*!EMBEDDED_LIBRARY*/
-
/**
- Initialize all MySQL global variables to default values.
-
- We don't need to set numeric variables refered to in my_long_options
- as these are initialized by my_getopt.
+ Initialize MySQL global variables to default values.
@note
The reason to set a lot of global variables to zero is to allow one to
@@ -8329,13 +7310,12 @@ To see what values a running MySQL server is using, type\n\
It's also needed on some exotic platforms where global variables are
not set to 0 when a program starts.
- We don't need to set numeric variables refered to in my_long_options
+ We don't need to set variables refered to in my_long_options
as these are initialized by my_getopt.
*/
static int mysql_init_variables(void)
{
- int error;
/* Things reset to zero */
opt_skip_slave_start= opt_reckless_slave = 0;
mysql_home[0]= pidfile_name[0]= log_error_file[0]= 0;
@@ -8344,23 +7324,18 @@ static int mysql_init_variables(void)
myisam_test_invalid_symlink= test_if_data_home_dir;
#endif
opt_log= opt_slow_log= 0;
- opt_update_log= 0;
- log_output_options= find_bit_type(log_output_str, &log_output_typelib);
- opt_bin_log= 0;
+ opt_bin_log= opt_bin_log_used= 0;
opt_disable_networking= opt_skip_show_db=0;
opt_skip_name_resolve= 0;
opt_ignore_builtin_innodb= 0;
- opt_logname= opt_update_logname= opt_binlog_index_name= opt_slow_logname= 0;
+ opt_logname= opt_binlog_index_name= opt_slow_logname= 0;
opt_log_basename= 0;
opt_tc_log_file= (char *)"tc.log"; // no hostname in tc_log file name !
opt_secure_auth= 0;
- opt_secure_file_priv= 0;
opt_bootstrap= opt_myisam_log= 0;
mqh_used= 0;
kill_in_progress= 0;
cleanup_done= 0;
- defaults_argc= 0;
- defaults_argv= 0;
server_id_supplied= 0;
test_flags= select_errors= dropping_tables= ha_open_options=0;
thread_count= thread_running= kill_cached_threads= wake_thread=0;
@@ -8379,11 +7354,11 @@ static int mysql_init_variables(void)
max_used_connections= slow_launch_threads = 0;
mysqld_user= mysqld_chroot= opt_init_file= opt_bin_logname = 0;
prepared_stmt_count= 0;
- errmesg= 0;
mysqld_unix_port= opt_mysql_tmpdir= my_bind_addr_str= NullS;
bzero((uchar*) &mysql_tmpdir_list, sizeof(mysql_tmpdir_list));
bzero((char *) &global_status_var, sizeof(global_status_var));
opt_large_pages= 0;
+ opt_super_large_pages= 0;
#if defined(ENABLED_DEBUG_SYNC)
opt_debug_sync_timeout= 0;
#endif /* defined(ENABLED_DEBUG_SYNC) */
@@ -8396,40 +7371,19 @@ static int mysql_init_variables(void)
table_alias_charset= &my_charset_bin;
character_set_filesystem= &my_charset_bin;
- opt_date_time_formats[0]= opt_date_time_formats[1]= opt_date_time_formats[2]= 0;
-
- /* Things with default values that are not zero */
- delay_key_write_options= (uint) DELAY_KEY_WRITE_ON;
- slave_exec_mode_options= find_bit_type_or_exit(slave_exec_mode_str,
- &slave_exec_mode_typelib,
- NULL, &error);
- /* Default mode string must not yield a error. */
- DBUG_ASSERT(!error);
- if (error)
- return 1;
opt_specialflag= SPECIAL_ENGLISH;
unix_sock= base_ip_sock= extra_ip_sock= INVALID_SOCKET;
mysql_home_ptr= mysql_home;
pidfile_name_ptr= pidfile_name;
log_error_file_ptr= log_error_file;
- language_ptr= language;
- mysql_data_home= mysql_real_data_home;
- thd_startup_options= (OPTION_AUTO_IS_NULL | OPTION_BIN_LOG |
- OPTION_QUOTE_SHOW_CREATE | OPTION_SQL_NOTES);
protocol_version= PROTOCOL_VERSION;
what_to_log= ~ (1L << (uint) COM_TIME);
- refresh_version= 1L; /* Increments on each reload */
+ refresh_version= 2L; /* Increments on each reload. 0 and 1 are reserved */
+ executed_events= 0;
global_query_id= thread_id= 1L;
+ my_atomic_rwlock_init(&global_query_id_lock);
+ my_atomic_rwlock_init(&thread_running_lock);
strmov(server_version, MYSQL_SERVER_VERSION);
- sql_mode_str= "";
-
- /* By default, auto-repair MyISAM tables after crash */
- myisam_recover_options_str= "DEFAULT";
- myisam_recover_options= HA_RECOVER_DEFAULT;
- ha_open_options|= HA_OPEN_ABORT_IF_CRASHED;
-
- myisam_stats_method_str= "nulls_unequal";
- my_bind_addr = htonl(INADDR_ANY);
threads.empty();
thread_cache.empty();
key_caches.empty();
@@ -8443,28 +7397,13 @@ static int mysql_init_variables(void)
/* set key_cache_hash.default_value = dflt_key_cache */
multi_keycache_init();
- /* Useful MariaDB variables */
- double2decimal((double) TIME_MAX_VALUE_SECONDS +
- TIME_MAX_SECOND_PART/(double)TIME_SECOND_PART_FACTOR,
- &max_seconds_for_time_type);
- longlong2decimal(TIME_SECOND_PART_FACTOR, &time_second_part_factor);
- my_decimal_set_zero(&decimal_zero); // set decimal_zero constant;
-
/* Set directory paths */
- strmake(language, LANGUAGE, sizeof(language)-1);
- strmake(mysql_real_data_home, get_relative_path(MYSQL_DATADIR),
- sizeof(mysql_real_data_home)-1);
- mysql_data_home_buff[0]=FN_CURLIB; // all paths are relative from here
- mysql_data_home_buff[1]=0;
- mysql_data_home_len= 2;
-
+ mysql_real_data_home_len=
+ strmake_buf(mysql_real_data_home,
+ get_relative_path(MYSQL_DATADIR)) - mysql_real_data_home;
/* Replication parameters */
- master_user= (char*) "test";
- master_password= master_host= 0;
master_info_file= (char*) "master.info",
relay_log_info_file= (char*) "relay-log.info";
- master_ssl_key= master_ssl_cert= master_ssl_ca=
- master_ssl_capath= master_ssl_cipher= 0;
report_user= report_password = report_host= 0; /* TO BE DELETED */
opt_relay_logname= opt_relaylog_index_name= 0;
@@ -8472,30 +7411,10 @@ static int mysql_init_variables(void)
charsets_dir= 0;
default_character_set_name= (char*) MYSQL_DEFAULT_CHARSET_NAME;
default_collation_name= compiled_default_collation_name;
- sys_charset_system.value= (char*) system_charset_info->csname;
character_set_filesystem_name= (char*) "binary";
+ lc_messages= (char*) "en_US";
lc_time_names_name= (char*) "en_US";
- /* Set default values for some option variables */
- default_storage_engine_str= (char*) "MyISAM";
- global_system_variables.table_plugin= NULL;
- global_system_variables.tx_isolation= ISO_REPEATABLE_READ;
- global_system_variables.select_limit= (ulonglong) HA_POS_ERROR;
- max_system_variables.select_limit= (ulonglong) HA_POS_ERROR;
- global_system_variables.max_join_size= (ulonglong) HA_POS_ERROR;
- max_system_variables.max_join_size= (ulonglong) HA_POS_ERROR;
- global_system_variables.old_passwords= 0;
- global_system_variables.old_alter_table= 0;
- global_system_variables.binlog_format= BINLOG_FORMAT_UNSPEC;
- global_system_variables.log_slow_verbosity= LOG_SLOW_VERBOSITY_INIT;
- global_system_variables.log_slow_filter= QPLAN_ALWAYS_SET;
- /*
- Default behavior for 4.1 and 5.0 is to treat NULL values as unequal
- when collecting index statistics for MyISAM tables.
- */
- global_system_variables.myisam_stats_method= MI_STATS_METHOD_NULLS_NOT_EQUAL;
-
- global_system_variables.optimizer_switch= OPTIMIZER_SWITCH_DEFAULT;
/* Variables that depends on compile options */
#ifndef DBUG_OFF
default_dbug_option=IF_WIN("d:t:i:O,\\mysqld.trace",
@@ -8503,18 +7422,13 @@ static int mysql_init_variables(void)
current_dbug_option= default_dbug_option;
#endif
opt_error_log= IF_WIN(1,0);
-#ifdef COMMUNITY_SERVER
- have_community_features = SHOW_OPTION_YES;
+#ifdef ENABLED_PROFILING
+ have_profiling = SHOW_OPTION_YES;
#else
- have_community_features = SHOW_OPTION_NO;
-#endif
- global_system_variables.ndb_index_stat_enable=FALSE;
- max_system_variables.ndb_index_stat_enable=TRUE;
- global_system_variables.ndb_index_stat_cache_entries=32;
- max_system_variables.ndb_index_stat_cache_entries=~0L;
- global_system_variables.ndb_index_stat_update_freq=20;
- max_system_variables.ndb_index_stat_update_freq=~0L;
-#ifdef HAVE_OPENSSL
+ have_profiling = SHOW_OPTION_NO;
+#endif
+
+#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
have_ssl=SHOW_OPTION_YES;
#else
have_ssl=SHOW_OPTION_NO;
@@ -8559,32 +7473,29 @@ static int mysql_init_variables(void)
#endif
#ifdef HAVE_OPENSSL
des_key_file = 0;
+#ifndef EMBEDDED_LIBRARY
ssl_acceptor_fd= 0;
-#endif
+#endif /* ! EMBEDDED_LIBRARY */
+#endif /* HAVE_OPENSSL */
#ifdef HAVE_SMEM
shared_memory_base_name= default_shared_memory_base_name;
#endif
-#if !defined(my_pthread_setprio) && !defined(HAVE_PTHREAD_SETSCHEDPARAM)
- opt_specialflag |= SPECIAL_NO_PRIOR;
-#endif
-#if defined(__WIN__) || defined(__NETWARE__)
- /* Allow Win32 and NetWare users to move MySQL anywhere */
+#if defined(__WIN__)
+ /* Allow Win32 users to move MySQL anywhere */
{
char prg_dev[LIBLEN];
-#if defined __WIN__
- char executing_path_name[LIBLEN];
- if (!test_if_hard_path(my_progname))
- {
- // we don't want to use GetModuleFileName inside of my_path since
- // my_path is a generic path dereferencing function and here we care
- // only about the executing binary.
- GetModuleFileName(NULL, executing_path_name, sizeof(executing_path_name));
- my_path(prg_dev, executing_path_name, NULL);
- }
- else
-#endif
- my_path(prg_dev,my_progname,"mysql/bin");
+ char executing_path_name[LIBLEN];
+ if (!test_if_hard_path(my_progname))
+ {
+ // we don't want to use GetModuleFileName inside of my_path since
+ // my_path is a generic path dereferencing function and here we care
+ // only about the executing binary.
+ GetModuleFileName(NULL, executing_path_name, sizeof(executing_path_name));
+ my_path(prg_dev, executing_path_name, NULL);
+ }
+ else
+ my_path(prg_dev, my_progname, "mysql/bin");
strcat(prg_dev,"/../"); // Remove 'bin' to get base dir
cleanup_dirname(mysql_home,prg_dev);
}
@@ -8592,57 +7503,19 @@ static int mysql_init_variables(void)
const char *tmpenv;
if (!(tmpenv = getenv("MY_BASEDIR_VERSION")))
tmpenv = DEFAULT_MYSQL_HOME;
- (void) strmake(mysql_home, tmpenv, sizeof(mysql_home)-1);
+ strmake_buf(mysql_home, tmpenv);
#endif
return 0;
}
-
-/**
- Find type for option
-
- If opt_ignore_wrong_options is set ignore wrong values
- otherwise exit
-
- @return
- @retval 0 ok ; *result is updated
- @retval 1 error ; *result is not touched
-*/
-
-static my_bool find_opt_type(const char *x, TYPELIB *typelib,
- const char *option, int *result)
-{
- int res;
-
- if (opt_ignore_wrong_options)
- {
- if ((res= find_type_with_warning(x, typelib, option)) <= 0)
- return 1;
- }
- else
- res= find_type_or_exit(x, typelib, option);
- *result= res;
- return 0;
-}
-
-
-/**
- Get next option from the command line
-*/
-
my_bool
mysqld_get_one_option(int optid,
const struct my_option *opt __attribute__((unused)),
char *argument)
{
- int error;
-
switch(optid) {
-#ifndef DBUG_OFF
- case OPT_DEBUG_FLUSH:
- argument= IF_WIN((char*) default_dbug_option, (char*) "d:t:i:O,/tmp/mysqld.trace");
- /* fall through */
case '#':
+#ifndef DBUG_OFF
if (!argument)
argument= (char*) default_dbug_option;
if (argument[0] == '0' && !argument[1])
@@ -8655,40 +7528,34 @@ mysqld_get_one_option(int optid,
break;
DBUG_SET_INITIAL(argument);
opt_endinfo=1; /* unireg: memory allocation */
- break;
+#else
+ sql_print_warning("'%s' is disabled in this build", opt->name);
#endif
- case '0':
- WARN_DEPRECATED(NULL, VER_CELOSIA, "--log-long-format", "--log-short-format");
break;
case OPT_DEPRECATED_OPTION:
- sql_print_warning("'%s' is deprecated and exists only for compatiblity with old my.cnf files; Please remove this option from all your my.cnf files!",
+ sql_print_warning("'%s' is deprecated. It does nothing and exists only "
+ "for compatiblity with old my.cnf files.",
opt->name);
break;
case 'a':
- global_system_variables.sql_mode= fix_sql_mode(MODE_ANSI);
+ global_system_variables.sql_mode= MODE_ANSI;
global_system_variables.tx_isolation= ISO_SERIALIZABLE;
break;
case 'b':
- strmake(mysql_home,argument,sizeof(mysql_home)-1);
+ strmake_buf(mysql_home, argument);
break;
- case OPT_DEFAULT_CHARACTER_SET_OLD: // --default-character-set
- WARN_DEPRECATED(NULL, VER_CELOSIA,
- "--default-character-set",
- "--character-set-server");
- /* Fall through */
case 'C':
if (default_collation_name == compiled_default_collation_name)
default_collation_name= 0;
break;
case 'l':
- WARN_DEPRECATED(NULL, "7.0", "--log", "'--general_log'/'--general_log_file'");
+ WARN_DEPRECATED(NULL, 7, 0, "--log", "'--general-log'/'--general-log-file'");
opt_log=1;
break;
case 'h':
- strmake(mysql_real_data_home,argument, sizeof(mysql_real_data_home)-1);
+ strmake_buf(mysql_real_data_home, argument);
/* Correct pointer set by my_getopt (for embedded library) */
- mysql_data_home= mysql_real_data_home;
- mysql_data_home_len= strlen(mysql_data_home);
+ mysql_real_data_home_ptr= mysql_real_data_home;
break;
case 'u':
if (!mysqld_user || !strcmp(mysqld_user, argument))
@@ -8697,37 +7564,18 @@ mysqld_get_one_option(int optid,
sql_print_warning("Ignoring user change to '%s' because the user was set to '%s' earlier on the command line\n", argument, mysqld_user);
break;
case 'L':
- strmake(language, argument, sizeof(language)-1);
- break;
- case 'O':
- WARN_DEPRECATED(NULL, VER_CELOSIA, "--set-variable", "--variable-name=value");
- break;
-#ifdef HAVE_REPLICATION
- case OPT_SLAVE_SKIP_ERRORS:
- init_slave_skip_errors(argument);
+ strmake_buf(lc_messages_dir, argument);
break;
- case OPT_SLAVE_EXEC_MODE:
- slave_exec_mode_options= find_bit_type_or_exit(argument,
- &slave_exec_mode_typelib,
- "", &error);
- if (error)
- return 1;
- break;
-#endif
- case OPT_SAFEMALLOC_MEM_LIMIT:
-#if !defined(DBUG_OFF) && defined(SAFEMALLOC)
- sf_malloc_mem_limit = atoi(argument);
-#endif
+ case OPT_BINLOG_FORMAT:
+ binlog_format_used= true;
break;
#include <sslopt-case.h>
#ifndef EMBEDDED_LIBRARY
case 'V':
print_version();
- exit(0);
+ opt_abort= 1; // Abort after parsing all options
+ break;
#endif /*EMBEDDED_LIBRARY*/
- case OPT_WARNINGS:
- WARN_DEPRECATED(NULL, VER_CELOSIA, "--warnings", "--log-warnings");
- /* Note: fall-through to 'W' */
case 'W':
if (!argument)
global_system_variables.log_warnings++;
@@ -8740,33 +7588,15 @@ mysqld_get_one_option(int optid,
test_flags= argument ? (uint) atoi(argument) : 0;
opt_endinfo=1;
break;
- case (int) OPT_DEFAULT_COLLATION_OLD:
- WARN_DEPRECATED(NULL, VER_CELOSIA, "--default-collation", "--collation-server");
- break;
- case (int) OPT_SAFE_SHOW_DB:
- WARN_DEPRECATED(NULL, VER_CELOSIA, "--safe-show-database", "GRANT SHOW DATABASES");
- break;
- case (int) OPT_LOG_BIN_TRUST_FUNCTION_CREATORS_OLD:
- WARN_DEPRECATED(NULL, VER_CELOSIA, "--log-bin-trust-routine-creators", "--log-bin-trust-function-creators");
- break;
- case (int) OPT_ENABLE_LOCK:
- WARN_DEPRECATED(NULL, VER_CELOSIA, "--enable-locking", "--external-locking");
- break;
- case (int) OPT_BIG_TABLES:
- thd_startup_options|=OPTION_BIG_TABLES;
- break;
- case (int) OPT_IGNORE_BUILTIN_INNODB:
- opt_ignore_builtin_innodb= 1;
+ case OPT_THREAD_CONCURRENCY:
+ WARN_DEPRECATED_NO_REPLACEMENT(NULL, "THREAD_CONCURRENCY");
break;
case (int) OPT_ISAM_LOG:
opt_myisam_log=1;
break;
- case (int) OPT_UPDATE_LOG:
- WARN_DEPRECATED(NULL, VER_CELOSIA, "--log-update", "--log-bin");
- opt_update_log=1;
- break;
case (int) OPT_BIN_LOG:
opt_bin_log= test(argument != disabled_my_option);
+ opt_bin_log_used= 1;
break;
case (int) OPT_LOG_BASENAME:
{
@@ -8776,49 +7606,27 @@ mysqld_get_one_option(int optid,
sql_print_error("Wrong argument for --log-basename. It can't be empty or contain '.' or '" FN_DIRSEP "'");
return 1;
}
- log_error_file_ptr= argument;
+ if (log_error_file_ptr != disabled_my_option)
+ log_error_file_ptr= opt_log_basename;
- /*
- The following file named needs explicite extensions (should be fixed in
- future by having the creating code do this).
- */
- opt_logname= make_once_alloced_filename(argument, ".log");
- opt_slow_logname= make_once_alloced_filename(argument, "-slow.log");
- opt_bin_logname= make_once_alloced_filename(argument, "-bin");
- opt_binlog_index_name= make_once_alloced_filename(argument, "-bin.index");
- opt_relay_logname= make_once_alloced_filename(argument, "-relay-bin");
- opt_relaylog_index_name=make_once_alloced_filename(argument,
- "-relay-bin.index");
+ make_default_log_name(&opt_logname, ".log", false);
+ make_default_log_name(&opt_slow_logname, "-slow.log", false);
+ make_default_log_name(&opt_bin_logname, "-bin", true);
+ make_default_log_name(&opt_binlog_index_name, "-bin.index", true);
+ make_default_log_name(&opt_relay_logname, "-relay-bin", true);
+ make_default_log_name(&opt_relaylog_index_name, "-relay-bin.index", true);
pidfile_name_ptr= pidfile_name;
strmake(pidfile_name, argument, sizeof(pidfile_name)-5);
strmov(fn_ext(pidfile_name),".pid");
- /* The following is depricated so don't set it by default */
- if (opt_update_logname)
- opt_update_logname= argument;
-
/* check for errors */
if (!opt_bin_logname || !opt_relaylog_index_name || ! opt_logname ||
- ! opt_slow_logname)
+ ! opt_slow_logname || !pidfile_name_ptr)
return 1; // out of memory error
break;
}
- case (int) OPT_ERROR_LOG_FILE:
- opt_error_log= (argument != disabled_my_option);
- break;
#ifdef HAVE_REPLICATION
- case (int) OPT_INIT_RPL_ROLE:
- {
- int role;
- LINT_INIT(role);
-
- if (!find_opt_type(argument, &rpl_role_typelib, opt->name, &role))
- {
- rpl_status = (role == 1) ? RPL_AUTH_MASTER : RPL_IDLE_SLAVE;
- break;
- }
- }
case (int)OPT_REPLICATE_IGNORE_DB:
{
rpl_filter->add_ignore_db(argument);
@@ -8866,17 +7674,6 @@ mysqld_get_one_option(int optid,
binlog_filter->add_ignore_db(argument);
break;
}
- case OPT_BINLOG_FORMAT:
- {
- int id;
- LINT_INIT(id);
-
- if (!find_opt_type(argument, &binlog_format_typelib, opt->name, &id))
- {
- global_system_variables.binlog_format= opt_binlog_format_id= id - 1;
- }
- break;
- }
case (int)OPT_BINLOG_DO_DB:
{
binlog_filter->add_do_db(argument);
@@ -8920,33 +7717,9 @@ mysqld_get_one_option(int optid,
}
#endif /* HAVE_REPLICATION */
case (int) OPT_SLOW_QUERY_LOG:
+ WARN_DEPRECATED(NULL, 7, 0, "--log-slow-queries", "'--slow-query-log'/'--slow-query-log-file'");
opt_slow_log= 1;
break;
- case OPT_LOG_OUTPUT:
- {
- if (!argument || !argument[0])
- {
- log_output_options= LOG_FILE;
- log_output_str= log_output_typelib.type_names[1];
- }
- else
- {
- log_output_str= argument;
- log_output_options=
- find_bit_type_or_exit(argument, &log_output_typelib, opt->name, &error);
- if (error)
- return 1;
- }
- break;
- }
- case OPT_EVENT_SCHEDULER:
-#ifndef HAVE_EVENT_SCHEDULER
- sql_print_error("Event scheduler is not supported in embedded build.");
-#else
- if (Events::set_opt_event_scheduler(argument))
- return 1;
-#endif
- break;
case (int) OPT_SAFE:
opt_specialflag|= SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC;
delay_key_write_options= (uint) DELAY_KEY_WRITE_NONE;
@@ -8955,25 +7728,14 @@ mysqld_get_one_option(int optid,
#ifdef HAVE_QUERY_CACHE
query_cache_size=0;
#endif
+ sql_print_warning("The syntax '--safe-mode' is deprecated and will be "
+ "removed in a future release.");
break;
case (int) OPT_SKIP_PRIOR:
opt_specialflag|= SPECIAL_NO_PRIOR;
sql_print_warning("The --skip-thread-priority startup option is deprecated "
- "and will be removed in MySQL 7.0. MySQL 6.0 and up do not "
- "give threads different priorities.");
- break;
- case (int) OPT_SKIP_LOCK:
- WARN_DEPRECATED(NULL, VER_CELOSIA, "--skip-locking", "--skip-external-locking");
- opt_external_locking=0;
- break;
- case (int) OPT_SQL_BIN_UPDATE_SAME:
- WARN_DEPRECATED(NULL, VER_CELOSIA, "--sql-bin-update-same", "the binary log");
- break;
- case (int) OPT_RECORD_BUFFER_OLD:
- WARN_DEPRECATED(NULL, VER_CELOSIA, "record_buffer", "read_buffer_size");
- break;
- case (int) OPT_SYMBOLIC_LINKS:
- WARN_DEPRECATED(NULL, VER_CELOSIA, "--use-symbolic-links", "--symbolic-links");
+ "and will be removed in MySQL 7.0. This option has no effect "
+ "as the implied behavior is already the default.");
break;
case (int) OPT_SKIP_HOST_CACHE:
opt_specialflag|= SPECIAL_NO_HOST_CACHE;
@@ -8982,317 +7744,25 @@ mysqld_get_one_option(int optid,
opt_skip_name_resolve= 1;
opt_specialflag|=SPECIAL_NO_RESOLVE;
break;
- case (int) OPT_SKIP_NETWORKING:
-#if defined(__NETWARE__)
- sql_perror("Can't start server: skip-networking option is currently not supported on NetWare");
- return 1;
-#endif
- opt_disable_networking=1;
- mysqld_port= mysqld_extra_port= 0;
- break;
- case (int) OPT_SKIP_SHOW_DB:
- opt_skip_show_db=1;
- opt_specialflag|=SPECIAL_SKIP_SHOW_DB;
- break;
case (int) OPT_WANT_CORE:
test_flags |= TEST_CORE_ON_SIGNAL;
break;
- case (int) OPT_SKIP_SYMLINKS:
- WARN_DEPRECATED(NULL, VER_CELOSIA, "--skip-symlink", "--skip-symbolic-links");
- my_use_symdir=0;
- break;
- case (int) OPT_BIND_ADDRESS:
- if ((my_bind_addr= (ulong) inet_addr(argument)) == INADDR_NONE)
- {
- struct hostent *ent;
- if (argument[0])
- ent=gethostbyname(argument);
- else
- {
- char myhostname[255];
- if (gethostname(myhostname,sizeof(myhostname)) < 0)
- {
- sql_perror("Can't start server: cannot get my own hostname!");
- return 1;
- }
- ent=gethostbyname(myhostname);
- }
- if (!ent)
- {
- sql_perror("Can't start server: cannot resolve hostname!");
- return 1;
- }
- my_bind_addr = (ulong) ((in_addr*)ent->h_addr_list[0])->s_addr;
- }
- break;
- case (int) OPT_PID_FILE:
- strmake(pidfile_name, argument, sizeof(pidfile_name)-1);
- break;
-#ifdef __WIN__
- case (int) OPT_STANDALONE: /* Dummy option for NT */
- break;
-#endif
- /*
- The following change issues a deprecation warning if the slave
- configuration is specified either in the my.cnf file or on
- the command-line. See BUG#21490.
- */
- case OPT_MASTER_HOST:
- case OPT_MASTER_USER:
- case OPT_MASTER_PASSWORD:
- case OPT_MASTER_PORT:
- case OPT_MASTER_CONNECT_RETRY:
- case OPT_MASTER_SSL:
- case OPT_MASTER_SSL_KEY:
- case OPT_MASTER_SSL_CERT:
- case OPT_MASTER_SSL_CAPATH:
- case OPT_MASTER_SSL_CIPHER:
- case OPT_MASTER_SSL_CA:
- if (!slave_warning_issued) //only show the warning once
- {
- slave_warning_issued = true;
- WARN_DEPRECATED(NULL, "6.0", opt->name, "'CHANGE MASTER'");
- }
- break;
case OPT_CONSOLE:
if (opt_console)
opt_error_log= 0; // Force logs to stdout
break;
- case (int) OPT_FLUSH:
- myisam_flush=1;
- flush_time=0; // No auto flush
- break;
- case OPT_LOW_PRIORITY_UPDATES:
- thr_upgraded_concurrent_insert_lock= TL_WRITE_LOW_PRIORITY;
- global_system_variables.low_priority_updates=1;
- break;
case OPT_BOOTSTRAP:
opt_noacl=opt_bootstrap=1;
break;
- case OPT_LOG_SLOW_FILTER:
- global_system_variables.log_slow_filter=
- find_bit_type_or_exit(argument, &log_slow_filter_typelib,
- opt->name, &error);
- /*
- If we are using filters, we set opt_slow_admin_statements to be always
- true so we can maintain everything with filters
- */
- opt_log_slow_admin_statements= 1;
- if (error)
- return 1;
- break;
- case OPT_LOG_SLOW_VERBOSITY:
- global_system_variables.log_slow_verbosity=
- find_bit_type_or_exit(argument, &log_slow_verbosity_typelib,
- opt->name, &error);
- if (error)
- return 1;
- break;
case OPT_SERVER_ID:
server_id_supplied = 1;
break;
- case OPT_DELAY_KEY_WRITE_ALL:
- WARN_DEPRECATED(NULL, VER_CELOSIA,
- "--delay-key-write-for-all-tables",
- "--delay-key-write=ALL");
- if (argument != disabled_my_option)
- argument= (char*) "ALL";
- /* Fall through */
- case OPT_DELAY_KEY_WRITE:
- if (argument == disabled_my_option)
- delay_key_write_options= (uint) DELAY_KEY_WRITE_NONE;
- else if (! argument)
- delay_key_write_options= (uint) DELAY_KEY_WRITE_ON;
- else
- {
- int type;
- LINT_INIT(type);
-
- if (!find_opt_type(argument, &delay_key_write_typelib, opt->name, &type))
- delay_key_write_options= (uint) type-1;
- }
- break;
- case OPT_CHARSETS_DIR:
- strmake(mysql_charsets_dir, argument, sizeof(mysql_charsets_dir)-1);
- charsets_dir = mysql_charsets_dir;
- break;
- case OPT_TX_ISOLATION:
- {
- int type;
- LINT_INIT(type);
-
- if (!find_opt_type(argument, &tx_isolation_typelib, opt->name, &type))
- global_system_variables.tx_isolation= (type-1);
- break;
- }
-#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
- case OPT_NDB_MGMD:
- case OPT_NDB_NODEID:
- {
- int len= my_snprintf(opt_ndb_constrbuf+opt_ndb_constrbuf_len,
- sizeof(opt_ndb_constrbuf)-opt_ndb_constrbuf_len,
- "%s%s%s",opt_ndb_constrbuf_len > 0 ? ",":"",
- optid == OPT_NDB_NODEID ? "nodeid=" : "",
- argument);
- opt_ndb_constrbuf_len+= len;
- }
- /* fall through to add the connectstring to the end
- * and set opt_ndbcluster_connectstring
- */
- case OPT_NDB_CONNECTSTRING:
- if (opt_ndb_connectstring && opt_ndb_connectstring[0])
- my_snprintf(opt_ndb_constrbuf+opt_ndb_constrbuf_len,
- sizeof(opt_ndb_constrbuf)-opt_ndb_constrbuf_len,
- "%s%s", opt_ndb_constrbuf_len > 0 ? ",":"",
- opt_ndb_connectstring);
- else
- opt_ndb_constrbuf[opt_ndb_constrbuf_len]= 0;
- opt_ndbcluster_connectstring= opt_ndb_constrbuf;
- break;
- case OPT_NDB_DISTRIBUTION:
- int id;
- if (!find_opt_type(argument, &ndb_distribution_typelib, opt->name, &id))
- opt_ndb_distribution_id= (enum ndb_distribution)(id-1);
- break;
- case OPT_NDB_EXTRA_LOGGING:
- if (!argument)
- ndb_extra_logging++;
- else if (argument == disabled_my_option)
- ndb_extra_logging= 0L;
- else
- ndb_extra_logging= atoi(argument);
- break;
-#endif
- case OPT_MYISAM_RECOVER:
- {
- if (argument && (!argument[0] ||
- my_strcasecmp(system_charset_info, argument, "OFF") == 0))
- {
- myisam_recover_options= HA_RECOVER_NONE;
- myisam_recover_options_str= "OFF";
- ha_open_options&= ~HA_OPEN_ABORT_IF_CRASHED;
- }
- else
- {
- if (!argument)
- {
- myisam_recover_options= HA_RECOVER_DEFAULT;
- myisam_recover_options_str= myisam_recover_typelib.type_names[0];
- }
- else
- {
- myisam_recover_options_str=argument;
- myisam_recover_options=
- find_bit_type_or_exit(argument, &myisam_recover_typelib, opt->name,
- &error);
- if (error)
- return 1;
- }
- ha_open_options|=HA_OPEN_ABORT_IF_CRASHED;
- }
- break;
- }
- case OPT_CONCURRENT_INSERT:
- /* The following code is mainly here to emulate old behavior */
- if (!argument) /* --concurrent-insert */
- myisam_concurrent_insert= 1;
- else if (argument == disabled_my_option)
- myisam_concurrent_insert= 0; /* --skip-concurrent-insert */
- break;
- case OPT_TC_HEURISTIC_RECOVER:
- find_opt_type(argument, &tc_heuristic_recover_typelib,
- opt->name, (int*) &tc_heuristic_recover);
- break;
- case OPT_MYISAM_STATS_METHOD:
- {
- ulong method_conv;
- int method;
- LINT_INIT(method_conv);
- LINT_INIT(method);
-
- myisam_stats_method_str= argument;
- if (!find_opt_type(argument, &myisam_stats_method_typelib,
- opt->name, &method))
- {
- switch (method-1) {
- case 2:
- method_conv= MI_STATS_METHOD_IGNORE_NULLS;
- break;
- case 1:
- method_conv= MI_STATS_METHOD_NULLS_EQUAL;
- break;
- case 0:
- default:
- method_conv= MI_STATS_METHOD_NULLS_NOT_EQUAL;
- break;
- }
- global_system_variables.myisam_stats_method= method_conv;
- }
- break;
- }
- case OPT_SQL_MODE:
- {
- sql_mode_str= argument;
- global_system_variables.sql_mode=
- find_bit_type_or_exit(argument, &sql_mode_typelib, opt->name, &error);
- if (error)
- return 1;
- global_system_variables.sql_mode= fix_sql_mode(global_system_variables.
- sql_mode);
- break;
- }
- case OPT_OPTIMIZER_SWITCH:
- {
- bool not_used;
- char *error= 0;
- uint error_len= 0;
- optimizer_switch_str= argument;
- global_system_variables.optimizer_switch=
- (ulong)find_set_from_flags(&optimizer_switch_typelib,
- optimizer_switch_typelib.count,
- global_system_variables.optimizer_switch,
- global_system_variables.optimizer_switch,
- argument, strlen(argument), NULL,
- &error, &error_len, &not_used);
- if (error)
- {
- char buf[512];
- char *cbuf= buf;
- cbuf += my_snprintf(buf, 512, "Error in parsing optimizer_switch setting near %*s\n", error_len, error);
- sql_perror(buf);
- return 1;
- }
- break;
- }
case OPT_ONE_THREAD:
thread_handling= SCHEDULER_NO_THREADS;
- opt_thread_handling= thread_handling_typelib.type_names[thread_handling];
- break;
- case OPT_THREAD_HANDLING:
- {
- int id;
- LINT_INIT(id);
- if (!find_opt_type(argument, &thread_handling_typelib, opt->name, &id))
- thread_handling= id - 1;
- opt_thread_handling= thread_handling_typelib.type_names[thread_handling];
- break;
- }
- case OPT_FT_BOOLEAN_SYNTAX:
- if (ft_boolean_check_syntax_string((uchar*) argument))
- {
- sql_print_error("Invalid ft-boolean-syntax string: %s\n", argument);
- return 1;
- }
- strmake(ft_boolean_syntax, argument, sizeof(ft_boolean_syntax)-1);
break;
case OPT_LOWER_CASE_TABLE_NAMES:
- lower_case_table_names= argument ? atoi(argument) : 1;
lower_case_table_names_used= 1;
break;
- case OPT_TEST_IGNORE_WRONG_OPTIONS:
- /* Used for testing options */
- opt_ignore_wrong_options= 1;
- break;
#if defined(ENABLED_DEBUG_SYNC)
case OPT_DEBUG_SYNC_TIMEOUT:
/*
@@ -9309,6 +7779,26 @@ mysqld_get_one_option(int optid,
}
break;
#endif /* defined(ENABLED_DEBUG_SYNC) */
+ case OPT_ENGINE_CONDITION_PUSHDOWN:
+ /*
+ The last of --engine-condition-pushdown and --optimizer_switch on
+ command line wins (see get_options().
+ */
+ if (global_system_variables.engine_condition_pushdown)
+ global_system_variables.optimizer_switch|=
+ OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN;
+ else
+ global_system_variables.optimizer_switch&=
+ ~OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN;
+ break;
+ case OPT_LOG_ERROR:
+ /*
+ "No --log-error" == "write errors to stderr",
+ "--log-error without argument" == "write errors to a file".
+ */
+ if (argument == NULL) /* no argument */
+ log_error_file_ptr= const_cast<char*>("");
+ break;
case OPT_MAX_LONG_DATA_SIZE:
max_long_data_size_used= true;
break;
@@ -9334,10 +7824,8 @@ mysqld_get_one_option(int optid,
/** Handle arguments for multiple key caches. */
+
C_MODE_START
-static void* mysql_getopt_value(const char *, uint,
- const struct my_option *, int *);
-C_MODE_END
static void*
mysql_getopt_value(const char *keyname, uint key_length,
@@ -9376,10 +7864,7 @@ mysql_getopt_value(const char *keyname, uint key_length,
return option->value;
}
-
-extern "C" void option_error_reporter(enum loglevel level, const char *format, ...);
-
-void option_error_reporter(enum loglevel level, const char *format, ...)
+static void option_error_reporter(enum loglevel level, const char *format, ...)
{
va_list args;
va_start(args, format);
@@ -9393,28 +7878,58 @@ void option_error_reporter(enum loglevel level, const char *format, ...)
va_end(args);
}
+C_MODE_END
/**
+ Get server options from the command line,
+ and perform related server initializations.
+ @param [in, out] argc_ptr command line options (count)
+ @param [in, out] argv_ptr command line options (values)
+ @return 0 on success
+
@todo
- FIXME add EXIT_TOO_MANY_ARGUMENTS to "mysys_err.h" and return that code?
*/
-static int get_options(int *argc,char **argv)
+static int get_options(int *argc_ptr, char ***argv_ptr)
{
int ho_error;
my_getopt_register_get_addr(mysql_getopt_value);
- strmake(def_ft_boolean_syntax, ft_boolean_syntax,
- sizeof(ft_boolean_syntax)-1);
my_getopt_error_reporter= option_error_reporter;
+ /* prepare all_options array */
+ my_init_dynamic_array(&all_options, sizeof(my_option),
+ array_elements(my_long_options),
+ array_elements(my_long_options)/4);
+ for (my_option *opt= my_long_options;
+ opt < my_long_options + array_elements(my_long_options) - 1;
+ opt++)
+ insert_dynamic(&all_options, (uchar*) opt);
+ sys_var_add_options(&all_options, 0);
+ add_terminator(&all_options);
+
/* Skip unknown options so that they may be processed later by plugins */
my_getopt_skip_unknown= TRUE;
- if ((ho_error= handle_options(argc, &argv, my_long_options,
+ if ((ho_error= handle_options(argc_ptr, argv_ptr, (my_option*)(all_options.buffer),
mysqld_get_one_option)))
return ho_error;
- (*argc)++; /* add back one for the progname handle_options removes */
- /* no need to do this for argv as we are discarding it. */
+
+ if (!opt_help)
+ delete_dynamic(&all_options);
+ else
+ opt_abort= 1;
+
+ /* Add back the program name handle_options removes */
+ (*argc_ptr)++;
+ (*argv_ptr)--;
+
+ /*
+ Options have been parsed. Now some of them need additional special
+ handling, like custom value checking, checking of incompatibilites
+ between options, setting of multiple variables, etc.
+ Do them here.
+ */
if ((opt_log_slow_admin_statements || opt_log_queries_not_using_indexes ||
opt_log_slow_slave_statements) &&
@@ -9429,6 +7944,52 @@ static int get_options(int *argc,char **argv)
global_system_variables.max_allowed_packet);
}
+ if (log_error_file_ptr != disabled_my_option)
+ opt_error_log= 1;
+ else
+ log_error_file_ptr= const_cast<char*>("");
+
+ opt_init_connect.length=strlen(opt_init_connect.str);
+ opt_init_slave.length=strlen(opt_init_slave.str);
+
+ if (global_system_variables.low_priority_updates)
+ thr_upgraded_concurrent_insert_lock= TL_WRITE_LOW_PRIORITY;
+
+ if (ft_boolean_check_syntax_string((uchar*) ft_boolean_syntax))
+ {
+ sql_print_error("Invalid ft-boolean-syntax string: %s\n",
+ ft_boolean_syntax);
+ return 1;
+ }
+
+ if (opt_disable_networking)
+ mysqld_port= mysqld_extra_port= 0;
+
+ if (opt_skip_show_db)
+ opt_specialflag|= SPECIAL_SKIP_SHOW_DB;
+
+ if (myisam_flush)
+ flush_time= 0;
+
+#ifdef HAVE_REPLICATION
+ if (opt_slave_skip_errors)
+ init_slave_skip_errors(opt_slave_skip_errors);
+#endif
+
+ if (global_system_variables.max_join_size == HA_POS_ERROR)
+ global_system_variables.option_bits|= OPTION_BIG_SELECTS;
+ else
+ global_system_variables.option_bits&= ~OPTION_BIG_SELECTS;
+
+ // Synchronize @@global.autocommit on --autocommit
+ const ulonglong turn_bit_on= opt_autocommit ?
+ OPTION_AUTOCOMMIT : OPTION_NOT_AUTOCOMMIT;
+ global_system_variables.option_bits=
+ (global_system_variables.option_bits &
+ ~(OPTION_NOT_AUTOCOMMIT | OPTION_AUTOCOMMIT)) | turn_bit_on;
+
+ global_system_variables.sql_mode=
+ expand_sql_mode(global_system_variables.sql_mode);
#if defined(HAVE_BROKEN_REALPATH)
my_use_symdir=0;
my_disable_symlinks=1;
@@ -9448,12 +8009,8 @@ static int get_options(int *argc,char **argv)
test_flags&= ~TEST_CORE_ON_SIGNAL;
}
/* Set global MyISAM variables from delay_key_write_options */
- fix_delay_key_write((THD*) 0, OPT_GLOBAL);
- /* Set global slave_exec_mode from its option */
- fix_slave_exec_mode();
+ fix_delay_key_write(0, 0, OPT_GLOBAL);
- global_system_variables.log_slow_filter=
- fix_log_slow_filter(global_system_variables.log_slow_filter);
#ifndef EMBEDDED_LIBRARY
if (mysqld_chroot)
set_root(mysqld_chroot);
@@ -9470,15 +8027,7 @@ static int get_options(int *argc,char **argv)
In most cases the global variables will not be used
*/
my_disable_locking= myisam_single_user= test(opt_external_locking == 0);
- my_disable_sync= opt_sync == 0;
- my_disable_thr_alarm= opt_thread_alarm == 0;
my_default_record_cache_size=global_system_variables.read_buff_size;
- myisam_max_temp_length=
- (my_off_t) global_system_variables.myisam_max_sort_file_size;
-
- /* Set global variables based on startup options */
- myisam_block_size=(uint) 1 << my_bit_log2(opt_myisam_block_size);
- my_crc_dbug_check= opt_my_crc_dbug_check;
/*
Log mysys errors when we don't have a thd or thd->log_all_errors is set
@@ -9493,40 +8042,52 @@ static int get_options(int *argc,char **argv)
if (my_assert_on_error)
debug_assert_if_crashed_table= 1;
- /* long_query_time is in microseconds */
- global_system_variables.long_query_time= max_system_variables.long_query_time=
- (longlong) (long_query_time * 1000000.0);
+ global_system_variables.long_query_time= (ulonglong)
+ (global_system_variables.long_query_time_double * 1e6);
if (opt_short_log_format)
opt_specialflag|= SPECIAL_SHORT_LOG_FORMAT;
if (init_global_datetime_format(MYSQL_TIMESTAMP_DATE,
- &global_system_variables.date_format) ||
+ &global_date_format) ||
init_global_datetime_format(MYSQL_TIMESTAMP_TIME,
- &global_system_variables.time_format) ||
+ &global_time_format) ||
init_global_datetime_format(MYSQL_TIMESTAMP_DATETIME,
- &global_system_variables.datetime_format))
+ &global_datetime_format))
return 1;
-#ifdef SAFEMALLOC
- sf_malloc_quick= !sf_malloc_trough_check;
-#endif
#ifdef EMBEDDED_LIBRARY
- one_thread_scheduler(&thread_scheduler);
- one_thread_scheduler(&extra_thread_scheduler);
+ one_thread_scheduler(thread_scheduler);
+ one_thread_scheduler(extra_thread_scheduler);
#else
+
+#ifdef _WIN32
+ /* workaround: disable thread pool on XP */
+ if (GetProcAddress(GetModuleHandle("kernel32"),"CreateThreadpool") == 0 &&
+ thread_handling > SCHEDULER_NO_THREADS)
+ thread_handling = SCHEDULER_ONE_THREAD_PER_CONNECTION;
+#endif
+
if (thread_handling <= SCHEDULER_ONE_THREAD_PER_CONNECTION)
- one_thread_per_connection_scheduler(&thread_scheduler, &max_connections,
+ one_thread_per_connection_scheduler(thread_scheduler, &max_connections,
&connection_count);
else if (thread_handling == SCHEDULER_NO_THREADS)
- one_thread_scheduler(&thread_scheduler);
+ one_thread_scheduler(thread_scheduler);
else
- pool_of_threads_scheduler(&thread_scheduler); /* purecov: tested */
- one_thread_per_connection_scheduler(&extra_thread_scheduler,
+ pool_of_threads_scheduler(thread_scheduler, &max_connections,
+ &connection_count);
+
+ one_thread_per_connection_scheduler(extra_thread_scheduler,
&extra_max_connections,
&extra_connection_count);
#endif
+ global_system_variables.engine_condition_pushdown=
+ test(global_system_variables.optimizer_switch &
+ OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN);
+
+ opt_readonly= read_only;
+
/*
If max_long_data_size is not specified explicitly use
value of max_allowed_packet.
@@ -9535,7 +8096,7 @@ static int get_options(int *argc,char **argv)
max_long_data_size= global_system_variables.max_allowed_packet;
/* Rember if max_user_connections was 0 at startup */
- max_user_connections_checking= max_user_connections != 0;
+ max_user_connections_checking= global_system_variables.max_user_connections != 0;
return 0;
}
@@ -9558,7 +8119,7 @@ void set_server_version(void)
if (!strstr(MYSQL_SERVER_SUFFIX_STR, "-debug"))
end= strmov(end, "-debug");
#endif
- if (opt_log || opt_update_log || opt_slow_log || opt_bin_log)
+ if (opt_log || opt_slow_log || opt_bin_log)
strmov(end, "-log"); // This may slow down system
}
@@ -9570,7 +8131,7 @@ static char *get_relative_path(const char *path)
strcmp(DEFAULT_MYSQL_HOME,FN_ROOTDIR))
{
path+=(uint) strlen(DEFAULT_MYSQL_HOME);
- while (*path == FN_LIBCHAR)
+ while (*path == FN_LIBCHAR || *path == FN_LIBCHAR2)
path++;
}
return (char*) path;
@@ -9671,14 +8232,17 @@ static int fix_paths(void)
pos[0]= FN_LIBCHAR;
pos[1]= 0;
}
- convert_dirname(language,language,NullS);
+ convert_dirname(lc_messages_dir, lc_messages_dir, NullS);
convert_dirname(mysql_real_data_home,mysql_real_data_home,NullS);
(void) my_load_path(mysql_home,mysql_home,""); // Resolve current dir
(void) my_load_path(mysql_real_data_home,mysql_real_data_home,mysql_home);
- (void) my_load_path(pidfile_name,pidfile_name,mysql_real_data_home);
- (void) my_load_path(opt_plugin_dir, opt_plugin_dir_ptr ? opt_plugin_dir_ptr :
- get_relative_path(PLUGINDIR), mysql_home);
+ (void) my_load_path(pidfile_name, pidfile_name_ptr, mysql_real_data_home);
+
+ convert_dirname(opt_plugin_dir, opt_plugin_dir_ptr ? opt_plugin_dir_ptr :
+ get_relative_path(PLUGINDIR), NullS);
+ (void) my_load_path(opt_plugin_dir, opt_plugin_dir, mysql_home);
opt_plugin_dir_ptr= opt_plugin_dir;
+ pidfile_name_ptr= pidfile_name;
my_realpath(mysql_unpacked_real_data_home, mysql_real_data_home, MYF(0));
mysql_unpacked_real_data_home_len=
@@ -9688,30 +8252,29 @@ static int fix_paths(void)
char *sharedir=get_relative_path(SHAREDIR);
if (test_if_hard_path(sharedir))
- strmake(buff,sharedir,sizeof(buff)-1); /* purecov: tested */
+ strmake_buf(buff, sharedir); /* purecov: tested */
else
strxnmov(buff,sizeof(buff)-1,mysql_home,sharedir,NullS);
convert_dirname(buff,buff,NullS);
- (void) my_load_path(language,language,buff);
+ (void) my_load_path(lc_messages_dir, lc_messages_dir, buff);
/* If --character-sets-dir isn't given, use shared library dir */
- if (charsets_dir != mysql_charsets_dir)
- {
+ if (charsets_dir)
+ strmake_buf(mysql_charsets_dir, charsets_dir);
+ else
strxnmov(mysql_charsets_dir, sizeof(mysql_charsets_dir)-1, buff,
CHARSET_DIR, NullS);
- }
(void) my_load_path(mysql_charsets_dir, mysql_charsets_dir, buff);
convert_dirname(mysql_charsets_dir, mysql_charsets_dir, NullS);
charsets_dir=mysql_charsets_dir;
if (init_tmpdir(&mysql_tmpdir_list, opt_mysql_tmpdir))
DBUG_RETURN(1);
+ if (!opt_mysql_tmpdir)
+ opt_mysql_tmpdir= mysql_tmpdir;
#ifdef HAVE_REPLICATION
if (!slave_load_tmpdir)
- {
- if (!(slave_load_tmpdir = (char*) my_strdup(mysql_tmpdir, MYF(MY_FAE))))
- DBUG_RETURN(1);
- }
+ slave_load_tmpdir= mysql_tmpdir;
#endif /* HAVE_REPLICATION */
/*
Convert the secure-file-priv option to system format, allowing
@@ -9721,120 +8284,27 @@ static int fix_paths(void)
{
if (*opt_secure_file_priv == 0)
{
- my_free(opt_secure_file_priv, MYF(0));
+ my_free(opt_secure_file_priv);
opt_secure_file_priv= 0;
}
else
{
- char *secure_file_real_path;
+ if (strlen(opt_secure_file_priv) >= FN_REFLEN)
+ opt_secure_file_priv[FN_REFLEN-1]= '\0';
if (my_realpath(buff, opt_secure_file_priv, 0))
{
sql_print_warning("Failed to normalize the argument for --secure-file-priv.");
DBUG_RETURN(1);
}
- secure_file_real_path= (char *)my_malloc(FN_REFLEN, MYF(MY_FAE));
+ char *secure_file_real_path= (char *)my_malloc(FN_REFLEN, MYF(MY_FAE));
convert_dirname(secure_file_real_path, buff, NullS);
- my_free(opt_secure_file_priv, MYF(0));
+ my_free(opt_secure_file_priv);
opt_secure_file_priv= secure_file_real_path;
}
}
DBUG_RETURN(0);
}
-
-static ulong find_bit_type_or_exit(const char *x, TYPELIB *bit_lib,
- const char *option, int *error)
-{
- ulong result;
- const char **ptr;
-
- *error= 0;
- if ((result= find_bit_type(x, bit_lib)) == ~(ulong) 0)
- {
- char *buff= (char *) my_alloca(2048);
- char *cbuf;
- ptr= bit_lib->type_names;
- cbuf= buff + ((!*x) ?
- my_snprintf(buff, 2048, "No option given to %s\n", option) :
- my_snprintf(buff, 2048, "Wrong option to %s. Option(s) given: %s\n",
- option, x));
- cbuf+= my_snprintf(cbuf, 2048 - (cbuf-buff), "Alternatives are: '%s'", *ptr);
- while (*++ptr)
- cbuf+= my_snprintf(cbuf, 2048 - (cbuf-buff), ",'%s'", *ptr);
- my_snprintf(cbuf, 2048 - (cbuf-buff), "\n");
- sql_perror(buff);
- *error= 1;
- my_afree(buff);
- return 0;
- }
-
- return result;
-}
-
-
-/**
- @return
- a bitfield from a string of substrings separated by ','
- or
- ~(ulong) 0 on error.
-*/
-
-static ulong find_bit_type(const char *x, TYPELIB *bit_lib)
-{
- bool found_end;
- int found_count;
- const char *end,*i,*j;
- const char **array, *pos;
- ulong found,found_int,bit;
- DBUG_ENTER("find_bit_type");
- DBUG_PRINT("enter",("x: '%s'",x));
-
- found=0;
- found_end= 0;
- pos=(char *) x;
- while (*pos == ' ') pos++;
- found_end= *pos == 0;
- while (!found_end)
- {
- if (!*(end=strcend(pos,','))) /* Let end point at fieldend */
- {
- while (end > pos && end[-1] == ' ')
- end--; /* Skip end-space */
- found_end=1;
- }
- found_int=0; found_count=0;
- for (array=bit_lib->type_names, bit=1 ; (i= *array++) ; bit<<=1)
- {
- j=pos;
- while (j != end)
- {
- if (my_toupper(mysqld_charset,*i++) !=
- my_toupper(mysqld_charset,*j++))
- goto skip;
- }
- found_int=bit;
- if (! *i)
- {
- found_count=1;
- break;
- }
- else if (j != pos) // Half field found
- {
- found_count++; // Could be one of two values
- }
-skip: ;
- }
- if (found_count != 1)
- DBUG_RETURN(~(ulong) 0); // No unique value
- found|=found_int;
- pos=end+1;
- }
-
- DBUG_PRINT("exit",("bit-field: %ld",(ulong) found));
- DBUG_RETURN(found);
-} /* find_bit_type */
-
-
/**
Check if file system used for databases is case insensitive.
@@ -9857,17 +8327,18 @@ static int test_if_case_insensitive(const char *dir_name)
MY_UNPACK_FILENAME | MY_REPLACE_EXT | MY_REPLACE_DIR);
fn_format(buff2, glob_hostname, dir_name, ".LOWER-TEST",
MY_UNPACK_FILENAME | MY_REPLACE_EXT | MY_REPLACE_DIR);
- (void) my_delete(buff2, MYF(0));
- if ((file= my_create(buff, 0666, O_RDWR, MYF(0))) < 0)
+ mysql_file_delete(key_file_casetest, buff2, MYF(0));
+ if ((file= mysql_file_create(key_file_casetest,
+ buff, 0666, O_RDWR, MYF(0))) < 0)
{
- if (!opt_help)
+ if (!opt_abort)
sql_print_warning("Can't create test file %s", buff);
DBUG_RETURN(-1);
}
- my_close(file, MYF(0));
- if (my_stat(buff2, &stat_info, MYF(0)))
+ mysql_file_close(file, MYF(0));
+ if (mysql_file_stat(key_file_casetest, buff2, &stat_info, MYF(0)))
result= 1; // Can access file
- (void) my_delete(buff, MYF(MY_WME));
+ mysql_file_delete(key_file_casetest, buff, MYF(MY_WME));
DBUG_PRINT("exit", ("result: %d", result));
DBUG_RETURN(result);
}
@@ -9881,19 +8352,20 @@ static int test_if_case_insensitive(const char *dir_name)
static void create_pid_file()
{
File file;
- if ((file = my_create(pidfile_name,0664,
- O_WRONLY | O_TRUNC, MYF(MY_WME))) >= 0)
+ if ((file= mysql_file_create(key_file_pid, pidfile_name, 0664,
+ O_WRONLY | O_TRUNC, MYF(MY_WME))) >= 0)
{
char buff[MAX_BIGINT_WIDTH + 1], *end;
end= int10_to_str((long) getpid(), buff, 10);
*end++= '\n';
- if (!my_write(file, (uchar*) buff, (uint) (end-buff), MYF(MY_WME | MY_NABP)))
+ if (!mysql_file_write(file, (uchar*) buff, (uint) (end-buff),
+ MYF(MY_WME | MY_NABP)))
{
- (void) my_close(file, MYF(0));
+ mysql_file_close(file, MYF(0));
pid_file_created= true;
return;
}
- (void) my_close(file, MYF(0));
+ mysql_file_close(file, MYF(0));
}
sql_perror("Can't start server: can't create PID file");
exit(1);
@@ -9910,11 +8382,11 @@ static void create_pid_file()
static void delete_pid_file(myf flags)
{
#ifndef EMBEDDED_LIBRARY
- if (opt_bootstrap || !pid_file_created)
- return;
-
- my_delete(pidfile_name, flags);
- pid_file_created= false;
+ if (pid_file_created)
+ {
+ mysql_file_delete(key_file_pid, pidfile_name, flags);
+ pid_file_created= false;
+ }
#endif /* EMBEDDED_LIBRARY */
return;
}
@@ -9923,7 +8395,7 @@ static void delete_pid_file(myf flags)
/** Clear most status variables. */
void refresh_status(THD *thd)
{
- pthread_mutex_lock(&LOCK_status);
+ mysql_mutex_lock(&LOCK_status);
/* Add thread's status variabes to global status */
add_to_status(&global_status_var, &thd->status_var);
@@ -9937,11 +8409,9 @@ void refresh_status(THD *thd)
reset_status_vars();
/* Reset the counters of all key caches (default and named). */
- process_key_caches(reset_key_cache_counters);
-#ifdef COMMUNITY_SERVER
+ process_key_caches(reset_key_cache_counters, 0);
flush_status_time= time((time_t*) 0);
-#endif
- pthread_mutex_unlock(&LOCK_status);
+ mysql_mutex_unlock(&LOCK_status);
/*
Set max_used_connections to the number of currently open
@@ -9949,9 +8419,9 @@ void refresh_status(THD *thd)
deadlocks. Status reset becomes not atomic, but status data is
not exact anyway.
*/
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
max_used_connections= thread_count-delayed_insert_threads;
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
}
@@ -9960,11 +8430,6 @@ void refresh_status(THD *thd)
This section should go away soon
*****************************************************************************/
-#ifndef WITH_NDBCLUSTER_STORAGE_ENGINE
-ulong ndb_cache_check_time;
-ulong ndb_extra_logging;
-#endif
-
/*****************************************************************************
Instantiate templates
*****************************************************************************/
@@ -9975,7 +8440,7 @@ template class I_List<THD>;
template class I_List_iterator<THD>;
template class I_List<i_string>;
template class I_List<i_string_pair>;
-template class I_List<NAMED_LIST>;
template class I_List<Statement>;
template class I_List_iterator<Statement>;
#endif
+
diff --git a/sql/mysqld.h b/sql/mysqld.h
new file mode 100644
index 00000000000..3480d02365b
--- /dev/null
+++ b/sql/mysqld.h
@@ -0,0 +1,560 @@
+/* Copyright (c) 2006, 2013, 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 MYSQLD_INCLUDED
+#define MYSQLD_INCLUDED
+
+#include "my_global.h" /* MYSQL_PLUGIN_IMPORT, FN_REFLEN, FN_EXTLEN */
+#include "sql_bitmap.h" /* Bitmap */
+#include "my_decimal.h" /* my_decimal */
+#include "mysql_com.h" /* SERVER_VERSION_LENGTH */
+#include "my_atomic.h" /* my_atomic_rwlock_t */
+#include "mysql/psi/mysql_file.h" /* MYSQL_FILE */
+#include "sql_list.h" /* I_List */
+
+class THD;
+struct handlerton;
+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
+typedef Bitmap<64> key_map; /* Used for finding keys */
+#else
+typedef Bitmap<((MAX_INDEXES+7)/8*8)> key_map; /* Used for finding keys */
+#endif
+
+ /* Bits from testflag */
+#define TEST_PRINT_CACHED_TABLES 1
+#define TEST_NO_KEY_GROUP 2
+#define TEST_MIT_THREAD 4
+#define TEST_BLOCKING 8
+#define TEST_KEEP_TMP_TABLES 16
+#define TEST_READCHECK 64 /**< Force use of readcheck */
+#define TEST_NO_EXTRA 128
+#define TEST_CORE_ON_SIGNAL 256 /**< Give core if signal */
+#define TEST_SIGINT 1024 /**< Allow sigint on threads */
+#define TEST_SYNCHRONIZATION 2048 /**< get server to do sleep in
+ some places */
+/* Function prototypes */
+void kill_mysql(void);
+void close_connection(THD *thd, uint sql_errno= 0);
+void handle_connection_in_main_thread(THD *thd);
+void create_thread_to_handle_connection(THD *thd);
+void unlink_thd(THD *thd);
+bool one_thread_per_connection_end(THD *thd, bool put_in_cache);
+void flush_thread_cache();
+void refresh_status(THD *thd);
+bool is_secure_file_path(char *path);
+
+extern "C" MYSQL_PLUGIN_IMPORT CHARSET_INFO *system_charset_info;
+extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *files_charset_info ;
+extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *national_charset_info;
+extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *table_alias_charset;
+
+/**
+ Character set of the buildin error messages loaded from errmsg.sys.
+*/
+extern CHARSET_INFO *error_message_charset_info;
+
+extern CHARSET_INFO *character_set_filesystem;
+
+extern MY_BITMAP temp_pool;
+extern bool opt_large_files, server_id_supplied;
+extern bool opt_update_log, opt_bin_log, opt_error_log;
+extern my_bool opt_log, opt_slow_log, opt_bootstrap;
+extern my_bool opt_backup_history_log;
+extern my_bool opt_backup_progress_log;
+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;
+extern my_bool opt_character_set_client_handshake;
+extern bool volatile abort_loop;
+extern bool in_bootstrap;
+extern uint volatile thread_count;
+extern uint connection_count;
+extern my_bool opt_safe_user_create;
+extern my_bool opt_safe_show_db, opt_local_infile, opt_myisam_use_mmap;
+extern my_bool opt_slave_compressed_protocol, use_temp_pool;
+extern ulong slave_exec_mode_options;
+extern ulonglong slave_type_conversions_options;
+extern my_bool read_only, opt_readonly;
+extern my_bool lower_case_file_system;
+extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs;
+extern my_bool opt_secure_auth;
+extern 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 my_bool opt_old_style_user_limits, trust_function_creators;
+extern uint opt_crash_binlog_innodb;
+extern char *shared_memory_base_name, *mysqld_unix_port;
+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 *default_storage_engine;
+extern bool opt_endinfo, using_udf_functions;
+extern my_bool locked_in_memory;
+extern bool opt_using_transactions;
+extern ulong max_long_data_size;
+extern ulong current_pid;
+extern ulong expire_logs_days;
+extern my_bool relay_log_recovery;
+extern uint sync_binlog_period, sync_relaylog_period,
+ sync_relayloginfo_period, sync_masterinfo_period;
+extern ulong opt_tc_log_size, tc_log_max_pages_used, tc_log_page_size;
+extern ulong tc_log_page_waits;
+extern my_bool relay_log_purge, opt_innodb_safe_binlog, opt_innodb;
+extern my_bool relay_log_recovery;
+extern uint test_flags,select_errors,ha_open_options;
+extern uint protocol_version, mysqld_port, dropping_tables;
+extern ulong delay_key_write_options;
+extern char *opt_logname, *opt_slow_logname, *opt_bin_logname,
+ *opt_relay_logname;
+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;
+extern char *mysql_home_ptr, *pidfile_name_ptr;
+extern char glob_hostname[FN_REFLEN], mysql_home[FN_REFLEN];
+extern char pidfile_name[FN_REFLEN], system_time_zone[30], *opt_init_file;
+extern char default_logfile_name[FN_REFLEN];
+extern char log_error_file[FN_REFLEN], *opt_tc_log_file;
+extern const double log_10[309];
+extern ulonglong keybuff_size;
+extern ulonglong thd_startup_options;
+extern ulong thread_id;
+extern ulong binlog_cache_use, binlog_cache_disk_use;
+extern ulong binlog_stmt_cache_use, binlog_stmt_cache_disk_use;
+extern ulong aborted_threads,aborted_connects;
+extern ulong delayed_insert_timeout;
+extern ulong delayed_insert_limit, delayed_queue_size;
+extern ulong delayed_insert_threads, delayed_insert_writes;
+extern ulong delayed_rows_in_use,delayed_insert_errors;
+extern ulong slave_open_temp_tables;
+extern ulonglong query_cache_size;
+extern ulong query_cache_min_res_unit;
+extern ulong slow_launch_threads, slow_launch_time;
+extern ulong table_cache_size, table_def_size;
+extern MYSQL_PLUGIN_IMPORT ulong max_connections;
+extern ulong max_connect_errors, connect_timeout;
+extern my_bool slave_allow_batching;
+extern my_bool allow_slave_start;
+extern LEX_CSTRING reason_slave_blocked;
+extern ulong slave_trans_retries;
+extern uint slave_net_timeout;
+extern int max_user_connections;
+extern ulong what_to_log,flush_time;
+extern ulong max_prepared_stmt_count, prepared_stmt_count;
+extern ulong open_files_limit;
+extern ulonglong binlog_cache_size, binlog_stmt_cache_size;
+extern ulonglong max_binlog_cache_size, max_binlog_stmt_cache_size;
+extern ulong max_binlog_size, max_relay_log_size;
+extern ulong slave_max_allowed_packet;
+extern ulong opt_binlog_rows_event_max_size;
+extern ulong rpl_recovery_rank, thread_cache_size;
+extern ulong stored_program_cache_size;
+extern ulong back_log;
+extern ulong executed_events;
+extern char language[FN_REFLEN];
+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 MYSQL_PLUGIN_IMPORT MY_TMPDIR mysql_tmpdir_list;
+extern const char *first_keyword, *delayed_user, *binary_keyword;
+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 SHOW_VAR status_vars[];
+extern struct system_variables max_system_variables;
+extern struct system_status_var global_status_var;
+extern struct my_rnd_struct sql_rand;
+extern const char *opt_date_time_formats[];
+extern handlerton *partition_hton;
+extern handlerton *myisam_hton;
+extern handlerton *heap_hton;
+extern const char *load_default_groups[];
+extern struct my_option my_long_options[];
+extern int mysqld_server_started;
+extern "C" MYSQL_PLUGIN_IMPORT int orig_argc;
+extern "C" MYSQL_PLUGIN_IMPORT char **orig_argv;
+extern pthread_attr_t connection_attrib;
+extern MYSQL_FILE *bootstrap_file;
+extern my_bool old_mode;
+extern LEX_STRING opt_init_connect, opt_init_slave;
+extern int bootstrap_error;
+extern I_List<THD> threads;
+extern char err_shared_dir[];
+
+/*
+ THR_MALLOC is a key which will be used to set/get MEM_ROOT** for a thread,
+ using my_pthread_setspecific_ptr()/my_thread_getspecific_ptr().
+*/
+extern pthread_key(MEM_ROOT**,THR_MALLOC);
+
+#ifdef HAVE_PSI_INTERFACE
+#ifdef HAVE_MMAP
+extern PSI_mutex_key key_PAGE_lock, key_LOCK_sync, key_LOCK_active,
+ key_LOCK_pool;
+#endif /* HAVE_MMAP */
+
+#ifdef HAVE_OPENSSL
+extern PSI_mutex_key key_LOCK_des_key_file;
+#endif
+
+extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_prep_xids,
+ key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi,
+ key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create,
+ key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
+ key_LOCK_gdl, key_LOCK_global_system_variables,
+ key_LOCK_logger, key_LOCK_manager,
+ key_LOCK_prepared_stmt_count,
+ key_LOCK_rpl_status, key_LOCK_server_started, key_LOCK_status,
+ key_LOCK_table_share, key_LOCK_thd_data,
+ key_LOCK_user_conn, key_LOG_LOCK_log,
+ key_master_info_data_lock, key_master_info_run_lock,
+ key_master_info_sleep_lock,
+ key_mutex_slave_reporting_capability_err_lock, key_relay_log_info_data_lock,
+ key_relay_log_info_log_space_lock, key_relay_log_info_run_lock,
+ key_relay_log_info_sleep_lock,
+ key_structure_guard_mutex, key_TABLE_SHARE_LOCK_ha_data,
+ key_LOCK_error_messages, key_LOCK_thread_count, key_PARTITION_LOCK_auto_inc;
+extern PSI_mutex_key key_RELAYLOG_LOCK_index;
+
+extern PSI_mutex_key key_LOCK_stats,
+ key_LOCK_global_user_client_stats, key_LOCK_global_table_stats,
+ key_LOCK_global_index_stats, key_LOCK_wakeup_ready;
+
+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;
+
+#ifdef HAVE_MMAP
+extern PSI_cond_key key_PAGE_cond, key_COND_active, key_COND_pool;
+#endif /* HAVE_MMAP */
+
+extern PSI_cond_key key_BINLOG_COND_prep_xids, key_BINLOG_update_cond,
+ key_COND_cache_status_changed, key_COND_manager,
+ key_COND_rpl_status, key_COND_server_started,
+ key_delayed_insert_cond, key_delayed_insert_cond_client,
+ key_item_func_sleep_cond, key_master_info_data_cond,
+ key_master_info_start_cond, key_master_info_stop_cond,
+ key_master_info_sleep_cond,
+ key_relay_log_info_data_cond, key_relay_log_info_log_space_cond,
+ key_relay_log_info_start_cond, key_relay_log_info_stop_cond,
+ key_relay_log_info_sleep_cond,
+ key_TABLE_SHARE_cond, key_user_level_lock_cond,
+ key_COND_thread_count, key_COND_thread_cache, key_COND_flush_thread_cache;
+extern PSI_cond_key key_RELAYLOG_update_cond, key_COND_wakeup_ready;
+extern PSI_cond_key key_RELAYLOG_COND_queue_busy;
+extern PSI_cond_key key_TC_LOG_MMAP_COND_queue_busy;
+
+extern PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert,
+ key_thread_handle_manager, key_thread_kill_server, key_thread_main,
+ key_thread_one_connection, key_thread_signal_hand;
+
+extern PSI_file_key key_file_binlog, key_file_binlog_index, key_file_casetest,
+ key_file_dbopt, key_file_des_key_file, key_file_ERRMSG, key_select_to_file,
+ key_file_fileparser, key_file_frm, key_file_global_ddl_log, key_file_load,
+ key_file_loadfile, key_file_log_event_data, key_file_log_event_info,
+ key_file_master_info, key_file_misc, key_file_partition,
+ key_file_pid, key_file_relay_log_info, key_file_send_file, key_file_tclog,
+ key_file_trg, key_file_trn, key_file_init;
+extern PSI_file_key key_file_query_log, key_file_slow_log;
+extern PSI_file_key key_file_relaylog, key_file_relaylog_index;
+
+void init_server_psi_keys();
+#endif /* HAVE_PSI_INTERFACE */
+
+#ifndef __WIN__
+extern pthread_t signal_thread;
+#endif
+
+#ifdef HAVE_OPENSSL
+extern struct st_VioSSLFd * ssl_acceptor_fd;
+#endif /* HAVE_OPENSSL */
+
+/*
+ The following variables were under INNODB_COMPABILITY_HOOKS
+ */
+extern my_bool opt_large_pages;
+extern uint opt_large_page_size;
+extern char lc_messages_dir[FN_REFLEN];
+extern char *lc_messages_dir_ptr, *log_error_file_ptr;
+extern MYSQL_PLUGIN_IMPORT char reg_ext[FN_EXTLEN];
+extern MYSQL_PLUGIN_IMPORT uint reg_ext_length;
+extern MYSQL_PLUGIN_IMPORT uint lower_case_table_names;
+extern MYSQL_PLUGIN_IMPORT bool mysqld_embedded;
+extern ulong specialflag;
+extern uint mysql_data_home_len;
+extern uint mysql_real_data_home_len;
+extern const char *mysql_real_data_home_ptr;
+extern ulong thread_handling;
+extern "C" MYSQL_PLUGIN_IMPORT char server_version[SERVER_VERSION_LENGTH];
+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];
+
+#define mysql_tmpdir (my_tmpdir(&mysql_tmpdir_list))
+
+extern MYSQL_PLUGIN_IMPORT const key_map key_map_empty;
+extern MYSQL_PLUGIN_IMPORT key_map key_map_full; /* Should be threaded as const */
+
+/*
+ Server mutex locks and condition variables.
+ */
+extern mysql_mutex_t
+ LOCK_user_locks, LOCK_status,
+ LOCK_error_log, LOCK_delayed_insert, LOCK_short_uuid_generator,
+ LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
+ LOCK_slave_list, LOCK_active_mi, LOCK_manager,
+ LOCK_global_system_variables, LOCK_user_conn,
+ LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count;
+extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_thread_count;
+#ifdef HAVE_OPENSSL
+extern mysql_mutex_t LOCK_des_key_file;
+#endif
+extern mysql_mutex_t LOCK_server_started;
+extern mysql_cond_t COND_server_started;
+extern mysql_rwlock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
+extern mysql_rwlock_t LOCK_system_variables_hash;
+extern mysql_cond_t COND_thread_count;
+extern mysql_cond_t COND_manager;
+extern int32 thread_running;
+extern my_atomic_rwlock_t thread_running_lock;
+
+extern char *opt_ssl_ca, *opt_ssl_capath, *opt_ssl_cert, *opt_ssl_cipher,
+ *opt_ssl_key;
+
+extern MYSQL_PLUGIN_IMPORT pthread_key(THD*, THR_THD);
+
+#ifdef MYSQL_SERVER
+
+/**
+ only options that need special treatment in get_one_option() deserve
+ to be listed below
+*/
+enum options_mysqld
+{
+ OPT_to_set_the_start_number=256,
+ OPT_BIND_ADDRESS,
+ OPT_BINLOG_DO_DB,
+ OPT_BINLOG_FORMAT,
+ OPT_BINLOG_IGNORE_DB,
+ OPT_BIN_LOG,
+ OPT_BOOTSTRAP,
+ OPT_CONSOLE,
+ OPT_DEBUG_SYNC_TIMEOUT,
+ OPT_DELAY_KEY_WRITE_ALL,
+ OPT_DEPRECATED_OPTION,
+ OPT_ENGINE_CONDITION_PUSHDOWN,
+ OPT_IGNORE_DB_DIRECTORY,
+ OPT_ISAM_LOG,
+ OPT_KEY_BUFFER_SIZE,
+ OPT_KEY_CACHE_AGE_THRESHOLD,
+ OPT_KEY_CACHE_BLOCK_SIZE,
+ OPT_KEY_CACHE_DIVISION_LIMIT,
+ OPT_KEY_CACHE_PARTITIONS,
+ OPT_LOG_BASENAME,
+ OPT_LOG_ERROR,
+ OPT_LOWER_CASE_TABLE_NAMES,
+ OPT_MAX_LONG_DATA_SIZE,
+ OPT_ONE_THREAD,
+ OPT_POOL_OF_THREADS,
+ OPT_REPLICATE_DO_DB,
+ OPT_REPLICATE_DO_TABLE,
+ OPT_REPLICATE_IGNORE_DB,
+ OPT_REPLICATE_IGNORE_TABLE,
+ OPT_REPLICATE_REWRITE_DB,
+ OPT_REPLICATE_WILD_DO_TABLE,
+ OPT_REPLICATE_WILD_IGNORE_TABLE,
+ OPT_SAFE,
+ OPT_SERVER_ID,
+ OPT_SKIP_HOST_CACHE,
+ OPT_SKIP_LOCK,
+ OPT_SKIP_PRIOR,
+ OPT_SKIP_RESOLVE,
+ OPT_SKIP_STACK_TRACE,
+ OPT_SKIP_SYMLINKS,
+ OPT_SLOW_QUERY_LOG,
+ OPT_SSL_CA,
+ OPT_SSL_CAPATH,
+ OPT_SSL_CERT,
+ OPT_SSL_CIPHER,
+ OPT_SSL_KEY,
+ OPT_THREAD_CONCURRENCY,
+ OPT_UPDATE_LOG,
+ OPT_WANT_CORE,
+ OPT_which_is_always_the_last
+};
+#endif
+
+/**
+ Query type constants (usable as bitmap flags).
+*/
+enum enum_query_type
+{
+ /// Nothing specific, ordinary SQL query.
+ QT_ORDINARY= 0,
+ /// In utf8.
+ QT_TO_SYSTEM_CHARSET= (1 << 0),
+ /// Without character set introducers.
+ QT_WITHOUT_INTRODUCERS= (1 << 1),
+ /// view internal representation (like QT_ORDINARY except ORDER BY clause)
+ QT_VIEW_INTERNAL= (1 << 2)
+};
+
+/* query_id */
+typedef int64 query_id_t;
+extern query_id_t global_query_id;
+extern my_atomic_rwlock_t global_query_id_lock;
+
+void unireg_end(void) __attribute__((noreturn));
+
+/* increment query_id and return it. */
+inline __attribute__((warn_unused_result)) query_id_t next_query_id()
+{
+ query_id_t id;
+ my_atomic_rwlock_wrlock(&global_query_id_lock);
+ id= my_atomic_add64(&global_query_id, 1);
+ my_atomic_rwlock_wrunlock(&global_query_id_lock);
+ return (id+1);
+}
+
+inline query_id_t get_query_id()
+{
+ query_id_t id;
+ my_atomic_rwlock_wrlock(&global_query_id_lock);
+ id= my_atomic_load64(&global_query_id);
+ my_atomic_rwlock_wrunlock(&global_query_id_lock);
+ return id;
+}
+
+
+/*
+ TODO: Replace this with an inline function.
+ */
+#ifndef EMBEDDED_LIBRARY
+extern "C" void unireg_abort(int exit_code) __attribute__((noreturn));
+#else
+extern "C" void unireg_clear(int exit_code);
+#define unireg_abort(exit_code) do { unireg_clear(exit_code); DBUG_RETURN(exit_code); } while(0)
+#endif
+
+inline void table_case_convert(char * name, uint length)
+{
+ if (lower_case_table_names)
+ files_charset_info->cset->casedn(files_charset_info,
+ name, length, name, length);
+}
+
+inline ulong sql_rnd_with_mutex()
+{
+ mysql_mutex_lock(&LOCK_thread_count);
+ ulong tmp=(ulong) (my_rnd(&sql_rand) * 0xffffffff); /* make all bits random */
+ mysql_mutex_unlock(&LOCK_thread_count);
+ return tmp;
+}
+
+inline int32
+inc_thread_running()
+{
+ int32 num_thread_running;
+ my_atomic_rwlock_wrlock(&thread_running_lock);
+ num_thread_running= my_atomic_add32(&thread_running, 1);
+ my_atomic_rwlock_wrunlock(&thread_running_lock);
+ return (num_thread_running+1);
+}
+
+inline int32
+dec_thread_running()
+{
+ int32 num_thread_running;
+ my_atomic_rwlock_wrlock(&thread_running_lock);
+ num_thread_running= my_atomic_add32(&thread_running, -1);
+ my_atomic_rwlock_wrunlock(&thread_running_lock);
+ return (num_thread_running-1);
+}
+
+inline int32
+get_thread_running()
+{
+ int32 num_thread_running;
+ my_atomic_rwlock_wrlock(&thread_running_lock);
+ num_thread_running= my_atomic_load32(&thread_running);
+ my_atomic_rwlock_wrunlock(&thread_running_lock);
+ return num_thread_running;
+}
+
+void set_server_version(void);
+
+#if defined(MYSQL_DYNAMIC_PLUGIN) && defined(_WIN32)
+extern "C" THD *_current_thd_noinline();
+#define _current_thd() _current_thd_noinline()
+#else
+/*
+ THR_THD is a key which will be used to set/get THD* for a thread,
+ using my_pthread_setspecific_ptr()/my_thread_getspecific_ptr().
+*/
+extern pthread_key(THD*, THR_THD);
+inline THD *_current_thd(void)
+{
+ return my_pthread_getspecific_ptr(THD*,THR_THD);
+}
+#endif
+#define current_thd _current_thd()
+
+/*
+ @todo remove, make it static in ha_maria.cc
+ currently it's needed for sql_select.cc
+*/
+extern handlerton *maria_hton;
+
+extern uint extra_connection_count;
+extern my_bool opt_userstat_running, debug_assert_if_crashed_table;
+extern uint mysqld_extra_port;
+extern ulong opt_progress_report_time;
+extern ulong extra_max_connections;
+extern ulonglong denied_connections;
+extern ulong thread_created;
+extern scheduler_functions *thread_scheduler, *extra_thread_scheduler;
+extern char *opt_log_basename;
+extern my_bool opt_master_verify_checksum;
+extern my_bool opt_stack_trace;
+extern my_bool opt_expect_abort;
+extern my_bool opt_slave_sql_verify_checksum;
+extern ulong binlog_checksum_options;
+extern bool max_user_connections_checking;
+extern ulong opt_binlog_dbug_fsync_sleep;
+
+extern uint internal_tmp_table_max_key_length;
+extern uint internal_tmp_table_max_key_segments;
+
+extern uint volatile global_disable_checkpoint;
+extern my_bool opt_help;
+
+#endif /* MYSQLD_INCLUDED */
diff --git a/sql/mysqld_suffix.h b/sql/mysqld_suffix.h
index c696b17611f..fd515ac5998 100644
--- a/sql/mysqld_suffix.h
+++ b/sql/mysqld_suffix.h
@@ -1,4 +1,8 @@
-/* Copyright (c) 2000-2004, 2007 MySQL AB
+#ifndef MYSQLD_SUFFIX_INCLUDED
+#define MYSQLD_SUFFIX_INCLUDED
+
+/* Copyright (c) 2000-2004, 2006, 2007 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
@@ -11,7 +15,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@file
@@ -27,3 +31,4 @@
#else
#define MYSQL_SERVER_SUFFIX_STR MYSQL_SERVER_SUFFIX_DEF
#endif
+#endif /* MYSQLD_SUFFIX_INCLUDED */
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index c223f12b21f..4b78492c857 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -1,4 +1,5 @@
-/* Copyright (c) 2000, 2011, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2012, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -35,7 +36,6 @@
*/
#include <my_global.h>
#include <mysql.h>
-#include <mysql_embed.h>
#include <mysql_com.h>
#include <mysqld_error.h>
#include <my_sys.h>
@@ -44,9 +44,7 @@
#include <violite.h>
#include <signal.h>
#include <errno.h>
-#ifdef __NETWARE__
-#include <sys/select.h>
-#endif
+#include "probes_mysql.h"
#ifdef EMBEDDED_LIBRARY
#undef MYSQL_SERVER
@@ -54,20 +52,38 @@
#define MYSQL_CLIENT
#endif /*EMBEDDED_LIBRARY */
+/*
+ to reduce the number of ifdef's in the code
+*/
+#ifdef EXTRA_DEBUG
+#define EXTRA_DEBUG_fprintf fprintf
+#define EXTRA_DEBUG_fflush fflush
+#else
+static void inline EXTRA_DEBUG_fprintf(...) {}
+static int inline EXTRA_DEBUG_fflush(...) { return 0; }
+#endif
+#ifdef MYSQL_SERVER
+#define MYSQL_SERVER_my_error my_error
+#else
+static void inline MYSQL_SERVER_my_error(...) {}
+#endif
+
/*
The following handles the differences when this is linked between the
client and the server.
- This gives an error if a too big packet is found
- The server can change this with the -O switch, but because the client
- can't normally do this the client should have a bigger max_allowed_packet.
+ This gives an error if a too big packet is found.
+ The server can change this, but because the client can't normally do this
+ the client should have a bigger max_allowed_packet.
*/
#if defined(__WIN__) || !defined(MYSQL_SERVER)
/* The following is because alarms doesn't work on windows. */
+#ifndef NO_ALARM
#define NO_ALARM
#endif
+#endif
#ifndef NO_ALARM
#include "my_pthread.h"
@@ -81,25 +97,19 @@ void sql_print_error(const char *format,...);
#ifdef MYSQL_SERVER
/*
The following variables/functions should really not be declared
- extern, but as it's hard to include mysql_priv.h here, we have to
+ 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 ulong bytes_sent, bytes_received, net_big_packet_count;
-extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received;
-#ifndef MYSQL_INSTANCE_MANAGER
#ifdef HAVE_QUERY_CACHE
#define USE_QUERY_CACHE
-extern void query_cache_init_query(NET *net);
-extern void query_cache_insert(NET *net, const char *packet, ulong length);
+extern void query_cache_insert(const char *packet, ulong length,
+ unsigned pkt_nr);
#endif // HAVE_QUERY_CACHE
#define update_statistics(A) A
-#endif /* MYSQL_INSTANCE_MANGER */
-#endif /* defined(MYSQL_SERVER) && !defined(MYSQL_INSTANCE_MANAGER) */
-
-#if !defined(MYSQL_SERVER) || defined(MYSQL_INSTANCE_MANAGER)
+#else
#define update_statistics(A)
-#define thd_increment_bytes_sent(N)
#endif
#define TEST_BLOCKING 8
@@ -120,7 +130,7 @@ my_bool my_net_init(NET *net, Vio* vio)
MYF(MY_WME))))
DBUG_RETURN(1);
net->buff_end=net->buff+net->max_packet;
- net->error=0; net->return_errno=0; net->return_status=0;
+ net->error=0; net->return_status=0;
net->pkt_nr=net->compress_pkt_nr=0;
net->write_pos=net->read_pos = net->buff;
net->last_error[0]=0;
@@ -128,11 +138,7 @@ my_bool my_net_init(NET *net, Vio* vio)
net->where_b = net->remain_in_buf=0;
net->net_skip_rest_factor= 0;
net->last_errno=0;
-#ifdef USE_QUERY_CACHE
- query_cache_init_query(net);
-#else
- net->query_cache_query= 0;
-#endif
+ net->unused= 0;
if (vio != 0) /* If real connection */
{
@@ -153,7 +159,7 @@ my_bool my_net_init(NET *net, Vio* vio)
void net_end(NET *net)
{
DBUG_ENTER("net_end");
- my_free(net->buff,MYF(MY_ALLOW_ZERO_PTR));
+ my_free(net->buff);
net->buff=0;
DBUG_VOID_RETURN;
}
@@ -175,9 +181,7 @@ my_bool net_realloc(NET *net, size_t length)
/* @todo: 1 and 2 codes are identical. */
net->error= 1;
net->last_errno= ER_NET_PACKET_TOO_LARGE;
-#ifdef MYSQL_SERVER
- my_error(ER_NET_PACKET_TOO_LARGE, MYF(0));
-#endif
+ MYSQL_SERVER_my_error(ER_NET_PACKET_TOO_LARGE, MYF(0));
DBUG_RETURN(1);
}
pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1);
@@ -299,10 +303,8 @@ void net_clear(NET *net, my_bool clear_buffer __attribute__((unused)))
{
DBUG_PRINT("info",("skipped %ld bytes from file: %s",
(long) count, vio_description(net->vio)));
-#if defined(EXTRA_DEBUG)
- fprintf(stderr,"Note: net_clear() skipped %ld bytes from file: %s\n",
+ EXTRA_DEBUG_fprintf(stderr,"Note: net_clear() skipped %ld bytes from file: %s\n",
(long) count, vio_description(net->vio));
-#endif
}
else
{
@@ -372,8 +374,13 @@ my_bool
my_net_write(NET *net,const uchar *packet,size_t len)
{
uchar buff[NET_HEADER_SIZE];
+ int rc;
+
if (unlikely(!net->vio)) /* nowhere to write */
return 0;
+
+ MYSQL_NET_WRITE_START(len);
+
/*
Big packets are handled by splitting them in packets of MAX_PACKET_LENGTH
length. The last packet is always a packet that is < MAX_PACKET_LENGTH.
@@ -386,7 +393,10 @@ my_net_write(NET *net,const uchar *packet,size_t len)
buff[3]= (uchar) net->pkt_nr++;
if (net_write_buff(net, buff, NET_HEADER_SIZE) ||
net_write_buff(net, packet, z_size))
+ {
+ MYSQL_NET_WRITE_DONE(1);
return 1;
+ }
packet += z_size;
len-= z_size;
}
@@ -394,11 +404,16 @@ my_net_write(NET *net,const uchar *packet,size_t len)
int3store(buff,len);
buff[3]= (uchar) net->pkt_nr++;
if (net_write_buff(net, buff, NET_HEADER_SIZE))
+ {
+ MYSQL_NET_WRITE_DONE(1);
return 1;
+ }
#ifndef DEBUG_DATA_PACKETS
DBUG_DUMP("packet_header", buff, NET_HEADER_SIZE);
#endif
- return test(net_write_buff(net,packet,len));
+ rc= test(net_write_buff(net,packet,len));
+ MYSQL_NET_WRITE_DONE(rc);
+ return rc;
}
/**
@@ -436,9 +451,12 @@ net_write_command(NET *net,uchar command,
size_t length=len+1+head_len; /* 1 extra byte for command */
uchar buff[NET_HEADER_SIZE+1];
uint header_size=NET_HEADER_SIZE+1;
+ int rc;
DBUG_ENTER("net_write_command");
DBUG_PRINT("enter",("length: %lu", (ulong) len));
+ MYSQL_NET_WRITE_START(length);
+
buff[4]=command; /* For first packet */
if (length >= MAX_PACKET_LENGTH)
@@ -452,7 +470,10 @@ net_write_command(NET *net,uchar command,
if (net_write_buff(net, buff, header_size) ||
net_write_buff(net, header, head_len) ||
net_write_buff(net, packet, len))
+ {
+ MYSQL_NET_WRITE_DONE(1);
DBUG_RETURN(1);
+ }
packet+= len;
length-= MAX_PACKET_LENGTH;
len= MAX_PACKET_LENGTH;
@@ -463,9 +484,11 @@ net_write_command(NET *net,uchar command,
}
int3store(buff,length);
buff[3]= (uchar) net->pkt_nr++;
- DBUG_RETURN(test(net_write_buff(net, buff, header_size) ||
- (head_len && net_write_buff(net, header, head_len)) ||
- net_write_buff(net, packet, len) || net_flush(net)));
+ rc= test(net_write_buff(net, buff, header_size) ||
+ (head_len && net_write_buff(net, header, head_len)) ||
+ net_write_buff(net, packet, len) || net_flush(net));
+ MYSQL_NET_WRITE_DONE(rc);
+ DBUG_RETURN(rc);
}
/**
@@ -566,7 +589,7 @@ net_real_write(NET *net,const uchar *packet, size_t len)
DBUG_ENTER("net_real_write");
#if defined(MYSQL_SERVER) && defined(USE_QUERY_CACHE)
- query_cache_insert(net, (char*) packet, len);
+ query_cache_insert((char*) packet, len, net->pkt_nr);
#endif
if (net->error == 2)
@@ -630,16 +653,12 @@ net_real_write(NET *net,const uchar *packet, size_t len)
{
if (vio_should_retry(net->vio) && retry_count++ < net->retry_count)
continue;
-#ifdef EXTRA_DEBUG
- fprintf(stderr,
+ EXTRA_DEBUG_fprintf(stderr,
"%s: my_net_write: fcntl returned error %d, aborting thread\n",
my_progname,vio_errno(net->vio));
-#endif /* EXTRA_DEBUG */
net->error= 2; /* Close socket */
net->last_errno= ER_NET_PACKET_TOO_LARGE;
-#ifdef MYSQL_SERVER
- my_error(ER_NET_PACKET_TOO_LARGE, MYF(0));
-#endif
+ MYSQL_SERVER_my_error(ER_NET_PACKET_TOO_LARGE, MYF(0));
goto end;
}
retry_count=0;
@@ -653,24 +672,20 @@ net_real_write(NET *net,const uchar *packet, size_t len)
{
if (retry_count++ < net->retry_count)
continue;
-#ifdef EXTRA_DEBUG
- fprintf(stderr, "%s: write looped, aborting thread\n",
+ EXTRA_DEBUG_fprintf(stderr, "%s: write looped, aborting thread\n",
my_progname);
-#endif /* EXTRA_DEBUG */
}
-#if defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER)
+#ifndef MYSQL_SERVER
if (vio_errno(net->vio) == SOCKET_EINTR)
{
DBUG_PRINT("warning",("Interrupted write. Retrying..."));
continue;
}
-#endif /* defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER) */
+#endif /* !defined(MYSQL_SERVER) */
net->error= 2; /* Close socket */
net->last_errno= (interrupted ? ER_NET_WRITE_INTERRUPTED :
ER_NET_ERROR_ON_WRITE);
-#ifdef MYSQL_SERVER
- my_error(net->last_errno, MYF(0));
-#endif /* MYSQL_SERVER */
+ MYSQL_SERVER_my_error(net->last_errno, MYF(0));
break;
}
pos+=length;
@@ -681,7 +696,7 @@ net_real_write(NET *net,const uchar *packet, size_t len)
#endif
#ifdef HAVE_COMPRESS
if (net->compress)
- my_free((char*) packet,MYF(0));
+ my_free((void*) packet);
#endif
if (thr_alarm_in_use(&alarmed))
{
@@ -828,10 +843,10 @@ my_real_read(NET *net, size_t *complen)
DBUG_PRINT("info",("vio_read returned %ld errno: %d",
(long) length, vio_errno(net->vio)));
-#if !defined(__WIN__) || defined(MYSQL_SERVER)
+#if !defined(__WIN__) && defined(MYSQL_SERVER)
/*
We got an error that there was no data on the socket. We now set up
- an alarm to not 'read forever', change the socket to non blocking
+ an alarm to not 'read forever', change the socket to the blocking
mode and try again
*/
if ((interrupted || length == 0) && !thr_alarm_in_use(&alarmed))
@@ -847,35 +862,29 @@ my_real_read(NET *net, size_t *complen)
DBUG_PRINT("error",
("fcntl returned error %d, aborting thread",
vio_errno(net->vio)));
-#ifdef EXTRA_DEBUG
- fprintf(stderr,
+ EXTRA_DEBUG_fprintf(stderr,
"%s: read: fcntl returned error %d, aborting thread\n",
my_progname,vio_errno(net->vio));
-#endif /* EXTRA_DEBUG */
len= packet_error;
net->error= 2; /* Close socket */
net->last_errno= ER_NET_FCNTL_ERROR;
-#ifdef MYSQL_SERVER
- my_error(ER_NET_FCNTL_ERROR, MYF(0));
-#endif
+ MYSQL_SERVER_my_error(ER_NET_FCNTL_ERROR, MYF(0));
goto end;
}
retry_count=0;
continue;
}
}
-#endif /* (!defined(__WIN__) || defined(MYSQL_SERVER) */
+#endif /* (!defined(__WIN__) && defined(MYSQL_SERVER) */
if (thr_alarm_in_use(&alarmed) && !thr_got_alarm(&alarmed) &&
interrupted)
{ /* Probably in MIT threads */
if (retry_count++ < net->retry_count)
continue;
-#ifdef EXTRA_DEBUG
- fprintf(stderr, "%s: read looped with error %d, aborting thread\n",
+ EXTRA_DEBUG_fprintf(stderr, "%s: read looped with error %d, aborting thread\n",
my_progname,vio_errno(net->vio));
-#endif /* EXTRA_DEBUG */
}
-#if defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER)
+#ifndef MYSQL_SERVER
if (vio_errno(net->vio) == SOCKET_EINTR)
{
DBUG_PRINT("warning",("Interrupted read. Retrying..."));
@@ -889,9 +898,7 @@ my_real_read(NET *net, size_t *complen)
net->last_errno= (vio_was_interrupted(net->vio) ?
ER_NET_READ_INTERRUPTED :
ER_NET_READ_ERROR);
-#ifdef MYSQL_SERVER
- my_error(net->last_errno, MYF(0));
-#endif
+ MYSQL_SERVER_my_error(net->last_errno, MYF(0));
goto end;
}
remain -= (uint32) length;
@@ -917,19 +924,17 @@ my_real_read(NET *net, size_t *complen)
the server expects the client to send a file, but the client
may reply with a new command instead.
*/
-#if defined (EXTRA_DEBUG) && !defined (MYSQL_SERVER)
- fflush(stdout);
- fprintf(stderr,"Error: Packets out of order (Found: %d, expected %d)\n",
+#ifndef MYSQL_SERVER
+ EXTRA_DEBUG_fflush(stdout);
+ EXTRA_DEBUG_fprintf(stderr,"Error: Packets out of order (Found: %d, expected %d)\n",
(int) net->buff[net->where_b + 3],
(uint) (uchar) net->pkt_nr);
- fflush(stderr);
+ EXTRA_DEBUG_fflush(stderr);
#endif
}
len= packet_error;
/* Not a NET error on the client. XXX: why? */
-#ifdef MYSQL_SERVER
- my_error(ER_NET_PACKETS_OUT_OF_ORDER, MYF(0));
-#endif
+ MYSQL_SERVER_my_error(ER_NET_PACKETS_OUT_OF_ORDER, MYF(0));
goto end;
}
net->compress_pkt_nr= ++net->pkt_nr;
@@ -1012,6 +1017,8 @@ my_net_read(NET *net)
{
size_t len, complen;
+ MYSQL_NET_READ_START();
+
#ifdef HAVE_COMPRESS
if (!net->compress)
{
@@ -1035,6 +1042,7 @@ my_net_read(NET *net)
net->read_pos = net->buff + net->where_b;
if (len != packet_error)
net->read_pos[len]=0; /* Safeguard for mysql_use_result */
+ MYSQL_NET_READ_DONE(0, len);
return len;
#ifdef HAVE_COMPRESS
}
@@ -1118,15 +1126,17 @@ my_net_read(NET *net)
net->where_b=buf_length;
if ((packet_len = my_real_read(net,&complen)) == packet_error)
+ {
+ MYSQL_NET_READ_DONE(1, 0);
return packet_error;
+ }
if (my_uncompress(net->buff + net->where_b, packet_len,
&complen))
{
net->error= 2; /* caller will close socket */
net->last_errno= ER_NET_UNCOMPRESS_ERROR;
-#ifdef MYSQL_SERVER
- my_error(ER_NET_UNCOMPRESS_ERROR, MYF(0));
-#endif
+ MYSQL_SERVER_my_error(ER_NET_UNCOMPRESS_ERROR, MYF(0));
+ MYSQL_NET_READ_DONE(1, 0);
return packet_error;
}
buf_length+= complen;
@@ -1141,6 +1151,7 @@ my_net_read(NET *net)
net->read_pos[len]=0; /* Safeguard for mysql_use_result */
}
#endif /* HAVE_COMPRESS */
+ MYSQL_NET_READ_DONE(0, len);
return len;
}
@@ -1149,6 +1160,8 @@ void my_net_set_read_timeout(NET *net, uint timeout)
{
DBUG_ENTER("my_net_set_read_timeout");
DBUG_PRINT("enter", ("timeout: %d", timeout));
+ if (net->read_timeout == timeout)
+ DBUG_VOID_RETURN;
net->read_timeout= timeout;
#ifdef NO_ALARM
if (net->vio)
@@ -1162,6 +1175,8 @@ void my_net_set_write_timeout(NET *net, uint timeout)
{
DBUG_ENTER("my_net_set_write_timeout");
DBUG_PRINT("enter", ("timeout: %d", timeout));
+ if (net->write_timeout == timeout)
+ DBUG_VOID_RETURN;
net->write_timeout= timeout;
#ifdef NO_ALARM
if (net->vio)
diff --git a/sql/nt_servc.cc b/sql/nt_servc.cc
index f41fa08f828..d6a8eac7ed5 100644
--- a/sql/nt_servc.cc
+++ b/sql/nt_servc.cc
@@ -10,6 +10,7 @@
#include <windows.h>
#include <process.h>
#include <stdio.h>
+#include <stdlib.h>
#include "nt_servc.h"
@@ -275,7 +276,13 @@ error:
void NTService::SetRunning()
{
if (pService)
- pService->SetStatus(SERVICE_RUNNING,NO_ERROR, 0, 0, 0);
+ pService->SetStatus(SERVICE_RUNNING, NO_ERROR, 0, 0, 0);
+}
+
+void NTService::SetSlowStarting(unsigned long timeout)
+{
+ if (pService)
+ pService->SetStatus(SERVICE_START_PENDING,NO_ERROR, 0, 0, timeout);
}
@@ -374,29 +381,6 @@ void NTService::ServiceCtrlHandler(DWORD ctrlCode)
dwState=pService->dwState; // get current state
switch(ctrlCode) {
-
-#ifdef NOT_USED /* do we need this ? */
- case SERVICE_CONTROL_PAUSE:
- if (pService->bRunning && ! pService->bPause)
- {
- dwState = SERVICE_PAUSED;
- pService->SetStatus(SERVICE_PAUSE_PENDING,NO_ERROR, 0, 1,
- pService->nPauseTimeOut);
- pService->PauseService();
- }
- break;
-
- case SERVICE_CONTROL_CONTINUE:
- if (pService->bRunning && pService->bPause)
- {
- dwState = SERVICE_RUNNING;
- pService->SetStatus(SERVICE_CONTINUE_PENDING,NO_ERROR, 0, 1,
- pService->nResumeTimeOut);
- pService->ResumeService();
- }
- break;
-#endif
-
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP:
dwState = SERVICE_STOP_PENDING;
diff --git a/sql/nt_servc.h b/sql/nt_servc.h
index 2f0d07df543..949499d8d7f 100644
--- a/sql/nt_servc.h
+++ b/sql/nt_servc.h
@@ -1,3 +1,6 @@
+#ifndef NT_SERVC_INCLUDED
+#define NT_SERVC_INCLUDED
+
/**
@file
@@ -68,6 +71,16 @@ class NTService
*/
void SetRunning(void);
+ /**
+ Sets a timeout after which SCM will abort service startup if SetRunning()
+ was not called or the timeout was not extended with another call to
+ SetSlowStarting(). Should be called when static initialization completes,
+ and the variable initialization part begins
+
+ @arg timeout the timeout to pass to the SCM (in milliseconds)
+ */
+ void SetSlowStarting(unsigned long timeout);
+
/*
Stop() is to be called by the application to stop
the service
@@ -98,3 +111,5 @@ class NTService
};
/* ------------------------- the end -------------------------------------- */
+
+#endif /* NT_SERVC_INCLUDED */
diff --git a/sql/opt_index_cond_pushdown.cc b/sql/opt_index_cond_pushdown.cc
index c2b49d863a1..df9dae8e442 100644
--- a/sql/opt_index_cond_pushdown.cc
+++ b/sql/opt_index_cond_pushdown.cc
@@ -1,5 +1,21 @@
-#include "mysql_priv.h"
+/*
+ Copyright (c) 2009, 2012, Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
#include "sql_select.h"
+#include "sql_test.h"
/****************************************************************************
* Index Condition Pushdown code starts
@@ -359,7 +375,7 @@ void push_index_cond(JOIN_TAB *tab, uint keyno)
if (idx_cond)
{
Item *idx_remainder_cond= 0;
- tab->pre_idx_push_select_cond= tab->select->cond;
+ tab->pre_idx_push_select_cond= tab->select_cond;
/*
For BKA cache we store condition to special BKA cache field
because evaluation of the condition requires additional operations
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index dbfb9497291..17b24aa70fd 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
Copyright (c) 2008, 2014, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
@@ -12,7 +12,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
TODO:
@@ -108,9 +108,16 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "key.h" // is_key_used, key_copy, key_cmp, key_restore
+#include "sql_parse.h" // check_stack_overrun
+#include "sql_partition.h" // get_part_id_func, PARTITION_ITERATOR,
+ // struct partition_info
+#include "sql_base.h" // free_io_cache
+#include "records.h" // init_read_record, end_read_record
#include <m_ctype.h>
#include "sql_select.h"
+#include "filesort.h" // filesort_free_buffers
#ifndef EXTRA_DEBUG
#define test_rb_tree(A,B) {}
@@ -351,31 +358,54 @@ public:
elements(1),use_count(1),left(0),right(0),
next_key_part(0), color(BLACK), type(type_arg)
{}
- inline bool is_same(SEL_ARG *arg)
+ /**
+ returns true if a range predicate is equal. Use all_same()
+ to check for equality of all the predicates on this keypart.
+ */
+ inline bool is_same(const SEL_ARG *arg) const
{
if (type != arg->type || part != arg->part)
- return 0;
+ return false;
if (type != KEY_RANGE)
- return 1;
+ return true;
return cmp_min_to_min(arg) == 0 && cmp_max_to_max(arg) == 0;
}
+ /**
+ returns true if all the predicates in the keypart tree are equal
+ */
+ bool all_same(const SEL_ARG *arg) const
+ {
+ if (type != arg->type || part != arg->part)
+ return false;
+ if (type != KEY_RANGE)
+ return true;
+ if (arg == this)
+ return true;
+ const SEL_ARG *cmp_arg= arg->first();
+ const SEL_ARG *cur_arg= first();
+ for (; cur_arg && cmp_arg && cur_arg->is_same(cmp_arg);
+ cur_arg= cur_arg->next, cmp_arg= cmp_arg->next) ;
+ if (cur_arg || cmp_arg)
+ return false;
+ return true;
+ }
inline void merge_flags(SEL_ARG *arg) { maybe_flag|=arg->maybe_flag; }
inline void maybe_smaller() { maybe_flag=1; }
/* Return true iff it's a single-point null interval */
inline bool is_null_interval() { return maybe_null && max_value[0] == 1; }
- inline int cmp_min_to_min(SEL_ARG* arg)
+ inline int cmp_min_to_min(const SEL_ARG* arg) const
{
return sel_cmp(field,min_value, arg->min_value, min_flag, arg->min_flag);
}
- inline int cmp_min_to_max(SEL_ARG* arg)
+ inline int cmp_min_to_max(const SEL_ARG* arg) const
{
return sel_cmp(field,min_value, arg->max_value, min_flag, arg->max_flag);
}
- inline int cmp_max_to_max(SEL_ARG* arg)
+ inline int cmp_max_to_max(const SEL_ARG* arg) const
{
return sel_cmp(field,max_value, arg->max_value, max_flag, arg->max_flag);
}
- inline int cmp_max_to_min(SEL_ARG* arg)
+ inline int cmp_max_to_min(const SEL_ARG* arg) const
{
return sel_cmp(field,max_value, arg->min_value, max_flag, arg->min_flag);
}
@@ -493,8 +523,19 @@ public:
return 0;
}
- /* returns a number of keypart values appended to the key buffer */
- int store_min_key(KEY_PART *key, uchar **range_key, uint *range_key_flag)
+ /*
+ Returns a number of keypart values appended to the key buffer
+ for min key and max key. This function is used by both Range
+ Analysis and Partition pruning. For partition pruning we have
+ to ensure that we don't store also subpartition fields. Thus
+ we have to stop at the last partition part and not step into
+ the subpartition fields. For Range Analysis we set last_part
+ to MAX_KEY which we should never reach.
+ */
+ int store_min_key(KEY_PART *key,
+ uchar **range_key,
+ uint *range_key_flag,
+ uint last_part)
{
SEL_ARG *key_tree= first();
uint res= key_tree->store_min(key[key_tree->part].store_length,
@@ -502,15 +543,21 @@ public:
*range_key_flag|= key_tree->min_flag;
if (key_tree->next_key_part &&
key_tree->next_key_part->type == SEL_ARG::KEY_RANGE &&
+ key_tree->part != last_part &&
key_tree->next_key_part->part == key_tree->part+1 &&
!(*range_key_flag & (NO_MIN_RANGE | NEAR_MIN)))
- res+= key_tree->next_key_part->store_min_key(key, range_key,
- range_key_flag);
+ res+= key_tree->next_key_part->store_min_key(key,
+ range_key,
+ range_key_flag,
+ last_part);
return res;
}
/* returns a number of keypart values appended to the key buffer */
- int store_max_key(KEY_PART *key, uchar **range_key, uint *range_key_flag)
+ int store_max_key(KEY_PART *key,
+ uchar **range_key,
+ uint *range_key_flag,
+ uint last_part)
{
SEL_ARG *key_tree= last();
uint res=key_tree->store_max(key[key_tree->part].store_length,
@@ -518,10 +565,13 @@ public:
(*range_key_flag)|= key_tree->max_flag;
if (key_tree->next_key_part &&
key_tree->next_key_part->type == SEL_ARG::KEY_RANGE &&
+ key_tree->part != last_part &&
key_tree->next_key_part->part == key_tree->part+1 &&
!(*range_key_flag & (NO_MAX_RANGE | NEAR_MAX)))
- res+= key_tree->next_key_part->store_max_key(key, range_key,
- range_key_flag);
+ res+= key_tree->next_key_part->store_max_key(key,
+ range_key,
+ range_key_flag,
+ last_part);
return res;
}
@@ -535,6 +585,7 @@ public:
void test_use_count(SEL_ARG *root);
#endif
SEL_ARG *first();
+ const SEL_ARG *first() const;
SEL_ARG *last();
void make_root();
inline bool simple_key()
@@ -624,6 +675,18 @@ public:
SEL_ARG *clone_tree(RANGE_OPT_PARAM *param);
};
+/**
+ Helper function to compare two SEL_ARG's.
+*/
+static bool all_same(const SEL_ARG *sa1, const SEL_ARG *sa2)
+{
+ if (sa1 == NULL && sa2 == NULL)
+ return true;
+ if ((sa1 != NULL && sa2 == NULL) || (sa1 == NULL && sa2 != NULL))
+ return false;
+ return sa1->all_same(sa2);
+}
+
class SEL_IMERGE;
#define CLONE_KEY1_MAYBE 1
@@ -816,8 +879,17 @@ public:
/* Number of SEL_ARG objects allocated by SEL_ARG::clone_tree operations */
uint alloced_sel_args;
+
bool force_default_mrr;
KEY_PART *key[MAX_KEY]; /* First key parts of keys used in the query */
+
+ bool statement_should_be_aborted() const
+ {
+ return
+ thd->is_fatal_error ||
+ thd->is_error() ||
+ alloced_sel_args > SEL_ARG::MAX_SEL_ARGS;
+ }
};
class PARAM : public RANGE_OPT_PARAM
@@ -897,7 +969,8 @@ TABLE_READ_PLAN *merge_same_index_scans(PARAM *param, SEL_IMERGE *imerge,
TRP_INDEX_MERGE *imerge_trp,
double read_time);
static
-TRP_GROUP_MIN_MAX *get_best_group_min_max(PARAM *param, SEL_TREE *tree);
+TRP_GROUP_MIN_MAX *get_best_group_min_max(PARAM *param, SEL_TREE *tree,
+ double read_time);
#ifndef DBUG_OFF
static void print_sel_tree(PARAM *param, SEL_TREE *tree, key_map *tree_map,
@@ -1698,7 +1771,7 @@ SQL_SELECT *make_select(TABLE *head, table_map const_tables,
select->file= *head->sort.io_cache;
select->records=(ha_rows) (select->file.end_of_file/
head->file->ref_length);
- my_free(head->sort.io_cache, MYF(0));
+ my_free(head->sort.io_cache);
head->sort.io_cache=0;
}
DBUG_RETURN(select);
@@ -1741,7 +1814,7 @@ 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)
- :doing_key_read(0),/*error(0),*/free_file(0),/*in_range(0),*/cur_range(NULL),last_range(0),dont_free(0)
+ :doing_key_read(0),free_file(0),cur_range(NULL),last_range(0),dont_free(0)
{
my_bitmap_map *bitmap;
DBUG_ENTER("QUICK_RANGE_SELECT::QUICK_RANGE_SELECT");
@@ -1766,8 +1839,6 @@ QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr,
bzero((char*) &alloc,sizeof(alloc));
file= head->file;
record= head->record[0];
- save_read_set= head->read_set;
- save_write_set= head->write_set;
/* Allocate a bitmap for used columns (Q: why not on MEM_ROOT?) */
if (!(bitmap= (my_bitmap_map*) my_malloc(head->s->column_bitmap_size,
@@ -1835,10 +1906,9 @@ QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT()
}
delete_dynamic(&ranges); /* ranges are allocated in alloc */
free_root(&alloc,MYF(0));
- my_free((char*) column_bitmap.bitmap, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(column_bitmap.bitmap);
}
- head->column_bitmaps_set(save_read_set, save_write_set);
- x_free(mrr_buf_desc);
+ my_free(mrr_buf_desc);
DBUG_VOID_RETURN;
}
@@ -1870,7 +1940,8 @@ int QUICK_INDEX_SORT_SELECT::init()
int QUICK_INDEX_SORT_SELECT::reset()
{
DBUG_ENTER("QUICK_INDEX_SORT_SELECT::reset");
- DBUG_RETURN(read_keys_and_merge());
+ const int retval= read_keys_and_merge();
+ DBUG_RETURN(retval);
}
bool
@@ -1979,14 +2050,16 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler, MEM_ROOT *alloc
{
handler *save_file= file, *org_file;
my_bool org_key_read;
- THD *thd;
+ THD *thd= head->in_use;
+ MY_BITMAP * const save_read_set= head->read_set;
+ MY_BITMAP * const save_write_set= head->write_set;
DBUG_ENTER("QUICK_RANGE_SELECT::init_ror_merged_scan");
in_ror_merged_scan= 1;
if (reuse_handler)
{
DBUG_PRINT("info", ("Reusing handler 0x%lx", (long) file));
- if (init() || reset())
+ if (init())
{
DBUG_RETURN(1);
}
@@ -2001,7 +2074,6 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler, MEM_ROOT *alloc
DBUG_RETURN(0);
}
- thd= head->in_use;
if (!(file= head->file->clone(head->s->normalized_path.str, alloc)))
{
/*
@@ -2021,7 +2093,7 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler, MEM_ROOT *alloc
if (file->ha_external_lock(thd, F_RDLCK))
goto failure;
- if (init() || reset())
+ if (init())
{
file->ha_external_lock(thd, F_UNLCK);
file->ha_close();
@@ -2031,6 +2103,7 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler, MEM_ROOT *alloc
last_rowid= file->ref;
end:
+ DBUG_ASSERT(head->read_set == &column_bitmap);
/*
We are only going to read key fields and call position() on 'file'
The following sets head->tmp_set to only use this key and then updates
@@ -2041,34 +2114,33 @@ end:
org_key_read= head->key_read;
head->file= file;
head->key_read= 0;
+ head->mark_columns_used_by_index_no_reset(index, head->read_set);
+
if (!head->no_keyread)
{
doing_key_read= 1;
- head->mark_columns_used_by_index(index);
+ head->enable_keyread();
}
head->prepare_for_position();
- if (head->no_keyread)
- {
- /*
- We can get here when doing multi-table delete and having index_merge
- condition on a table that we're deleting from. It probably doesn't make
- sense to use index_merge, but de-facto it is used.
-
- When it is used, we need to index columns to be read (before maria-5.3,
- read_multi_range_first() would set it).
- We shouldn't call mark_columns_used_by_index(), because it calls
- enable_keyread(), which is not allowed.
- */
- head->mark_columns_used_by_index_no_reset(index, head->read_set);
- }
-
head->file= org_file;
head->key_read= org_key_read;
- bitmap_copy(&column_bitmap, head->read_set);
- head->column_bitmaps_set(&column_bitmap, &column_bitmap);
+ /* Restore head->read_set (and write_set) to what they had before the call */
+ head->column_bitmaps_set(save_read_set, save_write_set);
+
+ if (reset())
+ {
+ if (!reuse_handler)
+ {
+ file->ha_external_lock(thd, F_UNLCK);
+ file->ha_close();
+ goto failure;
+ }
+ else
+ DBUG_RETURN(1);
+ }
DBUG_RETURN(0);
failure:
@@ -2107,16 +2179,27 @@ int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler,
There is no use of this->file. Use it for the first of merged range
selects.
*/
- if (quick->init_ror_merged_scan(TRUE, alloc))
- DBUG_RETURN(1);
+ int error= quick->init_ror_merged_scan(TRUE, alloc);
+ if (error)
+ DBUG_RETURN(error);
quick->file->extra(HA_EXTRA_KEYREAD_PRESERVE_FIELDS);
}
while ((cur= quick_it++))
{
quick= cur->quick;
+#ifndef DBUG_OFF
+ const MY_BITMAP * const save_read_set= quick->head->read_set;
+ const MY_BITMAP * const save_write_set= quick->head->write_set;
+#endif
if (quick->init_ror_merged_scan(FALSE, alloc))
DBUG_RETURN(1);
quick->file->extra(HA_EXTRA_KEYREAD_PRESERVE_FIELDS);
+
+ // Sets are shared by all members of "quick_selects" so must not change
+#ifndef DBUG_OFF
+ DBUG_ASSERT(quick->head->read_set == save_read_set);
+ DBUG_ASSERT(quick->head->write_set == save_write_set);
+#endif
/* All merged scans share the same record buffer in intersection. */
quick->record= head->record[0];
}
@@ -2207,6 +2290,29 @@ QUICK_ROR_UNION_SELECT::QUICK_ROR_UNION_SELECT(THD *thd_param,
/*
+ Comparison function to be used QUICK_ROR_UNION_SELECT::queue priority
+ queue.
+
+ SYNPOSIS
+ QUICK_ROR_UNION_SELECT_queue_cmp()
+ arg Pointer to QUICK_ROR_UNION_SELECT
+ val1 First merged select
+ val2 Second merged select
+*/
+
+C_MODE_START
+
+static int QUICK_ROR_UNION_SELECT_queue_cmp(void *arg, uchar *val1, uchar *val2)
+{
+ QUICK_ROR_UNION_SELECT *self= (QUICK_ROR_UNION_SELECT*)arg;
+ return self->head->file->cmp_ref(((QUICK_SELECT_I*)val1)->last_rowid,
+ ((QUICK_SELECT_I*)val2)->last_rowid);
+}
+
+C_MODE_END
+
+
+/*
Do post-constructor initialization.
SYNOPSIS
QUICK_ROR_UNION_SELECT::init()
@@ -2220,7 +2326,7 @@ int QUICK_ROR_UNION_SELECT::init()
{
DBUG_ENTER("QUICK_ROR_UNION_SELECT::init");
if (init_queue(&queue, quick_selects.elements, 0,
- FALSE , QUICK_ROR_UNION_SELECT::queue_cmp,
+ FALSE , QUICK_ROR_UNION_SELECT_queue_cmp,
(void*) this, 0, 0))
{
bzero(&queue, sizeof(QUEUE));
@@ -2235,25 +2341,6 @@ int QUICK_ROR_UNION_SELECT::init()
/*
- Comparison function to be used QUICK_ROR_UNION_SELECT::queue priority
- queue.
-
- SYNPOSIS
- QUICK_ROR_UNION_SELECT::queue_cmp()
- arg Pointer to QUICK_ROR_UNION_SELECT
- val1 First merged select
- val2 Second merged select
-*/
-
-int QUICK_ROR_UNION_SELECT::queue_cmp(void *arg, uchar *val1, uchar *val2)
-{
- QUICK_ROR_UNION_SELECT *self= (QUICK_ROR_UNION_SELECT*)arg;
- return self->head->file->cmp_ref(((QUICK_SELECT_I*)val1)->last_rowid,
- ((QUICK_SELECT_I*)val2)->last_rowid);
-}
-
-
-/*
Initialize quick select for row retrieval.
SYNOPSIS
reset()
@@ -2287,8 +2374,8 @@ int QUICK_ROR_UNION_SELECT::reset()
List_iterator_fast<QUICK_SELECT_I> it(quick_selects);
while ((quick= it++))
{
- if (quick->reset())
- DBUG_RETURN(1);
+ if ((error= quick->reset()))
+ DBUG_RETURN(error);
if ((error= quick->get_next()))
{
if (error == HA_ERR_END_OF_FILE)
@@ -2299,10 +2386,10 @@ int QUICK_ROR_UNION_SELECT::reset()
queue_insert(&queue, (uchar*)quick);
}
- if (head->file->ha_rnd_init_with_error(1))
+ if ((error= head->file->ha_rnd_init(1)))
{
DBUG_PRINT("error", ("ROR index_merge rnd_init call failed"));
- DBUG_RETURN(1);
+ DBUG_RETURN(error);
}
DBUG_RETURN(0);
@@ -2425,6 +2512,13 @@ SEL_ARG *SEL_ARG::clone(RANGE_OPT_PARAM *param, SEL_ARG *new_parent,
return tmp;
}
+/**
+ This gives the first SEL_ARG in the interval list, and the minimal element
+ in the red-black tree
+
+ @return
+ SEL_ARG first SEL_ARG in the interval list
+*/
SEL_ARG *SEL_ARG::first()
{
SEL_ARG *next_arg=this;
@@ -2435,6 +2529,11 @@ SEL_ARG *SEL_ARG::first()
return next_arg;
}
+const SEL_ARG *SEL_ARG::first() const
+{
+ return const_cast<SEL_ARG*>(this)->first();
+}
+
SEL_ARG *SEL_ARG::last()
{
SEL_ARG *next_arg=this;
@@ -2510,96 +2609,6 @@ SEL_ARG *SEL_ARG::clone_tree(RANGE_OPT_PARAM *param)
/*
- Find the best index to retrieve first N records in given order
-
- SYNOPSIS
- get_index_for_order()
- table Table to be accessed
- order Required ordering
- limit Number of records that will be retrieved
-
- DESCRIPTION
- Find the best index that allows to retrieve first #limit records in the
- given order cheaper then one would retrieve them using full table scan.
-
- IMPLEMENTATION
- Run through all table indexes and find the shortest index that allows
- records to be retrieved in given order. We look for the shortest index
- as we will have fewer index pages to read with it.
-
- This function is used only by UPDATE/DELETE, so we take into account how
- the UPDATE/DELETE code will work:
- * index can only be scanned in forward direction
- * HA_EXTRA_KEYREAD will not be used
- Perhaps these assumptions could be relaxed.
-
- RETURN
- Number of the index that produces the required ordering in the cheapest way
- MAX_KEY if no such index was found.
-*/
-
-uint get_index_for_order(TABLE *table, ORDER *order, ha_rows limit)
-{
- uint idx;
- uint match_key= MAX_KEY, match_key_len= MAX_KEY_LENGTH + 1;
- ORDER *ord;
-
- for (ord= order; ord; ord= ord->next)
- if (!ord->asc)
- return MAX_KEY;
-
- for (idx= 0; idx < table->s->keys; idx++)
- {
- if (!(table->keys_in_use_for_query.is_set(idx)))
- continue;
- KEY_PART_INFO *keyinfo= table->key_info[idx].key_part;
- uint n_parts= table->key_info[idx].key_parts;
- uint partno= 0;
-
- /*
- The below check is sufficient considering we now have either BTREE
- indexes (records are returned in order for any index prefix) or HASH
- indexes (records are not returned in order for any index prefix).
- */
- if (!(table->file->index_flags(idx, 0, 1) & HA_READ_ORDER))
- continue;
- for (ord= order; ord && partno < n_parts; ord= ord->next, partno++)
- {
- Item *item= order->item[0];
- if (!(item->type() == Item::FIELD_ITEM &&
- ((Item_field*)item)->field->eq(keyinfo[partno].field)))
- break;
- }
-
- if (!ord && table->key_info[idx].key_length < match_key_len)
- {
- /*
- Ok, the ordering is compatible and this key is shorter then
- previous match (we want shorter keys as we'll have to read fewer
- index pages for the same number of records)
- */
- match_key= idx;
- match_key_len= table->key_info[idx].key_length;
- }
- }
-
- if (match_key != MAX_KEY)
- {
- /*
- Found an index that allows records to be retrieved in the requested
- order. Now we'll check if using the index is cheaper then doing a table
- scan.
- */
- double full_scan_time= table->file->scan_time();
- double index_scan_time= table->file->read_time(match_key, 1, limit);
- if (index_scan_time > full_scan_time)
- match_key= MAX_KEY;
- }
- return match_key;
-}
-
-
-/*
Table rows retrieval plan. Range optimizer creates QUICK_SELECT_I-derived
objects from table read plans.
*/
@@ -2771,7 +2780,7 @@ public:
class TRP_GROUP_MIN_MAX : public TABLE_READ_PLAN
{
private:
- bool have_min, have_max;
+ bool have_min, have_max, have_agg_distinct;
KEY_PART_INFO *min_max_arg_part;
uint group_prefix_len;
uint used_key_parts;
@@ -2783,11 +2792,13 @@ private:
SEL_TREE *range_tree; /* Represents all range predicates in the query. */
SEL_ARG *index_tree; /* The SEL_ARG sub-tree corresponding to index_info. */
uint param_idx; /* Index of used key in param->key. */
- /* Number of records selected by the ranges in index_tree. */
+ bool is_index_scan; /* Use index_next() instead of random read */
public:
+ /* Number of records selected by the ranges in index_tree. */
ha_rows quick_prefix_records;
public:
- TRP_GROUP_MIN_MAX(bool have_min_arg, bool have_max_arg,
+ TRP_GROUP_MIN_MAX(bool have_min_arg, bool have_max_arg,
+ bool have_agg_distinct_arg,
KEY_PART_INFO *min_max_arg_part_arg,
uint group_prefix_len_arg, uint used_key_parts_arg,
uint group_key_parts_arg, KEY *index_info_arg,
@@ -2796,11 +2807,12 @@ public:
SEL_TREE *tree_arg, SEL_ARG *index_tree_arg,
uint param_idx_arg, ha_rows quick_prefix_records_arg)
: have_min(have_min_arg), have_max(have_max_arg),
+ have_agg_distinct(have_agg_distinct_arg),
min_max_arg_part(min_max_arg_part_arg),
group_prefix_len(group_prefix_len_arg), used_key_parts(used_key_parts_arg),
group_key_parts(group_key_parts_arg), index_info(index_info_arg),
index(index_arg), key_infix_len(key_infix_len_arg), range_tree(tree_arg),
- index_tree(index_tree_arg), param_idx(param_idx_arg),
+ index_tree(index_tree_arg), param_idx(param_idx_arg), is_index_scan(FALSE),
quick_prefix_records(quick_prefix_records_arg)
{
if (key_infix_len)
@@ -2810,6 +2822,7 @@ public:
QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows,
MEM_ROOT *parent_alloc);
+ void use_index_scan() { is_index_scan= TRUE; }
};
@@ -2990,9 +3003,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
keys_to_use.intersect(head->keys_in_use_for_query);
if (!keys_to_use.is_clear_all())
{
-#ifndef EMBEDDED_LIBRARY // Avoid compiler warning
uchar buff[STACK_BUFF_ALLOC];
-#endif
MEM_ROOT alloc;
SEL_TREE *tree= NULL;
KEY_PART *key_parts;
@@ -3020,9 +3031,10 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
thd->no_errors=1; // Don't warn about NULL
init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0);
- if (!(param.key_parts= (KEY_PART*) alloc_root(&alloc,
- sizeof(KEY_PART)*
- head->s->key_parts)) ||
+ if (!(param.key_parts=
+ (KEY_PART*) alloc_root(&alloc,
+ sizeof(KEY_PART) *
+ head->s->actual_n_key_parts(thd))) ||
fill_used_fields_bitmap(&param))
{
thd->no_errors=0;
@@ -3040,6 +3052,8 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
for (idx=0 ; idx < head->s->keys ; idx++, key_info++)
{
KEY_PART_INFO *key_part_info;
+ uint n_key_parts= head->actual_n_key_parts(key_info);
+
if (!keys_to_use.is_set(idx))
continue;
if (key_info->flags & HA_FULLTEXT)
@@ -3047,9 +3061,9 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
param.key[param.keys]=key_parts;
key_part_info= key_info->key_part;
- for (uint part=0 ; part < key_info->key_parts ;
- part++, key_parts++, key_part_info++)
- {
+ for (uint part= 0 ; part < n_key_parts ;
+ part++, key_parts++, key_part_info++)
+ {
key_parts->key= param.keys;
key_parts->part= part;
key_parts->length= key_part_info->length;
@@ -3105,7 +3119,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
Try to construct a QUICK_GROUP_MIN_MAX_SELECT.
Notice that it can be constructed no matter if there is a range tree.
*/
- group_trp= get_best_group_min_max(&param, tree);
+ group_trp= get_best_group_min_max(&param, tree, best_read_time);
if (group_trp)
{
param.table->quick_condition_rows= min(group_trp->records,
@@ -3375,6 +3389,8 @@ typedef struct st_part_prune_param
/* Same as above for subpartitioning */
my_bool *is_subpart_keypart;
+ my_bool ignore_part_fields; /* Ignore rest of partioning fields */
+
/***************************************************************
Following fields form find_used_partitions() recursion context:
**************************************************************/
@@ -3388,8 +3404,13 @@ typedef struct st_part_prune_param
/* Iterator to be used to obtain the "current" set of used partitions */
PARTITION_ITERATOR part_iter;
- /* Initialized bitmap of no_subparts size */
+ /* Initialized bitmap of num_subparts size */
MY_BITMAP subparts_bitmap;
+
+ uchar *cur_min_key;
+ uchar *cur_max_key;
+
+ uint cur_min_flag, cur_max_flag;
} PART_PRUNE_PARAM;
static bool create_partition_index_description(PART_PRUNE_PARAM *prune_par);
@@ -3507,6 +3528,11 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
prune_param.arg_stack_end= prune_param.arg_stack;
prune_param.cur_part_fields= 0;
prune_param.cur_subpart_fields= 0;
+
+ prune_param.cur_min_key= prune_param.range_param.min_key;
+ prune_param.cur_max_key= prune_param.range_param.max_key;
+ prune_param.cur_min_flag= prune_param.cur_max_flag= 0;
+
init_all_partitions_iterator(part_info, &prune_param.part_iter);
if (!tree->keys[0] || (-1 == (res= find_used_partitions(&prune_param,
tree->keys[0]))))
@@ -3644,8 +3670,8 @@ static void mark_full_partition_used_no_parts(partition_info* part_info,
static void mark_full_partition_used_with_parts(partition_info *part_info,
uint32 part_id)
{
- uint32 start= part_id * part_info->no_subparts;
- uint32 end= start + part_info->no_subparts;
+ uint32 start= part_id * part_info->num_subparts;
+ uint32 end= start + part_info->num_subparts;
DBUG_ENTER("mark_full_partition_used_with_parts");
for (; start != end; start++)
@@ -3743,6 +3769,11 @@ int find_used_partitions_imerge(PART_PRUNE_PARAM *ppar, SEL_IMERGE *imerge)
ppar->arg_stack_end= ppar->arg_stack;
ppar->cur_part_fields= 0;
ppar->cur_subpart_fields= 0;
+
+ ppar->cur_min_key= ppar->range_param.min_key;
+ ppar->cur_max_key= ppar->range_param.max_key;
+ ppar->cur_min_flag= ppar->cur_max_flag= 0;
+
init_all_partitions_iterator(ppar->part_info, &ppar->part_iter);
SEL_ARG *key_tree= (*ptree)->keys[0];
if (!key_tree || (-1 == (res |= find_used_partitions(ppar, key_tree))))
@@ -3866,9 +3897,14 @@ static
int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
{
int res, left_res=0, right_res=0;
- int partno= (int)key_tree->part;
- bool pushed= FALSE;
+ int key_tree_part= (int)key_tree->part;
bool set_full_part_if_bad_ret= FALSE;
+ bool ignore_part_fields= ppar->ignore_part_fields;
+ bool did_set_ignore_part_fields= FALSE;
+ RANGE_OPT_PARAM *range_par= &(ppar->range_param);
+
+ if (check_stack_overrun(range_par->thd, 3*STACK_MIN_SIZE, NULL))
+ return -1;
if (key_tree->left != &null_element)
{
@@ -3876,56 +3912,178 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
return -1;
}
+ /* Push SEL_ARG's to stack to enable looking backwards as well */
+ ppar->cur_part_fields+= ppar->is_part_keypart[key_tree_part];
+ ppar->cur_subpart_fields+= ppar->is_subpart_keypart[key_tree_part];
+ *(ppar->arg_stack_end++)= key_tree;
+
+ if (ignore_part_fields)
+ {
+ /*
+ We come here when a condition on the first partitioning
+ fields led to evaluating the partitioning condition
+ (due to finding a condition of the type a < const or
+ b > const). Thus we must ignore the rest of the
+ partitioning fields but we still want to analyse the
+ subpartitioning fields.
+ */
+ if (key_tree->next_key_part)
+ res= find_used_partitions(ppar, key_tree->next_key_part);
+ else
+ res= -1;
+ goto pop_and_go_right;
+ }
+
if (key_tree->type == SEL_ARG::KEY_RANGE)
{
- if (partno == 0 && (NULL != ppar->part_info->get_part_iter_for_interval))
- {
- /*
- Partitioning is done by RANGE|INTERVAL(monotonic_expr(fieldX)), and
- we got "const1 CMP fieldX CMP const2" interval <-- psergey-todo: change
+ if (ppar->part_info->get_part_iter_for_interval &&
+ key_tree->part <= ppar->last_part_partno)
+ {
+ /* Collect left and right bound, their lengths and flags */
+ uchar *min_key= ppar->cur_min_key;
+ uchar *max_key= ppar->cur_max_key;
+ uchar *tmp_min_key= min_key;
+ uchar *tmp_max_key= max_key;
+ key_tree->store_min(ppar->key[key_tree->part].store_length,
+ &tmp_min_key, ppar->cur_min_flag);
+ key_tree->store_max(ppar->key[key_tree->part].store_length,
+ &tmp_max_key, ppar->cur_max_flag);
+ uint flag;
+ if (key_tree->next_key_part &&
+ key_tree->next_key_part->part == key_tree->part+1 &&
+ key_tree->next_key_part->part <= ppar->last_part_partno &&
+ key_tree->next_key_part->type == SEL_ARG::KEY_RANGE)
+ {
+ /*
+ There are more key parts for partition pruning to handle
+ This mainly happens when the condition is an equality
+ condition.
+ */
+ if ((tmp_min_key - min_key) == (tmp_max_key - max_key) &&
+ (memcmp(min_key, max_key, (uint)(tmp_max_key - max_key)) == 0) &&
+ !key_tree->min_flag && !key_tree->max_flag)
+ {
+ /* Set 'parameters' */
+ ppar->cur_min_key= tmp_min_key;
+ ppar->cur_max_key= tmp_max_key;
+ uint save_min_flag= ppar->cur_min_flag;
+ uint save_max_flag= ppar->cur_max_flag;
+
+ ppar->cur_min_flag|= key_tree->min_flag;
+ ppar->cur_max_flag|= key_tree->max_flag;
+
+ res= find_used_partitions(ppar, key_tree->next_key_part);
+
+ /* Restore 'parameters' back */
+ ppar->cur_min_key= min_key;
+ ppar->cur_max_key= max_key;
+
+ ppar->cur_min_flag= save_min_flag;
+ ppar->cur_max_flag= save_max_flag;
+ goto pop_and_go_right;
+ }
+ /* We have arrived at the last field in the partition pruning */
+ uint tmp_min_flag= key_tree->min_flag,
+ tmp_max_flag= key_tree->max_flag;
+ if (!tmp_min_flag)
+ key_tree->next_key_part->store_min_key(ppar->key,
+ &tmp_min_key,
+ &tmp_min_flag,
+ ppar->last_part_partno);
+ if (!tmp_max_flag)
+ key_tree->next_key_part->store_max_key(ppar->key,
+ &tmp_max_key,
+ &tmp_max_flag,
+ ppar->last_part_partno);
+ flag= tmp_min_flag | tmp_max_flag;
+ }
+ else
+ flag= key_tree->min_flag | key_tree->max_flag;
+
+ if (tmp_min_key != range_par->min_key)
+ flag&= ~NO_MIN_RANGE;
+ else
+ flag|= NO_MIN_RANGE;
+ if (tmp_max_key != range_par->max_key)
+ flag&= ~NO_MAX_RANGE;
+ else
+ flag|= NO_MAX_RANGE;
+
+ /*
+ We need to call the interval mapper if we have a condition which
+ makes sense to prune on. In the example of COLUMNS on a and
+ b it makes sense if we have a condition on a, or conditions on
+ both a and b. If we only have conditions on b it might make sense
+ but this is a harder case we will solve later. For the harder case
+ this clause then turns into use of all partitions and thus we
+ simply set res= -1 as if the mapper had returned that.
+ TODO: What to do here is defined in WL#4065.
*/
- DBUG_EXECUTE("info", dbug_print_segment_range(key_tree,
- ppar->range_param.
- key_parts););
- res= ppar->part_info->
- get_part_iter_for_interval(ppar->part_info,
- FALSE,
- key_tree->min_value,
- key_tree->max_value,
- key_tree->min_flag | key_tree->max_flag,
- &ppar->part_iter);
- if (!res)
- goto go_right; /* res==0 --> no satisfying partitions */
+ if (ppar->arg_stack[0]->part == 0)
+ {
+ uint32 i;
+ uint32 store_length_array[MAX_KEY];
+ uint32 num_keys= ppar->part_fields;
+
+ for (i= 0; i < num_keys; i++)
+ store_length_array[i]= ppar->key[i].store_length;
+ res= ppar->part_info->
+ get_part_iter_for_interval(ppar->part_info,
+ FALSE,
+ store_length_array,
+ range_par->min_key,
+ range_par->max_key,
+ tmp_min_key - range_par->min_key,
+ tmp_max_key - range_par->max_key,
+ flag,
+ &ppar->part_iter);
+ if (!res)
+ goto pop_and_go_right; /* res==0 --> no satisfying partitions */
+ }
+ else
+ res= -1;
+
if (res == -1)
{
- //get a full range iterator
+ /* get a full range iterator */
init_all_partitions_iterator(ppar->part_info, &ppar->part_iter);
}
/*
Save our intent to mark full partition as used if we will not be able
to obtain further limits on subpartitions
*/
+ if (key_tree_part < ppar->last_part_partno)
+ {
+ /*
+ We need to ignore the rest of the partitioning fields in all
+ evaluations after this
+ */
+ did_set_ignore_part_fields= TRUE;
+ ppar->ignore_part_fields= TRUE;
+ }
set_full_part_if_bad_ret= TRUE;
goto process_next_key_part;
}
- if (partno == ppar->last_subpart_partno &&
+ if (key_tree_part == ppar->last_subpart_partno &&
(NULL != ppar->part_info->get_subpart_iter_for_interval))
{
PARTITION_ITERATOR subpart_iter;
DBUG_EXECUTE("info", dbug_print_segment_range(key_tree,
- ppar->range_param.
- key_parts););
+ range_par->key_parts););
res= ppar->part_info->
get_subpart_iter_for_interval(ppar->part_info,
TRUE,
+ NULL, /* Currently not used here */
key_tree->min_value,
key_tree->max_value,
- key_tree->min_flag | key_tree->max_flag,
+ 0, 0, /* Those are ignored here */
+ key_tree->min_flag |
+ key_tree->max_flag,
&subpart_iter);
DBUG_ASSERT(res); /* We can't get "no satisfying subpartitions" */
if (res == -1)
- return -1; /* all subpartitions satisfy */
+ goto pop_and_go_right; /* all subpartitions satisfy */
uint32 subpart_id;
bitmap_clear_all(&ppar->subparts_bitmap);
@@ -3938,23 +4096,19 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
while ((part_id= ppar->part_iter.get_next(&ppar->part_iter)) !=
NOT_A_PARTITION_ID)
{
- for (uint i= 0; i < ppar->part_info->no_subparts; i++)
+ for (uint i= 0; i < ppar->part_info->num_subparts; i++)
if (bitmap_is_set(&ppar->subparts_bitmap, i))
bitmap_set_bit(&ppar->part_info->used_partitions,
- part_id * ppar->part_info->no_subparts + i);
+ part_id * ppar->part_info->num_subparts + i);
}
- goto go_right;
+ goto pop_and_go_right;
}
if (key_tree->is_singlepoint())
{
- pushed= TRUE;
- ppar->cur_part_fields+= ppar->is_part_keypart[partno];
- ppar->cur_subpart_fields+= ppar->is_subpart_keypart[partno];
- *(ppar->arg_stack_end++) = key_tree;
-
- if (partno == ppar->last_part_partno &&
- ppar->cur_part_fields == ppar->part_fields)
+ if (key_tree_part == ppar->last_part_partno &&
+ ppar->cur_part_fields == ppar->part_fields &&
+ ppar->part_info->get_part_iter_for_interval == NULL)
{
/*
Ok, we've got "fieldN<=>constN"-type SEL_ARGs for all partitioning
@@ -3983,7 +4137,7 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
goto process_next_key_part;
}
- if (partno == ppar->last_subpart_partno &&
+ if (key_tree_part == ppar->last_subpart_partno &&
ppar->cur_subpart_fields == ppar->subpart_fields)
{
/*
@@ -4007,7 +4161,7 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
NOT_A_PARTITION_ID)
{
bitmap_set_bit(&part_info->used_partitions,
- part_id * part_info->no_subparts + subpart_id);
+ part_id * part_info->num_subparts + subpart_id);
}
res= 1; /* Some partitions were marked as used */
goto pop_and_go_right;
@@ -4020,8 +4174,18 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
we're processing subpartititoning's key parts, this means we'll not be
able to infer any suitable condition, so bail out.
*/
- if (partno >= ppar->last_part_partno)
- return -1;
+ if (key_tree_part >= ppar->last_part_partno)
+ {
+ res= -1;
+ goto pop_and_go_right;
+ }
+ /*
+ No meaning in continuing with rest of partitioning key parts.
+ Will try to continue with subpartitioning key parts.
+ */
+ ppar->ignore_part_fields= true;
+ did_set_ignore_part_fields= true;
+ goto process_next_key_part;
}
}
@@ -4030,7 +4194,17 @@ process_next_key_part:
res= find_used_partitions(ppar, key_tree->next_key_part);
else
res= -1;
-
+
+ if (did_set_ignore_part_fields)
+ {
+ /*
+ We have returned from processing all key trees linked to our next
+ key part. We are ready to be moving down (using right pointers) and
+ this tree is a new evaluation requiring its own decision on whether
+ to ignore partitioning fields.
+ */
+ ppar->ignore_part_fields= FALSE;
+ }
if (set_full_part_if_bad_ret)
{
if (res == -1)
@@ -4053,18 +4227,14 @@ process_next_key_part:
init_all_partitions_iterator(ppar->part_info, &ppar->part_iter);
}
- if (pushed)
- {
pop_and_go_right:
- /* Pop this key part info off the "stack" */
- ppar->arg_stack_end--;
- ppar->cur_part_fields-= ppar->is_part_keypart[partno];
- ppar->cur_subpart_fields-= ppar->is_subpart_keypart[partno];
- }
+ /* Pop this key part info off the "stack" */
+ ppar->arg_stack_end--;
+ ppar->cur_part_fields-= ppar->is_part_keypart[key_tree_part];
+ ppar->cur_subpart_fields-= ppar->is_subpart_keypart[key_tree_part];
if (res == -1)
return -1;
-go_right:
if (key_tree->right != &null_element)
{
if (-1 == (right_res= find_used_partitions(ppar,key_tree->right)))
@@ -4146,13 +4316,14 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar)
uint used_part_fields, used_subpart_fields;
used_part_fields= fields_ok_for_partition_index(part_info->part_field_array) ?
- part_info->no_part_fields : 0;
+ part_info->num_part_fields : 0;
used_subpart_fields=
fields_ok_for_partition_index(part_info->subpart_field_array)?
- part_info->no_subpart_fields : 0;
+ part_info->num_subpart_fields : 0;
uint total_parts= used_part_fields + used_subpart_fields;
+ ppar->ignore_part_fields= FALSE;
ppar->part_fields= used_part_fields;
ppar->last_part_partno= (int)used_part_fields - 1;
@@ -4187,10 +4358,10 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar)
if (ppar->subpart_fields)
{
my_bitmap_map *buf;
- uint32 bufsize= bitmap_buffer_size(ppar->part_info->no_subparts);
+ uint32 bufsize= bitmap_buffer_size(ppar->part_info->num_subparts);
if (!(buf= (my_bitmap_map*) alloc_root(alloc, bufsize)))
return TRUE;
- bitmap_init(&ppar->subparts_bitmap, buf, ppar->part_info->no_subparts,
+ bitmap_init(&ppar->subparts_bitmap, buf, ppar->part_info->num_subparts,
FALSE);
}
range_par->key_parts= key_part;
@@ -4201,12 +4372,8 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar)
{
key_part->key= 0;
key_part->part= part;
- key_part->store_length= key_part->length= (uint16) (*field)->key_length();
- if ((*field)->real_maybe_null())
- key_part->store_length+= HA_KEY_NULL_LENGTH;
- if ((*field)->type() == MYSQL_TYPE_BLOB ||
- (*field)->real_type() == MYSQL_TYPE_VARCHAR)
- key_part->store_length+= HA_KEY_BLOB_LENGTH;
+ key_part->length= (uint16)(*field)->key_length();
+ key_part->store_length= (uint16)get_partition_field_store_length(*field);
DBUG_PRINT("info", ("part %u length %u store_length %u", part,
key_part->length, key_part->store_length));
@@ -4806,8 +4973,10 @@ TABLE_READ_PLAN *merge_same_index_scans(PARAM *param, SEL_IMERGE *imerge,
bzero((*changed_tree)->keys,
sizeof((*changed_tree)->keys[0])*param->keys);
(*changed_tree)->keys_map.clear_all();
- key->incr_refs();
- (*tree)->keys[key_idx]->incr_refs();
+ if (key)
+ key->incr_refs();
+ if ((*tree)->keys[key_idx])
+ (*tree)->keys[key_idx]->incr_refs();
if (((*changed_tree)->keys[key_idx]=
key_or(param, key, (*tree)->keys[key_idx])))
(*changed_tree)->keys_map.set_bit(key_idx);
@@ -7014,6 +7183,34 @@ static SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, Item_func *cond_func,
{
new_interval->min_value= last_val->max_value;
new_interval->min_flag= NEAR_MIN;
+
+ /*
+ If the interval is over a partial keypart, the
+ interval must be "c_{i-1} <= X < c_i" instead of
+ "c_{i-1} < X < c_i". Reason:
+
+ Consider a table with a column "my_col VARCHAR(3)",
+ and an index with definition
+ "INDEX my_idx my_col(1)". If the table contains rows
+ with my_col values "f" and "foo", the index will not
+ distinguish the two rows.
+
+ Note that tree_or() below will effectively merge
+ this range with the range created for c_{i-1} and
+ we'll eventually end up with only one range:
+ "NULL < X".
+
+ Partitioning indexes are never partial.
+ */
+ if (param->using_real_indexes)
+ {
+ const KEY key=
+ param->table->key_info[param->real_keynr[idx]];
+ const KEY_PART_INFO *kpi= key.key_part + new_interval->part;
+
+ if (kpi->key_part_flag & HA_PART_KEY_SEG)
+ new_interval->min_flag= 0;
+ }
}
}
/*
@@ -7172,6 +7369,14 @@ static SEL_TREE *get_full_func_mm_tree(RANGE_OPT_PARAM *param,
param->current_table);
DBUG_ENTER("get_full_func_mm_tree");
+#ifdef HAVE_SPATIAL
+ if (field_item->field->type() == MYSQL_TYPE_GEOMETRY)
+ {
+ /* We have to be able to store all sorts of spatial features here */
+ ((Field_geom*) field_item->field)->geom_type= Field::GEOM_GEOMETRY;
+ }
+#endif /*HAVE_SPATIAL*/
+
for (uint i= 0; i < cond_func->arg_count; i++)
{
Item *arg= cond_func->arguments()[i]->real_item();
@@ -7218,34 +7423,35 @@ static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param,COND *cond)
if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
{
- tree=0;
+ tree= NULL;
Item *item;
while ((item=li++))
{
- SEL_TREE *new_tree=get_mm_tree(param,item);
- if (param->thd->is_fatal_error ||
- param->alloced_sel_args > SEL_ARG::MAX_SEL_ARGS)
- DBUG_RETURN(0); // out of memory
- tree=tree_and(param,tree,new_tree);
- if (tree && tree->type == SEL_TREE::IMPOSSIBLE)
- break;
+ SEL_TREE *new_tree= get_mm_tree(param,item);
+ if (param->statement_should_be_aborted())
+ DBUG_RETURN(NULL);
+ tree= tree_and(param,tree,new_tree);
+ if (tree && tree->type == SEL_TREE::IMPOSSIBLE)
+ break;
}
}
else
- { // COND OR
- tree=get_mm_tree(param,li++);
+ { // COND OR
+ tree= get_mm_tree(param,li++);
+ if (param->statement_should_be_aborted())
+ DBUG_RETURN(NULL);
if (tree)
{
- Item *item;
- while ((item=li++))
- {
- SEL_TREE *new_tree=get_mm_tree(param,item);
- if (!new_tree)
- DBUG_RETURN(0); // out of memory
- tree=tree_or(param,tree,new_tree);
- if (!tree || tree->type == SEL_TREE::ALWAYS)
- break;
- }
+ Item *item;
+ while ((item=li++))
+ {
+ SEL_TREE *new_tree=get_mm_tree(param,item);
+ if (new_tree == NULL || param->statement_should_be_aborted())
+ DBUG_RETURN(NULL);
+ tree= tree_or(param,tree,new_tree);
+ if (tree == NULL || tree->type == SEL_TREE::ALWAYS)
+ break;
+ }
}
}
DBUG_RETURN(tree);
@@ -7489,14 +7695,17 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field,
*/
if (field->result_type() == STRING_RESULT &&
+ ((Field_str*) field)->match_collation_to_optimize_range() &&
value->result_type() == STRING_RESULT &&
key_part->image_type == Field::itRAW &&
((Field_str*)field)->charset() != conf_func->compare_collation() &&
- !(conf_func->compare_collation()->state & MY_CS_BINSORT))
+ !(conf_func->compare_collation()->state & MY_CS_BINSORT &&
+ (type == Item_func::EQUAL_FUNC || type == Item_func::EQ_FUNC)))
goto end;
if (key_part->image_type == Field::itMBR)
{
+ // @todo: use is_spatial_operator() instead?
switch (type) {
case Item_func::SP_EQUALS_FUNC:
case Item_func::SP_DISJOINT_FUNC:
@@ -7614,6 +7823,17 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field,
if (field->cmp_type() == STRING_RESULT && value->cmp_type() != STRING_RESULT)
goto end;
err= value->save_in_field_no_warnings(field, 1);
+ if (err == 2 && field->cmp_type() == STRING_RESULT)
+ {
+ if (type == Item_func::EQ_FUNC)
+ {
+ tree= new (alloc) SEL_ARG(field, 0, 0);
+ tree->type= SEL_ARG::IMPOSSIBLE;
+ }
+ else
+ tree= NULL; /* Cannot infer anything */
+ goto end;
+ }
if (err > 0)
{
if (field->cmp_type() != value->result_type())
@@ -9826,8 +10046,8 @@ void SEL_ARG::test_use_count(SEL_ARG *root)
sql_print_warning("Wrong use count: %u (should be %u) for tree at 0x%lx",
e_count, elements, (long unsigned int) this);
}
-
#endif
+
/*
Calculate cost and E(#rows) for a given index and intervals tree
@@ -10082,13 +10302,14 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree, uint mrr_flags,
}
else
{
+ KEY *keyinfo= param->table->key_info+param->real_keynr[idx];
quick->mrr_flags= mrr_flags;
quick->mrr_buf_size= mrr_buf_size;
quick->key_parts=(KEY_PART*)
memdup_root(parent_alloc? parent_alloc : &quick->alloc,
(char*) param->key[idx],
sizeof(KEY_PART)*
- param->table->key_info[param->real_keynr[idx]].key_parts);
+ param->table->actual_n_key_parts(keyinfo));
}
}
DBUG_RETURN(quick);
@@ -10137,11 +10358,15 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
{
uint tmp_min_flag=key_tree->min_flag,tmp_max_flag=key_tree->max_flag;
if (!tmp_min_flag)
- min_part+= key_tree->next_key_part->store_min_key(key, &tmp_min_key,
- &tmp_min_flag);
+ min_part+= key_tree->next_key_part->store_min_key(key,
+ &tmp_min_key,
+ &tmp_min_flag,
+ MAX_KEY);
if (!tmp_max_flag)
- max_part+= key_tree->next_key_part->store_max_key(key, &tmp_max_key,
- &tmp_max_flag);
+ max_part+= key_tree->next_key_part->store_max_key(key,
+ &tmp_max_key,
+ &tmp_max_flag,
+ MAX_KEY);
flag=tmp_min_flag | tmp_max_flag;
}
}
@@ -10175,15 +10400,15 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
KEY *table_key=quick->head->key_info+quick->index;
flag=EQ_RANGE;
if ((table_key->flags & HA_NOSAME) &&
- key->part == table_key->key_parts-1)
+ key_tree->part == table_key->key_parts-1)
{
- if (!(table_key->flags & HA_NULL_PART_KEY) ||
- !null_part_in_key(key,
- param->min_key,
- (uint) (tmp_min_key - param->min_key)))
- flag|= UNIQUE_RANGE;
- else
- flag|= NULL_RANGE;
+ if ((table_key->flags & HA_NULL_PART_KEY) &&
+ null_part_in_key(key,
+ param->min_key,
+ (uint) (tmp_min_key - param->min_key)))
+ flag|= NULL_RANGE;
+ else
+ flag|= UNIQUE_RANGE;
}
}
}
@@ -10213,7 +10438,7 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
}
/*
- Return 1 if there is only one range and this uses the whole primary key
+ Return 1 if there is only one range and this uses the whole unique key
*/
bool QUICK_RANGE_SELECT::unique_key_range()
@@ -10224,8 +10449,7 @@ bool QUICK_RANGE_SELECT::unique_key_range()
if ((tmp->flag & (EQ_RANGE | NULL_RANGE)) == EQ_RANGE)
{
KEY *key=head->key_info+index;
- return ((key->flags & HA_NOSAME) &&
- key->key_length == tmp->min_length);
+ return (key->flags & HA_NOSAME) && key->key_length == tmp->min_length;
}
}
return 0;
@@ -10345,6 +10569,7 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
uint part;
bool create_err= FALSE;
COST_VECT cost;
+ uint max_used_key_len;
old_root= thd->mem_root;
/* The following call may change thd->mem_root */
@@ -10371,12 +10596,13 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
range->min_length= range->max_length= ref->key_length;
range->min_keypart_map= range->max_keypart_map=
make_prev_keypart_map(ref->key_parts);
- range->flag= (ref->key_length == key_info->key_length ? EQ_RANGE : 0);
+ range->flag= EQ_RANGE;
if (!(quick->key_parts=key_part=(KEY_PART *)
alloc_root(&quick->alloc,sizeof(KEY_PART)*ref->key_parts)))
goto err;
-
+
+ max_used_key_len=0;
for (part=0 ; part < ref->key_parts ;part++,key_part++)
{
key_part->part=part;
@@ -10385,7 +10611,12 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
key_part->store_length= key_info->key_part[part].store_length;
key_part->null_bit= key_info->key_part[part].null_bit;
key_part->flag= (uint8) key_info->key_part[part].key_part_flag;
+
+ max_used_key_len +=key_info->key_part[part].store_length;
}
+
+ quick->max_used_key_length= max_used_key_len;
+
if (insert_dynamic(&quick->ranges,(uchar*)&range))
goto err;
@@ -10416,11 +10647,6 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
(table->key_read ? HA_MRR_INDEX_ONLY : 0);
if (thd->lex->sql_command != SQLCOM_SELECT)
quick->mrr_flags |= HA_MRR_USE_DEFAULT_IMPL;
-#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
- if (!ref->null_ref_key && !key_has_nulls(key_info, range->min_key,
- ref->key_length))
- quick->mrr_flags |= HA_MRR_NO_NULL_ENDPOINTS;
-#endif
quick->mrr_buf_size= thd->variables.mrr_buff_size;
if (table->file->multi_range_read_info(quick->index, 1, (uint)records,
@@ -10468,12 +10694,13 @@ int read_keys_and_merge_scans(THD *thd,
Unique *unique= *unique_ptr;
handler *file= head->file;
bool with_cpk_filter= pk_quick_select != NULL;
-
+ bool enabled_keyread= 0;
DBUG_ENTER("read_keys_and_merge");
/* We're going to just read rowids. */
if (!head->key_read)
{
+ enabled_keyread= 1;
head->enable_keyread();
}
head->prepare_for_position();
@@ -10492,7 +10719,7 @@ int read_keys_and_merge_scans(THD *thd,
if (unique == NULL)
{
- DBUG_EXECUTE_IF("index_merge_may_not_create_a_Unique", abort(); );
+ DBUG_EXECUTE_IF("index_merge_may_not_create_a_Unique", DBUG_ABORT(); );
DBUG_EXECUTE_IF("only_one_Unique_may_be_created",
DBUG_SET("+d,index_merge_may_not_create_a_Unique"); );
@@ -10505,7 +10732,10 @@ int read_keys_and_merge_scans(THD *thd,
*unique_ptr= unique;
}
else
+ {
unique->reset();
+ filesort_free_buffers(head, false);
+ }
DBUG_ASSERT(file->ref_length == unique->get_size());
DBUG_ASSERT(thd->variables.sortbuff_size == unique->get_max_in_memory_size());
@@ -10564,13 +10794,15 @@ int read_keys_and_merge_scans(THD *thd,
/*
index merge currently doesn't support "using index" at all
*/
- head->disable_keyread();
+ if (enabled_keyread)
+ head->disable_keyread();
if (init_read_record(read_record, thd, head, (SQL_SELECT*) 0, 1 , 1, TRUE))
result= 1;
DBUG_RETURN(result);
err:
- head->disable_keyread();
+ if (enabled_keyread)
+ head->disable_keyread();
DBUG_RETURN(1);
}
@@ -10664,6 +10896,13 @@ int QUICK_INDEX_INTERSECT_SELECT::get_next()
If a Clustered PK scan is present, it is used only to check if row
satisfies its condition (and never used for row retrieval).
+ Locking: to ensure that exclusive locks are only set on records that
+ are included in the final result we must release the lock
+ on all rows we read but do not include in the final result. This
+ must be done on each index that reads the record and the lock
+ must be released using the same handler (the same quick object) as
+ used when reading the record.
+
RETURN
0 - Ok
other - Error code if any error occurred.
@@ -10674,6 +10913,12 @@ int QUICK_ROR_INTERSECT_SELECT::get_next()
List_iterator_fast<QUICK_SELECT_WITH_RECORD> quick_it(quick_selects);
QUICK_SELECT_WITH_RECORD *qr;
QUICK_RANGE_SELECT* quick;
+
+ /* quick that reads the given rowid first. This is needed in order
+ to be able to unlock the row using the same handler object that locked
+ it */
+ QUICK_RANGE_SELECT* quick_with_last_rowid;
+
int error, cmp;
uint last_rowid_count=0;
DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::get_next");
@@ -10687,7 +10932,10 @@ int QUICK_ROR_INTERSECT_SELECT::get_next()
if (cpk_quick)
{
while (!error && !cpk_quick->row_in_ranges())
+ {
+ quick->file->unlock_row(); /* row not in range; unlock */
error= quick->get_next();
+ }
}
if (error)
DBUG_RETURN(error);
@@ -10699,6 +10947,7 @@ int QUICK_ROR_INTERSECT_SELECT::get_next()
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)
{
@@ -10711,10 +10960,22 @@ int QUICK_ROR_INTERSECT_SELECT::get_next()
do
{
+ 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 (!current_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);
key_copy(qr->key_tuple, record, head->key_info + quick->index,
@@ -10728,13 +10989,21 @@ int QUICK_ROR_INTERSECT_SELECT::get_next()
{
while (!cpk_quick->row_in_ranges())
{
+ 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 (!current_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,
@@ -10842,16 +11111,32 @@ int QUICK_RANGE_SELECT::reset()
uchar *mrange_buff;
int error;
HANDLER_BUFFER empty_buf;
+ MY_BITMAP * const save_read_set= head->read_set;
+ MY_BITMAP * const save_write_set= head->write_set;
DBUG_ENTER("QUICK_RANGE_SELECT::reset");
last_range= NULL;
cur_range= (QUICK_RANGE**) ranges.buffer;
+ RANGE_SEQ_IF seq_funcs= {NULL, quick_range_seq_init, quick_range_seq_next, 0, 0};
+
+ if (in_ror_merged_scan)
+ head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap);
+
+ if (file->inited == handler::RND)
+ {
+ /* Handler could be left in this state by MRR */
+ if ((error= file->ha_rnd_end()))
+ DBUG_RETURN(error);
+ }
if (file->inited == handler::NONE)
{
- if (in_ror_merged_scan)
- head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap);
+ DBUG_EXECUTE_IF("bug14365043_2",
+ DBUG_SET("+d,ha_index_init_fail"););
if ((error= file->ha_index_init(index,1)))
- DBUG_RETURN(error);
+ {
+ file->print_error(error, MYF(0));
+ goto err;
+ }
}
/* Allocate buffer if we need one but haven't allocated it yet */
@@ -10873,7 +11158,7 @@ int QUICK_RANGE_SELECT::reset()
mrr_buf_desc->buffer= mrange_buff;
mrr_buf_desc->buffer_end= mrange_buff + buf_size;
mrr_buf_desc->end_of_used_area= mrange_buff;
-#ifdef HAVE_purify
+#ifdef HAVE_valgrind
/*
We need this until ndb will use the buffer efficiently
(Now ndb stores complete row in here, instead of only the used fields
@@ -10886,10 +11171,14 @@ int QUICK_RANGE_SELECT::reset()
if (!mrr_buf_desc)
empty_buf.buffer= empty_buf.buffer_end= empty_buf.end_of_used_area= NULL;
- RANGE_SEQ_IF seq_funcs= {NULL, quick_range_seq_init, quick_range_seq_next, 0, 0};
error= file->multi_range_read_init(&seq_funcs, (void*)this, ranges.elements,
mrr_flags, mrr_buf_desc? mrr_buf_desc:
&empty_buf);
+err:
+ /* Restore bitmaps set on entry */
+ if (in_ror_merged_scan)
+ head->column_bitmaps_set_no_signal(save_read_set, save_write_set);
+
DBUG_RETURN(error);
}
@@ -10912,6 +11201,9 @@ int QUICK_RANGE_SELECT::reset()
int QUICK_RANGE_SELECT::get_next()
{
range_id_t dummy;
+ MY_BITMAP * const save_read_set= head->read_set;
+ MY_BITMAP * const save_write_set= head->write_set;
+
DBUG_ENTER("QUICK_RANGE_SELECT::get_next");
if (in_ror_merged_scan)
{
@@ -11100,8 +11392,7 @@ bool QUICK_RANGE_SELECT::row_in_ranges()
*/
QUICK_SELECT_DESC::QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q,
- uint used_key_parts_arg,
- bool *create_err)
+ uint used_key_parts_arg)
:QUICK_RANGE_SELECT(*q), rev_it(rev_ranges),
used_key_parts (used_key_parts_arg)
{
@@ -11128,7 +11419,6 @@ QUICK_SELECT_DESC::QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q,
}
rev_it.rewind();
q->dont_free=1; // Don't free shared mem
- delete q;
}
@@ -11169,6 +11459,26 @@ int QUICK_SELECT_DESC::get_next()
if (!(last_range= rev_it++))
DBUG_RETURN(HA_ERR_END_OF_FILE); // All ranges used
+ key_range start_key;
+ start_key.key= (const uchar*) last_range->min_key;
+ start_key.length= last_range->min_length;
+ start_key.flag= ((last_range->flag & NEAR_MIN) ? HA_READ_AFTER_KEY :
+ (last_range->flag & EQ_RANGE) ?
+ HA_READ_KEY_EXACT : HA_READ_KEY_OR_NEXT);
+ start_key.keypart_map= last_range->min_keypart_map;
+ key_range end_key;
+ end_key.key= (const uchar*) last_range->max_key;
+ end_key.length= last_range->max_length;
+ end_key.flag= (last_range->flag & NEAR_MAX ? HA_READ_BEFORE_KEY :
+ HA_READ_AFTER_KEY);
+ end_key.keypart_map= last_range->max_keypart_map;
+ result= file->prepare_range_scan((last_range->flag & NO_MIN_RANGE) ? NULL : &start_key,
+ (last_range->flag & NO_MAX_RANGE) ? NULL : &end_key);
+ if (result)
+ {
+ DBUG_RETURN(result);
+ }
+
if (last_range->flag & NO_MAX_RANGE) // Read last record
{
int local_error;
@@ -11218,6 +11528,27 @@ int QUICK_SELECT_DESC::get_next()
}
+/**
+ Create a compatible quick select with the result ordered in an opposite way
+
+ @param used_key_parts_arg Number of used key parts
+
+ @retval NULL in case of errors (OOM etc)
+ @retval pointer to a newly created QUICK_SELECT_DESC if success
+*/
+
+QUICK_SELECT_I *QUICK_RANGE_SELECT::make_reverse(uint used_key_parts_arg)
+{
+ QUICK_SELECT_DESC *new_quick= new QUICK_SELECT_DESC(this, used_key_parts_arg);
+ if (new_quick == NULL)
+ {
+ delete new_quick;
+ return NULL;
+ }
+ return new_quick;
+}
+
+
/*
Compare if found key is over max-value
Returns 0 if key <= range->max_key
@@ -11480,6 +11811,66 @@ void QUICK_ROR_UNION_SELECT::add_keys_and_lengths(String *key_names,
}
+void QUICK_RANGE_SELECT::add_used_key_part_to_set(MY_BITMAP *col_set)
+{
+ uint key_len;
+ KEY_PART *part= key_parts;
+ for (key_len=0; key_len < max_used_key_length;
+ key_len += (part++)->store_length)
+ {
+ bitmap_set_bit(col_set, part->field->field_index);
+ }
+}
+
+
+void QUICK_GROUP_MIN_MAX_SELECT::add_used_key_part_to_set(MY_BITMAP *col_set)
+{
+ uint key_len;
+ KEY_PART_INFO *part= index_info->key_part;
+ for (key_len=0; key_len < max_used_key_length;
+ key_len += (part++)->store_length)
+ {
+ bitmap_set_bit(col_set, part->field->field_index);
+ }
+}
+
+
+void QUICK_ROR_INTERSECT_SELECT::add_used_key_part_to_set(MY_BITMAP *col_set)
+{
+ List_iterator_fast<QUICK_SELECT_WITH_RECORD> it(quick_selects);
+ QUICK_SELECT_WITH_RECORD *quick;
+ while ((quick= it++))
+ {
+ quick->quick->add_used_key_part_to_set(col_set);
+ }
+}
+
+
+void QUICK_INDEX_SORT_SELECT::add_used_key_part_to_set(MY_BITMAP *col_set)
+{
+ QUICK_RANGE_SELECT *quick;
+ List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
+ while ((quick= it++))
+ {
+ quick->add_used_key_part_to_set(col_set);
+ }
+ if (pk_quick_select)
+ pk_quick_select->add_used_key_part_to_set(col_set);
+}
+
+
+void QUICK_ROR_UNION_SELECT::add_used_key_part_to_set(MY_BITMAP *col_set)
+{
+ QUICK_SELECT_I *quick;
+ List_iterator_fast<QUICK_SELECT_I> it(quick_selects);
+
+ while ((quick= it++))
+ {
+ quick->add_used_key_part_to_set(col_set);
+ }
+}
+
+
/*******************************************************************************
* Implementation of QUICK_GROUP_MIN_MAX_SELECT
*******************************************************************************/
@@ -11487,6 +11878,8 @@ void QUICK_ROR_UNION_SELECT::add_keys_and_lengths(String *key_names,
static inline uint get_field_keypart(KEY *index, Field *field);
static inline SEL_ARG * get_index_range_tree(uint index, SEL_TREE* range_tree,
PARAM *param, uint *param_idx);
+static bool get_sel_arg_for_keypart(Field *field, SEL_ARG *index_range_tree,
+ SEL_ARG **cur_range);
static bool get_constant_key_infix(KEY *index_info, SEL_ARG *index_range_tree,
KEY_PART_INFO *first_non_group_part,
KEY_PART_INFO *min_max_arg_part,
@@ -11506,15 +11899,10 @@ cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
double *read_cost, ha_rows *records);
-/*
+/**
Test if this access method is applicable to a GROUP query with MIN/MAX
functions, and if so, construct a new TRP object.
- SYNOPSIS
- get_best_group_min_max()
- param Parameter from test_quick_select
- sel_tree Range tree generated by get_mm_tree
-
DESCRIPTION
Test whether a query can be computed via a QUICK_GROUP_MIN_MAX_SELECT.
Queries computable via a QUICK_GROUP_MIN_MAX_SELECT must satisfy the
@@ -11552,6 +11940,21 @@ cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
except MIN and MAX. For queries with DISTINCT, aggregate functions
are allowed.
SA5. The select list in DISTINCT queries should not contain expressions.
+ SA6. Clustered index can not be used by GROUP_MIN_MAX quick select
+ for AGG_FUNC(DISTINCT ...) optimization because cursor position is
+ never stored after a unique key lookup in the clustered index and
+ furhter index_next/prev calls can not be used. So loose index scan
+ optimization can not be used in this case.
+ SA7. If Q has both AGG_FUNC(DISTINCT ...) and MIN/MAX() functions then this
+ access method is not used.
+ For above queries MIN/MAX() aggregation has to be done at
+ nested_loops_join (end_send_group). But with current design MIN/MAX()
+ is always set as part of loose index scan. Because of this mismatch
+ MIN() and MAX() values will be set incorrectly. For such queries to
+ work we need a new interface for loose index scan. This new interface
+ should only fetch records with min and max values and let
+ end_send_group to do aggregation. Until then do not use
+ loose_index_scan.
GA1. If Q has a GROUP BY clause, then GA is a prefix of I. That is, if
G_i = A_j => i = j.
GA2. If Q has a DISTINCT clause, then there is a permutation of SA that
@@ -11583,6 +11986,8 @@ cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
above tests. By transitivity then it also follows that each WA_i
participates in the index I (if this was already tested for GA, NGA
and C).
+ WA2. If there is a predicate on C, then it must be in conjunction
+ to all predicates on all earlier keyparts in I.
C) Overall query form:
SELECT EXPR([A_1,...,A_k], [B_1,...,B_m], [MIN(C)], [MAX(C)])
@@ -11627,17 +12032,16 @@ cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
- Lift the limitation in condition (B3), that is, make this access method
applicable to ROLLUP queries.
- RETURN
- If mem_root != NULL
- - valid TRP_GROUP_MIN_MAX object if this QUICK class can be used for
- the query
- - NULL o/w.
- If mem_root == NULL
- - NULL
+ @param param Parameter from test_quick_select
+ @param sel_tree Range tree generated by get_mm_tree
+ @param read_time Best read time so far (=table/index scan time)
+ @return table read plan
+ @retval NULL Loose index scan not applicable or mem_root == NULL
+ @retval !NULL Loose index scan table read plan
*/
static TRP_GROUP_MIN_MAX *
-get_best_group_min_max(PARAM *param, SEL_TREE *tree)
+get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
{
THD *thd= param->thd;
JOIN *join= thd->lex->current_select->join;
@@ -11658,14 +12062,15 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
ORDER *tmp_group;
Item *item;
Item_field *item_field;
+ bool is_agg_distinct;
+ List<Item_field> agg_distinct_flds;
+
DBUG_ENTER("get_best_group_min_max");
/* Perform few 'cheap' tests whether this access method is applicable. */
if (!join)
DBUG_RETURN(NULL); /* This is not a select statement. */
if ((join->table_count != 1) || /* The query must reference one table. */
- ((!join->group_list) && /* Neither GROUP BY nor a DISTINCT query. */
- (!join->select_distinct)) ||
(join->select_lex->olap == ROLLUP_TYPE)) /* Check (B3) for ROLLUP */
DBUG_RETURN(NULL);
if (table->s->keys == 0) /* There are no indexes to use. */
@@ -11673,12 +12078,19 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
if (join->conds && join->conds->used_tables() & OUTER_REF_TABLE_BIT)
DBUG_RETURN(NULL); /* Cannot execute with correlated conditions. */
- /* Analyze the query in more detail. */
- List_iterator<Item> select_items_it(join->fields_list);
-
/* Check (SA1,SA4) and store the only MIN/MAX argument - the C attribute.*/
if (join->make_sum_func_list(join->all_fields, join->fields_list, 1))
DBUG_RETURN(NULL);
+
+ List_iterator<Item> select_items_it(join->fields_list);
+ is_agg_distinct = is_indexed_agg_distinct(join, &agg_distinct_flds);
+
+ if ((!join->group_list) && /* Neither GROUP BY nor a DISTINCT query. */
+ (!join->select_distinct) &&
+ !is_agg_distinct)
+ DBUG_RETURN(NULL);
+ /* Analyze the query in more detail. */
+
if (join->sum_funcs[0])
{
Item_sum *min_max_item;
@@ -11689,6 +12101,11 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
have_min= TRUE;
else if (min_max_item->sum_func() == Item_sum::MAX_FUNC)
have_max= TRUE;
+ else if (is_agg_distinct &&
+ (min_max_item->sum_func() == Item_sum::COUNT_DISTINCT_FUNC ||
+ min_max_item->sum_func() == Item_sum::SUM_DISTINCT_FUNC ||
+ min_max_item->sum_func() == Item_sum::AVG_DISTINCT_FUNC))
+ continue;
else
DBUG_RETURN(NULL);
@@ -11706,12 +12123,18 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
}
}
+ /* Check (SA7). */
+ if (is_agg_distinct && (have_max || have_min))
+ {
+ DBUG_RETURN(NULL);
+ }
+
/* Check (SA5). */
if (join->select_distinct)
{
while ((item= select_items_it++))
{
- if (item->type() != Item::FIELD_ITEM)
+ if (item->real_item()->type() != Item::FIELD_ITEM)
DBUG_RETURN(NULL);
}
}
@@ -11719,7 +12142,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
/* Check (GA4) - that there are no expressions among the group attributes. */
for (tmp_group= join->group_list; tmp_group; tmp_group= tmp_group->next)
{
- if ((*tmp_group->item)->type() != Item::FIELD_ITEM)
+ if ((*tmp_group->item)->real_item()->type() != Item::FIELD_ITEM)
DBUG_RETURN(NULL);
}
@@ -11738,6 +12161,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
uint best_param_idx= 0;
const uint pk= param->table->s->primary_key;
+ uint max_key_part;
SEL_ARG *cur_index_tree= NULL;
ha_rows cur_quick_prefix_records= 0;
uint cur_param_idx=MAX_KEY;
@@ -11751,6 +12175,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
KEY_PART_INFO *last_part;
KEY_PART_INFO *first_non_group_part;
KEY_PART_INFO *first_non_infix_part;
+ uint key_parts;
uint key_infix_parts;
uint cur_group_key_parts= 0;
uint cur_group_prefix_len= 0;
@@ -11766,6 +12191,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
goto next_index;
/*
+ Unless extended keys can be used for cur_index:
If the current storage manager is such that it appends the primary key to
each index, then the above condition is insufficient to check if the
index is covering. In such cases it may happen that some fields are
@@ -11774,7 +12200,8 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
does not qualify as covering in our case. If this is the case, below
we check that all query fields are indeed covered by 'cur_index'.
*/
- if (pk < MAX_KEY && cur_index != pk &&
+ if (cur_index_info->key_parts == table->actual_n_key_parts(cur_index_info)
+ && pk < MAX_KEY && cur_index != pk &&
(table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX))
{
/* For each table field */
@@ -11791,13 +12218,16 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
}
}
+ max_key_part= 0;
+ used_key_parts_map.clear_all();
+
/*
Check (GA1) for GROUP BY queries.
*/
if (join->group_list)
{
cur_part= cur_index_info->key_part;
- end_part= cur_part + cur_index_info->key_parts;
+ end_part= cur_part + table->actual_n_key_parts(cur_index_info);
/* Iterate in parallel over the GROUP list and the index parts. */
for (tmp_group= join->group_list; tmp_group && (cur_part != end_part);
tmp_group= tmp_group->next, cur_part++)
@@ -11808,12 +12238,14 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
first Item? If so, then why? What is the array for?
*/
/* Above we already checked that all group items are fields. */
- DBUG_ASSERT((*tmp_group->item)->type() == Item::FIELD_ITEM);
- Item_field *group_field= (Item_field *) (*tmp_group->item);
+ DBUG_ASSERT((*tmp_group->item)->real_item()->type() == Item::FIELD_ITEM);
+ Item_field *group_field= (Item_field *) (*tmp_group->item)->real_item();
if (group_field->field->eq(cur_part->field))
{
cur_group_prefix_len+= cur_part->store_length;
++cur_group_key_parts;
+ max_key_part= cur_part - cur_index_info->key_part + 1;
+ used_key_parts_map.set_bit(max_key_part);
}
else
goto next_index;
@@ -11834,14 +12266,26 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
Later group_fields_array of ORDER objects is used to convert the query
to a GROUP query.
*/
- else if (join->select_distinct)
+ if ((!join->group_list && join->select_distinct) ||
+ is_agg_distinct)
{
- select_items_it.rewind();
- used_key_parts_map.clear_all();
- uint max_key_part= 0;
- while ((item= select_items_it++))
+ if (!is_agg_distinct)
{
- item_field= (Item_field*) item; /* (SA5) already checked above. */
+ select_items_it.rewind();
+ }
+
+ List_iterator<Item_field> agg_distinct_flds_it (agg_distinct_flds);
+ while (NULL != (item = (is_agg_distinct ?
+ (Item *) agg_distinct_flds_it++ : select_items_it++)))
+ {
+ /* (SA5) already checked above. */
+ item_field= (Item_field*) item->real_item();
+ DBUG_ASSERT(item->real_item()->type() == Item::FIELD_ITEM);
+
+ /* not doing loose index scan for derived tables */
+ if (!item_field->field)
+ goto next_index;
+
/* Find the order of the key part in the index. */
key_part_nr= get_field_keypart(cur_index_info, item_field->field);
/*
@@ -11850,7 +12294,8 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
*/
if (used_key_parts_map.is_set(key_part_nr))
continue;
- if (key_part_nr < 1 || key_part_nr > join->fields_list.elements)
+ if (key_part_nr < 1 ||
+ (!is_agg_distinct && key_part_nr > join->fields_list.elements))
goto next_index;
cur_part= cur_index_info->key_part + key_part_nr - 1;
cur_group_prefix_len+= cur_part->store_length;
@@ -11865,15 +12310,11 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
cur_parts have bits set for only used keyparts.
*/
ulonglong all_parts, cur_parts;
- all_parts= (1<<max_key_part) - 1;
+ all_parts= (1ULL << max_key_part) - 1;
cur_parts= used_key_parts_map.to_ulonglong() >> 1;
if (all_parts != cur_parts)
goto next_index;
}
- else
- {
- DBUG_ASSERT(FALSE);
- }
/* Check (SA2). */
if (min_max_arg_item)
@@ -11899,8 +12340,9 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
must form a sequence without any gaps that starts immediately after the
last group keypart.
*/
- last_part= cur_index_info->key_part + cur_index_info->key_parts;
- first_non_group_part= (cur_group_key_parts < cur_index_info->key_parts) ?
+ key_parts= table->actual_n_key_parts(cur_index_info);
+ last_part= cur_index_info->key_part + key_parts;
+ first_non_group_part= (cur_group_key_parts < key_parts) ?
cur_index_info->key_part + cur_group_key_parts :
NULL;
first_non_infix_part= min_max_arg_part ?
@@ -11972,6 +12414,25 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
}
}
+ /**
+ Test WA2:If there are conditions on a column C participating in
+ MIN/MAX, those conditions must be conjunctions to all earlier
+ keyparts. Otherwise, Loose Index Scan cannot be used.
+ */
+ if (tree && min_max_arg_item)
+ {
+ uint dummy;
+ SEL_ARG *index_range_tree= get_index_range_tree(cur_index, tree, param,
+ &dummy);
+ SEL_ARG *cur_range= NULL;
+ if (get_sel_arg_for_keypart(min_max_arg_part->field,
+ index_range_tree, &cur_range) ||
+ (cur_range && cur_range->type != SEL_ARG::KEY_RANGE))
+ {
+ goto next_index;
+ }
+ }
+
/* If we got to this point, cur_index_info passes the test. */
key_infix_parts= cur_key_infix_len ? (uint)
(first_non_infix_part - first_non_group_part) : 0;
@@ -12033,9 +12494,17 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
&has_min_max_fld, &has_other_fld))
DBUG_RETURN(NULL);
+ /*
+ Check (SA6) if clustered key is used
+ */
+ if (is_agg_distinct && index == table->s->primary_key &&
+ table->file->primary_key_is_clustered())
+ DBUG_RETURN(NULL);
+
/* The query passes all tests, so construct a new TRP object. */
read_plan= new (param->mem_root)
- TRP_GROUP_MIN_MAX(have_min, have_max, min_max_arg_part,
+ TRP_GROUP_MIN_MAX(have_min, have_max, is_agg_distinct,
+ min_max_arg_part,
group_prefix_len, used_key_parts,
group_key_parts, index_info, index,
key_infix_len,
@@ -12049,6 +12518,11 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
read_plan->read_cost= best_read_cost;
read_plan->records= best_records;
+ if (read_time < best_read_cost && is_agg_distinct)
+ {
+ read_plan->read_cost= 0;
+ read_plan->use_index_scan();
+ }
DBUG_PRINT("info",
("Returning group min/max plan: cost: %g, records: %lu",
@@ -12065,12 +12539,13 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
SYNOPSIS
check_group_min_max_predicates()
- cond [in] the expression tree being analyzed
- min_max_arg [in] the field referenced by the MIN/MAX function(s)
- image_type [in]
- has_min_max_arg [out] true if the subtree being analyzed references min_max_arg
- has_other_arg [out] true if the subtree being analyzed references a column
- other min_max_arg
+ cond [in] the expression tree being analyzed
+ min_max_arg [in] the field referenced by the MIN/MAX function(s)
+ image_type [in]
+ has_min_max_arg [out] true if the subtree being analyzed references
+ min_max_arg
+ has_other_arg [out] true if the subtree being analyzed references a
+ column other min_max_arg
DESCRIPTION
The function walks recursively over the cond tree representing a WHERE
@@ -12114,7 +12589,7 @@ check_group_min_max_predicates(Item *cond, Item_field *min_max_arg_item,
(2) the subtree passes the test, but it is an OR and it references both
the min/max argument and other columns.
*/
- if (!check_group_min_max_predicates(and_or_arg, min_max_arg_item, //1
+ if (!check_group_min_max_predicates(and_or_arg, min_max_arg_item, //1
image_type,
&has_min_max, &has_other) ||
(func_type == Item_func::COND_OR_FUNC && has_min_max && has_other))//2
@@ -12130,7 +12605,7 @@ check_group_min_max_predicates(Item *cond, Item_field *min_max_arg_item,
a subquery in the WHERE clause.
*/
- if (cond_type == Item::SUBSELECT_ITEM)
+ if (unlikely(cond_type == Item::SUBSELECT_ITEM))
{
Item_subselect *subs_cond= (Item_subselect*) cond;
if (subs_cond->is_correlated)
@@ -12147,7 +12622,14 @@ check_group_min_max_predicates(Item *cond, Item_field *min_max_arg_item,
}
DBUG_RETURN(TRUE);
}
-
+ /*
+ Subquery with IS [NOT] NULL
+ TODO: Look into the cache_item and optimize it like we do for
+ subselect's above
+ */
+ if (unlikely(cond_type == Item::CACHE_ITEM))
+ DBUG_RETURN(cond->const_item());
+
/*
Condition of the form 'field' is equivalent to 'field <> 0' and thus
satisfies the SA3 condition.
@@ -12164,7 +12646,9 @@ check_group_min_max_predicates(Item *cond, Item_field *min_max_arg_item,
/* We presume that at this point there are no other Items than functions. */
DBUG_ASSERT(cond_type == Item::FUNC_ITEM);
-
+ if (unlikely(cond_type != Item::FUNC_ITEM)) /* Safety */
+ DBUG_RETURN(FALSE);
+
/* Test if cond references only group-by or non-group fields. */
Item_func *pred= (Item_func*) cond;
Item_func::Functype pred_type= pred->functype();
@@ -12276,73 +12760,75 @@ check_group_min_max_predicates(Item *cond, Item_field *min_max_arg_item,
/*
- Get SEL_ARG tree, if any, for the keypart covering non grouping
- attribute (NGA) field 'nga_field'.
+ Get the SEL_ARG tree 'tree' for the keypart covering 'field', if
+ any. 'tree' must be a unique conjunction to ALL predicates in earlier
+ keyparts of 'keypart_tree'.
+
+ E.g., if 'keypart_tree' is for a composite index (kp1,kp2) and kp2
+ covers 'field', all these conditions satisfies the requirement:
+
+ 1. "(kp1=2 OR kp1=3) AND kp2=10" => returns "kp2=10"
+ 2. "(kp1=2 AND kp2=10) OR (kp1=3 AND kp2=10)" => returns "kp2=10"
+ 3. "(kp1=2 AND (kp2=10 OR kp2=11)) OR (kp1=3 AND (kp2=10 OR kp2=11))"
+ => returns "kp2=10 OR kp2=11"
- This function enforces the NGA3 test: If 'keypart_tree' contains a
- condition for 'nga_field', there can only be one range. In the
- opposite case, this function returns with error and 'cur_range'
- should not be used.
+ whereas these do not
+ 1. "(kp1=2 AND kp2=10) OR kp1=3"
+ 2. "(kp1=2 AND kp2=10) OR (kp1=3 AND kp2=11)"
+ 3. "(kp1=2 AND kp2=10) OR (kp1=3 AND (kp2=10 OR kp2=11))"
- Note that the NGA1 and NGA2 requirements, like whether or not the
- range predicate for 'nga_field' is equality, is not tested by this
- function.
+ This function effectively tests requirement WA2. In combination with
+ a test that the returned tree has no more than one range it is also
+ a test of NGA3.
- @param[in] nga_field The NGA field we want the SEL_ARG tree for
+ @param[in] field The field we want the SEL_ARG tree for
@param[in] keypart_tree Root node of the SEL_ARG* tree for the index
@param[out] cur_range The SEL_ARG tree, if any, for the keypart
covering field 'keypart_field'
- @retval true 'keypart_tree' contained a predicate for 'nga_field' but
- multiple ranges exists. 'cur_range' should not be used.
+ @retval true 'keypart_tree' contained a predicate for 'field' that
+ is not conjunction to all predicates on earlier keyparts
@retval false otherwise
*/
static bool
-get_sel_arg_for_keypart(Field *nga_field,
+get_sel_arg_for_keypart(Field *field,
SEL_ARG *keypart_tree,
SEL_ARG **cur_range)
{
- if(keypart_tree == NULL)
+ if (keypart_tree == NULL)
return false;
- if(keypart_tree->field->eq(nga_field))
+ if (keypart_tree->field->eq(field))
{
- /*
- Enforce NGA3: If a condition for nga_field has been found, only
- a single range is allowed.
- */
- if (keypart_tree->prev || keypart_tree->next)
- return true; // There are multiple ranges
-
*cur_range= keypart_tree;
return false;
}
- SEL_ARG *found_tree= NULL;
+ SEL_ARG *tree_first_range= NULL;
SEL_ARG *first_kp= keypart_tree->first();
- for (SEL_ARG *cur_kp= first_kp; cur_kp && !found_tree; // && cur_range->type == SEL_ARG::KEY_RANGE;
- cur_kp= cur_kp->next)
+ for (SEL_ARG *cur_kp= first_kp; cur_kp; cur_kp= cur_kp->next)
{
+ SEL_ARG *curr_tree= NULL;
if (cur_kp->next_key_part)
{
- if (get_sel_arg_for_keypart(nga_field,
+ if (get_sel_arg_for_keypart(field,
cur_kp->next_key_part,
- &found_tree))
+ &curr_tree))
return true;
-
}
/*
- Enforce NGA3: If a condition for nga_field has been found,only
- a single range is allowed.
- */
- if (found_tree && first_kp->next)
- return true; // There are multiple ranges
+ Check if the SEL_ARG tree for 'field' is identical for all ranges in
+ 'keypart_tree
+ */
+ if (cur_kp == first_kp)
+ tree_first_range= curr_tree;
+ else if (!all_same(tree_first_range, curr_tree))
+ return true;
}
- *cur_range= found_tree;
+ *cur_range= tree_first_range;
return false;
}
-
/*
Extract a sequence of constants from a conjunction of equality predicates.
@@ -12365,7 +12851,8 @@ get_sel_arg_for_keypart(Field *nga_field,
(const_ci = NG_i).. In addition, there can only be one range when there is
such a gap.
Thus all the NGF_i attributes must fill the 'gap' between the last group-by
- attribute and the MIN/MAX attribute in the index (if present). If these
+ attribute and the MIN/MAX attribute in the index (if present). Also ensure
+ that there is only a single range on NGF_i (NGA3). If these
conditions hold, copy each constant from its corresponding predicate into
key_infix, in the order its NG_i attribute appears in the index, and update
key_infix_len with the total length of the key parts in key_infix.
@@ -12374,7 +12861,6 @@ get_sel_arg_for_keypart(Field *nga_field,
TRUE if the index passes the test
FALSE o/w
*/
-
static bool
get_constant_key_infix(KEY *index_info, SEL_ARG *index_range_tree,
KEY_PART_INFO *first_non_group_part,
@@ -12394,32 +12880,42 @@ get_constant_key_infix(KEY *index_info, SEL_ARG *index_range_tree,
{
cur_range= NULL;
/*
- Find the range tree for the current keypart. We assume that
- index_range_tree points to the first keypart in the index.
+ Check NGA3:
+ 1. get_sel_arg_for_keypart gets the range tree for the 'field' and also
+ checks for a unique conjunction of this tree with all the predicates
+ on the earlier keyparts in the index.
+ 2. Check for multiple ranges on the found keypart tree.
+
+ We assume that index_range_tree points to the leftmost keypart in
+ the index.
*/
- if(get_sel_arg_for_keypart(cur_part->field, index_range_tree, &cur_range))
+ if (get_sel_arg_for_keypart(cur_part->field, index_range_tree,
+ &cur_range))
+ return false;
+
+ if (cur_range && cur_range->elements > 1)
return false;
if (!cur_range || cur_range->type != SEL_ARG::KEY_RANGE)
{
if (min_max_arg_part)
- return FALSE; /* The current keypart has no range predicates at all. */
+ return false; /* The current keypart has no range predicates at all. */
else
{
*first_non_infix_part= cur_part;
- return TRUE;
+ return true;
}
}
if ((cur_range->min_flag & NO_MIN_RANGE) ||
(cur_range->max_flag & NO_MAX_RANGE) ||
(cur_range->min_flag & NEAR_MIN) || (cur_range->max_flag & NEAR_MAX))
- return FALSE;
+ return false;
uint field_length= cur_part->store_length;
if (cur_range->maybe_null &&
cur_range->min_value[0] && cur_range->max_value[0])
- {
+ {
/*
cur_range specifies 'IS NULL'. In this case the argument points
to a "null value" (is_null_string) that may not always be long
@@ -12438,7 +12934,7 @@ get_constant_key_infix(KEY *index_info, SEL_ARG *index_range_tree,
*key_infix_len+= field_length;
}
else
- return FALSE;
+ return false;
}
if (!min_max_arg_part && (cur_part == last_part))
@@ -12470,7 +12966,9 @@ get_field_keypart(KEY *index, Field *field)
{
KEY_PART_INFO *part, *end;
- for (part= index->key_part, end= part + index->key_parts; part < end; part++)
+ for (part= index->key_part,
+ end= part + field->table->actual_n_key_parts(index);
+ part < end; part++)
{
if (field->eq(part->field))
return part - index->key_part + 1;
@@ -12688,11 +13186,12 @@ TRP_GROUP_MIN_MAX::make_quick(PARAM *param, bool retrieve_full_rows,
quick= new QUICK_GROUP_MIN_MAX_SELECT(param->table,
param->thd->lex->current_select->join,
- have_min, have_max, min_max_arg_part,
+ have_min, have_max,
+ have_agg_distinct, min_max_arg_part,
group_prefix_len, group_key_parts,
used_key_parts, index_info, index,
read_cost, records, key_infix_len,
- key_infix, parent_alloc);
+ key_infix, parent_alloc, is_index_scan);
if (!quick)
DBUG_RETURN(NULL);
@@ -12773,6 +13272,9 @@ TRP_GROUP_MIN_MAX::make_quick(PARAM *param, bool retrieve_full_rows,
key_infix_len Length of the key infix appended to the group prefix
key_infix Infix of constants from equality predicates
parent_alloc Memory pool for this and quick_prefix_select data
+ is_index_scan get the next different key not by jumping on it via
+ index read, but by scanning until the end of the
+ rows with equal key value.
RETURN
None
@@ -12780,20 +13282,22 @@ TRP_GROUP_MIN_MAX::make_quick(PARAM *param, bool retrieve_full_rows,
QUICK_GROUP_MIN_MAX_SELECT::
QUICK_GROUP_MIN_MAX_SELECT(TABLE *table, JOIN *join_arg, bool have_min_arg,
- bool have_max_arg,
+ bool have_max_arg, bool have_agg_distinct_arg,
KEY_PART_INFO *min_max_arg_part_arg,
uint group_prefix_len_arg, uint group_key_parts_arg,
uint used_key_parts_arg, KEY *index_info_arg,
uint use_index, double read_cost_arg,
ha_rows records_arg, uint key_infix_len_arg,
- uchar *key_infix_arg, MEM_ROOT *parent_alloc)
+ uchar *key_infix_arg, MEM_ROOT *parent_alloc,
+ bool is_index_scan_arg)
:file(table->file), join(join_arg), index_info(index_info_arg),
group_prefix_len(group_prefix_len_arg),
group_key_parts(group_key_parts_arg), have_min(have_min_arg),
- have_max(have_max_arg), seen_first_key(FALSE), doing_key_read(FALSE),
- min_max_arg_part(min_max_arg_part_arg), key_infix(key_infix_arg),
- key_infix_len(key_infix_len_arg), min_functions_it(NULL),
- max_functions_it(NULL)
+ have_max(have_max_arg), have_agg_distinct(have_agg_distinct_arg),
+ seen_first_key(FALSE), doing_key_read(FALSE), min_max_arg_part(min_max_arg_part_arg),
+ key_infix(key_infix_arg), key_infix_len(key_infix_len_arg),
+ min_functions_it(NULL), max_functions_it(NULL),
+ is_index_scan(is_index_scan_arg)
{
head= table;
index= use_index;
@@ -12924,7 +13428,11 @@ QUICK_GROUP_MIN_MAX_SELECT::~QUICK_GROUP_MIN_MAX_SELECT()
DBUG_ASSERT(file == head->file);
if (doing_key_read)
head->disable_keyread();
- file->ha_index_end();
+ /*
+ There may be a code path when the same table was first accessed by index,
+ then the index is closed, and the table is scanned (order by + loose scan).
+ */
+ file->ha_index_or_rnd_end();
}
if (min_max_arg_part)
delete_dynamic(&min_max_ranges);
@@ -13115,7 +13623,10 @@ int QUICK_GROUP_MIN_MAX_SELECT::reset(void)
head->enable_keyread(); /* We need only the key attributes */
}
if ((result= file->ha_index_init(index,1)))
+ {
+ head->file->print_error(result, MYF(0));
DBUG_RETURN(result);
+ }
if (quick_prefix_select && quick_prefix_select->reset())
DBUG_RETURN(1);
result= file->ha_index_last(record);
@@ -13228,17 +13739,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::get_next()
} while ((result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE) &&
is_last_prefix != 0);
- if (result == 0)
- {
- /*
- Partially mimic the behavior of end_select_send. Copy the
- field data from Item_field::field into Item_field::result_field
- of each non-aggregated field (the group fields, and optionally
- other fields in non-ANSI SQL mode).
- */
- copy_fields(&join->tmp_table_param);
- }
- else if (result == HA_ERR_KEY_NOT_FOUND)
+ if (result == HA_ERR_KEY_NOT_FOUND)
result= HA_ERR_END_OF_FILE;
DBUG_RETURN(result);
@@ -13300,11 +13801,10 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min()
*/
if (min_max_arg_part && min_max_arg_part->field->is_null())
{
- uchar *key_buf= (uchar*)my_alloca(index_info->key_length);
-
+ uchar *tmp_key_buff= (uchar*)my_alloca(max_used_key_length);
/* Find the first subsequent record without NULL in the MIN/MAX field. */
- key_copy(key_buf, record, index_info, 0);
- result= file->ha_index_read_map(record, key_buf,
+ key_copy(tmp_key_buff, record, index_info, max_used_key_length);
+ result= file->ha_index_read_map(record, tmp_key_buff,
make_keypart_map(real_key_parts),
HA_READ_AFTER_KEY);
/*
@@ -13320,11 +13820,11 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min()
if (!result)
{
if (key_cmp(index_info->key_part, group_prefix, real_prefix_len))
- key_restore(record, key_buf, index_info, 0);
+ key_restore(record, tmp_key_buff, index_info, 0);
}
else if (result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE)
result= 0; /* There is a result in any case. */
- my_afree(key_buf);
+ my_afree(tmp_key_buff);
}
}
@@ -13369,6 +13869,56 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_max()
}
+/**
+ Find the next different key value by skiping all the rows with the same key
+ value.
+
+ Implements a specialized loose index access method for queries
+ containing aggregate functions with distinct of the form:
+ SELECT [SUM|COUNT|AVG](DISTINCT a,...) FROM t
+ This method comes to replace the index scan + Unique class
+ (distinct selection) for loose index scan that visits all the rows of a
+ covering index instead of jumping in the begining of each group.
+ TODO: Placeholder function. To be replaced by a handler API call
+
+ @param is_index_scan hint to use index scan instead of random index read
+ to find the next different value.
+ @param file table handler
+ @param key_part group key to compare
+ @param record row data
+ @param group_prefix current key prefix data
+ @param group_prefix_len length of the current key prefix data
+ @param group_key_parts number of the current key prefix columns
+ @return status
+ @retval 0 success
+ @retval !0 failure
+*/
+
+static int index_next_different (bool is_index_scan, handler *file,
+ KEY_PART_INFO *key_part, uchar * record,
+ const uchar * group_prefix,
+ uint group_prefix_len,
+ uint group_key_parts)
+{
+ if (is_index_scan)
+ {
+ int result= 0;
+
+ while (!key_cmp (key_part, group_prefix, group_prefix_len))
+ {
+ result= file->ha_index_next(record);
+ if (result)
+ return(result);
+ }
+ return result;
+ }
+ else
+ return file->ha_index_read_map(record, group_prefix,
+ make_prev_keypart_map(group_key_parts),
+ HA_READ_AFTER_KEY);
+}
+
+
/*
Determine the prefix of the next group.
@@ -13416,9 +13966,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_prefix()
else
{
/* Load the first key in this group into record. */
- result= file->ha_index_read_map(record, group_prefix,
- make_prev_keypart_map(group_key_parts),
- HA_READ_AFTER_KEY);
+ result= index_next_different (is_index_scan, file, index_info->key_part,
+ record, group_prefix, group_prefix_len,
+ group_key_parts);
if (result)
DBUG_RETURN(result);
}
@@ -13717,7 +14267,7 @@ void QUICK_GROUP_MIN_MAX_SELECT::update_min_result()
min_functions_it->rewind();
while ((min_func= (*min_functions_it)++))
- min_func->reset();
+ min_func->reset_and_add();
}
@@ -13749,7 +14299,7 @@ void QUICK_GROUP_MIN_MAX_SELECT::update_max_result()
max_functions_it->rewind();
while ((max_func= (*max_functions_it)++))
- max_func->reset();
+ max_func->reset_and_add();
}
@@ -14029,7 +14579,7 @@ void QUICK_GROUP_MIN_MAX_SELECT::dbug_dump(int indent, bool verbose)
}
-#endif /* NOT_USED */
+#endif /* !DBUG_OFF */
/*****************************************************************************
** Instantiate templates
diff --git a/sql/opt_range.h b/sql/opt_range.h
index 17bb6d16da8..b8b46ae5ab1 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -12,8 +12,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* classes to use when handling where clause */
@@ -25,6 +24,20 @@
#pragma interface /* gcc class implementation */
#endif
+#include "thr_malloc.h" /* sql_memdup */
+#include "records.h" /* READ_RECORD */
+#include "queues.h" /* QUEUE */
+/*
+ It is necessary to include set_var.h instead of item.h because there
+ are dependencies on include order for set_var.h and item.h. This
+ will be resolved later.
+*/
+#include "sql_class.h" // set_var.h: THD
+#include "set_var.h" /* Item */
+
+class JOIN;
+class Item_sum;
+
typedef struct st_key_part {
uint16 key,part;
/* See KEY_PART_INFO for meaning of the next two: */
@@ -346,6 +359,13 @@ public:
*/
virtual bool is_keys_used(const MY_BITMAP *fields);
+ /**
+ Simple sanity check that the quick select has been set up
+ correctly. Function is overridden by quick selects that merge
+ indices.
+ */
+ virtual bool is_valid() { return index != MAX_KEY; };
+
/*
rowid of last row retrieved by this quick select. This is used only when
doing ROR-index_merge selects
@@ -369,6 +389,18 @@ public:
*/
virtual void dbug_dump(int indent, bool verbose)= 0;
#endif
+
+ /*
+ Returns a QUICK_SELECT with reverse order of to the index.
+ */
+ virtual QUICK_SELECT_I *make_reverse(uint used_key_parts_arg) { return NULL; }
+
+ /*
+ Add the key columns used by the quick select into table's read set.
+
+ This is used by an optimization in filesort.
+ */
+ virtual void add_used_key_part_to_set(MY_BITMAP *col_set)=0;
};
@@ -405,7 +437,7 @@ protected:
/* Members to deal with case when this quick select is a ROR-merged scan */
bool in_ror_merged_scan;
- MY_BITMAP column_bitmap, *save_read_set, *save_write_set;
+ MY_BITMAP column_bitmap;
bool free_file; /* TRUE <=> this->file is "owned" by this quick select */
/* Range pointers to be used when not using MRR interface */
@@ -458,6 +490,10 @@ public:
void dbug_dump(int indent, bool verbose);
#endif
virtual void replace_handler(handler *new_file) { file= new_file; }
+ QUICK_SELECT_I *make_reverse(uint used_key_parts_arg);
+
+ virtual void add_used_key_part_to_set(MY_BITMAP *col_set);
+
private:
/* Default copy ctor used by QUICK_SELECT_DESC */
friend class TRP_ROR_INTERSECT;
@@ -600,9 +636,26 @@ public:
MEM_ROOT alloc;
THD *thd;
+ virtual bool is_valid()
+ {
+ List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
+ QUICK_RANGE_SELECT *quick;
+ bool valid= true;
+ while ((quick= it++))
+ {
+ if (!quick->is_valid())
+ {
+ valid= false;
+ break;
+ }
+ }
+ return valid;
+ }
virtual int read_keys_and_merge()= 0;
/* used to get rows collected in Unique */
READ_RECORD read_record;
+
+ virtual void add_used_key_part_to_set(MY_BITMAP *col_set);
};
@@ -678,6 +731,7 @@ public:
void add_keys_and_lengths(String *key_names, String *used_lengths);
void add_info_string(String *str);
bool is_keys_used(const MY_BITMAP *fields);
+ void add_used_key_part_to_set(MY_BITMAP *col_set);
#ifndef DBUG_OFF
void dbug_dump(int indent, bool verbose);
#endif
@@ -698,6 +752,22 @@ public:
*/
List<QUICK_SELECT_WITH_RECORD> quick_selects;
+ virtual bool is_valid()
+ {
+ List_iterator_fast<QUICK_SELECT_WITH_RECORD> it(quick_selects);
+ QUICK_SELECT_WITH_RECORD *quick;
+ bool valid= true;
+ while ((quick= it++))
+ {
+ if (!quick->quick->is_valid())
+ {
+ valid= false;
+ break;
+ }
+ }
+ return valid;
+ }
+
/*
Merged quick select that uses Clustered PK, if there is one. This quick
select is not used for row retrieval, it is used for row retrieval.
@@ -741,6 +811,7 @@ public:
void add_keys_and_lengths(String *key_names, String *used_lengths);
void add_info_string(String *str);
bool is_keys_used(const MY_BITMAP *fields);
+ void add_used_key_part_to_set(MY_BITMAP *col_set);
#ifndef DBUG_OFF
void dbug_dump(int indent, bool verbose);
#endif
@@ -749,6 +820,22 @@ public:
List<QUICK_SELECT_I> quick_selects; /* Merged quick selects */
+ virtual bool is_valid()
+ {
+ List_iterator_fast<QUICK_SELECT_I> it(quick_selects);
+ QUICK_SELECT_I *quick;
+ bool valid= true;
+ while ((quick= it++))
+ {
+ if (!quick->is_valid())
+ {
+ valid= false;
+ break;
+ }
+ }
+ return valid;
+ }
+
QUEUE queue; /* Priority queue for merge operation */
MEM_ROOT alloc; /* Memory pool for this and merged quick selects data. */
@@ -758,7 +845,6 @@ public:
bool have_prev_rowid; /* true if prev_rowid has valid data */
uint rowid_length; /* table rowid length */
private:
- static int queue_cmp(void *arg, uchar *val1, uchar *val2);
bool scans_inited;
};
@@ -810,6 +896,7 @@ private:
uchar *last_prefix; /* Prefix of the last group for detecting EOF. */
bool have_min; /* Specify whether we are computing */
bool have_max; /* a MIN, a MAX, or both. */
+ bool have_agg_distinct;/* aggregate_function(DISTINCT ...). */
bool seen_first_key; /* Denotes whether the first key was retrieved.*/
bool doing_key_read; /* true if we enabled key only reads */
@@ -825,6 +912,11 @@ private:
List<Item_sum> *max_functions;
List_iterator<Item_sum> *min_functions_it;
List_iterator<Item_sum> *max_functions_it;
+ /*
+ Use index scan to get the next different key instead of jumping into it
+ through index read
+ */
+ bool is_index_scan;
public:
/*
The following two members are public to allow easy access from
@@ -842,12 +934,13 @@ private:
void update_max_result();
public:
QUICK_GROUP_MIN_MAX_SELECT(TABLE *table, JOIN *join, bool have_min,
- bool have_max, KEY_PART_INFO *min_max_arg_part,
+ bool have_max, bool have_agg_distinct,
+ KEY_PART_INFO *min_max_arg_part,
uint group_prefix_len, uint group_key_parts,
uint used_key_parts, KEY *index_info, uint
use_index, double read_cost, ha_rows records, uint
key_infix_len, uchar *key_infix, MEM_ROOT
- *parent_alloc);
+ *parent_alloc, bool is_index_scan);
~QUICK_GROUP_MIN_MAX_SELECT();
bool add_range(SEL_ARG *sel_range);
void update_key_stat();
@@ -861,20 +954,30 @@ public:
bool unique_key_range() { return false; }
int get_type() { return QS_TYPE_GROUP_MIN_MAX; }
void add_keys_and_lengths(String *key_names, String *used_lengths);
+ void add_used_key_part_to_set(MY_BITMAP *col_set);
#ifndef DBUG_OFF
void dbug_dump(int indent, bool verbose);
#endif
+ bool is_agg_distinct() { return have_agg_distinct; }
+ virtual void append_loose_scan_type(String *str)
+ {
+ if (is_index_scan)
+ str->append(STRING_WITH_LEN(" (scanning)"));
+ }
};
class QUICK_SELECT_DESC: public QUICK_RANGE_SELECT
{
public:
- QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q, uint used_key_parts,
- bool *create_err);
+ QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q, uint used_key_parts);
int get_next();
bool reverse_sorted() { return 1; }
int get_type() { return QS_TYPE_RANGE_DESC; }
+ QUICK_SELECT_I *make_reverse(uint used_key_parts_arg)
+ {
+ return this; // is already reverse sorted
+ }
private:
bool range_reads_after_key(QUICK_RANGE *range);
int reset(void) { rev_it.rewind(); return QUICK_RANGE_SELECT::reset(); }
@@ -907,6 +1010,7 @@ class SQL_SELECT :public Sql_alloc {
SQL_SELECT();
~SQL_SELECT();
void cleanup();
+ void set_quick(QUICK_SELECT_I *new_quick) { delete quick; quick= new_quick; }
bool check_quick(THD *thd, bool force_quick_range, ha_rows limit)
{
key_map tmp;
@@ -949,11 +1053,15 @@ FT_SELECT *get_ft_select(THD *thd, TABLE *table, uint key);
QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
struct st_table_ref *ref,
ha_rows records);
-uint get_index_for_order(TABLE *table, ORDER *order, ha_rows limit);
+SQL_SELECT *make_select(TABLE *head, table_map const_tables,
+ table_map read_tables, COND *conds,
+ bool allow_null_cond, int *error);
#ifdef WITH_PARTITION_STORAGE_ENGINE
bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond);
void store_key_image_to_rec(Field *field, uchar *ptr, uint len);
#endif
+extern String null_string;
+
#endif
diff --git a/sql/opt_range_mrr.cc b/sql/opt_range_mrr.cc
index da6086d6cdc..1f4e36178db 100644
--- a/sql/opt_range_mrr.cc
+++ b/sql/opt_range_mrr.cc
@@ -1,3 +1,18 @@
+/*
+ Copyright (c) 2009, 2011, Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/****************************************************************************
MRR Range Sequence Interface implementation that walks a SEL_ARG* tree.
@@ -196,12 +211,12 @@ walk_right_n_up:
cur->min_key_parts +=
key_tree->next_key_part->store_min_key(seq->param->key[seq->keyno],
&cur->min_key,
- &cur->min_key_flag);
+ &cur->min_key_flag, MAX_KEY);
if (!key_tree->max_flag)
cur->max_key_parts +=
key_tree->next_key_part->store_max_key(seq->param->key[seq->keyno],
&cur->max_key,
- &cur->max_key_flag);
+ &cur->max_key_flag, MAX_KEY);
break;
}
}
@@ -233,6 +248,7 @@ walk_up_n_right:
/* Here minimum contains also function code bits, and maximum is +inf */
range->start_key.key= seq->param->min_key;
range->start_key.length= min_key_length;
+ range->start_key.keypart_map= make_prev_keypart_map(cur->min_key_parts);
range->start_key.flag= (ha_rkey_function) (cur->min_key_flag ^ GEOM_FLAG);
}
else
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index f19d9550f68..794b4f9b750 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -1,3 +1,19 @@
+/*
+ Copyright (c) 2010, 2012, Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
/**
@file
@@ -10,10 +26,11 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_base.h"
#include "sql_select.h"
+#include "filesort.h"
#include "opt_subselect.h"
-
+#include "sql_test.h"
#include <my_bit.h>
/*
@@ -573,8 +590,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
TODO: for PS, make the whole block execute only on the first execution
*/
Item_subselect *subselect;
- if (!(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW) && // (1)
- (subselect= parent_unit->item)) // (2)
+ if (!thd->lex->is_view_context_analysis() && // (1)
+ (subselect= parent_unit->item)) // (2)
{
Item_in_subselect *in_subs= NULL;
Item_allany_subselect *allany_subs= NULL;
@@ -834,8 +851,7 @@ bool subquery_types_allow_materialization(Item_in_subselect *in_subs)
create a blob column because item->max_length is too big.
The following check is copied from Item::make_string_field():
*/
- if (inner->max_length / inner->collation.collation->mbmaxlen >
- CONVERT_IF_BIGGER_TO_BLOB)
+ if (inner->too_big_for_varchar())
{
DBUG_RETURN(FALSE);
}
@@ -1905,11 +1921,25 @@ int pull_out_semijoin_tables(JOIN *join)
}
}
-
table_map pulled_tables= 0;
+ table_map dep_tables= 0;
if (have_join_nest_children)
goto skip;
+ /*
+ Calculate set of tables within this semi-join nest that have
+ other dependent tables
+ */
+ child_li.rewind();
+ while ((tbl= child_li++))
+ {
+ TABLE *const table= tbl->table;
+ if (table &&
+ (table->reginfo.join_tab->dependent &
+ sj_nest->nested_join->used_tables))
+ dep_tables|= table->reginfo.join_tab->dependent;
+ }
+
/* Action #1: Mark the constant tables to be pulled out */
child_li.rewind();
while ((tbl= child_li++))
@@ -1960,7 +1990,8 @@ int pull_out_semijoin_tables(JOIN *join)
child_li.rewind();
while ((tbl= child_li++))
{
- if (tbl->table && !(pulled_tables & tbl->table->map))
+ if (tbl->table && !(pulled_tables & tbl->table->map) &&
+ !(dep_tables & tbl->table->map))
{
if (find_eq_ref_candidate(tbl->table,
sj_nest->nested_join->used_tables &
@@ -3487,7 +3518,7 @@ bool setup_sj_materialization_part1(JOIN_TAB *sjm_tab)
sjm->sjm_table_cols, (ORDER*) 0,
TRUE /* distinct */,
1, /*save_sum_fields*/
- thd->options | TMP_TABLE_ALL_COLUMNS,
+ thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS,
HA_POS_ERROR /*rows_limit */,
(char*)"sj-materialize")))
DBUG_RETURN(TRUE); /* purecov: inspected */
@@ -3871,6 +3902,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
fn_format(path, path, mysql_tmpdir, "", MY_REPLACE_EXT|MY_UNPACK_FILENAME);
/* STEP 2: Figure if we'll be using a key or blob+constraint */
+ /* it always has my_charset_bin, so mbmaxlen==1 */
if (uniq_tuple_length_arg >= CONVERT_IF_BIGGER_TO_BLOB)
using_unique_constraint= TRUE;
@@ -4214,9 +4246,13 @@ int SJ_TMP_TABLE::sj_weedout_check_row(THD *thd)
/* create_internal_tmp_table_from_heap will generate error if needed */
if (!tmp_table->file->is_fatal_error(error, HA_CHECK_DUP))
DBUG_RETURN(1); /* Duplicate */
+
+ bool is_duplicate;
if (create_internal_tmp_table_from_heap(thd, tmp_table, start_recinfo,
- &recinfo, error, 1))
+ &recinfo, error, 1, &is_duplicate))
DBUG_RETURN(-1);
+ if (is_duplicate)
+ DBUG_RETURN(1);
}
DBUG_RETURN(0);
}
@@ -4900,7 +4936,43 @@ static void remove_subq_pushed_predicates(JOIN *join, Item **where)
bool JOIN::optimize_unflattened_subqueries()
{
- return select_lex->optimize_unflattened_subqueries();
+ return select_lex->optimize_unflattened_subqueries(false);
+}
+
+/**
+ Optimize all constant subqueries of a query that were not flattened into
+ a semijoin.
+
+ @details
+ Similar to other constant conditions, constant subqueries can be used in
+ various constant optimizations. Having optimized constant subqueries before
+ these constant optimizations, makes it possible to estimate if a subquery
+ is "cheap" enough to be executed during the optimization phase.
+
+ Constant subqueries can be optimized and evaluated independent of the outer
+ query, therefore if const_only = true, this method can be called early in
+ the optimization phase of the outer query.
+
+ @return Operation status
+ @retval FALSE success.
+ @retval TRUE error occurred.
+*/
+
+bool JOIN::optimize_constant_subqueries()
+{
+ ulonglong save_options= select_lex->options;
+ bool res;
+ /*
+ Constant subqueries may be executed during the optimization phase.
+ In EXPLAIN mode the optimizer doesn't initialize many of the data structures
+ needed for execution. In order to make it possible to execute subqueries
+ during optimization, constant subqueries must be optimized for execution,
+ not for EXPLAIN.
+ */
+ select_lex->options&= ~SELECT_DESCRIBE;
+ res= select_lex->optimize_unflattened_subqueries(true);
+ select_lex->options= save_options;
+ return res;
}
@@ -5003,7 +5075,7 @@ TABLE *create_dummy_tmp_table(THD *thd)
sjm_table_cols, (ORDER*) 0,
TRUE /* distinct */,
1, /*save_sum_fields*/
- thd->options | TMP_TABLE_ALL_COLUMNS,
+ thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS,
HA_POS_ERROR /*rows_limit */,
(char*)"dummy", TRUE /* Do not open */)))
{
@@ -5350,7 +5422,14 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
by the IN predicate.
*/
outer_join= unit->outer_select() ? unit->outer_select()->join : NULL;
- if (outer_join && outer_join->table_count > 0)
+ /*
+ Get the cost of the outer join if:
+ (1) It has at least one table, and
+ (2) It has been already optimized (if there is no join_tab, then the
+ outer join has not been optimized yet).
+ */
+ if (outer_join && outer_join->table_count > 0 && // (1)
+ outer_join->join_tab) // (2)
{
/*
TODO:
diff --git a/sql/opt_subselect.h b/sql/opt_subselect.h
index 444b3840950..0a74abec68b 100644
--- a/sql/opt_subselect.h
+++ b/sql/opt_subselect.h
@@ -1,4 +1,20 @@
/*
+ Copyright (c) 2010, 2012, Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
Semi-join subquery optimization code definitions
*/
@@ -171,12 +187,12 @@ public:
*/
if (try_loosescan && // (1)
- (handled_sj_equalities | bound_sj_equalities) == // (2)
- PREV_BITS(ulonglong, s->emb_sj_nest->sj_in_exprs) && // (2)
+ (handled_sj_equalities | bound_sj_equalities) == // (2)
+ PREV_BITS(ulonglong, s->emb_sj_nest->sj_in_exprs) && // (2)
(PREV_BITS(key_part_map, max_loose_keypart+1) & // (3)
(found_part | loose_scan_keyparts)) == // (3)
PREV_BITS(key_part_map, max_loose_keypart+1) && // (3)
- !key_uses_partial_cols(s->table, key))
+ !key_uses_partial_cols(s->table->s, key))
{
/* Ok, can use the strategy */
part1_conds_met= TRUE;
@@ -282,7 +298,7 @@ public:
};
-void advance_sj_state(JOIN *join, const table_map remaining_tables, uint idx,
+extern void advance_sj_state(JOIN *join, table_map remaining_tables, uint idx,
double *current_record_count, double *current_read_time,
POSITION *loose_scan_pos);
void restore_prev_sj_state(const table_map remaining_tables,
diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc
index b6aca56f047..aa8b17a2c85 100644
--- a/sql/opt_sum.cc
+++ b/sql/opt_sum.cc
@@ -12,8 +12,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@@ -49,7 +48,8 @@
(assuming a index for column d of table t2 is defined)
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "key.h" // key_cmp_if_same
#include "sql_select.h"
static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref, Field* field,
@@ -84,7 +84,7 @@ static ulonglong get_exact_record_count(List<TABLE_LIST> &tables)
while ((tl= ti++))
{
ha_rows tmp= tl->table->file->records();
- if ((tmp == HA_POS_ERROR))
+ if (tmp == HA_POS_ERROR)
return ULONGLONG_MAX;
count*= tmp;
}
@@ -315,8 +315,7 @@ int opt_sum_query(THD *thd,
error= tl->table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
if(error)
{
- tl->table->file->print_error(error, MYF(0));
- tl->table->in_use->fatal_error();
+ tl->table->file->print_error(error, MYF(ME_FATALERROR));
DBUG_RETURN(error);
}
count*= tl->table->file->stats.records;
@@ -432,15 +431,20 @@ int opt_sum_query(THD *thd,
const_result= 0;
break;
}
+ item_sum->set_aggregator(item_sum->has_with_distinct() ?
+ Aggregator::DISTINCT_AGGREGATOR :
+ Aggregator::SIMPLE_AGGREGATOR);
/*
If count == 0 (so is_exact_count == TRUE) and
there're no outer joins, set to NULL,
otherwise set to the constant value.
*/
if (!count && !outer_tables)
- item_sum->clear();
+ {
+ item_sum->aggregator_clear();
+ }
else
- item_sum->reset();
+ item_sum->reset_and_add();
item_sum->make_const();
recalc_const_item= 1;
break;
@@ -460,7 +464,7 @@ int opt_sum_query(THD *thd,
}
if (thd->is_error())
- DBUG_RETURN(thd->main_da.sql_errno());
+ DBUG_RETURN(thd->stmt_da->sql_errno());
/*
If we have a where clause, we can only ignore searching in the
@@ -475,6 +479,24 @@ int opt_sum_query(THD *thd,
}
+/*
+ Check if both item1 and item2 are strings, and item1 has fewer characters
+ than item2.
+*/
+
+static bool check_item1_shorter_item2(Item *item1, Item *item2)
+{
+ if (item1->cmp_type() == STRING_RESULT &&
+ item2->cmp_type() == STRING_RESULT)
+ {
+ int len1= item1->max_length / item1->collation.collation->mbmaxlen;
+ int len2= item2->max_length / item2->collation.collation->mbmaxlen;
+ return len1 < len2;
+ }
+ return false; /* When the check is not applicable, it means "not bigger" */
+}
+
+
/**
Test if the predicate compares a field with constants.
@@ -505,7 +527,7 @@ bool simple_pred(Item_func *func_item, Item **args, bool *inv_order)
if (!(item= it++))
return 0;
args[0]= item->real_item();
- if (args[0]->max_length < args[1]->max_length)
+ if (check_item1_shorter_item2(args[0], args[1]))
return 0;
if (it++)
return 0;
@@ -540,7 +562,7 @@ bool simple_pred(Item_func *func_item, Item **args, bool *inv_order)
}
else
return 0;
- if (args[0]->max_length < args[1]->max_length)
+ if (check_item1_shorter_item2(args[0], args[1]))
return 0;
break;
case 3:
@@ -555,7 +577,7 @@ bool simple_pred(Item_func *func_item, Item **args, bool *inv_order)
if (!item->const_item())
return 0;
args[i]= item;
- if (args[0]->max_length < args[i]->max_length)
+ if (check_item1_shorter_item2(args[0], args[1]))
return 0;
}
}
@@ -787,7 +809,7 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
{
/* Update endpoints for MAX/MIN, see function comment. */
Item *value= args[between && max_fl ? 2 : 1];
- store_val_in_field(part->field, value, CHECK_FIELD_IGNORE);
+ value->save_in_field_no_warnings(part->field, 1);
if (part->null_bit)
*key_ptr++= (uchar) test(part->field->is_null());
part->field->get_key_image(key_ptr, part->length, Field::itRAW);
@@ -860,7 +882,6 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
1 Can use key to optimize MIN()/MAX().
In this case ref, range_fl and prefix_len are updated
*/
-
static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref,
Field* field, COND *cond,
@@ -889,7 +910,8 @@ static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref,
continue;
uint jdx= 0;
*prefix_len= 0;
- for (part= keyinfo->key_part, part_end= part+keyinfo->key_parts ;
+ part_end= keyinfo->key_part+table->actual_n_key_parts(keyinfo);
+ for (part= keyinfo->key_part ;
part != part_end ;
part++, jdx++, key_part_to_use= (key_part_to_use << 1) | 1)
{
diff --git a/sql/opt_table_elimination.cc b/sql/opt_table_elimination.cc
index 1df35e93e45..9f304ad043b 100644
--- a/sql/opt_table_elimination.cc
+++ b/sql/opt_table_elimination.cc
@@ -1,3 +1,19 @@
+/*
+ Copyright (c) 2009, 2011, Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
/**
@file
@@ -12,7 +28,6 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
#include "my_bit.h"
#include "sql_select.h"
@@ -892,8 +907,11 @@ bool Dep_analysis_context::run_wave(List<Dep_module> *new_bound_modules)
iter= module->init_unbound_values_iter(iter_buf);
while ((value= module->get_next_unbound_value(this, iter)))
{
- value->make_bound();
- new_bound_values.push_back(value);
+ if (!value->is_bound())
+ {
+ value->make_bound();
+ new_bound_values.push_back(value);
+ }
}
}
new_bound_modules->empty();
@@ -1740,7 +1758,7 @@ Dep_module* Dep_value_field::get_next_unbound_module(Dep_analysis_context *dac,
- have this field as a part of them
*/
while (key_dep && (key_dep->is_applicable() ||
- !field->part_of_key.is_set(key_dep->keyno)))
+ !field->part_of_key_not_clustered.is_set(key_dep->keyno)))
{
key_dep= key_dep->next_table_key;
}
diff --git a/sql/parse_file.cc b/sql/parse_file.cc
index 761ab9ee10a..a6e3aa7ed66 100644
--- a/sql/parse_file.cc
+++ b/sql/parse_file.cc
@@ -1,6 +1,4 @@
-/*
- Copyright (c) 2004-2007 MySQL AB, 2008, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2004, 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
@@ -13,8 +11,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@file
@@ -23,7 +20,10 @@
Text .frm files management routines
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "parse_file.h"
+#include "unireg.h" // CREATE_MODE
+#include "sql_table.h" // build_table_filename
#include <errno.h>
#include <m_ctype.h>
#include <my_sys.h>
@@ -280,8 +280,9 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
// temporary file name
path[path_end]='~';
path[path_end+1]= '\0';
- if ((handler= my_create(path, CREATE_MODE, O_RDWR | O_TRUNC,
- MYF(MY_WME))) <= 0)
+ if ((handler= mysql_file_create(key_file_fileparser,
+ path, CREATE_MODE, O_RDWR | O_TRUNC,
+ MYF(MY_WME))) <= 0)
{
DBUG_RETURN(TRUE);
}
@@ -310,11 +311,11 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
goto err_w_file;
if (opt_sync_frm) {
- if (my_sync(handler, MYF(MY_WME)))
+ if (mysql_file_sync(handler, MYF(MY_WME)))
goto err_w_file;
}
- if (my_close(handler, MYF(MY_WME)))
+ if (mysql_file_close(handler, MYF(MY_WME)))
{
DBUG_RETURN(TRUE);
}
@@ -326,7 +327,7 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
char path_to[FN_REFLEN];
memcpy(path_to, path, path_end+1);
path[path_end]='~';
- if (my_rename(path, path_to, MYF(MY_WME)))
+ if (mysql_file_rename(key_file_fileparser, path, path_to, MYF(MY_WME)))
{
DBUG_RETURN(TRUE);
}
@@ -335,7 +336,7 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
err_w_cache:
end_io_cache(&file);
err_w_file:
- my_close(handler, MYF(MY_WME));
+ mysql_file_close(handler, MYF(MY_WME));
DBUG_RETURN(TRUE);
}
@@ -364,7 +365,7 @@ my_bool rename_in_schema_file(THD *thd,
build_table_filename(new_path, sizeof(new_path) - 1,
new_db, new_name, reg_ext, 0);
- if (my_rename(old_path, new_path, MYF(MY_WME)))
+ if (mysql_file_rename(key_file_frm, old_path, new_path, MYF(MY_WME)))
return 1;
/* check if arc_dir exists: disabled unused feature (see bug #17823). */
@@ -408,7 +409,8 @@ sql_parse_prepare(const LEX_STRING *file_name, MEM_ROOT *mem_root,
File file;
DBUG_ENTER("sql_parse_prepare");
- if (!my_stat(file_name->str, &stat_info, MYF(MY_WME)))
+ if (!mysql_file_stat(key_file_fileparser,
+ file_name->str, &stat_info, MYF(MY_WME)))
{
DBUG_RETURN(0);
}
@@ -429,20 +431,21 @@ sql_parse_prepare(const LEX_STRING *file_name, MEM_ROOT *mem_root,
DBUG_RETURN(0);
}
- if ((file= my_open(file_name->str, O_RDONLY | O_SHARE, MYF(MY_WME))) < 0)
+ if ((file= mysql_file_open(key_file_fileparser, file_name->str,
+ O_RDONLY | O_SHARE, MYF(MY_WME))) < 0)
{
DBUG_RETURN(0);
}
- if ((len= my_read(file, (uchar *)parser->buff,
- (size_t) stat_info.st_size, MYF(MY_WME))) ==
+ if ((len= mysql_file_read(file, (uchar *)parser->buff,
+ stat_info.st_size, MYF(MY_WME))) ==
MY_FILE_ERROR)
{
- my_close(file, MYF(MY_WME));
+ mysql_file_close(file, MYF(MY_WME));
DBUG_RETURN(0);
}
- if (my_close(file, MYF(MY_WME)))
+ if (mysql_file_close(file, MYF(MY_WME)))
{
DBUG_RETURN(0);
}
diff --git a/sql/parse_file.h b/sql/parse_file.h
index a435a4e66fd..2a0266e98b7 100644
--- a/sql/parse_file.h
+++ b/sql/parse_file.h
@@ -1,7 +1,5 @@
/* -*- C++ -*- */
-/*
- Copyright (c) 2004-2007 MySQL AB, 2008, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2004, 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
@@ -14,12 +12,19 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#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
+
+class THD;
+
+typedef struct st_mem_root MEM_ROOT;
+
#define PARSE_FILE_TIMESTAMPLENGTH 19
enum file_opt_type {
diff --git a/sql/partition_element.h b/sql/partition_element.h
index 591cd45b8fd..75033d3552f 100644
--- a/sql/partition_element.h
+++ b/sql/partition_element.h
@@ -1,4 +1,7 @@
-/* Copyright (c) 2006, 2007 MySQL AB
+#ifndef PARTITION_ELEMENT_INCLUDED
+#define PARTITION_ELEMENT_INCLUDED
+
+/* Copyright (c) 2005, 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
@@ -13,6 +16,9 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include "my_base.h" /* ha_rows */
+#include "handler.h" /* UNDEF_NODEGROUP */
+
/**
* An enum and a struct to handle partitioning and subpartitioning.
*/
@@ -32,10 +38,40 @@ enum partition_state {
PART_REORGED_DROPPED= 5,
PART_CHANGED= 6,
PART_IS_CHANGED= 7,
- PART_IS_ADDED= 8
+ PART_IS_ADDED= 8,
+ PART_ADMIN= 9
};
/*
+ This struct is used to keep track of column expressions as part
+ of the COLUMNS concept in conjunction with RANGE and LIST partitioning.
+ The value can be either of MINVALUE, MAXVALUE and an expression that
+ must be constant and evaluate to the same type as the column it
+ represents.
+
+ The data in this fixed in two steps. The parser will only fill in whether
+ it is a max_value or provide an expression. Filling in
+ column_value, part_info, partition_id, null_value is done by the
+ function fix_column_value_function. However the item tree needs
+ fixed also before writing it into the frm file (in add_column_list_values).
+ To distinguish between those two variants, fixed= 1 after the
+ fixing in add_column_list_values and fixed= 2 otherwise. This is
+ since the fixing in add_column_list_values isn't a complete fixing.
+*/
+
+typedef struct p_column_list_val
+{
+ void* column_value;
+ Item* item_expression;
+ partition_info *part_info;
+ uint partition_id;
+ bool max_value;
+ bool null_value;
+ char fixed;
+} part_column_list_val;
+
+
+/*
This struct is used to contain the value of an element
in the VALUES IN struct. It needs to keep knowledge of
whether it is a signed/unsigned value and whether it is
@@ -45,8 +81,10 @@ enum partition_state {
typedef struct p_elem_val
{
longlong value;
+ uint added_items;
bool null_value;
bool unsigned_flag;
+ part_column_list_val *col_val_array;
} part_elem_value;
struct st_ddl_log_memory_entry;
@@ -69,8 +107,8 @@ public:
enum partition_state part_state;
uint16 nodegroup_id;
bool has_null_value;
- bool signed_flag;/* Indicate whether this partition uses signed constants */
- bool max_value; /* Indicate whether this partition uses MAXVALUE */
+ bool signed_flag; // Range value signed
+ bool max_value; // MAXVALUE range
partition_element()
: part_max_rows(0), part_min_rows(0), range_value(0),
@@ -103,3 +141,5 @@ public:
}
~partition_element() {}
};
+
+#endif /* PARTITION_ELEMENT_INCLUDED */
diff --git a/sql/partition_info.cc b/sql/partition_info.cc
index ac3042fd815..1bfa8864cb9 100644
--- a/sql/partition_info.cc
+++ b/sql/partition_info.cc
@@ -1,5 +1,4 @@
-/*
- Copyright (c) 2006, 2010, Oracle and/or its affiliates.
+/* Copyright (c) 2006, 2013, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +11,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* Some general useful functions */
@@ -21,7 +19,13 @@
#pragma implementation
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
+// Required to get server definitions for mysql/plugin.h right
+#include "sql_plugin.h"
+#include "sql_partition.h" /* partition_info.h: LIST_PART_ENTRY */
+#include "partition_info.h"
+#include "sql_parse.h" // test_if_data_home_dir
+#include "sql_acl.h" // *_ACL
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
@@ -77,7 +81,7 @@ partition_info *partition_info::get_clone()
SYNOPSIS
create_default_partition_names()
part_no Partition number for subparts
- no_parts Number of partitions
+ num_parts Number of partitions
start_no Starting partition number
subpart Is it subpartitions
@@ -93,10 +97,10 @@ partition_info *partition_info::get_clone()
#define MAX_PART_NAME_SIZE 8
char *partition_info::create_default_partition_names(uint part_no,
- uint no_parts_arg,
+ uint num_parts_arg,
uint start_no)
{
- char *ptr= (char*) sql_calloc(no_parts_arg*MAX_PART_NAME_SIZE);
+ char *ptr= (char*) sql_calloc(num_parts_arg*MAX_PART_NAME_SIZE);
char *move_ptr= ptr;
uint i= 0;
DBUG_ENTER("create_default_partition_names");
@@ -107,17 +111,53 @@ char *partition_info::create_default_partition_names(uint part_no,
{
sprintf(move_ptr, "p%u", (start_no + i));
move_ptr+= MAX_PART_NAME_SIZE;
- } while (++i < no_parts_arg);
+ } while (++i < num_parts_arg);
}
else
{
- mem_alloc_error(no_parts_arg*MAX_PART_NAME_SIZE);
+ mem_alloc_error(num_parts_arg*MAX_PART_NAME_SIZE);
}
DBUG_RETURN(ptr);
}
/*
+ Generate a version string for partition expression
+ This function must be updated every time there is a possibility for
+ a new function of a higher version number than 5.5.0.
+
+ SYNOPSIS
+ set_show_version_string()
+ RETURN VALUES
+ None
+*/
+void partition_info::set_show_version_string(String *packet)
+{
+ int version= 0;
+ if (column_list)
+ packet->append(STRING_WITH_LEN("\n/*!50500"));
+ else
+ {
+ if (part_expr)
+ part_expr->walk(&Item::intro_version, 0, (uchar*)&version);
+ if (subpart_expr)
+ subpart_expr->walk(&Item::intro_version, 0, (uchar*)&version);
+ if (version == 0)
+ {
+ /* No new functions in partition function */
+ packet->append(STRING_WITH_LEN("\n/*!50100"));
+ }
+ else
+ {
+ char buf[65];
+ char *buf_ptr= longlong10_to_str((longlong)version, buf, 10);
+ packet->append(STRING_WITH_LEN("\n/*!"));
+ packet->append(buf, (size_t)(buf_ptr - buf));
+ }
+ }
+}
+
+/*
Create a unique name for the subpartition as part_name'sp''subpart_no'
SYNOPSIS
create_subpartition_name()
@@ -191,19 +231,19 @@ bool partition_info::set_up_default_partitions(handler *file,
goto end;
}
- if ((no_parts == 0) &&
- ((no_parts= file->get_default_no_partitions(info)) == 0))
+ if ((num_parts == 0) &&
+ ((num_parts= file->get_default_no_partitions(info)) == 0))
{
my_error(ER_PARTITION_NOT_DEFINED_ERROR, MYF(0), "partitions");
goto end;
}
- if (unlikely(no_parts > MAX_PARTITIONS))
+ if (unlikely(num_parts > MAX_PARTITIONS))
{
my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0));
goto end;
}
- if (unlikely((!(default_name= create_default_partition_names(0, no_parts,
+ if (unlikely((!(default_name= create_default_partition_names(0, num_parts,
start_no)))))
goto end;
i= 0;
@@ -222,7 +262,7 @@ bool partition_info::set_up_default_partitions(handler *file,
mem_alloc_error(sizeof(partition_element));
goto end;
}
- } while (++i < no_parts);
+ } while (++i < num_parts);
result= FALSE;
end:
DBUG_RETURN(result);
@@ -261,9 +301,9 @@ bool partition_info::set_up_default_subpartitions(handler *file,
List_iterator<partition_element> part_it(partitions);
DBUG_ENTER("partition_info::set_up_default_subpartitions");
- if (no_subparts == 0)
- no_subparts= file->get_default_no_partitions(info);
- if (unlikely((no_parts * no_subparts) > MAX_PARTITIONS))
+ if (num_subparts == 0)
+ num_subparts= file->get_default_no_partitions(info);
+ if (unlikely((num_parts * num_subparts) > MAX_PARTITIONS))
{
my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0));
goto end;
@@ -290,8 +330,8 @@ bool partition_info::set_up_default_subpartitions(handler *file,
mem_alloc_error(sizeof(partition_element));
goto end;
}
- } while (++j < no_subparts);
- } while (++i < no_parts);
+ } while (++j < num_subparts);
+ } while (++i < num_parts);
result= FALSE;
end:
DBUG_RETURN(result);
@@ -336,6 +376,49 @@ bool partition_info::set_up_defaults_for_partitioning(handler *file,
/*
+ Support routine for check_partition_info
+
+ SYNOPSIS
+ has_unique_fields
+ no parameters
+
+ RETURN VALUE
+ Erroneus field name Error, there are two fields with same name
+ NULL Ok, no field defined twice
+
+ DESCRIPTION
+ Check that the user haven't defined the same field twice in
+ key or column list partitioning.
+*/
+char* partition_info::has_unique_fields()
+{
+ char *field_name_outer, *field_name_inner;
+ List_iterator<char> it_outer(part_field_list);
+ uint num_fields= part_field_list.elements;
+ uint i,j;
+ DBUG_ENTER("partition_info::has_unique_fields");
+
+ for (i= 0; i < num_fields; i++)
+ {
+ field_name_outer= it_outer++;
+ List_iterator<char> it_inner(part_field_list);
+ for (j= 0; j < num_fields; j++)
+ {
+ field_name_inner= it_inner++;
+ if (i >= j)
+ continue;
+ if (!(my_strcasecmp(system_charset_info,
+ field_name_outer,
+ field_name_inner)))
+ {
+ DBUG_RETURN(field_name_outer);
+ }
+ }
+ }
+ DBUG_RETURN(NULL);
+}
+
+/*
A support function to check if a partition element's name is unique
SYNOPSIS
@@ -520,12 +603,12 @@ bool partition_info::check_engine_mix(handlerton *engine_type,
{
handlerton *old_engine_type= engine_type;
bool first= TRUE;
- uint no_parts= partitions.elements;
+ uint n_parts= partitions.elements;
DBUG_ENTER("partition_info::check_engine_mix");
DBUG_PRINT("info", ("in: engine_type = %s, table_engine_set = %u",
ha_resolve_storage_engine_name(engine_type),
table_engine_set));
- if (no_parts)
+ if (n_parts)
{
List_iterator<partition_element> part_it(partitions);
uint i= 0;
@@ -538,7 +621,7 @@ bool partition_info::check_engine_mix(handlerton *engine_type,
if (is_sub_partitioned() &&
part_elem->subpartitions.elements)
{
- uint no_subparts= part_elem->subpartitions.elements;
+ uint n_subparts= part_elem->subpartitions.elements;
uint j= 0;
List_iterator<partition_element> sub_it(part_elem->subpartitions);
do
@@ -550,7 +633,7 @@ bool partition_info::check_engine_mix(handlerton *engine_type,
if (check_engine_condition(sub_elem, table_engine_set,
&engine_type, &first))
goto error;
- } while (++j < no_subparts);
+ } while (++j < n_subparts);
/* ensure that the partition also has correct engine */
if (check_engine_condition(part_elem, table_engine_set,
&engine_type, &first))
@@ -559,7 +642,7 @@ bool partition_info::check_engine_mix(handlerton *engine_type,
else if (check_engine_condition(part_elem, table_engine_set,
&engine_type, &first))
goto error;
- } while (++i < no_parts);
+ } while (++i < n_parts);
}
DBUG_PRINT("info", ("engine_type = %s",
ha_resolve_storage_engine_name(engine_type)));
@@ -591,6 +674,7 @@ error:
SYNOPSIS
check_range_constants()
+ thd Thread object
RETURN VALUE
TRUE An error occurred during creation of range constants
@@ -603,76 +687,108 @@ error:
called for RANGE PARTITIONed tables.
*/
-bool partition_info::check_range_constants()
+bool partition_info::check_range_constants(THD *thd)
{
partition_element* part_def;
- longlong current_largest;
- longlong part_range_value;
bool first= TRUE;
uint i;
List_iterator<partition_element> it(partitions);
- bool result= TRUE;
- bool signed_flag= !part_expr->unsigned_flag;
+ int result= TRUE;
DBUG_ENTER("partition_info::check_range_constants");
- DBUG_PRINT("enter", ("INT_RESULT with %d parts", no_parts));
+ DBUG_PRINT("enter", ("RANGE with %d parts, column_list = %u", num_parts,
+ column_list));
- LINT_INIT(current_largest);
-
- part_result_type= INT_RESULT;
- range_int_array= (longlong*)sql_alloc(no_parts * sizeof(longlong));
- if (unlikely(range_int_array == NULL))
- {
- mem_alloc_error(no_parts * sizeof(longlong));
- goto end;
- }
- i= 0;
- do
+ if (column_list)
{
- part_def= it++;
- if ((i != (no_parts - 1)) || !defined_max_value)
+ 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*)sql_calloc(num_parts *
+ size_entries);
+ if (unlikely(range_col_array == NULL))
{
- part_range_value= part_def->range_value;
- if (!signed_flag)
- part_range_value-= 0x8000000000000000ULL;
+ mem_alloc_error(num_parts * size_entries);
+ goto end;
}
- else
- part_range_value= LONGLONG_MAX;
- if (first)
+ loc_range_col_array= range_col_array;
+ i= 0;
+ do
{
- current_largest= part_range_value;
- range_int_array[0]= part_range_value;
+ 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*)sql_alloc(num_parts * sizeof(longlong));
+ if (unlikely(range_int_array == NULL))
+ {
+ mem_alloc_error(num_parts * sizeof(longlong));
+ goto end;
}
- else
+ i= 0;
+ do
{
- if (likely(current_largest < part_range_value))
+ part_def= it++;
+ if ((i != (num_parts - 1)) || !defined_max_value)
{
- current_largest= part_range_value;
- range_int_array[i]= part_range_value;
- }
- else if (defined_max_value &&
- current_largest == part_range_value &&
- part_range_value == LONGLONG_MAX &&
- i == (no_parts - 1))
- {
- range_int_array[i]= part_range_value;
+ part_range_value= part_def->range_value;
+ if (!signed_flag)
+ part_range_value-= 0x8000000000000000ULL;
}
else
+ part_range_value= LONGLONG_MAX;
+
+ if (!first)
{
- my_error(ER_RANGE_NOT_INCREASING_ERROR, MYF(0));
- goto end;
+ 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;
}
- }
- } while (++i < no_parts);
+ 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 unsigned and one for signed.
+ constant list expressions. One routine for integers and one for
+ column lists.
SYNOPSIS
list_part_cmp()
@@ -685,7 +801,8 @@ end:
-1 a < b
*/
-int partition_info::list_part_cmp(const void* a, const void* 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;
@@ -698,6 +815,70 @@ int partition_info::list_part_cmp(const void* a, const void* b)
}
+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
@@ -706,6 +887,7 @@ int partition_info::list_part_cmp(const void* a, const void* b)
SYNOPSIS
check_list_constants()
+ thd Thread object
RETURN VALUE
TRUE An error occurred during creation of list constants
@@ -718,20 +900,23 @@ int partition_info::list_part_cmp(const void* a, const void* b)
called for LIST PARTITIONed tables.
*/
-bool partition_info::check_list_constants()
+bool partition_info::check_list_constants(THD *thd)
{
- uint i;
+ uint i, size_entries, num_column_values;
uint list_index= 0;
part_elem_value *list_value;
bool result= TRUE;
- longlong curr_value, prev_value, type_add, calc_value;
+ 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");
- part_result_type= INT_RESULT;
- no_list_values= 0;
+ num_list_values= 0;
/*
We begin by calculating the number of list values that have been
defined in the first step.
@@ -764,51 +949,85 @@ bool partition_info::check_list_constants()
}
List_iterator<part_elem_value> list_val_it1(part_def->list_val_list);
while (list_val_it1++)
- no_list_values++;
- } while (++i < no_parts);
+ num_list_values++;
+ } while (++i < num_parts);
list_func_it.rewind();
- list_array= (LIST_PART_ENTRY*)sql_alloc((no_list_values+1) *
- sizeof(LIST_PART_ENTRY));
- if (unlikely(list_array == NULL))
+ num_column_values= part_field_list.elements;
+ size_entries= column_list ?
+ (num_column_values * sizeof(part_column_list_val)) :
+ sizeof(LIST_PART_ENTRY);
+ ptr= sql_calloc((num_list_values+1) * size_entries);
+ if (unlikely(ptr == NULL))
{
- mem_alloc_error(no_list_values * sizeof(LIST_PART_ENTRY));
+ mem_alloc_error(num_list_values * size_entries);
goto end;
}
-
- i= 0;
- /*
- Fix to be able to reuse signed sort functions also for unsigned
- partition functions.
- */
- type_add= (longlong)(part_expr->unsigned_flag ?
+ 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++;
+ 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++;
- List_iterator<part_elem_value> list_val_it2(part_def->list_val_list);
- while ((list_value= list_val_it2++))
+ do
{
- calc_value= list_value->value - type_add;
- list_array[list_index].list_value= calc_value;
- list_array[list_index++].partition_id= i;
- }
- } while (++i < no_parts);
-
- if (fixed && no_list_values)
+ part_def= list_func_it++;
+ 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;
- my_qsort((void*)list_array, no_list_values, sizeof(LIST_PART_ENTRY),
- &list_part_cmp);
-
+ /*
+ 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;
- LINT_INIT(prev_value);
do
{
- DBUG_ASSERT(i < no_list_values);
- curr_value= list_array[i].list_value;
- if (likely(first || prev_value != curr_value))
+ 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;
@@ -818,23 +1037,48 @@ bool partition_info::check_list_constants()
my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0));
goto end;
}
- } while (++i < no_list_values);
+ } 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.
+
+ @param thd THD also containing sql_mode (looks from MODE_NO_DIR_IN_CREATE).
+ @param part_elem partition_element to check.
+*/
+static void warn_if_dir_in_part_elem(THD *thd, partition_element *part_elem)
+{
+#ifdef HAVE_READLINK
+ if (!my_use_symdir || (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE))
+#endif
+ {
+ if (part_elem->data_file_name)
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
+ "DATA DIRECTORY");
+ if (part_elem->index_file_name)
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
+ "INDEX DIRECTORY");
+ part_elem->data_file_name= part_elem->index_file_name= NULL;
+ }
+}
+
/*
This code is used early in the CREATE TABLE and ALTER TABLE process.
SYNOPSIS
check_partition_info()
+ thd Thread object
+ eng_type Return value for used engine in partitions
file A reference to a handler of the table
info Create info
- engine_type Return value for used engine in partitions
- check_partition_function Should we check the partition function
+ add_or_reorg_part Is it ALTER TABLE ADD/REORGANIZE command
RETURN VALUE
TRUE Error, something went wrong
@@ -850,7 +1094,7 @@ end:
bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
handler *file, HA_CREATE_INFO *info,
- bool check_partition_function)
+ bool add_or_reorg_part)
{
handlerton *table_engine= default_engine_type;
uint i, tot_partitions;
@@ -861,11 +1105,11 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
DBUG_PRINT("info", ("default table_engine = %s",
ha_resolve_storage_engine_name(table_engine)));
- if (check_partition_function)
+ if (!add_or_reorg_part)
{
int err= 0;
- if (part_type != HASH_PARTITION || !list_of_part_fields)
+ if (!list_of_part_fields)
{
DBUG_ASSERT(part_expr);
err= part_expr->walk(&Item::check_partition_func_processor, 0,
@@ -879,9 +1123,12 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
goto end;
}
+ if (thd->lex->sql_command == SQLCOM_CREATE_TABLE &&
+ fix_parser_data(thd))
+ goto end;
}
if (unlikely(!is_sub_partitioned() &&
- !(use_default_subpartitions && use_default_no_subpartitions)))
+ !(use_default_subpartitions && use_default_num_subpartitions)))
{
my_error(ER_SUBPARTITION_ERROR, MYF(0));
goto end;
@@ -939,6 +1186,12 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
}
}
+ if (part_field_list.elements > 0 &&
+ (same_name= has_unique_fields()))
+ {
+ my_error(ER_SAME_NAME_PARTITION_FIELD, MYF(0), same_name);
+ goto end;
+ }
if ((same_name= has_unique_names()))
{
my_error(ER_SAME_NAME_PARTITION, MYF(0), same_name);
@@ -947,30 +1200,17 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
i= 0;
{
List_iterator<partition_element> part_it(partitions);
- uint no_parts_not_set= 0;
- uint prev_no_subparts_not_set= no_subparts + 1;
+ uint num_parts_not_set= 0;
+ uint prev_num_subparts_not_set= num_subparts + 1;
do
{
partition_element *part_elem= part_it++;
-#ifdef HAVE_READLINK
- if (!my_use_symdir || (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE))
-#endif
- {
- if (part_elem->data_file_name)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
- "DATA DIRECTORY");
- if (part_elem->index_file_name)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
- "INDEX DIRECTORY");
- part_elem->data_file_name= part_elem->index_file_name= NULL;
- }
+ warn_if_dir_in_part_elem(thd, part_elem);
if (!is_sub_partitioned())
{
if (part_elem->engine_type == NULL)
{
- no_parts_not_set++;
+ num_parts_not_set++;
part_elem->engine_type= default_engine_type;
}
if (check_table_name(part_elem->partition_name,
@@ -985,12 +1225,13 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
else
{
uint j= 0;
- uint no_subparts_not_set= 0;
+ uint num_subparts_not_set= 0;
List_iterator<partition_element> sub_it(part_elem->subpartitions);
partition_element *sub_elem;
do
{
sub_elem= sub_it++;
+ warn_if_dir_in_part_elem(thd, sub_elem);
if (check_table_name(sub_elem->partition_name,
strlen(sub_elem->partition_name), FALSE))
{
@@ -1004,44 +1245,45 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
else
{
sub_elem->engine_type= default_engine_type;
- no_subparts_not_set++;
+ num_subparts_not_set++;
}
}
DBUG_PRINT("info", ("part = %d sub = %d engine = %s", i, j,
ha_resolve_storage_engine_name(sub_elem->engine_type)));
- } while (++j < no_subparts);
+ } while (++j < num_subparts);
- if (prev_no_subparts_not_set == (no_subparts + 1) &&
- (no_subparts_not_set == 0 || no_subparts_not_set == no_subparts))
- prev_no_subparts_not_set= no_subparts_not_set;
+ if (prev_num_subparts_not_set == (num_subparts + 1) &&
+ (num_subparts_not_set == 0 ||
+ num_subparts_not_set == num_subparts))
+ prev_num_subparts_not_set= num_subparts_not_set;
if (!table_engine_set &&
- prev_no_subparts_not_set != no_subparts_not_set)
+ prev_num_subparts_not_set != num_subparts_not_set)
{
- DBUG_PRINT("info", ("no_subparts_not_set = %u no_subparts = %u",
- no_subparts_not_set, no_subparts));
+ DBUG_PRINT("info", ("num_subparts_not_set = %u num_subparts = %u",
+ num_subparts_not_set, num_subparts));
my_error(ER_MIX_HANDLER_ERROR, MYF(0));
goto end;
}
if (part_elem->engine_type == NULL)
{
- if (no_subparts_not_set == 0)
+ if (num_subparts_not_set == 0)
part_elem->engine_type= sub_elem->engine_type;
else
{
- no_parts_not_set++;
+ num_parts_not_set++;
part_elem->engine_type= default_engine_type;
}
}
}
- } while (++i < no_parts);
+ } while (++i < num_parts);
if (!table_engine_set &&
- no_parts_not_set != 0 &&
- no_parts_not_set != no_parts)
+ num_parts_not_set != 0 &&
+ num_parts_not_set != num_parts)
{
- DBUG_PRINT("info", ("no_parts_not_set = %u no_parts = %u",
- no_parts_not_set, no_subparts));
+ DBUG_PRINT("info", ("num_parts_not_set = %u num_parts = %u",
+ num_parts_not_set, num_subparts));
my_error(ER_MIX_HANDLER_ERROR, MYF(0));
goto end;
}
@@ -1064,10 +1306,12 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
list constants.
*/
- if (fixed)
+ if (add_or_reorg_part)
{
- if (unlikely((part_type == RANGE_PARTITION && check_range_constants()) ||
- (part_type == LIST_PARTITION && check_list_constants())))
+ if (unlikely((part_type == RANGE_PARTITION &&
+ check_range_constants(thd)) ||
+ (part_type == LIST_PARTITION &&
+ check_list_constants(thd))))
goto end;
}
result= FALSE;
@@ -1086,32 +1330,113 @@ end:
RETURN VALUES
*/
-void partition_info::print_no_partition_found(TABLE *table)
+void partition_info::print_no_partition_found(TABLE *table_arg, myf errflag)
{
char buf[100];
char *buf_ptr= (char*)&buf;
TABLE_LIST table_list;
bzero(&table_list, sizeof(table_list));
- table_list.db= table->s->db.str;
- table_list.table_name= table->s->table_name.str;
+ table_list.db= table_arg->s->db.str;
+ table_list.table_name= table_arg->s->table_name.str;
if (check_single_table_access(current_thd,
SELECT_ACL, &table_list, TRUE))
+ {
my_message(ER_NO_PARTITION_FOR_GIVEN_VALUE,
- ER(ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT), MYF(0));
+ ER(ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT), errflag);
+ }
else
{
- my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
- if (part_expr->null_value)
- buf_ptr= (char*)"NULL";
+ if (column_list)
+ buf_ptr= (char*)"from column_list";
else
- longlong10_to_str(err_value, buf,
- part_expr->unsigned_flag ? 10 : -10);
- my_error(ER_NO_PARTITION_FOR_GIVEN_VALUE, MYF(0), buf_ptr);
- dbug_tmp_restore_column_map(table->read_set, old_map);
+ {
+ my_bitmap_map *old_map= dbug_tmp_use_all_columns(table_arg, table_arg->read_set);
+ if (part_expr->null_value)
+ buf_ptr= (char*)"NULL";
+ else
+ longlong10_to_str(err_value, buf,
+ part_expr->unsigned_flag ? 10 : -10);
+ dbug_tmp_restore_column_map(table_arg->read_set, old_map);
+ }
+ my_error(ER_NO_PARTITION_FOR_GIVEN_VALUE, errflag, buf_ptr);
}
}
+
+
+/*
+ Set fields related to partition expression
+ SYNOPSIS
+ set_part_expr()
+ start_token Start of partition function string
+ item_ptr Pointer to item tree
+ end_token End of partition function string
+ is_subpart Subpartition indicator
+ RETURN VALUES
+ TRUE Memory allocation error
+ FALSE Success
+*/
+
+bool partition_info::set_part_expr(char *start_token, Item *item_ptr,
+ char *end_token, bool is_subpart)
+{
+ uint expr_len= end_token - start_token;
+ char *func_string= (char*) sql_memdup(start_token, expr_len);
+
+ if (!func_string)
+ {
+ mem_alloc_error(expr_len);
+ return TRUE;
+ }
+ if (is_subpart)
+ {
+ list_of_subpart_fields= FALSE;
+ subpart_expr= item_ptr;
+ subpart_func_string= func_string;
+ subpart_func_len= expr_len;
+ }
+ else
+ {
+ list_of_part_fields= FALSE;
+ part_expr= item_ptr;
+ part_func_string= func_string;
+ part_func_len= expr_len;
+ }
+ return FALSE;
+}
+
+
+/*
+ Check that partition fields and subpartition fields are not too long
+
+ SYNOPSIS
+ check_partition_field_length()
+
+ RETURN VALUES
+ TRUE Total length was too big
+ FALSE Length is ok
+*/
+
+bool partition_info::check_partition_field_length()
+{
+ uint store_length= 0;
+ uint i;
+ DBUG_ENTER("partition_info::check_partition_field_length");
+
+ for (i= 0; i < num_part_fields; i++)
+ store_length+= get_partition_field_store_length(part_field_array[i]);
+ if (store_length > MAX_KEY_LENGTH)
+ DBUG_RETURN(TRUE);
+ store_length= 0;
+ for (i= 0; i < num_subpart_fields; i++)
+ store_length+= get_partition_field_store_length(subpart_field_array[i]);
+ if (store_length > MAX_KEY_LENGTH)
+ DBUG_RETURN(TRUE);
+ DBUG_RETURN(FALSE);
+}
+
+
/*
Set up buffers and arrays for fields requiring preparation
SYNOPSIS
@@ -1221,46 +1546,6 @@ bool partition_info::set_up_charset_field_preps()
}
subpart_charset_field_array[i]= NULL;
}
- if (tot_fields)
- {
- uint k;
- size= tot_fields*sizeof(char**);
- if (!(char_ptrs= (uchar**)sql_calloc(size)))
- goto error;
- full_part_field_buffers= char_ptrs;
- if (!(char_ptrs= (uchar**)sql_calloc(size)))
- goto error;
- restore_full_part_field_ptrs= char_ptrs;
- size= (tot_fields + 1) * sizeof(char**);
- if (!(char_ptrs= (uchar**)sql_calloc(size)))
- goto error;
- full_part_charset_field_array= (Field**)char_ptrs;
- for (i= 0; i < tot_part_fields; i++)
- {
- full_part_charset_field_array[i]= part_charset_field_array[i];
- full_part_field_buffers[i]= part_field_buffers[i];
- }
- k= tot_part_fields;
- for (i= 0; i < tot_subpart_fields; i++)
- {
- uint j;
- bool found= FALSE;
- field= subpart_charset_field_array[i];
-
- for (j= 0; j < tot_part_fields; j++)
- {
- if (field == part_charset_field_array[i])
- found= TRUE;
- }
- if (!found)
- {
- full_part_charset_field_array[k]= subpart_charset_field_array[i];
- full_part_field_buffers[k]= subpart_field_buffers[i];
- k++;
- }
- }
- full_part_charset_field_array[k]= NULL;
- }
DBUG_RETURN(FALSE);
error:
mem_alloc_error(size);
@@ -1322,4 +1607,965 @@ id_err:
}
+/**
+ Check what kind of error to report
+
+ @param use_subpart_expr Use the subpart_expr instead of part_expr
+ @param part_str Name of partition to report error (or NULL)
+*/
+void partition_info::report_part_expr_error(bool use_subpart_expr)
+{
+ Item *expr= part_expr;
+ DBUG_ENTER("partition_info::report_part_expr_error");
+ if (use_subpart_expr)
+ expr= subpart_expr;
+
+ if (expr->type() == Item::FIELD_ITEM)
+ {
+ partition_type type= part_type;
+ bool list_of_fields= list_of_part_fields;
+ Item_field *item_field= (Item_field*) expr;
+ /*
+ The expression consists of a single field.
+ It must be of integer type unless KEY or COLUMNS partitioning.
+ */
+ if (use_subpart_expr)
+ {
+ type= subpart_type;
+ list_of_fields= list_of_subpart_fields;
+ }
+ if (!column_list &&
+ item_field->field &&
+ item_field->field->result_type() != INT_RESULT &&
+ !(type == HASH_PARTITION && list_of_fields))
+ {
+ my_error(ER_FIELD_TYPE_NOT_ALLOWED_AS_PARTITION_FIELD, MYF(0),
+ item_field->name);
+ DBUG_VOID_RETURN;
+ }
+ }
+ if (use_subpart_expr)
+ my_error(ER_PARTITION_FUNC_NOT_ALLOWED_ERROR, MYF(0), "SUBPARTITION");
+ else
+ my_error(ER_PARTITION_FUNC_NOT_ALLOWED_ERROR, MYF(0), "PARTITION");
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Create a new column value in current list with maxvalue
+ Called from parser
+
+ SYNOPSIS
+ add_max_value()
+ RETURN
+ TRUE Error
+ FALSE Success
+*/
+
+int partition_info::add_max_value()
+{
+ DBUG_ENTER("partition_info::add_max_value");
+
+ part_column_list_val *col_val;
+ if (!(col_val= add_column_value()))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ col_val->max_value= TRUE;
+ DBUG_RETURN(FALSE);
+}
+
+/*
+ Create a new column value in current list
+ Called from parser
+
+ SYNOPSIS
+ add_column_value()
+ RETURN
+ >0 A part_column_list_val object which have been
+ inserted into its list
+ 0 Memory allocation failure
+*/
+
+part_column_list_val *partition_info::add_column_value()
+{
+ uint max_val= num_columns ? num_columns : MAX_REF_PARTS;
+ DBUG_ENTER("add_column_value");
+ DBUG_PRINT("enter", ("num_columns = %u, curr_list_object %u, max_val = %u",
+ num_columns, curr_list_object, max_val));
+ if (curr_list_object < max_val)
+ {
+ curr_list_val->added_items++;
+ DBUG_RETURN(&curr_list_val->col_val_array[curr_list_object++]);
+ }
+ if (!num_columns && part_type == LIST_PARTITION)
+ {
+ /*
+ We're trying to add more than MAX_REF_PARTS, this can happen
+ in ALTER TABLE using List partitions where the first partition
+ uses VALUES IN (1,2,3...,17) where the number of fields in
+ the list is more than MAX_REF_PARTS, in this case we know
+ that the number of columns must be 1 and we thus reorganize
+ into the structure used for 1 column. After this we call
+ ourselves recursively which should always succeed.
+ */
+ if (!reorganize_into_single_field_col_val())
+ {
+ DBUG_RETURN(add_column_value());
+ }
+ DBUG_RETURN(NULL);
+ }
+ if (column_list)
+ {
+ my_error(ER_PARTITION_COLUMN_LIST_ERROR, MYF(0));
+ }
+ else
+ {
+ if (part_type == RANGE_PARTITION)
+ my_error(ER_TOO_MANY_VALUES_ERROR, MYF(0), "RANGE");
+ else
+ my_error(ER_TOO_MANY_VALUES_ERROR, MYF(0), "LIST");
+ }
+ DBUG_RETURN(NULL);
+}
+
+
+/*
+ Initialise part_elem_value object at setting of a new object
+ (Helper functions to functions called by parser)
+
+ SYNOPSIS
+ init_col_val
+ col_val Column value object to be initialised
+ item Item object representing column value
+
+ RETURN VALUES
+ TRUE Failure
+ FALSE Success
+*/
+void partition_info::init_col_val(part_column_list_val *col_val, Item *item)
+{
+ DBUG_ENTER("partition_info::init_col_val");
+
+ col_val->item_expression= item;
+ col_val->null_value= item->null_value;
+ if (item->result_type() == INT_RESULT)
+ {
+ /*
+ This could be both column_list partitioning and function
+ partitioning, but it doesn't hurt to set the function
+ partitioning flags about unsignedness.
+ */
+ curr_list_val->value= item->val_int();
+ curr_list_val->unsigned_flag= TRUE;
+ if (!item->unsigned_flag &&
+ curr_list_val->value < 0)
+ curr_list_val->unsigned_flag= FALSE;
+ if (!curr_list_val->unsigned_flag)
+ curr_part_elem->signed_flag= TRUE;
+ }
+ col_val->part_info= NULL;
+ DBUG_VOID_RETURN;
+}
+/*
+ Add a column value in VALUES LESS THAN or VALUES IN
+ (Called from parser)
+
+ SYNOPSIS
+ add_column_list_value()
+ lex Parser's lex object
+ thd Thread object
+ item Item object representing column value
+
+ RETURN VALUES
+ TRUE Failure
+ FALSE Success
+*/
+bool partition_info::add_column_list_value(THD *thd, Item *item)
+{
+ part_column_list_val *col_val;
+ Name_resolution_context *context= &thd->lex->current_select->context;
+ TABLE_LIST *save_list= context->table_list;
+ const char *save_where= thd->where;
+ DBUG_ENTER("partition_info::add_column_list_value");
+
+ if (part_type == LIST_PARTITION &&
+ num_columns == 1U)
+ {
+ if (init_column_part())
+ {
+ DBUG_RETURN(TRUE);
+ }
+ }
+
+ context->table_list= 0;
+ if (column_list)
+ thd->where= "field list";
+ else
+ thd->where= "partition function";
+
+ if (item->walk(&Item::check_partition_func_processor, 0,
+ NULL))
+ {
+ my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ if (item->fix_fields(thd, (Item**)0) ||
+ ((context->table_list= save_list), FALSE) ||
+ (!item->const_item()))
+ {
+ context->table_list= save_list;
+ thd->where= save_where;
+ my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ thd->where= save_where;
+
+ if (!(col_val= add_column_value()))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ init_col_val(col_val, item);
+ DBUG_RETURN(FALSE);
+}
+
+/*
+ Initialise part_info object for receiving a set of column values
+ for a partition, called when parser reaches VALUES LESS THAN or
+ VALUES IN.
+
+ SYNOPSIS
+ init_column_part()
+ lex Parser's lex object
+
+ RETURN VALUES
+ TRUE Failure
+ FALSE Success
+*/
+bool partition_info::init_column_part()
+{
+ partition_element *p_elem= curr_part_elem;
+ part_column_list_val *col_val_array;
+ part_elem_value *list_val;
+ uint loc_num_columns;
+ DBUG_ENTER("partition_info::init_column_part");
+
+ if (!(list_val=
+ (part_elem_value*)sql_calloc(sizeof(part_elem_value))) ||
+ p_elem->list_val_list.push_back(list_val))
+ {
+ mem_alloc_error(sizeof(part_elem_value));
+ DBUG_RETURN(TRUE);
+ }
+ if (num_columns)
+ loc_num_columns= num_columns;
+ else
+ loc_num_columns= MAX_REF_PARTS;
+ if (!(col_val_array=
+ (part_column_list_val*)sql_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;
+ curr_list_object= 0;
+ DBUG_RETURN(FALSE);
+}
+
+/*
+ In the case of ALTER TABLE ADD/REORGANIZE PARTITION for LIST
+ partitions we can specify list values as:
+ VALUES IN (v1, v2,,,, v17) if we're using the first partitioning
+ variant with a function or a column list partitioned table with
+ one partition field. In this case the parser knows not the
+ number of columns start with and allocates MAX_REF_PARTS in the
+ array. If we try to allocate something beyond MAX_REF_PARTS we
+ will call this function to reorganize into a structure with
+ num_columns = 1. Also when the parser knows that we used LIST
+ partitioning and we used a VALUES IN like above where number of
+ values was smaller than MAX_REF_PARTS or equal, then we will
+ reorganize after discovering this in the parser.
+
+ SYNOPSIS
+ reorganize_into_single_field_col_val()
+
+ RETURN VALUES
+ TRUE Failure
+ FALSE Success
+*/
+int partition_info::reorganize_into_single_field_col_val()
+{
+ part_column_list_val *col_val, *new_col_val;
+ part_elem_value *val= curr_list_val;
+ uint loc_num_columns= num_columns;
+ uint i;
+ DBUG_ENTER("partition_info::reorganize_into_single_field_col_val");
+
+ num_columns= 1;
+ val->added_items= 1U;
+ col_val= &val->col_val_array[0];
+ init_col_val(col_val, col_val->item_expression);
+ for (i= 1; i < loc_num_columns; i++)
+ {
+ col_val= &val->col_val_array[i];
+ DBUG_ASSERT(part_type == LIST_PARTITION);
+ if (init_column_part())
+ {
+ DBUG_RETURN(TRUE);
+ }
+ if (!(new_col_val= add_column_value()))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ memcpy(new_col_val, col_val, sizeof(*col_val));
+ init_col_val(new_col_val, col_val->item_expression);
+ }
+ curr_list_val= val;
+ DBUG_RETURN(FALSE);
+}
+
+/*
+ This function handles the case of function-based partitioning.
+ It fixes some data structures created in the parser and puts
+ them in the format required by the rest of the partitioning
+ code.
+
+ SYNOPSIS
+ fix_partition_values()
+ thd Thread object
+ col_val Array of one value
+ part_elem The partition instance
+ part_id Id of partition instance
+
+ RETURN VALUES
+ TRUE Failure
+ FALSE Success
+*/
+int partition_info::fix_partition_values(THD *thd,
+ part_elem_value *val,
+ partition_element *part_elem,
+ uint part_id)
+{
+ part_column_list_val *col_val= val->col_val_array;
+ DBUG_ENTER("partition_info::fix_partition_values");
+
+ if (col_val->fixed)
+ {
+ DBUG_RETURN(FALSE);
+ }
+ if (val->added_items != 1)
+ {
+ my_error(ER_PARTITION_COLUMN_LIST_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ if (col_val->max_value)
+ {
+ /* The parser ensures we're not LIST partitioned here */
+ DBUG_ASSERT(part_type == RANGE_PARTITION);
+ if (defined_max_value)
+ {
+ my_error(ER_PARTITION_MAXVALUE_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ if (part_id == (num_parts - 1))
+ {
+ defined_max_value= TRUE;
+ part_elem->max_value= TRUE;
+ part_elem->range_value= LONGLONG_MAX;
+ }
+ else
+ {
+ my_error(ER_PARTITION_MAXVALUE_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ }
+ else
+ {
+ Item *item_expr= col_val->item_expression;
+ if ((val->null_value= item_expr->null_value))
+ {
+ if (part_elem->has_null_value)
+ {
+ my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ part_elem->has_null_value= TRUE;
+ }
+ else if (item_expr->result_type() != INT_RESULT)
+ {
+ my_error(ER_VALUES_IS_NOT_INT_TYPE_ERROR, MYF(0),
+ part_elem->partition_name);
+ DBUG_RETURN(TRUE);
+ }
+ if (part_type == RANGE_PARTITION)
+ {
+ if (part_elem->has_null_value)
+ {
+ my_error(ER_NULL_IN_VALUES_LESS_THAN, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ part_elem->range_value= val->value;
+ }
+ }
+ col_val->fixed= 2;
+ DBUG_RETURN(FALSE);
+}
+
+/*
+ Get column item with a proper character set according to the field
+
+ SYNOPSIS
+ get_column_item()
+ item Item object to start with
+ field Field for which the item will be compared to
+
+ RETURN VALUES
+ NULL Error
+ item Returned item
+*/
+
+Item* partition_info::get_column_item(Item *item, Field *field)
+{
+ if (field->result_type() == STRING_RESULT &&
+ item->collation.collation != field->charset())
+ {
+ if (!(item= convert_charset_partition_constant(item,
+ field->charset())))
+ {
+ my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
+ return NULL;
+ }
+ }
+ return item;
+}
+
+
+/*
+ Evaluate VALUES functions for column list values
+ SYNOPSIS
+ fix_column_value_functions()
+ thd Thread object
+ col_val List of column values
+ part_id Partition id we are fixing
+
+ RETURN VALUES
+ TRUE Error
+ FALSE Success
+ DESCRIPTION
+ Fix column VALUES and store in memory array adapted to the data type
+*/
+
+bool partition_info::fix_column_value_functions(THD *thd,
+ part_elem_value *val,
+ uint part_id)
+{
+ uint n_columns= part_field_list.elements;
+ bool result= FALSE;
+ uint i;
+ part_column_list_val *col_val= val->col_val_array;
+ DBUG_ENTER("partition_info::fix_column_value_functions");
+
+ if (col_val->fixed > 1)
+ {
+ DBUG_RETURN(FALSE);
+ }
+ for (i= 0; i < n_columns; col_val++, i++)
+ {
+ Item *column_item= col_val->item_expression;
+ Field *field= part_field_array[i];
+ col_val->part_info= this;
+ col_val->partition_id= part_id;
+ if (col_val->max_value)
+ col_val->column_value= NULL;
+ else
+ {
+ col_val->column_value= NULL;
+ if (!col_val->null_value)
+ {
+ uchar *val_ptr;
+ uint len= field->pack_length();
+ ulonglong save_sql_mode;
+ bool save_got_warning;
+
+ if (!(column_item= get_column_item(column_item,
+ field)))
+ {
+ result= TRUE;
+ goto end;
+ }
+ save_sql_mode= thd->variables.sql_mode;
+ thd->variables.sql_mode= 0;
+ save_got_warning= thd->got_warning;
+ thd->got_warning= 0;
+ if (column_item->save_in_field(field, TRUE) ||
+ thd->got_warning)
+ {
+ my_error(ER_WRONG_TYPE_COLUMN_VALUE_ERROR, MYF(0));
+ result= TRUE;
+ goto end;
+ }
+ thd->got_warning= save_got_warning;
+ thd->variables.sql_mode= save_sql_mode;
+ if (!(val_ptr= (uchar*) sql_calloc(len)))
+ {
+ mem_alloc_error(len);
+ result= TRUE;
+ goto end;
+ }
+ col_val->column_value= val_ptr;
+ memcpy(val_ptr, field->ptr, len);
+ }
+ }
+ col_val->fixed= 2;
+ }
+end:
+ DBUG_RETURN(result);
+}
+
+/*
+ The parser generates generic data structures, we need to set them up
+ as the rest of the code expects to find them. This is in reality part
+ of the syntax check of the parser code.
+
+ It is necessary to call this function in the case of a CREATE TABLE
+ statement, in this case we do it early in the check_partition_info
+ function.
+
+ It is necessary to call this function for ALTER TABLE where we
+ assign a completely new partition structure, in this case we do it
+ in prep_alter_part_table after discovering that the partition
+ structure is entirely redefined.
+
+ It's necessary to call this method also for ALTER TABLE ADD/REORGANIZE
+ of partitions, in this we call it in prep_alter_part_table after
+ making some initial checks but before going deep to check the partition
+ info, we also assign the column_list variable before calling this function
+ here.
+
+ Finally we also call it immediately after returning from parsing the
+ partitioning text found in the frm file.
+
+ This function mainly fixes the VALUES parts, these are handled differently
+ whether or not we use column list partitioning. Since the parser doesn't
+ know which we are using we need to set-up the old data structures after
+ the parser is complete when we know if what type of partitioning the
+ base table is using.
+
+ For column lists we will handle this in the fix_column_value_function.
+ For column lists it is sufficient to verify that the number of columns
+ and number of elements are in synch with each other. So only partitioning
+ using functions need to be set-up to their data structures.
+
+ SYNOPSIS
+ fix_parser_data()
+ thd Thread object
+
+ RETURN VALUES
+ TRUE Failure
+ FALSE Success
+*/
+
+int partition_info::fix_parser_data(THD *thd)
+{
+ List_iterator<partition_element> it(partitions);
+ partition_element *part_elem;
+ uint num_elements;
+ uint i= 0, j, k;
+ DBUG_ENTER("partition_info::fix_parser_data");
+
+ if (!(part_type == RANGE_PARTITION ||
+ part_type == LIST_PARTITION))
+ {
+ if (part_type == HASH_PARTITION && list_of_part_fields)
+ {
+ /* KEY partitioning, check ALGORITHM = N. Should not pass the parser! */
+ if (key_algorithm > KEY_ALGORITHM_55)
+ {
+ my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
+ DBUG_RETURN(true);
+ }
+ /* If not set, use DEFAULT = 2 for CREATE and ALTER! */
+ if ((thd_sql_command(thd) == SQLCOM_CREATE_TABLE ||
+ thd_sql_command(thd) == SQLCOM_ALTER_TABLE) &&
+ key_algorithm == KEY_ALGORITHM_NONE)
+ key_algorithm= KEY_ALGORITHM_55;
+ }
+ DBUG_RETURN(FALSE);
+ }
+ if (is_sub_partitioned() && list_of_subpart_fields)
+ {
+ /* KEY subpartitioning, check ALGORITHM = N. Should not pass the parser! */
+ if (key_algorithm > KEY_ALGORITHM_55)
+ {
+ my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
+ DBUG_RETURN(true);
+ }
+ /* If not set, use DEFAULT = 2 for CREATE and ALTER! */
+ if ((thd_sql_command(thd) == SQLCOM_CREATE_TABLE ||
+ thd_sql_command(thd) == SQLCOM_ALTER_TABLE) &&
+ key_algorithm == KEY_ALGORITHM_NONE)
+ key_algorithm= KEY_ALGORITHM_55;
+ }
+ do
+ {
+ part_elem= it++;
+ List_iterator<part_elem_value> list_val_it(part_elem->list_val_list);
+ num_elements= part_elem->list_val_list.elements;
+ DBUG_ASSERT(part_type == RANGE_PARTITION ?
+ num_elements == 1U : TRUE);
+ for (j= 0; j < num_elements; j++)
+ {
+ part_elem_value *val= list_val_it++;
+ if (column_list)
+ {
+ if (val->added_items != num_columns)
+ {
+ my_error(ER_PARTITION_COLUMN_LIST_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ for (k= 0; k < num_columns; k++)
+ {
+ part_column_list_val *col_val= &val->col_val_array[k];
+ if (col_val->null_value && part_type == RANGE_PARTITION)
+ {
+ my_error(ER_NULL_IN_VALUES_LESS_THAN, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ }
+ }
+ else
+ {
+ if (fix_partition_values(thd, val, part_elem, i))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ if (val->null_value)
+ {
+ /*
+ Null values aren't required in the value part, they are kept per
+ partition instance, only LIST partitions have NULL values.
+ */
+ list_val_it.remove();
+ }
+ }
+ }
+ } while (++i < num_parts);
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ helper function to compare strings that can also be
+ a NULL pointer.
+
+ @param a char pointer (can be NULL).
+ @param b char pointer (can be NULL).
+
+ @return false if equal
+ @retval true strings differs
+ @retval false strings is equal
+*/
+
+static bool strcmp_null(const char *a, const char *b)
+{
+ if (!a && !b)
+ return false;
+ if (a && b && !strcmp(a, b))
+ return false;
+ return true;
+}
+
+
+/**
+ Check if the new part_info has the same partitioning.
+
+ @param new_part_info New partition definition to compare with.
+
+ @return True if not considered to have changed the partitioning.
+ @retval true Allowed change (only .frm change, compatible distribution).
+ @retval false Different partitioning, will need redistribution of rows.
+
+ @note Currently only used to allow changing from non-set key_algorithm
+ to a specified key_algorithm, to avoid rebuild when upgrading from 5.1 of
+ 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.
+*/
+
+bool partition_info::has_same_partitioning(partition_info *new_part_info)
+{
+ DBUG_ENTER("partition_info::has_same_partitioning");
+
+ DBUG_ASSERT(part_field_array && part_field_array[0]);
+
+ /*
+ Only consider pre 5.5.3 .frm's to have same partitioning as
+ a new one with KEY ALGORITHM = 1 ().
+ */
+
+ if (part_field_array[0]->table->s->mysql_version >= 50503)
+ DBUG_RETURN(false);
+
+ if (!new_part_info ||
+ part_type != new_part_info->part_type ||
+ num_parts != new_part_info->num_parts ||
+ use_default_partitions != new_part_info->use_default_partitions ||
+ new_part_info->is_sub_partitioned() != is_sub_partitioned())
+ DBUG_RETURN(false);
+
+ if (part_type != HASH_PARTITION)
+ {
+ /*
+ RANGE or LIST partitioning, check if KEY subpartitioned.
+ Also COLUMNS partitioning was added in 5.5, so treat that as different.
+ */
+ if (!is_sub_partitioned() ||
+ !new_part_info->is_sub_partitioned() ||
+ column_list ||
+ new_part_info->column_list ||
+ !list_of_subpart_fields ||
+ !new_part_info->list_of_subpart_fields ||
+ new_part_info->num_subparts != num_subparts ||
+ new_part_info->subpart_field_list.elements !=
+ subpart_field_list.elements ||
+ new_part_info->use_default_subpartitions !=
+ use_default_subpartitions)
+ DBUG_RETURN(false);
+ }
+ else
+ {
+ /* Check if KEY partitioned. */
+ if (!new_part_info->list_of_part_fields ||
+ !list_of_part_fields ||
+ new_part_info->part_field_list.elements != part_field_list.elements)
+ DBUG_RETURN(false);
+ }
+
+ /* 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;
+ while ((old_name= old_field_name_it++))
+ {
+ new_name= new_field_name_it++;
+ if (!new_name || my_strcasecmp(system_charset_info,
+ new_name,
+ old_name))
+ DBUG_RETURN(false);
+ }
+
+ 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;
+ while ((old_name= old_field_name_it++))
+ {
+ new_name= new_field_name_it++;
+ if (!new_name || my_strcasecmp(system_charset_info,
+ new_name,
+ old_name))
+ DBUG_RETURN(false);
+ }
+ }
+
+ if (!use_default_partitions)
+ {
+ /*
+ Loop over partitions/subpartition to verify that they are
+ the same, including state and name.
+ */
+ List_iterator<partition_element> part_it(partitions);
+ List_iterator<partition_element> new_part_it(new_part_info->partitions);
+ uint i= 0;
+ do
+ {
+ partition_element *part_elem= part_it++;
+ partition_element *new_part_elem= new_part_it++;
+ /*
+ The following must match:
+ partition_name, tablespace_name, data_file_name, index_file_name,
+ engine_type, part_max_rows, part_min_rows, nodegroup_id.
+ (max_value, signed_flag, has_null_value only on partition level,
+ RANGE/LIST)
+ The following can differ:
+ - part_comment
+ part_state must be PART_NORMAL!
+ */
+ if (!part_elem || !new_part_elem ||
+ strcmp(part_elem->partition_name,
+ new_part_elem->partition_name) ||
+ part_elem->part_state != PART_NORMAL ||
+ new_part_elem->part_state != PART_NORMAL ||
+ part_elem->max_value != new_part_elem->max_value ||
+ part_elem->signed_flag != new_part_elem->signed_flag ||
+ part_elem->has_null_value != new_part_elem->has_null_value)
+ DBUG_RETURN(false);
+
+ /* new_part_elem may not have engine_type set! */
+ if (new_part_elem->engine_type &&
+ part_elem->engine_type != new_part_elem->engine_type)
+ DBUG_RETURN(false);
+
+ if (is_sub_partitioned())
+ {
+ /*
+ Check that both old and new partition has the same definition
+ (VALUES IN/VALUES LESS THAN) (No COLUMNS partitioning, see above)
+ */
+ if (part_type == LIST_PARTITION)
+ {
+ List_iterator<part_elem_value> list_vals(part_elem->list_val_list);
+ List_iterator<part_elem_value>
+ new_list_vals(new_part_elem->list_val_list);
+ part_elem_value *val;
+ part_elem_value *new_val;
+ while ((val= list_vals++))
+ {
+ new_val= new_list_vals++;
+ if (!new_val)
+ DBUG_RETURN(false);
+ if ((!val->null_value && !new_val->null_value) &&
+ val->value != new_val->value)
+ DBUG_RETURN(false);
+ }
+ if (new_list_vals++)
+ DBUG_RETURN(false);
+ }
+ else
+ {
+ DBUG_ASSERT(part_type == RANGE_PARTITION);
+ if (new_part_elem->range_value != part_elem->range_value)
+ DBUG_RETURN(false);
+ }
+
+ if (!use_default_subpartitions)
+ {
+ List_iterator<partition_element>
+ sub_part_it(part_elem->subpartitions);
+ List_iterator<partition_element>
+ new_sub_part_it(new_part_elem->subpartitions);
+ uint j= 0;
+ do
+ {
+ partition_element *sub_part_elem= sub_part_it++;
+ partition_element *new_sub_part_elem= new_sub_part_it++;
+ /* new_part_elem may not have engine_type set! */
+ if (new_sub_part_elem->engine_type &&
+ sub_part_elem->engine_type != new_sub_part_elem->engine_type)
+ DBUG_RETURN(false);
+
+ if (strcmp(sub_part_elem->partition_name,
+ new_sub_part_elem->partition_name) ||
+ sub_part_elem->part_state != PART_NORMAL ||
+ new_sub_part_elem->part_state != PART_NORMAL ||
+ sub_part_elem->part_min_rows !=
+ new_sub_part_elem->part_min_rows ||
+ sub_part_elem->part_max_rows !=
+ new_sub_part_elem->part_max_rows ||
+ sub_part_elem->nodegroup_id !=
+ new_sub_part_elem->nodegroup_id)
+ DBUG_RETURN(false);
+
+ if (strcmp_null(sub_part_elem->data_file_name,
+ new_sub_part_elem->data_file_name) ||
+ strcmp_null(sub_part_elem->index_file_name,
+ new_sub_part_elem->index_file_name) ||
+ strcmp_null(sub_part_elem->tablespace_name,
+ new_sub_part_elem->tablespace_name))
+ DBUG_RETURN(false);
+
+ } while (++j < num_subparts);
+ }
+ }
+ else
+ {
+ if (part_elem->part_min_rows != new_part_elem->part_min_rows ||
+ part_elem->part_max_rows != new_part_elem->part_max_rows ||
+ part_elem->nodegroup_id != new_part_elem->nodegroup_id)
+ DBUG_RETURN(false);
+
+ if (strcmp_null(part_elem->data_file_name,
+ new_part_elem->data_file_name) ||
+ strcmp_null(part_elem->index_file_name,
+ new_part_elem->index_file_name) ||
+ strcmp_null(part_elem->tablespace_name,
+ new_part_elem->tablespace_name))
+ DBUG_RETURN(false);
+ }
+ } while (++i < num_parts);
+ }
+
+ /*
+ Only if key_algorithm was not specified before and it is now set,
+ consider this as nothing was changed, and allow change without rebuild!
+ */
+ if (key_algorithm != partition_info::KEY_ALGORITHM_NONE ||
+ new_part_info->key_algorithm == partition_info::KEY_ALGORITHM_NONE)
+ DBUG_RETURN(false);
+
+ DBUG_RETURN(true);
+}
+
+
+void partition_info::print_debug(const char *str, uint *value)
+{
+ DBUG_ENTER("print_debug");
+ if (value)
+ DBUG_PRINT("info", ("parser: %s, val = %u", str, *value));
+ else
+ DBUG_PRINT("info", ("parser: %s", str));
+ DBUG_VOID_RETURN;
+}
+#else /* WITH_PARTITION_STORAGE_ENGINE */
+ /*
+ For builds without partitioning we need to define these functions
+ since we they are called from the parser. The parser cannot
+ remove code parts using ifdef, but the code parts cannot be called
+ so we simply need to add empty functions to make the linker happy.
+ */
+part_column_list_val *partition_info::add_column_value()
+{
+ return NULL;
+}
+
+bool partition_info::set_part_expr(char *start_token, Item *item_ptr,
+ char *end_token, bool is_subpart)
+{
+ (void)start_token;
+ (void)item_ptr;
+ (void)end_token;
+ (void)is_subpart;
+ return FALSE;
+}
+
+int partition_info::reorganize_into_single_field_col_val()
+{
+ return 0;
+}
+
+bool partition_info::init_column_part()
+{
+ return FALSE;
+}
+
+bool partition_info::add_column_list_value(THD *thd, Item *item)
+{
+ return FALSE;
+}
+int partition_info::add_max_value()
+{
+ return 0;
+}
+
+void partition_info::print_debug(const char *str, uint *value)
+{
+}
+
#endif /* WITH_PARTITION_STORAGE_ENGINE */
diff --git a/sql/partition_info.h b/sql/partition_info.h
index a5040983902..cff941a858a 100644
--- a/sql/partition_info.h
+++ b/sql/partition_info.h
@@ -1,6 +1,7 @@
-/*
- Copyright (c) 2006-2008 MySQL AB, 2008, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+#ifndef PARTITION_INFO_INCLUDED
+#define PARTITION_INFO_INCLUDED
+
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,8 +14,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifdef USE_PRAGMA_INTERFACE
#pragma interface /* gcc class implementation */
@@ -67,10 +67,9 @@ public:
/*
When we have various string fields we might need some preparation
before and clean-up after calling the get_part_id_func's. We need
- one such method for get_partition_id and one for
- get_part_partition_id and one for get_subpartition_id.
+ one such method for get_part_partition_id and one for
+ get_subpartition_id.
*/
- get_part_id_func get_partition_id_charset;
get_part_id_func get_part_partition_id_charset;
get_subpart_id_func get_subpartition_id_charset;
@@ -84,7 +83,6 @@ public:
without duplicates, NULL-terminated.
*/
Field **full_part_field_array;
- Field **full_part_charset_field_array;
/*
Set of all fields used in partition and subpartition expression.
Required for testing of partition fields in write_set when
@@ -100,10 +98,8 @@ public:
*/
uchar **part_field_buffers;
uchar **subpart_field_buffers;
- uchar **full_part_field_buffers;
uchar **restore_part_field_ptrs;
uchar **restore_subpart_field_ptrs;
- uchar **restore_full_part_field_ptrs;
Item *part_expr;
Item *subpart_expr;
@@ -127,6 +123,8 @@ public:
union {
longlong *range_int_array;
LIST_PART_ENTRY *list_array;
+ part_column_list_val *range_col_array;
+ part_column_list_val *list_col_array;
};
/********************************************
@@ -153,10 +151,12 @@ public:
char *part_func_string;
char *subpart_func_string;
- const char *part_state;
-
partition_element *curr_part_elem;
partition_element *current_partition;
+ part_elem_value *curr_list_val;
+ uint curr_list_object;
+ uint num_columns;
+
/*
These key_map's are used for Partitioning to enable quick decisions
on whether we can derive more information about which partition to
@@ -166,26 +166,24 @@ public:
key_map some_fields_in_PF;
handlerton *default_engine_type;
- Item_result part_result_type;
partition_type part_type;
partition_type subpart_type;
uint part_info_len;
- uint part_state_len;
uint part_func_len;
uint subpart_func_len;
- uint no_parts;
- uint no_subparts;
+ uint num_parts;
+ uint num_subparts;
uint count_curr_subparts;
uint part_error_code;
- uint no_list_values;
+ uint num_list_values;
- uint no_part_fields;
- uint no_subpart_fields;
- uint no_full_part_fields;
+ uint num_part_fields;
+ uint num_subpart_fields;
+ uint num_full_part_fields;
uint has_null_part_id;
/*
@@ -194,11 +192,24 @@ public:
but mainly of use to handlers supporting partitioning.
*/
uint16 linear_hash_mask;
+ /*
+ PARTITION BY KEY ALGORITHM=N
+ Which algorithm to use for hashing the fields.
+ N = 1 - Use 5.1 hashing (numeric fields are hashed as binary)
+ N = 2 - Use 5.5 hashing (numeric fields are hashed like latin1 bytes)
+ */
+ enum enum_key_algorithm
+ {
+ KEY_ALGORITHM_NONE= 0,
+ KEY_ALGORITHM_51= 1,
+ KEY_ALGORITHM_55= 2
+ };
+ enum_key_algorithm key_algorithm;
bool use_default_partitions;
- bool use_default_no_partitions;
+ bool use_default_num_partitions;
bool use_default_subpartitions;
- bool use_default_no_subpartitions;
+ bool use_default_num_subpartitions;
bool default_partitions_setup;
bool defined_max_value;
bool list_of_part_fields;
@@ -208,7 +219,7 @@ public:
bool is_auto_partitioned;
bool from_openfrm;
bool has_null_value;
-
+ bool column_list;
partition_info()
: get_partition_id(NULL), get_part_partition_id(NULL),
@@ -217,34 +228,31 @@ public:
part_charset_field_array(NULL),
subpart_charset_field_array(NULL),
full_part_field_array(NULL),
- full_part_charset_field_array(NULL),
part_field_buffers(NULL), subpart_field_buffers(NULL),
- full_part_field_buffers(NULL),
restore_part_field_ptrs(NULL), restore_subpart_field_ptrs(NULL),
- restore_full_part_field_ptrs(NULL),
part_expr(NULL), subpart_expr(NULL), item_free_list(NULL),
first_log_entry(NULL), exec_log_entry(NULL), frm_log_entry(NULL),
list_array(NULL), err_value(0),
part_info_string(NULL),
part_func_string(NULL), subpart_func_string(NULL),
- part_state(NULL),
curr_part_elem(NULL), current_partition(NULL),
+ curr_list_object(0), num_columns(0),
default_engine_type(NULL),
- part_result_type(INT_RESULT),
part_type(NOT_A_PARTITION), subpart_type(NOT_A_PARTITION),
- part_info_len(0), part_state_len(0),
+ part_info_len(0),
part_func_len(0), subpart_func_len(0),
- no_parts(0), no_subparts(0),
+ num_parts(0), num_subparts(0),
count_curr_subparts(0), part_error_code(0),
- no_list_values(0), no_part_fields(0), no_subpart_fields(0),
- no_full_part_fields(0), has_null_part_id(0), linear_hash_mask(0),
- use_default_partitions(TRUE), use_default_no_partitions(TRUE),
- use_default_subpartitions(TRUE), use_default_no_subpartitions(TRUE),
+ num_list_values(0), num_part_fields(0), num_subpart_fields(0),
+ num_full_part_fields(0), has_null_part_id(0), linear_hash_mask(0),
+ key_algorithm(KEY_ALGORITHM_NONE),
+ use_default_partitions(TRUE), use_default_num_partitions(TRUE),
+ use_default_subpartitions(TRUE), use_default_num_subpartitions(TRUE),
default_partitions_setup(FALSE), defined_max_value(FALSE),
list_of_part_fields(FALSE), list_of_subpart_fields(FALSE),
linear_hash_ind(FALSE), fixed(FALSE),
is_auto_partitioned(FALSE), from_openfrm(FALSE),
- has_null_value(FALSE)
+ has_null_value(FALSE), column_list(FALSE)
{
all_fields_in_PF.clear_all();
all_fields_in_PPF.clear_all();
@@ -267,27 +275,50 @@ public:
/* Returns the total number of partitions on the leaf level */
uint get_tot_partitions()
{
- return no_parts * (is_sub_partitioned() ? no_subparts : 1);
+ return num_parts * (is_sub_partitioned() ? num_subparts : 1);
}
bool set_up_defaults_for_partitioning(handler *file, HA_CREATE_INFO *info,
uint start_no);
+ char *has_unique_fields();
char *has_unique_names();
bool check_engine_mix(handlerton *engine_type, bool default_engine);
- bool check_range_constants();
- bool check_list_constants();
+ 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);
- void print_no_partition_found(TABLE *table);
+ void print_no_partition_found(TABLE *table, myf errflag);
+ void print_debug(const char *str, uint*);
+ Item* get_column_item(Item *item, Field *field);
+ int fix_partition_values(THD *thd,
+ part_elem_value *val,
+ partition_element *part_elem,
+ uint part_id);
+ bool fix_column_value_functions(THD *thd,
+ part_elem_value *val,
+ uint part_id);
+ int fix_parser_data(THD *thd);
+ int add_max_value();
+ void init_col_val(part_column_list_val *col_val, Item *item);
+ int reorganize_into_single_field_col_val();
+ part_column_list_val *add_column_value();
+ bool set_part_expr(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_up_charset_field_preps();
+ bool check_partition_field_length();
+ bool init_column_part();
+ bool add_column_list_value(THD *thd, Item *item);
+ void set_show_version_string(String *packet);
+ void report_part_expr_error(bool use_subpart_expr);
+ bool has_same_partitioning(partition_info *new_part_info);
private:
static int list_part_cmp(const void* a, const void* b);
- static int list_part_cmp_unsigned(const void* a, const void* b);
bool set_up_default_partitions(handler *file, HA_CREATE_INFO *info,
uint start_no);
bool set_up_default_subpartitions(handler *file, HA_CREATE_INFO *info);
- char *create_default_partition_names(uint part_no, uint no_parts,
+ char *create_default_partition_names(uint part_no, uint num_parts,
uint start_no);
char *create_subpartition_name(uint subpart_no, const char *part_name);
bool has_unique_name(partition_element *element);
@@ -313,7 +344,9 @@ void init_all_partitions_iterator(partition_info *part_info,
PARTITION_ITERATOR *part_iter)
{
part_iter->part_nums.start= part_iter->part_nums.cur= 0;
- part_iter->part_nums.end= part_info->no_parts;
+ part_iter->part_nums.end= part_info->num_parts;
part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE;
part_iter->get_next= get_next_partition_id_range;
}
+
+#endif /* PARTITION_INFO_INCLUDED */
diff --git a/sql/password.c b/sql/password.c
index 5f1e962a529..947620ddf7a 100644
--- a/sql/password.c
+++ b/sql/password.c
@@ -1,5 +1,6 @@
/*
- Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2000, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2012, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +13,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* password checking routines */
/*****************************************************************************
@@ -60,6 +60,7 @@
*****************************************************************************/
+#include <password.h>
#include <my_global.h>
#include <my_sys.h>
#include <m_string.h>
diff --git a/sql/procedure.cc b/sql/procedure.cc
index 71df120cda1..bdaced20586 100644
--- a/sql/procedure.cc
+++ b/sql/procedure.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000-2002, 2004-2007 MySQL AB
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,7 +11,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* Procedures (functions with changes output of select) */
@@ -20,7 +20,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
#include "procedure.h"
#include "sql_analyse.h" // Includes procedure
#ifdef USE_PROC_RANGE
diff --git a/sql/procedure.h b/sql/procedure.h
index 43156c6abee..6870b97de57 100644
--- a/sql/procedure.h
+++ b/sql/procedure.h
@@ -1,4 +1,7 @@
-/* Copyright (c) 2000, 2002-2008 MySQL AB
+#ifndef PROCEDURE_INCLUDED
+#define PROCEDURE_INCLUDED
+
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,10 +14,7 @@
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
-
-#ifndef PROCEDURE_INCLUDED
-#define PROCEDURE_INCLUDED
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* When using sql procedures */
@@ -22,10 +22,18 @@
#pragma interface /* gcc class implementation */
#endif
+/*
+ It is necessary to include set_var.h instead of item.h because there
+ are dependencies on include order for set_var.h and item.h. This
+ will be resolved later.
+*/
+#include "sql_class.h" /* select_result, set_var.h: THD */
+#include "set_var.h" /* Item */
+
#define PROC_NO_SORT 1 /**< Bits in flags */
#define PROC_GROUP 2 /**< proc must have group */
-/* Procedure items used by procedures to store values for send_fields */
+/* Procedure items used by procedures to store values for send_result_set_metadata */
class Item_proc :public Item
{
diff --git a/sql/protocol.cc b/sql/protocol.cc
index f7420c64a7c..ac9fb1e9384 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -12,8 +12,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@file
@@ -26,17 +25,21 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h" // REQUIRED: for other includes
+#include "protocol.h"
+#include "sql_class.h" // THD
#include <stdarg.h>
static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024;
/* Declared non-static only because of the embedded library. */
-bool net_send_error_packet(THD *thd, uint sql_errno, const char *err);
-bool net_send_ok(THD *, uint, uint, ha_rows, ulonglong, const char *);
-bool net_send_eof(THD *thd, uint server_status, uint total_warn_count);
+bool net_send_error_packet(THD *, uint, const char *, const char *);
+/* Declared non-static only because of the embedded library. */
+bool net_send_ok(THD *, uint, uint, ulonglong, ulonglong, const char *);
+/* Declared non-static only because of the embedded library. */
+bool net_send_eof(THD *thd, uint server_status, uint statement_warn_count);
#ifndef EMBEDDED_LIBRARY
-static bool write_eof_packet(THD *thd, NET *net,
- uint server_status, uint total_warn_count);
+static bool write_eof_packet(THD *, NET *, uint, uint);
#endif
#ifndef EMBEDDED_LIBRARY
@@ -61,26 +64,28 @@ bool Protocol_binary::net_store_data(const uchar *from, size_t length)
/*
- net_store_data() - extended version with character set conversion.
+ net_store_data_cs() - extended version with character set conversion.
It is optimized for short strings whose length after
conversion is garanteed to be less than 251, which accupies
exactly one byte to store length. It allows not to use
the "convert" member as a temporary buffer, conversion
is done directly to the "packet" member.
- The limit 251 is good enough to optimize send_fields()
+ The limit 251 is good enough to optimize send_result_set_metadata()
because column, table, database names fit into this limit.
*/
#ifndef EMBEDDED_LIBRARY
-bool Protocol::net_store_data(const uchar *from, size_t length,
+bool Protocol::net_store_data_cs(const uchar *from, size_t length,
CHARSET_INFO *from_cs, CHARSET_INFO *to_cs)
+#else
+bool Protocol_binary::net_store_data_cs(const uchar *from, size_t length,
+ CHARSET_INFO *from_cs, CHARSET_INFO *to_cs)
+#endif
{
uint dummy_errors;
/* Calculate maxumum possible result length */
- size_t conv_length= to_cs->mbmaxlen * length / from_cs->mbminlen;
- ulong packet_length, new_length;
- char *length_pos, *to;
+ uint conv_length= to_cs->mbmaxlen * length / from_cs->mbminlen;
if (conv_length > 250)
{
@@ -95,19 +100,19 @@ bool Protocol::net_store_data(const uchar *from, size_t length,
Thus conversion directly to "packet" is not worthy.
Let's use "convert" as a temporary buffer.
*/
- return (convert->copy((const char*) from, length, from_cs, to_cs,
- &dummy_errors) ||
+ return (convert->copy((const char*) from, length, from_cs,
+ to_cs, &dummy_errors) ||
net_store_data((const uchar*) convert->ptr(), convert->length()));
}
- packet_length= packet->length();
- new_length= packet_length + conv_length + 1;
+ ulong packet_length= packet->length();
+ ulong new_length= packet_length + conv_length + 1;
if (new_length > packet->alloced_length() && packet->realloc(new_length))
return 1;
- length_pos= (char*) packet->ptr() + packet_length;
- to= length_pos + 1;
+ char *length_pos= (char*) packet->ptr() + packet_length;
+ char *to= length_pos + 1;
to+= copy_and_convert(to, conv_length, to_cs,
(const char*) from, length, from_cs, &dummy_errors);
@@ -116,7 +121,6 @@ bool Protocol::net_store_data(const uchar *from, size_t length,
packet->length((uint) (to - packet->ptr()));
return 0;
}
-#endif
/**
@@ -141,29 +145,33 @@ bool Protocol::net_store_data(const uchar *from, size_t length,
@retval TRUE An error occurred and the message wasn't sent properly
*/
-bool net_send_error(THD *thd, uint sql_errno, const char *err)
+bool net_send_error(THD *thd, uint sql_errno, const char *err,
+ const char* sqlstate)
{
+ bool error;
DBUG_ENTER("net_send_error");
DBUG_ASSERT(!thd->spcont);
DBUG_ASSERT(sql_errno);
- DBUG_ASSERT(err && err[0]);
+ DBUG_ASSERT(err);
DBUG_PRINT("enter",("sql_errno: %d err: %s", sql_errno, err));
- bool error;
+
+ if (sqlstate == NULL)
+ sqlstate= mysql_errno_to_sqlstate(sql_errno);
/*
It's one case when we can push an error even though there
is an OK or EOF already.
*/
- thd->main_da.can_overwrite_status= TRUE;
+ thd->stmt_da->can_overwrite_status= TRUE;
/* Abort multi-result sets */
thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
- error= net_send_error_packet(thd, sql_errno, err);
+ error= net_send_error_packet(thd, sql_errno, err, sqlstate);
- thd->main_da.can_overwrite_status= FALSE;
+ thd->stmt_da->can_overwrite_status= FALSE;
DBUG_RETURN(error);
}
@@ -185,7 +193,7 @@ bool net_send_error(THD *thd, uint sql_errno, const char *err)
@param thd Thread handler
@param server_status The server status
- @param total_warn_count Total number of warnings
+ @param statement_warn_count Total number of warnings
@param affected_rows Number of rows changed by statement
@param id Auto_increment id for first row (if used)
@param message Message to send to the client (Used by mysql_status)
@@ -199,8 +207,8 @@ bool net_send_error(THD *thd, uint sql_errno, const char *err)
#ifndef EMBEDDED_LIBRARY
bool
net_send_ok(THD *thd,
- uint server_status, uint total_warn_count,
- ha_rows affected_rows, ulonglong id, const char *message)
+ uint server_status, uint statement_warn_count,
+ ulonglong affected_rows, ulonglong id, const char *message)
{
NET *net= &thd->net;
uchar buff[MYSQL_ERRMSG_SIZE+10],*pos;
@@ -223,12 +231,12 @@ net_send_ok(THD *thd,
(ulong) affected_rows,
(ulong) id,
(uint) (server_status & 0xffff),
- (uint) total_warn_count));
+ (uint) statement_warn_count));
int2store(pos, server_status);
pos+=2;
/* We can only return up to 65535 warnings in two bytes */
- uint tmp= min(total_warn_count, 65535);
+ uint tmp= min(statement_warn_count, 65535);
int2store(pos, tmp);
pos+= 2;
}
@@ -237,7 +245,7 @@ net_send_ok(THD *thd,
int2store(pos, server_status);
pos+=2;
}
- thd->main_da.can_overwrite_status= TRUE;
+ thd->stmt_da->can_overwrite_status= TRUE;
if (message && message[0])
pos= net_store_data(pos, (uchar*) message, strlen(message));
@@ -245,7 +253,8 @@ net_send_ok(THD *thd,
if (!error)
error= net_flush(net);
- thd->main_da.can_overwrite_status= FALSE;
+
+ thd->stmt_da->can_overwrite_status= FALSE;
DBUG_PRINT("info", ("OK sent, so no more error sending allowed"));
DBUG_RETURN(error);
@@ -269,7 +278,7 @@ static uchar eof_buff[1]= { (uchar) 254 }; /* Marker for end of fields */
@param thd Thread handler
@param server_status The server status
- @param total_warn_count Total number of warnings
+ @param statement_warn_count Total number of warnings
@return
@retval FALSE The message was successfully sent
@@ -277,7 +286,7 @@ static uchar eof_buff[1]= { (uchar) 254 }; /* Marker for end of fields */
*/
bool
-net_send_eof(THD *thd, uint server_status, uint total_warn_count)
+net_send_eof(THD *thd, uint server_status, uint statement_warn_count)
{
NET *net= &thd->net;
bool error= FALSE;
@@ -285,11 +294,11 @@ net_send_eof(THD *thd, uint server_status, uint total_warn_count)
/* Set to TRUE if no active vio, to work well in case of --init-file */
if (net->vio != 0)
{
- thd->main_da.can_overwrite_status= TRUE;
- error= write_eof_packet(thd, net, server_status, total_warn_count);
+ thd->stmt_da->can_overwrite_status= TRUE;
+ error= write_eof_packet(thd, net, server_status, statement_warn_count);
if (!error)
error= net_flush(net);
- thd->main_da.can_overwrite_status= FALSE;
+ thd->stmt_da->can_overwrite_status= FALSE;
DBUG_PRINT("info", ("EOF sent, so no more error sending allowed"));
}
DBUG_RETURN(error);
@@ -303,7 +312,7 @@ net_send_eof(THD *thd, uint server_status, uint total_warn_count)
@param thd The thread handler
@param net The network handler
@param server_status The server status
- @param total_warn_count The number of warnings
+ @param statement_warn_count The number of warnings
@return
@@ -313,7 +322,7 @@ net_send_eof(THD *thd, uint server_status, uint total_warn_count)
static bool write_eof_packet(THD *thd, NET *net,
uint server_status,
- uint total_warn_count)
+ uint statement_warn_count)
{
bool error;
if (thd->client_capabilities & CLIENT_PROTOCOL_41)
@@ -323,7 +332,7 @@ static bool write_eof_packet(THD *thd, NET *net,
Don't send warn count during SP execution, as the warn_list
is cleared between substatements, and mysqltest gets confused
*/
- uint tmp= min(total_warn_count, 65535);
+ uint tmp= min(statement_warn_count, 65535);
buff[0]= 254;
int2store(buff+1, tmp);
/*
@@ -352,14 +361,18 @@ static bool write_eof_packet(THD *thd, NET *net,
@retval TRUE An error occurred and the messages wasn't sent properly
*/
-bool net_send_error_packet(THD *thd, uint sql_errno, const char *err)
+bool net_send_error_packet(THD *thd, uint sql_errno, const char *err,
+ const char* sqlstate)
+
{
NET *net= &thd->net;
uint length;
/*
buff[]: sql_errno:2 + ('#':1 + SQLSTATE_LENGTH:5) + MYSQL_ERRMSG_SIZE:512
*/
- uchar buff[2+1+SQLSTATE_LENGTH+MYSQL_ERRMSG_SIZE], *pos;
+ uint error;
+ char converted_err[MYSQL_ERRMSG_SIZE];
+ char buff[2+1+SQLSTATE_LENGTH+MYSQL_ERRMSG_SIZE], *pos;
DBUG_ENTER("send_error_packet");
@@ -373,27 +386,23 @@ bool net_send_error_packet(THD *thd, uint sql_errno, const char *err)
DBUG_RETURN(FALSE);
}
- if (net->return_errno)
- { // new client code; Add errno before message
- int2store(buff,sql_errno);
- pos= buff+2;
- if (thd->client_capabilities & CLIENT_PROTOCOL_41)
- {
- /* The first # is to make the protocol backward compatible */
- buff[2]= '#';
- pos= (uchar*) strmov((char*) buff+3, mysql_errno_to_sqlstate(sql_errno));
- }
- length= (uint) (strmake((char*) pos, err, MYSQL_ERRMSG_SIZE-1) -
- (char*) buff);
- err= (char*) buff;
- }
- else
+ int2store(buff,sql_errno);
+ pos= buff+2;
+ if (thd->client_capabilities & CLIENT_PROTOCOL_41)
{
- length=(uint) strlen(err);
- set_if_smaller(length,MYSQL_ERRMSG_SIZE-1);
+ /* The first # is to make the protocol backward compatible */
+ buff[2]= '#';
+ pos= strmov(buff+3, sqlstate);
}
- DBUG_RETURN(net_write_command(net,(uchar) 255, (uchar*) "", 0, (uchar*) err,
- length));
+
+ convert_error_message(converted_err, sizeof(converted_err),
+ thd->variables.character_set_results,
+ err, strlen(err), system_charset_info, &error);
+ /* Converted error message is always null-terminated. */
+ length= (uint) (strmake(pos, converted_err, MYSQL_ERRMSG_SIZE - 1) - buff);
+
+ DBUG_RETURN(net_write_command(net,(uchar) 255, (uchar*) "", 0, (uchar*) buff,
+ length));
}
#endif /* EMBEDDED_LIBRARY */
@@ -457,6 +466,12 @@ static uchar *net_store_length_fast(uchar *packet, uint length)
packet is "buffered" in the diagnostics area and sent to the client
in the end of statement.
+ @note This method defines a template, but delegates actual
+ sending of data to virtual Protocol::send_{ok,eof,error}. This
+ allows for implementation of protocols that "intercept" ok/eof/error
+ messages, and store them in memory, etc, instead of sending to
+ the client.
+
@pre The diagnostics area is assigned or disabled. It can not be empty
-- we assume that every SQL statement or COM_* command
generates OK, ERROR, or EOF status.
@@ -471,51 +486,96 @@ static uchar *net_store_length_fast(uchar *packet, uint length)
Diagnostics_area::is_sent is set for debugging purposes only.
*/
-void net_end_statement(THD *thd)
+void Protocol::end_statement()
{
- DBUG_ENTER("net_end_statement");
- DBUG_ASSERT(! thd->main_da.is_sent);
+ DBUG_ENTER("Protocol::end_statement");
+ DBUG_ASSERT(! thd->stmt_da->is_sent);
+ bool error= FALSE;
/* Can not be true, but do not take chances in production. */
- if (thd->main_da.is_sent)
- return;
-
- bool error= FALSE;
+ if (thd->stmt_da->is_sent)
+ DBUG_VOID_RETURN;
- switch (thd->main_da.status()) {
+ switch (thd->stmt_da->status()) {
case Diagnostics_area::DA_ERROR:
/* The query failed, send error to log and abort bootstrap. */
- error= net_send_error(thd,
- thd->main_da.sql_errno(),
- thd->main_da.message());
+ error= send_error(thd->stmt_da->sql_errno(),
+ thd->stmt_da->message(),
+ thd->stmt_da->get_sqlstate());
break;
case Diagnostics_area::DA_EOF:
- error= net_send_eof(thd,
- thd->main_da.server_status(),
- thd->main_da.total_warn_count());
+ error= send_eof(thd->server_status,
+ thd->stmt_da->statement_warn_count());
break;
case Diagnostics_area::DA_OK:
- error= net_send_ok(thd,
- thd->main_da.server_status(),
- thd->main_da.total_warn_count(),
- thd->main_da.affected_rows(),
- thd->main_da.last_insert_id(),
- thd->main_da.message());
+ error= send_ok(thd->server_status,
+ thd->stmt_da->statement_warn_count(),
+ thd->stmt_da->affected_rows(),
+ thd->stmt_da->last_insert_id(),
+ thd->stmt_da->message());
break;
case Diagnostics_area::DA_DISABLED:
break;
case Diagnostics_area::DA_EMPTY:
default:
DBUG_ASSERT(0);
- error= net_send_ok(thd, thd->server_status, thd->total_warn_count,
- 0, 0, NULL);
+ error= send_ok(thd->server_status, 0, 0, 0, NULL);
break;
}
if (!error)
- thd->main_da.is_sent= TRUE;
+ thd->stmt_da->is_sent= TRUE;
DBUG_VOID_RETURN;
}
+/**
+ A default implementation of "OK" packet response to the client.
+
+ Currently this implementation is re-used by both network-oriented
+ protocols -- the binary and text one. They do not differ
+ in their OK packet format, which allows for a significant simplification
+ on client side.
+*/
+
+bool Protocol::send_ok(uint server_status, uint statement_warn_count,
+ ulonglong affected_rows, ulonglong last_insert_id,
+ const char *message)
+{
+ DBUG_ENTER("Protocol::send_ok");
+ const bool retval=
+ net_send_ok(thd, server_status, statement_warn_count,
+ affected_rows, last_insert_id, message);
+ DBUG_RETURN(retval);
+}
+
+
+/**
+ A default implementation of "EOF" packet response to the client.
+
+ Binary and text protocol do not differ in their EOF packet format.
+*/
+
+bool Protocol::send_eof(uint server_status, uint statement_warn_count)
+{
+ DBUG_ENTER("Protocol::send_eof");
+ const bool retval= net_send_eof(thd, server_status, statement_warn_count);
+ DBUG_RETURN(retval);
+}
+
+
+/**
+ A default implementation of "ERROR" packet response to the client.
+
+ Binary and text protocol do not differ in ERROR packet format.
+*/
+
+bool Protocol::send_error(uint sql_errno, const char *err_msg,
+ const char *sql_state)
+{
+ DBUG_ENTER("Protocol::send_error");
+ const bool retval= net_send_error_packet(thd, sql_errno, err_msg, sql_state);
+ DBUG_RETURN(retval);
+}
+
/**
Send a progress report to the client
@@ -631,9 +691,9 @@ bool Protocol::flush()
{
#ifndef EMBEDDED_LIBRARY
bool error;
- thd->main_da.can_overwrite_status= TRUE;
+ thd->stmt_da->can_overwrite_status= TRUE;
error= net_flush(&thd->net);
- thd->main_da.can_overwrite_status= FALSE;
+ thd->stmt_da->can_overwrite_status= FALSE;
return error;
#else
return 0;
@@ -660,16 +720,16 @@ bool Protocol::flush()
1 Error (Note that in this case the error is not sent to the
client)
*/
-bool Protocol::send_fields(List<Item> *list, uint flags)
+bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
{
List_iterator_fast<Item> it(*list);
Item *item;
- uchar buff[80];
+ uchar buff[MAX_FIELD_WIDTH];
String tmp((char*) buff,sizeof(buff),&my_charset_bin);
Protocol_text prot(thd);
String *local_packet= prot.storage_packet();
CHARSET_INFO *thd_charset= thd->variables.character_set_results;
- DBUG_ENTER("Protocol::send_fields");
+ DBUG_ENTER("Protocol::send_result_set_metadata");
if (flags & SEND_NUM_ROWS)
{ // Packet with number of elements
@@ -720,17 +780,16 @@ bool Protocol::send_fields(List<Item> *list, uint flags)
*pos++= 12; // Length of packed fields
/* inject a NULL to test the client */
DBUG_EXECUTE_IF("poison_rs_fields", pos[-1]= 0xfb;);
- if (item->collation.collation == &my_charset_bin || thd_charset == NULL)
+ if (item->charset_for_protocol() == &my_charset_bin || thd_charset == NULL)
{
/* No conversion */
- int2store(pos, field.charsetnr);
+ int2store(pos, item->charset_for_protocol()->number);
int4store(pos+2, field.length);
}
else
{
/* With conversion */
- ulonglong max_length;
- uint32 field_length;
+ uint32 field_length, max_length;
int2store(pos, thd_charset->number);
/*
For TEXT/BLOB columns, field_length describes the maximum data
@@ -753,9 +812,8 @@ bool Protocol::send_fields(List<Item> *list, uint flags)
field.type <= MYSQL_TYPE_BLOB) ?
field.length / item->collation.collation->mbminlen :
field.length / item->collation.collation->mbmaxlen;
- max_length*= thd_charset->mbmaxlen;
- field_length= (max_length > UINT_MAX32) ?
- UINT_MAX32 : (uint32) max_length;
+ field_length= char_to_byte_length_safe(max_length,
+ thd_charset->mbmaxlen);
int4store(pos + 2, field_length);
}
pos[6]= field.type;
@@ -774,31 +832,14 @@ bool Protocol::send_fields(List<Item> *list, uint flags)
local_packet->realloc(local_packet->length()+10))
goto err;
pos= (char*) local_packet->ptr()+local_packet->length();
-
-#ifdef TO_BE_DELETED_IN_6
- if (!(thd->client_capabilities & CLIENT_LONG_FLAG))
- {
- pos[0]=3;
- int3store(pos+1,field.length);
- pos[4]=1;
- pos[5]=field.type;
- pos[6]=2;
- pos[7]= (char) field.flags;
- pos[8]= (char) field.decimals;
- pos+= 9;
- }
- else
-#endif
- {
- pos[0]=3;
- int3store(pos+1,field.length);
- pos[4]=1;
- pos[5]=field.type;
- pos[6]=3;
- int2store(pos+7,field.flags);
- pos[9]= (char) field.decimals;
- pos+= 10;
- }
+ pos[0]=3;
+ int3store(pos+1,field.length);
+ pos[4]=1;
+ pos[5]=field.type;
+ pos[6]=3;
+ int2store(pos+7,field.flags);
+ pos[9]= (char) field.decimals;
+ pos+= 10;
}
local_packet->length((uint) (pos - local_packet->ptr()));
if (flags & SEND_DEFAULTS)
@@ -818,10 +859,10 @@ bool Protocol::send_fields(List<Item> *list, uint flags)
Send no warning information, as it will be sent at statement end.
*/
if (write_eof_packet(thd, &thd->net, thd->server_status,
- thd->total_warn_count))
+ thd->warning_info->statement_warn_count()))
DBUG_RETURN(1);
}
- DBUG_RETURN(prepare_for_send(list));
+ DBUG_RETURN(prepare_for_send(list->elements));
err:
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES),
@@ -840,6 +881,47 @@ bool Protocol::write()
/**
+ Send one result set row.
+
+ @param row_items a collection of column values for that row
+
+ @return Error status.
+ @retval TRUE Error.
+ @retval FALSE Success.
+*/
+
+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))
+ {
+ // 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())
+ 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);
+}
+
+
+/**
Send \\0 end terminated string.
@param from NullS or \\0 terminated string
@@ -885,7 +967,6 @@ bool Protocol::store(I_List<i_string>* str_list)
return store((char*) tmp.ptr(), len, tmp.charset());
}
-
/****************************************************************************
Functions to handle the simple (default) protocol where everything is
This protocol is the one that is used by default between the MySQL server
@@ -929,7 +1010,7 @@ bool Protocol::store_string_aux(const char *from, size_t length,
tocs != &my_charset_bin)
{
/* Store with conversion */
- return net_store_data((uchar*) from, length, fromcs, tocs);
+ return net_store_data_cs((uchar*) from, length, fromcs, tocs);
}
/* Store without conversion */
return net_store_data((uchar*) from, length);
@@ -1134,6 +1215,53 @@ bool Protocol_text::store_time(MYSQL_TIME *tm, int decimals)
return net_store_data((uchar*) buff, length);
}
+/**
+ Assign OUT-parameters to user variables.
+
+ @param sp_params List of PS/SP parameters (both input and output).
+
+ @return Error status.
+ @retval FALSE Success.
+ @retval TRUE Error.
+*/
+
+bool Protocol_text::send_out_parameters(List<Item_param> *sp_params)
+{
+ DBUG_ASSERT(sp_params->elements ==
+ thd->lex->prepared_stmt_params.elements);
+
+ List_iterator_fast<Item_param> item_param_it(*sp_params);
+ List_iterator_fast<LEX_STRING> user_var_name_it(thd->lex->prepared_stmt_params);
+
+ while (true)
+ {
+ Item_param *item_param= item_param_it++;
+ LEX_STRING *user_var_name= user_var_name_it++;
+
+ if (!item_param || !user_var_name)
+ break;
+
+ if (!item_param->get_out_param_info())
+ continue; // It's an IN-parameter.
+
+ Item_func_set_user_var *suv=
+ new Item_func_set_user_var(*user_var_name, item_param);
+ /*
+ Item_func_set_user_var is not fixed after construction, call
+ fix_fields().
+ */
+ if (suv->fix_fields(thd, NULL))
+ return TRUE;
+
+ if (suv->check(FALSE))
+ return TRUE;
+
+ if (suv->update())
+ return TRUE;
+ }
+
+ return FALSE;
+}
/****************************************************************************
Functions to handle the binary protocol used with prepared statements
@@ -1154,14 +1282,13 @@ bool Protocol_text::store_time(MYSQL_TIME *tm, int decimals)
[..]..[[length]data] data
****************************************************************************/
-bool Protocol_binary::prepare_for_send(List<Item> *item_list)
+bool Protocol_binary::prepare_for_send(uint num_columns)
{
- Protocol::prepare_for_send(item_list);
+ Protocol::prepare_for_send(num_columns);
bit_fields= (field_count+9)/8;
- if (packet->alloc(bit_fields+1))
- return 1;
+ return packet->alloc(bit_fields+1);
+
/* prepare_for_resend will be called after this one */
- return 0;
}
@@ -1356,3 +1483,80 @@ bool Protocol_binary::store_time(MYSQL_TIME *tm, int decimals)
buff[0]=(char) length; // Length is stored first
return packet->append(buff, length+1, PACKET_BUFFER_EXTRA_ALLOC);
}
+
+/**
+ Send a result set with OUT-parameter values by means of PS-protocol.
+
+ @param sp_params List of PS/SP parameters (both input and output).
+
+ @return Error status.
+ @retval FALSE Success.
+ @retval TRUE Error.
+*/
+
+bool Protocol_binary::send_out_parameters(List<Item_param> *sp_params)
+{
+ if (!(thd->client_capabilities & CLIENT_PS_MULTI_RESULTS))
+ {
+ /* The client does not support OUT-parameters. */
+ return FALSE;
+ }
+
+ List<Item> out_param_lst;
+
+ {
+ List_iterator_fast<Item_param> item_param_it(*sp_params);
+
+ while (true)
+ {
+ Item_param *item_param= item_param_it++;
+
+ if (!item_param)
+ break;
+
+ if (!item_param->get_out_param_info())
+ continue; // It's an IN-parameter.
+
+ if (out_param_lst.push_back(item_param))
+ return TRUE;
+ }
+ }
+
+ if (!out_param_lst.elements)
+ return FALSE;
+
+ /*
+ We have to set SERVER_PS_OUT_PARAMS in THD::server_status, because it
+ is used in send_result_set_metadata().
+ */
+
+ thd->server_status|= SERVER_PS_OUT_PARAMS | SERVER_MORE_RESULTS_EXISTS;
+
+ /* Send meta-data. */
+ if (send_result_set_metadata(&out_param_lst, SEND_NUM_ROWS | SEND_EOF))
+ return TRUE;
+
+ /* Send data. */
+
+ prepare_for_resend();
+
+ if (send_result_set_row(&out_param_lst))
+ return TRUE;
+
+ if (write())
+ return TRUE;
+
+ /* Restore THD::server_status. */
+ thd->server_status&= ~SERVER_PS_OUT_PARAMS;
+
+ /* Send EOF-packet. */
+ net_send_eof(thd, thd->server_status, 0);
+
+ /*
+ Reset SERVER_MORE_RESULTS_EXISTS bit, because this is the last packet
+ for sure.
+ */
+ thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
+
+ return FALSE;
+}
diff --git a/sql/protocol.h b/sql/protocol.h
index bd7462a1280..871d6018458 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -1,6 +1,7 @@
-/*
- Copyright (c) 2002-2007 MySQL AB, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+#ifndef PROTOCOL_INCLUDED
+#define PROTOCOL_INCLUDED
+
+/* Copyright (c) 2002, 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
@@ -13,16 +14,19 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifdef USE_PRAGMA_INTERFACE
#pragma interface /* gcc class implementation */
#endif
+#include "sql_error.h"
+#include "my_decimal.h" /* my_decimal */
class i_string;
+class Field;
class THD;
+class Item_param;
typedef struct st_mysql_field MYSQL_FIELD;
typedef struct st_mysql_rows MYSQL_ROWS;
@@ -39,16 +43,28 @@ protected:
uint field_count;
#ifndef EMBEDDED_LIBRARY
bool net_store_data(const uchar *from, size_t length);
+ bool net_store_data_cs(const uchar *from, size_t length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
#else
virtual bool net_store_data(const uchar *from, size_t length);
+ virtual bool net_store_data_cs(const uchar *from, size_t length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
char **next_field;
MYSQL_FIELD *next_mysql_field;
MEM_ROOT *alloc;
#endif
- bool net_store_data(const uchar *from, size_t length,
- CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
bool store_string_aux(const char *from, size_t length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
+
+ virtual bool send_ok(uint server_status, uint statement_warn_count,
+ ulonglong affected_rows, ulonglong last_insert_id,
+ const char *message);
+
+ virtual bool send_eof(uint server_status, uint statement_warn_count);
+
+ virtual bool send_error(uint sql_errno, const char *err_msg,
+ const char *sql_state);
+
public:
Protocol() {}
Protocol(THD *thd_arg) { init(thd_arg); }
@@ -56,7 +72,8 @@ public:
void init(THD* thd_arg);
enum { SEND_NUM_ROWS= 1, SEND_DEFAULTS= 2, SEND_EOF= 4 };
- virtual bool send_fields(List<Item> *list, uint flags);
+ virtual bool send_result_set_metadata(List<Item> *list, uint flags);
+ bool send_result_set_row(List<Item> *row_items);
bool store(I_List<i_string> *str_list);
bool store(const char *from, CHARSET_INFO *cs);
@@ -74,9 +91,9 @@ public:
inline bool store(String *str)
{ return store((char*) str->ptr(), str->length(), str->charset()); }
- virtual bool prepare_for_send(List<Item> *item_list)
+ virtual bool prepare_for_send(uint num_columns)
{
- field_count=item_list->elements;
+ field_count= num_columns;
return 0;
}
virtual bool flush();
@@ -98,6 +115,8 @@ public:
virtual bool store_date(MYSQL_TIME *time)=0;
virtual bool store_time(MYSQL_TIME *time, int decimals)=0;
virtual bool store(Field *field)=0;
+
+ virtual bool send_out_parameters(List<Item_param> *sp_params)=0;
#ifdef EMBEDDED_LIBRARY
int begin_dataset();
virtual void remove_last_row() {}
@@ -106,13 +125,15 @@ public:
#endif
enum enum_protocol_type
{
- PROTOCOL_TEXT= 0, PROTOCOL_BINARY= 1
/*
- before adding here or change the values, consider that it is cast to a
- bit in sql_cache.cc.
+ Before adding a new type, please make sure
+ there is enough storage for it in Query_cache_query_flags.
*/
+ PROTOCOL_TEXT= 0, PROTOCOL_BINARY= 1, PROTOCOL_LOCAL= 2
};
virtual enum enum_protocol_type type()= 0;
+
+ void end_statement();
};
@@ -139,6 +160,8 @@ public:
virtual bool store(float nr, uint32 decimals, String *buffer);
virtual bool store(double from, uint32 decimals, String *buffer);
virtual bool store(Field *field);
+
+ virtual bool send_out_parameters(List<Item_param> *sp_params);
#ifdef EMBEDDED_LIBRARY
void remove_last_row();
#endif
@@ -153,11 +176,13 @@ private:
public:
Protocol_binary() {}
Protocol_binary(THD *thd_arg) :Protocol(thd_arg) {}
- virtual bool prepare_for_send(List<Item> *item_list);
+ virtual bool prepare_for_send(uint num_columns);
virtual void prepare_for_resend();
#ifdef EMBEDDED_LIBRARY
virtual bool write();
bool net_store_data(const uchar *from, size_t length);
+ bool net_store_data_cs(const uchar *from, size_t length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
#endif
virtual bool store_null();
virtual bool store_tiny(longlong from);
@@ -174,14 +199,18 @@ public:
virtual bool store(float nr, uint32 decimals, String *buffer);
virtual bool store(double from, uint32 decimals, String *buffer);
virtual bool store(Field *field);
+
+ virtual bool send_out_parameters(List<Item_param> *sp_params);
+
virtual enum enum_protocol_type type() { return PROTOCOL_BINARY; };
};
void send_warning(THD *thd, uint sql_errno, const char *err=0);
-bool net_send_error(THD *thd, uint sql_errno=0, const char *err=0);
-void net_end_statement(THD *thd);
+bool net_send_error(THD *thd, uint sql_errno, const char *err,
+ const char* sqlstate);
void net_send_progress_packet(THD *thd);
uchar *net_store_data(uchar *to,const uchar *from, size_t length);
uchar *net_store_data(uchar *to,int32 from);
uchar *net_store_data(uchar *to,longlong from);
+#endif /* PROTOCOL_INCLUDED */
diff --git a/sql/records.cc b/sql/records.cc
index 130d30a6d85..aca950d7435 100644
--- a/sql/records.cc
+++ b/sql/records.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2000, 2010, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,9 +12,11 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#ifdef USE_PRAGMA_INTERFACE
+#pragma implementation /* gcc class implementation */
+#endif
/**
@file
@@ -23,7 +25,13 @@
Functions for easy reading of records, possible through a cache
*/
-#include "mysql_priv.h"
+#include "records.h"
+#include "sql_priv.h"
+#include "records.h"
+#include "filesort.h" // filesort_free_buffers
+#include "opt_range.h" // SQL_SELECT
+#include "sql_class.h" // THD
+#include "sql_base.h"
static int rr_quick(READ_RECORD *info);
int rr_sequential(READ_RECORD *info);
@@ -35,12 +43,14 @@ static int rr_from_cache(READ_RECORD *info);
static int init_rr_cache(THD *thd, READ_RECORD *info);
static int rr_cmp(uchar *a,uchar *b);
static int rr_index_first(READ_RECORD *info);
+static int rr_index_last(READ_RECORD *info);
static int rr_index(READ_RECORD *info);
+static int rr_index_desc(READ_RECORD *info);
/**
- Initialize READ_RECORD structure to perform full index scan (in forward
- direction) using read_record.read_record() interface.
+ Initialize READ_RECORD structure to perform full index scan in desired
+ direction using read_record.read_record() interface
This function has been added at late stage and is used only by
UPDATE/DELETE. Other statements perform index scans using
@@ -52,11 +62,13 @@ static int rr_index(READ_RECORD *info);
@param print_error If true, call table->file->print_error() if an error
occurs (except for end-of-records error)
@param idx index to scan
+ @param reverse Scan in the reverse direction
*/
void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
- bool print_error, uint idx)
+ bool print_error, uint idx, bool reverse)
{
+ int error;
empty_record(table);
bzero((char*) info,sizeof(*info));
info->thd= thd;
@@ -66,10 +78,15 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
info->unlock_row= rr_unlock_row;
table->status=0; /* And it's always found */
- if (!table->file->inited)
- table->file->ha_index_init(idx, 1);
+ if (!table->file->inited &&
+ (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= rr_index_first;
+ info->read_record= reverse ? rr_index_last : rr_index_first;
}
@@ -172,9 +189,10 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
info->table=table;
info->forms= &info->table; /* Only one table */
- if (table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE &&
+ if ((table->s->tmp_table == INTERNAL_TMP_TABLE ||
+ table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE) &&
!table->sort.addon_field)
- VOID(table->file->extra(HA_EXTRA_MMAP));
+ (void) table->file->extra(HA_EXTRA_MMAP);
if (table->sort.addon_field)
{
@@ -265,11 +283,12 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
!(table->s->db_options_in_use & HA_OPTION_PACK_RECORD) ||
(use_record_cache < 0 &&
!(table->file->ha_table_flags() & HA_NOT_DELETE_WITH_CACHE))))
- VOID(table->file->extra_opt(HA_EXTRA_CACHE,
- thd->variables.read_buff_size));
+ (void) table->file->extra_opt(HA_EXTRA_CACHE,
+ thd->variables.read_buff_size);
}
/* Condition pushdown to storage engine */
- if (thd->variables.engine_condition_pushdown &&
+ if ((thd->variables.optimizer_switch &
+ OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN) &&
select && select->cond &&
(select->cond->used_tables() & table->map) &&
!table->file->pushed_cond)
@@ -284,7 +303,7 @@ void end_read_record(READ_RECORD *info)
{ /* free cache if used */
if (info->cache)
{
- my_free_lock((char*) info->cache,MYF(0));
+ my_free_lock(info->cache);
info->cache=0;
}
if (info->table)
@@ -353,7 +372,15 @@ static int rr_quick(READ_RECORD *info)
static int rr_index_first(READ_RECORD *info)
{
- int tmp= info->table->file->ha_index_first(info->record);
+ int tmp;
+ // tell handler that we are doing an index scan
+ if ((tmp = info->table->file->prepare_index_scan()))
+ {
+ tmp= rr_handle_error(info, tmp);
+ return tmp;
+ }
+
+ tmp= info->table->file->ha_index_first(info->record);
info->read_record= rr_index;
if (tmp)
tmp= rr_handle_error(info, tmp);
@@ -362,6 +389,29 @@ static int rr_index_first(READ_RECORD *info)
/**
+ Reads last row in an index scan.
+
+ @param info Scan info
+
+ @retval
+ 0 Ok
+ @retval
+ -1 End of records
+ @retval
+ 1 Error
+*/
+
+static int rr_index_last(READ_RECORD *info)
+{
+ int tmp= info->table->file->ha_index_last(info->record);
+ info->read_record= rr_index_desc;
+ if (tmp)
+ tmp= rr_handle_error(info, tmp);
+ return tmp;
+}
+
+
+/**
Reads index sequentially after first row.
Read the next index record (in forward direction) and translate return
@@ -386,6 +436,31 @@ static int rr_index(READ_RECORD *info)
}
+/**
+ Reads index sequentially from the last row to the first.
+
+ Read the prev index record (in backward direction) and translate return
+ value.
+
+ @param info Scan info
+
+ @retval
+ 0 Ok
+ @retval
+ -1 End of records
+ @retval
+ 1 Error
+*/
+
+static int rr_index_desc(READ_RECORD *info)
+{
+ int tmp= info->table->file->ha_index_prev(info->record);
+ if (tmp)
+ tmp= rr_handle_error(info, tmp);
+ return tmp;
+}
+
+
int rr_sequential(READ_RECORD *info)
{
int tmp;
diff --git a/sql/records.h b/sql/records.h
new file mode 100644
index 00000000000..57467d665d4
--- /dev/null
+++ b/sql/records.h
@@ -0,0 +1,86 @@
+#ifndef SQL_RECORDS_H
+#define SQL_RECORDS_H
+/* 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-1301 USA */
+
+#ifdef USE_PRAGMA_INTERFACE
+#pragma interface /* gcc class implementation */
+#endif
+#include <my_global.h> /* for uint typedefs */
+
+struct st_join_table;
+class handler;
+struct TABLE;
+class THD;
+class SQL_SELECT;
+class Copy_field;
+
+/**
+ A context for reading through a single table using a chosen access method:
+ index read, scan, etc, use of cache, etc.
+
+ Use by:
+ READ_RECORD read_record;
+ init_read_record(&read_record, ...);
+ while (read_record.read_record())
+ {
+ ...
+ }
+ end_read_record();
+*/
+
+struct READ_RECORD
+{
+ typedef int (*Read_func)(READ_RECORD*);
+ typedef void (*Unlock_row_func)(st_join_table *);
+ 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;
+ THD *thd;
+ SQL_SELECT *select;
+ uint cache_records;
+ uint ref_length,struct_length,reclength,rec_cache_size,error_offset;
+ uint index;
+ 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_io_cache *io_cache;
+ bool print_error, ignore_not_found_rows;
+
+ /*
+ SJ-Materialization runtime may need to read fields from the materialized
+ table and unpack them into original table fields:
+ */
+ Copy_field *copy_field;
+ Copy_field *copy_field_end;
+public:
+ READ_RECORD() {}
+};
+
+bool init_read_record(READ_RECORD *info, THD *thd, TABLE *reg_form,
+ SQL_SELECT *select, int use_record_cache,
+ bool print_errors, bool disable_rr_cache);
+void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
+ bool print_error, uint idx, bool reverse);
+void end_read_record(READ_RECORD *info);
+
+void rr_unlock_row(st_join_table *tab);
+
+#endif /* SQL_RECORDS_H */
diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc
index fee8a538fa9..89fb1bb27de 100644
--- a/sql/repl_failsafe.cc
+++ b/sql/repl_failsafe.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2001, 2010, Oracle and/or its affiliates.
+ Copyright (c) 2001, 2011, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +12,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@file
@@ -25,10 +24,12 @@
functions like register_slave()) are working.
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "sql_parse.h" // check_access
#ifdef HAVE_REPLICATION
#include "repl_failsafe.h"
+#include "sql_acl.h" // REPL_SLAVE_ACL
#include "sql_repl.h"
#include "slave.h"
#include "rpl_mi.h"
@@ -40,9 +41,9 @@
#define SLAVE_ERRMSG_SIZE (FN_REFLEN+64)
-RPL_STATUS rpl_status=RPL_NULL;
-pthread_mutex_t LOCK_rpl_status;
-pthread_cond_t COND_rpl_status;
+ulong rpl_status=RPL_NULL;
+mysql_mutex_t LOCK_rpl_status;
+mysql_cond_t COND_rpl_status;
HASH slave_list;
const char *rpl_role_type[] = {"MASTER","SLAVE",NullS};
@@ -51,16 +52,9 @@ TYPELIB rpl_role_typelib = {array_elements(rpl_role_type)-1,"",
const char* rpl_status_type[]=
{
- "AUTH_MASTER","ACTIVE_SLAVE","IDLE_SLAVE", "LOST_SOLDIER","TROOP_SOLDIER",
+ "AUTH_MASTER","IDLE_SLAVE","ACTIVE_SLAVE","LOST_SOLDIER","TROOP_SOLDIER",
"RECOVERY_CAPTAIN","NULL",NullS
};
-TYPELIB rpl_status_typelib= {array_elements(rpl_status_type)-1,"",
- rpl_status_type, NULL};
-
-
-static Slave_log_event* find_slave_event(IO_CACHE* log,
- const char* log_file_name,
- char* errmsg);
/*
All of the functions defined in this file which are not used (the ones to
@@ -70,53 +64,13 @@ static Slave_log_event* find_slave_event(IO_CACHE* log,
functions like register_slave()) are working.
*/
-#if NOT_USED
-static int init_failsafe_rpl_thread(THD* thd)
+void change_rpl_status(ulong from_status, ulong to_status)
{
- DBUG_ENTER("init_failsafe_rpl_thread");
- thd->system_thread = SYSTEM_THREAD_DELAYED_INSERT;
- /*
- thd->bootstrap is to report errors barely to stderr; if this code is
- enable again one day, one should check if bootstrap is still needed (maybe
- this thread has no other error reporting method).
- */
- thd->bootstrap = 1;
- thd->security_ctx->skip_grants();
- my_net_init(&thd->net, 0);
- thd->net.read_timeout = slave_net_timeout;
- thd->max_client_packet_length=thd->net.max_packet;
- pthread_mutex_lock(&LOCK_thread_count);
- thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
- pthread_mutex_unlock(&LOCK_thread_count);
-
- if (init_thr_lock() || thd->store_globals())
- {
- /* purecov: begin inspected */
- close_connection(thd, ER_OUT_OF_RESOURCES, 1); // is this needed?
- statistic_increment(aborted_connects,&LOCK_status);
- one_thread_per_connection_end(thd,0);
- DBUG_RETURN(-1);
- /* purecov: end */
- }
-
- thd->mem_root->free= thd->mem_root->used= 0;
- if (thd->variables.max_join_size == HA_POS_ERROR)
- thd->options|= OPTION_BIG_SELECTS;
-
- thd_proc_info(thd, "Thread initialized");
- thd->version=refresh_version;
- thd->set_time();
- DBUG_RETURN(0);
-}
-#endif
-
-void change_rpl_status(RPL_STATUS from_status, RPL_STATUS to_status)
-{
- pthread_mutex_lock(&LOCK_rpl_status);
+ mysql_mutex_lock(&LOCK_rpl_status);
if (rpl_status == from_status || rpl_status == RPL_ANY)
rpl_status = to_status;
- pthread_cond_signal(&COND_rpl_status);
- pthread_mutex_unlock(&LOCK_rpl_status);
+ mysql_cond_signal(&COND_rpl_status);
+ mysql_mutex_unlock(&LOCK_rpl_status);
}
@@ -133,28 +87,21 @@ void change_rpl_status(RPL_STATUS from_status, RPL_STATUS to_status)
}\
-static inline int cmp_master_pos(Slave_log_event* sev, LEX_MASTER_INFO* mi)
-{
- return cmp_master_pos(sev->master_log, sev->master_pos, mi->log_file_name,
- mi->pos);
-}
-
-
void unregister_slave(THD* thd, bool only_mine, bool need_mutex)
{
if (thd->server_id)
{
if (need_mutex)
- pthread_mutex_lock(&LOCK_slave_list);
+ mysql_mutex_lock(&LOCK_slave_list);
SLAVE_INFO* old_si;
- if ((old_si = (SLAVE_INFO*)hash_search(&slave_list,
- (uchar*)&thd->server_id, 4)) &&
+ if ((old_si = (SLAVE_INFO*)my_hash_search(&slave_list,
+ (uchar*)&thd->server_id, 4)) &&
(!only_mine || old_si->thd == thd))
- hash_delete(&slave_list, (uchar*)old_si);
+ my_hash_delete(&slave_list, (uchar*)old_si);
if (need_mutex)
- pthread_mutex_unlock(&LOCK_slave_list);
+ mysql_mutex_unlock(&LOCK_slave_list);
}
}
@@ -175,7 +122,7 @@ int register_slave(THD* thd, uchar* packet, uint packet_length)
uchar *p= packet, *p_end= packet + packet_length;
const char *errmsg= "Wrong parameters to function register_slave";
- if (check_access(thd, REPL_SLAVE_ACL, any_db,0,0,0,0))
+ if (check_access(thd, REPL_SLAVE_ACL, any_db, NULL, NULL, 0, 0))
return 1;
if (!(si = (SLAVE_INFO*)my_malloc(sizeof(SLAVE_INFO), MYF(MY_WME))))
goto err2;
@@ -189,20 +136,26 @@ int register_slave(THD* thd, uchar* packet, uint packet_length)
goto err;
si->port= uint2korr(p);
p += 2;
- si->rpl_recovery_rank= uint4korr(p);
+ /*
+ We need to by pass the bytes used in the fake rpl_recovery_rank
+ variable. It was removed in patch for BUG#13963. But this would
+ make a server with that patch unable to connect to an old master.
+ See: BUG#49259
+ */
+ // si->rpl_recovery_rank= uint4korr(p);
p += 4;
if (!(si->master_id= uint4korr(p)))
si->master_id= server_id;
si->thd= thd;
- pthread_mutex_lock(&LOCK_slave_list);
+ mysql_mutex_lock(&LOCK_slave_list);
unregister_slave(thd,0,0);
res= my_hash_insert(&slave_list, (uchar*) si);
- pthread_mutex_unlock(&LOCK_slave_list);
+ mysql_mutex_unlock(&LOCK_slave_list);
return res;
err:
- my_free(si, MYF(MY_WME));
+ my_free(si);
my_message(ER_UNKNOWN_ERROR, errmsg, MYF(0)); /* purecov: inspected */
err2:
return 1;
@@ -218,443 +171,51 @@ extern "C" uint32
extern "C" void slave_info_free(void *s)
{
- my_free(s, MYF(MY_WME));
-}
-
-void init_slave_list()
-{
- hash_init(&slave_list, system_charset_info, SLAVE_LIST_CHUNK, 0, 0,
- (hash_get_key) slave_list_key, (hash_free_key) slave_info_free, 0);
- pthread_mutex_init(&LOCK_slave_list, MY_MUTEX_INIT_FAST);
-}
-
-void end_slave_list()
-{
- /* No protection by a mutex needed as we are only called at shutdown */
- if (hash_inited(&slave_list))
- {
- hash_free(&slave_list);
- pthread_mutex_destroy(&LOCK_slave_list);
- }
-}
-
-static int find_target_pos(LEX_MASTER_INFO *mi, IO_CACHE *log, char *errmsg)
-{
- my_off_t log_pos = (my_off_t) mi->pos;
- uint32 target_server_id = mi->server_id;
-
- for (;;)
- {
- Log_event* ev;
- if (!(ev = Log_event::read_log_event(log, (pthread_mutex_t*) 0, 0,
- opt_slave_sql_verify_checksum)))
- {
- if (log->error > 0)
- strmov(errmsg, "Binary log truncated in the middle of event");
- else if (log->error < 0)
- strmov(errmsg, "I/O error reading binary log");
- else
- strmov(errmsg, "Could not find target event in the binary log");
- return 1;
- }
-
- if (ev->log_pos >= log_pos && ev->server_id == target_server_id)
- {
- delete ev;
- mi->pos = my_b_tell(log);
- return 0;
- }
- delete ev;
- }
- /* Impossible */
+ my_free(s);
}
-/**
- @details
- Before 4.0.15 we had a member of THD called log_pos, it was meant for
- failsafe replication code in repl_failsafe.cc which is disabled until
- it is reworked. Event's log_pos used to be preserved through
- log-slave-updates to make code in repl_failsafe.cc work (this
- function, SHOW NEW MASTER); but on the other side it caused unexpected
- values in Exec_Master_Log_Pos in A->B->C replication setup,
- synchronization problems in master_pos_wait(), ... So we
- (Dmitri & Guilhem) removed it.
-
- So for now this function is broken.
-*/
+#ifdef HAVE_PSI_INTERFACE
+static PSI_mutex_key key_LOCK_slave_list;
-int translate_master(THD* thd, LEX_MASTER_INFO* mi, char* errmsg)
+static PSI_mutex_info all_slave_list_mutexes[]=
{
- LOG_INFO linfo;
- char last_log_name[FN_REFLEN];
- IO_CACHE log;
- File file = -1, last_file = -1;
- pthread_mutex_t *log_lock;
- const char* errmsg_p;
- Slave_log_event* sev = 0;
- my_off_t last_pos = 0;
- int error = 1;
- int cmp_res;
- LINT_INIT(cmp_res);
- DBUG_ENTER("translate_master");
-
- if (!mysql_bin_log.is_open())
- {
- strmov(errmsg,"Binary log is not open");
- DBUG_RETURN(1);
- }
-
- if (!server_id_supplied)
- {
- strmov(errmsg, "Misconfigured master - server id was not set");
- DBUG_RETURN(1);
- }
-
- if (mysql_bin_log.find_log_pos(&linfo, NullS, 1))
- {
- strmov(errmsg,"Could not find first log");
- DBUG_RETURN(1);
- }
- thd->current_linfo = &linfo;
-
- bzero((char*) &log,sizeof(log));
- log_lock = mysql_bin_log.get_log_lock();
- pthread_mutex_lock(log_lock);
-
- for (;;)
- {
- if ((file=open_binlog(&log, linfo.log_file_name, &errmsg_p)) < 0)
- {
- strmov(errmsg, errmsg_p);
- goto err;
- }
-
- if (!(sev = find_slave_event(&log, linfo.log_file_name, errmsg)))
- goto err;
-
- cmp_res = cmp_master_pos(sev, mi);
- delete sev;
-
- if (!cmp_res)
- {
- /* Copy basename */
- fn_format(mi->log_file_name, linfo.log_file_name, "","",1);
- mi->pos = my_b_tell(&log);
- goto mi_inited;
- }
- else if (cmp_res > 0)
- {
- if (!last_pos)
- {
- strmov(errmsg,
- "Slave event in first log points past the target position");
- goto err;
- }
- end_io_cache(&log);
- (void) my_close(file, MYF(MY_WME));
- if (init_io_cache(&log, (file = last_file), IO_SIZE, READ_CACHE, 0, 0,
- MYF(MY_WME)))
- {
- errmsg[0] = 0;
- goto err;
- }
- break;
- }
-
- strmov(last_log_name, linfo.log_file_name);
- last_pos = my_b_tell(&log);
-
- switch (mysql_bin_log.find_next_log(&linfo, 1)) {
- case LOG_INFO_EOF:
- if (last_file >= 0)
- (void)my_close(last_file, MYF(MY_WME));
- last_file = -1;
- goto found_log;
- case 0:
- break;
- default:
- strmov(errmsg, "Error reading log index");
- goto err;
- }
-
- end_io_cache(&log);
- if (last_file >= 0)
- (void) my_close(last_file, MYF(MY_WME));
- last_file = file;
- }
-
-found_log:
- my_b_seek(&log, last_pos);
- if (find_target_pos(mi,&log,errmsg))
- goto err;
- fn_format(mi->log_file_name, last_log_name, "","",1); /* Copy basename */
-
-mi_inited:
- error = 0;
-err:
- pthread_mutex_unlock(log_lock);
- end_io_cache(&log);
- pthread_mutex_lock(&LOCK_thread_count);
- thd->current_linfo = 0;
- pthread_mutex_unlock(&LOCK_thread_count);
- if (file >= 0)
- (void) my_close(file, MYF(MY_WME));
- if (last_file >= 0 && last_file != file)
- (void) my_close(last_file, MYF(MY_WME));
-
- DBUG_RETURN(error);
-}
-
-
-/**
- Caller must delete result when done.
-*/
+ { &key_LOCK_slave_list, "LOCK_slave_list", PSI_FLAG_GLOBAL}
+};
-static Slave_log_event* find_slave_event(IO_CACHE* log,
- const char* log_file_name,
- char* errmsg)
+static void init_all_slave_list_mutexes(void)
{
- Log_event* ev;
- int i;
- bool slave_event_found = 0;
- LINT_INIT(ev);
-
- for (i = 0; i < 2; i++)
- {
- if (!(ev = Log_event::read_log_event(log, (pthread_mutex_t*)0, 0,
- opt_slave_sql_verify_checksum)))
- {
- my_snprintf(errmsg, SLAVE_ERRMSG_SIZE,
- "Error reading event in log '%s'",
- (char*)log_file_name);
- return 0;
- }
- if (ev->get_type_code() == SLAVE_EVENT)
- {
- slave_event_found = 1;
- break;
- }
- delete ev;
- }
- if (!slave_event_found)
- {
- my_snprintf(errmsg, SLAVE_ERRMSG_SIZE,
- "Could not find slave event in log '%s'",
- (char*)log_file_name);
- return 0;
- }
-
- return (Slave_log_event*)ev;
-}
+ const char* category= "sql";
+ int count;
-/**
- This function is broken now.
-
- @seealso translate_master()
-*/
-
-bool show_new_master(THD* thd)
-{
- Protocol *protocol= thd->protocol;
- DBUG_ENTER("show_new_master");
- List<Item> field_list;
- char errmsg[SLAVE_ERRMSG_SIZE];
- LEX_MASTER_INFO* lex_mi= &thd->lex->mi;
+ if (PSI_server == NULL)
+ return;
- errmsg[0]=0; // Safety
- if (translate_master(thd, lex_mi, errmsg))
- {
- if (errmsg[0])
- my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0),
- "SHOW NEW MASTER", errmsg);
- DBUG_RETURN(TRUE);
- }
- else
- {
- field_list.push_back(new Item_empty_string("Log_name", 20));
- field_list.push_back(new Item_return_int("Log_pos", 10,
- MYSQL_TYPE_LONGLONG));
- if (protocol->send_fields(&field_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
- DBUG_RETURN(TRUE);
- protocol->prepare_for_resend();
- protocol->store(lex_mi->log_file_name, &my_charset_bin);
- protocol->store((ulonglong) lex_mi->pos);
- if (protocol->write())
- DBUG_RETURN(TRUE);
- my_eof(thd);
- DBUG_RETURN(FALSE);
- }
+ count= array_elements(all_slave_list_mutexes);
+ PSI_server->register_mutex(category, all_slave_list_mutexes, count);
}
+#endif /* HAVE_PSI_INTERFACE */
-/**
- Asks the master for the list of its other connected slaves.
-
- This is for failsafe replication:
- in order for failsafe replication to work, the servers involved in
- replication must know of each other. We accomplish this by having each
- slave report to the master how to reach it, and on connection, each
- slave receives information about where the other slaves are.
-
- @param mysql pre-existing connection to the master
- @param mi master info
-
- @note
- mi is used only to give detailed error messages which include the
- hostname/port of the master, the username used by the slave to connect to
- the master.
- If the user used by the slave to connect to the master does not have the
- REPLICATION SLAVE privilege, it will pop in this function because
- SHOW SLAVE HOSTS will fail on the master.
-
- @retval
- 1 error
- @retval
- 0 success
-*/
-
-int update_slave_list(MYSQL* mysql, Master_info* mi)
+void init_slave_list()
{
- MYSQL_RES* res=0;
- MYSQL_ROW row;
- const char* error=0;
- bool have_auth_info;
- int port_ind;
- DBUG_ENTER("update_slave_list");
-
- if (mysql_real_query(mysql, STRING_WITH_LEN("SHOW SLAVE HOSTS")) ||
- !(res = mysql_store_result(mysql)))
- {
- error= mysql_error(mysql);
- goto err;
- }
-
- switch (mysql_num_fields(res)) {
- case 5:
- have_auth_info = 0;
- port_ind=2;
- break;
- case 7:
- have_auth_info = 1;
- port_ind=4;
- break;
- default:
- error= "the master returned an invalid number of fields for SHOW SLAVE \
-HOSTS";
- goto err;
- }
-
- pthread_mutex_lock(&LOCK_slave_list);
-
- while ((row= mysql_fetch_row(res)))
- {
- uint32 log_server_id;
- SLAVE_INFO* si, *old_si;
- log_server_id = atoi(row[0]);
- if ((old_si= (SLAVE_INFO*)hash_search(&slave_list,
- (uchar*)&log_server_id,4)))
- si = old_si;
- else
- {
- if (!(si = (SLAVE_INFO*)my_malloc(sizeof(SLAVE_INFO), MYF(MY_WME))))
- {
- error= "the slave is out of memory";
- pthread_mutex_unlock(&LOCK_slave_list);
- goto err;
- }
- si->server_id = log_server_id;
- if (my_hash_insert(&slave_list, (uchar*)si))
- {
- error= "the slave is out of memory";
- pthread_mutex_unlock(&LOCK_slave_list);
- goto err;
- }
- }
- strmake(si->host, row[1], sizeof(si->host)-1);
- si->port = atoi(row[port_ind]);
- si->rpl_recovery_rank = atoi(row[port_ind+1]);
- si->master_id = atoi(row[port_ind+2]);
- if (have_auth_info)
- {
- strmake(si->user, row[2], sizeof(si->user)-1);
- strmake(si->password, row[3], sizeof(si->password)-1);
- }
- }
- pthread_mutex_unlock(&LOCK_slave_list);
-
-err:
- if (res)
- mysql_free_result(res);
- if (error)
- {
- sql_print_error("While trying to obtain the list of slaves from the master "
- "'%s:%d', user '%s' got the following error: '%s'",
- mi->host, mi->port, mi->user, error);
- DBUG_RETURN(1);
- }
- DBUG_RETURN(0);
-}
-
+#ifdef HAVE_PSI_INTERFACE
+ init_all_slave_list_mutexes();
+#endif
-#if NOT_USED
-int find_recovery_captain(THD* thd, MYSQL* mysql)
-{
- return 0;
+ my_hash_init(&slave_list, system_charset_info, SLAVE_LIST_CHUNK, 0, 0,
+ (my_hash_get_key) slave_list_key,
+ (my_hash_free_key) slave_info_free, 0);
+ mysql_mutex_init(key_LOCK_slave_list, &LOCK_slave_list, MY_MUTEX_INIT_FAST);
}
-#endif
-#if NOT_USED
-pthread_handler_t handle_failsafe_rpl(void *arg)
+void end_slave_list()
{
- DBUG_ENTER("handle_failsafe_rpl");
- THD *thd = new THD;
- thd->thread_stack = (char*)&thd;
- MYSQL* recovery_captain = 0;
- const char* msg;
-
- pthread_detach_this_thread();
- if (init_failsafe_rpl_thread(thd) || !(recovery_captain=mysql_init(0)))
- {
- sql_print_error("Could not initialize failsafe replication thread");
- goto err;
- }
- pthread_mutex_lock(&LOCK_rpl_status);
- msg= thd->enter_cond(&COND_rpl_status,
- &LOCK_rpl_status, "Waiting for request");
- while (!thd->killed && !abort_loop)
+ /* No protection by a mutex needed as we are only called at shutdown */
+ if (my_hash_inited(&slave_list))
{
- bool break_req_chain = 0;
- pthread_cond_wait(&COND_rpl_status, &LOCK_rpl_status);
- thd_proc_info(thd, "Processing request");
- while (!break_req_chain)
- {
- switch (rpl_status) {
- case RPL_LOST_SOLDIER:
- if (find_recovery_captain(thd, recovery_captain))
- rpl_status=RPL_TROOP_SOLDIER;
- else
- rpl_status=RPL_RECOVERY_CAPTAIN;
- break_req_chain=1; /* for now until other states are implemented */
- break;
- default:
- break_req_chain=1;
- break;
- }
- }
+ my_hash_free(&slave_list);
+ mysql_mutex_destroy(&LOCK_slave_list);
}
- thd->exit_cond(msg);
-err:
- if (recovery_captain)
- mysql_close(recovery_captain);
- delete thd;
-
- DBUG_LEAVE; // Must match DBUG_ENTER()
- my_thread_end();
- pthread_exit(0);
- return 0; // Avoid compiler warnings
}
-#endif
-
/**
Execute a SHOW SLAVE HOSTS statement.
@@ -680,20 +241,18 @@ bool show_slave_hosts(THD* thd)
field_list.push_back(new Item_empty_string("Password",20));
}
field_list.push_back(new Item_return_int("Port", 7, MYSQL_TYPE_LONG));
- field_list.push_back(new Item_return_int("Rpl_recovery_rank", 7,
- MYSQL_TYPE_LONG));
field_list.push_back(new Item_return_int("Master_id", 10,
MYSQL_TYPE_LONG));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
- pthread_mutex_lock(&LOCK_slave_list);
+ mysql_mutex_lock(&LOCK_slave_list);
for (uint i = 0; i < slave_list.records; ++i)
{
- SLAVE_INFO* si = (SLAVE_INFO*) hash_element(&slave_list, i);
+ SLAVE_INFO* si = (SLAVE_INFO*) my_hash_element(&slave_list, i);
protocol->prepare_for_resend();
protocol->store((uint32) si->server_id);
protocol->store(si->host, &my_charset_bin);
@@ -703,347 +262,17 @@ bool show_slave_hosts(THD* thd)
protocol->store(si->password, &my_charset_bin);
}
protocol->store((uint32) si->port);
- protocol->store((uint32) si->rpl_recovery_rank);
protocol->store((uint32) si->master_id);
if (protocol->write())
{
- pthread_mutex_unlock(&LOCK_slave_list);
+ mysql_mutex_unlock(&LOCK_slave_list);
DBUG_RETURN(TRUE);
}
}
- pthread_mutex_unlock(&LOCK_slave_list);
+ mysql_mutex_unlock(&LOCK_slave_list);
my_eof(thd);
DBUG_RETURN(FALSE);
}
-
-int connect_to_master(THD *thd, MYSQL* mysql, Master_info* mi)
-{
- DBUG_ENTER("connect_to_master");
-
- if (!mi->host || !*mi->host) /* empty host */
- {
- strmov(mysql->net.last_error, "Master is not configured");
- DBUG_RETURN(1);
- }
- mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *) &slave_net_timeout);
- mysql_options(mysql, MYSQL_OPT_READ_TIMEOUT, (char *) &slave_net_timeout);
-
-#ifdef HAVE_OPENSSL
- if (mi->ssl)
- {
- mysql_ssl_set(mysql,
- mi->ssl_key[0]?mi->ssl_key:0,
- mi->ssl_cert[0]?mi->ssl_cert:0,
- mi->ssl_ca[0]?mi->ssl_ca:0,
- mi->ssl_capath[0]?mi->ssl_capath:0,
- mi->ssl_cipher[0]?mi->ssl_cipher:0);
- mysql_options(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
- &mi->ssl_verify_server_cert);
- }
-#endif
-
- mysql_options(mysql, MYSQL_SET_CHARSET_NAME, default_charset_info->csname);
- mysql_options(mysql, MYSQL_SET_CHARSET_DIR, (char *) charsets_dir);
- if (!mysql_real_connect(mysql, mi->host, mi->user, mi->password, 0,
- mi->port, 0, 0))
- DBUG_RETURN(1);
- mysql->reconnect= 1;
- DBUG_RETURN(0);
-}
-
-
-static inline void cleanup_mysql_results(MYSQL_RES* db_res,
- MYSQL_RES** cur, MYSQL_RES** start)
-{
- for (; cur >= start; --cur)
- {
- if (*cur)
- mysql_free_result(*cur);
- }
- mysql_free_result(db_res);
-}
-
-
-static int fetch_db_tables(THD *thd, MYSQL *mysql, const char *db,
- MYSQL_RES *table_res, Master_info *mi)
-{
- MYSQL_ROW row;
- for (row = mysql_fetch_row(table_res); row;
- row = mysql_fetch_row(table_res))
- {
- TABLE_LIST table;
- const char* table_name= row[0];
- int error;
- if (rpl_filter->is_on())
- {
- bzero((char*) &table, sizeof(table)); //just for safe
- table.db= (char*) db;
- table.table_name= (char*) table_name;
- table.updating= 1;
-
- if (!rpl_filter->tables_ok(thd->db, &table))
- continue;
- }
- /* download master's table and overwrite slave's table */
- if ((error= fetch_master_table(thd, db, table_name, mi, mysql, 1)))
- return error;
- }
- return 0;
-}
-
-/**
- Load all MyISAM tables from master to this slave.
-
- REQUIREMENTS
- - No active transaction (flush_relay_log_info would not work in this case).
-
- @todo
- - add special option, not enabled
- by default, to allow inclusion of mysql database into load
- data from master
-*/
-
-bool load_master_data(THD* thd)
-{
- MYSQL mysql;
- MYSQL_RES* master_status_res = 0;
- int error = 0;
- const char* errmsg=0;
- int restart_thread_mask;
- HA_CREATE_INFO create_info;
-
- mysql_init(&mysql);
-
- /*
- We do not want anyone messing with the slave at all for the entire
- duration of the data load.
- */
- pthread_mutex_lock(&LOCK_active_mi);
- lock_slave_threads(active_mi);
- init_thread_mask(&restart_thread_mask,active_mi,0 /*not inverse*/);
- if (restart_thread_mask &&
- (error=terminate_slave_threads(active_mi,restart_thread_mask,
- 1 /*skip lock*/)))
- {
- my_message(error, ER(error), MYF(0));
- unlock_slave_threads(active_mi);
- pthread_mutex_unlock(&LOCK_active_mi);
- return TRUE;
- }
-
- if (connect_to_master(thd, &mysql, active_mi))
- {
- my_error(error= ER_CONNECT_TO_MASTER, MYF(0), mysql_error(&mysql));
- goto err;
- }
-
- // now that we are connected, get all database and tables in each
- {
- MYSQL_RES *db_res, **table_res, **table_res_end, **cur_table_res;
- uint num_dbs;
-
- if (mysql_real_query(&mysql, STRING_WITH_LEN("SHOW DATABASES")) ||
- !(db_res = mysql_store_result(&mysql)))
- {
- my_error(error= ER_QUERY_ON_MASTER, MYF(0), mysql_error(&mysql));
- goto err;
- }
-
- if (!(num_dbs = (uint) mysql_num_rows(db_res)))
- goto err;
- /*
- In theory, the master could have no databases at all
- and run with skip-grant
- */
-
- if (!(table_res = (MYSQL_RES**)thd->alloc(num_dbs * sizeof(MYSQL_RES*))))
- {
- my_message(error = ER_OUTOFMEMORY, ER(ER_OUTOFMEMORY), MYF(0));
- goto err;
- }
-
- /*
- This is a temporary solution until we have online backup
- capabilities - to be replaced once online backup is working
- we wait to issue FLUSH TABLES WITH READ LOCK for as long as we
- can to minimize the lock time.
- */
- if (mysql_real_query(&mysql,
- STRING_WITH_LEN("FLUSH TABLES WITH READ LOCK")) ||
- mysql_real_query(&mysql, STRING_WITH_LEN("SHOW MASTER STATUS")) ||
- !(master_status_res = mysql_store_result(&mysql)))
- {
- my_error(error= ER_QUERY_ON_MASTER, MYF(0), mysql_error(&mysql));
- goto err;
- }
-
- /*
- Go through every table in every database, and if the replication
- rules allow replicating it, get it
- */
-
- table_res_end = table_res + num_dbs;
-
- for (cur_table_res = table_res; cur_table_res < table_res_end;
- cur_table_res++)
- {
- // since we know how many rows we have, this can never be NULL
- MYSQL_ROW row = mysql_fetch_row(db_res);
- char* db = row[0];
-
- /*
- Do not replicate databases excluded by rules. We also test
- replicate_wild_*_table rules (replicate_wild_ignore_table='db1.%' will
- be considered as "ignore the 'db1' database as a whole, as it already
- works for CREATE DATABASE and DROP DATABASE).
- Also skip 'mysql' database - in most cases the user will
- mess up and not exclude mysql database with the rules when
- he actually means to - in this case, he is up for a surprise if
- his priv tables get dropped and downloaded from master
- TODO - add special option, not enabled
- by default, to allow inclusion of mysql database into load
- data from master
- */
-
- if (!rpl_filter->db_ok(db) ||
- !rpl_filter->db_ok_with_wild_table(db) ||
- !strcmp(db,"mysql") ||
- is_schema_db(db))
- {
- *cur_table_res = 0;
- continue;
- }
-
- bzero((char*) &create_info, sizeof(create_info));
- create_info.options= HA_LEX_CREATE_IF_NOT_EXISTS;
-
- if (mysql_create_db(thd, db, &create_info, 1))
- {
- cleanup_mysql_results(db_res, cur_table_res - 1, table_res);
- goto err;
- }
- /* Clear the result of mysql_create_db(). */
- thd->main_da.reset_diagnostics_area();
-
- if (mysql_select_db(&mysql, db) ||
- mysql_real_query(&mysql, STRING_WITH_LEN("SHOW TABLES")) ||
- !(*cur_table_res = mysql_store_result(&mysql)))
- {
- my_error(error= ER_QUERY_ON_MASTER, MYF(0), mysql_error(&mysql));
- cleanup_mysql_results(db_res, cur_table_res - 1, table_res);
- goto err;
- }
-
- if ((error = fetch_db_tables(thd,&mysql,db,*cur_table_res,active_mi)))
- {
- // we do not report the error - fetch_db_tables handles it
- cleanup_mysql_results(db_res, cur_table_res, table_res);
- goto err;
- }
- }
-
- cleanup_mysql_results(db_res, cur_table_res - 1, table_res);
-
- // adjust replication coordinates from the master
- if (master_status_res)
- {
- MYSQL_ROW row = mysql_fetch_row(master_status_res);
-
- /*
- We need this check because the master may not be running with
- log-bin, but it will still allow us to do all the steps
- of LOAD DATA FROM MASTER - no reason to forbid it, really,
- although it does not make much sense for the user to do it
- */
- if (row && row[0] && row[1])
- {
- /*
- If the slave's master info is not inited, we init it, then we write
- the new coordinates to it. Must call init_master_info() *before*
- setting active_mi, because init_master_info() sets active_mi with
- defaults.
- */
- int error_2;
-
- if (init_master_info(active_mi, master_info_file, relay_log_info_file,
- 0, (SLAVE_IO | SLAVE_SQL)))
- my_message(ER_MASTER_INFO, ER(ER_MASTER_INFO), MYF(0));
- strmake(active_mi->master_log_name, row[0],
- sizeof(active_mi->master_log_name) -1);
- active_mi->master_log_pos= my_strtoll10(row[1], (char**) 0, &error_2);
- /* at least in recent versions, the condition below should be false */
- if (active_mi->master_log_pos < BIN_LOG_HEADER_SIZE)
- active_mi->master_log_pos = BIN_LOG_HEADER_SIZE;
- /*
- Relay log's IO_CACHE may not be inited (even if we are sure that some
- host was specified; there could have been a problem when replication
- started, which led to relay log's IO_CACHE to not be inited.
- */
- if (flush_master_info(active_mi, FALSE, FALSE))
- sql_print_error("Failed to flush master info file");
- }
- mysql_free_result(master_status_res);
- }
-
- if (mysql_real_query(&mysql, STRING_WITH_LEN("UNLOCK TABLES")))
- {
- my_error(error= ER_QUERY_ON_MASTER, MYF(0), mysql_error(&mysql));
- goto err;
- }
- }
- thd_proc_info(thd, "purging old relay logs");
- if (purge_relay_logs(&active_mi->rli,thd,
- 0 /* not only reset, but also reinit */,
- &errmsg))
- {
- my_error(ER_RELAY_LOG_FAIL, MYF(0), errmsg);
- unlock_slave_threads(active_mi);
- pthread_mutex_unlock(&LOCK_active_mi);
- return TRUE;
- }
- pthread_mutex_lock(&active_mi->rli.data_lock);
- active_mi->rli.group_master_log_pos = active_mi->master_log_pos;
- strmake(active_mi->rli.group_master_log_name,active_mi->master_log_name,
- sizeof(active_mi->rli.group_master_log_name)-1);
- /*
- Cancel the previous START SLAVE UNTIL, as the fact to download
- a new copy logically makes UNTIL irrelevant.
- */
- active_mi->rli.clear_until_condition();
-
- /*
- No need to update rli.event* coordinates, they will be when the slave
- threads start ; only rli.group* coordinates are necessary here.
- */
- flush_relay_log_info(&active_mi->rli);
- pthread_cond_broadcast(&active_mi->rli.data_cond);
- pthread_mutex_unlock(&active_mi->rli.data_lock);
- thd_proc_info(thd, "starting slave");
- if (restart_thread_mask)
- {
- error=start_slave_threads(0 /* mutex not needed */,
- 1 /* wait for start */,
- active_mi,master_info_file,relay_log_info_file,
- restart_thread_mask);
- }
-
-err:
- unlock_slave_threads(active_mi);
- pthread_mutex_unlock(&LOCK_active_mi);
- thd_proc_info(thd, 0);
-
- mysql_close(&mysql); // safe to call since we always do mysql_init()
- if (!error)
- my_ok(thd);
-
- return error;
-}
-
-#elif defined(__WIN__)
-
-// Remove linker warning 4221 about empty file
-namespace { char dummy; };
-
#endif /* HAVE_REPLICATION */
diff --git a/sql/repl_failsafe.h b/sql/repl_failsafe.h
index b364297155c..2cc031a462d 100644
--- a/sql/repl_failsafe.h
+++ b/sql/repl_failsafe.h
@@ -1,4 +1,7 @@
-/* Copyright (c) 2001-2007 MySQL AB
+#ifndef REPL_FAILSAFE_INCLUDED
+#define REPL_FAILSAFE_INCLUDED
+
+/* Copyright (c) 2001, 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
@@ -11,7 +14,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifdef HAVE_REPLICATION
@@ -19,33 +22,27 @@
#include "my_sys.h"
#include "slave.h"
-typedef enum {RPL_AUTH_MASTER=0,RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE,
+typedef enum {RPL_AUTH_MASTER=0,RPL_IDLE_SLAVE,RPL_ACTIVE_SLAVE,
RPL_LOST_SOLDIER,RPL_TROOP_SOLDIER,
RPL_RECOVERY_CAPTAIN,RPL_NULL /* inactive */,
RPL_ANY /* wild card used by change_rpl_status */ } RPL_STATUS;
-extern RPL_STATUS rpl_status;
+extern ulong rpl_status;
-extern pthread_mutex_t LOCK_rpl_status;
-extern pthread_cond_t COND_rpl_status;
-extern TYPELIB rpl_role_typelib, rpl_status_typelib;
+extern mysql_mutex_t LOCK_rpl_status;
+extern mysql_cond_t COND_rpl_status;
+extern TYPELIB rpl_role_typelib;
extern const char* rpl_role_type[], *rpl_status_type[];
-pthread_handler_t handle_failsafe_rpl(void *arg);
-void change_rpl_status(RPL_STATUS from_status, RPL_STATUS to_status);
+void change_rpl_status(ulong from_status, ulong to_status);
int find_recovery_captain(THD* thd, MYSQL* mysql);
-int update_slave_list(MYSQL* mysql, Master_info* mi);
extern HASH slave_list;
-bool load_master_data(THD* thd);
-int connect_to_master(THD *thd, MYSQL* mysql, Master_info* mi);
-
-bool show_new_master(THD* thd);
bool show_slave_hosts(THD* thd);
-int translate_master(THD* thd, LEX_MASTER_INFO* mi, char* errmsg);
void init_slave_list();
void end_slave_list();
int register_slave(THD* thd, uchar* packet, uint packet_length);
void unregister_slave(THD* thd, bool only_mine, bool need_mutex);
#endif /* HAVE_REPLICATION */
+#endif /* REPL_FAILSAFE_INCLUDED */
diff --git a/sql/replication.h b/sql/replication.h
new file mode 100644
index 00000000000..9492c54fabd
--- /dev/null
+++ b/sql/replication.h
@@ -0,0 +1,566 @@
+/* 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-1301 USA */
+
+#ifndef REPLICATION_H
+#define REPLICATION_H
+
+/***************************************************************************
+ 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.
+
+ 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.
+***************************************************************************/
+
+#include <mysql.h>
+
+typedef struct st_mysql MYSQL;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ Transaction observer flags.
+*/
+enum Trans_flags {
+ /** Transaction is a real transaction */
+ TRANS_IS_REAL_TRANS = 1
+};
+
+/**
+ Transaction observer parameter
+*/
+typedef struct Trans_param {
+ uint32 server_id;
+ uint32 flags;
+
+ /*
+ The latest binary log file name and position written by current
+ transaction, if binary log is disabled or no log event has been
+ written into binary log file by current transaction (events
+ written into transaction log cache are not counted), these two
+ member will be zero.
+ */
+ const char *log_file;
+ my_off_t log_pos;
+} Trans_param;
+
+/**
+ Observes and extends transaction execution
+*/
+typedef struct Trans_observer {
+ uint32 len;
+
+ /**
+ This callback is called after transaction commit
+
+ This callback is called right after commit to storage engines for
+ transactional tables.
+
+ For non-transactional tables, this is called at the end of the
+ statement, before sending statement status, if the statement
+ succeeded.
+
+ @note The return value is currently ignored by the server.
+
+ @param param The parameter for transaction observers
+
+ @retval 0 Sucess
+ @retval 1 Failure
+ */
+ int (*after_commit)(Trans_param *param);
+
+ /**
+ This callback is called after transaction rollback
+
+ This callback is called right after rollback to storage engines
+ for transactional tables.
+
+ For non-transactional tables, this is called at the end of the
+ statement, before sending statement status, if the statement
+ failed.
+
+ @note The return value is currently ignored by the server.
+
+ @param param The parameter for transaction observers
+
+ @retval 0 Sucess
+ @retval 1 Failure
+ */
+ int (*after_rollback)(Trans_param *param);
+} Trans_observer;
+
+/**
+ Binlog storage flags
+*/
+enum Binlog_storage_flags {
+ /** Binary log was sync:ed */
+ BINLOG_STORAGE_IS_SYNCED = 1
+};
+
+/**
+ Binlog storage observer parameters
+ */
+typedef struct Binlog_storage_param {
+ uint32 server_id;
+} Binlog_storage_param;
+
+/**
+ Observe binlog logging storage
+*/
+typedef struct Binlog_storage_observer {
+ uint32 len;
+
+ /**
+ This callback is called after binlog has been flushed
+
+ This callback is called after cached events have been flushed to
+ binary log file. Whether the binary log file is synchronized to
+ disk is indicated by the bit BINLOG_STORAGE_IS_SYNCED in @a flags.
+
+ @param param Observer common parameter
+ @param log_file Binlog file name been updated
+ @param log_pos Binlog position after update
+ @param flags flags for binlog storage
+
+ @retval 0 Sucess
+ @retval 1 Failure
+ */
+ int (*after_flush)(Binlog_storage_param *param,
+ const char *log_file, my_off_t log_pos,
+ uint32 flags);
+} Binlog_storage_observer;
+
+/**
+ Replication binlog transmitter (binlog dump) observer parameter.
+*/
+typedef struct Binlog_transmit_param {
+ uint32 server_id;
+ uint32 flags;
+} Binlog_transmit_param;
+
+/**
+ Observe and extends the binlog dumping thread.
+*/
+typedef struct Binlog_transmit_observer {
+ uint32 len;
+
+ /**
+ This callback is called when binlog dumping starts
+
+
+ @param param Observer common parameter
+ @param log_file Binlog file name to transmit from
+ @param log_pos Binlog position to transmit from
+
+ @retval 0 Sucess
+ @retval 1 Failure
+ */
+ int (*transmit_start)(Binlog_transmit_param *param,
+ const char *log_file, my_off_t log_pos);
+
+ /**
+ This callback is called when binlog dumping stops
+
+ @param param Observer common parameter
+
+ @retval 0 Sucess
+ @retval 1 Failure
+ */
+ int (*transmit_stop)(Binlog_transmit_param *param);
+
+ /**
+ This callback is called to reserve bytes in packet header for event transmission
+
+ This callback is called when resetting transmit packet header to
+ reserve bytes for this observer in packet header.
+
+ The @a header buffer is allocated by the server code, and @a size
+ is the size of the header buffer. Each observer can only reserve
+ a maximum size of @a size in the header.
+
+ @param param Observer common parameter
+ @param header Pointer of the header buffer
+ @param size Size of the header buffer
+ @param len Header length reserved by this observer
+
+ @retval 0 Sucess
+ @retval 1 Failure
+ */
+ int (*reserve_header)(Binlog_transmit_param *param,
+ unsigned char *header,
+ unsigned long size,
+ unsigned long *len);
+
+ /**
+ This callback is called before sending an event packet to slave
+
+ @param param Observer common parameter
+ @param packet Binlog event packet to send
+ @param len Length of the event packet
+ @param log_file Binlog file name of the event packet to send
+ @param log_pos Binlog position of the event packet to send
+
+ @retval 0 Sucess
+ @retval 1 Failure
+ */
+ int (*before_send_event)(Binlog_transmit_param *param,
+ unsigned char *packet, unsigned long len,
+ const char *log_file, my_off_t log_pos );
+
+ /**
+ This callback is called after sending an event packet to slave
+
+ @param param Observer common parameter
+ @param event_buf Binlog event packet buffer sent
+ @param len length of the event packet buffer
+
+ @retval 0 Sucess
+ @retval 1 Failure
+ */
+ int (*after_send_event)(Binlog_transmit_param *param,
+ const char *event_buf, unsigned long len);
+
+ /**
+ This callback is called after resetting master status
+
+ This is called when executing the command RESET MASTER, and is
+ used to reset status variables added by observers.
+
+ @param param Observer common parameter
+
+ @retval 0 Sucess
+ @retval 1 Failure
+ */
+ int (*after_reset_master)(Binlog_transmit_param *param);
+} Binlog_transmit_observer;
+
+/**
+ Binlog relay IO flags
+*/
+enum Binlog_relay_IO_flags {
+ /** Binary relay log was sync:ed */
+ BINLOG_RELAY_IS_SYNCED = 1
+};
+
+
+/**
+ Replication binlog relay IO observer parameter
+*/
+typedef struct Binlog_relay_IO_param {
+ uint32 server_id;
+
+ /* Master host, user and port */
+ char *host;
+ char *user;
+ unsigned int port;
+
+ char *master_log_name;
+ my_off_t master_log_pos;
+
+ MYSQL *mysql; /* the connection to master */
+} Binlog_relay_IO_param;
+
+/**
+ Observes and extends the service of slave IO thread.
+*/
+typedef struct Binlog_relay_IO_observer {
+ uint32 len;
+
+ /**
+ This callback is called when slave IO thread starts
+
+ @param param Observer common parameter
+
+ @retval 0 Sucess
+ @retval 1 Failure
+ */
+ int (*thread_start)(Binlog_relay_IO_param *param);
+
+ /**
+ This callback is called when slave IO thread stops
+
+ @param param Observer common parameter
+
+ @retval 0 Sucess
+ @retval 1 Failure
+ */
+ int (*thread_stop)(Binlog_relay_IO_param *param);
+
+ /**
+ This callback is called before slave requesting binlog transmission from master
+
+ This is called before slave issuing BINLOG_DUMP command to master
+ to request binlog.
+
+ @param param Observer common parameter
+ @param flags binlog dump flags
+
+ @retval 0 Sucess
+ @retval 1 Failure
+ */
+ int (*before_request_transmit)(Binlog_relay_IO_param *param, uint32 flags);
+
+ /**
+ This callback is called after read an event packet from master
+
+ @param param Observer common parameter
+ @param packet The event packet read from master
+ @param len Length of the event packet read from master
+ @param event_buf The event packet return after process
+ @param event_len The length of event packet return after process
+
+ @retval 0 Sucess
+ @retval 1 Failure
+ */
+ int (*after_read_event)(Binlog_relay_IO_param *param,
+ const char *packet, unsigned long len,
+ const char **event_buf, unsigned long *event_len);
+
+ /**
+ This callback is called after written an event packet to relay log
+
+ @param param Observer common parameter
+ @param event_buf Event packet written to relay log
+ @param event_len Length of the event packet written to relay log
+ @param flags flags for relay log
+
+ @retval 0 Sucess
+ @retval 1 Failure
+ */
+ int (*after_queue_event)(Binlog_relay_IO_param *param,
+ const char *event_buf, unsigned long event_len,
+ uint32 flags);
+
+ /**
+ This callback is called after reset slave relay log IO status
+
+ @param param Observer common parameter
+
+ @retval 0 Sucess
+ @retval 1 Failure
+ */
+ int (*after_reset_slave)(Binlog_relay_IO_param *param);
+} Binlog_relay_IO_observer;
+
+
+/**
+ Register a transaction observer
+
+ @param observer The transaction observer to register
+ @param p pointer to the internal plugin structure
+
+ @retval 0 Sucess
+ @retval 1 Observer already exists
+*/
+int register_trans_observer(Trans_observer *observer, void *p);
+
+/**
+ Unregister a transaction observer
+
+ @param observer The transaction observer to unregister
+ @param p pointer to the internal plugin structure
+
+ @retval 0 Sucess
+ @retval 1 Observer not exists
+*/
+int unregister_trans_observer(Trans_observer *observer, void *p);
+
+/**
+ Register a binlog storage observer
+
+ @param observer The binlog storage observer to register
+ @param p pointer to the internal plugin structure
+
+ @retval 0 Sucess
+ @retval 1 Observer already exists
+*/
+int register_binlog_storage_observer(Binlog_storage_observer *observer, void *p);
+
+/**
+ Unregister a binlog storage observer
+
+ @param observer The binlog storage observer to unregister
+ @param p pointer to the internal plugin structure
+
+ @retval 0 Sucess
+ @retval 1 Observer not exists
+*/
+int unregister_binlog_storage_observer(Binlog_storage_observer *observer, void *p);
+
+/**
+ Register a binlog transmit observer
+
+ @param observer The binlog transmit observer to register
+ @param p pointer to the internal plugin structure
+
+ @retval 0 Sucess
+ @retval 1 Observer already exists
+*/
+int register_binlog_transmit_observer(Binlog_transmit_observer *observer, void *p);
+
+/**
+ Unregister a binlog transmit observer
+
+ @param observer The binlog transmit observer to unregister
+ @param p pointer to the internal plugin structure
+
+ @retval 0 Sucess
+ @retval 1 Observer not exists
+*/
+int unregister_binlog_transmit_observer(Binlog_transmit_observer *observer, void *p);
+
+/**
+ Register a binlog relay IO (slave IO thread) observer
+
+ @param observer The binlog relay IO observer to register
+ @param p pointer to the internal plugin structure
+
+ @retval 0 Sucess
+ @retval 1 Observer already exists
+*/
+int register_binlog_relay_io_observer(Binlog_relay_IO_observer *observer, void *p);
+
+/**
+ Unregister a binlog relay IO (slave IO thread) observer
+
+ @param observer The binlog relay IO observer to unregister
+ @param p pointer to the internal plugin structure
+
+ @retval 0 Sucess
+ @retval 1 Observer not exists
+*/
+int unregister_binlog_relay_io_observer(Binlog_relay_IO_observer *observer, void *p);
+
+/**
+ Connect to master
+
+ This function can only used in the slave I/O thread context, and
+ will use the same master information to do the connection.
+
+ @code
+ MYSQL *mysql = mysql_init(NULL);
+ if (rpl_connect_master(mysql))
+ {
+ // do stuff with the connection
+ }
+ mysql_close(mysql); // close the connection
+ @endcode
+
+ @param mysql address of MYSQL structure to use, pass NULL will
+ create a new one
+
+ @return address of MYSQL structure on success, NULL on failure
+*/
+MYSQL *rpl_connect_master(MYSQL *mysql);
+
+/**
+ Set thread entering a condition
+
+ This function should be called before putting a thread to wait for
+ a condition. @a mutex should be held before calling this
+ function. After being waken up, @f thd_exit_cond should be called.
+
+ @param thd The thread entering the condition, NULL means current thread
+ @param cond The condition the thread is going to wait for
+ @param mutex The mutex associated with the condition, this must be
+ held before call this function
+ @param msg The new process message for the thread
+*/
+const char* thd_enter_cond(MYSQL_THD thd, mysql_cond_t *cond,
+ mysql_mutex_t *mutex, const char *msg);
+
+/**
+ Set thread leaving a condition
+
+ This function should be called after a thread being waken up for a
+ condition.
+
+ @param thd The thread entering the condition, NULL means current thread
+ @param old_msg The process message, ususally this should be the old process
+ message before calling @f thd_enter_cond
+*/
+void thd_exit_cond(MYSQL_THD thd, const char *old_msg);
+
+/**
+ Get the value of user variable as an integer.
+
+ This function will return the value of variable @a name as an
+ integer. If the original value of the variable is not an integer,
+ the value will be converted into an integer.
+
+ @param name user variable name
+ @param value pointer to return the value
+ @param null_value if not NULL, the function will set it to true if
+ the value of variable is null, set to false if not
+
+ @retval 0 Success
+ @retval 1 Variable not found
+*/
+int get_user_var_int(const char *name,
+ long long int *value, int *null_value);
+
+/**
+ Get the value of user variable as a double precision float number.
+
+ This function will return the value of variable @a name as real
+ number. If the original value of the variable is not a real number,
+ the value will be converted into a real number.
+
+ @param name user variable name
+ @param value pointer to return the value
+ @param null_value if not NULL, the function will set it to true if
+ the value of variable is null, set to false if not
+
+ @retval 0 Success
+ @retval 1 Variable not found
+*/
+int get_user_var_real(const char *name,
+ double *value, int *null_value);
+
+/**
+ Get the value of user variable as a string.
+
+ This function will return the value of variable @a name as
+ string. If the original value of the variable is not a string,
+ the value will be converted into a string.
+
+ @param name user variable name
+ @param value pointer to the value buffer
+ @param len length of the value buffer
+ @param precision precision of the value if it is a float number
+ @param null_value if not NULL, the function will set it to true if
+ the value of variable is null, set to false if not
+
+ @retval 0 Success
+ @retval 1 Variable not found
+*/
+int get_user_var_str(const char *name,
+ char *value, unsigned long len,
+ unsigned int precision, int *null_value);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* REPLICATION_H */
diff --git a/sql/rpl_filter.cc b/sql/rpl_filter.cc
index 95fc21b64d1..9103cad337d 100644
--- a/sql/rpl_filter.cc
+++ b/sql/rpl_filter.cc
@@ -1,6 +1,4 @@
-/*
- Copyright (c) 2005-2007 MySQL AB, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2000, 2013, 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
@@ -13,11 +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-1301 USA
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "mysqld.h" // system_charset_info
#include "rpl_filter.h"
+#include "hash.h" // my_hash_free
+#include "table.h" // TABLE_LIST
#define TABLE_RULE_HASH_SIZE 16
#define TABLE_RULE_ARR_SIZE 16
@@ -35,15 +35,15 @@ Rpl_filter::Rpl_filter() :
Rpl_filter::~Rpl_filter()
{
if (do_table_inited)
- hash_free(&do_table);
+ my_hash_free(&do_table);
if (ignore_table_inited)
- hash_free(&ignore_table);
+ my_hash_free(&ignore_table);
if (wild_do_table_inited)
free_string_array(&wild_do_table);
if (wild_ignore_table_inited)
free_string_array(&wild_ignore_table);
- free_list(&do_db);
- free_list(&ignore_db);
+ free_string_list(&do_db);
+ free_string_list(&ignore_db);
free_list(&rewrite_db);
}
@@ -107,12 +107,12 @@ Rpl_filter::tables_ok(const char* db, TABLE_LIST* tables)
len= (uint) (strmov(end, tables->table_name) - hash_key);
if (do_table_inited) // if there are any do's
{
- if (hash_search(&do_table, (uchar*) hash_key, len))
+ if (my_hash_search(&do_table, (uchar*) hash_key, len))
DBUG_RETURN(1);
}
if (ignore_table_inited) // if there are any ignores
{
- if (hash_search(&ignore_table, (uchar*) hash_key, len))
+ if (my_hash_search(&ignore_table, (uchar*) hash_key, len))
DBUG_RETURN(0);
}
if (wild_do_table_inited &&
@@ -264,6 +264,57 @@ Rpl_filter::is_on()
}
+/**
+ Parse and add the given comma-separated sequence of filter rules.
+
+ @param spec Comma-separated sequence of filter rules.
+ @param add Callback member function to add a filter rule.
+
+ @return true if error, false otherwise.
+*/
+
+int
+Rpl_filter::parse_filter_rule(const char* spec, Add_filter add)
+{
+ int status= 0;
+ char *arg, *ptr, *pstr;
+
+ if (! (ptr= my_strdup(spec, MYF(MY_WME))))
+ return true;
+
+ pstr= ptr;
+
+ while (pstr)
+ {
+ arg= pstr;
+
+ /* Parse token string. */
+ pstr= strpbrk(arg, ",");
+
+ /* NUL terminate the token string. */
+ if (pstr)
+ *pstr++= '\0';
+
+ /* Skip an empty token string. */
+ if (arg[0] == '\0')
+ continue;
+
+ /* Skip leading spaces. */
+ while (my_isspace(system_charset_info, *arg))
+ arg++;
+
+ status= (this->*add)(arg);
+
+ if (status)
+ break;
+ }
+
+ my_free(ptr);
+
+ return status;
+}
+
+
int
Rpl_filter::add_do_table(const char* table_spec)
{
@@ -286,6 +337,46 @@ Rpl_filter::add_ignore_table(const char* table_spec)
}
+int
+Rpl_filter::set_do_table(const char* table_spec)
+{
+ int status;
+
+ if (do_table_inited)
+ my_hash_reset(&do_table);
+
+ status= parse_filter_rule(table_spec, &Rpl_filter::add_do_table);
+
+ if (!do_table.records)
+ {
+ my_hash_free(&do_table);
+ do_table_inited= 0;
+ }
+
+ return status;
+}
+
+
+int
+Rpl_filter::set_ignore_table(const char* table_spec)
+{
+ int status;
+
+ if (ignore_table_inited)
+ my_hash_reset(&ignore_table);
+
+ status= parse_filter_rule(table_spec, &Rpl_filter::add_ignore_table);
+
+ if (!ignore_table.records)
+ {
+ my_hash_free(&ignore_table);
+ ignore_table_inited= 0;
+ }
+
+ return status;
+}
+
+
int
Rpl_filter::add_wild_do_table(const char* table_spec)
{
@@ -308,6 +399,46 @@ Rpl_filter::add_wild_ignore_table(const char* table_spec)
}
+int
+Rpl_filter::set_wild_do_table(const char* table_spec)
+{
+ int status;
+
+ if (wild_do_table_inited)
+ free_string_array(&wild_do_table);
+
+ status= parse_filter_rule(table_spec, &Rpl_filter::add_wild_do_table);
+
+ if (!wild_do_table.elements)
+ {
+ delete_dynamic(&wild_do_table);
+ wild_do_table_inited= 0;
+ }
+
+ return status;
+}
+
+
+int
+Rpl_filter::set_wild_ignore_table(const char* table_spec)
+{
+ int status;
+
+ if (wild_ignore_table_inited)
+ free_string_array(&wild_ignore_table);
+
+ status= parse_filter_rule(table_spec, &Rpl_filter::add_wild_ignore_table);
+
+ if (!wild_ignore_table.elements)
+ {
+ delete_dynamic(&wild_ignore_table);
+ wild_ignore_table_inited= 0;
+ }
+
+ return status;
+}
+
+
void
Rpl_filter::add_db_rewrite(const char* from_db, const char* to_db)
{
@@ -356,25 +487,59 @@ Rpl_filter::add_wild_table_rule(DYNAMIC_ARRAY* a, const char* table_spec)
}
-void
+int
+Rpl_filter::add_string_list(I_List<i_string> *list, const char* spec)
+{
+ char *str;
+ i_string *node;
+
+ if (! (str= my_strdup(spec, MYF(MY_WME))))
+ return true;
+
+ if (! (node= new i_string(str)))
+ {
+ my_free(str);
+ return true;
+ }
+
+ list->push_back(node);
+
+ return false;
+}
+
+
+int
Rpl_filter::add_do_db(const char* table_spec)
{
DBUG_ENTER("Rpl_filter::add_do_db");
- i_string *db = new i_string(table_spec);
- do_db.push_back(db);
- DBUG_VOID_RETURN;
+ DBUG_RETURN(add_string_list(&do_db, table_spec));
}
-void
+int
Rpl_filter::add_ignore_db(const char* table_spec)
{
DBUG_ENTER("Rpl_filter::add_ignore_db");
- i_string *db = new i_string(table_spec);
- ignore_db.push_back(db);
- DBUG_VOID_RETURN;
+ DBUG_RETURN(add_string_list(&ignore_db, table_spec));
}
+
+int
+Rpl_filter::set_do_db(const char* db_spec)
+{
+ free_string_list(&do_db);
+ return parse_filter_rule(db_spec, &Rpl_filter::add_do_db);
+}
+
+
+int
+Rpl_filter::set_ignore_db(const char* db_spec)
+{
+ free_string_list(&ignore_db);
+ return parse_filter_rule(db_spec, &Rpl_filter::add_ignore_db);
+}
+
+
extern "C" uchar *get_table_key(const uchar *, size_t *, my_bool);
extern "C" void free_table_ent(void* a);
@@ -392,14 +557,14 @@ void free_table_ent(void* a)
{
TABLE_RULE_ENT *e= (TABLE_RULE_ENT *) a;
- my_free((uchar*) e, MYF(0));
+ my_free(e);
}
void
Rpl_filter::init_table_rule_hash(HASH* h, bool* h_inited)
{
- hash_init(h, system_charset_info,TABLE_RULE_HASH_SIZE,0,0,
+ my_hash_init(h, system_charset_info,TABLE_RULE_HASH_SIZE,0,0,
get_table_key, free_table_ent, 0);
*h_inited = 1;
}
@@ -443,12 +608,29 @@ Rpl_filter::free_string_array(DYNAMIC_ARRAY *a)
{
char* p;
get_dynamic(a, (uchar*) &p, i);
- my_free(p, MYF(MY_WME));
+ my_free(p);
}
delete_dynamic(a);
}
+void
+Rpl_filter::free_string_list(I_List<i_string> *l)
+{
+ void *ptr;
+ i_string *tmp;
+
+ while ((tmp= l->get()))
+ {
+ ptr= (void *) tmp->ptr;
+ my_free(ptr);
+ delete tmp;
+ }
+
+ l->empty();
+}
+
+
/*
Builds a String from a HASH of TABLE_RULE_ENT. Cannot be used for any other
hash, as it assumes that the hash entries are TABLE_RULE_ENT.
@@ -470,7 +652,7 @@ Rpl_filter::table_rule_ent_hash_to_str(String* s, HASH* h, bool inited)
{
for (uint i= 0; i < h->records; i++)
{
- TABLE_RULE_ENT* e= (TABLE_RULE_ENT*) hash_element(h, i);
+ TABLE_RULE_ENT* e= (TABLE_RULE_ENT*) my_hash_element(h, i);
if (s->length())
s->append(',');
s->append(e->db,e->key_len);
@@ -565,3 +747,37 @@ Rpl_filter::get_ignore_db()
{
return &ignore_db;
}
+
+
+void
+Rpl_filter::db_rule_ent_list_to_str(String* str, I_List<i_string>* list)
+{
+ I_List_iterator<i_string> it(*list);
+ i_string* s;
+
+ str->length(0);
+
+ while ((s= it++))
+ {
+ str->append(s->ptr);
+ str->append(',');
+ }
+
+ // Remove last ','
+ if (!str->is_empty())
+ str->chop();
+}
+
+
+void
+Rpl_filter::get_do_db(String* str)
+{
+ db_rule_ent_list_to_str(str, get_do_db());
+}
+
+
+void
+Rpl_filter::get_ignore_db(String* str)
+{
+ db_rule_ent_list_to_str(str, get_ignore_db());
+}
diff --git a/sql/rpl_filter.h b/sql/rpl_filter.h
index 890355d1d54..2eb0340b714 100644
--- a/sql/rpl_filter.h
+++ b/sql/rpl_filter.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000-2003, 2005-2007 MySQL AB
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,12 +11,18 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef RPL_FILTER_H
#define RPL_FILTER_H
#include "mysql.h"
+#include "sql_list.h" /* I_List */
+#include "hash.h" /* HASH */
+
+class String;
+struct TABLE_LIST;
+typedef struct st_dynamic_array DYNAMIC_ARRAY;
typedef struct st_table_rule_ent
{
@@ -55,11 +61,20 @@ public:
int add_do_table(const char* table_spec);
int add_ignore_table(const char* table_spec);
+ int set_do_table(const char* table_spec);
+ int set_ignore_table(const char* table_spec);
+
int add_wild_do_table(const char* table_spec);
int add_wild_ignore_table(const char* table_spec);
- void add_do_db(const char* db_spec);
- void add_ignore_db(const char* db_spec);
+ int set_wild_do_table(const char* table_spec);
+ int set_wild_ignore_table(const char* table_spec);
+
+ int add_do_db(const char* db_spec);
+ int add_ignore_db(const char* db_spec);
+
+ int set_do_db(const char* db_spec);
+ int set_ignore_db(const char* db_spec);
void add_db_rewrite(const char* from_db, const char* to_db);
@@ -77,6 +92,9 @@ public:
I_List<i_string>* get_do_db();
I_List<i_string>* get_ignore_db();
+ void get_do_db(String* str);
+ void get_ignore_db(String* str);
+
private:
bool table_rules_on;
@@ -86,13 +104,21 @@ private:
int add_table_rule(HASH* h, const char* table_spec);
int add_wild_table_rule(DYNAMIC_ARRAY* a, const char* table_spec);
+ typedef int (Rpl_filter::*Add_filter)(char const*);
+
+ int parse_filter_rule(const char* spec, Add_filter func);
+
void free_string_array(DYNAMIC_ARRAY *a);
+ void free_string_list(I_List<i_string> *l);
void table_rule_ent_hash_to_str(String* s, HASH* h, bool inited);
void table_rule_ent_dynamic_array_to_str(String* s, DYNAMIC_ARRAY* a,
bool inited);
+ void db_rule_ent_list_to_str(String* s, I_List<i_string>* l);
TABLE_RULE_ENT* find_wild(DYNAMIC_ARRAY *a, const char* key, int len);
+ int add_string_list(I_List<i_string> *list, const char* spec);
+
/*
Those 4 structures below are uninitialized memory unless the
corresponding *_inited variables are "true".
diff --git a/sql/rpl_handler.cc b/sql/rpl_handler.cc
new file mode 100644
index 00000000000..9480a9e0454
--- /dev/null
+++ b/sql/rpl_handler.cc
@@ -0,0 +1,515 @@
+/* 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-1301 USA */
+
+#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;
+
+static pthread_key(Trans_binlog_info*, RPL_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
+
+ if (pthread_key_create(&RPL_TRANS_BINLOG_INFO, NULL))
+ {
+ sql_print_error("Error while creating pthread specific data key for replication. "
+ "Please report a bug.");
+ return 1;
+ }
+
+ 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->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;
+ bool is_real_trans= (all || thd->transaction.all.ha_list == 0);
+
+ param.flags = is_real_trans ? TRANS_IS_REAL_TRANS : 0;
+
+ Trans_binlog_info *log_info=
+ my_pthread_getspecific_ptr(Trans_binlog_info*, RPL_TRANS_BINLOG_INFO);
+
+ param.log_file= log_info ? log_info->log_file : 0;
+ param.log_pos= log_info ? log_info->log_pos : 0;
+
+ int ret= 0;
+ FOREACH_OBSERVER(ret, after_commit, false, (&param));
+
+ /*
+ This is the end of a real transaction or autocommit statement, we
+ can free the memory allocated for binlog file and position.
+ */
+ if (is_real_trans && log_info)
+ {
+ my_pthread_setspecific_ptr(RPL_TRANS_BINLOG_INFO, NULL);
+ my_free(log_info);
+ }
+ return ret;
+}
+
+int Trans_delegate::after_rollback(THD *thd, bool all)
+{
+ Trans_param param;
+ bool is_real_trans= (all || thd->transaction.all.ha_list == 0);
+
+ param.flags = is_real_trans ? TRANS_IS_REAL_TRANS : 0;
+
+ Trans_binlog_info *log_info=
+ my_pthread_getspecific_ptr(Trans_binlog_info*, RPL_TRANS_BINLOG_INFO);
+
+ param.log_file= log_info ? log_info->log_file : 0;
+ param.log_pos= log_info ? log_info->log_pos : 0;
+
+ int ret= 0;
+ FOREACH_OBSERVER(ret, after_rollback, false, (&param));
+
+ /*
+ This is the end of a real transaction or autocommit statement, we
+ can free the memory allocated for binlog file and position.
+ */
+ if (is_real_trans && log_info)
+ {
+ my_pthread_setspecific_ptr(RPL_TRANS_BINLOG_INFO, NULL);
+ my_free(log_info);
+ }
+ return ret;
+}
+
+int Binlog_storage_delegate::after_flush(THD *thd,
+ const char *log_file,
+ my_off_t log_pos,
+ bool synced)
+{
+ Binlog_storage_param param;
+ uint32 flags=0;
+ if (synced)
+ flags |= BINLOG_STORAGE_IS_SYNCED;
+
+ Trans_binlog_info *log_info=
+ my_pthread_getspecific_ptr(Trans_binlog_info*, RPL_TRANS_BINLOG_INFO);
+
+ if (!log_info)
+ {
+ if(!(log_info=
+ (Trans_binlog_info *)my_malloc(sizeof(Trans_binlog_info), MYF(0))))
+ return 1;
+ my_pthread_setspecific_ptr(RPL_TRANS_BINLOG_INFO, log_info);
+ }
+
+ strcpy(log_info->log_file, log_file+dirname_length(log_file));
+ log_info->log_pos = log_pos;
+
+ int ret= 0;
+ FOREACH_OBSERVER(ret, after_flush, false,
+ (&param, log_info->log_file, log_info->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->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);
+}
+#endif /* HAVE_REPLICATION */
diff --git a/sql/rpl_handler.h b/sql/rpl_handler.h
new file mode 100644
index 00000000000..362f3c74a4b
--- /dev/null
+++ b/sql/rpl_handler.h
@@ -0,0 +1,213 @@
+/* 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-1301 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);
+ 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);
+};
+
+#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 b84cfe413a6..ec1a96e8a2b 100644
--- a/sql/rpl_injector.cc
+++ b/sql/rpl_injector.cc
@@ -1,5 +1,4 @@
-/*
- Copyright (c) 2006, 2010, Oracle and/or its affiliates.
+/* Copyright (c) 2006, 2011, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,11 +11,15 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h" // REQUIRED by later includes
#include "rpl_injector.h"
+#include "transaction.h"
+#include "sql_parse.h" // begin_trans, end_trans, COMMIT
+#include "sql_base.h" // close_thread_tables
+#include "log_event.h" // Incident_log_event
/*
injector::transaction - member definitions
@@ -38,9 +41,7 @@ injector::transaction::transaction(MYSQL_BIN_LOG *log, THD *thd)
m_start_pos.m_file_pos= log_info.pos;
m_thd->lex->start_transaction_opt= 0; /* for begin_trans() */
- begin_trans(m_thd);
-
- thd->set_current_stmt_binlog_row_based();
+ trans_begin(m_thd);
}
injector::transaction::~transaction()
@@ -58,7 +59,7 @@ injector::transaction::~transaction()
*/
*the_memory= '\0';
- my_free(the_memory, MYF(0));
+ my_free(the_memory);
}
/**
@@ -88,11 +89,16 @@ int injector::transaction::commit()
is committed by committing the statement transaction
explicitly.
*/
- error |= ha_autocommit_or_rollback(m_thd, error);
- end_trans(m_thd, error ? ROLLBACK : COMMIT);
+ trans_commit_stmt(m_thd);
+ if (!trans_commit(m_thd))
+ {
+ close_thread_tables(m_thd);
+ m_thd->mdl_context.release_transactional_locks();
+ }
DBUG_RETURN(error);
}
+
int injector::transaction::use_table(server_id_type sid, table tbl)
{
DBUG_ENTER("injector::transaction::use_table");
@@ -116,7 +122,7 @@ int injector::transaction::write_row (server_id_type sid, table tbl,
record_type record)
{
DBUG_ENTER("injector::transaction::write_row(...)");
-
+
int error= check_state(ROW_STATE);
if (error)
DBUG_RETURN(error);
@@ -232,7 +238,7 @@ int injector::record_incident(THD *thd, Incident incident)
Incident_log_event ev(thd, incident);
if (int error= mysql_bin_log.write(&ev))
return error;
- return mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE);
+ return mysql_bin_log.rotate_and_purge(true);
}
int injector::record_incident(THD *thd, Incident incident, LEX_STRING const message)
@@ -240,5 +246,5 @@ int injector::record_incident(THD *thd, Incident incident, LEX_STRING const mess
Incident_log_event ev(thd, incident, message);
if (int error= mysql_bin_log.write(&ev))
return error;
- return mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE);
+ return mysql_bin_log.rotate_and_purge(true);
}
diff --git a/sql/rpl_injector.h b/sql/rpl_injector.h
index 8b5df5e9353..f4790cc963a 100644
--- a/sql/rpl_injector.h
+++ b/sql/rpl_injector.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2006, 2007 MySQL AB
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,13 +21,13 @@
#include <my_bitmap.h>
#include "rpl_constants.h"
+#include "table.h" /* TABLE */
/* Forward declarations */
class handler;
class MYSQL_BIN_LOG;
-struct st_table;
+struct TABLE;
-typedef st_table TABLE;
/*
Injector to inject rows into the MySQL server.
diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc
index 3239c64d6c8..3c5a99121fa 100644
--- a/sql/rpl_mi.cc
+++ b/sql/rpl_mi.cc
@@ -1,5 +1,5 @@
-/*
- Copyright (c) 2006, 2010, Oracle and/or its affiliates.
+/* Copyright (c) 2006, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2011, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,91 +12,127 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include <my_global.h> // For HAVE_REPLICATION
-#include "mysql_priv.h"
+#include "sql_priv.h"
#include <my_dir.h>
-
+#include "unireg.h" // REQUIRED by other includes
#include "rpl_mi.h"
+#include "slave.h" // SLAVE_MAX_HEARTBEAT_PERIOD
#ifdef HAVE_REPLICATION
+#define DEFAULT_CONNECT_RETRY 60
+
+static void init_master_log_pos(Master_info* mi);
-Master_info::Master_info()
+Master_info::Master_info(bool is_slave_recovery)
:Slave_reporting_capability("I/O"),
- ssl(0), ssl_verify_server_cert(0), fd(-1), io_thd(0),
- checksum_alg_before_fd(BINLOG_CHECKSUM_ALG_UNDEF), inited(0),
- abort_slave(0),slave_running(0),
- slave_run_id(0)
+ ssl(0), ssl_verify_server_cert(1), fd(-1), io_thd(0),
+ rli(is_slave_recovery), port(MYSQL_PORT),
+ checksum_alg_before_fd(BINLOG_CHECKSUM_ALG_UNDEF),
+ connect_retry(DEFAULT_CONNECT_RETRY), inited(0), abort_slave(0),
+ slave_running(0), slave_run_id(0), sync_counter(0),
+ heartbeat_period(0), received_heartbeats(0), master_id(0)
{
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;
+ my_init_dynamic_array(&ignore_server_ids, sizeof(::server_id), 16, 16);
bzero((char*) &file, sizeof(file));
- /*
- We have to use MYF_NO_DEADLOCK_DETECTION because mysqld doesn't
- lock run_lock and data_lock consistently.
- Should be fixed as this can easily lead to deadlocks
- */
- my_pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST,
- "Master_info::run_lock", MYF_NO_DEADLOCK_DETECTION);
- my_pthread_mutex_init(&data_lock, MY_MUTEX_INIT_FAST,
- "Master_info::data_lock", MYF_NO_DEADLOCK_DETECTION);
- pthread_cond_init(&data_cond, NULL);
- pthread_cond_init(&start_cond, NULL);
- pthread_cond_init(&stop_cond, NULL);
-
-#ifdef SAFE_MUTEX
- /* Define mutex order for locks to find wrong lock usage */
- pthread_mutex_lock(&data_lock);
- pthread_mutex_lock(&run_lock);
- pthread_mutex_unlock(&run_lock);
- pthread_mutex_unlock(&data_lock);
-#endif
+ mysql_mutex_init(key_master_info_run_lock, &run_lock, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_master_info_data_lock, &data_lock, MY_MUTEX_INIT_FAST);
+ mysql_mutex_setflags(&run_lock, MYF_NO_DEADLOCK_DETECTION);
+ mysql_mutex_setflags(&data_lock, MYF_NO_DEADLOCK_DETECTION);
+ mysql_mutex_init(key_master_info_sleep_lock, &sleep_lock, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_master_info_data_cond, &data_cond, NULL);
+ mysql_cond_init(key_master_info_start_cond, &start_cond, NULL);
+ mysql_cond_init(key_master_info_stop_cond, &stop_cond, NULL);
+ mysql_cond_init(key_master_info_sleep_cond, &sleep_cond, NULL);
}
Master_info::~Master_info()
{
- pthread_mutex_destroy(&run_lock);
- pthread_mutex_destroy(&data_lock);
- pthread_cond_destroy(&data_cond);
- pthread_cond_destroy(&start_cond);
- pthread_cond_destroy(&stop_cond);
+ delete_dynamic(&ignore_server_ids);
+ mysql_mutex_destroy(&run_lock);
+ mysql_mutex_destroy(&data_lock);
+ mysql_mutex_destroy(&sleep_lock);
+ mysql_cond_destroy(&data_cond);
+ mysql_cond_destroy(&start_cond);
+ mysql_cond_destroy(&stop_cond);
+ mysql_cond_destroy(&sleep_cond);
}
+/**
+ A comparison function to be supplied as argument to @c sort_dynamic()
+ and @c bsearch()
-void init_master_info_with_options(Master_info* mi)
+ @return -1 if first argument is less, 0 if it equal to, 1 if it is greater
+ than the second
+*/
+int change_master_server_id_cmp(ulong *id1, ulong *id2)
{
- DBUG_ENTER("init_master_info_with_options");
+ return *id1 < *id2? -1 : (*id1 > *id2? 1 : 0);
+}
+
+
+/**
+ Reports if the s_id server has been configured to ignore events
+ it generates with
+
+ CHANGE MASTER IGNORE_SERVER_IDS= ( list of server ids )
+
+ Method is called from the io thread event receiver filtering.
+
+ @param s_id the master server identifier
+
+ @retval TRUE if s_id is in the list of ignored master servers,
+ @retval FALSE otherwise.
+ */
+bool Master_info::shall_ignore_server_id(ulong s_id)
+{
+ if (likely(ignore_server_ids.elements == 1))
+ return (* (ulong*) dynamic_array_ptr(&ignore_server_ids, 0)) == s_id;
+ else
+ return bsearch((const ulong *) &s_id,
+ ignore_server_ids.buffer,
+ ignore_server_ids.elements, sizeof(ulong),
+ (int (*) (const void*, const void*)) change_master_server_id_cmp)
+ != NULL;
+}
+
+void Master_info::clear_in_memory_info(bool all)
+{
+ init_master_log_pos(this);
+ if (all)
+ {
+ port= MYSQL_PORT;
+ host[0] = 0; user[0] = 0; password[0] = 0;
+ }
+}
+
+void init_master_log_pos(Master_info* mi)
+{
+ DBUG_ENTER("init_master_log_pos");
mi->master_log_name[0] = 0;
mi->master_log_pos = BIN_LOG_HEADER_SIZE; // skip magic number
- if (master_host)
- strmake(mi->host, master_host, sizeof(mi->host) - 1);
- if (master_user)
- strmake(mi->user, master_user, sizeof(mi->user) - 1);
- if (master_password)
- strmake(mi->password, master_password, MAX_PASSWORD_LENGTH);
- mi->port = master_port;
- mi->connect_retry = master_connect_retry;
-
- mi->ssl= master_ssl;
- if (master_ssl_ca)
- strmake(mi->ssl_ca, master_ssl_ca, sizeof(mi->ssl_ca)-1);
- if (master_ssl_capath)
- strmake(mi->ssl_capath, master_ssl_capath, sizeof(mi->ssl_capath)-1);
- if (master_ssl_cert)
- strmake(mi->ssl_cert, master_ssl_cert, sizeof(mi->ssl_cert)-1);
- if (master_ssl_cipher)
- strmake(mi->ssl_cipher, master_ssl_cipher, sizeof(mi->ssl_cipher)-1);
- if (master_ssl_key)
- strmake(mi->ssl_key, master_ssl_key, sizeof(mi->ssl_key)-1);
/* Intentionally init ssl_verify_server_cert to 0, no option available */
mi->ssl_verify_server_cert= 0;
+ /*
+ always request heartbeat unless master_heartbeat_period is set
+ explicitly zero. Here is the default value for heartbeat period
+ if CHANGE MASTER did not specify it. (no data loss in conversion
+ as hb period has a max)
+ */
+ mi->heartbeat_period= (float) min(SLAVE_MAX_HEARTBEAT_PERIOD,
+ (slave_net_timeout/2.0));
+ DBUG_ASSERT(mi->heartbeat_period > (float) 0.001
+ || mi->heartbeat_period == 0);
+
DBUG_VOID_RETURN;
}
@@ -106,9 +142,14 @@ enum {
/* 5.1.16 added value of master_ssl_verify_server_cert */
LINE_FOR_MASTER_SSL_VERIFY_SERVER_CERT= 15,
-
+ /* 6.0 added value of master_heartbeat_period */
+ LINE_FOR_MASTER_HEARTBEAT_PERIOD= 16,
+ /* MySQL Cluster 6.3 added master_bind */
+ LINE_FOR_MASTER_BIND = 17,
+ /* 6.0 added value of master_ignore_server_id */
+ LINE_FOR_REPLICATE_IGNORE_SERVER_IDS= 18,
/* Number of lines currently used when saving master info file */
- LINES_IN_MASTER_INFO= LINE_FOR_MASTER_SSL_VERIFY_SERVER_CERT
+ LINES_IN_MASTER_INFO= LINE_FOR_REPLICATE_IGNORE_SERVER_IDS
};
int init_master_info(Master_info* mi, const char* master_info_fname,
@@ -136,7 +177,29 @@ int init_master_info(Master_info* mi, const char* master_info_fname,
*/
if (thread_mask & SLAVE_SQL)
{
+ bool hot_log= FALSE;
+ /*
+ my_b_seek does an implicit flush_io_cache, so we need to:
+
+ 1. check if this log is active (hot)
+ 2. if it is we keep log_lock until the seek ends, otherwise
+ release it right away.
+
+ If we did not take log_lock, SQL thread might race with IO
+ thread for the IO_CACHE mutex.
+
+ */
+ mysql_mutex_t *log_lock= mi->rli.relay_log.get_log_lock();
+ mysql_mutex_lock(log_lock);
+ hot_log= mi->rli.relay_log.is_active(mi->rli.linfo.log_file_name);
+
+ if (!hot_log)
+ mysql_mutex_unlock(log_lock);
+
my_b_seek(mi->rli.cur_log, (my_off_t) 0);
+
+ if (hot_log)
+ mysql_mutex_unlock(log_lock);
}
DBUG_RETURN(0);
}
@@ -150,7 +213,7 @@ int init_master_info(Master_info* mi, const char* master_info_fname,
keep other threads from reading bogus info
*/
- pthread_mutex_lock(&mi->data_lock);
+ mysql_mutex_lock(&mi->data_lock);
fd = mi->fd;
/* does master.info exist ? */
@@ -159,7 +222,7 @@ int init_master_info(Master_info* mi, const char* master_info_fname,
{
if (abort_if_no_master_info_file)
{
- pthread_mutex_unlock(&mi->data_lock);
+ mysql_mutex_unlock(&mi->data_lock);
DBUG_RETURN(0);
}
/*
@@ -167,8 +230,9 @@ int init_master_info(Master_info* mi, const char* master_info_fname,
the old descriptor and re-create the old file
*/
if (fd >= 0)
- my_close(fd, MYF(MY_WME));
- if ((fd = my_open(fname, O_CREAT|O_RDWR|O_BINARY, MYF(MY_WME))) < 0 )
+ mysql_file_close(fd, MYF(MY_WME));
+ if ((fd= mysql_file_open(key_file_master_info,
+ fname, O_CREAT|O_RDWR|O_BINARY, MYF(MY_WME))) < 0 )
{
sql_print_error("Failed to create a new master info file (\
file '%s', errno %d)", fname, my_errno);
@@ -183,7 +247,7 @@ file '%s')", fname);
}
mi->fd = fd;
- init_master_info_with_options(mi);
+ mi->clear_in_memory_info(false);
}
else // file exists
@@ -192,7 +256,8 @@ file '%s')", fname);
reinit_io_cache(&mi->file, READ_CACHE, 0L,0,0);
else
{
- if ((fd = my_open(fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0 )
+ if ((fd= mysql_file_open(key_file_master_info,
+ fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0 )
{
sql_print_error("Failed to open the existing master info file (\
file '%s', errno %d)", fname, my_errno);
@@ -210,7 +275,9 @@ file '%s')", fname);
mi->fd = fd;
int port, connect_retry, master_log_pos, lines;
int ssl= 0, ssl_verify_server_cert= 0;
+ float master_heartbeat_period= 0.0;
char *first_non_digit;
+ char dummy_buf[HOSTNAME_LENGTH+1];
/*
Starting from 4.1.x master.info has new format. Now its
@@ -255,36 +322,34 @@ file '%s')", fname);
lines= 7;
if (init_intvar_from_file(&master_log_pos, &mi->file, 4) ||
- init_strvar_from_file(mi->host, sizeof(mi->host), &mi->file,
- master_host) ||
- init_strvar_from_file(mi->user, sizeof(mi->user), &mi->file,
- master_user) ||
+ init_strvar_from_file(mi->host, sizeof(mi->host), &mi->file, 0) ||
+ init_strvar_from_file(mi->user, sizeof(mi->user), &mi->file, "test") ||
init_strvar_from_file(mi->password, SCRAMBLED_PASSWORD_CHAR_LENGTH+1,
- &mi->file, master_password) ||
- init_intvar_from_file(&port, &mi->file, master_port) ||
+ &mi->file, 0) ||
+ init_intvar_from_file(&port, &mi->file, MYSQL_PORT) ||
init_intvar_from_file(&connect_retry, &mi->file,
- master_connect_retry))
+ DEFAULT_CONNECT_RETRY))
goto errwithmsg;
/*
If file has ssl part use it even if we have server without
- SSL support. But these option will be ignored later when
+ SSL support. But these options will be ignored later when
slave will try connect to master, so in this case warning
is printed.
*/
if (lines >= LINES_IN_MASTER_INFO_WITH_SSL)
{
- if (init_intvar_from_file(&ssl, &mi->file, master_ssl) ||
+ if (init_intvar_from_file(&ssl, &mi->file, 0) ||
init_strvar_from_file(mi->ssl_ca, sizeof(mi->ssl_ca),
- &mi->file, master_ssl_ca) ||
+ &mi->file, 0) ||
init_strvar_from_file(mi->ssl_capath, sizeof(mi->ssl_capath),
- &mi->file, master_ssl_capath) ||
+ &mi->file, 0) ||
init_strvar_from_file(mi->ssl_cert, sizeof(mi->ssl_cert),
- &mi->file, master_ssl_cert) ||
+ &mi->file, 0) ||
init_strvar_from_file(mi->ssl_cipher, sizeof(mi->ssl_cipher),
- &mi->file, master_ssl_cipher) ||
+ &mi->file, 0) ||
init_strvar_from_file(mi->ssl_key, sizeof(mi->ssl_key),
- &mi->file, master_ssl_key))
+ &mi->file, 0))
goto errwithmsg;
/*
@@ -294,14 +359,37 @@ file '%s')", fname);
if (lines >= LINE_FOR_MASTER_SSL_VERIFY_SERVER_CERT &&
init_intvar_from_file(&ssl_verify_server_cert, &mi->file, 0))
goto errwithmsg;
-
+ /*
+ Starting from 6.0 master_heartbeat_period might be
+ in the file
+ */
+ if (lines >= LINE_FOR_MASTER_HEARTBEAT_PERIOD &&
+ init_floatvar_from_file(&master_heartbeat_period, &mi->file, 0.0))
+ goto errwithmsg;
+ /*
+ Starting from MySQL Cluster 6.3 master_bind might be in the file
+ (this is just a reservation to avoid future upgrade problems)
+ */
+ if (lines >= LINE_FOR_MASTER_BIND &&
+ init_strvar_from_file(dummy_buf, sizeof(dummy_buf), &mi->file, ""))
+ goto errwithmsg;
+ /*
+ Starting from 6.0 list of server_id of ignorable servers might be
+ in the file
+ */
+ if (lines >= LINE_FOR_REPLICATE_IGNORE_SERVER_IDS &&
+ init_dynarray_intvar_from_file(&mi->ignore_server_ids, &mi->file))
+ {
+ sql_print_error("Failed to initialize master info ignore_server_ids");
+ goto errwithmsg;
+ }
}
#ifndef HAVE_OPENSSL
if (ssl)
sql_print_warning("SSL information in the master info file "
- "('%s') are ignored because this MySQL slave was compiled "
- "without SSL support.", fname);
+ "('%s') are ignored because this MySQL slave was "
+ "compiled without SSL support.", fname);
#endif /* HAVE_OPENSSL */
/*
@@ -313,6 +401,7 @@ file '%s')", fname);
mi->connect_retry= (uint) connect_retry;
mi->ssl= (my_bool) ssl;
mi->ssl_verify_server_cert= ssl_verify_server_cert;
+ mi->heartbeat_period= master_heartbeat_period;
}
DBUG_PRINT("master_info",("log_file_name: %s position: %ld",
mi->master_log_name,
@@ -323,11 +412,12 @@ file '%s')", fname);
goto err;
mi->inited = 1;
+ mi->rli.is_relay_log_recovery= FALSE;
// now change cache READ -> WRITE - must do this before flush_master_info
reinit_io_cache(&mi->file, WRITE_CACHE, 0L, 0, 1);
if ((error=test(flush_master_info(mi, TRUE, TRUE))))
sql_print_error("Failed to flush master info file");
- pthread_mutex_unlock(&mi->data_lock);
+ mysql_mutex_unlock(&mi->data_lock);
DBUG_RETURN(error);
errwithmsg:
@@ -336,11 +426,11 @@ errwithmsg:
err:
if (fd >= 0)
{
- my_close(fd, MYF(0));
+ mysql_file_close(fd, MYF(0));
end_io_cache(&mi->file);
}
mi->fd= -1;
- pthread_mutex_unlock(&mi->data_lock);
+ mysql_mutex_unlock(&mi->data_lock);
DBUG_RETURN(1);
}
@@ -376,21 +466,41 @@ int flush_master_info(Master_info* mi,
*/
if (flush_relay_log_cache)
{
- pthread_mutex_t *log_lock= mi->rli.relay_log.get_log_lock();
+ mysql_mutex_t *log_lock= mi->rli.relay_log.get_log_lock();
IO_CACHE *log_file= mi->rli.relay_log.get_log_file();
if (need_lock_relay_log)
- pthread_mutex_lock(log_lock);
+ mysql_mutex_lock(log_lock);
- safe_mutex_assert_owner(log_lock);
+ mysql_mutex_assert_owner(log_lock);
err= flush_io_cache(log_file);
if (need_lock_relay_log)
- pthread_mutex_unlock(log_lock);
+ mysql_mutex_unlock(log_lock);
if (err)
DBUG_RETURN(2);
}
+
+ /*
+ produce a line listing the total number and all the ignored server_id:s
+ */
+ char* ignore_server_ids_buf;
+ {
+ ignore_server_ids_buf=
+ (char *) my_malloc((sizeof(::server_id) * 3 + 1) *
+ (1 + mi->ignore_server_ids.elements), MYF(MY_WME));
+ if (!ignore_server_ids_buf)
+ DBUG_RETURN(1);
+ ulong cur_len= sprintf(ignore_server_ids_buf, "%u",
+ mi->ignore_server_ids.elements);
+ for (ulong i= 0; i < mi->ignore_server_ids.elements; i++)
+ {
+ ulong s_id;
+ get_dynamic(&mi->ignore_server_ids, (uchar*) &s_id, i);
+ cur_len+= sprintf(ignore_server_ids_buf + cur_len, " %lu", s_id);
+ }
+ }
/*
We flushed the relay log BEFORE the master.info file, because if we crash
@@ -408,17 +518,27 @@ int flush_master_info(Master_info* mi,
contents of file). But because of number of lines in the first line
of file we don't care about this garbage.
*/
-
+ char heartbeat_buf[sizeof(mi->heartbeat_period) * 4]; // buffer to suffice always
+ sprintf(heartbeat_buf, "%.3f", mi->heartbeat_period);
my_b_seek(file, 0L);
my_b_printf(file,
- "%u\n%s\n%s\n%s\n%s\n%s\n%d\n%d\n%d\n%s\n%s\n%s\n%s\n%s\n%d\n",
+ "%u\n%s\n%s\n%s\n%s\n%s\n%d\n%d\n%d\n%s\n%s\n%s\n%s\n%s\n%d\n%s\n%s\n%s\n",
LINES_IN_MASTER_INFO,
mi->master_log_name, llstr(mi->master_log_pos, lbuf),
mi->host, mi->user,
mi->password, mi->port, mi->connect_retry,
(int)(mi->ssl), mi->ssl_ca, mi->ssl_capath, mi->ssl_cert,
- mi->ssl_cipher, mi->ssl_key, mi->ssl_verify_server_cert);
- DBUG_RETURN(-flush_io_cache(file));
+ mi->ssl_cipher, mi->ssl_key, mi->ssl_verify_server_cert,
+ heartbeat_buf, "", ignore_server_ids_buf);
+ my_free(ignore_server_ids_buf);
+ err= flush_io_cache(file);
+ if (sync_masterinfo_period && !err &&
+ ++(mi->sync_counter) >= sync_masterinfo_period)
+ {
+ err= my_sync(mi->fd, MYF(MY_WME));
+ mi->sync_counter= 0;
+ }
+ DBUG_RETURN(-err);
}
@@ -432,7 +552,7 @@ void end_master_info(Master_info* mi)
if (mi->fd >= 0)
{
end_io_cache(&mi->file);
- (void)my_close(mi->fd, MYF(MY_WME));
+ mysql_file_close(mi->fd, MYF(MY_WME));
mi->fd = -1;
}
mi->inited = 0;
diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h
index 05d3fc412d9..f9c8c8ea5b2 100644
--- a/sql/rpl_mi.h
+++ b/sql/rpl_mi.h
@@ -1,5 +1,4 @@
-/*
- Copyright (c) 2006, 2010, Oracle and/or its affiliates.
+/* Copyright (c) 2006, 2012, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +11,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef RPL_MI_H
#define RPL_MI_H
@@ -22,10 +20,11 @@
#include "rpl_rli.h"
#include "rpl_reporting.h"
+#include "my_sys.h"
+typedef struct st_mysql MYSQL;
/*****************************************************************************
-
Replication IO Thread
Master_info contains:
@@ -60,14 +59,16 @@
class Master_info : public Slave_reporting_capability
{
public:
- Master_info();
+ Master_info(bool is_slave_recovery);
~Master_info();
+ bool shall_ignore_server_id(ulong s_id);
+ void clear_in_memory_info(bool all);
/* the variables below are needed because we can change masters on the fly */
char master_log_name[FN_REFLEN];
- char host[HOSTNAME_LENGTH+1];
+ char host[HOSTNAME_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1];
char user[USERNAME_LENGTH+1];
- char password[MAX_PASSWORD_LENGTH+1];
+ char password[MAX_PASSWORD_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1];
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];
@@ -77,20 +78,20 @@ class Master_info : public Slave_reporting_capability
File fd; // we keep the file open, so we need to remember the file pointer
IO_CACHE file;
- pthread_mutex_t data_lock,run_lock;
- pthread_cond_t data_cond,start_cond,stop_cond;
+ mysql_mutex_t data_lock, run_lock, sleep_lock;
+ mysql_cond_t data_cond, start_cond, stop_cond, sleep_cond;
THD *io_thd;
MYSQL* mysql;
uint32 file_id; /* for 3.23 load data infile */
Relay_log_info rli;
uint port;
- uint connect_retry;
/*
to hold checksum alg in use until IO thread has received FD.
Initialized to novalue, then set to the queried from master
@@global.binlog_checksum and deactivated once FD has been received.
*/
uint8 checksum_alg_before_fd;
+ uint connect_retry;
#ifndef DBUG_OFF
int events_till_disconnect;
#endif
@@ -108,9 +109,17 @@ class Master_info : public Slave_reporting_capability
*/
long clock_diff_with_master;
+ /*
+ Keeps track of the number of events before fsyncing.
+ The option --sync-master-info determines how many
+ events should happen before fsyncing.
+ */
+ uint sync_counter;
+ float heartbeat_period; // interface with CHANGE MASTER or master.info
+ ulonglong received_heartbeats; // counter of received heartbeat events
+ DYNAMIC_ARRAY ignore_server_ids;
+ ulong master_id;
};
-
-void init_master_info_with_options(Master_info* mi);
int init_master_info(Master_info* mi, const char* master_info_fname,
const char* slave_info_fname,
bool abort_if_no_master_info_file,
@@ -119,5 +128,7 @@ void end_master_info(Master_info* mi);
int flush_master_info(Master_info* mi,
bool flush_relay_log_cache,
bool need_lock_relay_log);
+int change_master_server_id_cmp(ulong *id1, ulong *id2);
+
#endif /* HAVE_REPLICATION */
#endif /* RPL_MI_H */
diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc
index ea8324a1fc7..db65cfa8757 100644
--- a/sql/rpl_record.cc
+++ b/sql/rpl_record.cc
@@ -1,6 +1,5 @@
-/*
- Copyright (c) 2007, 2010, Oracle and/or its affiliates.
- Copyright (c) 2008-2011 Monty Program Ab
+/* Copyright (c) 2007, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,10 +12,10 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
#include "rpl_rli.h"
#include "rpl_record.h"
#include "slave.h" // Need to pull in slave_print_msg
@@ -80,8 +79,6 @@ pack_row(TABLE *table, MY_BITMAP const* cols,
unsigned int null_mask= 1U;
for ( ; (field= *p_field) ; p_field++)
{
- DBUG_PRINT("debug", ("null_mask: %d null_ptr: %p row_data: %p null_byte_count: %d",
- null_mask, null_ptr, row_data, null_byte_count));
if (bitmap_is_set(cols, p_field - table->field))
{
my_ptrdiff_t offset;
@@ -107,11 +104,12 @@ pack_row(TABLE *table, MY_BITMAP const* cols,
#endif
pack_ptr= field->pack(pack_ptr, field->ptr + offset,
field->max_data_length());
- DBUG_PRINT("debug", ("field: %s; pack_ptr: 0x%lx;"
+ DBUG_PRINT("debug", ("field: %s; real_type: %d, pack_ptr: 0x%lx;"
" pack_ptr':0x%lx; bytes: %d",
- field->field_name, (ulong) old_pack_ptr,
- (ulong) pack_ptr,
+ field->field_name, field->real_type(),
+ (ulong) old_pack_ptr, (ulong) pack_ptr,
(int) (pack_ptr - old_pack_ptr)));
+ DBUG_DUMP("packed_data", old_pack_ptr, pack_ptr - old_pack_ptr);
}
null_mask <<= 1;
@@ -153,29 +151,39 @@ pack_row(TABLE *table, MY_BITMAP const* cols,
the various member functions of Field and subclasses expect to
write.
- The row is assumed to only consist of the fields for which the corresponding
- bit in bitset @c cols is set; the other parts of the record are left alone.
+ The row is assumed to only consist of the fields for which the
+ corresponding bit in bitset @c cols is set; the other parts of the
+ record are left alone.
At most @c colcnt columns are read: if the table is larger than
that, the remaining fields are not filled in.
- @param rli Relay log info
+ @note The relay log information can be NULL, which means that no
+ checking or comparison with the source table is done, simply
+ because it is not used. This feature is used by MySQL Backup to
+ unpack a row from from the backup image, but can be used for other
+ purposes as well.
+
+ @param rli Relay log info, which can be NULL
@param table Table to unpack into
@param colcnt Number of columns to read from record
@param row_data
Packed row data
@param cols Pointer to bitset describing columns to fill in
- @param row_end Pointer to variable that will hold the value of the
- one-after-end position for the row
+ @param curr_row_end
+ Pointer to variable that will hold the value of the
+ one-after-end position for the current row
@param master_reclength
Pointer to variable that will be set to the length of the
record on the master side
+ @param row_end
+ Pointer to variable that will hold the value of the
+ end position for the data in the row event
@retval 0 No error
- @retval ER_NO_DEFAULT_FOR_FIELD
- Returned if one of the fields existing on the slave but not on the
- master does not have a default value (and isn't nullable)
+ @retval HA_ERR_GENERIC
+ A generic, internal, error caused the unpacking to fail.
@retval ER_SLAVE_CORRUPT_EVENT
Found error when trying to unpack fields.
*/
@@ -183,12 +191,13 @@ pack_row(TABLE *table, MY_BITMAP const* cols,
int
unpack_row(Relay_log_info const *rli,
TABLE *table, uint const colcnt,
- uchar const *const row_data, uchar const *const row_buffer_end,
- MY_BITMAP const *cols,
- uchar const **const row_end, ulong *const master_reclength)
+ uchar const *const row_data, MY_BITMAP const *cols,
+ uchar const **const current_row_end, ulong *const master_reclength,
+ uchar const *const row_end)
{
DBUG_ENTER("unpack_row");
DBUG_ASSERT(row_data);
+ DBUG_ASSERT(table);
size_t const master_null_byte_count= (bitmap_bits_set(cols) + 7) / 8;
int error= 0;
@@ -206,10 +215,38 @@ unpack_row(Relay_log_info const *rli,
// The "current" null bits
unsigned int null_bits= *null_ptr++;
uint i= 0;
- table_def *tabledef= ((Relay_log_info*)rli)->get_tabledef(table);
+ table_def *tabledef= NULL;
+ TABLE *conv_table= NULL;
+ bool table_found= rli && rli->get_table_data(table, &tabledef, &conv_table);
+ DBUG_PRINT("debug", ("Table data: table_found: %d, tabldef: %p, conv_table: %p",
+ table_found, tabledef, conv_table));
+ DBUG_ASSERT(table_found);
+
+ /*
+ If rli is NULL it means that there is no source table and that the
+ row shall just be unpacked without doing any checks. This feature
+ is used by MySQL Backup, but can be used for other purposes as
+ well.
+ */
+ if (rli && !table_found)
+ DBUG_RETURN(HA_ERR_GENERIC);
+
for (field_ptr= begin_ptr ; field_ptr < end_ptr && *field_ptr ; ++field_ptr)
{
- Field *const f= *field_ptr;
+ /*
+ If there is a conversion table, we pick up the field pointer to
+ the conversion table. If the conversion table or the field
+ pointer is NULL, no conversions are necessary.
+ */
+ Field *conv_field=
+ conv_table ? conv_table->field[field_ptr - begin_ptr] : NULL;
+ Field *const f=
+ conv_field ? conv_field : *field_ptr;
+ DBUG_PRINT("debug", ("Conversion %srequired for field '%s' (#%ld)",
+ conv_field ? "" : "not ",
+ (*field_ptr)->field_name,
+ (long) (field_ptr - begin_ptr)));
+ DBUG_ASSERT(f != NULL);
/*
No need to bother about columns that does not exist: they have
@@ -271,7 +308,7 @@ unpack_row(Relay_log_info const *rli,
#ifndef DBUG_OFF
uchar const *const old_pack_ptr= pack_ptr;
#endif
- pack_ptr= f->unpack(f->ptr, pack_ptr, row_buffer_end, metadata);
+ pack_ptr= f->unpack(f->ptr, pack_ptr, row_end, metadata);
DBUG_PRINT("debug", ("field: %s; metadata: 0x%x;"
" pack_ptr: 0x%lx; pack_ptr': 0x%lx; bytes: %d",
f->field_name, metadata,
@@ -280,12 +317,46 @@ unpack_row(Relay_log_info const *rli,
if (!pack_ptr)
{
rli->report(ERROR_LEVEL, ER_SLAVE_CORRUPT_EVENT,
- "Could not read field `%s` of table `%s`.`%s`",
+ "Could not read field '%s' of table '%s.%s'",
f->field_name, table->s->db.str,
table->s->table_name.str);
DBUG_RETURN(ER_SLAVE_CORRUPT_EVENT);
}
}
+
+ /*
+ If conv_field is set, then we are doing a conversion. In this
+ case, we have unpacked the master data to the conversion
+ table, so we need to copy the value stored in the conversion
+ table into the final table and do the conversion at the same time.
+ */
+ if (conv_field)
+ {
+ Copy_field copy;
+#ifndef DBUG_OFF
+ char source_buf[MAX_FIELD_WIDTH];
+ char value_buf[MAX_FIELD_WIDTH];
+ String source_type(source_buf, sizeof(source_buf), system_charset_info);
+ String value_string(value_buf, sizeof(value_buf), system_charset_info);
+ 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,
+ source_type.c_ptr_safe(), value_string.c_ptr_safe()));
+#endif
+ copy.set(*field_ptr, f, TRUE);
+ (*copy.do_copy)(&copy);
+#ifndef DBUG_OFF
+ char target_buf[MAX_FIELD_WIDTH];
+ String target_type(target_buf, sizeof(target_buf), system_charset_info);
+ (*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,
+ target_type.c_ptr_safe(), value_string.c_ptr_safe()));
+#endif
+ }
+
null_mask <<= 1;
}
i++;
@@ -307,8 +378,11 @@ unpack_row(Relay_log_info const *rli,
}
DBUG_ASSERT(null_mask & 0xFF); // One of the 8 LSB should be set
- if (!((null_bits & null_mask) && tabledef->maybe_null(i)))
- pack_ptr+= tabledef->calc_field_size(i, (uchar *) pack_ptr);
+ if (!((null_bits & null_mask) && tabledef->maybe_null(i))) {
+ uint32 len= tabledef->calc_field_size(i, (uchar *) pack_ptr);
+ DBUG_DUMP("field_data", pack_ptr, len);
+ pack_ptr+= len;
+ }
null_mask <<= 1;
}
}
@@ -321,7 +395,7 @@ unpack_row(Relay_log_info const *rli,
DBUG_DUMP("row_data", row_data, pack_ptr - row_data);
- *row_end = pack_ptr;
+ *current_row_end = pack_ptr;
if (master_reclength)
{
if (*field_ptr)
diff --git a/sql/rpl_record.h b/sql/rpl_record.h
index eefcdebc114..d4eb8986846 100644
--- a/sql/rpl_record.h
+++ b/sql/rpl_record.h
@@ -1,6 +1,5 @@
-/*
- Copyright (c) 2007, 2010, Oracle and/or its affiliates.
- Copyright (c) 2008-2011 Monty Program Ab
+/* Copyright (c) 2007, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2013, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,13 +12,17 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef RPL_RECORD_H
#define RPL_RECORD_H
#include <rpl_reporting.h>
+#include "my_global.h" /* uchar */
+
+class Relay_log_info;
+struct TABLE;
+typedef struct st_bitmap MY_BITMAP;
#if !defined(MYSQL_CLIENT)
size_t pack_row(TABLE* table, MY_BITMAP const* cols,
@@ -29,9 +32,9 @@ size_t pack_row(TABLE* table, MY_BITMAP const* cols,
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
int unpack_row(Relay_log_info const *rli,
TABLE *table, uint const colcnt,
- uchar const *const row_data, uchar const *row_buffer_end,
- MY_BITMAP const *cols,
- uchar const **const row_end, ulong *const master_reclength);
+ uchar const *const row_data, MY_BITMAP const *cols,
+ uchar const **const curr_row_end, ulong *const master_reclength,
+ uchar const *const row_end);
// Fill table's record[0] with default values.
int prepare_record(TABLE *const table, const uint skip, const bool check);
diff --git a/sql/rpl_record_old.cc b/sql/rpl_record_old.cc
index 6709c4f6e1a..fa0c49b413c 100644
--- a/sql/rpl_record_old.cc
+++ b/sql/rpl_record_old.cc
@@ -1,5 +1,4 @@
-/* Copyright (c) 2007 MySQL AB
- Use is subject to license terms.
+/* Copyright (c) 2007, 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
@@ -14,9 +13,11 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h" // REQUIRED by other includes
#include "rpl_rli.h"
#include "rpl_record_old.h"
+#include "log_event.h" // Log_event_type
size_t
pack_row_old(TABLE *table, MY_BITMAP const* cols,
diff --git a/sql/rpl_record_old.h b/sql/rpl_record_old.h
index 6bce1aadf8d..ea981fb23c3 100644
--- a/sql/rpl_record_old.h
+++ b/sql/rpl_record_old.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007 MySQL AB
+/* Copyright (c) 2007, 2010, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,11 +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 Street, Fifth Floor, Boston, MA 02110-1301, USA */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef RPL_RECORD_OLD_H
#define RPL_RECORD_OLD_H
+#include "log_event.h" /* Log_event_type */
+
#ifndef MYSQL_CLIENT
size_t pack_row_old(TABLE *table, MY_BITMAP const* cols,
uchar *row_data, const uchar *record);
diff --git a/sql/rpl_reporting.cc b/sql/rpl_reporting.cc
index 65370d1e3d9..f442f3a37c0 100644
--- a/sql/rpl_reporting.cc
+++ b/sql/rpl_reporting.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2007 MySQL AB, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+
+/* Copyright (c) 2007, 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
@@ -14,8 +14,17 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
+#include "sql_priv.h"
#include "rpl_reporting.h"
+#include "log.h" // sql_print_error, sql_print_warning,
+ // sql_print_information
+
+Slave_reporting_capability::Slave_reporting_capability(char const *thread_name)
+ : m_thread_name(thread_name)
+{
+ mysql_mutex_init(key_mutex_slave_reporting_capability_err_lock,
+ &err_lock, MY_MUTEX_INIT_FAST);
+}
void
Slave_reporting_capability::report(loglevel level, int err_code,
@@ -28,7 +37,7 @@ Slave_reporting_capability::report(loglevel level, int err_code,
va_list args;
va_start(args, msg);
- pthread_mutex_lock(&err_lock);
+ mysql_mutex_lock(&err_lock);
switch (level)
{
case ERROR_LEVEL:
@@ -54,7 +63,7 @@ Slave_reporting_capability::report(loglevel level, int err_code,
my_vsnprintf(pbuff, pbuffsize, msg, args);
- pthread_mutex_unlock(&err_lock);
+ mysql_mutex_unlock(&err_lock);
va_end(args);
/* If the msg string ends with '.', do not add a ',' it would be ugly */
@@ -66,5 +75,5 @@ Slave_reporting_capability::report(loglevel level, int err_code,
Slave_reporting_capability::~Slave_reporting_capability()
{
- pthread_mutex_destroy(&err_lock);
+ mysql_mutex_destroy(&err_lock);
}
diff --git a/sql/rpl_reporting.h b/sql/rpl_reporting.h
index cc7c4c2bb71..2b5e0527b9b 100644
--- a/sql/rpl_reporting.h
+++ b/sql/rpl_reporting.h
@@ -1,5 +1,4 @@
-/* Copyright (c) 2007 MySQL AB, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,11 +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-1301 USA */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef RPL_REPORTING_H
#define RPL_REPORTING_H
+#include "my_sys.h" /* loglevel */
+
/**
Maximum size of an error message from a slave thread.
*/
@@ -33,17 +34,13 @@ class Slave_reporting_capability
{
public:
/** lock used to synchronize m_last_error on 'SHOW SLAVE STATUS' **/
- mutable pthread_mutex_t err_lock;
+ mutable mysql_mutex_t err_lock;
/**
Constructor.
@param thread_name Printable name of the slave thread that is reporting.
*/
- Slave_reporting_capability(char const *thread_name)
- : m_thread_name(thread_name)
- {
- pthread_mutex_init(&err_lock, MY_MUTEX_INIT_FAST);
- }
+ Slave_reporting_capability(char const *thread_name);
/**
Writes a message and, if it's an error message, to Last_Error
@@ -63,9 +60,9 @@ public:
STATUS</code>.
*/
void clear_error() {
- pthread_mutex_lock(&err_lock);
+ mysql_mutex_lock(&err_lock);
m_last_error.clear();
- pthread_mutex_unlock(&err_lock);
+ mysql_mutex_unlock(&err_lock);
}
/**
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index 57c6e1afc7d..b01f74408a6 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -1,5 +1,5 @@
-/*
- Copyright (c) 2006, 2012, Oracle and/or its affiliates.
+/* Copyright (c) 2006, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2011, 2013, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,25 +12,38 @@
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 "mysql_priv.h"
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include "sql_priv.h"
+#include "unireg.h" // HAVE_*
#include "rpl_mi.h"
#include "rpl_rli.h"
+#include "sql_base.h" // close_thread_tables
#include <my_dir.h> // For MY_STAT
#include "sql_repl.h" // For check_binlog_magic
+#include "log_event.h" // Format_description_log_event, Log_event,
+ // FORMAT_DESCRIPTION_LOG_EVENT, ROTATE_EVENT,
+ // PREFIX_SQL_LOAD
#include "rpl_utility.h"
+#include "transaction.h"
+#include "sql_parse.h" // end_trans, ROLLBACK
+#include <mysql/plugin.h>
+#include <mysql/service_thd_wait.h>
static int count_relay_log_space(Relay_log_info* rli);
+// Defined in slave.cc
+int init_intvar_from_file(int* var, IO_CACHE* f, int default_val);
+int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
+ const char *default_val);
-Relay_log_info::Relay_log_info()
+Relay_log_info::Relay_log_info(bool is_slave_recovery)
:Slave_reporting_capability("SQL"),
no_storage(FALSE), replicate_same_server_id(::replicate_same_server_id),
- info_fd(-1), cur_log_fd(-1), save_temporary_tables(0),
- cur_log_old_open_count(0), group_relay_log_pos(0), event_relay_log_pos(0),
+ info_fd(-1), cur_log_fd(-1), relay_log(&sync_relaylog_period),
+ sync_counter(0), is_relay_log_recovery(is_slave_recovery),
+ save_temporary_tables(0), cur_log_old_open_count(0), group_relay_log_pos(0),
+ event_relay_log_pos(0),
#if HAVE_valgrind
is_fake(FALSE),
#endif
@@ -40,26 +53,37 @@ Relay_log_info::Relay_log_info()
inited(0), abort_slave(0), slave_running(0), until_condition(UNTIL_NONE),
until_log_pos(0), retried_trans(0),
tables_to_lock(0), tables_to_lock_count(0),
- last_event_start_time(0),
- deferred_events(NULL),
- m_flags(0),
+ last_event_start_time(0), deferred_events(NULL),m_flags(0),
+ row_stmt_start_timestamp(0), long_find_row_note_printed(false),
m_annotate_event(0)
{
DBUG_ENTER("Relay_log_info::Relay_log_info");
+#ifdef HAVE_PSI_INTERFACE
+ relay_log.set_psi_keys(key_RELAYLOG_LOCK_index,
+ key_RELAYLOG_update_cond,
+ key_file_relaylog,
+ key_file_relaylog_index,
+ key_RELAYLOG_COND_queue_busy);
+#endif
+
group_relay_log_name[0]= event_relay_log_name[0]=
group_master_log_name[0]= 0;
until_log_name[0]= ign_master_log_name_end[0]= 0;
bzero((char*) &info_file, sizeof(info_file));
bzero((char*) &cache_buf, sizeof(cache_buf));
cached_charset_invalidate();
- pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST);
- pthread_mutex_init(&data_lock, MY_MUTEX_INIT_FAST);
- pthread_mutex_init(&log_space_lock, MY_MUTEX_INIT_FAST);
- pthread_cond_init(&data_cond, NULL);
- pthread_cond_init(&start_cond, NULL);
- pthread_cond_init(&stop_cond, NULL);
- pthread_cond_init(&log_space_cond, NULL);
+ mysql_mutex_init(key_relay_log_info_run_lock, &run_lock, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_relay_log_info_data_lock,
+ &data_lock, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_relay_log_info_log_space_lock,
+ &log_space_lock, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_relay_log_info_sleep_lock, &sleep_lock, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_relay_log_info_data_cond, &data_cond, NULL);
+ mysql_cond_init(key_relay_log_info_start_cond, &start_cond, NULL);
+ mysql_cond_init(key_relay_log_info_stop_cond, &stop_cond, NULL);
+ mysql_cond_init(key_relay_log_info_log_space_cond, &log_space_cond, NULL);
+ mysql_cond_init(key_relay_log_info_sleep_cond, &sleep_cond, NULL);
relay_log.init_pthread_objects();
DBUG_VOID_RETURN;
}
@@ -69,13 +93,15 @@ Relay_log_info::~Relay_log_info()
{
DBUG_ENTER("Relay_log_info::~Relay_log_info");
- pthread_mutex_destroy(&run_lock);
- pthread_mutex_destroy(&data_lock);
- pthread_mutex_destroy(&log_space_lock);
- pthread_cond_destroy(&data_cond);
- pthread_cond_destroy(&start_cond);
- pthread_cond_destroy(&stop_cond);
- pthread_cond_destroy(&log_space_cond);
+ mysql_mutex_destroy(&run_lock);
+ mysql_mutex_destroy(&data_lock);
+ mysql_mutex_destroy(&log_space_lock);
+ mysql_mutex_destroy(&sleep_lock);
+ mysql_cond_destroy(&data_cond);
+ mysql_cond_destroy(&start_cond);
+ mysql_cond_destroy(&stop_cond);
+ mysql_cond_destroy(&log_space_cond);
+ mysql_cond_destroy(&sleep_cond);
relay_log.cleanup();
free_annotate_event();
DBUG_VOID_RETURN;
@@ -95,7 +121,7 @@ int init_relay_log_info(Relay_log_info* rli,
if (rli->inited) // Set if this function called
DBUG_RETURN(0);
fn_format(fname, info_fname, mysql_data_home, "", 4+32);
- pthread_mutex_lock(&rli->data_lock);
+ mysql_mutex_lock(&rli->data_lock);
info_fd = rli->info_fd;
rli->cur_log_fd = -1;
rli->slave_skip_counter=0;
@@ -110,7 +136,7 @@ int init_relay_log_info(Relay_log_info* rli,
if (fn_format(pattern, PREFIX_SQL_LOAD, pattern, "",
MY_SAFE_PATH | MY_RETURN_REAL_PATH) == NullS)
{
- pthread_mutex_unlock(&rli->data_lock);
+ mysql_mutex_unlock(&rli->data_lock);
sql_print_error("Unable to use slave's temporary directory %s",
slave_load_tmpdir);
DBUG_RETURN(1);
@@ -139,7 +165,7 @@ int init_relay_log_info(Relay_log_info* rli,
if (opt_relay_logname &&
opt_relay_logname[strlen(opt_relay_logname) - 1] == FN_LIBCHAR)
{
- pthread_mutex_unlock(&rli->data_lock);
+ mysql_mutex_unlock(&rli->data_lock);
sql_print_error("Path '%s' is a directory name, please specify \
a file name for --relay-log option", opt_relay_logname);
DBUG_RETURN(1);
@@ -151,7 +177,7 @@ a file name for --relay-log option", opt_relay_logname);
opt_relaylog_index_name[strlen(opt_relaylog_index_name) - 1]
== FN_LIBCHAR)
{
- pthread_mutex_unlock(&rli->data_lock);
+ mysql_mutex_unlock(&rli->data_lock);
sql_print_error("Path '%s' is a directory name, please specify \
a file name for --relay-log-index option", opt_relaylog_index_name);
DBUG_RETURN(1);
@@ -163,7 +189,8 @@ a file name for --relay-log-index option", opt_relaylog_index_name);
ln= rli->relay_log.generate_name(opt_relay_logname, "-relay-bin",
1, buf);
/* We send the warning only at startup, not after every RESET SLAVE */
- if (!opt_relay_logname && !opt_relaylog_index_name && !name_warning_sent)
+ if (!opt_relay_logname && !opt_relaylog_index_name && !name_warning_sent &&
+ !opt_bootstrap)
{
/*
User didn't give us info to name the relay log index file.
@@ -192,7 +219,7 @@ a file name for --relay-log-index option", opt_relaylog_index_name);
(max_relay_log_size ? max_relay_log_size :
max_binlog_size), 1, TRUE))
{
- pthread_mutex_unlock(&rli->data_lock);
+ mysql_mutex_unlock(&rli->data_lock);
sql_print_error("Failed in open_log() called from init_relay_log_info()");
DBUG_RETURN(1);
}
@@ -206,12 +233,13 @@ a file name for --relay-log-index option", opt_relaylog_index_name);
the old descriptor and re-create the old file
*/
if (info_fd >= 0)
- my_close(info_fd, MYF(MY_WME));
- if ((info_fd = my_open(fname, O_CREAT|O_RDWR|O_BINARY, MYF(MY_WME))) < 0)
+ mysql_file_close(info_fd, MYF(MY_WME));
+ if ((info_fd= mysql_file_open(key_file_relay_log_info,
+ fname, O_CREAT|O_RDWR|O_BINARY, MYF(MY_WME))) < 0)
{
sql_print_error("Failed to create a new relay log info file (\
file '%s', errno %d)", fname, my_errno);
- msg= current_thd->main_da.message();
+ msg= current_thd->stmt_da->message();
goto err;
}
if (init_io_cache(&rli->info_file, info_fd, IO_SIZE*2, READ_CACHE, 0L,0,
@@ -219,7 +247,7 @@ file '%s', errno %d)", fname, my_errno);
{
sql_print_error("Failed to create a cache on relay log info file '%s'",
fname);
- msg= current_thd->main_da.message();
+ msg= current_thd->stmt_da->message();
goto err;
}
@@ -241,7 +269,8 @@ file '%s', errno %d)", fname, my_errno);
else
{
int error=0;
- if ((info_fd = my_open(fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0)
+ if ((info_fd= mysql_file_open(key_file_relay_log_info,
+ fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0)
{
sql_print_error("\
Failed to open the existing relay log info file '%s' (errno %d)",
@@ -258,10 +287,10 @@ Failed to open the existing relay log info file '%s' (errno %d)",
if (error)
{
if (info_fd >= 0)
- my_close(info_fd, MYF(0));
+ mysql_file_close(info_fd, MYF(0));
rli->info_fd= -1;
rli->relay_log.close(LOG_CLOSE_INDEX | LOG_CLOSE_STOP_EVENT);
- pthread_mutex_unlock(&rli->data_lock);
+ mysql_mutex_unlock(&rli->data_lock);
DBUG_RETURN(1);
}
}
@@ -281,11 +310,13 @@ Failed to open the existing relay log info file '%s' (errno %d)",
msg="Error reading slave log configuration";
goto err;
}
- strmake(rli->event_relay_log_name,rli->group_relay_log_name,
- sizeof(rli->event_relay_log_name)-1);
+ strmake_buf(rli->event_relay_log_name,rli->group_relay_log_name);
rli->group_relay_log_pos= rli->event_relay_log_pos= relay_log_pos;
rli->group_master_log_pos= master_log_pos;
+ if (rli->is_relay_log_recovery && init_recovery(rli->mi, &msg))
+ goto err;
+
if (init_relay_log_pos(rli,
rli->group_relay_log_name,
rli->group_relay_log_pos,
@@ -317,24 +348,27 @@ Failed to open the existing relay log info file '%s' (errno %d)",
*/
reinit_io_cache(&rli->info_file, WRITE_CACHE,0L,0,1);
if ((error= flush_relay_log_info(rli)))
- sql_print_error("Failed to flush relay log info file");
+ {
+ msg= "Failed to flush relay log info file";
+ goto err;
+ }
if (count_relay_log_space(rli))
{
msg="Error counting relay log space";
goto err;
}
rli->inited= 1;
- pthread_mutex_unlock(&rli->data_lock);
+ mysql_mutex_unlock(&rli->data_lock);
DBUG_RETURN(error);
err:
sql_print_error("%s", msg);
end_io_cache(&rli->info_file);
if (info_fd >= 0)
- my_close(info_fd, MYF(0));
+ mysql_file_close(info_fd, MYF(0));
rli->info_fd= -1;
rli->relay_log.close(LOG_CLOSE_INDEX | LOG_CLOSE_STOP_EVENT);
- pthread_mutex_unlock(&rli->data_lock);
+ mysql_mutex_unlock(&rli->data_lock);
DBUG_RETURN(1);
}
@@ -343,7 +377,8 @@ static inline int add_relay_log(Relay_log_info* rli,LOG_INFO* linfo)
{
MY_STAT s;
DBUG_ENTER("add_relay_log");
- if (!my_stat(linfo->log_file_name,&s,MYF(0)))
+ if (!mysql_file_stat(key_file_relaylog,
+ linfo->log_file_name, &s, MYF(0)))
{
sql_print_error("log %s listed in the index, but failed to stat",
linfo->log_file_name);
@@ -443,10 +478,10 @@ int init_relay_log_pos(Relay_log_info* rli,const char* log,
DBUG_PRINT("info", ("pos: %lu", (ulong) pos));
*errmsg=0;
- pthread_mutex_t *log_lock=rli->relay_log.get_log_lock();
+ mysql_mutex_t *log_lock= rli->relay_log.get_log_lock();
if (need_data_lock)
- pthread_mutex_lock(&rli->data_lock);
+ mysql_mutex_lock(&rli->data_lock);
/*
Slave threads are not the only users of init_relay_log_pos(). CHANGE MASTER
@@ -466,13 +501,13 @@ int init_relay_log_pos(Relay_log_info* rli,const char* log,
rli->relay_log.description_event_for_exec= new
Format_description_log_event(3);
- pthread_mutex_lock(log_lock);
+ mysql_mutex_lock(log_lock);
/* Close log file and free buffers if it's already open */
if (rli->cur_log_fd >= 0)
{
end_io_cache(&rli->cache_buf);
- my_close(rli->cur_log_fd, MYF(MY_WME));
+ mysql_file_close(rli->cur_log_fd, MYF(MY_WME));
rli->cur_log_fd = -1;
}
@@ -493,10 +528,8 @@ int init_relay_log_pos(Relay_log_info* rli,const char* log,
*errmsg="Could not find target log during relay log initialization";
goto err;
}
- strmake(rli->group_relay_log_name,rli->linfo.log_file_name,
- sizeof(rli->group_relay_log_name)-1);
- strmake(rli->event_relay_log_name,rli->linfo.log_file_name,
- sizeof(rli->event_relay_log_name)-1);
+ strmake_buf(rli->group_relay_log_name,rli->linfo.log_file_name);
+ strmake_buf(rli->event_relay_log_name,rli->linfo.log_file_name);
if (rli->relay_log.is_active(rli->linfo.log_file_name))
{
/*
@@ -607,12 +640,12 @@ err:
*/
if (!relay_log_purge)
rli->log_space_limit= 0;
- pthread_cond_broadcast(&rli->data_cond);
+ mysql_cond_broadcast(&rli->data_cond);
- pthread_mutex_unlock(log_lock);
+ mysql_mutex_unlock(log_lock);
if (need_data_lock)
- pthread_mutex_unlock(&rli->data_lock);
+ mysql_mutex_unlock(&rli->data_lock);
if (!rli->relay_log.description_event_for_exec->is_valid() && !*errmsg)
*errmsg= "Invalid Format_description log event; could be out of memory";
@@ -663,7 +696,7 @@ int Relay_log_info::wait_for_pos(THD* thd, String* log_name,
log_name->c_ptr(), (ulong) log_pos, (ulong) timeout));
set_timespec(abstime,timeout);
- pthread_mutex_lock(&data_lock);
+ mysql_mutex_lock(&data_lock);
msg= thd->enter_cond(&data_cond, &data_lock,
"Waiting for the slave SQL thread to "
"advance position");
@@ -776,26 +809,28 @@ int Relay_log_info::wait_for_pos(THD* thd, String* log_name,
DBUG_PRINT("info",("Waiting for master update"));
/*
- We are going to pthread_cond_(timed)wait(); if the SQL thread stops it
+ We are going to mysql_cond_(timed)wait(); if the SQL thread stops it
will wake us up.
*/
+ thd_wait_begin(thd, THD_WAIT_BINLOG);
if (timeout > 0)
{
/*
- Note that pthread_cond_timedwait checks for the timeout
+ Note that mysql_cond_timedwait checks for the timeout
before for the condition ; i.e. it returns ETIMEDOUT
if the system time equals or exceeds the time specified by abstime
before the condition variable is signaled or broadcast, _or_ if
the absolute time specified by abstime has already passed at the time
of the call.
- For that reason, pthread_cond_timedwait will do the "timeoutting" job
+ For that reason, mysql_cond_timedwait will do the "timeoutting" job
even if its condition is always immediately signaled (case of a loaded
master).
*/
- error=pthread_cond_timedwait(&data_cond, &data_lock, &abstime);
+ error= mysql_cond_timedwait(&data_cond, &data_lock, &abstime);
}
else
- pthread_cond_wait(&data_cond, &data_lock);
+ mysql_cond_wait(&data_cond, &data_lock);
+ thd_wait_end(thd);
DBUG_PRINT("info",("Got signal of master update or timed out"));
if (error == ETIMEDOUT || error == ETIME)
{
@@ -831,11 +866,10 @@ void Relay_log_info::inc_group_relay_log_pos(ulonglong log_pos,
DBUG_ENTER("Relay_log_info::inc_group_relay_log_pos");
if (!skip_lock)
- pthread_mutex_lock(&data_lock);
+ mysql_mutex_lock(&data_lock);
inc_event_relay_log_pos();
group_relay_log_pos= event_relay_log_pos;
- strmake(group_relay_log_name,event_relay_log_name,
- sizeof(group_relay_log_name)-1);
+ strmake_buf(group_relay_log_name,event_relay_log_name);
notify_group_relay_log_name_update();
@@ -875,9 +909,9 @@ void Relay_log_info::inc_group_relay_log_pos(ulonglong log_pos,
{
group_master_log_pos= log_pos;
}
- pthread_cond_broadcast(&data_cond);
+ mysql_cond_broadcast(&data_cond);
if (!skip_lock)
- pthread_mutex_unlock(&data_lock);
+ mysql_mutex_unlock(&data_lock);
DBUG_VOID_RETURN;
}
@@ -946,7 +980,7 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset,
DBUG_ASSERT(rli->mi->slave_running == 0);
rli->slave_skip_counter=0;
- pthread_mutex_lock(&rli->data_lock);
+ mysql_mutex_lock(&rli->data_lock);
/*
we close the relay log fd possibly left open by the slave SQL thread,
@@ -957,7 +991,7 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset,
if (rli->cur_log_fd >= 0)
{
end_io_cache(&rli->cache_buf);
- my_close(rli->cur_log_fd, MYF(MY_WME));
+ mysql_file_close(rli->cur_log_fd, MYF(MY_WME));
rli->cur_log_fd= -1;
}
@@ -968,10 +1002,8 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset,
goto err;
}
/* Save name of used relay log file */
- strmake(rli->group_relay_log_name, rli->relay_log.get_log_fname(),
- sizeof(rli->group_relay_log_name)-1);
- strmake(rli->event_relay_log_name, rli->relay_log.get_log_fname(),
- sizeof(rli->event_relay_log_name)-1);
+ strmake_buf(rli->group_relay_log_name, rli->relay_log.get_log_fname());
+ strmake_buf(rli->event_relay_log_name, rli->relay_log.get_log_fname());
rli->group_relay_log_pos= rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE;
if (count_relay_log_space(rli))
{
@@ -989,7 +1021,7 @@ err:
char buf[22];
#endif
DBUG_PRINT("info",("log_space_total: %s",llstr(rli->log_space_total,buf)));
- pthread_mutex_unlock(&rli->data_lock);
+ mysql_mutex_unlock(&rli->data_lock);
DBUG_RETURN(error);
}
@@ -1041,7 +1073,7 @@ bool Relay_log_info::is_until_satisfied(THD *thd, Log_event *ev)
DBUG_RETURN(FALSE);
log_name= group_master_log_name;
log_pos= (!ev)? group_master_log_pos :
- ((thd->options & OPTION_BEGIN || !ev->log_pos) ?
+ ((thd->variables.option_bits & OPTION_BEGIN || !ev->log_pos) ?
group_master_log_pos : ev->log_pos - ev->data_written);
}
else
@@ -1165,7 +1197,7 @@ void Relay_log_info::stmt_done(my_off_t event_master_log_pos,
middle of the "transaction". START SLAVE will resume at BEGIN
while the MyISAM table has already been updated.
*/
- if ((sql_thd->options & OPTION_BEGIN) && opt_using_transactions)
+ if ((sql_thd->variables.option_bits & OPTION_BEGIN) && opt_using_transactions)
inc_event_relay_log_pos();
else
{
@@ -1179,7 +1211,8 @@ void Relay_log_info::stmt_done(my_off_t event_master_log_pos,
is that value may take some time to display in
Seconds_Behind_Master - not critical).
*/
- if (!(event_creation_time == 0 IF_DBUG(&& debug_not_change_ts_if_art_event > 0)))
+ if (!(event_creation_time == 0 &&
+ IF_DBUG(debug_not_change_ts_if_art_event > 0, 1)))
last_master_timestamp= event_creation_time;
}
}
@@ -1204,24 +1237,50 @@ void Relay_log_info::cleanup_context(THD *thd, bool error)
*/
if (error)
{
- ha_autocommit_or_rollback(thd, 1); // if a "statement transaction"
- end_trans(thd, ROLLBACK); // if a "real transaction"
+ trans_rollback_stmt(thd); // if a "statement transaction"
+ trans_rollback(thd); // if a "real transaction"
}
m_table_map.clear_tables();
- close_thread_tables(thd);
- clear_tables_to_lock();
+ slave_close_thread_tables(thd);
+ if (error)
+ thd->mdl_context.release_transactional_locks();
clear_flag(IN_STMT);
/*
Cleanup for the flags that have been set at do_apply_event.
*/
- thd->options&= ~OPTION_NO_FOREIGN_KEY_CHECKS;
- thd->options&= ~OPTION_RELAXED_UNIQUE_CHECKS;
- last_event_start_time= 0;
+ thd->variables.option_bits&= ~OPTION_NO_FOREIGN_KEY_CHECKS;
+ thd->variables.option_bits&= ~OPTION_RELAXED_UNIQUE_CHECKS;
+
+ /*
+ Reset state related to long_find_row notes in the error log:
+ - timestamp
+ - flag that decides whether the slave prints or not
+ */
+ reset_row_stmt_start_timestamp();
+ unset_long_find_row_note_printed();
+
DBUG_VOID_RETURN;
}
void Relay_log_info::clear_tables_to_lock()
{
+ DBUG_ENTER("Relay_log_info::clear_tables_to_lock()");
+#ifndef DBUG_OFF
+ /**
+ When replicating in RBR and MyISAM Merge tables are involved
+ open_and_lock_tables (called in do_apply_event) appends the
+ base tables to the list of tables_to_lock. Then these are
+ removed from the list in close_thread_tables (which is called
+ before we reach this point).
+
+ This assertion just confirms that we get no surprises at this
+ point.
+ */
+ uint i=0;
+ for (TABLE_LIST *ptr= tables_to_lock ; ptr ; ptr= ptr->next_global, i++) ;
+ DBUG_ASSERT(i == tables_to_lock_count);
+#endif
+
while (tables_to_lock)
{
uchar* to_free= reinterpret_cast<uchar*>(tables_to_lock);
@@ -1230,12 +1289,56 @@ void Relay_log_info::clear_tables_to_lock()
tables_to_lock->m_tabledef.table_def::~table_def();
tables_to_lock->m_tabledef_valid= FALSE;
}
+
+ /*
+ If blob fields were used during conversion of field values
+ from the master table into the slave table, then we need to
+ free the memory used temporarily to store their values before
+ copying into the slave's table.
+ */
+ if (tables_to_lock->m_conv_table)
+ free_blobs(tables_to_lock->m_conv_table);
+
tables_to_lock=
static_cast<RPL_TABLE_LIST*>(tables_to_lock->next_global);
tables_to_lock_count--;
- my_free(to_free, MYF(MY_WME));
+ my_free(to_free);
}
DBUG_ASSERT(tables_to_lock == NULL && tables_to_lock_count == 0);
+ DBUG_VOID_RETURN;
}
+void Relay_log_info::slave_close_thread_tables(THD *thd)
+{
+ DBUG_ENTER("Relay_log_info::slave_close_thread_tables(THD *thd)");
+ thd->stmt_da->can_overwrite_status= TRUE;
+ thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
+ thd->stmt_da->can_overwrite_status= FALSE;
+
+ close_thread_tables(thd);
+ /*
+ - If transaction rollback was requested due to deadlock
+ perform it and release metadata locks.
+ - If inside a multi-statement transaction,
+ defer the release of metadata locks until the current
+ transaction is either committed or rolled back. This prevents
+ other statements from modifying the table for the entire
+ duration of this transaction. This provides commit ordering
+ and guarantees serializability across multiple transactions.
+ - If in autocommit mode, or outside a transactional context,
+ automatically release metadata locks of the current statement.
+ */
+ if (thd->transaction_rollback_request)
+ {
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+ else if (! thd->in_multi_stmt_transaction_mode())
+ thd->mdl_context.release_transactional_locks();
+ else
+ thd->mdl_context.release_statement_locks();
+
+ clear_tables_to_lock();
+ DBUG_VOID_RETURN;
+}
#endif
diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h
index 4c4f6759118..b989283deb4 100644
--- a/sql/rpl_rli.h
+++ b/sql/rpl_rli.h
@@ -1,5 +1,4 @@
-/*
- Copyright (c) 2005, 2012, Oracle and/or its affiliates.
+/* Copyright (c) 2005, 2012, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +11,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef RPL_RLI_H
#define RPL_RLI_H
@@ -21,9 +19,13 @@
#include "rpl_tblmap.h"
#include "rpl_reporting.h"
#include "rpl_utility.h"
+#include "log.h" /* LOG_INFO, MYSQL_BIN_LOG */
+#include "sql_class.h" /* THD */
+#include "log_event.h"
struct RPL_TABLE_LIST;
class Master_info;
+extern uint sql_slave_skip_counter;
/****************************************************************************
@@ -108,6 +110,19 @@ public:
*/
IO_CACHE cache_buf,*cur_log;
+ /*
+ Keeps track of the number of transactions that commits
+ before fsyncing. The option --sync-relay-log-info determines
+ how many transactions should commit before fsyncing.
+ */
+ uint sync_counter;
+
+ /*
+ Identifies when the recovery process is going on.
+ See sql/slave.cc:init_recovery for further details.
+ */
+ bool is_relay_log_recovery;
+
/* The following variables are safe to read any time */
/* IO_CACHE of the info file - set only during init or end */
@@ -121,18 +136,16 @@ public:
TABLE *save_temporary_tables;
/*
- standard lock acquistion order to avoid deadlocks:
+ standard lock acquisition order to avoid deadlocks:
run_lock, data_lock, relay_log.LOCK_log, relay_log.LOCK_index
*/
- pthread_mutex_t data_lock,run_lock;
-
+ mysql_mutex_t data_lock, run_lock, sleep_lock;
/*
start_cond is broadcast when SQL thread is started
stop_cond - when stopped
data_cond - when data protected by data_lock changes
*/
- pthread_cond_t start_cond, stop_cond, data_cond;
-
+ mysql_cond_t start_cond, stop_cond, data_cond, sleep_cond;
/* parent Master_info structure */
Master_info *mi;
@@ -220,19 +233,19 @@ public:
volatile uint32 slave_skip_counter;
volatile ulong abort_pos_wait; /* Incremented on change master */
volatile ulong slave_run_id; /* Incremented on slave start */
- pthread_mutex_t log_space_lock;
- pthread_cond_t log_space_cond;
+ mysql_mutex_t log_space_lock;
+ mysql_cond_t log_space_cond;
THD * sql_thd;
#ifndef DBUG_OFF
int events_till_abort;
#endif
- /* if not set, the value of other members of the structure are undefined */
/*
inited changes its value within LOCK_active_mi-guarded critical
sections at times of start_slave_threads() (0->1) and end_slave() (1->0).
Readers may not acquire the mutex while they realize potential concurrency
issue.
+ If not set, the value of other members of the structure are undefined.
*/
volatile bool inited;
volatile bool abort_slave;
@@ -292,7 +305,7 @@ public:
char slave_patternload_file[FN_REFLEN];
size_t slave_patternload_file_size;
- Relay_log_info();
+ Relay_log_info(bool is_slave_recovery);
~Relay_log_info();
/*
@@ -339,13 +352,21 @@ public:
uint tables_to_lock_count; /* RBR: Count of tables to lock */
table_mapping m_table_map; /* RBR: Mapping table-id to table */
- inline table_def *get_tabledef(TABLE *tbl)
+ bool get_table_data(TABLE *table_arg, table_def **tabledef_var, TABLE **conv_table_var) const
{
- table_def *td= 0;
- for (TABLE_LIST *ptr= tables_to_lock; ptr && !td; ptr= ptr->next_global)
- if (ptr->table == tbl)
- td= &((RPL_TABLE_LIST *)ptr)->m_tabledef;
- return (td);
+ DBUG_ASSERT(tabledef_var && conv_table_var);
+ for (TABLE_LIST *ptr= tables_to_lock ; ptr != NULL ; ptr= ptr->next_global)
+ if (ptr->table == table_arg)
+ {
+ *tabledef_var= &static_cast<RPL_TABLE_LIST*>(ptr)->m_tabledef;
+ *conv_table_var= static_cast<RPL_TABLE_LIST*>(ptr)->m_conv_table;
+ DBUG_PRINT("debug", ("Fetching table data for table %s.%s:"
+ " tabledef: %p, conv_table: %p",
+ table_arg->s->db.str, table_arg->s->table_name.str,
+ *tabledef_var, *conv_table_var));
+ return true;
+ }
+ return false;
}
/*
@@ -358,15 +379,14 @@ public:
bool cached_charset_compare(char *charset) const;
void cleanup_context(THD *, bool);
+ void slave_close_thread_tables(THD *);
void clear_tables_to_lock();
/*
- Used by row-based replication to detect that it should not stop at
- this event, but give it a chance to send more events. The time
- where the last event inside a group started is stored here. If the
- variable is zero, we are not in a group (but may be in a
- transaction).
- */
+ Used to defer stopping the SQL thread to give it a chance
+ to finish up the current group of events.
+ The timestamp is set and reset in @c sql_slave_killed().
+ */
time_t last_event_start_time;
/*
@@ -469,7 +489,7 @@ public:
@retval false Replication thread is currently not inside a group
*/
bool is_in_group() const {
- return (sql_thd->options & OPTION_BEGIN) ||
+ return (sql_thd->variables.option_bits & OPTION_BEGIN) ||
(m_flags & (1UL << IN_STMT));
}
@@ -510,8 +530,50 @@ public:
}
}
+ time_t get_row_stmt_start_timestamp()
+ {
+ return row_stmt_start_timestamp;
+ }
+
+ time_t set_row_stmt_start_timestamp()
+ {
+ if (row_stmt_start_timestamp == 0)
+ row_stmt_start_timestamp= my_time(0);
+
+ return row_stmt_start_timestamp;
+ }
+
+ void reset_row_stmt_start_timestamp()
+ {
+ row_stmt_start_timestamp= 0;
+ }
+
+ void set_long_find_row_note_printed()
+ {
+ long_find_row_note_printed= true;
+ }
+
+ void unset_long_find_row_note_printed()
+ {
+ long_find_row_note_printed= false;
+ }
+
+ bool is_long_find_row_note_printed()
+ {
+ return long_find_row_note_printed;
+ }
+
private:
+
uint32 m_flags;
+
+ /*
+ Runtime state for printing a note when slave is taking
+ too long while processing a row event.
+ */
+ time_t row_stmt_start_timestamp;
+ bool long_find_row_note_printed;
+
Annotate_rows_log_event *m_annotate_event;
};
diff --git a/sql/rpl_tblmap.cc b/sql/rpl_tblmap.cc
index 78b695b8e7c..b7ac1b2d091 100644
--- a/sql/rpl_tblmap.cc
+++ b/sql/rpl_tblmap.cc
@@ -1,6 +1,4 @@
-/*
- Copyright (c) 2005-2008 MySQL AB, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2005, 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
@@ -13,14 +11,17 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "my_global.h" // HAVE_REPLICATION
#ifdef HAVE_REPLICATION
#include "rpl_tblmap.h"
+#ifndef MYSQL_CLIENT
+#include "table.h"
+#endif
#ifdef MYSQL_CLIENT
#define MAYBE_TABLE_NAME(T) ("")
@@ -33,18 +34,20 @@
table_mapping::table_mapping()
: m_free(0)
{
+ DBUG_ENTER("table_mapping::table_mapping");
/*
No "free_element" function for entries passed here, as the entries are
allocated in a MEM_ROOT (freed as a whole in the destructor), they cannot
be freed one by one.
- Note that below we don't test if hash_init() succeeded. This constructor
- is called at startup only.
+ Note that below we don't test if my_hash_init() succeeded. This
+ constructor is called at startup only.
*/
- (void) hash_init(&m_table_ids,&my_charset_bin,TABLE_ID_HASH_SIZE,
+ (void) my_hash_init(&m_table_ids,&my_charset_bin,TABLE_ID_HASH_SIZE,
offsetof(entry,table_id),sizeof(ulong),
0,0,0);
/* We don't preallocate any block, this is consistent with m_free=0 above */
init_alloc_root(&m_mem_root, TABLE_ID_HASH_SIZE*sizeof(entry), 0);
+ DBUG_VOID_RETURN;
}
table_mapping::~table_mapping()
@@ -52,7 +55,7 @@ table_mapping::~table_mapping()
#ifdef MYSQL_CLIENT
clear_tables();
#endif
- hash_free(&m_table_ids);
+ my_hash_free(&m_table_ids);
free_root(&m_mem_root, MYF(0));
}
@@ -118,7 +121,7 @@ int table_mapping::set_table(ulong table_id, TABLE* table)
#ifdef MYSQL_CLIENT
free_table_map_log_event(e->table);
#endif
- hash_delete(&m_table_ids,(uchar *)e);
+ my_hash_delete(&m_table_ids,(uchar *)e);
}
e->table_id= table_id;
e->table= table;
@@ -141,7 +144,7 @@ int table_mapping::remove_table(ulong table_id)
entry *e= find_entry(table_id);
if (e)
{
- hash_delete(&m_table_ids,(uchar *)e);
+ my_hash_delete(&m_table_ids,(uchar *)e);
/* we add this entry to the chain of free (free for use) entries */
e->next= m_free;
m_free= e;
@@ -159,7 +162,7 @@ void table_mapping::clear_tables()
DBUG_ENTER("table_mapping::clear_tables()");
for (uint i= 0; i < m_table_ids.records; i++)
{
- entry *e= (entry *)hash_element(&m_table_ids, i);
+ entry *e= (entry *)my_hash_element(&m_table_ids, i);
#ifdef MYSQL_CLIENT
free_table_map_log_event(e->table);
#endif
diff --git a/sql/rpl_tblmap.h b/sql/rpl_tblmap.h
index f57bee98194..9fb1c4afbd7 100644
--- a/sql/rpl_tblmap.h
+++ b/sql/rpl_tblmap.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2005-2008 MySQL AB
+/* Copyright (c) 2005, 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
@@ -11,15 +11,14 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef TABLE_MAPPING_H
#define TABLE_MAPPING_H
/* Forward declarations */
#ifndef MYSQL_CLIENT
-struct st_table;
-typedef st_table TABLE;
+struct TABLE;
#else
class Table_map_log_event;
typedef Table_map_log_event TABLE;
@@ -53,6 +52,8 @@ void free_table_map_log_event(TABLE *table);
A dedicated MEM_ROOT needs to be used, see below.
*/
+#include "hash.h" /* HASH */
+
class table_mapping {
private:
@@ -91,9 +92,9 @@ private:
entry *find_entry(ulong table_id)
{
- return (entry *)hash_search(&m_table_ids,
- (uchar*)&table_id,
- sizeof(table_id));
+ return (entry *) my_hash_search(&m_table_ids,
+ (uchar*)&table_id,
+ sizeof(table_id));
}
int expand();
diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc
index ac5aef936ef..22241da1348 100644
--- a/sql/rpl_utility.cc
+++ b/sql/rpl_utility.cc
@@ -1,5 +1,5 @@
-/*
- Copyright (c) 2006, 2013, Oracle and/or its affiliates.
+/* Copyright (c) 2006, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2011, 2013, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,14 +12,181 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include "rpl_utility.h"
+#include "log_event.h"
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
#include "rpl_rli.h"
+#include "sql_select.h"
+
+/**
+ Function to compare two size_t integers for their relative
+ order. Used below.
+ */
+int compare(size_t a, size_t b)
+{
+ if (a < b)
+ return -1;
+ if (b < a)
+ return 1;
+ return 0;
+}
+
+
+/**
+ Max value for an unsigned integer of 'bits' bits.
+
+ The somewhat contorted expression is to avoid overflow.
+ */
+uint32 uint_max(int bits) {
+ return (((1UL << (bits - 1)) - 1) << 1) | 1;
+}
+
+
+/**
+ Compute the maximum display length of a field.
+
+ @param sql_type Type of the field
+ @param metadata The metadata from the master for the field.
+ @return Maximum length of the field in bytes.
+ */
+static uint32
+max_display_length_for_field(enum_field_types sql_type, unsigned int metadata)
+{
+ DBUG_PRINT("debug", ("sql_type: %d, metadata: 0x%x", sql_type, metadata));
+ DBUG_ASSERT(metadata >> 16 == 0);
+
+ switch (sql_type) {
+ case MYSQL_TYPE_NEWDECIMAL:
+ return metadata >> 8;
+
+ case MYSQL_TYPE_FLOAT:
+ return 12;
+
+ case MYSQL_TYPE_DOUBLE:
+ return 22;
+
+ case MYSQL_TYPE_SET:
+ case MYSQL_TYPE_ENUM:
+ return metadata & 0x00ff;
+
+ case MYSQL_TYPE_STRING:
+ {
+ uchar type= metadata >> 8;
+ if (type == MYSQL_TYPE_SET || type == MYSQL_TYPE_ENUM)
+ return metadata & 0xff;
+ else
+ /* This is taken from Field_string::unpack. */
+ return (((metadata >> 4) & 0x300) ^ 0x300) + (metadata & 0x00ff);
+ }
+
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_TINY:
+ return 4;
+
+ case MYSQL_TYPE_SHORT:
+ return 6;
+
+ case MYSQL_TYPE_INT24:
+ return 9;
+
+ case MYSQL_TYPE_LONG:
+ return 11;
+
+#ifdef HAVE_LONG_LONG
+ case MYSQL_TYPE_LONGLONG:
+ return 20;
+
+#endif
+ case MYSQL_TYPE_NULL:
+ return 0;
+
+ case MYSQL_TYPE_NEWDATE:
+ return 3;
+
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_TIME:
+ return 3;
+
+ case MYSQL_TYPE_TIMESTAMP:
+ return 4;
+
+ case MYSQL_TYPE_DATETIME:
+ return 8;
+ case MYSQL_TYPE_BIT:
+ /*
+ Decode the size of the bit field from the master.
+ */
+ DBUG_ASSERT((metadata & 0xff) <= 7);
+ return 8 * (metadata >> 8U) + (metadata & 0x00ff);
+
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_VARCHAR:
+ return metadata;
+
+ /*
+ The actual length for these types does not really matter since
+ they are used to calc_pack_length, which ignores the given
+ length for these types.
+
+ Since we want this to be accurate for other uses, we return the
+ maximum size in bytes of these BLOBs.
+ */
+
+ case MYSQL_TYPE_TINY_BLOB:
+ return uint_max(1 * 8);
+
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ return uint_max(3 * 8);
+
+ case MYSQL_TYPE_BLOB:
+ /*
+ 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 uint_max(metadata * 8);
+
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_GEOMETRY:
+ return uint_max(4 * 8);
+
+ default:
+ return ~(uint32) 0;
+ }
+}
+
+
+/*
+ Compare the pack lengths of a source field (on the master) and a
+ target field (on the slave).
+
+ @param field Target field.
+ @param type Source field type.
+ @param metadata Source field metadata.
+
+ @retval -1 The length of the source field is smaller than the target field.
+ @retval 0 The length of the source and target fields are the same.
+ @retval 1 The length of the source field is greater than the target field.
+ */
+int compare_lengths(Field *field, enum_field_types source_type, uint16 metadata)
+{
+ DBUG_ENTER("compare_lengths");
+ size_t const source_length=
+ max_display_length_for_field(source_type, metadata);
+ size_t const target_length= field->max_display_length();
+ DBUG_PRINT("debug", ("source_length: %lu, source_type: %u,"
+ " target_length: %lu, target_type: %u",
+ (unsigned long) source_length, source_type,
+ (unsigned long) target_length, field->real_type()));
+ int result= compare(source_length, target_length);
+ DBUG_PRINT("result", ("%d", result));
+ DBUG_RETURN(result);
+}
+#endif //MYSQL_CLIENT
/*********************************************************************
* table_def member definitions *
*********************************************************************/
@@ -30,7 +197,7 @@
*/
uint32 table_def::calc_field_size(uint col, uchar *master_data) const
{
- uint32 length;
+ uint32 length= 0;
switch (type(col)) {
case MYSQL_TYPE_NEWDECIMAL:
@@ -118,7 +285,6 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const
case MYSQL_TYPE_VARCHAR:
{
length= m_field_metadata[col] > 255 ? 2 : 1; // c&p of Field_varstring::data_length()
- DBUG_ASSERT(uint2korr(master_data) > 0);
length+= length == 1 ? (uint32) *master_data : uint2korr(master_data);
break;
}
@@ -128,17 +294,6 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const
case MYSQL_TYPE_BLOB:
case MYSQL_TYPE_GEOMETRY:
{
-#if 1
- /*
- BUG#29549:
- This is currently broken for NDB, which is using big-endian
- order when packing length of BLOB. Once they have decided how to
- fix the issue, we can enable the code below to make sure to
- always read the length in little-endian order.
- */
- Field_blob fb(m_field_metadata[col]);
- length= fb.get_packed_size(master_data);
-#else
/*
Compute the length of the data. We cannot use get_length() here
since it is dependent on the specific table (and also checks the
@@ -164,7 +319,6 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const
}
length+= m_field_metadata[col];
-#endif
break;
}
default:
@@ -173,122 +327,734 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const
return length;
}
-/*
+#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+/**
+ */
+void show_sql_type(enum_field_types type, uint16 metadata, String *str, CHARSET_INFO *field_cs)
+{
+ DBUG_ENTER("show_sql_type");
+ DBUG_PRINT("enter", ("type: %d, metadata: 0x%x", type, metadata));
+
+ switch (type)
+ {
+ case MYSQL_TYPE_TINY:
+ str->set_ascii(STRING_WITH_LEN("tinyint"));
+ break;
+
+ case MYSQL_TYPE_SHORT:
+ str->set_ascii(STRING_WITH_LEN("smallint"));
+ break;
+
+ case MYSQL_TYPE_LONG:
+ str->set_ascii(STRING_WITH_LEN("int"));
+ break;
+
+ case MYSQL_TYPE_FLOAT:
+ str->set_ascii(STRING_WITH_LEN("float"));
+ break;
+
+ case MYSQL_TYPE_DOUBLE:
+ str->set_ascii(STRING_WITH_LEN("double"));
+ break;
+
+ case MYSQL_TYPE_NULL:
+ str->set_ascii(STRING_WITH_LEN("null"));
+ break;
+
+ case MYSQL_TYPE_TIMESTAMP:
+ str->set_ascii(STRING_WITH_LEN("timestamp"));
+ break;
+
+ case MYSQL_TYPE_LONGLONG:
+ str->set_ascii(STRING_WITH_LEN("bigint"));
+ break;
+
+ case MYSQL_TYPE_INT24:
+ str->set_ascii(STRING_WITH_LEN("mediumint"));
+ break;
+
+ case MYSQL_TYPE_NEWDATE:
+ case MYSQL_TYPE_DATE:
+ str->set_ascii(STRING_WITH_LEN("date"));
+ break;
+
+ case MYSQL_TYPE_TIME:
+ str->set_ascii(STRING_WITH_LEN("time"));
+ break;
+
+ case MYSQL_TYPE_DATETIME:
+ str->set_ascii(STRING_WITH_LEN("datetime"));
+ break;
+
+ case MYSQL_TYPE_YEAR:
+ str->set_ascii(STRING_WITH_LEN("year"));
+ break;
+
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_VARCHAR:
+ {
+ CHARSET_INFO *cs= str->charset();
+ uint32 length=
+ cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
+ "varchar(%u)", metadata);
+ str->length(length);
+ }
+ break;
+
+ case MYSQL_TYPE_BIT:
+ {
+ CHARSET_INFO *cs= str->charset();
+ int bit_length= 8 * (metadata >> 8) + (metadata & 0xFF);
+ uint32 length=
+ cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
+ "bit(%d)", bit_length);
+ str->length(length);
+ }
+ break;
+
+ case MYSQL_TYPE_DECIMAL:
+ {
+ CHARSET_INFO *cs= str->charset();
+ uint32 length=
+ cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
+ "decimal(%d,?)", metadata);
+ str->length(length);
+ }
+ break;
+
+ case MYSQL_TYPE_NEWDECIMAL:
+ {
+ CHARSET_INFO *cs= str->charset();
+ uint32 length=
+ cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
+ "decimal(%d,%d)", metadata >> 8, metadata & 0xff);
+ str->length(length);
+ }
+ break;
+
+ case MYSQL_TYPE_ENUM:
+ str->set_ascii(STRING_WITH_LEN("enum"));
+ break;
+
+ case MYSQL_TYPE_SET:
+ str->set_ascii(STRING_WITH_LEN("set"));
+ break;
+
+ case MYSQL_TYPE_BLOB:
+ /*
+ Field::real_type() lies regarding the actual type of a BLOB, so
+ it is necessary to check the pack length to figure out what kind
+ of blob it really is.
+ */
+ switch (get_blob_type_from_length(metadata))
+ {
+ case MYSQL_TYPE_TINY_BLOB:
+ str->set_ascii(STRING_WITH_LEN("tinyblob"));
+ break;
+
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ str->set_ascii(STRING_WITH_LEN("mediumblob"));
+ break;
+
+ case MYSQL_TYPE_LONG_BLOB:
+ str->set_ascii(STRING_WITH_LEN("longblob"));
+ break;
+
+ case MYSQL_TYPE_BLOB:
+ str->set_ascii(STRING_WITH_LEN("blob"));
+ break;
+
+ default:
+ DBUG_ASSERT(0);
+ break;
+ }
+ break;
+
+ case MYSQL_TYPE_STRING:
+ {
+ /*
+ This is taken from Field_string::unpack.
+ */
+ CHARSET_INFO *cs= str->charset();
+ uint bytes= (((metadata >> 4) & 0x300) ^ 0x300) + (metadata & 0x00ff);
+ uint32 length=
+ cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
+ "char(%d)", bytes / field_cs->mbmaxlen);
+ str->length(length);
+ }
+ break;
+
+ case MYSQL_TYPE_GEOMETRY:
+ str->set_ascii(STRING_WITH_LEN("geometry"));
+ break;
+
+ default:
+ str->set_ascii(STRING_WITH_LEN("<unknown type>"));
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Check the order variable and print errors if the order is not
+ acceptable according to the current settings.
+
+ @param order The computed order of the conversion needed.
+ @param rli The relay log info data structure: for error reporting.
+ */
+bool is_conversion_ok(int order, Relay_log_info *rli)
+{
+ DBUG_ENTER("is_conversion_ok");
+ bool allow_non_lossy, allow_lossy;
+
+ allow_non_lossy = slave_type_conversions_options &
+ (ULL(1) << SLAVE_TYPE_CONVERSIONS_ALL_NON_LOSSY);
+ allow_lossy= slave_type_conversions_options &
+ (ULL(1) << SLAVE_TYPE_CONVERSIONS_ALL_LOSSY);
+
+ DBUG_PRINT("enter", ("order: %d, flags:%s%s", order,
+ allow_non_lossy ? " ALL_NON_LOSSY" : "",
+ allow_lossy ? " ALL_LOSSY" : ""));
+ if (order < 0 && !allow_non_lossy)
+ {
+ /* !!! Add error message saying that non-lossy conversions need to be allowed. */
+ DBUG_RETURN(false);
+ }
+
+ if (order > 0 && !allow_lossy)
+ {
+ /* !!! Add error message saying that lossy conversions need to be allowed. */
+ DBUG_RETURN(false);
+ }
+
+ DBUG_RETURN(true);
+}
+
+
+/**
+ Can a type potentially be converted to another type?
+
+ This function check if the types are convertible and what
+ conversion is required.
+
+ If conversion is not possible, and error is printed.
+
+ If conversion is possible:
+
+ - *order will be set to -1 if source type is smaller than target
+ type and a non-lossy conversion can be required. This includes
+ the case where the field types are different but types could
+ actually be converted in either direction.
+
+ - *order will be set to 0 if no conversion is required.
+
+ - *order will be set to 1 if the source type is strictly larger
+ than the target type and that conversion is potentially lossy.
+
+ @param[in] field Target field
+ @param[in] type Source field type
+ @param[in] metadata Source field metadata
+ @param[in] rli Relay log info (for error reporting)
+ @param[in] mflags Flags from the table map event
+ @param[out] order Order between source field and target field
+
+ @return @c true if conversion is possible according to the current
+ settings, @c false if conversion is not possible according to the
+ current setting.
+ */
+static bool
+can_convert_field_to(Field *field,
+ enum_field_types source_type, uint16 metadata,
+ Relay_log_info *rli, uint16 mflags,
+ int *order_var)
+{
+ DBUG_ENTER("can_convert_field_to");
+#ifndef DBUG_OFF
+ char field_type_buf[MAX_FIELD_WIDTH];
+ String field_type(field_type_buf, sizeof(field_type_buf), &my_charset_latin1);
+ field->sql_type(field_type);
+ DBUG_PRINT("enter", ("field_type: %s, target_type: %d, source_type: %d, source_metadata: 0x%x",
+ field_type.c_ptr_safe(), field->real_type(), source_type, metadata));
+#endif
+ /*
+ 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 (metadata == 0) // Metadata can only be zero if no metadata was provided
+ {
+ /*
+ If there is no metadata, we either have an old event where no
+ metadata were supplied, or a type that does not require any
+ metadata. In either case, conversion can be done but no
+ conversion table is necessary.
+ */
+ DBUG_PRINT("debug", ("Base types are identical, but there is no metadata"));
+ *order_var= 0;
+ DBUG_RETURN(true);
+ }
+
+ DBUG_PRINT("debug", ("Base types are identical, doing field size comparison"));
+ if (field->compatible_field_size(metadata, rli, mflags, order_var))
+ DBUG_RETURN(is_conversion_ok(*order_var, rli));
+ else
+ DBUG_RETURN(false);
+ }
+ else if (!slave_type_conversions_options)
+ DBUG_RETURN(false);
+
+ /*
+ Here, from and to will always be different. Since the types are
+ different, we cannot use the compatible_field_size() function, but
+ have to rely on hard-coded max-sizes for fields.
+ */
+
+ DBUG_PRINT("debug", ("Base types are different, checking conversion"));
+ switch (source_type) // Source type (on master)
+ {
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ switch (field->real_type())
+ {
+ case MYSQL_TYPE_NEWDECIMAL:
+ /*
+ Then the other type is either FLOAT, DOUBLE, or old style
+ DECIMAL, so we require lossy conversion.
+ */
+ *order_var= 1;
+ DBUG_RETURN(is_conversion_ok(*order_var, rli));
+
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ {
+ if (source_type == MYSQL_TYPE_NEWDECIMAL ||
+ source_type == MYSQL_TYPE_DECIMAL)
+ *order_var = 1; // Always require lossy conversions
+ else
+ *order_var= compare_lengths(field, source_type, metadata);
+ DBUG_ASSERT(*order_var != 0);
+ DBUG_RETURN(is_conversion_ok(*order_var, rli));
+ }
+
+ default:
+ DBUG_RETURN(false);
+ }
+ break;
+
+ /*
+ The length comparison check will do the correct job of comparing
+ the field lengths (in bytes) of two integer types.
+ */
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_LONGLONG:
+ switch (field->real_type())
+ {
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_LONGLONG:
+ *order_var= compare_lengths(field, source_type, metadata);
+ DBUG_ASSERT(*order_var != 0);
+ DBUG_RETURN(is_conversion_ok(*order_var, rli));
+
+ default:
+ DBUG_RETURN(false);
+ }
+ break;
+
+ /*
+ Since source and target type is different, and it is not possible
+ to convert bit types to anything else, this will return false.
+ */
+ case MYSQL_TYPE_BIT:
+ DBUG_RETURN(false);
+
+ /*
+ If all conversions are disabled, it is not allowed to convert
+ between these types. Since the TEXT vs. BINARY is distinguished by
+ the charset, and the charset is not replicated, we cannot
+ currently distinguish between , e.g., TEXT and BLOB.
+ */
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_VARCHAR:
+ 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_STRING:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_VARCHAR:
+ *order_var= compare_lengths(field, source_type, metadata);
+ /*
+ Here we know that the types are different, so if the order
+ gives that they do not require any conversion, we still need
+ to have non-lossy conversion enabled to allow conversion
+ between different (string) types of the same length.
+ */
+ if (*order_var == 0)
+ *order_var= -1;
+ DBUG_RETURN(is_conversion_ok(*order_var, rli));
+
+ default:
+ DBUG_RETURN(false);
+ }
+ break;
+
+ case MYSQL_TYPE_GEOMETRY:
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_NEWDATE:
+ case MYSQL_TYPE_NULL:
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ DBUG_RETURN(false);
+ }
+ DBUG_RETURN(false); // To keep GCC happy
+}
+
+
+/**
Is the definition compatible with a table?
+ This function will compare the master table with an existing table
+ on the slave and see if they are compatible with respect to the
+ current settings of @c SLAVE_TYPE_CONVERSIONS.
+
+ If the tables are compatible and conversions are required, @c
+ *tmp_table_var will be set to a virtual temporary table with field
+ pointers for the fields that require conversions. This allow simple
+ checking of whether a conversion are to be applied or not.
+
+ If tables are compatible, but no conversions are necessary, @c
+ *tmp_table_var will be set to NULL.
+
+ @param rli_arg[in]
+ Relay log info, for error reporting.
+
+ @param table[in]
+ Table to compare with
+
+ @param tmp_table_var[out]
+ Virtual temporary table for performing conversions, if necessary.
+
+ @retval true Master table is compatible with slave table.
+ @retval false Master table is not compatible with slave table.
*/
-int
-table_def::compatible_with(Relay_log_info const *rli_arg, TABLE *table)
+bool
+table_def::compatible_with(THD *thd, Relay_log_info *rli,
+ TABLE *table, TABLE **conv_table_var)
const
{
/*
We only check the initial columns for the tables.
*/
uint const cols_to_check= min(table->s->fields, size());
- int error= 0;
- Relay_log_info const *rli= const_cast<Relay_log_info*>(rli_arg);
-
- TABLE_SHARE const *const tsh= table->s;
+ TABLE *tmp_table= NULL;
for (uint col= 0 ; col < cols_to_check ; ++col)
{
Field *const field= table->field[col];
- if (field->type() != type(col))
+ int order;
+ if (can_convert_field_to(field, type(col), field_metadata(col), rli, m_flags, &order))
{
- DBUG_ASSERT(col < size() && col < tsh->fields);
- DBUG_ASSERT(tsh->db.str && tsh->table_name.str);
- error= 1;
- char buf[256];
- my_snprintf(buf, sizeof(buf), "Column %d type mismatch - "
- "received type %d, %s.%s has type %d",
- col, type(col), tsh->db.str, tsh->table_name.str,
- field->type());
- rli->report(ERROR_LEVEL, ER_BINLOG_ROW_WRONG_TABLE_DEF,
- ER(ER_BINLOG_ROW_WRONG_TABLE_DEF), buf);
+ DBUG_PRINT("debug", ("Checking column %d -"
+ " field '%s' can be converted - order: %d",
+ col, field->field_name, order));
+ DBUG_ASSERT(order >= -1 && order <= 1);
+
+ /*
+ If order is not 0, a conversion is required, so we need to set
+ up the conversion table.
+ */
+ if (order != 0 && tmp_table == NULL)
+ {
+ /*
+ This will create the full table with all fields. This is
+ necessary to ge the correct field lengths for the record.
+ */
+ tmp_table= create_conversion_table(thd, rli, table);
+ if (tmp_table == NULL)
+ return false;
+ /*
+ Clear all fields up to, but not including, this column.
+ */
+ for (unsigned int i= 0; i < col; ++i)
+ tmp_table->field[i]= NULL;
+ }
+
+ if (order == 0 && tmp_table != NULL)
+ tmp_table->field[col]= NULL;
}
- /*
- Check the slave's field size against that of the master.
- */
- if (!error &&
- !field->compatible_field_size(field_metadata(col), rli_arg, m_flags))
+ else
{
- error= 1;
- char buf[256];
- my_snprintf(buf, sizeof(buf), "Column %d size mismatch - "
- "master has size %d, %s.%s on slave has size %d."
- " Master's column size should be <= the slave's "
- "column size.", col,
- field->pack_length_from_metadata(m_field_metadata[col]),
- tsh->db.str, tsh->table_name.str,
- field->row_pack_length());
- rli->report(ERROR_LEVEL, ER_BINLOG_ROW_WRONG_TABLE_DEF,
- ER(ER_BINLOG_ROW_WRONG_TABLE_DEF), buf);
+ DBUG_PRINT("debug", ("Checking column %d -"
+ " field '%s' can not be converted",
+ col, field->field_name));
+ DBUG_ASSERT(col < size() && col < table->s->fields);
+ DBUG_ASSERT(table->s->db.str && table->s->table_name.str);
+ const char *db_name= table->s->db.str;
+ const char *tbl_name= table->s->table_name.str;
+ char source_buf[MAX_FIELD_WIDTH];
+ char target_buf[MAX_FIELD_WIDTH];
+ String source_type(source_buf, sizeof(source_buf), &my_charset_latin1);
+ String target_type(target_buf, sizeof(target_buf), &my_charset_latin1);
+ show_sql_type(type(col), field_metadata(col), &source_type, field->charset());
+ field->sql_type(target_type);
+ rli->report(ERROR_LEVEL, ER_SLAVE_CONVERSION_FAILED,
+ ER(ER_SLAVE_CONVERSION_FAILED),
+ col, db_name, tbl_name,
+ source_type.c_ptr_safe(), target_type.c_ptr_safe());
+ return false;
}
}
- return error;
-}
+#ifndef DBUG_OFF
+ if (tmp_table)
+ {
+ for (unsigned int col= 0; col < tmp_table->s->fields; ++col)
+ if (tmp_table->field[col])
+ {
+ char source_buf[MAX_FIELD_WIDTH];
+ char target_buf[MAX_FIELD_WIDTH];
+ String source_type(source_buf, sizeof(source_buf), &my_charset_latin1);
+ String target_type(target_buf, sizeof(target_buf), &my_charset_latin1);
+ tmp_table->field[col]->sql_type(source_type);
+ table->field[col]->sql_type(target_type);
+ DBUG_PRINT("debug", ("Field %s - conversion required."
+ " Source type: '%s', Target type: '%s'",
+ tmp_table->field[col]->field_name,
+ source_type.c_ptr_safe(), target_type.c_ptr_safe()));
+ }
+ }
+#endif
-Deferred_log_events::Deferred_log_events(Relay_log_info *rli) : last_added(NULL)
-{
- my_init_dynamic_array(&array, sizeof(Log_event *), 32, 16);
+ *conv_table_var= tmp_table;
+ return true;
}
-Deferred_log_events::~Deferred_log_events()
-{
- delete_dynamic(&array);
-}
+/**
+ Create a conversion table.
-int Deferred_log_events::add(Log_event *ev)
-{
- last_added= ev;
- insert_dynamic(&array, (uchar*) &ev);
- return 0;
-}
+ If the function is unable to create the conversion table, an error
+ will be printed and NULL will be returned.
-bool Deferred_log_events::is_empty()
-{
- return array.elements == 0;
-}
+ @return Pointer to conversion table, or NULL if unable to create
+ conversion table.
+ */
-bool Deferred_log_events::execute(Relay_log_info *rli)
+TABLE *table_def::create_conversion_table(THD *thd, Relay_log_info *rli, TABLE *target_table) const
{
- bool res= false;
-
- DBUG_ASSERT(rli->deferred_events_collecting);
+ DBUG_ENTER("table_def::create_conversion_table");
- rli->deferred_events_collecting= false;
- for (uint i= 0; !res && i < array.elements; i++)
+ List<Create_field> field_list;
+ TABLE *conv_table= NULL;
+ /*
+ At slave, columns may differ. So we should create
+ min(columns@master, columns@slave) columns in the
+ conversion table.
+ */
+ uint const cols_to_create= min(target_table->s->fields, size());
+ for (uint col= 0 ; col < cols_to_create; ++col)
{
- Log_event *ev= (* (Log_event **)
- dynamic_array_ptr(&array, i));
- res= ev->apply_event(rli);
+ Create_field *field_def=
+ (Create_field*) alloc_root(thd->mem_root, sizeof(Create_field));
+ if (field_list.push_back(field_def))
+ DBUG_RETURN(NULL);
+
+ uint decimals= 0;
+ TYPELIB* interval= NULL;
+ uint pack_length= 0;
+ uint32 max_length=
+ max_display_length_for_field(type(col), field_metadata(col));
+
+ switch(type(col))
+ {
+ int precision;
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ interval= static_cast<Field_enum*>(target_table->field[col])->typelib;
+ pack_length= field_metadata(col) & 0x00ff;
+ break;
+
+ case MYSQL_TYPE_NEWDECIMAL:
+ /*
+ The display length of a DECIMAL type is not the same as the
+ length that should be supplied to make_field, so we correct
+ the length here.
+ */
+ precision= field_metadata(col) >> 8;
+ decimals= field_metadata(col) & 0x00ff;
+ max_length=
+ my_decimal_precision_to_length(precision, decimals, FALSE);
+ break;
+
+ case MYSQL_TYPE_DECIMAL:
+ sql_print_error("In RBR mode, Slave received incompatible DECIMAL field "
+ "(old-style decimal field) from Master while creating "
+ "conversion table. Please consider changing datatype on "
+ "Master to new style decimal by executing ALTER command for"
+ " column Name: %s.%s.%s.",
+ target_table->s->db.str,
+ target_table->s->table_name.str,
+ target_table->field[col]->field_name);
+ goto err;
+
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_GEOMETRY:
+ pack_length= field_metadata(col) & 0x00ff;
+ break;
+
+ default:
+ break;
+ }
+
+ DBUG_PRINT("debug", ("sql_type: %d, target_field: '%s', max_length: %d, decimals: %d,"
+ " maybe_null: %d, unsigned_flag: %d, pack_length: %u",
+ type(col), target_table->field[col]->field_name,
+ max_length, decimals, TRUE, FALSE, pack_length));
+ field_def->init_for_tmp_table(type(col),
+ max_length,
+ decimals,
+ TRUE, // maybe_null
+ FALSE, // unsigned_flag
+ pack_length);
+ field_def->charset= target_table->field[col]->charset();
+ field_def->interval= interval;
}
- rli->deferred_events_collecting= true;
- return res;
+
+ conv_table= create_virtual_tmp_table(thd, field_list);
+
+err:
+ if (conv_table == NULL)
+ rli->report(ERROR_LEVEL, ER_SLAVE_CANT_CREATE_CONVERSION,
+ ER(ER_SLAVE_CANT_CREATE_CONVERSION),
+ target_table->s->db.str,
+ target_table->s->table_name.str);
+ DBUG_RETURN(conv_table);
}
+#endif /* MYSQL_CLIENT */
-void Deferred_log_events::rewind()
+table_def::table_def(unsigned char *types, ulong size,
+ uchar *field_metadata, int metadata_size,
+ uchar *null_bitmap, uint16 flags)
+ : m_size(size), m_type(0), m_field_metadata_size(metadata_size),
+ m_field_metadata(0), m_null_bits(0), m_flags(flags),
+ m_memory(NULL)
{
+ m_memory= (uchar *)my_multi_malloc(MYF(MY_WME),
+ &m_type, size,
+ &m_field_metadata,
+ size * sizeof(uint16),
+ &m_null_bits, (size + 7) / 8,
+ NULL);
+
+ bzero(m_field_metadata, size * sizeof(uint16));
+
+ if (m_type)
+ memcpy(m_type, types, size);
+ else
+ m_size= 0;
/*
- Reset preceeding Query log event events which execution was
- deferred because of slave side filtering.
+ Extract the data from the table map into the field metadata array
+ iff there is field metadata. The variable metadata_size will be
+ 0 if we are replicating from an older version server since no field
+ metadata was written to the table map. This can also happen if
+ there were no fields in the master that needed extra metadata.
*/
- if (!is_empty())
- {
- for (uint i= 0; i < array.elements; i++)
+ if (m_size && metadata_size)
+ {
+ int index= 0;
+ for (unsigned int i= 0; i < m_size; i++)
{
- Log_event *ev= *(Log_event **) dynamic_array_ptr(&array, i);
- delete ev;
+ switch (m_type[i]) {
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_DOUBLE:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_GEOMETRY:
+ {
+ /*
+ These types store a single byte.
+ */
+ m_field_metadata[i]= field_metadata[index];
+ index++;
+ break;
+ }
+ case MYSQL_TYPE_SET:
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_STRING:
+ {
+ uint16 x= field_metadata[index++] << 8U; // real_type
+ x+= field_metadata[index++]; // pack or field length
+ m_field_metadata[i]= x;
+ break;
+ }
+ case MYSQL_TYPE_BIT:
+ {
+ uint16 x= field_metadata[index++];
+ x = x + (field_metadata[index++] << 8U);
+ m_field_metadata[i]= x;
+ break;
+ }
+ case MYSQL_TYPE_VARCHAR:
+ {
+ /*
+ These types store two bytes.
+ */
+ char *ptr= (char *)&field_metadata[index];
+ m_field_metadata[i]= uint2korr(ptr);
+ index= index + 2;
+ break;
+ }
+ case MYSQL_TYPE_NEWDECIMAL:
+ {
+ uint16 x= field_metadata[index++] << 8U; // precision
+ x+= field_metadata[index++]; // decimals
+ m_field_metadata[i]= x;
+ break;
+ }
+ default:
+ m_field_metadata[i]= 0;
+ break;
+ }
}
- last_added= NULL;
- if (array.elements > array.max_element)
- freeze_size(&array);
- reset_dynamic(&array);
}
+ if (m_size && null_bitmap)
+ memcpy(m_null_bits, null_bitmap, (m_size + 7) / 8);
}
-#endif /* MYSQL_CLIENT */
+
+table_def::~table_def()
+{
+ my_free(m_memory);
+#ifndef DBUG_OFF
+ m_type= 0;
+ m_size= 0;
+#endif
+}
/**
@@ -347,3 +1113,67 @@ bool event_checksum_test(uchar *event_buf, ulong event_len, uint8 alg)
return DBUG_EVALUATE_IF("simulate_checksum_test_failure", TRUE, res);
}
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+
+Deferred_log_events::Deferred_log_events(Relay_log_info *rli) : last_added(NULL)
+{
+ my_init_dynamic_array(&array, sizeof(Log_event *), 32, 16);
+}
+
+Deferred_log_events::~Deferred_log_events()
+{
+ delete_dynamic(&array);
+}
+
+int Deferred_log_events::add(Log_event *ev)
+{
+ last_added= ev;
+ insert_dynamic(&array, (uchar*) &ev);
+ return 0;
+}
+
+bool Deferred_log_events::is_empty()
+{
+ return array.elements == 0;
+}
+
+bool Deferred_log_events::execute(Relay_log_info *rli)
+{
+ bool res= false;
+ DBUG_ENTER("Deferred_log_events::execute");
+ DBUG_ASSERT(rli->deferred_events_collecting);
+
+ rli->deferred_events_collecting= false;
+ for (uint i= 0; !res && i < array.elements; i++)
+ {
+ Log_event *ev= (* (Log_event **)
+ dynamic_array_ptr(&array, i));
+ res= ev->apply_event(rli);
+ }
+ rli->deferred_events_collecting= true;
+ DBUG_RETURN(res);
+}
+
+void Deferred_log_events::rewind()
+{
+ /*
+ Reset preceeding Query log event events which execution was
+ deferred because of slave side filtering.
+ */
+ if (!is_empty())
+ {
+ for (uint i= 0; i < array.elements; i++)
+ {
+ Log_event *ev= *(Log_event **) dynamic_array_ptr(&array, i);
+ delete ev;
+ }
+ last_added= NULL;
+ if (array.elements > array.max_element)
+ freeze_size(&array);
+ reset_dynamic(&array);
+ }
+ last_added= NULL;
+}
+
+#endif
+
diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h
index 5b58f27e649..79f4517c492 100644
--- a/sql/rpl_utility.h
+++ b/sql/rpl_utility.h
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2006, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2006, 2010, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +12,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef RPL_UTILITY_H
#define RPL_UTILITY_H
@@ -22,10 +21,15 @@
#error "Don't include this C++ header file from a non-C++ file!"
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "m_string.h" /* bzero, memcpy */
+#ifdef MYSQL_SERVER
+#include "table.h" /* TABLE_LIST */
+#endif
+#include "mysql_com.h"
class Relay_log_info;
-
+class Log_event;
/**
A table definition from the master.
@@ -40,116 +44,18 @@ class table_def
{
public:
/**
- Convenience declaration of the type of the field type data in a
- table map event.
- */
- typedef unsigned char field_type;
-
- /**
Constructor.
- @param types Array of types
+ @param types Array of types, each stored as a byte
@param size Number of elements in array 'types'
@param field_metadata Array of extra information about fields
@param metadata_size Size of the field_metadata array
@param null_bitmap The bitmap of fields that can be null
*/
- table_def(field_type *types, ulong size, uchar *field_metadata,
- int metadata_size, uchar *null_bitmap, uint16 flags)
- : m_size(size), m_type(0), m_field_metadata_size(metadata_size),
- m_field_metadata(0), m_null_bits(0), m_flags(flags), m_memory(NULL)
- {
- m_memory= (uchar *)my_multi_malloc(MYF(MY_WME),
- &m_type, size,
- &m_field_metadata,
- size * sizeof(uint16),
- &m_null_bits, (size + 7) / 8,
- NULL);
-
- bzero(m_field_metadata, size * sizeof(uint16));
-
- if (m_type)
- memcpy(m_type, types, size);
- else
- m_size= 0;
- /*
- Extract the data from the table map into the field metadata array
- iff there is field metadata. The variable metadata_size will be
- 0 if we are replicating from an older version server since no field
- metadata was written to the table map. This can also happen if
- there were no fields in the master that needed extra metadata.
- */
- if (m_size && metadata_size)
- {
- int index= 0;
- for (unsigned int i= 0; i < m_size; i++)
- {
- switch (m_type[i]) {
- case MYSQL_TYPE_TINY_BLOB:
- case MYSQL_TYPE_BLOB:
- case MYSQL_TYPE_MEDIUM_BLOB:
- case MYSQL_TYPE_LONG_BLOB:
- case MYSQL_TYPE_DOUBLE:
- case MYSQL_TYPE_FLOAT:
- case MYSQL_TYPE_GEOMETRY:
- {
- /*
- These types store a single byte.
- */
- m_field_metadata[i]= field_metadata[index];
- index++;
- break;
- }
- case MYSQL_TYPE_SET:
- case MYSQL_TYPE_ENUM:
- case MYSQL_TYPE_STRING:
- {
- uint16 x= field_metadata[index++] << 8U; // real_type
- x+= field_metadata[index++]; // pack or field length
- m_field_metadata[i]= x;
- break;
- }
- case MYSQL_TYPE_BIT:
- {
- uint16 x= field_metadata[index++];
- x = x + (field_metadata[index++] << 8U);
- m_field_metadata[i]= x;
- break;
- }
- case MYSQL_TYPE_VARCHAR:
- {
- /*
- These types store two bytes.
- */
- char *ptr= (char *)&field_metadata[index];
- m_field_metadata[i]= uint2korr(ptr);
- index= index + 2;
- break;
- }
- case MYSQL_TYPE_NEWDECIMAL:
- {
- uint16 x= field_metadata[index++] << 8U; // precision
- x+= field_metadata[index++]; // decimals
- m_field_metadata[i]= x;
- break;
- }
- default:
- m_field_metadata[i]= 0;
- break;
- }
- }
- }
- if (m_size && null_bitmap)
- memcpy(m_null_bits, null_bitmap, (m_size + 7) / 8);
- }
+ table_def(unsigned char *types, ulong size, uchar *field_metadata,
+ int metadata_size, uchar *null_bitmap, uint16 flags);
- ~table_def() {
- my_free(m_memory, MYF(0));
-#ifndef DBUG_OFF
- m_type= 0;
- m_size= 0;
-#endif
- }
+ ~table_def();
/**
Return the number of fields there is type data for.
@@ -168,10 +74,40 @@ public:
<code>index</code>. Currently, only the type identifier is
returned.
*/
- field_type type(ulong index) const
+ enum_field_types type(ulong index) const
{
DBUG_ASSERT(index < m_size);
- return m_type[index];
+ /*
+ If the source type is MYSQL_TYPE_STRING, it can in reality be
+ either MYSQL_TYPE_STRING, MYSQL_TYPE_ENUM, or MYSQL_TYPE_SET, so
+ we might need to modify the type to get the real type.
+ */
+ enum_field_types source_type= static_cast<enum_field_types>(m_type[index]);
+ uint16 source_metadata= m_field_metadata[index];
+ switch (source_type)
+ {
+ case MYSQL_TYPE_STRING:
+ {
+ int real_type= source_metadata >> 8;
+ if (real_type == MYSQL_TYPE_ENUM || real_type == MYSQL_TYPE_SET)
+ source_type= static_cast<enum_field_types>(real_type);
+ break;
+ }
+
+ /*
+ This type has not been used since before row-based replication,
+ so we can safely assume that it really is MYSQL_TYPE_NEWDATE.
+ */
+ case MYSQL_TYPE_DATE:
+ source_type= MYSQL_TYPE_NEWDATE;
+ break;
+
+ default:
+ /* Do nothing */
+ break;
+ }
+
+ return source_type;
}
@@ -223,23 +159,58 @@ public:
with it.
A table definition is compatible with a table if:
- - the columns types of the table definition is a (not
- necessarily proper) prefix of the column type of the table, or
- - the other way around
+ - The columns types of the table definition is a (not
+ necessarily proper) prefix of the column type of the table.
+
+ - The other way around.
+ - Each column on the master that also exists on the slave can be
+ converted according to the current settings of @c
+ SLAVE_TYPE_CONVERSIONS.
+
+ @param thd
@param rli Pointer to relay log info
@param table Pointer to table to compare with.
+ @param[out] tmp_table_var Pointer to temporary table for holding
+ conversion table.
+
@retval 1 if the table definition is not compatible with @c table
@retval 0 if the table definition is compatible with @c table
*/
#ifndef MYSQL_CLIENT
- int compatible_with(Relay_log_info const *rli, TABLE *table) const;
+ bool compatible_with(THD *thd, Relay_log_info *rli, TABLE *table,
+ TABLE **conv_table_var) const;
+
+ /**
+ Create a virtual in-memory temporary table structure.
+
+ The table structure has records and field array so that a row can
+ be unpacked into the record for further processing.
+
+ In the virtual table, each field that requires conversion will
+ have a non-NULL value, while fields that do not require
+ conversion will have a NULL value.
+
+ Some information that is missing in the events, such as the
+ character set for string types, are taken from the table that the
+ field is going to be pushed into, so the target table that the data
+ eventually need to be pushed into need to be supplied.
+
+ @param thd Thread to allocate memory from.
+ @param rli Relay log info structure, for error reporting.
+ @param target_table Target table for fields.
+
+ @return A pointer to a temporary table with memory allocated in the
+ thread's memroot, NULL if the table could not be created
+ */
+ TABLE *create_conversion_table(THD *thd, Relay_log_info *rli, TABLE *target_table) const;
#endif
+
private:
ulong m_size; // Number of elements in the types array
- field_type *m_type; // Array of type descriptors
+ unsigned char *m_type; // Array of type descriptors
uint m_field_metadata_size;
uint16 *m_field_metadata;
uchar *m_null_bits;
@@ -258,11 +229,12 @@ struct RPL_TABLE_LIST
{
bool m_tabledef_valid;
table_def m_tabledef;
+ TABLE *m_conv_table;
};
/* Anonymous namespace for template functions/classes */
-namespace {
+CPP_UNNAMED_NS_START
/*
Smart pointer that will automatically call my_afree (a macro) when
@@ -289,7 +261,7 @@ namespace {
Obj* get() { return m_ptr; }
};
-}
+CPP_UNNAMED_NS_END
class Deferred_log_events
{
diff --git a/sql/scheduler.cc b/sql/scheduler.cc
index 5b7da9b1219..1cdda089a6b 100644
--- a/sql/scheduler.cc
+++ b/sql/scheduler.cc
@@ -1,4 +1,5 @@
-/* Copyright (C) 2007 MySQL AB
+/* Copyright (c) 2007, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2012, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,7 +12,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
Implementation for the thread scheduler
@@ -21,34 +22,11 @@
#pragma implementation
#endif
-#include <mysql_priv.h>
-#if MYSQL_VERSION_ID >= 60000
-#include "sql_audit.h"
-#endif
-
-/*
- 'Dummy' functions to be used when we don't need any handling for a scheduler
- event
- */
-
-static bool init_dummy(void) {return 0;}
-static void post_kill_dummy(THD *thd) {}
-static void end_dummy(void) {}
-static bool end_thread_dummy(THD *thd, bool cache_thread) { return 0; }
-
-/*
- Initialize default scheduler with dummy functions so that setup functions
- only need to declare those that are relvant for their usage
-*/
-
-scheduler_functions::scheduler_functions()
- :init(init_dummy),
- init_new_connection_thread(init_new_connection_handler_thread),
- add_connection(0), // Must be defined
- post_kill_notification(post_kill_dummy),
- end_thread(end_thread_dummy), end(end_dummy)
-{}
-
+#include "sql_connect.h" // init_new_connection_handler_thread
+#include "scheduler.h"
+#include "mysqld.h"
+#include "sql_class.h"
+#include "sql_callback.h"
/*
End connection, in case when we are using 'no-threads'
@@ -57,699 +35,155 @@ scheduler_functions::scheduler_functions()
static bool no_threads_end(THD *thd, bool put_in_cache)
{
unlink_thd(thd);
- pthread_mutex_unlock(&LOCK_thread_count);
return 1; // Abort handle_one_connection
}
-
-/*
- Initailize scheduler for --thread-handling=no-threads
-*/
-
-void one_thread_scheduler(scheduler_functions *func)
-{
- func->max_threads= 1;
- func->max_connections= &max_connections;
- func->connection_count= &connection_count;
-#ifndef EMBEDDED_LIBRARY
- func->add_connection= handle_connection_in_main_thread;
-#endif
- func->init_new_connection_thread= init_dummy;
- func->end_thread= no_threads_end;
-}
-
-
-/*
- Initialize scheduler for --thread-handling=one-thread-per-connection
-*/
-
-#ifndef EMBEDDED_LIBRARY
-void one_thread_per_connection_scheduler(scheduler_functions *func,
- ulong *arg_max_connections,
- uint *arg_connection_count)
-{
- func->max_threads= *arg_max_connections + 1;
- func->max_connections= arg_max_connections;
- func->connection_count= arg_connection_count;
- func->add_connection= create_thread_to_handle_connection;
- func->end_thread= one_thread_per_connection_end;
-}
-#endif /* EMBEDDED_LIBRARY */
-
-
-#if defined(HAVE_LIBEVENT) && HAVE_POOL_OF_THREADS == 1
-
-#include "event.h"
-
-static struct event_base *base;
-
-static uint created_threads, killed_threads;
-static bool kill_pool_threads;
-
-static struct event thd_add_event;
-static struct event thd_kill_event;
-
-static pthread_mutex_t LOCK_thd_add; /* protects thds_need_adding */
-static LIST *thds_need_adding; /* list of thds to add to libevent queue */
-
-static int thd_add_pair[2]; /* pipe to signal add a connection to libevent*/
-static int thd_kill_pair[2]; /* pipe to signal kill a connection in libevent */
-
-/*
- LOCK_event_loop protects the non-thread safe libevent calls (event_add and
- event_del) and thds_need_processing and thds_waiting_for_io.
-*/
-static pthread_mutex_t LOCK_event_loop;
-static LIST *thds_need_processing; /* list of thds that needs some processing */
-static LIST *thds_waiting_for_io; /* list of thds with added events */
-
-pthread_handler_t libevent_thread_proc(void *arg);
-static void libevent_end();
-static bool libevent_needs_immediate_processing(THD *thd);
-static void libevent_connection_close(THD *thd);
-static bool libevent_should_close_connection(THD* thd);
-static void libevent_thd_add(THD* thd);
-void libevent_io_callback(int Fd, short Operation, void *ctx);
-void libevent_add_thd_callback(int Fd, short Operation, void *ctx);
-void libevent_kill_thd_callback(int Fd, short Operation, void *ctx);
-
-
-/*
- Create a pipe and set to non-blocking.
- Returns TRUE if there is an error.
-*/
-
-static bool init_socketpair(int sock_pair[])
-{
- sock_pair[0]= sock_pair[1]= -1;
- return (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, sock_pair) < 0 ||
- evutil_make_socket_nonblocking(sock_pair[0]) == -1 ||
- evutil_make_socket_nonblocking(sock_pair[1]) == -1);
-}
-
-static void close_socketpair(int sock_pair[])
-{
- if (sock_pair[0] != -1)
- EVUTIL_CLOSESOCKET(sock_pair[0]);
- if (sock_pair[1] != -1)
- EVUTIL_CLOSESOCKET(sock_pair[1]);
-}
-
-/*
- thd_scheduler keeps the link between THD and events.
- It's embedded in the THD class.
+/** @internal
+ Helper functions to allow mysys to call the thread scheduler when
+ waiting for locks.
*/
-thd_scheduler::thd_scheduler()
- : logged_in(FALSE), io_event(NULL), thread_attached(FALSE)
-{
-#ifndef DBUG_OFF
- dbug_explain[0]= '\0';
- set_explain= FALSE;
-#endif
-}
-
-
-thd_scheduler::~thd_scheduler()
+/**@{*/
+extern "C"
{
- my_free(io_event, MYF(MY_ALLOW_ZERO_PTR));
+static void scheduler_wait_lock_begin(void) {
+ thd_wait_begin(NULL, THD_WAIT_TABLE_LOCK);
}
-
-bool thd_scheduler::init(THD *parent_thd)
-{
- io_event=
- (struct event*)my_malloc(sizeof(*io_event),MYF(MY_ZEROFILL|MY_WME));
-
- if (!io_event)
- {
- sql_print_error("Memory allocation error in thd_scheduler::init\n");
- return TRUE;
- }
-
- event_set(io_event, (int)parent_thd->net.vio->sd, EV_READ,
- libevent_io_callback, (void*)parent_thd);
-
- list.data= parent_thd;
-
- return FALSE;
+static void scheduler_wait_lock_end(void) {
+ thd_wait_end(NULL);
}
-
-/*
- Attach/associate the connection with the OS thread, for command processing.
-*/
-
-bool thd_scheduler::thread_attach()
-{
- DBUG_ASSERT(!thread_attached);
- THD* thd = (THD*)list.data;
- if (libevent_should_close_connection(thd) ||
- setup_connection_thread_globals(thd))
- {
- return TRUE;
- }
- my_errno= 0;
- thd->mysys_var->abort= 0;
- thread_attached= TRUE;
-#ifndef DBUG_OFF
- /*
- When we attach the thread for a connection for the first time,
- we know that there is no session value set yet. Thus
- the initial setting of set_explain to FALSE is OK.
- */
- if (set_explain)
- DBUG_SET(dbug_explain);
-#endif
- return FALSE;
+static void scheduler_wait_sync_begin(void) {
+ thd_wait_begin(NULL, THD_WAIT_SYNC);
}
-
-/*
- Detach/disassociate the connection with the OS thread.
-*/
-
-void thd_scheduler::thread_detach()
-{
- if (thread_attached)
- {
- THD* thd = (THD*)list.data;
- thd->reset_globals();
- thread_attached= FALSE;
-#ifndef DBUG_OFF
- /*
- If during the session @@session.dbug was assigned, the
- dbug options/state has been pushed. Check if this is the
- case, to be able to restore the state when we attach this
- logical connection to a physical thread.
- */
- if (_db_is_pushed_())
- {
- set_explain= TRUE;
- if (DBUG_EXPLAIN(dbug_explain, sizeof(dbug_explain)))
- sql_print_error("thd_scheduler: DBUG_EXPLAIN buffer is too small");
- }
- /* DBUG_POP() is a no-op in case there is no session state */
- DBUG_POP();
-#endif
- }
+static void scheduler_wait_sync_end(void) {
+ thd_wait_end(NULL);
}
+};
+/**@}*/
/**
- Create all threads for the thread pool
-
- NOTES
- After threads are created we wait until all threads has signaled that
- they have started before we return
+ Common scheduler init function.
- RETURN
- 0 ok
- 1 We got an error creating the thread pool
- In this case we will abort all created threads
-*/
-
-static bool libevent_init(void)
-{
- uint i;
- DBUG_ENTER("libevent_init");
-
- base= (struct event_base *) event_init();
-
- created_threads= 0;
- killed_threads= 0;
- kill_pool_threads= FALSE;
-
- pthread_mutex_init(&LOCK_event_loop, MY_MUTEX_INIT_FAST);
- pthread_mutex_init(&LOCK_thd_add, MY_MUTEX_INIT_FAST);
-
- /* set up sockets used to add new thds to the event pool */
- if (init_socketpair(thd_add_pair))
- {
- sql_print_error("init_socketpair(thd_add_spair) error in libevent_init");
- DBUG_RETURN(1);
- }
- /* set up sockets used to kill thds in the event queue */
- if (init_socketpair(thd_kill_pair))
- {
- sql_print_error("init_socketpair(thd_kill_pair) error in libevent_init");
- close_socketpair(thd_add_pair);
- DBUG_RETURN(1);
- }
- event_set(&thd_add_event, thd_add_pair[0], EV_READ|EV_PERSIST,
- libevent_add_thd_callback, NULL);
- event_set(&thd_kill_event, thd_kill_pair[0], EV_READ|EV_PERSIST,
- libevent_kill_thd_callback, NULL);
-
- if (event_add(&thd_add_event, NULL) || event_add(&thd_kill_event, NULL))
- {
- sql_print_error("thd_add_event event_add error in libevent_init");
- libevent_end();
- DBUG_RETURN(1);
- }
- /* Set up the thread pool */
- created_threads= killed_threads= 0;
- pthread_mutex_lock(&LOCK_thread_count);
-
- for (i= 0; i < thread_pool_size; i++)
- {
- pthread_t thread;
- int error;
- if ((error= pthread_create(&thread, &connection_attrib,
- libevent_thread_proc, 0)))
- {
- sql_print_error("Can't create completion port thread (error %d)",
- error);
- pthread_mutex_unlock(&LOCK_thread_count);
- libevent_end(); // Cleanup
- DBUG_RETURN(TRUE);
- }
- }
-
- /* Wait until all threads are created */
- while (created_threads != thread_pool_size)
- pthread_cond_wait(&COND_thread_count,&LOCK_thread_count);
- pthread_mutex_unlock(&LOCK_thread_count);
-
- DBUG_PRINT("info", ("%u threads created", (uint) thread_pool_size));
- DBUG_RETURN(FALSE);
-}
-
-
-/*
- This is called when data is ready on the socket.
-
- NOTES
- This is only called by the thread that owns LOCK_event_loop.
-
- We add the thd that got the data to thds_need_processing, and
- cause the libevent event_loop() to terminate. Then this same thread will
- return from event_loop and pick the thd value back up for processing.
-*/
-
-void libevent_io_callback(int, short, void *ctx)
-{
- safe_mutex_assert_owner(&LOCK_event_loop);
- THD *thd= (THD*)ctx;
- thds_waiting_for_io= list_delete(thds_waiting_for_io, &thd->event_scheduler.list);
- thds_need_processing= list_add(thds_need_processing, &thd->event_scheduler.list);
-}
-
-/*
- This is called when we have a thread we want to be killed.
-
- NOTES
- This is only called by the thread that owns LOCK_event_loop.
-*/
-
-void libevent_kill_thd_callback(int Fd, short, void*)
-{
- safe_mutex_assert_owner(&LOCK_event_loop);
-
- /* clear the pending events */
- char c;
- while (recv(Fd, &c, sizeof(c), 0) == sizeof(c))
- {}
-
- LIST* list= thds_waiting_for_io;
- while (list)
- {
- THD *thd= (THD*)list->data;
- list= list_rest(list);
- if ((int) thd->killed >= (int) KILL_CONNECTION)
- {
- /*
- Delete from libevent and add to the processing queue.
- */
- event_del(thd->event_scheduler.io_event);
- thds_waiting_for_io= list_delete(thds_waiting_for_io,
- &thd->event_scheduler.list);
- thds_need_processing= list_add(thds_need_processing,
- &thd->event_scheduler.list);
- }
- }
-}
-
-
-/*
- This is used to add connections to the pool. This callback is invoked from
- the libevent event_loop() call whenever the thd_add_pair[1] has a byte
- written to it.
-
- NOTES
- This is only called by the thread that owns LOCK_event_loop.
-*/
-
-void libevent_add_thd_callback(int Fd, short, void *)
-{
- safe_mutex_assert_owner(&LOCK_event_loop);
-
- /* clear the pending events */
- char c;
- while (recv(Fd, &c, sizeof(c), 0) == sizeof(c))
- {}
-
- pthread_mutex_lock(&LOCK_thd_add);
- while (thds_need_adding)
- {
- /* pop the first thd off the list */
- THD* thd= (THD*)thds_need_adding->data;
- thds_need_adding= list_delete(thds_need_adding, thds_need_adding);
-
- pthread_mutex_unlock(&LOCK_thd_add);
-
- if (!thd->event_scheduler.logged_in || libevent_should_close_connection(thd))
- {
- /*
- Add thd to thds_need_processing list. If it needs closing we'll close
- it outside of event_loop().
- */
- thds_need_processing= list_add(thds_need_processing,
- &thd->event_scheduler.list);
- }
- else
- {
- /* Add to libevent */
- if (event_add(thd->event_scheduler.io_event, NULL))
- {
- sql_print_error("event_add error in libevent_add_thd_callback");
- libevent_connection_close(thd);
- }
- else
- {
- thds_waiting_for_io= list_add(thds_waiting_for_io,
- &thd->event_scheduler.list);
- }
- }
- pthread_mutex_lock(&LOCK_thd_add);
- }
- pthread_mutex_unlock(&LOCK_thd_add);
+ The scheduler is either initialized by calling
+ one_thread_scheduler() or one_thread_per_connection_scheduler() in
+ mysqld.cc, so this init function will always be called.
+ */
+void scheduler_init() {
+ thr_set_lock_wait_callback(scheduler_wait_lock_begin,
+ scheduler_wait_lock_end);
+ thr_set_sync_wait_callback(scheduler_wait_sync_begin,
+ scheduler_wait_sync_end);
}
/**
- Notify the thread pool about a new connection
+ Kill notification callback, used by one-thread-per-connection
+ and threadpool scheduler.
- NOTES
- LOCK_thread_count is locked on entry. This function MUST unlock it!
+ Wakes up a thread that is stuck in read/poll/epoll/event-poll
+ routines used by threadpool, such that subsequent attempt to
+ read from client connection will result in IO error.
*/
-static void libevent_add_connection(THD *thd)
+void post_kill_notification(THD *thd)
{
- DBUG_ENTER("libevent_add_connection");
- DBUG_PRINT("enter", ("thd: %p thread_id: %lu",
- thd, thd->thread_id));
-
- if (thd->event_scheduler.init(thd))
- {
- sql_print_error("Scheduler init error in libevent_add_new_connection");
- pthread_mutex_unlock(&LOCK_thread_count);
- libevent_connection_close(thd);
+ DBUG_ENTER("post_kill_notification");
+ if (current_thd == thd || thd->system_thread)
DBUG_VOID_RETURN;
- }
- threads.append(thd);
- libevent_thd_add(thd);
- pthread_mutex_unlock(&LOCK_thread_count);
+ if (thd->net.vio)
+ vio_shutdown(thd->net.vio, SHUT_RD);
DBUG_VOID_RETURN;
}
-
-/**
- @brief Signal a waiting connection it's time to die.
-
- @details This function will signal libevent the THD should be killed.
- Either the global LOCK_thd_count or the THD's LOCK_thd_data must be locked
- upon entry.
-
- @param[in] thd The connection to kill
-*/
-
-static void libevent_post_kill_notification(THD *)
-{
- /*
- Note, we just wake up libevent with an event that a THD should be killed,
- It will search its list of thds for thd->killed == KILL_CONNECTION to
- find the THDs it should kill.
-
- So we don't actually tell it which one and we don't actually use the
- THD being passed to us, but that's just a design detail that could change
- later.
- */
- char c= 0;
- send(thd_kill_pair[1], &c, sizeof(c), 0);
-}
-
-
/*
- Close and delete a connection.
+ Initialize scheduler for --thread-handling=one-thread-per-connection
*/
-static void libevent_connection_close(THD *thd)
-{
- DBUG_ENTER("libevent_connection_close");
- DBUG_PRINT("enter", ("thd: %p", thd));
-
- thd->killed= KILL_CONNECTION; // Avoid error messages
-
- if (thd->net.vio->type != VIO_CLOSED) // not already closed
- {
- end_connection(thd);
- close_connection(thd, 0, 1);
- }
- thd->event_scheduler.thread_detach();
- unlink_thd(thd); /* locks LOCK_thread_count and deletes thd */
- pthread_mutex_unlock(&LOCK_thread_count);
-
- DBUG_VOID_RETURN;
-}
-
+#ifndef EMBEDDED_LIBRARY
-/*
- Returns true if we should close and delete a THD connection.
-*/
-static bool libevent_should_close_connection(THD* thd)
+void one_thread_per_connection_scheduler(scheduler_functions *func,
+ ulong *arg_max_connections,
+ uint *arg_connection_count)
{
- return (thd->net.error ||
- thd->net.vio == 0 ||
- (int) thd->killed >= (int) KILL_CONNECTION);
+ scheduler_init();
+ func->max_threads= *arg_max_connections + 1;
+ func->max_connections= arg_max_connections;
+ func->connection_count= arg_connection_count;
+ func->init_new_connection_thread= init_new_connection_handler_thread;
+ func->add_connection= create_thread_to_handle_connection;
+ func->end_thread= one_thread_per_connection_end;
+ func->post_kill_notification= post_kill_notification;
}
-
+#endif
/*
- libevent_thread_proc is the outer loop of each thread in the thread pool.
- These procs only return/terminate on shutdown (kill_pool_threads == true).
+ Initailize scheduler for --thread-handling=no-threads
*/
-pthread_handler_t libevent_thread_proc(void *arg)
+void one_thread_scheduler(scheduler_functions *func)
{
- if (init_new_connection_handler_thread())
- {
- my_thread_global_end();
- sql_print_error("libevent_thread_proc: my_thread_init() failed");
- exit(1);
- }
- DBUG_ENTER("libevent_thread_proc");
-
- /*
- Signal libevent_init() when all threads has been created and are ready to
- receive events.
- */
- (void) pthread_mutex_lock(&LOCK_thread_count);
- created_threads++;
- thread_created++;
- if (created_threads == thread_pool_size)
- (void) pthread_cond_signal(&COND_thread_count);
- (void) pthread_mutex_unlock(&LOCK_thread_count);
-
- for (;;)
- {
- THD *thd= NULL;
- (void) pthread_mutex_lock(&LOCK_event_loop);
-
- /* get thd(s) to process */
- while (!thds_need_processing)
- {
- if (kill_pool_threads)
- {
- /* the flag that we should die has been set */
- (void) pthread_mutex_unlock(&LOCK_event_loop);
- goto thread_exit;
- }
- event_loop(EVLOOP_ONCE);
- }
-
- /* pop the first thd off the list */
- thd= (THD*)thds_need_processing->data;
- thds_need_processing= list_delete(thds_need_processing,
- thds_need_processing);
-
- (void) pthread_mutex_unlock(&LOCK_event_loop);
-
- /* now we process the connection (thd) */
-
- /* set up the thd<->thread links. */
- thd->thread_stack= (char*) &thd;
-
- if (thd->event_scheduler.thread_attach())
- {
- libevent_connection_close(thd);
- continue;
- }
-
- /* is the connection logged in yet? */
- if (!thd->event_scheduler.logged_in)
- {
- DBUG_PRINT("info", ("init new connection. sd: %d",
- thd->net.vio->sd));
- lex_start(thd);
- if (login_connection(thd))
- {
- /* Failed to log in */
- libevent_connection_close(thd);
- continue;
- }
- else
- {
- /* login successful */
-#if MYSQL_VERSION_ID >= 60000
- MYSQL_CONNECTION_START(thd->thread_id, thd->security_ctx->priv_user,
- (char *) thd->security_ctx->host_or_ip);
+ scheduler_init();
+ func->max_threads= 1;
+ func->max_connections= &max_connections;
+ func->connection_count= &connection_count;
+#ifndef EMBEDDED_LIBRARY
+ func->init_new_connection_thread= init_new_connection_handler_thread;
+ func->add_connection= handle_connection_in_main_thread;
#endif
- thd->event_scheduler.logged_in= TRUE;
- prepare_new_connection_state(thd);
- if (!libevent_needs_immediate_processing(thd))
- continue; /* New connection is now waiting for data in libevent*/
- }
- }
-
- do
- {
- /* Process a query */
- if (do_command(thd))
- {
- libevent_connection_close(thd);
- break;
- }
- } while (libevent_needs_immediate_processing(thd));
- }
-
-thread_exit:
- DBUG_PRINT("exit", ("ending thread"));
- (void) pthread_mutex_lock(&LOCK_thread_count);
- killed_threads++;
- pthread_cond_broadcast(&COND_thread_count);
- (void) pthread_mutex_unlock(&LOCK_thread_count);
- my_thread_end();
- pthread_exit(0);
- DBUG_RETURN(0); /* purify: deadcode */
+ func->end_thread= no_threads_end;
}
-/*
- Returns TRUE if the connection needs immediate processing and FALSE if
- instead it's queued for libevent processing or closed,
-*/
-
-static bool libevent_needs_immediate_processing(THD *thd)
-{
- if (libevent_should_close_connection(thd))
- {
- libevent_connection_close(thd);
- return FALSE;
- }
- /*
- If more data in the socket buffer, return TRUE to process another command.
-
- Note: we cannot add for event processing because the whole request might
- already be buffered and we wouldn't receive an event.
- */
- if (vio_pending(thd->net.vio) > 0)
- return TRUE;
-
- thd->event_scheduler.thread_detach();
- libevent_thd_add(thd);
- return FALSE;
-}
-
/*
- Adds a THD to queued for libevent processing.
-
- This call does not actually register the event with libevent.
- Instead, it places the THD onto a queue and signals libevent by writing
- a byte into thd_add_pair, which will cause our libevent_add_thd_callback to
- be invoked which will find the THD on the queue and add it to libevent.
+ no pluggable schedulers in mariadb.
+ when we'll want it, we'll do it properly
*/
+#if 0
-static void libevent_thd_add(THD* thd)
-{
- char c= 0;
- /* release any audit resources, this thd is going to sleep */
-#if MYSQL_VERSION_ID >= 60000
- mysql_audit_release(thd);
-#endif
- pthread_mutex_lock(&LOCK_thd_add);
- /* queue for libevent */
- thds_need_adding= list_add(thds_need_adding, &thd->event_scheduler.list);
- /* notify libevent */
- send(thd_add_pair[1], &c, sizeof(c), 0);
- pthread_mutex_unlock(&LOCK_thd_add);
-}
-
-
-/**
- Wait until all pool threads have been deleted for clean shutdown
-*/
+static scheduler_functions *saved_thread_scheduler;
+static uint saved_thread_handling;
-static void libevent_end()
+extern "C"
+int my_thread_scheduler_set(scheduler_functions *scheduler)
{
- DBUG_ENTER("libevent_end");
- DBUG_PRINT("enter", ("created_threads: %d killed_threads: %u",
- created_threads, killed_threads));
-
- /*
- check if initialized. This may not be the case if get an error at
- startup
- */
- if (!base)
- DBUG_VOID_RETURN;
-
- (void) pthread_mutex_lock(&LOCK_thread_count);
-
-
- kill_pool_threads= TRUE;
- while (killed_threads != created_threads)
- {
- /* wake up the event loop */
- char c= 0;
- send(thd_add_pair[1], &c, sizeof(c), 0);
- pthread_cond_wait(&COND_thread_count, &LOCK_thread_count);
- }
- (void) pthread_mutex_unlock(&LOCK_thread_count);
+ DBUG_ASSERT(scheduler != 0);
- event_del(&thd_add_event);
- close_socketpair(thd_add_pair);
- event_del(&thd_kill_event);
- close_socketpair(thd_kill_pair);
- event_base_free(base);
- base= 0;
+ if (scheduler == NULL)
+ return 1;
- (void) pthread_mutex_destroy(&LOCK_event_loop);
- (void) pthread_mutex_destroy(&LOCK_thd_add);
- DBUG_VOID_RETURN;
+ saved_thread_scheduler= thread_scheduler;
+ saved_thread_handling= thread_handling;
+ thread_scheduler= scheduler;
+ // Scheduler loaded dynamically
+ thread_handling= SCHEDULER_TYPES_COUNT;
+ return 0;
}
-void pool_of_threads_scheduler(scheduler_functions* func)
+extern "C"
+int my_thread_scheduler_reset()
{
- func->max_threads= thread_pool_size;
- func->max_connections= &max_connections;
- func->connection_count= &connection_count;
- func->init= libevent_init;
- func->end= libevent_end;
- func->post_kill_notification= libevent_post_kill_notification;
- func->add_connection= libevent_add_connection;
+ DBUG_ASSERT(saved_thread_scheduler != NULL);
+
+ if (saved_thread_scheduler == NULL)
+ return 1;
+
+ thread_scheduler= saved_thread_scheduler;
+ thread_handling= saved_thread_handling;
+ saved_thread_scheduler= 0;
+ return 0;
}
+#else
+extern "C" int my_thread_scheduler_set(scheduler_functions *scheduler)
+{ return 1; }
+extern "C" int my_thread_scheduler_reset()
+{ return 1; }
#endif
+
diff --git a/sql/scheduler.h b/sql/scheduler.h
index 7c64441b2b8..f7aff377eac 100644
--- a/sql/scheduler.h
+++ b/sql/scheduler.h
@@ -1,4 +1,8 @@
-/* Copyright (C) 2007 MySQL AB
+#ifndef SCHEDULER_INCLUDED
+#define SCHEDULER_INCLUDED
+
+/* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2012, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,7 +15,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
Classes for the thread scheduler
@@ -21,72 +25,87 @@
#pragma interface
#endif
+#include <my_global.h>
+
class THD;
/* Functions used when manipulating threads */
-class scheduler_functions
+struct scheduler_functions
{
-public:
uint max_threads, *connection_count;
ulong *max_connections;
bool (*init)(void);
bool (*init_new_connection_thread)(void);
void (*add_connection)(THD *thd);
+ void (*thd_wait_begin)(THD *thd, int wait_type);
+ void (*thd_wait_end)(THD *thd);
void (*post_kill_notification)(THD *thd);
bool (*end_thread)(THD *thd, bool cache_thread);
void (*end)(void);
- scheduler_functions();
};
+
+/**
+ Scheduler types enumeration.
+
+ The default of --thread-handling is the first one in the
+ thread_handling_names array, this array has to be consistent with
+ the order in this array, so to change default one has to change the
+ first entry in this enum and the first entry in the
+ thread_handling_names array.
+
+ @note The last entry of the enumeration is also used to mark the
+ thread handling as dynamic. In this case the name of the thread
+ handling is fetched from the name of the plugin that implements it.
+*/
enum scheduler_types
{
+ /*
+ The default of --thread-handling is the first one in the
+ thread_handling_names array, this array has to be consistent with
+ the order in this array, so to change default one has to change
+ the first entry in this enum and the first entry in the
+ thread_handling_names array.
+ */
SCHEDULER_ONE_THREAD_PER_CONNECTION=0,
SCHEDULER_NO_THREADS,
- SCHEDULER_POOL_OF_THREADS
+ SCHEDULER_TYPES_COUNT
};
void one_thread_per_connection_scheduler(scheduler_functions *func,
- ulong *arg_max_connections,
- uint *arg_connection_count);
-void one_thread_scheduler(scheduler_functions* func);
-
-#if defined(HAVE_LIBEVENT) && !defined(EMBEDDED_LIBRARY)
-
-#define HAVE_POOL_OF_THREADS 1
+ ulong *arg_max_connections, uint *arg_connection_count);
+void one_thread_scheduler(scheduler_functions *func);
-struct event;
-
-class thd_scheduler
+extern void scheduler_init();
+extern void post_kill_notification(THD *);
+/*
+ To be used for pool-of-threads (implemeneted differently on various OSs)
+*/
+struct thd_scheduler
{
public:
- bool logged_in;
- struct event* io_event;
- LIST list;
- bool thread_attached; /* Indicates if THD is attached to the OS thread */
-
-#ifndef DBUG_OFF
- char dbug_explain[256];
- bool set_explain;
-#endif
-
- thd_scheduler();
- ~thd_scheduler();
- bool init(THD* parent_thd);
- bool thread_attach();
- void thread_detach();
+ /*
+ Thread instrumentation for the user job.
+ This member holds the instrumentation while the user job is not run
+ by a thread.
+
+ Note that this member is not conditionally declared
+ (ifdef HAVE_PSI_INTERFACE), because doing so will change the binary
+ layout of THD, which is exposed to plugin code that may be compiled
+ differently.
+ */
+ PSI_thread *m_psi;
+ void *data; /* scheduler-specific data structure */
};
-void pool_of_threads_scheduler(scheduler_functions* func);
-
+#ifdef HAVE_POOL_OF_THREADS
+void pool_of_threads_scheduler(scheduler_functions* func,
+ ulong *arg_max_connections,
+ uint *arg_connection_count);
#else
+#define pool_of_threads_scheduler(A,B,C) \
+ one_thread_per_connection_scheduler(A, B, C)
+#endif /*HAVE_POOL_OF_THREADS*/
-#define HAVE_POOL_OF_THREADS 0 /* For easyer tests */
-#define pool_of_threads_scheduler(A) \
- one_thread_per_connection_scheduler(A, &max_connections, \
- &connection_count)
-
-class thd_scheduler
-{};
-
-#endif
+#endif /* SCHEDULER_INCLUDED */
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 7c341e20d75..9efa29d8041 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -1,6 +1,5 @@
-/*
- Copyright (c) 2002, 2012, Oracle and/or its affiliates.
- Copyright (c) 2008, 2014, Monty Program Ab.
+/* Copyright (c) 2002, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,2272 +12,368 @@
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-*/
-
-/**
- @file
-
- @brief
- Handling of MySQL SQL variables
-
- @details
- To add a new variable, one has to do the following:
-
- - Use one of the 'sys_var... classes from set_var.h or write a specific
- one for the variable type.
- - Define it in the 'variable definition list' in this file.
- - If the variable is thread specific, add it to 'system_variables' struct.
- If not, add it to mysqld.cc and an declaration in 'mysql_priv.h'
- - If the variable should be changed from the command line, add a definition
- of it in the my_option structure list in mysqld.cc
- - Don't forget to initialize new fields in global_system_variables and
- max_system_variables!
-
- @todo
- Add full support for the variable character_set (for 4.1)
-
- @todo
- When updating myisam_delay_key_write, we should do a 'flush tables'
- of all MyISAM tables to ensure that they are reopen with the
- new attribute.
-
- @note
- Be careful with var->save_result: sys_var::check() only updates
- ulonglong_value; so other members of the union are garbage then; to use
- them you must first assign a value to them (in specific ::check() for
- example).
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
-#include "mysql_priv.h"
-#include <mysql.h>
-#include "slave.h"
-#include "rpl_mi.h"
-#include <my_getopt.h>
-#include <thr_alarm.h>
-#include <myisam.h>
-#ifdef WITH_ARIA_STORAGE_ENGINE
-#include <maria.h>
-#endif
-#include <my_dir.h>
-#include <waiting_threads.h>
-#include "events.h"
-#include "sql_show.h" // opt_ignore_db_dirs
-
-/* WITH_NDBCLUSTER_STORAGE_ENGINE */
-#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
-extern ulong ndb_cache_check_time;
-extern char opt_ndb_constrbuf[];
-extern ulong ndb_extra_logging;
-#endif
-
-#ifdef HAVE_NDB_BINLOG
-extern ulong ndb_report_thresh_binlog_epoch_slip;
-extern ulong ndb_report_thresh_binlog_mem_usage;
-#endif
-
-extern CHARSET_INFO *character_set_filesystem;
-
+#pragma implementation
+#endif
+
+/* variable declarations are in sys_vars.cc now !!! */
+
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_class.h" // set_var.h: session_var_ptr
+#include "set_var.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "mysqld.h" // lc_messages_dir
+#include "sys_vars_shared.h"
+#include "transaction.h"
+#include "sql_locale.h" // my_locale_by_number,
+ // my_locale_by_name
+#include "strfunc.h" // find_set_from_flags, find_set
+#include "sql_parse.h" // check_global_access
+#include "sql_table.h" // reassign_keycache_tables
+#include "sql_time.h" // date_time_format_copy,
+ // date_time_format_make
+#include "derror.h"
+#include "tztime.h" // my_tz_find, my_tz_SYSTEM, struct Time_zone
+#include "sql_acl.h" // SUPER_ACL
+#include "sql_select.h" // free_underlaid_joins
+#include "sql_view.h" // updatable_views_with_limit_typelib
+#include "lock.h" // lock_global_read_lock,
+ // make_global_read_lock_block_commit,
+ // unlock_global_read_lock
static HASH system_variable_hash;
+static PolyLock_mutex PLock_global_system_variables(&LOCK_global_system_variables);
-const char *bool_type_names[]= { "OFF", "ON", NullS };
-TYPELIB bool_typelib=
-{
- array_elements(bool_type_names)-1, "", bool_type_names, NULL
-};
-
-const char *delay_key_write_type_names[]= { "OFF", "ON", "ALL", NullS };
-TYPELIB delay_key_write_typelib=
-{
- array_elements(delay_key_write_type_names)-1, "",
- delay_key_write_type_names, NULL
-};
-
-static const char *slave_exec_mode_names[]= { "STRICT", "IDEMPOTENT", NullS };
-static unsigned int slave_exec_mode_names_len[]= { sizeof("STRICT") - 1,
- sizeof("IDEMPOTENT") - 1, 0 };
-TYPELIB slave_exec_mode_typelib=
-{
- array_elements(slave_exec_mode_names)-1, "",
- slave_exec_mode_names, slave_exec_mode_names_len
-};
-
-static int sys_check_ftb_syntax(THD *thd, set_var *var);
-static bool sys_update_ftb_syntax(THD *thd, set_var * var);
-static void sys_default_ftb_syntax(THD *thd, enum_var_type type);
-static bool sys_update_init_connect(THD*, set_var*);
-static void sys_default_init_connect(THD*, enum_var_type type);
-static bool sys_update_init_slave(THD*, set_var*);
-static void sys_default_init_slave(THD*, enum_var_type type);
-static bool set_option_bit(THD *thd, set_var *var);
-static bool set_option_log_bin_bit(THD *thd, set_var *var);
-static bool set_option_autocommit(THD *thd, set_var *var);
-static int check_log_update(THD *thd, set_var *var);
-static bool set_log_update(THD *thd, set_var *var);
-static int check_pseudo_thread_id(THD *thd, set_var *var);
-void fix_binlog_format_after_update(THD *thd, enum_var_type type);
-static void fix_low_priority_updates(THD *thd, enum_var_type type);
-static int check_tx_isolation(THD *thd, set_var *var);
-static void fix_tx_isolation(THD *thd, enum_var_type type);
-static int check_completion_type(THD *thd, set_var *var);
-static void fix_completion_type(THD *thd, enum_var_type type);
-static void fix_net_read_timeout(THD *thd, enum_var_type type);
-static void fix_net_write_timeout(THD *thd, enum_var_type type);
-static void fix_net_retry_count(THD *thd, enum_var_type type);
-static void fix_max_join_size(THD *thd, enum_var_type type);
-#ifdef HAVE_QUERY_CACHE
-static void fix_query_cache_size(THD *thd, enum_var_type type);
-static void fix_query_cache_min_res_unit(THD *thd, enum_var_type type);
-static int check_query_cache_type(THD *thd, set_var *var);
-static void fix_query_cache_type(THD *thd, enum_var_type type);
-#endif
-static void fix_myisam_max_sort_file_size(THD *thd, enum_var_type type);
-static void fix_max_binlog_size(THD *thd, enum_var_type type);
-static void fix_max_relay_log_size(THD *thd, enum_var_type type);
-static void fix_max_connections(THD *thd, enum_var_type type);
-static int check_max_delayed_threads(THD *thd, set_var *var);
-static void fix_thd_mem_root(THD *thd, enum_var_type type);
-static void fix_trans_mem_root(THD *thd, enum_var_type type);
-static void fix_server_id(THD *thd, enum_var_type type);
-bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd,
- const char *name, longlong val);
-static KEY_CACHE *create_key_cache(const char *name, uint length);
-void fix_sql_mode_var(THD *thd, enum_var_type type);
-static uchar *get_error_count(THD *thd);
-static uchar *get_warning_count(THD *thd);
-static uchar *get_tmpdir(THD *thd);
-static int sys_check_log_path(THD *thd, set_var *var);
-static bool sys_update_general_log_path(THD *thd, set_var * var);
-static void sys_default_general_log_path(THD *thd, enum_var_type type);
-static bool sys_update_slow_log_path(THD *thd, set_var * var);
-static void sys_default_slow_log_path(THD *thd, enum_var_type type);
-static void fix_sys_log_slow_filter(THD *thd, enum_var_type);
-static uchar *get_myisam_mmap_size(THD *thd);
-static uchar *in_transaction(THD *thd);
-static int check_max_allowed_packet(THD *thd, set_var *var);
-static int check_net_buffer_length(THD *thd, set_var *var);
-
-/*
- Variable definition list
-
- These are variables that can be set from the command line, in
- alphabetic order.
-
- The variables are linked into the list. A variable is added to
- it in the constructor (see sys_var class for details).
-*/
-
-static sys_var_chain vars = { NULL, NULL };
-
-static sys_var_thd_ulong
-sys_auto_increment_increment(&vars, "auto_increment_increment",
- &SV::auto_increment_increment, NULL, NULL,
- sys_var::SESSION_VARIABLE_IN_BINLOG);
-static sys_var_thd_ulong
-sys_auto_increment_offset(&vars, "auto_increment_offset",
- &SV::auto_increment_offset, NULL, NULL,
- sys_var::SESSION_VARIABLE_IN_BINLOG);
-
-static sys_var_bool_ptr sys_automatic_sp_privileges(&vars, "automatic_sp_privileges",
- &sp_automatic_privileges);
-
-static sys_var_const sys_back_log(&vars, "back_log",
- OPT_GLOBAL, SHOW_LONG,
- (uchar*) &back_log);
-static sys_var_const_os_str sys_basedir(&vars, "basedir", mysql_home);
-static sys_var_thd_bool
-sys_binlog_annotate_row_events(&vars, "binlog_annotate_row_events",
- &SV::binlog_annotate_row_events);
-static sys_var_long_ptr sys_binlog_cache_size(&vars, "binlog_cache_size",
- &binlog_cache_size);
-static sys_var_thd_binlog_format sys_binlog_format(&vars, "binlog_format",
- &SV::binlog_format);
-static sys_var_thd_bool sys_binlog_direct_non_trans_update(&vars, "binlog_direct_non_transactional_updates",
- &SV::binlog_direct_non_trans_update);
-static sys_var_thd_ulong sys_bulk_insert_buff_size(&vars, "bulk_insert_buffer_size",
- &SV::bulk_insert_buff_size);
-static sys_var_const_os sys_character_sets_dir(&vars,
- "character_sets_dir",
- OPT_GLOBAL, SHOW_CHAR,
- (uchar*)
- mysql_charsets_dir);
-static sys_var_character_set_sv
-sys_character_set_server(&vars, "character_set_server",
- &SV::collation_server, &default_charset_info, 0,
- sys_var::SESSION_VARIABLE_IN_BINLOG);
-sys_var_const_str sys_charset_system(&vars, "character_set_system",
- (char *)my_charset_utf8_general_ci.name);
-static sys_var_character_set_database
-sys_character_set_database(&vars, "character_set_database",
- sys_var::SESSION_VARIABLE_IN_BINLOG);
-static sys_var_character_set_client
-sys_character_set_client(&vars, "character_set_client",
- &SV::character_set_client,
- &default_charset_info,
- sys_var::SESSION_VARIABLE_IN_BINLOG);
-static sys_var_character_set_sv
-sys_character_set_connection(&vars, "character_set_connection",
- &SV::collation_connection,
- &default_charset_info, 0,
- sys_var::SESSION_VARIABLE_IN_BINLOG);
-static sys_var_character_set_sv sys_character_set_results(&vars, "character_set_results",
- &SV::character_set_results,
- &default_charset_info, true);
-static sys_var_character_set_sv sys_character_set_filesystem(&vars, "character_set_filesystem",
- &SV::character_set_filesystem,
- &character_set_filesystem);
-static sys_var_thd_ulong sys_completion_type(&vars, "completion_type",
- &SV::completion_type,
- check_completion_type,
- fix_completion_type);
-static sys_var_collation_sv
-sys_collation_connection(&vars, "collation_connection",
- &SV::collation_connection, &default_charset_info,
- sys_var::SESSION_VARIABLE_IN_BINLOG);
-static sys_var_collation_sv
-sys_collation_database(&vars, "collation_database", &SV::collation_database,
- &default_charset_info,
- sys_var::SESSION_VARIABLE_IN_BINLOG);
-static sys_var_collation_sv
-sys_collation_server(&vars, "collation_server", &SV::collation_server,
- &default_charset_info,
- sys_var::SESSION_VARIABLE_IN_BINLOG);
-static sys_var_long_ptr sys_concurrent_insert(&vars, "concurrent_insert",
- &myisam_concurrent_insert);
-static sys_var_long_ptr sys_connect_timeout(&vars, "connect_timeout",
- &connect_timeout);
-static sys_var_const_os_str sys_datadir(&vars, "datadir", mysql_real_data_home);
-
-static sys_var_thd_ulong sys_deadlock_search_depth_short(&vars,
- "deadlock_search_depth_short",
- &SV::wt_deadlock_search_depth_short);
-static sys_var_thd_ulong sys_deadlock_search_depth_long(&vars,
- "deadlock_search_depth_long",
- &SV::wt_deadlock_search_depth_long);
-static sys_var_thd_ulong sys_deadlock_timeout_short(&vars,
- "deadlock_timeout_short",
- &SV::wt_timeout_short);
-static sys_var_thd_ulong sys_deadlock_timeout_long(&vars,
- "deadlock_timeout_long",
- &SV::wt_timeout_long);
-#ifndef DBUG_OFF
-static sys_var_thd_dbug sys_dbug(&vars, "debug");
-static sys_var_long_ptr sys_var_debug_binlog_fsync_sleep(&vars,
- "debug_binlog_fsync_sleep",
- &opt_binlog_dbug_fsync_sleep);
-#endif
-static sys_var_enum sys_delay_key_write(&vars, "delay_key_write",
- &delay_key_write_options,
- &delay_key_write_typelib,
- fix_delay_key_write);
-static sys_var_long_ptr sys_delayed_insert_limit(&vars, "delayed_insert_limit",
- &delayed_insert_limit);
-static sys_var_long_ptr sys_delayed_insert_timeout(&vars, "delayed_insert_timeout",
- &delayed_insert_timeout);
-static sys_var_long_ptr sys_delayed_queue_size(&vars, "delayed_queue_size",
- &delayed_queue_size);
-
-#ifdef HAVE_EVENT_SCHEDULER
-static sys_var_event_scheduler sys_event_scheduler(&vars, "event_scheduler");
-#endif
-
-static sys_var_const sys_extra_port(&vars, "extra_port",
- OPT_GLOBAL, SHOW_INT,
- (uchar*) &mysqld_extra_port);
-static sys_var_long_ptr sys_extra_max_connections(&vars,
- "extra_max_connections",
- &extra_max_connections,
- fix_max_connections);
-static sys_var_long_ptr sys_expire_logs_days(&vars, "expire_logs_days",
- &expire_logs_days);
-static sys_var_bool_ptr sys_flush(&vars, "flush", &myisam_flush);
-static sys_var_long_ptr sys_flush_time(&vars, "flush_time", &flush_time);
-static sys_var_str sys_ft_boolean_syntax(&vars, "ft_boolean_syntax",
- sys_check_ftb_syntax,
- sys_update_ftb_syntax,
- sys_default_ftb_syntax,
- ft_boolean_syntax);
-static sys_var_const sys_ft_max_word_len(&vars, "ft_max_word_len",
- OPT_GLOBAL, SHOW_LONG,
- (uchar*) &ft_max_word_len);
-static sys_var_const sys_ft_min_word_len(&vars, "ft_min_word_len",
- OPT_GLOBAL, SHOW_LONG,
- (uchar*) &ft_min_word_len);
-static sys_var_const sys_ft_query_expansion_limit(&vars,
- "ft_query_expansion_limit",
- OPT_GLOBAL, SHOW_LONG,
- (uchar*)
- &ft_query_expansion_limit);
-static sys_var_const sys_ft_stopword_file(&vars, "ft_stopword_file",
- OPT_GLOBAL, SHOW_CHAR_PTR,
- (uchar*) &ft_stopword_file);
-
-static sys_var_const sys_ignore_builtin_innodb(&vars, "ignore_builtin_innodb",
- OPT_GLOBAL, SHOW_BOOL,
- (uchar*) &opt_ignore_builtin_innodb);
-
-sys_var_str sys_init_connect(&vars, "init_connect", 0,
- sys_update_init_connect,
- sys_default_init_connect,0);
-static sys_var_const sys_init_file(&vars, "init_file",
- OPT_GLOBAL, SHOW_CHAR_PTR,
- (uchar*) &opt_init_file);
-sys_var_str sys_init_slave(&vars, "init_slave", 0,
- sys_update_init_slave,
- sys_default_init_slave,0);
-static sys_var_thd_ulong sys_interactive_timeout(&vars, "interactive_timeout",
- &SV::net_interactive_timeout);
-static sys_var_thd_ulong sys_join_buffer_size(&vars, "join_buffer_size",
- &SV::join_buff_size);
-static sys_var_thd_ulonglong sys_join_buffer_space_limit(&vars,
- "join_buffer_space_limit",
- &SV::join_buff_space_limit);
-static sys_var_thd_ulong sys_join_cache_level(&vars, "join_cache_level",
- &SV::join_cache_level);
-static sys_var_key_buffer_size sys_key_buffer_size(&vars, "key_buffer_size");
-static sys_var_key_cache_long sys_key_cache_block_size(&vars,
- "key_cache_block_size",
- offsetof(KEY_CACHE,param_block_size));
-static sys_var_key_cache_long sys_key_cache_division_limit(&vars,
- "key_cache_division_limit",
- offsetof(KEY_CACHE, param_division_limit));
-static sys_var_key_cache_long sys_key_cache_age_threshold(&vars,
- "key_cache_age_threshold",
- offsetof(KEY_CACHE, param_age_threshold));
-static sys_var_key_cache_long sys_key_cache_partitions(&vars,
- "key_cache_segments",
- offsetof(KEY_CACHE, param_partitions));
-static sys_var_const sys_language(&vars, "language",
- OPT_GLOBAL, SHOW_CHAR,
- (uchar*) language);
-static sys_var_const sys_large_files_support(&vars, "large_files_support",
- OPT_GLOBAL, SHOW_BOOL,
- (uchar*) &opt_large_files);
-static sys_var_const sys_large_page_size(&vars, "large_page_size",
- OPT_GLOBAL, SHOW_INT,
- (uchar*) &opt_large_page_size);
-static sys_var_const sys_large_pages(&vars, "large_pages",
- OPT_GLOBAL, SHOW_MY_BOOL,
- (uchar*) &opt_large_pages);
-static sys_var_bool_ptr sys_local_infile(&vars, "local_infile",
- &opt_local_infile);
-#ifdef HAVE_MLOCKALL
-static sys_var_const sys_locked_in_memory(&vars, "locked_in_memory",
- OPT_GLOBAL, SHOW_MY_BOOL,
- (uchar*) &locked_in_memory);
-#endif
-static sys_var_const sys_log_bin(&vars, "log_bin",
- OPT_GLOBAL, SHOW_BOOL,
- (uchar*) &opt_bin_log);
-static sys_var_trust_routine_creators
-sys_trust_routine_creators(&vars, "log_bin_trust_routine_creators",
- &trust_function_creators);
-static sys_var_bool_ptr
-sys_trust_function_creators(&vars, "log_bin_trust_function_creators",
- &trust_function_creators);
-static sys_var_const sys_log_error(&vars, "log_error",
- OPT_GLOBAL, SHOW_CHAR,
- (uchar*) log_error_file);
-static sys_var_bool_ptr
- sys_log_queries_not_using_indexes(&vars, "log_queries_not_using_indexes",
- &opt_log_queries_not_using_indexes);
-static sys_var_thd_ulong sys_log_warnings(&vars, "log_warnings", &SV::log_warnings);
-static sys_var_microseconds sys_var_long_query_time(&vars, "long_query_time",
- &SV::long_query_time);
-static sys_var_microseconds sys_var_long_query_time2(&vars,
- "log_slow_time",
- &SV::long_query_time);
-static sys_var_thd_bool sys_low_priority_updates(&vars, "low_priority_updates",
- &SV::low_priority_updates,
- fix_low_priority_updates);
-#ifndef TO_BE_DELETED /* Alias for the low_priority_updates */
-static sys_var_thd_bool sys_sql_low_priority_updates(&vars, "sql_low_priority_updates",
- &SV::low_priority_updates,
- fix_low_priority_updates);
-#endif
-static sys_var_const sys_lower_case_file_system(&vars,
- "lower_case_file_system",
- OPT_GLOBAL, SHOW_MY_BOOL,
- (uchar*)
- &lower_case_file_system);
-static sys_var_const sys_lower_case_table_names(&vars,
- "lower_case_table_names",
- OPT_GLOBAL, SHOW_INT,
- (uchar*)
- &lower_case_table_names);
-static sys_var_thd_ulong_session_readonly sys_max_allowed_packet(&vars, "max_allowed_packet",
- &SV::max_allowed_packet,
- check_max_allowed_packet);
-static sys_var_long_ptr sys_slave_max_allowed_packet(&vars, "slave_max_allowed_packet",
- &slave_max_allowed_packet);
-static sys_var_ulonglong_ptr sys_max_binlog_cache_size(&vars, "max_binlog_cache_size",
- &max_binlog_cache_size);
-static sys_var_long_ptr sys_max_binlog_size(&vars, "max_binlog_size",
- &max_binlog_size,
- fix_max_binlog_size);
-static sys_var_long_ptr sys_max_connections(&vars, "max_connections",
- &max_connections,
- fix_max_connections);
-static sys_var_long_ptr sys_max_connect_errors(&vars, "max_connect_errors",
- &max_connect_errors);
-static sys_var_thd_ulong sys_max_insert_delayed_threads(&vars, "max_insert_delayed_threads",
- &SV::max_insert_delayed_threads,
- check_max_delayed_threads,
- fix_max_connections);
-static sys_var_thd_ulong sys_max_delayed_threads(&vars, "max_delayed_threads",
- &SV::max_insert_delayed_threads,
- check_max_delayed_threads,
- fix_max_connections);
-static sys_var_thd_ulong sys_max_error_count(&vars, "max_error_count",
- &SV::max_error_count);
-static sys_var_thd_ulonglong sys_max_heap_table_size(&vars, "max_heap_table_size",
- &SV::max_heap_table_size);
-static sys_var_thd_ulong sys_pseudo_thread_id(&vars, "pseudo_thread_id",
- &SV::pseudo_thread_id,
- check_pseudo_thread_id, 0,
- sys_var::SESSION_VARIABLE_IN_BINLOG);
-static sys_var_thd_ha_rows sys_max_join_size(&vars, "max_join_size",
- &SV::max_join_size,
- fix_max_join_size);
-static sys_var_thd_ulong sys_max_seeks_for_key(&vars, "max_seeks_for_key",
- &SV::max_seeks_for_key);
-static sys_var_thd_ulong sys_max_length_for_sort_data(&vars, "max_length_for_sort_data",
- &SV::max_length_for_sort_data);
-static sys_var_const sys_max_long_data_size(&vars,
- "max_long_data_size",
- OPT_GLOBAL, SHOW_LONG,
- (uchar*)
- &max_long_data_size);
-
-#ifndef TO_BE_DELETED /* Alias for max_join_size */
-static sys_var_thd_ha_rows sys_sql_max_join_size(&vars, "sql_max_join_size",
- &SV::max_join_size,
- fix_max_join_size);
-#endif
-static sys_var_long_ptr_global
-sys_max_prepared_stmt_count(&vars, "max_prepared_stmt_count",
- &max_prepared_stmt_count,
- &LOCK_prepared_stmt_count);
-static sys_var_long_ptr sys_max_relay_log_size(&vars, "max_relay_log_size",
- &max_relay_log_size,
- fix_max_relay_log_size);
-static sys_var_thd_ulong sys_max_sort_length(&vars, "max_sort_length",
- &SV::max_sort_length);
-static sys_var_thd_ulong sys_max_sp_recursion_depth(&vars, "max_sp_recursion_depth",
- &SV::max_sp_recursion_depth);
-static sys_var_max_user_conn sys_max_user_connections(&vars, "max_user_connections");
-static sys_var_thd_ulong sys_max_tmp_tables(&vars, "max_tmp_tables",
- &SV::max_tmp_tables);
-static sys_var_long_ptr sys_max_write_lock_count(&vars, "max_write_lock_count",
- &max_write_lock_count);
-static sys_var_thd_ulong sys_min_examined_row_limit(&vars, "min_examined_row_limit",
- &SV::min_examined_row_limit);
-static sys_var_long_ptr sys_myisam_data_pointer_size(&vars, "myisam_data_pointer_size",
- &myisam_data_pointer_size);
-static sys_var_thd_ulonglong sys_myisam_max_sort_file_size(&vars, "myisam_max_sort_file_size", &SV::myisam_max_sort_file_size, fix_myisam_max_sort_file_size, 1);
-static sys_var_const sys_myisam_recover_options(&vars, "myisam_recover_options",
- OPT_GLOBAL, SHOW_CHAR_PTR,
- (uchar*)
- &myisam_recover_options_str);
-static sys_var_thd_ulong sys_myisam_repair_threads(&vars, "myisam_repair_threads", &SV::myisam_repair_threads);
-static sys_var_thd_ulong sys_myisam_sort_buffer_size(&vars, "myisam_sort_buffer_size", &SV::myisam_sort_buff_size);
-static sys_var_bool_ptr sys_myisam_use_mmap(&vars, "myisam_use_mmap",
- &opt_myisam_use_mmap);
-
-static sys_var_thd_enum sys_myisam_stats_method(&vars, "myisam_stats_method",
- &SV::myisam_stats_method,
- &myisam_stats_method_typelib,
- NULL);
-
-static sys_var_const sys_myisam_block_size(&vars, "myisam_block_size",
- OPT_GLOBAL, SHOW_LONG,
- (uchar*) &myisam_block_size);
-
-#ifdef __NT__
-/* purecov: begin inspected */
-static sys_var_const sys_named_pipe(&vars, "named_pipe",
- OPT_GLOBAL, SHOW_MY_BOOL,
- (uchar*) &opt_enable_named_pipe);
-/* purecov: end */
-#endif
-static sys_var_thd_ulong_session_readonly sys_net_buffer_length(&vars, "net_buffer_length",
- &SV::net_buffer_length,
- check_net_buffer_length);
-static sys_var_thd_ulong sys_net_read_timeout(&vars, "net_read_timeout",
- &SV::net_read_timeout,
- 0, fix_net_read_timeout);
-static sys_var_thd_ulong sys_net_write_timeout(&vars, "net_write_timeout",
- &SV::net_write_timeout,
- 0, fix_net_write_timeout);
-static sys_var_thd_ulong sys_net_retry_count(&vars, "net_retry_count",
- &SV::net_retry_count,
- 0, fix_net_retry_count);
-static sys_var_thd_bool sys_new_mode(&vars, "new", &SV::new_mode);
-static sys_var_bool_ptr_readonly sys_old_mode(&vars, "old",
- &global_system_variables.old_mode);
-/* these two cannot be static */
-sys_var_thd_bool sys_old_alter_table(&vars, "old_alter_table",
- &SV::old_alter_table);
-sys_var_thd_bool sys_old_passwords(&vars, "old_passwords", &SV::old_passwords);
-static sys_var_const sys_open_files_limit(&vars, "open_files_limit",
- OPT_GLOBAL, SHOW_LONG,
- (uchar*)
- &open_files_limit);
-static sys_var_thd_ulong sys_optimizer_prune_level(&vars, "optimizer_prune_level",
- &SV::optimizer_prune_level);
-static sys_var_thd_ulong sys_optimizer_search_depth(&vars, "optimizer_search_depth",
- &SV::optimizer_search_depth);
-static sys_var_thd_optimizer_switch sys_optimizer_switch(&vars, "optimizer_switch",
- &SV::optimizer_switch);
-
-static sys_var_thd_ulong sys_progress_report_time(&vars,
- "progress_report_time",
- &SV::progress_report_time);
-
-static sys_var_const sys_pid_file(&vars, "pid_file",
- OPT_GLOBAL, SHOW_CHAR,
- (uchar*) pidfile_name);
-static sys_var_const_os sys_plugin_dir(&vars, "plugin_dir",
- OPT_GLOBAL, SHOW_CHAR,
- (uchar*) opt_plugin_dir);
-static sys_var_const sys_port(&vars, "port",
- OPT_GLOBAL, SHOW_INT,
- (uchar*) &mysqld_port);
-static sys_var_thd_ulong sys_preload_buff_size(&vars, "preload_buffer_size",
- &SV::preload_buff_size);
-static sys_var_const sys_protocol_version(&vars, "protocol_version",
- OPT_GLOBAL, SHOW_INT,
- (uchar*)
- &protocol_version);
-static sys_var_thd_ulong sys_read_buff_size(&vars, "read_buffer_size",
- &SV::read_buff_size);
-static sys_var_opt_readonly sys_readonly(&vars, "read_only", &opt_readonly);
-static sys_var_bool_ptr sys_userstat(&vars, "userstat",
- &opt_userstat_running);
-
-static sys_var_thd_ulong sys_read_rnd_buff_size(&vars, "read_rnd_buffer_size",
- &SV::read_rnd_buff_size);
-static sys_var_thd_ulong sys_mrr_buff_size(&vars, "mrr_buffer_size",
- &SV::mrr_buff_size);
-static sys_var_thd_ulong sys_div_precincrement(&vars, "div_precision_increment",
- &SV::div_precincrement);
-static sys_var_long_ptr sys_rpl_recovery_rank(&vars, "rpl_recovery_rank",
- &rpl_recovery_rank);
-
-static sys_var_thd_ulong sys_range_alloc_block_size(&vars, "range_alloc_block_size",
- &SV::range_alloc_block_size);
-static sys_var_thd_ulong sys_rowid_merge_buff_size(&vars, "rowid_merge_buff_size",
- &SV::rowid_merge_buff_size);
-
-static sys_var_thd_ulong sys_query_alloc_block_size(&vars, "query_alloc_block_size",
- &SV::query_alloc_block_size,
- 0, fix_thd_mem_root);
-static sys_var_thd_ulong sys_query_prealloc_size(&vars, "query_prealloc_size",
- &SV::query_prealloc_size,
- 0, fix_thd_mem_root);
-#ifdef HAVE_SMEM
-/* purecov: begin tested */
-static sys_var_const sys_shared_memory(&vars, "shared_memory",
- OPT_GLOBAL, SHOW_MY_BOOL,
- (uchar*)
- &opt_enable_shared_memory);
-static sys_var_const sys_shared_memory_base_name(&vars,
- "shared_memory_base_name",
- OPT_GLOBAL, SHOW_CHAR_PTR,
- (uchar*)
- &shared_memory_base_name);
-/* purecov: end */
-#endif
-static sys_var_const sys_skip_external_locking(&vars,
- "skip_external_locking",
- OPT_GLOBAL, SHOW_MY_BOOL,
- (uchar*)
- &my_disable_locking);
-static sys_var_const sys_skip_networking(&vars, "skip_networking",
- OPT_GLOBAL, SHOW_BOOL,
- (uchar*) &opt_disable_networking);
-static sys_var_const sys_skip_show_database(&vars, "skip_show_database",
- OPT_GLOBAL, SHOW_BOOL,
- (uchar*) &opt_skip_show_db);
-
-static sys_var_const sys_skip_name_resolve(&vars, "skip_name_resolve",
- OPT_GLOBAL, SHOW_BOOL,
- (uchar*) &opt_skip_name_resolve);
-
-static sys_var_const sys_socket(&vars, "socket",
- OPT_GLOBAL, SHOW_CHAR_PTR,
- (uchar*) &mysqld_unix_port);
-
-#ifdef HAVE_THR_SETCONCURRENCY
-/* purecov: begin tested */
-static sys_var_const sys_thread_concurrency(&vars, "thread_concurrency",
- OPT_GLOBAL, SHOW_LONG,
- (uchar*) &concurrency);
-/* purecov: end */
-#endif
-static sys_var_const sys_thread_stack(&vars, "thread_stack",
- OPT_GLOBAL, SHOW_LONG,
- (uchar*) &my_thread_stack_size);
-static sys_var_readonly_os sys_tmpdir(&vars, "tmpdir", OPT_GLOBAL, SHOW_CHAR, get_tmpdir);
-static sys_var_thd_ulong sys_trans_alloc_block_size(&vars, "transaction_alloc_block_size",
- &SV::trans_alloc_block_size,
- 0, fix_trans_mem_root);
-static sys_var_thd_ulong sys_trans_prealloc_size(&vars, "transaction_prealloc_size",
- &SV::trans_prealloc_size,
- 0, fix_trans_mem_root);
-sys_var_enum_const sys_thread_handling(&vars, "thread_handling",
- &thread_handling,
- &thread_handling_typelib);
-
-#ifdef HAVE_QUERY_CACHE
-static sys_var_long_ptr sys_query_cache_size(&vars, "query_cache_size",
- &query_cache_size,
- fix_query_cache_size);
-static sys_var_long_ptr sys_query_cache_limit(&vars, "query_cache_limit",
- &query_cache.query_cache_limit);
-static sys_var_long_ptr
- sys_query_cache_min_res_unit(&vars, "query_cache_min_res_unit",
- &query_cache_min_res_unit,
- fix_query_cache_min_res_unit);
-static sys_var_thd_enum sys_query_cache_type(&vars, "query_cache_type",
- &SV::query_cache_type,
- &query_cache_type_typelib,
- fix_query_cache_type,
- check_query_cache_type);
-static sys_var_thd_bool
-sys_query_cache_wlock_invalidate(&vars, "query_cache_wlock_invalidate",
- &SV::query_cache_wlock_invalidate);
-static sys_var_bool_ptr sys_query_cache_strip_comments(&vars, "query_cache_strip_comments",
- &opt_query_cache_strip_comments);
-#endif /* HAVE_QUERY_CACHE */
-static sys_var_bool_ptr sys_secure_auth(&vars, "secure_auth", &opt_secure_auth);
-static sys_var_const_str_ptr sys_secure_file_priv(&vars, "secure_file_priv",
- &opt_secure_file_priv);
-static sys_var_long_ptr sys_server_id(&vars, "server_id", &server_id, fix_server_id);
-static sys_var_bool_ptr sys_slave_compressed_protocol(&vars, "slave_compressed_protocol",
- &opt_slave_compressed_protocol);
-static sys_var_set_slave_mode slave_exec_mode(&vars,
- "slave_exec_mode",
- &slave_exec_mode_options,
- &slave_exec_mode_typelib,
- 0);
-static sys_var_long_ptr sys_slow_launch_time(&vars, "slow_launch_time",
- &slow_launch_time);
-static sys_var_thd_ulong sys_sort_buffer(&vars, "sort_buffer_size",
- &SV::sortbuff_size);
-/*
- sql_mode should *not* have binlog_mode=SESSION_VARIABLE_IN_BINLOG:
- even though it is written to the binlog, the slave ignores the
- MODE_NO_DIR_IN_CREATE variable, so slave's value differs from
- master's (see log_event.cc: Query_log_event::do_apply_event()).
-*/
-static sys_var_thd_sql_mode sys_sql_mode(&vars, "sql_mode",
- &SV::sql_mode);
-#ifdef HAVE_OPENSSL
-extern char *opt_ssl_ca, *opt_ssl_capath, *opt_ssl_cert, *opt_ssl_cipher,
- *opt_ssl_key;
-static sys_var_const_os_str_ptr sys_ssl_ca(&vars, "ssl_ca", &opt_ssl_ca);
-static sys_var_const_os_str_ptr sys_ssl_capath(&vars, "ssl_capath", &opt_ssl_capath);
-static sys_var_const_os_str_ptr sys_ssl_cert(&vars, "ssl_cert", &opt_ssl_cert);
-static sys_var_const_os_str_ptr sys_ssl_cipher(&vars, "ssl_cipher", &opt_ssl_cipher);
-static sys_var_const_os_str_ptr sys_ssl_key(&vars, "ssl_key", &opt_ssl_key);
-#else
-static sys_var_const_os_str sys_ssl_ca(&vars, "ssl_ca", NULL);
-static sys_var_const_os_str sys_ssl_capath(&vars, "ssl_capath", NULL);
-static sys_var_const_os_str sys_ssl_cert(&vars, "ssl_cert", NULL);
-static sys_var_const_os_str sys_ssl_cipher(&vars, "ssl_cipher", NULL);
-static sys_var_const_os_str sys_ssl_key(&vars, "ssl_key", NULL);
-#endif
-static sys_var_thd_enum
-sys_updatable_views_with_limit(&vars, "updatable_views_with_limit",
- &SV::updatable_views_with_limit,
- &updatable_views_with_limit_typelib);
-
-static sys_var_thd_table_type sys_table_type(&vars, "table_type",
- &SV::table_plugin);
-static sys_var_thd_storage_engine sys_storage_engine(&vars, "storage_engine",
- &SV::table_plugin);
-static sys_var_bool_ptr sys_sync_frm(&vars, "sync_frm", &opt_sync_frm);
-static sys_var_const_str sys_system_time_zone(&vars, "system_time_zone",
- system_time_zone);
-static sys_var_long_ptr sys_table_def_size(&vars, "table_definition_cache",
- &table_def_size);
-static sys_var_long_ptr sys_table_cache_size(&vars, "table_open_cache",
- &table_cache_size);
-static sys_var_long_ptr sys_table_lock_wait_timeout(&vars, "table_lock_wait_timeout",
- &table_lock_wait_timeout);
-
-#if defined(ENABLED_DEBUG_SYNC)
-/* Debug Sync Facility. Implemented in debug_sync.cc. */
-static sys_var_debug_sync sys_debug_sync(&vars, "debug_sync");
-#endif /* defined(ENABLED_DEBUG_SYNC) */
-
-static sys_var_long_ptr sys_thread_cache_size(&vars, "thread_cache_size",
- &thread_cache_size);
-#if HAVE_POOL_OF_THREADS == 1
-static sys_var_long_ptr sys_thread_pool_size(&vars, "thread_pool_size",
- &thread_pool_size);
-#endif
-static sys_var_thd_enum sys_tx_isolation(&vars, "tx_isolation",
- &SV::tx_isolation,
- &tx_isolation_typelib,
- fix_tx_isolation,
- check_tx_isolation);
-static sys_var_thd_ulonglong sys_tmp_table_size(&vars, "tmp_table_size",
- &SV::tmp_table_size);
-static sys_var_bool_ptr sys_timed_mutexes(&vars, "timed_mutexes",
- &timed_mutexes);
-static sys_var_const_str sys_version(&vars, "version", server_version);
-static sys_var_const_str sys_version_comment(&vars, "version_comment",
- MYSQL_COMPILATION_COMMENT);
-static sys_var_const_str sys_version_compile_machine(&vars, "version_compile_machine",
- MACHINE_TYPE);
-static sys_var_const_str sys_version_compile_os(&vars, "version_compile_os",
- SYSTEM_TYPE);
-static sys_var_thd_ulong sys_net_wait_timeout(&vars, "wait_timeout",
- &SV::net_wait_timeout);
-
-/* Condition pushdown to storage engine */
-static sys_var_thd_bool
-sys_engine_condition_pushdown(&vars, "engine_condition_pushdown",
- &SV::engine_condition_pushdown);
-
-#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
-/* ndb thread specific variable settings */
-static sys_var_thd_ulong
-sys_ndb_autoincrement_prefetch_sz(&vars, "ndb_autoincrement_prefetch_sz",
- &SV::ndb_autoincrement_prefetch_sz);
-static sys_var_thd_bool
-sys_ndb_force_send(&vars, "ndb_force_send", &SV::ndb_force_send);
-#ifdef HAVE_NDB_BINLOG
-static sys_var_long_ptr
-sys_ndb_report_thresh_binlog_epoch_slip(&vars, "ndb_report_thresh_binlog_epoch_slip",
- &ndb_report_thresh_binlog_epoch_slip);
-static sys_var_long_ptr
-sys_ndb_report_thresh_binlog_mem_usage(&vars, "ndb_report_thresh_binlog_mem_usage",
- &ndb_report_thresh_binlog_mem_usage);
-#endif
-static sys_var_thd_bool
-sys_ndb_use_exact_count(&vars, "ndb_use_exact_count", &SV::ndb_use_exact_count);
-static sys_var_thd_bool
-sys_ndb_use_transactions(&vars, "ndb_use_transactions", &SV::ndb_use_transactions);
-static sys_var_long_ptr
-sys_ndb_cache_check_time(&vars, "ndb_cache_check_time", &ndb_cache_check_time);
-static sys_var_const_str
-sys_ndb_connectstring(&vars, "ndb_connectstring", opt_ndb_constrbuf);
-static sys_var_thd_bool
-sys_ndb_index_stat_enable(&vars, "ndb_index_stat_enable",
- &SV::ndb_index_stat_enable);
-static sys_var_thd_ulong
-sys_ndb_index_stat_cache_entries(&vars, "ndb_index_stat_cache_entries",
- &SV::ndb_index_stat_cache_entries);
-static sys_var_thd_ulong
-sys_ndb_index_stat_update_freq(&vars, "ndb_index_stat_update_freq",
- &SV::ndb_index_stat_update_freq);
-static sys_var_long_ptr
-sys_ndb_extra_logging(&vars, "ndb_extra_logging", &ndb_extra_logging);
-static sys_var_thd_bool
-sys_ndb_use_copying_alter_table(&vars, "ndb_use_copying_alter_table", &SV::ndb_use_copying_alter_table);
-#endif //WITH_NDBCLUSTER_STORAGE_ENGINE
-
-/* Time/date/datetime formats */
-
-static sys_var_thd_date_time_format sys_time_format(&vars, "time_format",
- &SV::time_format,
- MYSQL_TIMESTAMP_TIME);
-static sys_var_thd_date_time_format sys_date_format(&vars, "date_format",
- &SV::date_format,
- MYSQL_TIMESTAMP_DATE);
-static sys_var_thd_date_time_format sys_datetime_format(&vars, "datetime_format",
- &SV::datetime_format,
- MYSQL_TIMESTAMP_DATETIME);
-
-/* Variables that are bits in THD */
-
-sys_var_thd_bit sys_autocommit(&vars, "autocommit", 0,
- set_option_autocommit,
- OPTION_NOT_AUTOCOMMIT,
- 1);
-static sys_var_thd_bit sys_big_tables(&vars, "big_tables", 0,
- set_option_bit,
- OPTION_BIG_TABLES);
-#ifndef TO_BE_DELETED /* Alias for big_tables */
-static sys_var_thd_bit sys_sql_big_tables(&vars, "sql_big_tables", 0,
- set_option_bit,
- OPTION_BIG_TABLES);
-#endif
-static sys_var_thd_bit sys_big_selects(&vars, "sql_big_selects", 0,
- set_option_bit,
- OPTION_BIG_SELECTS);
-static sys_var_thd_bit sys_log_off(&vars, "sql_log_off",
- check_log_update,
- set_option_bit,
- OPTION_LOG_OFF);
-static sys_var_thd_bit sys_log_update(&vars, "sql_log_update",
- check_log_update,
- set_log_update,
- OPTION_BIN_LOG);
-static sys_var_thd_bit sys_log_binlog(&vars, "sql_log_bin",
- check_log_update,
- set_option_log_bin_bit,
- OPTION_BIN_LOG);
-static sys_var_thd_bit sys_sql_warnings(&vars, "sql_warnings", 0,
- set_option_bit,
- OPTION_WARNINGS);
-static sys_var_thd_bit sys_sql_notes(&vars, "sql_notes", 0,
- set_option_bit,
- OPTION_SQL_NOTES);
-static sys_var_thd_bit sys_auto_is_null(&vars, "sql_auto_is_null", 0,
- set_option_bit,
- OPTION_AUTO_IS_NULL, 0,
- sys_var::SESSION_VARIABLE_IN_BINLOG);
-static sys_var_thd_bit sys_safe_updates(&vars, "sql_safe_updates", 0,
- set_option_bit,
- OPTION_SAFE_UPDATES);
-static sys_var_thd_bit sys_buffer_results(&vars, "sql_buffer_result", 0,
- set_option_bit,
- OPTION_BUFFER_RESULT);
-static sys_var_thd_bit sys_quote_show_create(&vars, "sql_quote_show_create", 0,
- set_option_bit,
- OPTION_QUOTE_SHOW_CREATE);
-static sys_var_thd_bit sys_foreign_key_checks(&vars, "foreign_key_checks", 0,
- set_option_bit,
- OPTION_NO_FOREIGN_KEY_CHECKS,
- 1, sys_var::SESSION_VARIABLE_IN_BINLOG);
-static sys_var_thd_bit sys_unique_checks(&vars, "unique_checks", 0,
- set_option_bit,
- OPTION_RELAXED_UNIQUE_CHECKS,
- 1,
- sys_var::SESSION_VARIABLE_IN_BINLOG);
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
-static sys_var_thd_bit sys_profiling(&vars, "profiling", NULL,
- set_option_bit,
- ulonglong(OPTION_PROFILING));
-static sys_var_thd_ulong sys_profiling_history_size(&vars, "profiling_history_size",
- &SV::profiling_history_size);
-#endif
-
-/* Local state variables */
-
-static sys_var_thd_ha_rows sys_select_limit(&vars, "sql_select_limit",
- &SV::select_limit);
-static sys_var_timestamp sys_timestamp(&vars, "timestamp",
- sys_var::SESSION_VARIABLE_IN_BINLOG);
-static sys_var_last_insert_id
-sys_last_insert_id(&vars, "last_insert_id",
- sys_var::SESSION_VARIABLE_IN_BINLOG);
-/*
- identity is an alias for last_insert_id(), so that we are compatible
- with Sybase
-*/
-static sys_var_last_insert_id
-sys_identity(&vars, "identity", sys_var::SESSION_VARIABLE_IN_BINLOG);
-
-static sys_var_thd_lc_time_names
-sys_lc_time_names(&vars, "lc_time_names", sys_var::SESSION_VARIABLE_IN_BINLOG);
-
-/*
- insert_id should *not* be marked as written to the binlog (i.e., it
- should *not* have binlog_status==SESSION_VARIABLE_IN_BINLOG),
- because we want any statement that refers to insert_id explicitly to
- be unsafe. (By "explicitly", we mean using @@session.insert_id,
- whereas insert_id is used "implicitly" when NULL value is inserted
- into an auto_increment column).
-
- We want statements referring explicitly to @@session.insert_id to be
- unsafe, because insert_id is modified internally by the slave sql
- thread when NULL values are inserted in an AUTO_INCREMENT column.
- This modification interfers with the value of the
- @@session.insert_id variable if @@session.insert_id is referred
- explicitly by an insert statement (as is seen by executing "SET
- @@session.insert_id=0; CREATE TABLE t (a INT, b INT KEY
- AUTO_INCREMENT); INSERT INTO t(a) VALUES (@@session.insert_id);" in
- statement-based logging mode: t will be different on master and
- slave).
+/**
+ Return variable name and length for hashing of variables.
*/
-static sys_var_insert_id sys_insert_id(&vars, "insert_id");
-static sys_var_readonly sys_error_count(&vars, "error_count",
- OPT_SESSION,
- SHOW_LONG,
- get_error_count);
-static sys_var_readonly sys_warning_count(&vars, "warning_count",
- OPT_SESSION,
- SHOW_LONG,
- get_warning_count);
-
-static sys_var_rand_seed1 sys_rand_seed1(&vars, "rand_seed1",
- sys_var::SESSION_VARIABLE_IN_BINLOG);
-static sys_var_rand_seed2 sys_rand_seed2(&vars, "rand_seed2",
- sys_var::SESSION_VARIABLE_IN_BINLOG);
-
-static sys_var_thd_ulong sys_default_week_format(&vars, "default_week_format",
- &SV::default_week_format);
-
-sys_var_thd_ulong sys_group_concat_max_len(&vars, "group_concat_max_len",
- &SV::group_concat_max_len);
-
-sys_var_thd_time_zone sys_time_zone(&vars, "time_zone",
- sys_var::SESSION_VARIABLE_IN_BINLOG);
-
-/* Unique variables for MariaDB */
-static sys_var_thd_ulong sys_log_slow_rate_limit(&vars,
- "log_slow_rate_limit",
- &SV::log_slow_rate_limit);
-static sys_var_thd_set sys_log_slow_filter(&vars, "log_slow_filter",
- &SV::log_slow_filter,
- &log_slow_filter_typelib,
- QPLAN_VISIBLE_MASK,
- fix_sys_log_slow_filter);
-static sys_var_thd_set sys_log_slow_verbosity(&vars,
- "log_slow_verbosity",
- &SV::log_slow_verbosity,
- &log_slow_verbosity_typelib);
-/* Global read-only variable containing hostname */
-static sys_var_const_str sys_hostname(&vars, "hostname", glob_hostname);
-static sys_var_const_str_ptr sys_log_basename(&vars, "log_basename",
- &opt_log_basename);
-
-#ifndef EMBEDDED_LIBRARY
-static sys_var_const_str_ptr sys_repl_report_host(&vars, "report_host", &report_host);
-static sys_var_const_str_ptr sys_repl_report_user(&vars, "report_user", &report_user);
-static sys_var_const_str_ptr sys_repl_report_password(&vars, "report_password", &report_password);
-
-static uchar *slave_get_report_port(THD *thd)
+static uchar *get_sys_var_length(const sys_var *var, size_t *length,
+ my_bool first)
{
- thd->sys_var_tmp.long_value= report_port;
- return (uchar*) &thd->sys_var_tmp.long_value;
+ *length= var->name.length;
+ return (uchar*) var->name.str;
}
-static sys_var_readonly sys_repl_report_port(&vars, "report_port", OPT_GLOBAL, SHOW_LONG, slave_get_report_port);
-
-#endif
-
-sys_var_thd_bool sys_keep_files_on_create(&vars, "keep_files_on_create",
- &SV::keep_files_on_create);
-/* Read only variables */
+sys_var_chain all_sys_vars = { NULL, NULL };
-static sys_var_have_variable sys_have_compress(&vars, "have_compress", &have_compress);
-static sys_var_have_variable sys_have_crypt(&vars, "have_crypt", &have_crypt);
-static sys_var_have_plugin sys_have_csv(&vars, "have_csv", C_STRING_WITH_LEN("csv"), MYSQL_STORAGE_ENGINE_PLUGIN);
-static sys_var_have_variable sys_have_dlopen(&vars, "have_dynamic_loading", &have_dlopen);
-static sys_var_have_variable sys_have_geometry(&vars, "have_geometry", &have_geometry);
-static sys_var_have_plugin sys_have_innodb(&vars, "have_innodb", C_STRING_WITH_LEN("innodb"), MYSQL_STORAGE_ENGINE_PLUGIN);
-static sys_var_have_plugin sys_have_ndbcluster(&vars, "have_ndbcluster", C_STRING_WITH_LEN("ndbcluster"), MYSQL_STORAGE_ENGINE_PLUGIN);
-static sys_var_have_variable sys_have_openssl(&vars, "have_openssl", &have_ssl);
-static sys_var_have_variable sys_have_ssl(&vars, "have_ssl", &have_ssl);
-static sys_var_have_plugin sys_have_partition_db(&vars, "have_partitioning", C_STRING_WITH_LEN("partition"), MYSQL_STORAGE_ENGINE_PLUGIN);
-static sys_var_have_variable sys_have_query_cache(&vars, "have_query_cache",
- &have_query_cache);
-static sys_var_have_variable sys_have_community_features(&vars, "have_community_features", &have_community_features);
-static sys_var_have_variable sys_have_rtree_keys(&vars, "have_rtree_keys", &have_rtree_keys);
-static sys_var_have_variable sys_have_symlink(&vars, "have_symlink", &have_symlink);
-/* Global read-only variable describing server license */
-static sys_var_const_str sys_license(&vars, "license", STRINGIFY_ARG(LICENSE));
-/* Global variables which enable|disable logging */
-static sys_var_log_state sys_var_general_log(&vars, "general_log", &opt_log,
- QUERY_LOG_GENERAL);
-/* Synonym of "general_log" for consistency with SHOW VARIABLES output */
-static sys_var_log_state sys_var_log(&vars, "log", &opt_log,
- QUERY_LOG_GENERAL);
-static sys_var_log_state sys_var_slow_query_log(&vars, "slow_query_log", &opt_slow_log,
- QUERY_LOG_SLOW);
-/* Synonym of "slow_query_log" for consistency with SHOW VARIABLES output */
-static sys_var_log_state sys_var_log_slow(&vars, "log_slow_queries",
- &opt_slow_log, QUERY_LOG_SLOW);
-sys_var_str sys_var_general_log_path(&vars, "general_log_file", sys_check_log_path,
- sys_update_general_log_path,
- sys_default_general_log_path,
- opt_logname);
-sys_var_str sys_var_slow_log_path(&vars, "slow_query_log_file", sys_check_log_path,
- sys_update_slow_log_path,
- sys_default_slow_log_path,
- opt_slow_logname);
-static sys_var_log_output sys_var_log_output_state(&vars, "log_output", &log_output_options,
- &log_output_typelib, 0);
-static sys_var_readonly sys_myisam_mmap_size(&vars, "myisam_mmap_size",
- OPT_GLOBAL,
- SHOW_LONGLONG,
- get_myisam_mmap_size);
-
-static sys_var_enum_const sys_plugin_maturity(&vars, "plugin_maturity",
- &plugin_maturity,
- &plugin_maturity_values);
-
-static sys_var_readonly sys_in_transaction(&vars, "in_transaction",
- OPT_SESSION, SHOW_BOOL,
- in_transaction);
-
-static sys_var_const_str_ptr sys_ignore_db_dirs(&vars, "ignore_db_dirs",
- &opt_ignore_db_dirs);
-
-
-bool sys_var::check(THD *thd, set_var *var)
+int sys_var_init()
{
- var->save_result.ulonglong_value= var->value->val_int();
- return 0;
-}
-
-bool sys_var_str::check(THD *thd, set_var *var)
-{
- int res;
- if (!check_func)
- return 0;
-
- if ((res=(*check_func)(thd, var)) < 0)
- my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0),
- name, var->value->str_value.c_ptr());
- return res;
-}
-
-/*
- Functions to check and update variables
-*/
-
+ DBUG_ENTER("sys_var_init");
-/*
- Update variables 'init_connect, init_slave'.
-
- In case of 'DEFAULT' value
- (for example: 'set GLOBAL init_connect=DEFAULT')
- 'var' parameter is NULL pointer.
-*/
-
-bool update_sys_var_str(sys_var_str *var_str, rw_lock_t *var_mutex,
- set_var *var)
-{
- char *res= 0, *old_value=(char *)(var ? var->value->str_value.ptr() : 0);
- uint new_length= (var ? var->value->str_value.length() : 0);
- if (!old_value)
- old_value= (char*) "";
- if (!(res= my_strndup(old_value, new_length, MYF(0))))
- return 1;
- /*
- Replace the old value in such a way that the any thread using
- the value will work.
- */
- rw_wrlock(var_mutex);
- old_value= var_str->value;
- var_str->value= res;
- var_str->value_length= new_length;
- var_str->is_os_charset= FALSE;
- rw_unlock(var_mutex);
- my_free(old_value, MYF(MY_ALLOW_ZERO_PTR));
- return 0;
-}
+ /* Must be already initialized. */
+ DBUG_ASSERT(system_charset_info != NULL);
+ if (my_hash_init(&system_variable_hash, system_charset_info, 100, 0,
+ 0, (my_hash_get_key) get_sys_var_length, 0, HASH_UNIQUE))
+ goto error;
-static bool sys_update_init_connect(THD *thd, set_var *var)
-{
- return update_sys_var_str(&sys_init_connect, &LOCK_sys_init_connect, var);
-}
+ if (mysql_add_sys_var_chain(all_sys_vars.first))
+ goto error;
+ DBUG_RETURN(0);
-static void sys_default_init_connect(THD* thd, enum_var_type type)
-{
- update_sys_var_str(&sys_init_connect, &LOCK_sys_init_connect, 0);
+error:
+ fprintf(stderr, "failed to initialize System variables");
+ DBUG_RETURN(1);
}
-
-static bool sys_update_init_slave(THD *thd, set_var *var)
+int sys_var_add_options(DYNAMIC_ARRAY *long_options, int parse_flags)
{
- return update_sys_var_str(&sys_init_slave, &LOCK_sys_init_slave, var);
-}
-
+ uint saved_elements= long_options->elements;
-static void sys_default_init_slave(THD* thd, enum_var_type type)
-{
- update_sys_var_str(&sys_init_slave, &LOCK_sys_init_slave, 0);
-}
+ DBUG_ENTER("sys_var_add_options");
-static int sys_check_ftb_syntax(THD *thd, set_var *var)
-{
- if (thd->security_ctx->master_access & SUPER_ACL)
- return (ft_boolean_check_syntax_string((uchar*)
- var->value->str_value.c_ptr()) ?
- -1 : 0);
- else
+ for (sys_var *var=all_sys_vars.first; var; var= var->next)
{
- my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
- return 1;
+ if (var->register_option(long_options, parse_flags))
+ goto error;
}
-}
-
-static bool sys_update_ftb_syntax(THD *thd, set_var * var)
-{
- strmake(ft_boolean_syntax, var->value->str_value.c_ptr(),
- sizeof(ft_boolean_syntax)-1);
-
-#ifdef HAVE_QUERY_CACHE
- query_cache.flush();
-#endif /* HAVE_QUERY_CACHE */
- return 0;
-}
+ DBUG_RETURN(0);
-static void sys_default_ftb_syntax(THD *thd, enum_var_type type)
-{
- strmake(ft_boolean_syntax, def_ft_boolean_syntax,
- sizeof(ft_boolean_syntax)-1);
+error:
+ fprintf(stderr, "failed to initialize System variables");
+ long_options->elements= saved_elements;
+ DBUG_RETURN(1);
}
-
-/**
- If one sets the LOW_PRIORIY UPDATES flag, we also must change the
- used lock type.
-*/
-
-static void fix_low_priority_updates(THD *thd, enum_var_type type)
+void sys_var_end()
{
- if (type == OPT_GLOBAL)
- thr_upgraded_concurrent_insert_lock=
- (global_system_variables.low_priority_updates ?
- TL_WRITE_LOW_PRIORITY : TL_WRITE);
- else
- thd->update_lock_default= (thd->variables.low_priority_updates ?
- TL_WRITE_LOW_PRIORITY : TL_WRITE);
-}
+ DBUG_ENTER("sys_var_end");
+ my_hash_free(&system_variable_hash);
-static void
-fix_myisam_max_sort_file_size(THD *thd, enum_var_type type)
-{
- myisam_max_temp_length=
- (my_off_t) global_system_variables.myisam_max_sort_file_size;
-}
-
-/**
- Set the OPTION_BIG_SELECTS flag if max_join_size == HA_POS_ERROR.
-*/
+ for (sys_var *var=all_sys_vars.first; var; var= var->next)
+ var->cleanup();
-static void fix_max_join_size(THD *thd, enum_var_type type)
-{
- if (type != OPT_GLOBAL)
- {
- if (thd->variables.max_join_size == HA_POS_ERROR)
- thd->options|= OPTION_BIG_SELECTS;
- else
- thd->options&= ~OPTION_BIG_SELECTS;
- }
+ DBUG_VOID_RETURN;
}
-
/**
- Can't change the 'next' tx_isolation while we are already in
- a transaction
-*/
-static int check_tx_isolation(THD *thd, set_var *var)
-{
- if (var->type == OPT_DEFAULT && (thd->server_status & SERVER_STATUS_IN_TRANS))
- {
- my_error(ER_CANT_CHANGE_TX_ISOLATION, MYF(0));
- return 1;
- }
- return 0;
-}
-
-/*
- If one doesn't use the SESSION modifier, the isolation level
- is only active for the next command.
-*/
-static void fix_tx_isolation(THD *thd, enum_var_type type)
-{
- if (type == OPT_SESSION)
- thd->session_tx_isolation= ((enum_tx_isolation)
- thd->variables.tx_isolation);
-}
-
-static void fix_completion_type(THD *thd __attribute__((unused)),
- enum_var_type type __attribute__((unused))) {}
-
-static int check_completion_type(THD *thd, set_var *var)
-{
- longlong val= var->value->val_int();
- if (val < 0 || val > 2)
- {
- char buf[64];
- my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name, llstr(val, buf));
- return 1;
- }
- return 0;
-}
-
-
-/*
- If we are changing the thread variable, we have to copy it to NET too
-*/
-
-#ifdef HAVE_REPLICATION
-static void fix_net_read_timeout(THD *thd, enum_var_type type)
+ sys_var constructor
+
+ @param chain variables are linked into chain for mysql_add_sys_var_chain()
+ @param name_arg the name of the variable. Must be 0-terminated and exist
+ for the liftime of the sys_var object. @sa my_option::name
+ @param comment shown in mysqld --help, @sa my_option::comment
+ @param flags_arg or'ed flag_enum values
+ @param off offset of the global variable value from the
+ &global_system_variables.
+ @param getopt_id -1 for no command-line option, otherwise @sa my_option::id
+ @param getopt_arg_type @sa my_option::arg_type
+ @param show_val_type_arg what value_ptr() returns for sql_show.cc
+ @param def_val default value, @sa my_option::def_value
+ @param lock mutex or rw_lock that protects the global variable
+ *in addition* to LOCK_global_system_variables.
+ @param binlog_status_enum @sa binlog_status_enum
+ @param on_check_func a function to be called at the end of sys_var::check,
+ put your additional checks here
+ @param on_update_func a function to be called at the end of sys_var::update,
+ any post-update activity should happen here
+ @param substitute If non-NULL, this variable is deprecated and the
+ string describes what one should use instead. If an empty string,
+ the variable is deprecated but no replacement is offered.
+*/
+sys_var::sys_var(sys_var_chain *chain, const char *name_arg,
+ const char *comment, int flags_arg, ptrdiff_t off,
+ int getopt_id, enum get_opt_arg_type getopt_arg_type,
+ SHOW_TYPE show_val_type_arg, longlong def_val,
+ PolyLock *lock, enum binlog_status_enum binlog_status_arg,
+ on_check_function on_check_func,
+ on_update_function on_update_func,
+ const char *substitute) :
+ next(0),
+ binlog_status(binlog_status_arg),
+ flags(flags_arg), show_val_type(show_val_type_arg),
+ guard(lock), offset(off), on_check(on_check_func), on_update(on_update_func),
+ deprecation_substitute(substitute),
+ is_os_charset(FALSE)
{
- if (type != OPT_GLOBAL)
- my_net_set_read_timeout(&thd->net, thd->variables.net_read_timeout);
-}
-
-
-static void fix_net_write_timeout(THD *thd, enum_var_type type)
-{
- if (type != OPT_GLOBAL)
- my_net_set_write_timeout(&thd->net, thd->variables.net_write_timeout);
-}
-
-static void fix_net_retry_count(THD *thd, enum_var_type type)
-{
- if (type != OPT_GLOBAL)
- thd->net.retry_count=thd->variables.net_retry_count;
-}
-#else /* HAVE_REPLICATION */
-static void fix_net_read_timeout(THD *thd __attribute__((unused)),
- enum_var_type type __attribute__((unused)))
-{}
-static void fix_net_write_timeout(THD *thd __attribute__((unused)),
- enum_var_type type __attribute__((unused)))
-{}
-static void fix_net_retry_count(THD *thd __attribute__((unused)),
- enum_var_type type __attribute__((unused)))
-{}
-#endif /* HAVE_REPLICATION */
-
-#ifdef HAVE_QUERY_CACHE
-static void fix_query_cache_size(THD *thd, enum_var_type type)
-{
- ulong new_cache_size= query_cache.resize(query_cache_size);
-
/*
- Note: query_cache_size is a global variable reflecting the
- requested cache size. See also query_cache_size_arg
+ There is a limitation in handle_options() related to short options:
+ - either all short options should be declared when parsing in multiple stages,
+ - or none should be declared.
+ Because a lot of short options are used in the normal parsing phase
+ for mysqld, we enforce here that no short option is present
+ in the first (PARSE_EARLY) stage.
+ See handle_options() for details.
*/
+ DBUG_ASSERT(!(flags & PARSE_EARLY) || getopt_id <= 0 || getopt_id >= 255);
- if (query_cache_size != new_cache_size)
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_WARN_QC_RESIZE, ER(ER_WARN_QC_RESIZE),
- query_cache_size, new_cache_size);
-
- query_cache_size= new_cache_size;
-}
-
-
-/**
- Trigger before query_cache_type variable is updated.
- @param thd Thread handler
- @param var Pointer to the new variable status
+ name.str= name_arg; // ER_NO_DEFAULT relies on 0-termination of name_arg
+ name.length= strlen(name_arg); // and so does this.
+ DBUG_ASSERT(name.length <= NAME_CHAR_LEN);
- @return Status code
- @retval TRUE Failure
- @retval FALSE Success
-*/
+ bzero(&option, sizeof(option));
+ option.name= name_arg;
+ option.id= getopt_id;
+ option.comment= comment;
+ option.arg_type= getopt_arg_type;
+ option.value= (uchar **)global_var_ptr();
+ option.def_value= def_val;
-static int check_query_cache_type(THD *thd, set_var *var)
-{
- /*
- Don't allow changes of the query_cache_type if the query cache
- is disabled.
- */
- if (query_cache.is_disable_in_progress())
- {
- my_error(ER_QUERY_CACHE_IS_DISABLED, MYF(0));
- return TRUE;
- }
- if (var->type != OPT_GLOBAL &&
- global_system_variables.query_cache_type == 0 &&
- var->value->val_int() != 0)
- {
- my_error(ER_QUERY_CACHE_IS_GLOBALY_DISABLED, MYF(0));
- return TRUE;
- }
-
- return FALSE;
+ if (chain->last)
+ chain->last->next= this;
+ else
+ chain->first= this;
+ chain->last= this;
}
-
-static void fix_query_cache_type(THD *thd, enum_var_type type)
+bool sys_var::update(THD *thd, set_var *var)
{
- if (type == OPT_GLOBAL)
+ enum_var_type type= var->type;
+ if (type == OPT_GLOBAL || scope() == GLOBAL)
{
- if (global_system_variables.query_cache_type != 0 &&
- query_cache.is_disabled())
- {
- /* if disabling in progress variable will not be set */
- DBUG_ASSERT(!query_cache.is_disable_in_progress());
- /* Enable query cache because it was disabled */
- fix_query_cache_size(thd, type);
- }
- else if (global_system_variables.query_cache_type == 0)
- query_cache.disable_query_cache(thd);
+ /*
+ Yes, both locks need to be taken before an update, just as
+ both are taken to get a value. If we'll take only 'guard' here,
+ then value_ptr() for strings won't be safe in SHOW VARIABLES anymore,
+ to make it safe we'll need value_ptr_unlock().
+ */
+ AutoWLock lock1(&PLock_global_system_variables);
+ AutoWLock lock2(guard);
+ return global_update(thd, var) ||
+ (on_update && on_update(this, thd, OPT_GLOBAL));
}
+ else
+ return session_update(thd, var) ||
+ (on_update && on_update(this, thd, OPT_SESSION));
}
-
-static void fix_query_cache_min_res_unit(THD *thd, enum_var_type type)
+uchar *sys_var::session_value_ptr(THD *thd, LEX_STRING *base)
{
- query_cache_min_res_unit=
- query_cache.set_min_res_unit(query_cache_min_res_unit);
+ return session_var_ptr(thd);
}
-#endif
-
-extern void fix_delay_key_write(THD *thd, enum_var_type type)
+uchar *sys_var::global_value_ptr(THD *thd, LEX_STRING *base)
{
- switch ((enum_delay_key_write) delay_key_write_options) {
- case DELAY_KEY_WRITE_NONE:
- myisam_delay_key_write=0;
-#ifdef WITH_ARIA_STORAGE_ENGINE
- maria_delay_key_write= 0;
-#endif
- ha_open_options&= ~HA_OPEN_DELAY_KEY_WRITE;
- break;
- case DELAY_KEY_WRITE_ON:
- myisam_delay_key_write=1;
-#ifdef WITH_ARIA_STORAGE_ENGINE
- maria_delay_key_write= 1;
-#endif
- ha_open_options&= ~HA_OPEN_DELAY_KEY_WRITE;
- break;
- case DELAY_KEY_WRITE_ALL:
- myisam_delay_key_write=1;
-#ifdef WITH_ARIA_STORAGE_ENGINE
- maria_delay_key_write= 1;
-#endif
- ha_open_options|= HA_OPEN_DELAY_KEY_WRITE;
- break;
- }
+ return global_var_ptr();
}
-bool sys_var_set::update(THD *thd, set_var *var)
-{
- *value= var->save_result.ulong_value;
- return 0;
-}
-
-uchar *sys_var_set::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
+bool sys_var::check(THD *thd, set_var *var)
{
- char buff[256];
- String tmp(buff, sizeof(buff), &my_charset_latin1);
- ulong length;
- ulong val= *value;
-
- tmp.length(0);
- for (uint i= 0; val; val>>= 1, i++)
+ if ((var->value && do_check(thd, var))
+ || (on_check && on_check(this, thd, var)))
{
- if (val & 1)
+ if (!thd->is_error())
{
- tmp.append(enum_names->type_names[i],
- enum_names->type_lengths[i]);
- tmp.append(',');
+ char buff[STRING_BUFFER_USUAL_SIZE];
+ String str(buff, sizeof(buff), system_charset_info), *res;
+
+ if (!var->value)
+ {
+ str.set(STRING_WITH_LEN("DEFAULT"), &my_charset_latin1);
+ res= &str;
+ }
+ else if (!(res=var->value->val_str(&str)))
+ {
+ str.set(STRING_WITH_LEN("NULL"), &my_charset_latin1);
+ res= &str;
+ }
+ ErrConvString err(res);
+ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name.str, err.ptr());
}
+ return true;
}
-
- if ((length= tmp.length()))
- length--;
- return (uchar*) thd->strmake(tmp.ptr(), length);
-}
-
-void sys_var_set_slave_mode::set_default(THD *thd, enum_var_type type)
-{
- slave_exec_mode_options= SLAVE_EXEC_MODE_STRICT;
+ return false;
}
-bool sys_var_set_slave_mode::check(THD *thd, set_var *var)
+uchar *sys_var::value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
{
- bool rc= sys_var_set::check(thd, var);
- if (!rc &&
- test_all_bits(var->save_result.ulong_value,
- SLAVE_EXEC_MODE_STRICT | SLAVE_EXEC_MODE_IDEMPOTENT))
+ if (type == OPT_GLOBAL || scope() == GLOBAL)
{
- rc= true;
- my_error(ER_SLAVE_AMBIGOUS_EXEC_MODE, MYF(0), "");
+ mysql_mutex_assert_owner(&LOCK_global_system_variables);
+ AutoRLock lock(guard);
+ return global_value_ptr(thd, base);
}
- return rc;
-}
-
-bool sys_var_set_slave_mode::update(THD *thd, set_var *var)
-{
- bool rc;
- pthread_mutex_lock(&LOCK_global_system_variables);
- rc= sys_var_set::update(thd, var);
- pthread_mutex_unlock(&LOCK_global_system_variables);
- return rc;
+ else
+ return session_value_ptr(thd, base);
}
-void fix_slave_exec_mode(void)
+bool sys_var::set_default(THD *thd, set_var* var)
{
- DBUG_ENTER("fix_slave_exec_mode");
-
- if (test_all_bits(slave_exec_mode_options,
- SLAVE_EXEC_MODE_STRICT | SLAVE_EXEC_MODE_IDEMPOTENT))
- {
- sql_print_error("Ambiguous slave modes combination. STRICT will be used");
- slave_exec_mode_options&= ~SLAVE_EXEC_MODE_IDEMPOTENT;
- }
- if (!(slave_exec_mode_options & SLAVE_EXEC_MODE_IDEMPOTENT))
- slave_exec_mode_options|= SLAVE_EXEC_MODE_STRICT;
- DBUG_VOID_RETURN;
-}
-
+ if (var->type == OPT_GLOBAL || scope() == GLOBAL)
+ global_save_default(thd, var);
+ else
+ session_save_default(thd, var);
-bool sys_var_thd_binlog_format::check(THD *thd, set_var *var) {
- /*
- All variables that affect writing to binary log (either format or
- turning logging on and off) use the same checking. We call the
- superclass ::check function to assign the variable correctly, and
- then check the value.
- */
- bool result= sys_var_thd_enum::check(thd, var);
- if (!result)
- result= check_log_update(thd, var);
- return result;
+ return check(thd, var) || update(thd, var);
}
-
-bool sys_var_thd_binlog_format::is_readonly() const
+void sys_var::do_deprecated_warning(THD *thd)
{
- /*
- Under certain circumstances, the variable is read-only (unchangeable):
- */
- THD *thd= current_thd;
- /*
- If RBR and open temporary tables, their CREATE TABLE may not be in the
- binlog, so we can't toggle to SBR in this connection.
- The test below will also prevent SET GLOBAL, well it was not easy to test
- if global or not here.
- And this test will also prevent switching from RBR to RBR (a no-op which
- should not happen too often).
-
- If we don't have row-based replication compiled in, the variable
- is always read-only.
- */
- if ((thd->variables.binlog_format == BINLOG_FORMAT_ROW) &&
- thd->temporary_tables)
- {
- my_error(ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR, MYF(0));
- return 1;
- }
- /*
- if in a stored function/trigger, it's too late to change mode
- */
- if (thd->in_sub_stmt)
+ if (deprecation_substitute != NULL)
{
- my_error(ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT, MYF(0));
- return 1;
- }
- return sys_var_thd_enum::is_readonly();
-}
-
-
-void fix_binlog_format_after_update(THD *thd, enum_var_type type)
-{
- thd->reset_current_stmt_binlog_row_based();
-}
-
-
-static void fix_max_binlog_size(THD *thd, enum_var_type type)
-{
- DBUG_ENTER("fix_max_binlog_size");
- DBUG_PRINT("info",("max_binlog_size=%lu max_relay_log_size=%lu",
- max_binlog_size, max_relay_log_size));
- mysql_bin_log.set_max_size(max_binlog_size);
-#ifdef HAVE_REPLICATION
- if (!max_relay_log_size)
- active_mi->rli.relay_log.set_max_size(max_binlog_size);
-#endif
- DBUG_VOID_RETURN;
-}
-
-static void fix_max_relay_log_size(THD *thd, enum_var_type type)
-{
- DBUG_ENTER("fix_max_relay_log_size");
- DBUG_PRINT("info",("max_binlog_size=%lu max_relay_log_size=%lu",
- max_binlog_size, max_relay_log_size));
-#ifdef HAVE_REPLICATION
- active_mi->rli.relay_log.set_max_size(max_relay_log_size ?
- max_relay_log_size: max_binlog_size);
-#endif
- DBUG_VOID_RETURN;
-}
-
+ char buf1[NAME_CHAR_LEN + 3];
+ strxnmov(buf1, sizeof(buf1)-1, "@@", name.str, 0);
-static int check_max_delayed_threads(THD *thd, set_var *var)
-{
- longlong val= var->value->val_int();
- if (var->type != OPT_GLOBAL && val != 0 &&
- val != (longlong) global_system_variables.max_insert_delayed_threads)
- {
- char buf[64];
- my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name, llstr(val, buf));
- return 1;
+ /*
+ if deprecation_substitute is an empty string,
+ there is no replacement for the syntax
+ */
+ uint errmsg= deprecation_substitute[0] == '\0'
+ ? ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT
+ : ER_WARN_DEPRECATED_SYNTAX;
+ if (thd)
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DEPRECATED_SYNTAX, ER(errmsg),
+ buf1, deprecation_substitute);
+ else
+ sql_print_warning(ER_DEFAULT(errmsg), buf1, deprecation_substitute);
}
- return 0;
-}
-
-static void fix_max_connections(THD *thd, enum_var_type type)
-{
-#ifndef EMBEDDED_LIBRARY
- resize_thr_alarm(max_connections + extra_max_connections +
- global_system_variables.max_insert_delayed_threads + 10);
-#endif
}
-
-static void fix_thd_mem_root(THD *thd, enum_var_type type)
-{
- if (type != OPT_GLOBAL)
- reset_root_defaults(thd->mem_root,
- thd->variables.query_alloc_block_size,
- thd->variables.query_prealloc_size);
-}
-
-
-static void fix_trans_mem_root(THD *thd, enum_var_type type)
-{
-#ifdef USING_TRANSACTIONS
- if (type != OPT_GLOBAL)
- reset_root_defaults(&thd->transaction.mem_root,
- thd->variables.trans_alloc_block_size,
- thd->variables.trans_prealloc_size);
-#endif
-}
-
-
-static void fix_server_id(THD *thd, enum_var_type type)
-{
- server_id_supplied = 1;
- thd->server_id= server_id;
-}
-
-
/**
Throw warning (error in STRICT mode) if value for variable needed bounding.
- Only call from check(), not update(), because an error in update() would be
- bad mojo. Plug-in interface also uses this.
+ Plug-in interface also uses this.
- @param thd thread handle
- @param fixed did we have to correct the value? (throw warn/err if so)
- @param unsignd is value's type unsigned?
- @param name variable's name
- @param val variable's value
+ @param thd thread handle
+ @param name variable's name
+ @param fixed did we have to correct the value? (throw warn/err if so)
+ @param is_unsigned is value's type unsigned?
+ @param v variable's value
- @retval TRUE on error, FALSE otherwise (warning or OK)
+ @retval true on error, false otherwise (warning or ok)
*/
-bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd,
- const char *name, longlong val)
+bool throw_bounds_warning(THD *thd, const char *name,
+ bool fixed, bool is_unsigned, longlong v)
{
if (fixed)
{
char buf[22];
- if (unsignd)
- ullstr((ulonglong) val, buf);
+ if (is_unsigned)
+ ullstr((ulonglong) v, buf);
else
- llstr(val, buf);
+ llstr(v, buf);
if (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES)
{
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, buf);
- return TRUE;
+ return true;
}
-
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), name, buf);
}
- return FALSE;
-}
-
-
-/**
- Get unsigned system-variable.
- Negative value does not wrap around, but becomes zero.
- Check user-supplied value for a systemvariable against bounds.
- If we needed to adjust the value, throw a warning or error depending
- on SQL-mode.
-
- @param thd thread handle
- @param var the system-variable to get
- @param user_max a limit given with --maximum-variable-name=... or 0
- @param var_type function will bound on systems where necessary.
-
- @retval TRUE on error, FALSE otherwise (warning or OK)
- */
-static bool get_unsigned(THD *thd, set_var *var, ulonglong user_max,
- ulong var_type)
-{
- int warnings= 0;
- ulonglong unadjusted;
- const struct my_option *limits= var->var->option_limits;
- struct my_option fallback;
-
- /* get_unsigned() */
- if (var->value->unsigned_flag)
- var->save_result.ulonglong_value= (ulonglong) var->value->val_int();
- else
- {
- longlong v= var->value->val_int();
- var->save_result.ulonglong_value= (ulonglong) ((v < 0) ? 0 : v);
- if (v < 0)
- {
- warnings++;
- if (throw_bounds_warning(thd, TRUE, FALSE, var->var->name, v))
- return TRUE; /* warning was promoted to error, give up */
- }
- }
-
- unadjusted= var->save_result.ulonglong_value;
-
- /* max, if any */
-
- if ((user_max > 0) && (unadjusted > user_max))
- {
- var->save_result.ulonglong_value= user_max;
-
- if ((warnings == 0) && throw_bounds_warning(thd, TRUE, TRUE,
- var->var->name,
- (longlong) unadjusted))
- return TRUE;
-
- warnings++;
- }
-
- /*
- if the sysvar doesn't have a proper bounds record but the check
- function would like bounding to ULONG where its size differs from
- that of ULONGLONG, we make up a bogus limits record here and let
- the usual suspects handle the actual limiting.
- */
-
- if (!limits && var_type != GET_ULL)
- {
- bzero(&fallback, sizeof(fallback));
- fallback.var_type= var_type;
- limits= &fallback;
- }
-
- /* fix_unsigned() */
- if (limits)
- {
- my_bool fixed;
-
- var->save_result.ulonglong_value= getopt_ull_limit_value(var->save_result.
- ulonglong_value,
- limits, &fixed);
-
- if ((warnings == 0) && throw_bounds_warning(thd, fixed, TRUE,
- var->var->name,
- (longlong) unadjusted))
- return TRUE;
- }
-
- return FALSE;
-}
-
-
-sys_var_long_ptr::
-sys_var_long_ptr(sys_var_chain *chain, const char *name_arg, ulong *value_ptr_arg,
- sys_after_update_func after_update_arg)
- :sys_var_long_ptr_global(chain, name_arg, value_ptr_arg,
- &LOCK_global_system_variables, after_update_arg)
-{}
-
-
-bool sys_var_long_ptr_global::check(THD *thd, set_var *var)
-{
- return get_unsigned(thd, var, 0, GET_ULONG);
-}
-
-bool sys_var_long_ptr_global::update(THD *thd, set_var *var)
-{
- pthread_mutex_lock(guard);
- *value= (ulong) var->save_result.ulonglong_value;
- pthread_mutex_unlock(guard);
- return 0;
-}
-
-
-void sys_var_long_ptr_global::set_default(THD *thd, enum_var_type type)
-{
- my_bool not_used;
- pthread_mutex_lock(guard);
- *value= (ulong) getopt_ull_limit_value((ulong) option_limits->def_value,
- option_limits, &not_used);
- pthread_mutex_unlock(guard);
-}
-
-
-bool sys_var_ulonglong_ptr::check(THD *thd, set_var *var)
-{
- return get_unsigned(thd, var, 0, GET_ULL);
-}
-
-
-bool sys_var_ulonglong_ptr::update(THD *thd, set_var *var)
-{
- ulonglong tmp= var->save_result.ulonglong_value;
- pthread_mutex_lock(&LOCK_global_system_variables);
- *value= (ulonglong) tmp;
- pthread_mutex_unlock(&LOCK_global_system_variables);
- return 0;
-}
-
-
-void sys_var_ulonglong_ptr::set_default(THD *thd, enum_var_type type)
-{
- my_bool not_used;
- pthread_mutex_lock(&LOCK_global_system_variables);
- *value= getopt_ull_limit_value((ulonglong) option_limits->def_value,
- option_limits, &not_used);
- pthread_mutex_unlock(&LOCK_global_system_variables);
-}
-
-
-bool sys_var_bool_ptr::update(THD *thd, set_var *var)
-{
- *value= (my_bool) var->save_result.ulong_value;
- return 0;
-}
-
-
-void sys_var_bool_ptr::set_default(THD *thd, enum_var_type type)
-{
- *value= (my_bool) option_limits->def_value;
-}
-
-
-bool sys_var_enum::update(THD *thd, set_var *var)
-{
- *value= (uint) var->save_result.ulong_value;
- return 0;
-}
-
-
-uchar *sys_var_enum::value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
-{
- return (uchar*) enum_names->type_names[*value];
-}
-
-bool sys_var_thd_ulong::check(THD *thd, set_var *var)
-{
- if (get_unsigned(thd, var, max_system_variables.*offset, GET_ULONG))
- return TRUE;
- DBUG_ASSERT(var->save_result.ulonglong_value <= ULONG_MAX);
- return ((check_func && (*check_func)(thd, var)));
-}
-
-bool sys_var_thd_ulong::update(THD *thd, set_var *var)
-{
- if (var->type == OPT_GLOBAL)
- global_system_variables.*offset= (ulong) var->save_result.ulonglong_value;
- else
- thd->variables.*offset= (ulong) var->save_result.ulonglong_value;
-
- return 0;
-}
-
-
-void sys_var_thd_ulong::set_default(THD *thd, enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- {
- my_bool not_used;
- /* We will not come here if option_limits is not set */
- global_system_variables.*offset=
- (ulong) getopt_ull_limit_value((ulong) option_limits->def_value,
- option_limits, &not_used);
- }
- else
- thd->variables.*offset= global_system_variables.*offset;
-}
-
-
-uchar *sys_var_thd_ulong::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- if (type == OPT_GLOBAL)
- return (uchar*) &(global_system_variables.*offset);
- return (uchar*) &(thd->variables.*offset);
-}
-
-
-bool sys_var_thd_ha_rows::check(THD *thd, set_var *var)
-{
- return get_unsigned(thd, var, max_system_variables.*offset,
-#ifdef BIG_TABLES
- GET_ULL
-#else
- GET_ULONG
-#endif
- );
-}
-
-
-bool sys_var_thd_ha_rows::update(THD *thd, set_var *var)
-{
- if (var->type == OPT_GLOBAL)
- {
- /* Lock is needed to make things safe on 32 bit systems */
- pthread_mutex_lock(&LOCK_global_system_variables);
- global_system_variables.*offset= (ha_rows)
- var->save_result.ulonglong_value;
- pthread_mutex_unlock(&LOCK_global_system_variables);
- }
- else
- thd->variables.*offset= (ha_rows) var->save_result.ulonglong_value;
- return 0;
-}
-
-
-void sys_var_thd_ha_rows::set_default(THD *thd, enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- {
- my_bool not_used;
- /* We will not come here if option_limits is not set */
- pthread_mutex_lock(&LOCK_global_system_variables);
- global_system_variables.*offset=
- (ha_rows) getopt_ull_limit_value((ha_rows) option_limits->def_value,
- option_limits, &not_used);
- pthread_mutex_unlock(&LOCK_global_system_variables);
- }
- else
- thd->variables.*offset= global_system_variables.*offset;
-}
-
-
-uchar *sys_var_thd_ha_rows::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- if (type == OPT_GLOBAL)
- return (uchar*) &(global_system_variables.*offset);
- return (uchar*) &(thd->variables.*offset);
-}
-
-bool sys_var_thd_ulonglong::check(THD *thd, set_var *var)
-{
- return get_unsigned(thd, var, max_system_variables.*offset, GET_ULL);
-}
-
-bool sys_var_thd_ulonglong::update(THD *thd, set_var *var)
-{
- if (var->type == OPT_GLOBAL)
- {
- /* Lock is needed to make things safe on 32 bit systems */
- pthread_mutex_lock(&LOCK_global_system_variables);
- global_system_variables.*offset= (ulonglong)
- var->save_result.ulonglong_value;
- pthread_mutex_unlock(&LOCK_global_system_variables);
- }
- else
- thd->variables.*offset= (ulonglong) var->save_result.ulonglong_value;
- return 0;
-}
-
-
-void sys_var_thd_ulonglong::set_default(THD *thd, enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- {
- my_bool not_used;
- pthread_mutex_lock(&LOCK_global_system_variables);
- global_system_variables.*offset=
- getopt_ull_limit_value((ulonglong) option_limits->def_value,
- option_limits, &not_used);
- pthread_mutex_unlock(&LOCK_global_system_variables);
- }
- else
- thd->variables.*offset= global_system_variables.*offset;
-}
-
-
-uchar *sys_var_thd_ulonglong::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- if (type == OPT_GLOBAL)
- return (uchar*) &(global_system_variables.*offset);
- return (uchar*) &(thd->variables.*offset);
-}
-
-
-bool sys_var_thd_bool::update(THD *thd, set_var *var)
-{
- if (var->type == OPT_GLOBAL)
- global_system_variables.*offset= (my_bool) var->save_result.ulong_value;
- else
- thd->variables.*offset= (my_bool) var->save_result.ulong_value;
- return 0;
-}
-
-
-void sys_var_thd_bool::set_default(THD *thd, enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- global_system_variables.*offset= (my_bool) option_limits->def_value;
- else
- thd->variables.*offset= global_system_variables.*offset;
-}
-
-
-uchar *sys_var_thd_bool::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- if (type == OPT_GLOBAL)
- return (uchar*) &(global_system_variables.*offset);
- return (uchar*) &(thd->variables.*offset);
-}
-
-
-bool sys_var::check_enum(THD *thd, set_var *var, const TYPELIB *enum_names)
-{
- char buff[STRING_BUFFER_USUAL_SIZE];
- const char *value;
- String str(buff, sizeof(buff) - 1, system_charset_info), *res;
-
- if (var->value->result_type() == STRING_RESULT)
- {
- if (!(res=var->value->val_str(&str)) ||
- ((long) (var->save_result.ulong_value=
- (ulong) find_type(enum_names, res->ptr(),
- res->length(),1)-1)) < 0)
- {
- value= res ? res->c_ptr() : "NULL";
- goto err;
- }
- }
- else
- {
- ulonglong tmp=var->value->val_int();
- if (tmp >= enum_names->count)
- {
- llstr(tmp,buff);
- value=buff; // Wrong value is here
- goto err;
- }
- var->save_result.ulong_value= (ulong) tmp; // Save for update
- }
- return 0;
-
-err:
- my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, value);
- return 1;
+ return false;
}
-/**
- Check vality of set
-
- Note that this sets 'save_result.ulong_value' for the update function,
- which means that we don't need a separate sys_var::update() function
-*/
-
-bool sys_var::check_set(THD *thd, set_var *var, TYPELIB *enum_names)
+bool throw_bounds_warning(THD *thd, const char *name, bool fixed, double v)
{
- bool not_used;
- char buff[256], *error= 0;
- uint error_len= 0;
- String str(buff, sizeof(buff) - 1, system_charset_info), *res;
-
- if (var->value->result_type() == STRING_RESULT)
- {
- if (!(res= var->value->val_str(&str)))
- {
- strmov(buff, "NULL");
- goto err;
- }
-
- if (!m_allow_empty_value && res->length() == 0)
- {
- buff[0]= 0;
- goto err;
- }
-
- var->save_result.ulong_value= ((ulong)
- find_set(enum_names, res->c_ptr_safe(),
- res->length(),
- NULL,
- &error, &error_len,
- &not_used));
- if (error_len)
- {
- strmake(buff, error, min(sizeof(buff) - 1, error_len));
- goto err;
- }
- }
- else
+ if (fixed)
{
- ulonglong tmp= var->value->val_int();
-
- if (!m_allow_empty_value && tmp == 0)
- {
- buff[0]= '0';
- buff[1]= 0;
- goto err;
- }
-
- /*
- For when the enum is made to contain 64 elements, as 1ULL<<64 is
- undefined, we guard with a "count<64" test.
- */
- if (unlikely((tmp >= ((ULL(1)) << enum_names->count)) &&
- (enum_names->count < 64)))
- {
- llstr(tmp, buff);
- goto err;
- }
- var->save_result.ulong_value= (ulong) tmp; // Save for update
- }
- return 0;
-
-err:
- my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, buff);
- return 1;
-}
-
-
-/**
- Make string representation of set
-
- @param[in] thd thread handler
- @param[in] val sql_mode value
- @param[in] names names for the different bits
- @param[out] rep Result string
-
- @return
- 0 ok
- 1 end of memory
-*/
+ char buf[64];
-bool sys_var::make_set(THD *thd, ulonglong val, TYPELIB *names,
- LEX_STRING *rep)
-{
- /* Strings for typelib may be big; This is reallocated on demand */
- char buff[256];
- String tmp(buff, sizeof(buff) - 1, &my_charset_latin1);
- bool error= 0;
+ my_gcvt(v, MY_GCVT_ARG_DOUBLE, sizeof(buf) - 1, buf, NULL);
- tmp.length(0);
- for (uint i= 0; val; val>>= 1, i++)
- {
- if (val & 1)
+ if (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES)
{
- error|= tmp.append(names->type_names[i],
- names->type_lengths[i]);
- error|= tmp.append(',');
+ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, buf);
+ return true;
}
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_TRUNCATED_WRONG_VALUE,
+ ER(ER_TRUNCATED_WRONG_VALUE), name, buf);
}
-
- if (tmp.length())
- tmp.length(tmp.length() - 1); /* trim the trailing comma */
-
- /* Allocate temporary copy of string */
- if (!(rep->str= thd->strmake(tmp.ptr(), tmp.length())))
- error= 1;
- rep->length= tmp.length();
- return error; /* Error in case of out of memory */
+ return false;
}
-
CHARSET_INFO *sys_var::charset(THD *thd)
{
- return is_os_charset ? thd->variables.character_set_filesystem :
+ return is_os_charset ? thd->variables.character_set_filesystem :
system_charset_info;
}
-
-bool sys_var_thd_enum::update(THD *thd, set_var *var)
-{
- if (var->type == OPT_GLOBAL)
- global_system_variables.*offset= var->save_result.ulong_value;
- else
- thd->variables.*offset= var->save_result.ulong_value;
- return 0;
-}
-
-
-void sys_var_thd_enum::set_default(THD *thd, enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- global_system_variables.*offset= (ulong) option_limits->def_value;
- else
- thd->variables.*offset= global_system_variables.*offset;
-}
-
-
-uchar *sys_var_thd_enum::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- ulong tmp= ((type == OPT_GLOBAL) ?
- global_system_variables.*offset :
- thd->variables.*offset);
- return (uchar*) enum_names->type_names[tmp];
-}
-
-uchar *sys_var_thd_set::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- LEX_STRING sql_mode;
- ulong val= ((type == OPT_GLOBAL) ? global_system_variables.*offset :
- thd->variables.*offset);
- (void) make_set(thd, val & visible_value_mask, enum_names, &sql_mode);
- return (uchar *) sql_mode.str;
-}
-
-bool sys_var_thd_bit::check(THD *thd, set_var *var)
-{
- return (check_enum(thd, var, &bool_typelib) ||
- (check_func && (*check_func)(thd, var)));
-}
-
-bool sys_var_thd_bit::update(THD *thd, set_var *var)
-{
- int res= (*update_func)(thd, var);
- return res;
-}
-
-
-uchar *sys_var_thd_bit::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- /*
- If reverse is 0 (default) return 1 if bit is set.
- If reverse is 1, return 0 if bit is set
- */
- thd->sys_var_tmp.my_bool_value= ((thd->options & bit_flag) ?
- !reverse : reverse);
- return (uchar*) &thd->sys_var_tmp.my_bool_value;
-}
-
-
-/** Update a date_time format variable based on given value. */
-
-void sys_var_thd_date_time_format::update2(THD *thd, enum_var_type type,
- DATE_TIME_FORMAT *new_value)
-{
- DATE_TIME_FORMAT *old;
- DBUG_ENTER("sys_var_date_time_format::update2");
- DBUG_DUMP("positions", (uchar*) new_value->positions,
- sizeof(new_value->positions));
-
- if (type == OPT_GLOBAL)
- {
- pthread_mutex_lock(&LOCK_global_system_variables);
- old= (global_system_variables.*offset);
- (global_system_variables.*offset)= new_value;
- pthread_mutex_unlock(&LOCK_global_system_variables);
- }
- else
- {
- old= (thd->variables.*offset);
- (thd->variables.*offset)= new_value;
- }
- my_free((char*) old, MYF(MY_ALLOW_ZERO_PTR));
- DBUG_VOID_RETURN;
-}
-
-
-bool sys_var_thd_date_time_format::update(THD *thd, set_var *var)
-{
- DATE_TIME_FORMAT *new_value;
- /* We must make a copy of the last value to get it into normal memory */
- new_value= date_time_format_copy((THD*) 0,
- var->save_result.date_time_format);
- if (!new_value)
- return 1; // Out of memory
- update2(thd, var->type, new_value); // Can't fail
- return 0;
-}
-
-
-bool sys_var_thd_date_time_format::check(THD *thd, set_var *var)
-{
- char buff[STRING_BUFFER_USUAL_SIZE];
- String str(buff,sizeof(buff) - 1, system_charset_info), *res;
- DATE_TIME_FORMAT *format;
-
- if (!(res= var->value->val_str(&str)) ||
- !(format= date_time_format_make(date_time_type,
- res->ptr(), res->length())))
- {
- my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, res ? res->c_ptr() : "NULL");
- return 1;
- }
-
- /*
- We must copy result to thread space to not get a memory leak if
- update is aborted
- */
- var->save_result.date_time_format= date_time_format_copy(thd, format);
- my_free((char*) format, MYF(0));
- return var->save_result.date_time_format == 0;
-}
-
-
-void sys_var_thd_date_time_format::set_default(THD *thd, enum_var_type type)
-{
- DATE_TIME_FORMAT *res= 0;
-
- if (type == OPT_GLOBAL)
- {
- const char *format;
- if ((format= opt_date_time_formats[date_time_type]))
- res= date_time_format_make(date_time_type, format, strlen(format));
- }
- else
- {
- /* Make copy with malloc */
- res= date_time_format_copy((THD *) 0, global_system_variables.*offset);
- }
-
- if (res) // Should always be true
- update2(thd, type, res);
-}
-
-
-uchar *sys_var_thd_date_time_format::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- if (type == OPT_GLOBAL)
- {
- char *res;
- /*
- We do a copy here just to be sure things will work even if someone
- is modifying the original string while the copy is accessed
- (Can't happen now in SQL SHOW, but this is a good safety for the future)
- */
- res= thd->strmake((global_system_variables.*offset)->format.str,
- (global_system_variables.*offset)->format.length);
- return (uchar*) res;
- }
- return (uchar*) (thd->variables.*offset)->format.str;
-}
-
-
typedef struct old_names_map_st
{
const char *old_name;
const char *new_name;
} my_old_conv;
-static my_old_conv old_conv[]=
-{
- { "cp1251_koi8" , "cp1251" },
- { "cp1250_latin2" , "cp1250" },
- { "kam_latin2" , "keybcs2" },
- { "mac_latin2" , "MacRoman" },
- { "macce_latin2" , "MacCE" },
- { "pc2_latin2" , "pclatin2" },
- { "vga_latin2" , "pclatin1" },
- { "koi8_cp1251" , "koi8r" },
- { "win1251ukr_koi8_ukr" , "win1251ukr" },
- { "koi8_ukr_win1251ukr" , "koi8u" },
- { NULL , NULL }
+static my_old_conv old_conv[]=
+{
+ { "cp1251_koi8" , "cp1251" },
+ { "cp1250_latin2" , "cp1250" },
+ { "kam_latin2" , "keybcs2" },
+ { "mac_latin2" , "MacRoman" },
+ { "macce_latin2" , "MacCE" },
+ { "pc2_latin2" , "pclatin2" },
+ { "vga_latin2" , "pclatin1" },
+ { "koi8_cp1251" , "koi8r" },
+ { "win1251ukr_koi8_ukr" , "win1251ukr" },
+ { "koi8_ukr_win1251ukr" , "koi8u" },
+ { NULL , NULL }
};
CHARSET_INFO *get_old_charset_by_name(const char *name)
{
my_old_conv *conv;
-
+
for (conv= old_conv; conv->old_name; conv++)
{
if (!my_strcasecmp(&my_charset_latin1, name, conv->old_name))
@@ -2287,1147 +382,6 @@ CHARSET_INFO *get_old_charset_by_name(const char *name)
return NULL;
}
-
-bool sys_var_collation::check(THD *thd, set_var *var)
-{
- CHARSET_INFO *tmp;
- LINT_INIT(tmp);
-
- if (var->value->result_type() == STRING_RESULT)
- {
- char buff[STRING_BUFFER_USUAL_SIZE];
- String str(buff,sizeof(buff) - 1, system_charset_info), *res;
- if (!(res=var->value->val_str(&str)))
- {
- my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, "NULL");
- return 1;
- }
- if (!(tmp=get_charset_by_name(res->c_ptr(),MYF(0))))
- {
- my_error(ER_UNKNOWN_COLLATION, MYF(0), res->c_ptr());
- return 1;
- }
- }
- else // INT_RESULT
- {
- if (!(tmp=get_charset((int) var->value->val_int(),MYF(0))))
- {
- char buf[20];
- int10_to_str((int) var->value->val_int(), buf, -10);
- my_error(ER_UNKNOWN_COLLATION, MYF(0), buf);
- return 1;
- }
- }
- var->save_result.charset= tmp; // Save for update
- return 0;
-}
-
-
-bool sys_var_character_set::check(THD *thd, set_var *var)
-{
- CHARSET_INFO *tmp;
- LINT_INIT(tmp);
-
- if (var->value->result_type() == STRING_RESULT)
- {
- char buff[STRING_BUFFER_USUAL_SIZE];
- String str(buff,sizeof(buff) - 1, system_charset_info), *res;
- if (!(res=var->value->val_str(&str)))
- {
- if (!nullable)
- {
- my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, "NULL");
- return 1;
- }
- tmp= NULL;
- }
- else
- {
- const char *name= res->c_ptr_safe();
- if (!(tmp=get_charset_by_csname(name,MY_CS_PRIMARY,MYF(0))) &&
- !(tmp=get_old_charset_by_name(name)))
- {
- my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), name);
- return 1;
- }
- }
- }
- else // INT_RESULT
- {
- if (!(tmp=get_charset((int) var->value->val_int(),MYF(0))))
- {
- char buf[20];
- int10_to_str((int) var->value->val_int(), buf, -10);
- my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), buf);
- return 1;
- }
- }
- var->save_result.charset= tmp; // Save for update
- return 0;
-}
-
-
-bool sys_var_character_set::update(THD *thd, set_var *var)
-{
- ci_ptr(thd,var->type)[0]= var->save_result.charset;
- thd->update_charset();
- return 0;
-}
-
-
-uchar *sys_var_character_set::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- CHARSET_INFO *cs= ci_ptr(thd,type)[0];
- return cs ? (uchar*) cs->csname : (uchar*) NULL;
-}
-
-
-void sys_var_character_set_sv::set_default(THD *thd, enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- global_system_variables.*offset= *global_default;
- else
- {
- thd->variables.*offset= global_system_variables.*offset;
- thd->update_charset();
- }
-}
-CHARSET_INFO **sys_var_character_set_sv::ci_ptr(THD *thd, enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- return &(global_system_variables.*offset);
- else
- return &(thd->variables.*offset);
-}
-
-
-bool sys_var_character_set_client::check(THD *thd, set_var *var)
-{
- if (sys_var_character_set_sv::check(thd, var))
- return 1;
- /* Currently, UCS-2 cannot be used as a client character set */
- if (!is_supported_parser_charset(var->save_result.charset))
- {
- my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name,
- var->save_result.charset->csname);
- return 1;
- }
- return 0;
-}
-
-
-CHARSET_INFO ** sys_var_character_set_database::ci_ptr(THD *thd,
- enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- return &global_system_variables.collation_database;
- else
- return &thd->variables.collation_database;
-}
-
-
-void sys_var_character_set_database::set_default(THD *thd, enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- global_system_variables.collation_database= default_charset_info;
- else
- {
- thd->variables.collation_database= thd->db_charset;
- thd->update_charset();
- }
-}
-
-
-bool sys_var_collation_sv::update(THD *thd, set_var *var)
-{
- if (var->type == OPT_GLOBAL)
- global_system_variables.*offset= var->save_result.charset;
- else
- {
- thd->variables.*offset= var->save_result.charset;
- thd->update_charset();
- }
- return 0;
-}
-
-
-void sys_var_collation_sv::set_default(THD *thd, enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- global_system_variables.*offset= *global_default;
- else
- {
- thd->variables.*offset= global_system_variables.*offset;
- thd->update_charset();
- }
-}
-
-
-uchar *sys_var_collation_sv::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- CHARSET_INFO *cs= ((type == OPT_GLOBAL) ?
- global_system_variables.*offset : thd->variables.*offset);
- return cs ? (uchar*) cs->name : (uchar*) "NULL";
-}
-
-
-LEX_STRING default_key_cache_base= {(char *) "default", 7 };
-
-static KEY_CACHE zero_key_cache;
-
-KEY_CACHE *get_key_cache(LEX_STRING *cache_name)
-{
- safe_mutex_assert_owner(&LOCK_global_system_variables);
- if (!cache_name || ! cache_name->length)
- cache_name= &default_key_cache_base;
- return ((KEY_CACHE*) find_named(&key_caches,
- cache_name->str, cache_name->length, 0));
-}
-
-uchar *sys_var_key_cache_param::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- KEY_CACHE *key_cache= get_key_cache(base);
- if (!key_cache)
- key_cache= &zero_key_cache;
- return (uchar*) key_cache + offset ;
-}
-
-
-bool sys_var_key_buffer_size::check(THD *thd, set_var *var)
-{
- return get_unsigned(thd, var, 0, GET_ULL);
-}
-
-
-bool sys_var_key_buffer_size::update(THD *thd, set_var *var)
-{
- ulonglong tmp= var->save_result.ulonglong_value;
- LEX_STRING *base_name= &var->base;
- KEY_CACHE *key_cache;
- bool error= 0;
-
- /* If no basename, assume it's for the key cache named 'default' */
- if (!base_name->length)
- base_name= &default_key_cache_base;
-
- pthread_mutex_lock(&LOCK_global_system_variables);
- key_cache= get_key_cache(base_name);
-
- if (!key_cache)
- {
- /* Key cache didn't exist */
- if (!tmp) // Tried to delete cache
- goto end; // Ok, nothing to do
- if (!(key_cache= create_key_cache(base_name->str, base_name->length)))
- {
- error= 1;
- goto end;
- }
- }
-
- /*
- Abort if some other thread is changing the key cache
- TODO: This should be changed so that we wait until the previous
- assignment is done and then do the new assign
- */
- if (key_cache->in_init)
- goto end;
-
- if (!tmp) // Zero size means delete
- {
- if (key_cache == dflt_key_cache)
- {
- error= 1;
- my_error(ER_WARN_CANT_DROP_DEFAULT_KEYCACHE, MYF(0));
- goto end; // Ignore default key cache
- }
-
- if (key_cache->key_cache_inited) // If initied
- {
- /*
- Move tables using this key cache to the default key cache
- and clear the old key cache.
- */
- NAMED_LIST *list;
- key_cache= (KEY_CACHE *) find_named(&key_caches, base_name->str,
- base_name->length, &list);
- key_cache->in_init= 1;
- pthread_mutex_unlock(&LOCK_global_system_variables);
- error= reassign_keycache_tables(thd, key_cache, dflt_key_cache);
- pthread_mutex_lock(&LOCK_global_system_variables);
- key_cache->in_init= 0;
- }
- /*
- We don't delete the key cache as some running threads my still be
- in the key cache code with a pointer to the deleted (empty) key cache
- */
- goto end;
- }
-
- key_cache->param_buff_size= (ulonglong) tmp;
-
- /* If key cache didn't exist initialize it, else resize it */
- key_cache->in_init= 1;
- pthread_mutex_unlock(&LOCK_global_system_variables);
-
- if (!key_cache->key_cache_inited)
- error= (bool) (ha_init_key_cache("", key_cache));
- else
- error= (bool)(ha_resize_key_cache(key_cache));
-
- pthread_mutex_lock(&LOCK_global_system_variables);
- key_cache->in_init= 0;
-
-end:
- pthread_mutex_unlock(&LOCK_global_system_variables);
-
- var->save_result.ulonglong_value = SIZE_T_MAX;
-
- return error;
-}
-
-
-bool sys_var_key_cache_long::check(THD *thd, set_var *var)
-{
- return get_unsigned(thd, var, 0, GET_ULONG);
-}
-
-
-/**
- @todo
- Abort if some other thread is changing the key cache.
- This should be changed so that we wait until the previous
- assignment is done and then do the new assign
-*/
-bool sys_var_key_cache_long::update(THD *thd, set_var *var)
-{
- LEX_STRING *base_name= &var->base;
- bool error= 0;
-
- if (!base_name->length)
- base_name= &default_key_cache_base;
-
- pthread_mutex_lock(&LOCK_global_system_variables);
- KEY_CACHE *key_cache= get_key_cache(base_name);
-
- if (!key_cache && !(key_cache= create_key_cache(base_name->str,
- base_name->length)))
- {
- error= 1;
- goto end;
- }
-
- /*
- Abort if some other thread is changing the key cache
- TODO: This should be changed so that we wait until the previous
- assignment is done and then do the new assign
- */
- if (key_cache->in_init)
- goto end;
-
- *((ulong*) (((char*) key_cache) + offset))= (ulong)
- var->save_result.ulonglong_value;
-
- /*
- Don't create a new key cache if it didn't exist
- (key_caches are created only when the user sets block_size)
- */
- key_cache->in_init= 1;
-
- pthread_mutex_unlock(&LOCK_global_system_variables);
-
- if (offset == offsetof(KEY_CACHE, param_block_size))
- error= (bool) (ha_resize_key_cache(key_cache));
- else
- if (offset == offsetof(KEY_CACHE, param_division_limit) ||
- offset == offsetof(KEY_CACHE, param_age_threshold))
- error= (bool) (ha_change_key_cache_param(key_cache));
- else
- if (offset == offsetof(KEY_CACHE, param_partitions))
- error= (bool) (ha_repartition_key_cache(key_cache));
-
- pthread_mutex_lock(&LOCK_global_system_variables);
- key_cache->in_init= 0;
-
-end:
- pthread_mutex_unlock(&LOCK_global_system_variables);
- return error;
-}
-
-
-bool sys_var_log_state::update(THD *thd, set_var *var)
-{
- bool res;
- if (this == &sys_var_log)
- WARN_DEPRECATED(thd, "7.0", "@@log", "'@@general_log'");
- else if (this == &sys_var_log_slow)
- WARN_DEPRECATED(thd, "7.0", "@@log_slow_queries", "'@@slow_query_log'");
-
- if (!var->save_result.ulong_value)
- {
- logger.deactivate_log_handler(thd, log_type);
- res= false;
- }
- else
- res= logger.activate_log_handler(thd, log_type);
- return res;
-}
-
-void sys_var_log_state::set_default(THD *thd, enum_var_type type)
-{
- if (this == &sys_var_log)
- WARN_DEPRECATED(thd, "7.0", "@@log", "'@@general_log'");
- else if (this == &sys_var_log_slow)
- WARN_DEPRECATED(thd, "7.0", "@@log_slow_queries", "'@@slow_query_log'");
-
- logger.deactivate_log_handler(thd, log_type);
-}
-
-
-static int sys_check_log_path(THD *thd, set_var *var)
-{
- char path[FN_REFLEN], buff[FN_REFLEN];
- MY_STAT f_stat;
- String str(buff, sizeof(buff), system_charset_info), *res;
- const char *log_file_str= 0;
- size_t path_length;
-
- if (!(res= var->value->val_str(&str)))
- goto err;
-
- log_file_str= res->c_ptr_safe();
- bzero(&f_stat, sizeof(MY_STAT));
-
- path_length= unpack_filename(path, log_file_str);
-
- if (!path_length)
- {
- /* File name is empty. */
-
- goto err;
- }
-
- if (my_stat(path, &f_stat, MYF(0)))
- {
- /*
- A file system object exists. Check if argument is a file and we have
- 'write' permission.
- */
-
- if (!MY_S_ISREG(f_stat.st_mode) ||
- !(f_stat.st_mode & MY_S_IWRITE))
- goto err;
-
- return 0;
- }
-
- /* Get dirname of the file path. */
- (void) dirname_part(path, log_file_str, &path_length);
-
- /* Dirname is empty if file path is relative. */
- if (!path_length)
- return 0;
-
- /*
- Check if directory exists and we have permission to create file and
- write to file.
- */
- if (my_access(path, (F_OK|W_OK)))
- goto err;
-
- return 0;
-
-err:
- my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->var->name,
- log_file_str ? log_file_str : "NULL");
- return 1;
-}
-
-
-bool update_sys_var_str_path(THD *thd, sys_var_str *var_str,
- set_var *var, const char *log_ext,
- bool log_state, uint log_type)
-{
- MYSQL_QUERY_LOG *file_log= 0;
- char buff[FN_REFLEN];
- char *res= 0, *old_value=(char *)(var ? var->value->str_value.ptr() : 0);
- bool result= 0;
- uint str_length= (var ? var->value->str_value.length() : 0);
-
- switch (log_type) {
- case QUERY_LOG_SLOW:
- file_log= logger.get_slow_log_file_handler();
- break;
- case QUERY_LOG_GENERAL:
- file_log= logger.get_log_file_handler();
- break;
- default:
- MY_ASSERT_UNREACHABLE();
- }
-
- if (!old_value)
- {
- old_value= make_default_log_name(buff, log_ext);
- str_length= strlen(old_value);
- }
- if (!(res= my_strndup(old_value, str_length, MYF(MY_FAE+MY_WME))))
- {
- result= 1;
- goto err;
- }
-
- logger.lock_exclusive();
-
- if (file_log && log_state)
- file_log->close(0);
- if (file_log && log_state)
- {
- switch (log_type) {
- case QUERY_LOG_SLOW:
- file_log->open_slow_log(res);
- break;
- case QUERY_LOG_GENERAL:
- file_log->open_query_log(res);
- break;
- default:
- DBUG_ASSERT(0);
- }
- }
-
- logger.unlock();
-
- /* update global variable */
- pthread_mutex_lock(&LOCK_global_system_variables);
- old_value= var_str->value;
- var_str->value= res;
- var_str->value_length= str_length;
- my_free(old_value, MYF(MY_ALLOW_ZERO_PTR));
- pthread_mutex_unlock(&LOCK_global_system_variables);
-
-err:
- return result;
-}
-
-
-static bool sys_update_general_log_path(THD *thd, set_var * var)
-{
- return update_sys_var_str_path(thd, &sys_var_general_log_path,
- var, ".log", opt_log, QUERY_LOG_GENERAL);
-}
-
-
-static void sys_default_general_log_path(THD *thd, enum_var_type type)
-{
- (void) update_sys_var_str_path(thd, &sys_var_general_log_path,
- 0, ".log", opt_log, QUERY_LOG_GENERAL);
-}
-
-
-static bool sys_update_slow_log_path(THD *thd, set_var * var)
-{
- return update_sys_var_str_path(thd, &sys_var_slow_log_path,
- var, "-slow.log", opt_slow_log,
- QUERY_LOG_SLOW);
-}
-
-
-static void sys_default_slow_log_path(THD *thd, enum_var_type type)
-{
- (void) update_sys_var_str_path(thd, &sys_var_slow_log_path,
- 0, "-slow.log", opt_slow_log,
- QUERY_LOG_SLOW);
-}
-
-
-bool sys_var_log_output::update(THD *thd, set_var *var)
-{
- logger.lock_exclusive();
- logger.init_slow_log(var->save_result.ulong_value);
- logger.init_general_log(var->save_result.ulong_value);
- *value= var->save_result.ulong_value;
- logger.unlock();
- return 0;
-}
-
-
-void sys_var_log_output::set_default(THD *thd, enum_var_type type)
-{
- logger.lock_exclusive();
- logger.init_slow_log(LOG_FILE);
- logger.init_general_log(LOG_FILE);
- *value= LOG_FILE;
- logger.unlock();
-}
-
-
-uchar *sys_var_log_output::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- char buff[256];
- String tmp(buff, sizeof(buff), &my_charset_latin1);
- ulong length;
- ulong val= *value;
-
- tmp.length(0);
- for (uint i= 0; val; val>>= 1, i++)
- {
- if (val & 1)
- {
- tmp.append(log_output_typelib.type_names[i],
- log_output_typelib.type_lengths[i]);
- tmp.append(',');
- }
- }
-
- if ((length= tmp.length()))
- length--;
- return (uchar*) thd->strmake(tmp.ptr(), length);
-}
-
-
-/*****************************************************************************
- Functions to handle SET NAMES and SET CHARACTER SET
-*****************************************************************************/
-
-int set_var_collation_client::check(THD *thd)
-{
- /* Currently, UCS-2 cannot be used as a client character set */
- if (character_set_client->mbminlen > 1)
- {
- my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "character_set_client",
- character_set_client->csname);
- return 1;
- }
- return 0;
-}
-
-int set_var_collation_client::update(THD *thd)
-{
- thd->variables.character_set_client= character_set_client;
- thd->variables.character_set_results= character_set_results;
- thd->variables.collation_connection= collation_connection;
- thd->update_charset();
- thd->protocol_text.init(thd);
- thd->protocol_binary.init(thd);
- return 0;
-}
-
-/****************************************************************************/
-
-bool sys_var_timestamp::check(THD *thd, set_var *var)
-{
- ulonglong sec;
- ulong sec_part;
- char buf[64], *errval= 0;
- if (var->value->get_seconds(&sec, &sec_part))
- errval= llstr(sec, buf);
- else if (sec > TIMESTAMP_MAX_VALUE)
- errval= ullstr(sec, buf);
-
- if (errval)
- {
- my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "timestamp", errval);
- return TRUE;
- }
- var->save_result.ulonglong_value= hrtime_from_time(sec)+sec_part;
- return FALSE;
-}
-
-
-bool sys_var_timestamp::update(THD *thd, set_var *var)
-{
- my_hrtime_t hrtime = { var->save_result.ulonglong_value };
- thd->set_time(hrtime);
- return FALSE;
-}
-
-
-void sys_var_timestamp::set_default(THD *thd, enum_var_type type)
-{
- thd->user_time.val= 0;
-}
-
-
-uchar *sys_var_timestamp::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- thd->sys_var_tmp.double_value= thd->start_time + thd->start_time_sec_part/1e6;
- return (uchar*) &thd->sys_var_tmp.double_value;
-}
-
-
-bool sys_var_last_insert_id::update(THD *thd, set_var *var)
-{
- thd->first_successful_insert_id_in_prev_stmt=
- var->save_result.ulonglong_value;
- return 0;
-}
-
-
-uchar *sys_var_last_insert_id::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- /*
- this tmp var makes it robust againt change of type of
- read_first_successful_insert_id_in_prev_stmt().
- */
- thd->sys_var_tmp.ulonglong_value=
- thd->read_first_successful_insert_id_in_prev_stmt();
- return (uchar*) &thd->sys_var_tmp.ulonglong_value;
-}
-
-
-bool sys_var_insert_id::update(THD *thd, set_var *var)
-{
- thd->force_one_auto_inc_interval(var->save_result.ulonglong_value);
- return 0;
-}
-
-
-uchar *sys_var_insert_id::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- thd->sys_var_tmp.ulonglong_value=
- thd->auto_inc_intervals_forced.minimum();
- return (uchar*) &thd->sys_var_tmp.ulonglong_value;
-}
-
-
-bool sys_var_rand_seed1::update(THD *thd, set_var *var)
-{
- thd->rand.seed1= (ulong) var->save_result.ulonglong_value;
- return 0;
-}
-
-bool sys_var_rand_seed2::update(THD *thd, set_var *var)
-{
- thd->rand.seed2= (ulong) var->save_result.ulonglong_value;
- return 0;
-}
-
-
-bool sys_var_thd_time_zone::check(THD *thd, set_var *var)
-{
- char buff[MAX_TIME_ZONE_NAME_LENGTH];
- String str(buff, sizeof(buff), &my_charset_latin1);
- String *res= var->value->val_str(&str);
-
- if (!(var->save_result.time_zone= my_tz_find(thd, res)))
- {
- my_error(ER_UNKNOWN_TIME_ZONE, MYF(0), res ? res->c_ptr() : "NULL");
- return 1;
- }
- return 0;
-}
-
-
-bool sys_var_thd_time_zone::update(THD *thd, set_var *var)
-{
- /* We are using Time_zone object found during check() phase. */
- if (var->type == OPT_GLOBAL)
- {
- pthread_mutex_lock(&LOCK_global_system_variables);
- global_system_variables.time_zone= var->save_result.time_zone;
- pthread_mutex_unlock(&LOCK_global_system_variables);
- }
- else
- thd->variables.time_zone= var->save_result.time_zone;
- return 0;
-}
-
-
-uchar *sys_var_thd_time_zone::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- /*
- We can use ptr() instead of c_ptr() here because String contaning
- time zone name is guaranteed to be zero ended.
- */
- if (type == OPT_GLOBAL)
- return (uchar *)(global_system_variables.time_zone->get_name()->ptr());
- else
- {
- /*
- This is an ugly fix for replication: we don't replicate properly queries
- invoking system variables' values to update tables; but
- CONVERT_TZ(,,@@session.time_zone) is so popular that we make it
- replicable (i.e. we tell the binlog code to store the session
- timezone). If it's the global value which was used we can't replicate
- (binlog code stores session value only).
- */
- thd->time_zone_used= 1;
- return (uchar *)(thd->variables.time_zone->get_name()->ptr());
- }
-}
-
-
-void sys_var_thd_time_zone::set_default(THD *thd, enum_var_type type)
-{
- pthread_mutex_lock(&LOCK_global_system_variables);
- if (type == OPT_GLOBAL)
- {
- if (default_tz_name)
- {
- String str(default_tz_name, &my_charset_latin1);
- /*
- We are guaranteed to find this time zone since its existence
- is checked during start-up.
- */
- global_system_variables.time_zone= my_tz_find(thd, &str);
- }
- else
- global_system_variables.time_zone= my_tz_SYSTEM;
- }
- else
- thd->variables.time_zone= global_system_variables.time_zone;
- pthread_mutex_unlock(&LOCK_global_system_variables);
-}
-
-
-bool sys_var_max_user_conn::check(THD *thd, set_var *var)
-{
- if (var->type == OPT_GLOBAL)
- {
- if (! max_user_connections_checking)
- {
- /*
- We can't change the value of max_user_connections from 0 as then
- connect counting would be wrong.
- */
- my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
- "--max-user-connections=0");
- return TRUE;
- }
- return sys_var_thd::check(thd, var);
- }
- else
- {
- /*
- Per-session values of max_user_connections can't be set directly.
- May be we should have a separate error message for this?
- */
- my_error(ER_GLOBAL_VARIABLE, MYF(0), name);
- return TRUE;
- }
-}
-
-bool sys_var_max_user_conn::update(THD *thd, set_var *var)
-{
- DBUG_ASSERT(var->type == OPT_GLOBAL);
- pthread_mutex_lock(&LOCK_global_system_variables);
- max_user_connections= (int) var->save_result.ulonglong_value;
- pthread_mutex_unlock(&LOCK_global_system_variables);
- return 0;
-}
-
-
-void sys_var_max_user_conn::set_default(THD *thd, enum_var_type type)
-{
- DBUG_ASSERT(type == OPT_GLOBAL);
- pthread_mutex_lock(&LOCK_global_system_variables);
- max_user_connections= (ulong) option_limits->def_value;
- pthread_mutex_unlock(&LOCK_global_system_variables);
-}
-
-
-uchar *sys_var_max_user_conn::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- if (type != OPT_GLOBAL &&
- thd->user_connect && thd->user_connect->user_resources.user_conn)
- return (uchar*) &(thd->user_connect->user_resources.user_conn);
- return (uchar*) &(max_user_connections);
-}
-
-
-bool sys_var_thd_ulong_session_readonly::check(THD *thd, set_var *var)
-{
- if (var->type != OPT_GLOBAL)
- {
- my_error(ER_VARIABLE_IS_READONLY, MYF(0), "SESSION", name, "GLOBAL");
- return TRUE;
- }
-
- return sys_var_thd_ulong::check(thd, var);
-}
-
-
-bool sys_var_thd_lc_time_names::check(THD *thd, set_var *var)
-{
- MY_LOCALE *locale_match;
-
- if (var->value->result_type() == INT_RESULT)
- {
- if (!(locale_match= my_locale_by_number((uint) var->value->val_int())))
- {
- char buf[20];
- int10_to_str((int) var->value->val_int(), buf, -10);
- my_printf_error(ER_UNKNOWN_ERROR, "Unknown locale: '%s'", MYF(0), buf);
- return 1;
- }
- }
- else // STRING_RESULT
- {
- char buff[6];
- String str(buff, sizeof(buff), &my_charset_latin1), *res;
- if (!(res=var->value->val_str(&str)))
- {
- my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, "NULL");
- return 1;
- }
- const char *locale_str= res->c_ptr_safe();
- if (!(locale_match= my_locale_by_name(locale_str)))
- {
- my_printf_error(ER_UNKNOWN_ERROR,
- "Unknown locale: '%s'", MYF(0), locale_str);
- return 1;
- }
- }
-
- var->save_result.locale_value= locale_match;
- return 0;
-}
-
-
-bool sys_var_thd_lc_time_names::update(THD *thd, set_var *var)
-{
- if (var->type == OPT_GLOBAL)
- global_system_variables.lc_time_names= var->save_result.locale_value;
- else
- thd->variables.lc_time_names= var->save_result.locale_value;
- return 0;
-}
-
-
-uchar *sys_var_thd_lc_time_names::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- return type == OPT_GLOBAL ?
- (uchar *) global_system_variables.lc_time_names->name :
- (uchar *) thd->variables.lc_time_names->name;
-}
-
-
-void sys_var_thd_lc_time_names::set_default(THD *thd, enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- global_system_variables.lc_time_names= my_default_lc_time_names;
- else
- thd->variables.lc_time_names= global_system_variables.lc_time_names;
-}
-
-/*
- Handling of microseconds given as seconds.part_seconds
-
- NOTES
- The argument to long query time is in seconds in decimal
- which is converted to ulonglong integer holding microseconds for storage.
- This is used for handling long_query_time
-*/
-
-bool sys_var_microseconds::update(THD *thd, set_var *var)
-{
- double num= var->value->val_real();
- longlong microseconds;
- if (num > (double) option_limits->max_value)
- num= (double) option_limits->max_value;
- if (num < (double) option_limits->min_value)
- num= (double) option_limits->min_value;
- microseconds= (longlong) (num * 1000000.0 + 0.5);
- if (var->type == OPT_GLOBAL)
- {
- pthread_mutex_lock(&LOCK_global_system_variables);
- (global_system_variables.*offset)= microseconds;
- pthread_mutex_unlock(&LOCK_global_system_variables);
- }
- else
- thd->variables.*offset= microseconds;
- return 0;
-}
-
-
-void sys_var_microseconds::set_default(THD *thd, enum_var_type type)
-{
- longlong microseconds= (longlong) (option_limits->def_value * 1000000.0);
- if (type == OPT_GLOBAL)
- {
- pthread_mutex_lock(&LOCK_global_system_variables);
- global_system_variables.*offset= microseconds;
- pthread_mutex_unlock(&LOCK_global_system_variables);
- }
- else
- thd->variables.*offset= microseconds;
-}
-
-
-uchar *sys_var_microseconds::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- thd->sys_var_tmp.double_value= (double) ((type == OPT_GLOBAL) ?
- global_system_variables.*offset :
- thd->variables.*offset) / 1000000.0;
- return (uchar*) &thd->sys_var_tmp.double_value;
-}
-
-
-/*
- Functions to update thd->options bits
-*/
-
-static bool set_option_bit(THD *thd, set_var *var)
-{
- sys_var_thd_bit *sys_var= ((sys_var_thd_bit*) var->var);
- if ((var->save_result.ulong_value != 0) == sys_var->reverse)
- thd->options&= ~sys_var->bit_flag;
- else
- thd->options|= sys_var->bit_flag;
- return 0;
-}
-
-/*
- Functions to be only used to update thd->options OPTION_BIN_LOG bit
-*/
-static bool set_option_log_bin_bit(THD *thd, set_var *var)
-{
- set_option_bit(thd, var);
- if (!thd->in_sub_stmt)
- thd->sql_log_bin_toplevel= thd->options & OPTION_BIN_LOG;
- return 0;
-}
-
-static bool set_option_autocommit(THD *thd, set_var *var)
-{
- ulonglong new_options= thd->options;
-
- /* The test is negative as the flag we use is NOT autocommit */
- if (var->save_result.ulong_value)
- new_options&= ~OPTION_NOT_AUTOCOMMIT;
- else
- new_options|= OPTION_NOT_AUTOCOMMIT;
-
- if ((new_options ^ thd->options) & OPTION_NOT_AUTOCOMMIT)
- {
- if ((thd->options & OPTION_NOT_AUTOCOMMIT))
- {
- if (end_active_trans(thd))
- return 1;
- thd->server_status|= SERVER_STATUS_AUTOCOMMIT;
- thd->options= new_options;
- }
- else
- {
- thd->options= new_options;
- thd->transaction.all.modified_non_trans_table= FALSE;
- thd->server_status&= ~SERVER_STATUS_AUTOCOMMIT;
- }
- }
- return 0;
-}
-
-static int check_log_update(THD *thd, set_var *var)
-{
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (!(thd->security_ctx->master_access & SUPER_ACL))
- {
- my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
- return 1;
- }
-#endif
- return 0;
-}
-
-static bool set_log_update(THD *thd, set_var *var)
-{
- /*
- The update log is not supported anymore since 5.0.
- See sql/mysqld.cc/, comments in function init_server_components() for an
- explaination of the different warnings we send below
- */
-
- if (opt_sql_bin_update)
- {
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_UPDATE_LOG_DEPRECATED_TRANSLATED,
- ER(ER_UPDATE_LOG_DEPRECATED_TRANSLATED));
- }
- else
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_UPDATE_LOG_DEPRECATED_IGNORED,
- ER(ER_UPDATE_LOG_DEPRECATED_IGNORED));
- set_option_bit(thd, var);
- return 0;
-}
-
-
-static int check_pseudo_thread_id(THD *thd, set_var *var)
-{
- var->save_result.ulonglong_value= var->value->val_int();
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (thd->security_ctx->master_access & SUPER_ACL)
- return 0;
- else
- {
- my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
- return 1;
- }
-#else
- return 0;
-#endif
-}
-
-static uchar *get_warning_count(THD *thd)
-{
- thd->sys_var_tmp.long_value=
- (thd->warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_NOTE] +
- thd->warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_ERROR] +
- thd->warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_WARN]);
- return (uchar*) &thd->sys_var_tmp.long_value;
-}
-
-static uchar *get_error_count(THD *thd)
-{
- thd->sys_var_tmp.long_value=
- thd->warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_ERROR];
- return (uchar*) &thd->sys_var_tmp.long_value;
-}
-
-
-/**
- Get the tmpdir that was specified or chosen by default.
-
- This is necessary because if the user does not specify a temporary
- directory via the command line, one is chosen based on the environment
- or system defaults. But we can't just always use mysql_tmpdir, because
- that is actually a call to my_tmpdir() which cycles among possible
- temporary directories.
-
- @param thd thread handle
-
- @retval
- ptr pointer to NUL-terminated string
-*/
-static uchar *get_tmpdir(THD *thd)
-{
- if (opt_mysql_tmpdir)
- return (uchar *)opt_mysql_tmpdir;
- return (uchar*)mysql_tmpdir;
-}
-
-static uchar *get_myisam_mmap_size(THD *thd)
-{
- return (uchar *)&myisam_mmap_size;
-}
-
-static uchar *in_transaction(THD *thd)
-{
- thd->sys_var_tmp.my_bool_value=
- test(thd->server_status & SERVER_STATUS_IN_TRANS);
- return (uchar*) &thd->sys_var_tmp.my_bool_value;
-}
-
/****************************************************************************
Main handling of variables:
- Initialisation
@@ -3436,132 +390,86 @@ static uchar *in_transaction(THD *thd)
****************************************************************************/
/**
- Find variable name in option my_getopt structure used for
- command line args.
+ Add variables to the dynamic hash of system variables
- @param opt option structure array to search in
- @param name variable name
+ @param first Pointer to first system variable to add
@retval
- 0 Error
- @retval
- ptr pointer to option structure
-*/
-
-static struct my_option *find_option(struct my_option *opt, const char *name)
-{
- uint length=strlen(name);
- for (; opt->name; opt++)
- {
- if (!getopt_compare_strings(opt->name, name, length) &&
- !opt->name[length])
- {
- /*
- Only accept the option if one can set values through it.
- If not, there is no default value or limits in the option.
- */
- return (opt->value) ? opt : 0;
- }
- }
- return 0;
-}
-
-
-/**
- Return variable name and length for hashing of variables.
-*/
-
-static uchar *get_sys_var_length(const sys_var *var, size_t *length,
- my_bool first)
-{
- *length= var->name_length;
- return (uchar*) var->name;
-}
-
-
-/*
- Add variables to the dynamic hash of system variables
-
- SYNOPSIS
- mysql_add_sys_var_chain()
- first Pointer to first system variable to add
- long_opt (optional)command line arguments may be tied for limit checks.
-
- RETURN VALUES
0 SUCCESS
+ @retval
otherwise FAILURE
*/
-int mysql_add_sys_var_chain(sys_var *first, struct my_option *long_options)
+int mysql_add_sys_var_chain(sys_var *first)
{
sys_var *var;
-
+
/* A write lock should be held on LOCK_system_variables_hash */
-
+
for (var= first; var; var= var->next)
{
- var->name_length= strlen(var->name);
/* this fails if there is a conflicting variable name. see HASH_UNIQUE */
if (my_hash_insert(&system_variable_hash, (uchar*) var))
+ {
+ fprintf(stderr, "*** duplicate variable name '%s' ?\n", var->name.str);
goto error;
- if (long_options)
- var->option_limits= find_option(long_options, var->name);
+ }
}
return 0;
error:
for (; first != var; first= first->next)
- hash_delete(&system_variable_hash, (uchar*) first);
+ my_hash_delete(&system_variable_hash, (uchar*) first);
return 1;
}
-
-
+
+
/*
Remove variables to the dynamic hash of system variables
-
+
SYNOPSIS
mysql_del_sys_var_chain()
first Pointer to first system variable to remove
-
+
RETURN VALUES
0 SUCCESS
otherwise FAILURE
*/
-
+
int mysql_del_sys_var_chain(sys_var *first)
{
int result= 0;
-
- /* A write lock should be held on LOCK_system_variables_hash */
-
+
+ mysql_rwlock_wrlock(&LOCK_system_variables_hash);
for (sys_var *var= first; var; var= var->next)
- result|= hash_delete(&system_variable_hash, (uchar*) var);
+ result|= my_hash_delete(&system_variable_hash, (uchar*) var);
+ mysql_rwlock_unlock(&LOCK_system_variables_hash);
return result;
}
-
-
+
+
static int show_cmp(SHOW_VAR *a, SHOW_VAR *b)
{
return strcmp(a->name, b->name);
}
-
-
-/*
+
+
+/**
Constructs an array of system variables for display to the user.
-
- SYNOPSIS
- enumerate_sys_vars()
- thd current thread
- sorted If TRUE, the system variables should be sorted
-
- RETURN VALUES
+
+ @param thd current thread
+ @param sorted If TRUE, the system variables should be sorted
+ @param type OPT_GLOBAL or OPT_SESSION for SHOW GLOBAL|SESSION VARIABLES
+
+ @retval
pointer Array of SHOW_VAR elements for display
+ @retval
NULL FAILURE
*/
-SHOW_VAR* enumerate_sys_vars(THD *thd, bool sorted)
+SHOW_VAR* enumerate_sys_vars(THD *thd, bool sorted, enum enum_var_type type)
{
int count= system_variable_hash.records, i;
int size= sizeof(SHOW_VAR) * (count + 1);
@@ -3573,8 +481,13 @@ SHOW_VAR* enumerate_sys_vars(THD *thd, bool sorted)
for (i= 0; i < count; i++)
{
- sys_var *var= (sys_var*) hash_element(&system_variable_hash, i);
- show->name= var->name;
+ sys_var *var= (sys_var*) my_hash_element(&system_variable_hash, i);
+
+ // don't show session-only variables in SHOW GLOBAL VARIABLES
+ if (type == OPT_GLOBAL && var->check_type(type))
+ continue;
+
+ show->name= var->name.str;
show->value= (char*) var;
show->type= SHOW_SYS;
show++;
@@ -3582,79 +495,29 @@ SHOW_VAR* enumerate_sys_vars(THD *thd, bool sorted)
/* sort into order */
if (sorted)
- my_qsort(result, count, sizeof(SHOW_VAR),
+ my_qsort(result, show-result, sizeof(SHOW_VAR),
(qsort_cmp) show_cmp);
-
+
/* make last element empty */
bzero(show, sizeof(SHOW_VAR));
}
return result;
}
-
-/*
- Initialize the system variables
-
- SYNOPSIS
- set_var_init()
-
- RETURN VALUES
- 0 SUCCESS
- otherwise FAILURE
-*/
-
-int set_var_init()
-{
- uint count= 0;
- DBUG_ENTER("set_var_init");
-
- for (sys_var *var=vars.first; var; var= var->next, count++) ;
-
- if (hash_init(&system_variable_hash, system_charset_info, count, 0,
- 0, (hash_get_key) get_sys_var_length, 0, HASH_UNIQUE))
- goto error;
-
- vars.last->next= NULL;
- if (mysql_add_sys_var_chain(vars.first, my_long_options))
- goto error;
-
- /*
- Special cases
- Needed because MySQL can't find the limits for a variable it it has
- a different name than the command line option.
- As these variables are deprecated, this code will disappear soon...
- */
- sys_sql_max_join_size.option_limits= sys_max_join_size.option_limits;
-
- DBUG_RETURN(0);
-
-error:
- fprintf(stderr, "failed to initialize system variables");
- DBUG_RETURN(1);
-}
-
-
-void set_var_free()
-{
- hash_free(&system_variable_hash);
-}
-
-
/**
Find a user set-table variable.
- @param str Name of system variable to find
+ @param str Name of system variable to find
@param length Length of variable. zero means that we should use strlen()
on the variable
- @param no_error Refuse to emit an error, even if one occurred.
@retval
- pointer pointer to variable definitions
+ pointer pointer to variable definitions
@retval
- 0 Unknown variable (error message is given)
+ 0 Unknown variable (error message is given)
*/
-sys_var *intern_find_sys_var(const char *str, uint length, bool no_error)
+sys_var *intern_find_sys_var(const char *str, uint length)
{
sys_var *var;
@@ -3662,11 +525,8 @@ sys_var *intern_find_sys_var(const char *str, uint length, bool no_error)
This function is only called from the sql_plugin.cc.
A lock on LOCK_system_variable_hash should be held
*/
- var= (sys_var*) hash_search(&system_variable_hash,
- (uchar*) str, length ? length : strlen(str));
- if (!(var || no_error))
- my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), (char*) str);
-
+ var= (sys_var*) my_hash_search(&system_variable_hash,
+ (uchar*) str, length ? length : strlen(str));
return var;
}
@@ -3680,13 +540,13 @@ sys_var *intern_find_sys_var(const char *str, uint length, bool no_error)
This should ensure that in all normal cases none all or variables are
updated.
- @param THD Thread id
+ @param THD Thread id
@param var_list List of variables to update
@retval
- 0 ok
+ 0 ok
@retval
- 1 ERROR, message sent (normally no variables was updated)
+ 1 ERROR, message sent (normally no variables was updated)
@retval
-1 ERROR, message not sent
*/
@@ -3715,36 +575,6 @@ err:
DBUG_RETURN(error);
}
-
-/**
- Say if all variables set by a SET support the ONE_SHOT keyword
- (currently, only character set and collation do; later timezones
- will).
-
- @param var_list List of variables to update
-
- @note
- It has a "not_" because it makes faster tests (no need to "!")
-
- @retval
- 0 all variables of the list support ONE_SHOT
- @retval
- 1 at least one does not support ONE_SHOT
-*/
-
-bool not_all_support_one_shot(List<set_var_base> *var_list)
-{
- List_iterator_fast<set_var_base> it(*var_list);
- set_var_base *var;
- while ((var= it++))
- {
- if (var->no_support_one_shot())
- return 1;
- }
- return 0;
-}
-
-
/*****************************************************************************
Functions to handle SET mysql_internal_variable=const_expr
*****************************************************************************/
@@ -3755,42 +585,36 @@ bool not_all_support_one_shot(List<set_var_base> *var_list)
@param thd Thread handler
@return status code
- @retval -1 Failure
- @retval 0 Success
-*/
+ @retval -1 Failure
+ @retval 0 Success
+ */
int set_var::check(THD *thd)
{
+ var->do_deprecated_warning(thd);
if (var->is_readonly())
{
- my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0), var->name, "read only");
+ my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0), var->name.str, "read only");
return -1;
}
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;
/* value is a NULL pointer if we are using SET ... = DEFAULT */
if (!value)
- {
- if (var->check_default(type))
- {
- my_error(ER_NO_DEFAULT, MYF(0), var->name);
- return -1;
- }
return 0;
- }
if ((!value->fixed &&
value->fix_fields(thd, &value)) || value->check_cols(1))
return -1;
if (var->check_update_type(value->result_type()))
{
- my_error(ER_WRONG_TYPE_FOR_VAR, MYF(0), var->name);
+ my_error(ER_WRONG_TYPE_FOR_VAR, MYF(0), var->name.str);
return -1;
}
return var->check(thd, this) ? -1 : 0;
@@ -3800,12 +624,12 @@ int set_var::check(THD *thd)
/**
Check variable, but without assigning value (used by PS).
- @param thd thread handler
+ @param thd thread handler
@retval
- 0 ok
+ 0 ok
@retval
- 1 ERROR, message sent (normally no variables was updated)
+ 1 ERROR, message sent (normally no variables was updated)
@retval
-1 ERROR, message not sent
*/
@@ -3830,7 +654,7 @@ int set_var::light_check(THD *thd)
Update variable
@param thd thread handler
- @returns 0|1 ok or ERROR
+ @returns 0|1 ok or ERROR
@note ERROR can be only due to abnormal operations involving
the server's execution evironment such as
@@ -3840,13 +664,7 @@ int set_var::light_check(THD *thd)
*/
int set_var::update(THD *thd)
{
- if (!value)
- var->set_default(thd, type);
- else if (var->update(thd, this))
- return -1; // should never happen
- if (var->after_update)
- (*var->after_update)(thd, type);
- return 0;
+ return value ? var->update(thd, this) : var->set_default(thd, this);
}
@@ -3861,19 +679,19 @@ int set_var_user::check(THD *thd)
0 can be passed as last argument (reference on item)
*/
return (user_var_item->fix_fields(thd, (Item**) 0) ||
- user_var_item->check(0)) ? -1 : 0;
+ user_var_item->check(0)) ? -1 : 0;
}
/**
Check variable, but without assigning value (used by PS).
- @param thd thread handler
+ @param thd thread handler
@retval
- 0 ok
+ 0 ok
@retval
- 1 ERROR, message sent (normally no variables was updated)
+ 1 ERROR, message sent (normally no variables was updated)
@retval
-1 ERROR, message not sent
*/
@@ -3922,9 +740,9 @@ int set_var_password::check(THD *thd)
}
if (!user->user.str)
{
- DBUG_ASSERT(thd->security_ctx->priv_user);
- user->user.str= (char *) thd->security_ctx->priv_user;
- user->user.length= strlen(thd->security_ctx->priv_user);
+ DBUG_ASSERT(thd->security_ctx->user);
+ user->user.str= (char *) thd->security_ctx->user;
+ user->user.length= strlen(thd->security_ctx->user);
}
/* Returns 1 as the function sends error to client */
return check_change_password(thd, user->host.str, user->user.str,
@@ -3939,632 +757,36 @@ int set_var_password::update(THD *thd)
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/* Returns 1 as the function sends error to client */
return change_password(thd, user->host.str, user->user.str, password) ?
- 1 : 0;
+ 1 : 0;
#else
return 0;
#endif
}
-/****************************************************************************
- Functions to handle log_slow_filter
-****************************************************************************/
-
-/* Ensure that the proper bits are set for easy test of logging */
-static void fix_sys_log_slow_filter(THD *thd, enum_var_type type)
-{
- /* Maintain everything with filters */
- opt_log_slow_admin_statements= 1;
- if (type == OPT_GLOBAL)
- global_system_variables.log_slow_filter=
- fix_log_slow_filter(global_system_variables.log_slow_filter);
- else
- thd->variables.log_slow_filter=
- fix_log_slow_filter(thd->variables.log_slow_filter);
-}
-
-
-/****************************************************************************
- Functions to handle table_type
-****************************************************************************/
-
-/* Based upon sys_var::check_enum() */
-
-bool sys_var_thd_storage_engine::check(THD *thd, set_var *var)
-{
- char buff[STRING_BUFFER_USUAL_SIZE];
- const char *value;
- String str(buff, sizeof(buff) - 1, &my_charset_latin1), *res;
-
- var->save_result.plugin= NULL;
- if (var->value->result_type() == STRING_RESULT)
- {
- LEX_STRING engine_name;
- handlerton *hton;
- if (!(res=var->value->val_str(&str)) ||
- !(engine_name.str= (char *)res->ptr()) ||
- !(engine_name.length= res->length()) ||
- !(var->save_result.plugin= ha_resolve_by_name(thd, &engine_name)) ||
- !(hton= plugin_data(var->save_result.plugin, handlerton *)) ||
- ha_checktype(thd, ha_legacy_type(hton), 1, 0) != hton)
- {
- value= res ? res->c_ptr() : "NULL";
- goto err;
- }
- return 0;
- }
- value= "unknown";
-
-err:
- my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), value);
- return 1;
-}
-
-
-uchar *sys_var_thd_storage_engine::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- uchar* result;
- handlerton *hton;
- LEX_STRING *engine_name;
- plugin_ref plugin= thd->variables.*offset;
- if (type == OPT_GLOBAL)
- plugin= my_plugin_lock(thd, global_system_variables.*offset);
- hton= plugin_data(plugin, handlerton*);
- engine_name= hton_name(hton);
- result= (uchar *) thd->strmake(engine_name->str, engine_name->length);
- if (type == OPT_GLOBAL)
- plugin_unlock(thd, plugin);
- return result;
-}
-
-
-void sys_var_thd_storage_engine::set_default(THD *thd, enum_var_type type)
-{
- plugin_ref old_value, new_value, *value;
- if (type == OPT_GLOBAL)
- {
- value= &(global_system_variables.*offset);
- new_value= ha_lock_engine(NULL, myisam_hton);
- }
- else
- {
- value= &(thd->variables.*offset);
- new_value= my_plugin_lock(NULL, global_system_variables.*offset);
- }
- DBUG_ASSERT(new_value);
- old_value= *value;
- *value= new_value;
- plugin_unlock(NULL, old_value);
-}
-
-
-bool sys_var_thd_storage_engine::update(THD *thd, set_var *var)
-{
- plugin_ref *value= &(global_system_variables.*offset), old_value;
- if (var->type != OPT_GLOBAL)
- value= &(thd->variables.*offset);
- old_value= *value;
- if (old_value != var->save_result.plugin)
- {
- *value= my_plugin_lock(NULL, var->save_result.plugin);
- plugin_unlock(NULL, old_value);
- }
- return 0;
-}
-
-void sys_var_thd_table_type::warn_deprecated(THD *thd)
-{
- WARN_DEPRECATED(thd, "6.0", "@@table_type", "'@@storage_engine'");
-}
-
-void sys_var_thd_table_type::set_default(THD *thd, enum_var_type type)
-{
- warn_deprecated(thd);
- sys_var_thd_storage_engine::set_default(thd, type);
-}
-
-bool sys_var_thd_table_type::update(THD *thd, set_var *var)
-{
- warn_deprecated(thd);
- return sys_var_thd_storage_engine::update(thd, var);
-}
-
-
-/****************************************************************************
- Functions to handle sql_mode
-****************************************************************************/
-
-void fix_sql_mode_var(THD *thd, enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- global_system_variables.sql_mode=
- fix_sql_mode(global_system_variables.sql_mode);
- else
- {
- thd->variables.sql_mode= fix_sql_mode(thd->variables.sql_mode);
- /*
- Update thd->server_status
- */
- if (thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES)
- thd->server_status|= SERVER_STATUS_NO_BACKSLASH_ESCAPES;
- else
- thd->server_status&= ~SERVER_STATUS_NO_BACKSLASH_ESCAPES;
- }
-}
-
-/** Map database specific bits to function bits. */
-
-ulong fix_sql_mode(ulong sql_mode)
-{
- /*
- Note that we dont set
- MODE_NO_KEY_OPTIONS | MODE_NO_TABLE_OPTIONS | MODE_NO_FIELD_OPTIONS
- to allow one to get full use of MySQL in this mode.
- */
-
- if (sql_mode & MODE_ANSI)
- {
- sql_mode|= (MODE_REAL_AS_FLOAT | MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES |
- MODE_IGNORE_SPACE);
- /*
- MODE_ONLY_FULL_GROUP_BY removed from ANSI mode because it is currently
- overly restrictive (see BUG#8510).
- */
- }
- if (sql_mode & MODE_ORACLE)
- 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);
- if (sql_mode & MODE_MSSQL)
- sql_mode|= (MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES |
- MODE_IGNORE_SPACE |
- MODE_NO_KEY_OPTIONS | MODE_NO_TABLE_OPTIONS |
- MODE_NO_FIELD_OPTIONS);
- if (sql_mode & MODE_POSTGRESQL)
- sql_mode|= (MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES |
- MODE_IGNORE_SPACE |
- MODE_NO_KEY_OPTIONS | MODE_NO_TABLE_OPTIONS |
- MODE_NO_FIELD_OPTIONS);
- if (sql_mode & MODE_DB2)
- sql_mode|= (MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES |
- MODE_IGNORE_SPACE |
- MODE_NO_KEY_OPTIONS | MODE_NO_TABLE_OPTIONS |
- MODE_NO_FIELD_OPTIONS);
- if (sql_mode & MODE_MAXDB)
- 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);
- if (sql_mode & MODE_MYSQL40)
- sql_mode|= MODE_HIGH_NOT_PRECEDENCE;
- if (sql_mode & MODE_MYSQL323)
- sql_mode|= MODE_HIGH_NOT_PRECEDENCE;
- if (sql_mode & MODE_TRADITIONAL)
- sql_mode|= (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES |
- MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE |
- MODE_ERROR_FOR_DIVISION_BY_ZERO | MODE_NO_AUTO_CREATE_USER);
- return sql_mode;
-}
-
-
-bool
-sys_var_thd_optimizer_switch::
-symbolic_mode_representation(THD *thd, ulonglong val, LEX_STRING *rep)
-{
- char buff[STRING_BUFFER_USUAL_SIZE*18];
- String tmp(buff, sizeof(buff), &my_charset_latin1);
- int i;
- ulonglong bit;
- tmp.length(0);
-
- for (i= 0, bit=1; bit != OPTIMIZER_SWITCH_LAST; i++, bit= bit << 1)
- {
- tmp.append(optimizer_switch_typelib.type_names[i],
- optimizer_switch_typelib.type_lengths[i]);
- tmp.append('=');
- tmp.append((val & bit)? "on":"off");
- tmp.append(',');
- }
-
- if (tmp.length())
- tmp.length(tmp.length() - 1); /* trim the trailing comma */
-
- rep->str= thd->strmake(tmp.ptr(), tmp.length());
-
- rep->length= rep->str ? tmp.length() : 0;
-
- return rep->length != tmp.length();
-}
-
-
-uchar *sys_var_thd_optimizer_switch::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- LEX_STRING opts;
- ulonglong val= ((type == OPT_GLOBAL) ? global_system_variables.*offset :
- thd->variables.*offset);
- (void) symbolic_mode_representation(thd, val, &opts);
- return (uchar *) opts.str;
-}
-
-
-/*
- Check (and actually parse) string representation of @@optimizer_switch.
-*/
-
-bool sys_var_thd_optimizer_switch::check(THD *thd, set_var *var)
-{
- bool not_used;
- char buff[STRING_BUFFER_USUAL_SIZE], *error= 0;
- uint error_len= 0;
- String str(buff, sizeof(buff), system_charset_info), *res;
-
- if (!(res= var->value->val_str(&str)))
- {
- strmov(buff, "NULL");
- goto err;
- }
-
- if (res->length() == 0)
- {
- buff[0]= 0;
- goto err;
- }
-
- var->save_result.ulong_value=
- (ulong)find_set_from_flags(&optimizer_switch_typelib,
- optimizer_switch_typelib.count,
- thd->variables.optimizer_switch,
- global_system_variables.optimizer_switch,
- res->ptr(), res->length(), NULL,
- &error, &error_len, &not_used);
- if (error_len)
- {
- strmake(buff, error, min(sizeof(buff) - 1, error_len));
- goto err;
- }
- return FALSE;
-err:
- my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, buff);
- return TRUE;
-}
-
-
-void sys_var_thd_optimizer_switch::set_default(THD *thd, enum_var_type type)
-{
- if (type == OPT_GLOBAL)
- global_system_variables.*offset= OPTIMIZER_SWITCH_DEFAULT;
- else
- thd->variables.*offset= global_system_variables.*offset;
-}
-
-/****************************************************************************
- Named list handling
-****************************************************************************/
-
-uchar* find_named(I_List<NAMED_LIST> *list, const char *name, uint length,
- NAMED_LIST **found)
-{
- I_List_iterator<NAMED_LIST> it(*list);
- NAMED_LIST *element;
- while ((element= it++))
- {
- if (element->cmp(name, length))
- {
- if (found)
- *found= element;
- return element->data;
- }
- }
- return 0;
-}
-
-
-void delete_elements(I_List<NAMED_LIST> *list,
- void (*free_element)(const char *name, uchar*))
-{
- NAMED_LIST *element;
- DBUG_ENTER("delete_elements");
- while ((element= list->get()))
- {
- (*free_element)(element->name, element->data);
- delete element;
- }
- DBUG_VOID_RETURN;
-}
-
-
-/* Key cache functions */
-
-static KEY_CACHE *create_key_cache(const char *name, uint length)
-{
- KEY_CACHE *key_cache;
- DBUG_ENTER("create_key_cache");
- DBUG_PRINT("enter",("name: %.*s", length, name));
-
- if ((key_cache= (KEY_CACHE*) my_malloc(sizeof(KEY_CACHE),
- MYF(MY_ZEROFILL | MY_WME))))
- {
- if (!new NAMED_LIST(&key_caches, name, length, (uchar*) key_cache))
- {
- my_free((char*) key_cache, MYF(0));
- key_cache= 0;
- }
- else
- {
- /*
- Set default values for a key cache
- The values in dflt_key_cache_var is set by my_getopt() at startup
-
- We don't set 'buff_size' as this is used to enable the key cache
- */
- key_cache->param_block_size= dflt_key_cache_var.param_block_size;
- key_cache->param_division_limit= dflt_key_cache_var.param_division_limit;
- key_cache->param_age_threshold= dflt_key_cache_var.param_age_threshold;
- key_cache->param_partitions= dflt_key_cache_var.param_partitions;
- }
- }
- DBUG_RETURN(key_cache);
-}
-
-
-KEY_CACHE *get_or_create_key_cache(const char *name, uint length)
-{
- LEX_STRING key_cache_name;
- KEY_CACHE *key_cache;
-
- key_cache_name.str= (char *) name;
- key_cache_name.length= length;
- pthread_mutex_lock(&LOCK_global_system_variables);
- if (!(key_cache= get_key_cache(&key_cache_name)))
- key_cache= create_key_cache(name, length);
- pthread_mutex_unlock(&LOCK_global_system_variables);
- return key_cache;
-}
-
-
-void free_key_cache(const char *name, KEY_CACHE *key_cache)
-{
- ha_end_key_cache(key_cache);
- my_free((char*) key_cache, MYF(0));
-}
-
-
-bool process_key_caches(process_key_cache_t func)
-{
- I_List_iterator<NAMED_LIST> it(key_caches);
- NAMED_LIST *element;
-
- while ((element= it++))
- {
- KEY_CACHE *key_cache= (KEY_CACHE *) element->data;
- func(element->name, key_cache);
- }
- return 0;
-}
-
-
-void sys_var_trust_routine_creators::warn_deprecated(THD *thd)
-{
- WARN_DEPRECATED(thd, VER_CELOSIA, "@@log_bin_trust_routine_creators",
- "'@@log_bin_trust_function_creators'");
-}
-
-void sys_var_trust_routine_creators::set_default(THD *thd, enum_var_type type)
-{
- warn_deprecated(thd);
- sys_var_bool_ptr::set_default(thd, type);
-}
-
-bool sys_var_trust_routine_creators::update(THD *thd, set_var *var)
-{
- warn_deprecated(thd);
- return sys_var_bool_ptr::update(thd, var);
-}
-
-bool sys_var_opt_readonly::update(THD *thd, set_var *var)
-{
- bool result;
-
- DBUG_ENTER("sys_var_opt_readonly::update");
-
- /* Prevent self dead-lock */
- if (thd->locked_tables || thd->active_transaction())
- {
- my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
- DBUG_RETURN(true);
- }
-
- if (thd->global_read_lock)
- {
- /*
- This connection already holds the global read lock.
- This can be the case with:
- - FLUSH TABLES WITH READ LOCK
- - SET GLOBAL READ_ONLY = 1
- */
- result= sys_var_bool_ptr::update(thd, var);
- DBUG_RETURN(result);
- }
-
- /*
- Perform a 'FLUSH TABLES WITH READ LOCK'.
- This is a 3 step process:
- - [1] lock_global_read_lock()
- - [2] close_cached_tables()
- - [3] make_global_read_lock_block_commit()
- [1] prevents new connections from obtaining tables locked for write.
- [2] waits until all existing connections close their tables.
- [3] prevents transactions from being committed.
- */
-
- if (lock_global_read_lock(thd))
- DBUG_RETURN(true);
-
- /*
- This call will be blocked by any connection holding a READ or WRITE lock.
- Ideally, we want to wait only for pending WRITE locks, but since:
- con 1> LOCK TABLE T FOR READ;
- con 2> LOCK TABLE T FOR WRITE; (blocked by con 1)
- con 3> SET GLOBAL READ ONLY=1; (blocked by con 2)
- can cause to wait on a read lock, it's required for the client application
- to unlock everything, and acceptable for the server to wait on all locks.
- */
- if ((result= close_cached_tables_set_readonly(thd)))
- goto end_with_read_lock;
-
- if ((result= make_global_read_lock_block_commit(thd)))
- goto end_with_read_lock;
-
- /* Change the opt_readonly system variable, safe because the lock is held */
- result= sys_var_bool_ptr::update(thd, var);
-
-end_with_read_lock:
- /* Release the lock */
- unlock_global_read_lock(thd);
- DBUG_RETURN(result);
-}
-
-
-#ifndef DBUG_OFF
-/* even session variable here requires SUPER, because of -#o,file */
-bool sys_var_thd_dbug::check(THD *thd, set_var *var)
-{
- return check_global_access(thd, SUPER_ACL);
-}
-
-bool sys_var_thd_dbug::update(THD *thd, set_var *var)
-{
- char buf[256];
- String str(buf, sizeof(buf), system_charset_info), *res;
- const char *command;
-
- res= var->value->val_str(&str);
- command= res ? res->c_ptr(): 0;
- if (!command)
- command= "";
-
- if (var->type == OPT_GLOBAL)
- DBUG_SET_INITIAL(command);
- else
- {
- if (_db_is_pushed_())
- {
- /* We have already a local state done with DBUG_PUSH; Modify the state */
- DBUG_SET(command);
- }
- else
- {
- /*
- We are sharing the state with the global state;
- Create a local state for this thread.
- */
- DBUG_PUSH(command);
- }
- }
- return 0;
-}
-
-
-uchar *sys_var_thd_dbug::value_ptr(THD *thd, enum_var_type type, LEX_STRING *b)
-{
- char buf[1024];
- if (type == OPT_GLOBAL)
- {
- DBUG_EXPLAIN_INITIAL(buf, sizeof(buf));
- }
- else
- {
- DBUG_EXPLAIN(buf, sizeof(buf));
- }
- return (uchar*) thd->strdup(buf);
-}
-#endif /* DBUG_OFF */
-
-
-#ifdef HAVE_EVENT_SCHEDULER
-bool sys_var_event_scheduler::check(THD *thd, set_var *var)
-{
- return check_enum(thd, var, &Events::var_typelib);
-}
-
-/*
- The update method of the global variable event_scheduler.
- If event_scheduler is switched from 0 to 1 then the scheduler main
- thread is resumed and if from 1 to 0 the scheduler thread is suspended
-
- SYNOPSIS
- sys_var_event_scheduler::update()
- thd Thread context (unused)
- var The new value
-
- Returns
- FALSE OK
- TRUE Error
-*/
-
-bool
-sys_var_event_scheduler::update(THD *thd, set_var *var)
-{
- int res;
- /* here start the thread if not running. */
- DBUG_ENTER("sys_var_event_scheduler::update");
- DBUG_PRINT("info", ("new_value: %d", (int) var->save_result.ulong_value));
-
- enum Events::enum_opt_event_scheduler
- new_state=
- (enum Events::enum_opt_event_scheduler) var->save_result.ulong_value;
-
- res= Events::switch_event_scheduler_state(new_state);
-
- DBUG_RETURN((bool) res);
-}
-
-
-uchar *sys_var_event_scheduler::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
-{
- return (uchar *) Events::get_opt_event_scheduler_str();
-}
-#endif
-
+/*****************************************************************************
+ Functions to handle SET NAMES and SET CHARACTER SET
+*****************************************************************************/
-int
-check_max_allowed_packet(THD *thd, set_var *var)
+int set_var_collation_client::check(THD *thd)
{
- longlong val= var->value->val_int();
- if (val < (longlong) global_system_variables.net_buffer_length)
+ /* Currently, UCS-2 cannot be used as a client character set */
+ if (!is_supported_parser_charset(character_set_client))
{
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_UNKNOWN_ERROR,
- "The value of 'max_allowed_packet' should be no less than "
- "the value of 'net_buffer_length'");
+ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "character_set_client",
+ character_set_client->csname);
+ return 1;
}
return 0;
}
-
-int
-check_net_buffer_length(THD *thd, set_var *var)
+int set_var_collation_client::update(THD *thd)
{
- longlong val= var->value->val_int();
- if (val > (longlong) global_system_variables.max_allowed_packet)
- {
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_UNKNOWN_ERROR,
- "The value of 'max_allowed_packet' should be no less than "
- "the value of 'net_buffer_length'");
- }
+ thd->variables.character_set_client= character_set_client;
+ thd->variables.character_set_results= character_set_results;
+ thd->variables.collation_connection= collation_connection;
+ thd->update_charset();
+ thd->protocol_text.init(thd);
+ thd->protocol_binary.init(thd);
return 0;
}
-/****************************************************************************
- Used templates
-****************************************************************************/
-
-#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-template class List<set_var_base>;
-template class List_iterator_fast<set_var_base>;
-template class I_List_iterator<NAMED_LIST>;
-#endif
diff --git a/sql/set_var.h b/sql/set_var.h
index d1c96b3df5d..32207e834d9 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -1,4 +1,7 @@
-/* Copyright (c) 2002, 2010, Oracle and/or its affiliates.
+#ifndef SET_VAR_INCLUDED
+#define SET_VAR_INCLUDED
+/* Copyright (c) 2002, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,31 +16,28 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
-/* Classes to support the SET command */
+/**
+ @file
+ "public" interface to sys_var - server configuration variables.
+*/
#ifdef USE_PRAGMA_INTERFACE
-#pragma interface /* gcc class implementation */
+#pragma interface /* gcc class implementation */
#endif
-/****************************************************************************
- Variables that are changable runtime are declared using the
- following classes
-****************************************************************************/
+#include <my_getopt.h>
class sys_var;
class set_var;
-class sys_var_pluginvar; /* opaque */
-typedef struct system_variables SV;
-typedef struct my_locale_st MY_LOCALE;
+class sys_var_pluginvar;
+class PolyLock;
+class Item_func_set_user_var;
-extern TYPELIB bool_typelib, delay_key_write_typelib, sql_mode_typelib,
- optimizer_switch_typelib, slave_exec_mode_typelib;
+// This include needs to be here since item.h requires enum_var_type :-P
+#include "item.h" /* Item */
+#include "sql_class.h" /* THD */
-typedef int (*sys_check_func)(THD *, set_var *);
-typedef bool (*sys_update_func)(THD *, set_var *);
-typedef void (*sys_after_update_func)(THD *,enum_var_type);
-typedef void (*sys_set_default_func)(THD *, enum_var_type);
-typedef uchar *(*sys_value_ptr_func)(THD *thd);
+extern TYPELIB bool_typelib;
struct sys_var_chain
{
@@ -45,1289 +45,181 @@ struct sys_var_chain
sys_var *last;
};
+int mysql_add_sys_var_chain(sys_var *chain);
+int mysql_del_sys_var_chain(sys_var *chain);
+
+/**
+ A class representing one system variable - that is something
+ that can be accessed as @@global.variable_name or @@session.variable_name,
+ visible in SHOW xxx VARIABLES and in INFORMATION_SCHEMA.xxx_VARIABLES,
+ optionally it can be assigned to, optionally it can have a command-line
+ counterpart with the same name.
+*/
class sys_var
{
public:
-
- /**
- Enumeration type to indicate for a system variable whether it will be written to the binlog or not.
- */
- enum Binlog_status_enum
- {
- /* The variable value is not in the binlog. */
- NOT_IN_BINLOG,
- /* The value of the @@session variable is in the binlog. */
- SESSION_VARIABLE_IN_BINLOG
- /*
- Currently, no @@global variable is ever in the binlog, so we
- don't need an enumeration value for that.
- */
- };
-
sys_var *next;
- struct my_option *option_limits; /* Updated by by set_var_init() */
- uint name_length; /* Updated by by set_var_init() */
- const char *name;
-
- sys_after_update_func after_update;
- bool no_support_one_shot;
- /*
- true if the value is in character_set_filesystem,
- false otherwise.
- Note that we can't use a pointer to the charset as the system var is
- instantiated in global scope and the charset pointers are initialized
- later.
- */
- bool is_os_charset;
- sys_var(const char *name_arg, sys_after_update_func func= NULL,
- Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
- :name(name_arg), after_update(func), no_support_one_shot(1),
- is_os_charset (FALSE),
- binlog_status(binlog_status_arg),
- m_allow_empty_value(TRUE)
- {}
- virtual ~sys_var() {}
- void chain_sys_var(sys_var_chain *chain_arg)
- {
- if (chain_arg->last)
- chain_arg->last->next= this;
- else
- chain_arg->first= this;
- chain_arg->last= this;
- }
- virtual bool check(THD *thd, set_var *var);
- bool check_enum(THD *thd, set_var *var, const TYPELIB *enum_names);
- bool check_set(THD *thd, set_var *var, TYPELIB *enum_names);
- static bool make_set(THD *thd, ulonglong sql_mode, TYPELIB *names,
- LEX_STRING *rep);
- bool is_written_to_binlog(enum_var_type type)
- {
- return (type == OPT_SESSION || type == OPT_DEFAULT) &&
- (binlog_status == SESSION_VARIABLE_IN_BINLOG);
- }
- virtual bool update(THD *thd, set_var *var)=0;
- virtual void set_default(THD *thd_arg, enum_var_type type) {}
- virtual SHOW_TYPE show_type() { return SHOW_UNDEF; }
- virtual uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
- { return 0; }
- virtual bool check_type(enum_var_type type)
- { return type != OPT_GLOBAL; } /* Error if not GLOBAL */
- virtual bool check_update_type(Item_result type)
- { return type != INT_RESULT; } /* Assume INT */
- virtual bool check_default(enum_var_type type)
- { return option_limits == 0; }
- virtual bool is_struct() { return 0; }
- virtual bool is_readonly() const { return 0; }
- CHARSET_INFO *charset(THD *thd);
- virtual sys_var_pluginvar *cast_pluginvar() { return 0; }
-
-protected:
- void set_allow_empty_value(bool allow_empty_value)
- {
- m_allow_empty_value= allow_empty_value;
- }
-
-private:
- const Binlog_status_enum binlog_status;
-
- bool m_allow_empty_value;
-};
-
-
-/*
- A base class for all variables that require its access to
- be guarded with a mutex.
-*/
+ LEX_CSTRING name;
+ enum flag_enum { GLOBAL, SESSION, ONLY_SESSION, SCOPE_MASK=1023,
+ READONLY=1024, ALLOCATED=2048, PARSE_EARLY=4096 };
+ /**
+ Enumeration type to indicate for a system variable whether
+ it will be written to the binlog or not.
+ */
+ enum binlog_status_enum { VARIABLE_NOT_IN_BINLOG,
+ SESSION_VARIABLE_IN_BINLOG } binlog_status;
-class sys_var_global: public sys_var
-{
protected:
- pthread_mutex_t *guard;
-public:
- sys_var_global(const char *name_arg, sys_after_update_func after_update_arg,
- pthread_mutex_t *guard_arg)
- :sys_var(name_arg, after_update_arg), guard(guard_arg) {}
-};
-
-
-/*
- A global-only ulong variable that requires its access to be
- protected with a mutex.
-*/
-
-class sys_var_long_ptr_global: public sys_var_global
-{
-public:
- ulong *value;
- sys_var_long_ptr_global(sys_var_chain *chain, const char *name_arg,
- ulong *value_ptr_arg,
- pthread_mutex_t *guard_arg,
- sys_after_update_func after_update_arg= NULL)
- :sys_var_global(name_arg, after_update_arg, guard_arg),
- value(value_ptr_arg)
- { chain_sys_var(chain); }
- bool check(THD *thd, set_var *var);
- bool update(THD *thd, set_var *var);
- void set_default(THD *thd, enum_var_type type);
- SHOW_TYPE show_type() { return SHOW_LONG; }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
- { return (uchar*) value; }
-};
-
-
-/*
- A global ulong variable that is protected by LOCK_global_system_variables
-*/
-
-class sys_var_long_ptr :public sys_var_long_ptr_global
-{
-public:
- sys_var_long_ptr(sys_var_chain *chain, const char *name_arg, ulong *value_ptr,
- sys_after_update_func after_update_arg= NULL);
-};
-
-
-class sys_var_ulonglong_ptr :public sys_var
-{
-public:
- ulonglong *value;
- sys_var_ulonglong_ptr(sys_var_chain *chain, const char *name_arg, ulonglong *value_ptr_arg)
- :sys_var(name_arg),value(value_ptr_arg)
- { chain_sys_var(chain); }
- sys_var_ulonglong_ptr(sys_var_chain *chain, const char *name_arg, ulonglong *value_ptr_arg,
- sys_after_update_func func)
- :sys_var(name_arg,func), value(value_ptr_arg)
- { chain_sys_var(chain); }
- bool check(THD *thd, set_var *var);
- bool update(THD *thd, set_var *var);
- void set_default(THD *thd, enum_var_type type);
- SHOW_TYPE show_type() { return SHOW_LONGLONG; }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
- { return (uchar*) value; }
-};
-
-
-class sys_var_bool_ptr :public sys_var
-{
-public:
- my_bool *value;
- sys_var_bool_ptr(sys_var_chain *chain, const char *name_arg, my_bool *value_arg)
- :sys_var(name_arg),value(value_arg)
- { chain_sys_var(chain); }
- bool check(THD *thd, set_var *var)
- {
- return check_enum(thd, var, &bool_typelib);
- }
- bool update(THD *thd, set_var *var);
- void set_default(THD *thd, enum_var_type type);
- SHOW_TYPE show_type() { return SHOW_MY_BOOL; }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
- { return (uchar*) value; }
- bool check_update_type(Item_result type) { return 0; }
-};
-
-
-class sys_var_bool_ptr_readonly :public sys_var_bool_ptr
-{
-public:
- sys_var_bool_ptr_readonly(sys_var_chain *chain, const char *name_arg,
- my_bool *value_arg)
- :sys_var_bool_ptr(chain, name_arg, value_arg)
- {}
- bool is_readonly() const { return 1; }
-};
-
-
-class sys_var_str :public sys_var
-{
-public:
- char *value; // Pointer to allocated string
- uint value_length;
- sys_check_func check_func;
- sys_update_func update_func;
- sys_set_default_func set_default_func;
- sys_var_str(sys_var_chain *chain, const char *name_arg,
- sys_check_func check_func_arg,
- sys_update_func update_func_arg,
- sys_set_default_func set_default_func_arg,
- char *value_arg)
- :sys_var(name_arg), value(value_arg), check_func(check_func_arg),
- update_func(update_func_arg),set_default_func(set_default_func_arg)
- { chain_sys_var(chain); }
- bool check(THD *thd, set_var *var);
- bool update(THD *thd, set_var *var)
- {
- return (*update_func)(thd, var);
- }
- void set_default(THD *thd, enum_var_type type)
- {
- (*set_default_func)(thd, type);
- }
- SHOW_TYPE show_type() { return SHOW_CHAR; }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
- { return (uchar*) value; }
- bool check_update_type(Item_result type)
- {
- return type != STRING_RESULT; /* Only accept strings */
- }
- bool check_default(enum_var_type type) { return 0; }
-};
-
-
-class sys_var_const_str :public sys_var
-{
-public:
- char *value; // Pointer to const value
- sys_var_const_str(sys_var_chain *chain, const char *name_arg,
- const char *value_arg)
- :sys_var(name_arg), value((char*) value_arg)
- { chain_sys_var(chain); }
- bool check(THD *thd, set_var *var)
- {
- return 1;
- }
- bool update(THD *thd, set_var *var)
- {
- return 1;
- }
- SHOW_TYPE show_type() { return SHOW_CHAR; }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
- {
- return (uchar*) value;
- }
- bool check_update_type(Item_result type)
- {
- return 1;
- }
- bool check_default(enum_var_type type) { return 1; }
- bool is_readonly() const { return 1; }
-};
-
-
-class sys_var_const_os_str: public sys_var_const_str
-{
-public:
- sys_var_const_os_str(sys_var_chain *chain, const char *name_arg,
- const char *value_arg)
- :sys_var_const_str(chain, name_arg, value_arg)
- {
- is_os_charset= TRUE;
- }
-};
-
-
-class sys_var_const_str_ptr :public sys_var
-{
-public:
- char **value; // Pointer to const value
- sys_var_const_str_ptr(sys_var_chain *chain, const char *name_arg, char **value_arg)
- :sys_var(name_arg),value(value_arg)
- { chain_sys_var(chain); }
- bool check(THD *thd, set_var *var)
- {
- return 1;
- }
- bool update(THD *thd, set_var *var)
- {
- return 1;
- }
- SHOW_TYPE show_type() { return SHOW_CHAR; }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
- {
- return (uchar*) *value;
- }
- bool check_update_type(Item_result type)
- {
- return 1;
- }
- bool check_default(enum_var_type type) { return 1; }
- bool is_readonly() const { return 1; }
-};
-
-
-class sys_var_const_os_str_ptr :public sys_var_const_str_ptr
-{
-public:
- sys_var_const_os_str_ptr(sys_var_chain *chain, const char *name_arg,
- char **value_arg)
- :sys_var_const_str_ptr(chain, name_arg, value_arg)
- {
- is_os_charset= TRUE;
- }
-};
-
-
-class sys_var_enum :public sys_var
-{
- uint *value;
- TYPELIB *enum_names;
-public:
- sys_var_enum(sys_var_chain *chain, const char *name_arg, uint *value_arg,
- TYPELIB *typelib, sys_after_update_func func)
- :sys_var(name_arg,func), value(value_arg), enum_names(typelib)
- { chain_sys_var(chain); }
- bool check(THD *thd, set_var *var)
- {
- return check_enum(thd, var, enum_names);
- }
- bool update(THD *thd, set_var *var);
- SHOW_TYPE show_type() { return SHOW_CHAR; }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
- bool check_update_type(Item_result type) { return 0; }
-};
-
-
-class sys_var_enum_const :public sys_var_enum
-{
-public:
- sys_var_enum_const(sys_var_chain *chain, const char *name_arg,
- uint *value_arg, TYPELIB *typelib)
- :sys_var_enum(chain, name_arg, value_arg, typelib, 0)
- { }
- bool is_readonly() const { return 1; }
-};
+ typedef bool (*on_check_function)(sys_var *self, THD *thd, set_var *var);
+ typedef bool (*on_update_function)(sys_var *self, THD *thd, enum_var_type type);
+
+ int flags; ///< or'ed flag_enum values
+ const SHOW_TYPE show_val_type; ///< what value_ptr() returns for sql_show.cc
+ my_option option; ///< min, max, default values are stored here
+ PolyLock *guard; ///< *second* lock that protects the variable
+ ptrdiff_t offset; ///< offset to the value from global_system_variables
+ on_check_function on_check;
+ on_update_function on_update;
+ const char *const deprecation_substitute;
+ bool is_os_charset; ///< true if the value is in character_set_filesystem
+
+public:
+ sys_var(sys_var_chain *chain, const char *name_arg, const char *comment,
+ int flag_args, ptrdiff_t off, int getopt_id,
+ enum get_opt_arg_type getopt_arg_type, SHOW_TYPE show_val_type_arg,
+ longlong def_val, PolyLock *lock, enum binlog_status_enum binlog_status_arg,
+ on_check_function on_check_func, on_update_function on_update_func,
+ const char *substitute);
+ virtual ~sys_var() {}
-class sys_var_thd :public sys_var
-{
-public:
- sys_var_thd(const char *name_arg,
- sys_after_update_func func= NULL,
- Binlog_status_enum binlog_status= NOT_IN_BINLOG)
- :sys_var(name_arg, func, binlog_status)
- {}
- bool check_type(enum_var_type type) { return 0; }
- bool check_default(enum_var_type type)
- {
- return type == OPT_GLOBAL && !option_limits;
- }
-};
-
+ /**
+ All the cleanup procedures should be performed here
+ */
+ virtual void cleanup() {}
+ /**
+ downcast for sys_var_pluginvar. Returns this if it's an instance
+ of sys_var_pluginvar, and 0 otherwise.
+ */
+ virtual sys_var_pluginvar *cast_pluginvar() { return 0; }
-class sys_var_thd_ulong :public sys_var_thd
-{
- sys_check_func check_func;
-public:
- ulong SV::*offset;
- sys_var_thd_ulong(sys_var_chain *chain, const char *name_arg,
- ulong SV::*offset_arg,
- sys_check_func c_func= NULL,
- sys_after_update_func au_func= NULL,
- Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
- :sys_var_thd(name_arg, au_func, binlog_status_arg), check_func(c_func),
- offset(offset_arg)
- { chain_sys_var(chain); }
bool check(THD *thd, set_var *var);
- bool update(THD *thd, set_var *var);
- void set_default(THD *thd, enum_var_type type);
- SHOW_TYPE show_type() { return SHOW_LONG; }
uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
-};
-
-class sys_var_thd_ha_rows :public sys_var_thd
-{
-public:
- ha_rows SV::*offset;
- sys_var_thd_ha_rows(sys_var_chain *chain, const char *name_arg,
- ha_rows SV::*offset_arg)
- :sys_var_thd(name_arg), offset(offset_arg)
- { chain_sys_var(chain); }
- sys_var_thd_ha_rows(sys_var_chain *chain, const char *name_arg,
- ha_rows SV::*offset_arg,
- sys_after_update_func func)
- :sys_var_thd(name_arg,func), offset(offset_arg)
- { chain_sys_var(chain); }
- bool check(THD *thd, set_var *var);
+ /**
+ Update the system variable with the default value from either
+ session or global scope. The default value is stored in the
+ 'var' argument. Return false when successful.
+ */
+ bool set_default(THD *thd, set_var *var);
bool update(THD *thd, set_var *var);
- void set_default(THD *thd, enum_var_type type);
- SHOW_TYPE show_type() { return SHOW_HA_ROWS; }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
-};
-
-class sys_var_thd_ulonglong :public sys_var_thd
-{
-public:
- ulonglong SV::*offset;
- bool only_global;
- sys_var_thd_ulonglong(sys_var_chain *chain, const char *name_arg,
- ulonglong SV::*offset_arg)
- :sys_var_thd(name_arg), offset(offset_arg)
- { chain_sys_var(chain); }
- sys_var_thd_ulonglong(sys_var_chain *chain, const char *name_arg,
- ulonglong SV::*offset_arg,
- sys_after_update_func func, bool only_global_arg)
- :sys_var_thd(name_arg, func), offset(offset_arg),
- only_global(only_global_arg)
- { chain_sys_var(chain); }
- bool update(THD *thd, set_var *var);
- void set_default(THD *thd, enum_var_type type);
- SHOW_TYPE show_type() { return SHOW_LONGLONG; }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
- bool check(THD *thd, set_var *var);
- bool check_default(enum_var_type type)
- {
- return type == OPT_GLOBAL && !option_limits;
- }
+ SHOW_TYPE show_type() { return show_val_type; }
+ int scope() const { return flags & SCOPE_MASK; }
+ CHARSET_INFO *charset(THD *thd);
+ bool is_readonly() const { return flags & READONLY; }
+ /**
+ the following is only true for keycache variables,
+ that support the syntax @@keycache_name.variable_name
+ */
+ bool is_struct() { return option.var_type & GET_ASK_ADDR; }
+ bool is_written_to_binlog(enum_var_type type)
+ { return type != OPT_GLOBAL && binlog_status == SESSION_VARIABLE_IN_BINLOG; }
+ virtual bool check_update_type(Item_result type) = 0;
bool check_type(enum_var_type type)
{
- return (only_global && type != OPT_GLOBAL);
- }
-};
-
-
-class sys_var_thd_bool :public sys_var_thd
-{
-public:
- my_bool SV::*offset;
- sys_var_thd_bool(sys_var_chain *chain, const char *name_arg, my_bool SV::*offset_arg)
- :sys_var_thd(name_arg), offset(offset_arg)
- { chain_sys_var(chain); }
- sys_var_thd_bool(sys_var_chain *chain, const char *name_arg, my_bool SV::*offset_arg,
- sys_after_update_func func)
- :sys_var_thd(name_arg,func), offset(offset_arg)
- { chain_sys_var(chain); }
- bool update(THD *thd, set_var *var);
- void set_default(THD *thd, enum_var_type type);
- SHOW_TYPE show_type() { return SHOW_MY_BOOL; }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
- bool check(THD *thd, set_var *var)
- {
- return check_enum(thd, var, &bool_typelib);
- }
- bool check_update_type(Item_result type) { return 0; }
-};
-
-
-class sys_var_thd_enum :public sys_var_thd
-{
-protected:
- ulong SV::*offset;
- TYPELIB *enum_names;
- sys_check_func check_func;
-public:
- sys_var_thd_enum(sys_var_chain *chain, const char *name_arg,
- ulong SV::*offset_arg, TYPELIB *typelib,
- sys_after_update_func func= NULL,
- sys_check_func check= NULL)
- :sys_var_thd(name_arg, func), offset(offset_arg),
- enum_names(typelib), check_func(check)
- { chain_sys_var(chain); }
- bool check(THD *thd, set_var *var)
- {
- /*
- check_enum fails if the character representation supplied was wrong
- or that the integer value was wrong or missing.
- */
- if (check_enum(thd, var, enum_names))
- return TRUE;
- if ((check_func && (*check_func)(thd, var)))
- return TRUE;
- return FALSE;
- }
- bool update(THD *thd, set_var *var);
- void set_default(THD *thd, enum_var_type type);
- SHOW_TYPE show_type() { return SHOW_CHAR; }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
- bool check_update_type(Item_result type) { return 0; }
-};
-
-
-class sys_var_thd_set :public sys_var_thd_enum
-{
- ulong visible_value_mask; /* Mask away internal bits */
-public:
- sys_var_thd_set(sys_var_chain *chain, const char *name_arg,
- ulong SV::*offset_arg, TYPELIB *typelib,
- ulong value_mask= ~ (ulong) 0,
- sys_after_update_func func= NULL)
- :sys_var_thd_enum(chain, name_arg, offset_arg, typelib,
- func), visible_value_mask(value_mask)
- {}
- bool check(THD *thd, set_var *var)
- {
- return check_set(thd, var, enum_names);
- }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
-};
-
-
-class sys_var_thd_optimizer_switch :public sys_var_thd_enum
-{
-public:
- sys_var_thd_optimizer_switch(sys_var_chain *chain, const char *name_arg,
- ulong SV::*offset_arg)
- :sys_var_thd_enum(chain, name_arg, offset_arg, &optimizer_switch_typelib)
- {}
- bool check(THD *thd, set_var *var);
- void set_default(THD *thd, enum_var_type type);
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
- static bool symbolic_mode_representation(THD *thd, ulonglong sql_mode,
- LEX_STRING *rep);
-};
-
-extern void fix_sql_mode_var(THD *thd, enum_var_type type);
-
-class sys_var_thd_sql_mode :public sys_var_thd_set
-{
-public:
- sys_var_thd_sql_mode(sys_var_chain *chain, const char *name_arg,
- ulong SV::*offset_arg)
- :sys_var_thd_set(chain, name_arg, offset_arg, &sql_mode_typelib,
- ~(ulong) 0, fix_sql_mode_var)
- {}
-};
-
-
-class sys_var_thd_storage_engine :public sys_var_thd
-{
-protected:
- plugin_ref SV::*offset;
-public:
- sys_var_thd_storage_engine(sys_var_chain *chain, const char *name_arg,
- plugin_ref SV::*offset_arg)
- :sys_var_thd(name_arg), offset(offset_arg)
- { chain_sys_var(chain); }
- bool check(THD *thd, set_var *var);
- SHOW_TYPE show_type() { return SHOW_CHAR; }
- bool check_update_type(Item_result type)
- {
- return type != STRING_RESULT; /* Only accept strings */
- }
- void set_default(THD *thd, enum_var_type type);
- bool update(THD *thd, set_var *var);
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
-};
-
-class sys_var_thd_table_type :public sys_var_thd_storage_engine
-{
-public:
- sys_var_thd_table_type(sys_var_chain *chain, const char *name_arg,
- plugin_ref SV::*offset_arg)
- :sys_var_thd_storage_engine(chain, name_arg, offset_arg)
- {}
- void warn_deprecated(THD *thd);
- void set_default(THD *thd, enum_var_type type);
- bool update(THD *thd, set_var *var);
-};
-
-class sys_var_thd_bit :public sys_var_thd
-{
- sys_check_func check_func;
- sys_update_func update_func;
-public:
- ulonglong bit_flag;
- bool reverse;
- sys_var_thd_bit(sys_var_chain *chain, const char *name_arg,
- sys_check_func c_func, sys_update_func u_func,
- ulonglong bit, bool reverse_arg=0,
- Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
- :sys_var_thd(name_arg, NULL, binlog_status_arg), check_func(c_func),
- update_func(u_func), bit_flag(bit), reverse(reverse_arg)
- { chain_sys_var(chain); }
- bool check(THD *thd, set_var *var);
- bool update(THD *thd, set_var *var);
- bool check_update_type(Item_result type) { return 0; }
- bool check_type(enum_var_type type) { return type == OPT_GLOBAL; }
- SHOW_TYPE show_type() { return SHOW_MY_BOOL; }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
-};
-
-#ifndef DBUG_OFF
-class sys_var_thd_dbug :public sys_var_thd
-{
-public:
- sys_var_thd_dbug(sys_var_chain *chain, const char *name_arg)
- :sys_var_thd(name_arg)
- { chain_sys_var(chain); }
- bool check_update_type(Item_result type) { return type != STRING_RESULT; }
- bool check(THD *thd, set_var *var);
- SHOW_TYPE show_type() { return SHOW_CHAR; }
- bool update(THD *thd, set_var *var);
- void set_default(THD *thd, enum_var_type type) { DBUG_POP(); }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *b);
-};
-#endif /* DBUG_OFF */
-
-#if defined(ENABLED_DEBUG_SYNC)
-/* Debug Sync Facility. Implemented in debug_sync.cc. */
-class sys_var_debug_sync :public sys_var_thd
-{
-public:
- sys_var_debug_sync(sys_var_chain *chain, const char *name_arg)
- :sys_var_thd(name_arg)
- { chain_sys_var(chain); }
- bool check(THD *thd, set_var *var);
- bool update(THD *thd, set_var *var);
- SHOW_TYPE show_type() { return SHOW_CHAR; }
- bool check_update_type(Item_result type) { return type != STRING_RESULT; }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
-};
-#endif /* defined(ENABLED_DEBUG_SYNC) */
-
-/* some variables that require special handling */
-
-class sys_var_timestamp :public sys_var
-{
-public:
- sys_var_timestamp(sys_var_chain *chain, const char *name_arg,
- Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
- :sys_var(name_arg, NULL, binlog_status_arg)
- { chain_sys_var(chain); }
- bool check(THD *thd, set_var *var);
- bool update(THD *thd, set_var *var);
- void set_default(THD *thd, enum_var_type type);
- bool check_type(enum_var_type type) { return type == OPT_GLOBAL; }
- bool check_default(enum_var_type type) { return 0; }
- SHOW_TYPE show_type() { return SHOW_DOUBLE; }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
- virtual bool check_update_type(Item_result type)
- {
- return type != INT_RESULT && type != REAL_RESULT && type != DECIMAL_RESULT;
- }
-};
-
-
-class sys_var_last_insert_id :public sys_var
-{
-public:
- sys_var_last_insert_id(sys_var_chain *chain, const char *name_arg,
- Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
- :sys_var(name_arg, NULL, binlog_status_arg)
- { chain_sys_var(chain); }
- bool update(THD *thd, set_var *var);
- bool check_type(enum_var_type type) { return type == OPT_GLOBAL; }
- SHOW_TYPE show_type() { return SHOW_LONGLONG; }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
-};
-
-
-class sys_var_insert_id :public sys_var
-{
-public:
- sys_var_insert_id(sys_var_chain *chain, const char *name_arg)
- :sys_var(name_arg)
- { chain_sys_var(chain); }
- bool update(THD *thd, set_var *var);
- bool check_type(enum_var_type type) { return type == OPT_GLOBAL; }
- SHOW_TYPE show_type() { return SHOW_LONGLONG; }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
-};
-
-
-class sys_var_rand_seed1 :public sys_var
-{
-public:
- sys_var_rand_seed1(sys_var_chain *chain, const char *name_arg,
- Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
- :sys_var(name_arg, NULL, binlog_status_arg)
- { chain_sys_var(chain); }
- bool update(THD *thd, set_var *var);
- bool check_type(enum_var_type type) { return type == OPT_GLOBAL; }
-};
-
-class sys_var_rand_seed2 :public sys_var
-{
-public:
- sys_var_rand_seed2(sys_var_chain *chain, const char *name_arg,
- Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
- :sys_var(name_arg, NULL, binlog_status_arg)
- { chain_sys_var(chain); }
- bool update(THD *thd, set_var *var);
- bool check_type(enum_var_type type) { return type == OPT_GLOBAL; }
-};
-
-
-class sys_var_collation :public sys_var_thd
-{
-public:
- sys_var_collation(const char *name_arg,
- Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
- :sys_var_thd(name_arg, NULL, binlog_status_arg)
- {
- no_support_one_shot= 0;
- }
- bool check(THD *thd, set_var *var);
- SHOW_TYPE show_type() { return SHOW_CHAR; }
- bool check_update_type(Item_result type)
- {
- return ((type != STRING_RESULT) && (type != INT_RESULT));
- }
- bool check_default(enum_var_type type) { return 0; }
- virtual void set_default(THD *thd, enum_var_type type)= 0;
-};
-
-class sys_var_character_set :public sys_var_thd
-{
-public:
- bool nullable;
- sys_var_character_set(const char *name_arg, bool is_nullable= 0,
- Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
- :sys_var_thd(name_arg, NULL, binlog_status_arg), nullable(is_nullable)
- {
- /*
- In fact only almost all variables derived from sys_var_character_set
- support ONE_SHOT; character_set_results doesn't. But that's good enough.
- */
- no_support_one_shot= 0;
- }
- bool check(THD *thd, set_var *var);
- SHOW_TYPE show_type() { return SHOW_CHAR; }
- bool check_update_type(Item_result type)
- {
- return ((type != STRING_RESULT) && (type != INT_RESULT));
- }
- bool check_default(enum_var_type type) { return 0; }
- bool update(THD *thd, set_var *var);
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
- virtual void set_default(THD *thd, enum_var_type type)= 0;
- virtual CHARSET_INFO **ci_ptr(THD *thd, enum_var_type type)= 0;
-};
-
-class sys_var_character_set_sv :public sys_var_character_set
-{
- CHARSET_INFO *SV::*offset;
- CHARSET_INFO **global_default;
-public:
- sys_var_character_set_sv(sys_var_chain *chain, const char *name_arg,
- CHARSET_INFO *SV::*offset_arg,
- CHARSET_INFO **global_default_arg,
- bool is_nullable= 0,
- Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
- : sys_var_character_set(name_arg, is_nullable, binlog_status_arg),
- offset(offset_arg), global_default(global_default_arg)
- { chain_sys_var(chain); }
- void set_default(THD *thd, enum_var_type type);
- CHARSET_INFO **ci_ptr(THD *thd, enum_var_type type);
-};
-
-
-class sys_var_character_set_client: public sys_var_character_set_sv
-{
-public:
- sys_var_character_set_client(sys_var_chain *chain, const char *name_arg,
- CHARSET_INFO *SV::*offset_arg,
- CHARSET_INFO **global_default_arg,
- Binlog_status_enum binlog_status_arg)
- : sys_var_character_set_sv(chain, name_arg, offset_arg, global_default_arg,
- 0, binlog_status_arg)
- { }
- bool check(THD *thd, set_var *var);
-};
-
-
-class sys_var_character_set_database :public sys_var_character_set
-{
-public:
- sys_var_character_set_database(sys_var_chain *chain, const char *name_arg,
- Binlog_status_enum binlog_status_arg=
- NOT_IN_BINLOG)
- : sys_var_character_set(name_arg, 0, binlog_status_arg)
- { chain_sys_var(chain); }
- void set_default(THD *thd, enum_var_type type);
- CHARSET_INFO **ci_ptr(THD *thd, enum_var_type type);
-};
-
-class sys_var_collation_sv :public sys_var_collation
-{
- CHARSET_INFO *SV::*offset;
- CHARSET_INFO **global_default;
-public:
- sys_var_collation_sv(sys_var_chain *chain, const char *name_arg,
- CHARSET_INFO *SV::*offset_arg,
- CHARSET_INFO **global_default_arg,
- Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
- :sys_var_collation(name_arg, binlog_status_arg),
- offset(offset_arg), global_default(global_default_arg)
- {
- chain_sys_var(chain);
- }
- bool update(THD *thd, set_var *var);
- void set_default(THD *thd, enum_var_type type);
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
-};
-
-
-class sys_var_key_cache_param :public sys_var
-{
-protected:
- size_t offset;
-public:
- sys_var_key_cache_param(sys_var_chain *chain, const char *name_arg,
- size_t offset_arg)
- :sys_var(name_arg), offset(offset_arg)
- { chain_sys_var(chain); }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
- bool check_default(enum_var_type type) { return 1; }
- bool is_struct() { return 1; }
-};
-
-
-class sys_var_key_buffer_size :public sys_var_key_cache_param
-{
-public:
- sys_var_key_buffer_size(sys_var_chain *chain, const char *name_arg)
- :sys_var_key_cache_param(chain, name_arg,
- offsetof(KEY_CACHE, param_buff_size))
- {}
- bool check(THD *thd, set_var *var);
- bool update(THD *thd, set_var *var);
- SHOW_TYPE show_type() { return SHOW_LONGLONG; }
-};
-
-
-class sys_var_key_cache_long :public sys_var_key_cache_param
-{
-public:
- sys_var_key_cache_long(sys_var_chain *chain, const char *name_arg, size_t offset_arg)
- :sys_var_key_cache_param(chain, name_arg, offset_arg)
- {}
- bool check(THD *thd, set_var *var);
- bool update(THD *thd, set_var *var);
- SHOW_TYPE show_type() { return SHOW_LONG; }
-};
-
-
-class sys_var_thd_date_time_format :public sys_var_thd
-{
- DATE_TIME_FORMAT *SV::*offset;
- timestamp_type date_time_type;
-public:
- sys_var_thd_date_time_format(sys_var_chain *chain, const char *name_arg,
- DATE_TIME_FORMAT *SV::*offset_arg,
- timestamp_type date_time_type_arg)
- :sys_var_thd(name_arg), offset(offset_arg),
- date_time_type(date_time_type_arg)
- { chain_sys_var(chain); }
- SHOW_TYPE show_type() { return SHOW_CHAR; }
- bool check_update_type(Item_result type)
- {
- return type != STRING_RESULT; /* Only accept strings */
- }
- bool check_default(enum_var_type type) { return 0; }
- bool check(THD *thd, set_var *var);
- bool update(THD *thd, set_var *var);
- void update2(THD *thd, enum_var_type type, DATE_TIME_FORMAT *new_value);
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
- void set_default(THD *thd, enum_var_type type);
-};
-
-
-class sys_var_log_state :public sys_var_bool_ptr
-{
- uint log_type;
-public:
- sys_var_log_state(sys_var_chain *chain, const char *name_arg, my_bool *value_arg,
- uint log_type_arg)
- :sys_var_bool_ptr(chain, name_arg, value_arg), log_type(log_type_arg) {}
- bool update(THD *thd, set_var *var);
- void set_default(THD *thd, enum_var_type type);
-};
-
-
-class sys_var_set :public sys_var
-{
-protected:
- ulong *value;
- TYPELIB *enum_names;
-public:
- sys_var_set(sys_var_chain *chain, const char *name_arg, ulong *value_arg,
- TYPELIB *typelib, sys_after_update_func func)
- :sys_var(name_arg, func), value(value_arg), enum_names(typelib)
- { chain_sys_var(chain); }
- virtual bool check(THD *thd, set_var *var)
- {
- return check_set(thd, var, enum_names);
- }
- virtual void set_default(THD *thd, enum_var_type type)
- {
- *value= 0;
- }
- bool update(THD *thd, set_var *var);
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
- bool check_update_type(Item_result type) { return 0; }
- SHOW_TYPE show_type() { return SHOW_CHAR; }
-};
-
-class sys_var_set_slave_mode :public sys_var_set
-{
-public:
- sys_var_set_slave_mode(sys_var_chain *chain, const char *name_arg,
- ulong *value_arg,
- TYPELIB *typelib, sys_after_update_func func) :
- sys_var_set(chain, name_arg, value_arg, typelib, func) {}
- void set_default(THD *thd, enum_var_type type);
- bool check(THD *thd, set_var *var);
- bool update(THD *thd, set_var *var);
-};
-
-class sys_var_log_output :public sys_var
-{
- ulong *value;
- TYPELIB *enum_names;
-public:
- sys_var_log_output(sys_var_chain *chain, const char *name_arg, ulong *value_arg,
- TYPELIB *typelib, sys_after_update_func func)
- :sys_var(name_arg,func), value(value_arg), enum_names(typelib)
- {
- chain_sys_var(chain);
- set_allow_empty_value(FALSE);
- }
- virtual bool check(THD *thd, set_var *var)
- {
- return check_set(thd, var, enum_names);
- }
- bool update(THD *thd, set_var *var);
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
- bool check_update_type(Item_result type) { return 0; }
- void set_default(THD *thd, enum_var_type type);
- SHOW_TYPE show_type() { return SHOW_CHAR; }
-};
-
-
-/* Variable that you can only read from */
-
-class sys_var_readonly: public sys_var
-{
-public:
- enum_var_type var_type;
- SHOW_TYPE show_type_value;
- sys_value_ptr_func value_ptr_func;
- sys_var_readonly(sys_var_chain *chain, const char *name_arg, enum_var_type type,
- SHOW_TYPE show_type_arg,
- sys_value_ptr_func value_ptr_func_arg)
- :sys_var(name_arg), var_type(type),
- show_type_value(show_type_arg), value_ptr_func(value_ptr_func_arg)
- { chain_sys_var(chain); }
- bool update(THD *thd, set_var *var) { return 1; }
- bool check_default(enum_var_type type) { return 1; }
- bool check_type(enum_var_type type) { return type != var_type; }
- bool check_update_type(Item_result type) { return 1; }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
- {
- return (*value_ptr_func)(thd);
- }
- SHOW_TYPE show_type() { return show_type_value; }
- bool is_readonly() const { return 1; }
-};
-
-
-class sys_var_readonly_os: public sys_var_readonly
-{
-public:
- sys_var_readonly_os(sys_var_chain *chain, const char *name_arg, enum_var_type type,
- SHOW_TYPE show_type_arg,
- sys_value_ptr_func value_ptr_func_arg)
- :sys_var_readonly(chain, name_arg, type, show_type_arg, value_ptr_func_arg)
- {
- is_os_charset= TRUE;
- }
-};
-
-
-/**
- Global-only, read-only variable. E.g. command line option.
-*/
-
-class sys_var_const: public sys_var
-{
-public:
- enum_var_type var_type;
- SHOW_TYPE show_type_value;
- uchar *ptr;
- sys_var_const(sys_var_chain *chain, const char *name_arg, enum_var_type type,
- SHOW_TYPE show_type_arg, uchar *ptr_arg)
- :sys_var(name_arg), var_type(type),
- show_type_value(show_type_arg), ptr(ptr_arg)
- { chain_sys_var(chain); }
- bool update(THD *thd, set_var *var) { return 1; }
- bool check_default(enum_var_type type) { return 1; }
- bool check_type(enum_var_type type) { return type != var_type; }
- bool check_update_type(Item_result type) { return 1; }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
- {
- return ptr;
+ switch (scope())
+ {
+ case GLOBAL: return type != OPT_GLOBAL;
+ case SESSION: return false; // always ok
+ case ONLY_SESSION: return type == OPT_GLOBAL;
+ }
+ return true; // keep gcc happy
}
- SHOW_TYPE show_type() { return show_type_value; }
- bool is_readonly() const { return 1; }
-};
-
-
-class sys_var_const_os: public sys_var_const
-{
-public:
- enum_var_type var_type;
- SHOW_TYPE show_type_value;
- uchar *ptr;
- sys_var_const_os(sys_var_chain *chain, const char *name_arg,
- enum_var_type type,
- SHOW_TYPE show_type_arg, uchar *ptr_arg)
- :sys_var_const(chain, name_arg, type, show_type_arg, ptr_arg)
+ bool register_option(DYNAMIC_ARRAY *array, int parse_flags)
{
- is_os_charset= TRUE;
+ return (option.id != -1) && ((flags & PARSE_EARLY) == parse_flags) &&
+ insert_dynamic(array, (uchar*)&option);
}
-};
+ void do_deprecated_warning(THD *thd);
+private:
+ virtual bool do_check(THD *thd, set_var *var) = 0;
+ /**
+ save the session default value of the variable in var
+ */
+ virtual void session_save_default(THD *thd, set_var *var) = 0;
+ /**
+ save the global default value of the variable in var
+ */
+ virtual void global_save_default(THD *thd, set_var *var) = 0;
+ virtual bool session_update(THD *thd, set_var *var) = 0;
+ virtual bool global_update(THD *thd, set_var *var) = 0;
-class sys_var_have_option: public sys_var
-{
protected:
- virtual SHOW_COMP_OPTION get_option() = 0;
-public:
- sys_var_have_option(sys_var_chain *chain, const char *variable_name):
- sys_var(variable_name)
- { chain_sys_var(chain); }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base)
- {
- return (uchar*) show_comp_option_name[get_option()];
- }
- bool update(THD *thd, set_var *var) { return 1; }
- bool check_default(enum_var_type type) { return 1; }
- bool check_type(enum_var_type type) { return type != OPT_GLOBAL; }
- bool check_update_type(Item_result type) { return 1; }
- SHOW_TYPE show_type() { return SHOW_CHAR; }
- bool is_readonly() const { return 1; }
-};
-
-
-class sys_var_have_variable: public sys_var_have_option
-{
- SHOW_COMP_OPTION *have_variable;
-
-public:
- sys_var_have_variable(sys_var_chain *chain, const char *variable_name,
- SHOW_COMP_OPTION *have_variable_arg):
- sys_var_have_option(chain, variable_name),
- have_variable(have_variable_arg)
- { }
- SHOW_COMP_OPTION get_option() { return *have_variable; }
-};
-
-
-class sys_var_have_plugin: public sys_var_have_option
-{
- const char *plugin_name_str;
- const uint plugin_name_len;
- const int plugin_type;
-
-public:
- sys_var_have_plugin(sys_var_chain *chain, const char *variable_name,
- const char *plugin_name_str_arg, uint plugin_name_len_arg,
- int plugin_type_arg):
- sys_var_have_option(chain, variable_name),
- plugin_name_str(plugin_name_str_arg), plugin_name_len(plugin_name_len_arg),
- plugin_type(plugin_type_arg)
- { }
- /* the following method is declared in sql_plugin.cc */
- SHOW_COMP_OPTION get_option();
-};
-
-
-class sys_var_thd_time_zone :public sys_var_thd
-{
-public:
- sys_var_thd_time_zone(sys_var_chain *chain, const char *name_arg,
- Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
- :sys_var_thd(name_arg, NULL, binlog_status_arg)
- {
- no_support_one_shot= 0;
- chain_sys_var(chain);
- }
- bool check(THD *thd, set_var *var);
- SHOW_TYPE show_type() { return SHOW_CHAR; }
- bool check_update_type(Item_result type)
- {
- return type != STRING_RESULT; /* Only accept strings */
- }
- bool check_default(enum_var_type type) { return 0; }
- bool update(THD *thd, set_var *var);
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
- virtual void set_default(THD *thd, enum_var_type type);
-};
-
-
-class sys_var_max_user_conn : public sys_var_thd
-{
-public:
- sys_var_max_user_conn(sys_var_chain *chain, const char *name_arg):
- sys_var_thd(name_arg)
- { chain_sys_var(chain); }
- bool check(THD *thd, set_var *var);
- bool update(THD *thd, set_var *var);
- bool check_default(enum_var_type type)
- {
- return type != OPT_GLOBAL || !option_limits;
- }
- void set_default(THD *thd, enum_var_type type);
- SHOW_TYPE show_type() { return SHOW_INT; }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
-};
-
-
-/**
- * @brief This is a specialization of sys_var_thd_ulong that implements a
- read-only session variable. The class overrides check() and check_default()
- to achieve the read-only property for the session part of the variable.
- */
-class sys_var_thd_ulong_session_readonly : public sys_var_thd_ulong
-{
-public:
- sys_var_thd_ulong_session_readonly(sys_var_chain *chain_arg,
- const char *name_arg, ulong SV::*offset_arg,
- sys_check_func c_func= NULL,
- sys_after_update_func au_func= NULL,
- Binlog_status_enum bl_status_arg= NOT_IN_BINLOG):
- sys_var_thd_ulong(chain_arg, name_arg, offset_arg, c_func, au_func, bl_status_arg)
- { }
- bool check(THD *thd, set_var *var);
- bool check_default(enum_var_type type)
- {
- return type != OPT_GLOBAL || !option_limits;
- }
-};
-
-
-class sys_var_microseconds :public sys_var_thd
-{
- ulonglong SV::*offset;
-public:
- sys_var_microseconds(sys_var_chain *chain, const char *name_arg,
- ulonglong SV::*offset_arg):
- sys_var_thd(name_arg), offset(offset_arg)
- { chain_sys_var(chain); }
- bool check(THD *thd, set_var *var) {return 0;}
- bool update(THD *thd, set_var *var);
- void set_default(THD *thd, enum_var_type type);
- SHOW_TYPE show_type() { return SHOW_DOUBLE; }
- bool check_update_type(Item_result type)
- {
- return (type != INT_RESULT && type != REAL_RESULT && type != DECIMAL_RESULT);
- }
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
-};
-
-
-class sys_var_trust_routine_creators :public sys_var_bool_ptr
-{
- /* We need a derived class only to have a warn_deprecated() */
-public:
- sys_var_trust_routine_creators(sys_var_chain *chain,
- const char *name_arg, my_bool *value_arg) :
- sys_var_bool_ptr(chain, name_arg, value_arg) {};
- void warn_deprecated(THD *thd);
- void set_default(THD *thd, enum_var_type type);
- bool update(THD *thd, set_var *var);
-};
-
-/**
- Handler for setting the system variable --read-only.
-*/
-
-class sys_var_opt_readonly :public sys_var_bool_ptr
-{
-public:
- sys_var_opt_readonly(sys_var_chain *chain, const char *name_arg,
- my_bool *value_arg) :
- sys_var_bool_ptr(chain, name_arg, value_arg) {};
- ~sys_var_opt_readonly() {};
- bool update(THD *thd, set_var *var);
-};
-
+ /**
+ A pointer to a value of the variable for SHOW.
+ It must be of show_val_type type (bool for SHOW_BOOL, int for SHOW_INT,
+ longlong for SHOW_LONGLONG, etc).
+ */
+ virtual uchar *session_value_ptr(THD *thd, LEX_STRING *base);
+ virtual uchar *global_value_ptr(THD *thd, LEX_STRING *base);
-class sys_var_thd_lc_time_names :public sys_var_thd
-{
-public:
- sys_var_thd_lc_time_names(sys_var_chain *chain, const char *name_arg,
- Binlog_status_enum binlog_status_arg= NOT_IN_BINLOG)
- : sys_var_thd(name_arg, NULL, binlog_status_arg)
- {
-#if MYSQL_VERSION_ID < 50000
- no_support_one_shot= 0;
-#endif
- chain_sys_var(chain);
- }
- bool check(THD *thd, set_var *var);
- SHOW_TYPE show_type() { return SHOW_CHAR; }
- bool check_update_type(Item_result type)
- {
- return ((type != STRING_RESULT) && (type != INT_RESULT));
- }
- bool check_default(enum_var_type type) { return 0; }
- bool update(THD *thd, set_var *var);
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
- virtual void set_default(THD *thd, enum_var_type type);
-};
+ /**
+ A pointer to a storage area of the variable, to the raw data.
+ Typically it's the same as session_value_ptr(), but it's different,
+ for example, for ENUM, that is printed as a string, but stored as a number.
+ */
+ uchar *session_var_ptr(THD *thd)
+ { return ((uchar*)&(thd->variables)) + offset; }
-#ifdef HAVE_EVENT_SCHEDULER
-class sys_var_event_scheduler :public sys_var_long_ptr
-{
- /* We need a derived class only to have a warn_deprecated() */
-public:
- sys_var_event_scheduler(sys_var_chain *chain, const char *name_arg) :
- sys_var_long_ptr(chain, name_arg, NULL, NULL) {};
- bool update(THD *thd, set_var *var);
- uchar *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
- SHOW_TYPE show_type() { return SHOW_CHAR; }
- bool check(THD *thd, set_var *var);
- bool check_update_type(Item_result type)
- {
- return type != STRING_RESULT && type != INT_RESULT;
- }
+ uchar *global_var_ptr()
+ { return ((uchar*)&global_system_variables) + offset; }
};
-#endif
-extern void fix_binlog_format_after_update(THD *thd, enum_var_type type);
+#include "sql_plugin.h" /* SHOW_HA_ROWS, SHOW_MY_BOOL */
-class sys_var_thd_binlog_format :public sys_var_thd_enum
-{
-public:
- sys_var_thd_binlog_format(sys_var_chain *chain, const char *name_arg,
- ulong SV::*offset_arg)
- :sys_var_thd_enum(chain, name_arg, offset_arg,
- &binlog_format_typelib,
- fix_binlog_format_after_update)
- {};
- bool check(THD *thd, set_var *var);
- bool is_readonly() const;
-};
/****************************************************************************
Classes for parsing of the SET command
****************************************************************************/
+/**
+ A base class for everything that can be set with SET command.
+ It's similar to Items, an instance of this is created by the parser
+ for every assigmnent in SET (or elsewhere, e.g. in SELECT).
+*/
class set_var_base :public Sql_alloc
{
public:
set_var_base() {}
virtual ~set_var_base() {}
- virtual int check(THD *thd)=0; /* To check privileges etc. */
- virtual int update(THD *thd)=0; /* To set the value */
- /* light check for PS */
- virtual int light_check(THD *thd) { return check(thd); }
- virtual bool no_support_one_shot() { return 1; }
+ virtual int check(THD *thd)=0; /* To check privileges etc. */
+ virtual int update(THD *thd)=0; /* To set the value */
+ virtual int light_check(THD *thd) { return check(thd); } /* for PS */
};
-/* MySQL internal variables, like query_cache_size */
-
+/**
+ set_var_base descendant for assignments to the system variables.
+*/
class set_var :public set_var_base
{
public:
- sys_var *var;
- Item *value;
+ sys_var *var; ///< system variable to be updated
+ Item *value; ///< the expression that provides the new value of the variable
enum_var_type type;
- union
- {
- CHARSET_INFO *charset;
- ulong ulong_value;
- ulonglong ulonglong_value;
- plugin_ref plugin;
- DATE_TIME_FORMAT *date_time_format;
- Time_zone *time_zone;
- MY_LOCALE *locale_value;
+ union ///< temp storage to hold a value between sys_var::check and ::update
+ {
+ ulonglong ulonglong_value; ///< for unsigned integer, set, enum sysvars
+ longlong longlong_value; ///< for signed integer
+ double double_value; ///< for Sys_var_double
+ plugin_ref plugin; ///< for Sys_var_plugin
+ 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 structs */
+ LEX_STRING base; /**< for structured variables, like keycache_name.variable_name */
set_var(enum_var_type type_arg, sys_var *var_arg,
const LEX_STRING *base_name_arg, Item *value_arg)
@@ -1340,33 +232,21 @@ public:
if (value_arg && value_arg->type() == Item::FIELD_ITEM)
{
Item_field *item= (Item_field*) value_arg;
- if (item->field_name)
- {
- if (!(value= new Item_string(item->field_name,
- (uint) strlen(item->field_name),
- item->collation.collation)))
- value= value_arg; /* Give error message later */
- }
- else
- {
- /* Both Item_field and Item_insert_value will return the type as
- Item::FIELD_ITEM. If the item->field_name is NULL, we assume the
- object to be Item_insert_value. */
- value= value_arg;
- }
+ if (!(value=new Item_string(item->field_name,
+ (uint) strlen(item->field_name),
+ system_charset_info))) // names are utf8
+ value=value_arg; /* Give error message later */
}
else
- value= value_arg;
+ value=value_arg;
}
int check(THD *thd);
int update(THD *thd);
int light_check(THD *thd);
- bool no_support_one_shot() { return var->no_support_one_shot; }
};
/* User variables like @my_own_variable */
-
class set_var_user: public set_var_base
{
Item_func_set_user_var *user_var_item;
@@ -1403,8 +283,8 @@ class set_var_collation_client: public set_var_base
CHARSET_INFO *collation_connection;
public:
set_var_collation_client(CHARSET_INFO *client_coll_arg,
- CHARSET_INFO *connection_coll_arg,
- CHARSET_INFO *result_coll_arg)
+ CHARSET_INFO *connection_coll_arg,
+ CHARSET_INFO *result_coll_arg)
:character_set_client(client_coll_arg),
character_set_results(result_coll_arg),
collation_connection(connection_coll_arg)
@@ -1414,84 +294,38 @@ public:
};
-extern "C"
-{
- typedef int (*process_key_cache_t) (const char *, KEY_CACHE *);
-}
-
-/* Named lists (used for keycaches) */
-
-class NAMED_LIST :public ilink
-{
- const char *name;
- uint name_length;
-public:
- uchar* data;
-
- NAMED_LIST(I_List<NAMED_LIST> *links, const char *name_arg,
- uint 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)
- {
- return length == name_length && !memcmp(name, name_cmp, length);
- }
- ~NAMED_LIST()
- {
- my_free((uchar*) name, MYF(0));
- }
- friend bool process_key_caches(process_key_cache_t func);
- friend int fill_key_cache_tables(THD *thd, TABLE_LIST *tables, COND *cond);
- friend void delete_elements(I_List<NAMED_LIST> *list,
- void (*free_element)(const char*, uchar*));
-};
-
-/* updated in sql_acl.cc */
-
-extern sys_var_thd_bool sys_old_alter_table;
-extern sys_var_thd_bool sys_old_passwords;
-extern LEX_STRING default_key_cache_base;
+/* optional things, have_* variables */
+extern SHOW_COMP_OPTION have_csv, have_innodb;
+extern SHOW_COMP_OPTION have_ndbcluster, have_partitioning;
+extern SHOW_COMP_OPTION have_profiling;
-/* For sql_yacc */
-struct sys_var_with_base
-{
- sys_var *var;
- LEX_STRING base_name;
-};
+extern SHOW_COMP_OPTION have_ssl, have_symlink, have_dlopen;
+extern SHOW_COMP_OPTION have_query_cache;
+extern SHOW_COMP_OPTION have_geometry, have_rtree_keys;
+extern SHOW_COMP_OPTION have_crypt;
+extern SHOW_COMP_OPTION have_compress;
/*
Prototypes for helper functions
*/
-int set_var_init();
-void set_var_free();
-SHOW_VAR* enumerate_sys_vars(THD *thd, bool sorted);
-int mysql_add_sys_var_chain(sys_var *chain, struct my_option *long_options);
-int mysql_del_sys_var_chain(sys_var *chain);
+SHOW_VAR* enumerate_sys_vars(THD *thd, bool sorted, enum enum_var_type type);
+
sys_var *find_sys_var(THD *thd, const char *str, uint length=0);
int sql_set_variables(THD *thd, List<set_var_base> *var_list);
-bool not_all_support_one_shot(List<set_var_base> *var_list);
-void fix_delay_key_write(THD *thd, enum_var_type type);
-void fix_slave_exec_mode(void);
-ulong fix_sql_mode(ulong sql_mode);
-extern sys_var_const_str sys_charset_system;
-extern sys_var_str sys_init_connect;
-extern sys_var_str sys_init_slave;
-extern sys_var_thd_time_zone sys_time_zone;
-extern sys_var_thd_bit sys_autocommit;
+
+bool fix_delay_key_write(sys_var *self, THD *thd, enum_var_type type);
+
+ulonglong expand_sql_mode(ulonglong sql_mode);
+bool sql_mode_string_representation(THD *thd, ulonglong sql_mode, LEX_STRING *ls);
+
+extern sys_var *Sys_autocommit_ptr;
+
CHARSET_INFO *get_old_charset_by_name(const char *old_name);
-uchar* find_named(I_List<NAMED_LIST> *list, const char *name, uint length,
- NAMED_LIST **found);
-extern sys_var_str sys_var_general_log_path, sys_var_slow_log_path;
+int sys_var_init();
+int sys_var_add_options(DYNAMIC_ARRAY *long_options, int parse_flags);
+void sys_var_end(void);
+
+#endif
-/* key_cache functions */
-KEY_CACHE *get_key_cache(LEX_STRING *cache_name);
-KEY_CACHE *get_or_create_key_cache(const char *name, uint length);
-void free_key_cache(const char *name, KEY_CACHE *key_cache);
-bool process_key_caches(process_key_cache_t func);
-void delete_elements(I_List<NAMED_LIST> *list,
- void (*free_element)(const char*, uchar*));
diff --git a/sql/sha2.cc b/sql/sha2.cc
new file mode 100644
index 00000000000..f2201974172
--- /dev/null
+++ b/sql/sha2.cc
@@ -0,0 +1,68 @@
+/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+
+/**
+ @file
+ A compatibility layer to our built-in SSL implementation, to mimic the
+ oft-used external library, OpenSSL.
+*/
+
+#include <my_global.h>
+#include <sha2.h>
+
+#ifdef HAVE_YASSL
+
+/*
+ If TaoCrypt::SHA512 or ::SHA384 are not defined (but ::SHA256 is), it's
+ probably that neither of config.h's SIZEOF_LONG or SIZEOF_LONG_LONG are
+ 64 bits long. At present, both OpenSSL and YaSSL require 64-bit integers
+ for SHA-512. (The SIZEOF_* definitions come from autoconf's config.h .)
+*/
+
+# define GEN_YASSL_SHA2_BRIDGE(size) \
+unsigned char* SHA##size(const unsigned char *input_ptr, size_t input_length, \
+ char unsigned *output_ptr) { \
+ TaoCrypt::SHA##size hasher; \
+ \
+ hasher.Update(input_ptr, input_length); \
+ hasher.Final(output_ptr); \
+ return(output_ptr); \
+}
+
+
+/**
+ @fn SHA512
+ @fn SHA384
+ @fn SHA256
+ @fn SHA224
+
+ Instantiate an hash object, fill in the cleartext value, compute the digest,
+ and extract the result from the object.
+
+ (Generate the functions. See similar .h code for the prototypes.)
+*/
+# ifndef OPENSSL_NO_SHA512
+GEN_YASSL_SHA2_BRIDGE(512);
+GEN_YASSL_SHA2_BRIDGE(384);
+# else
+# warning Some SHA2 functionality is missing. See OPENSSL_NO_SHA512.
+# endif
+GEN_YASSL_SHA2_BRIDGE(256);
+GEN_YASSL_SHA2_BRIDGE(224);
+
+# undef GEN_YASSL_SHA2_BRIDGE
+
+#endif /* HAVE_YASSL */
diff --git a/sql/share/.cvsignore b/sql/share/.cvsignore
deleted file mode 100644
index 282522db034..00000000000
--- a/sql/share/.cvsignore
+++ /dev/null
@@ -1,2 +0,0 @@
-Makefile
-Makefile.in
diff --git a/sql/share/CMakeLists.txt b/sql/share/CMakeLists.txt
index 0c8ddf402d2..e0d5fb6c1a7 100644
--- a/sql/share/CMakeLists.txt
+++ b/sql/share/CMakeLists.txt
@@ -1,4 +1,5 @@
-# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2006 MySQL AB, 2009, 2010 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
@@ -40,15 +41,15 @@ serbian
)
SET(files
- errmsg.txt
+ errmsg-utf8.txt
)
FOREACH (dir ${dirs})
INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${dir}
- DESTINATION share COMPONENT Server)
+ DESTINATION ${INSTALL_MYSQLSHAREDIR} COMPONENT Server)
ENDFOREACH()
-INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/charsets DESTINATION share COMPONENT Server
- PATTERN "languages.html" EXCLUDE
+INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/charsets DESTINATION ${INSTALL_MYSQLSHAREDIR}
+ COMPONENT Common PATTERN "languages.html" EXCLUDE
)
-INSTALL(FILES ${files} DESTINATION share COMPONENT Server)
+INSTALL(FILES ${files} DESTINATION ${INSTALL_MYSQLSHAREDIR} COMPONENT Server)
diff --git a/sql/share/Makefile.am b/sql/share/Makefile.am
deleted file mode 100644
index 8b7792481e6..00000000000
--- a/sql/share/Makefile.am
+++ /dev/null
@@ -1,60 +0,0 @@
-# Copyright (c) 2000, 2010, Oracle and/or its affiliates
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; version 2 of the License.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-## Process this file with automake to create Makefile.in
-
-EXTRA_DIST= errmsg.txt CMakeLists.txt
-
-dist-hook:
- for dir in charsets @AVAILABLE_LANGUAGES@; do \
- test -d $(distdir)/$$dir || mkdir $(distdir)/$$dir; \
- $(INSTALL_DATA) $(srcdir)/$$dir/*.* $(distdir)/$$dir; \
- done; \
- sleep 1 ; touch $(srcdir)/*/errmsg.sys
- $(INSTALL_DATA) $(srcdir)/charsets/README $(distdir)/charsets
- $(INSTALL_DATA) $(srcdir)/charsets/Index.xml $(distdir)/charsets
-
-all-local: english/errmsg.sys
-
-# Use the english errmsg.sys as a flag that all errmsg.sys needs to be
-# created. Normally these are created by extra/Makefile
-
-english/errmsg.sys: errmsg.txt
- rm -f $(top_builddir)/include/mysqld_error.h
- (cd $(top_builddir)/extra && $(MAKE))
-
-install-data-local:
- for lang in @AVAILABLE_LANGUAGES@; \
- do \
- $(mkinstalldirs) $(DESTDIR)$(pkgdatadir)/$$lang; \
- $(INSTALL_DATA) $(srcdir)/$$lang/errmsg.sys \
- $(DESTDIR)$(pkgdatadir)/$$lang/errmsg.sys; \
- done
- $(mkinstalldirs) $(DESTDIR)$(pkgdatadir)/charsets
- $(INSTALL_DATA) $(srcdir)/errmsg.txt \
- $(DESTDIR)$(pkgdatadir)/errmsg.txt; \
- $(INSTALL_DATA) $(srcdir)/charsets/README $(DESTDIR)$(pkgdatadir)/charsets/README
- $(INSTALL_DATA) $(srcdir)/charsets/*.xml $(DESTDIR)$(pkgdatadir)/charsets
-
-# FIXME maybe shouldn't remove, could be needed by other installation?
-# Note that this removes the directory that support-files are using!
-uninstall-local:
- @RM@ -f -r $(DESTDIR)$(pkgdatadir)
-
-distclean-local:
- @RM@ -f */errmsg.sys
-
-# Do nothing
-link_sources:
diff --git a/sql/share/charsets/Index.xml b/sql/share/charsets/Index.xml
index 3e402226a34..9764d629625 100644
--- a/sql/share/charsets/Index.xml
+++ b/sql/share/charsets/Index.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (c) 2003-2005 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
diff --git a/sql/share/charsets/armscii8.xml b/sql/share/charsets/armscii8.xml
index 94152751e77..c1eb93b1f91 100644
--- a/sql/share/charsets/armscii8.xml
+++ b/sql/share/charsets/armscii8.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (c) 2003, 2004 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
diff --git a/sql/share/charsets/ascii.xml b/sql/share/charsets/ascii.xml
index 29336b3a665..c516a68516c 100644
--- a/sql/share/charsets/ascii.xml
+++ b/sql/share/charsets/ascii.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (c) 2003, 2007 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
diff --git a/sql/share/charsets/cp1250.xml b/sql/share/charsets/cp1250.xml
index 1b4a71ef6d5..e6681a625a2 100644
--- a/sql/share/charsets/cp1250.xml
+++ b/sql/share/charsets/cp1250.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (c) 2003, 2005 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
diff --git a/sql/share/charsets/cp1256.xml b/sql/share/charsets/cp1256.xml
index 806fef961f7..ab0ba855f3b 100644
--- a/sql/share/charsets/cp1256.xml
+++ b/sql/share/charsets/cp1256.xml
@@ -6,6 +6,7 @@
<copyright>
Copyright (C) 2003 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
diff --git a/sql/share/charsets/cp1257.xml b/sql/share/charsets/cp1257.xml
index 8ae73fdf25a..61d1d276b0a 100644
--- a/sql/share/charsets/cp1257.xml
+++ b/sql/share/charsets/cp1257.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (C) 2003 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
diff --git a/sql/share/charsets/cp850.xml b/sql/share/charsets/cp850.xml
index 198b336daef..06465540a75 100644
--- a/sql/share/charsets/cp850.xml
+++ b/sql/share/charsets/cp850.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (C) 2003 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
diff --git a/sql/share/charsets/cp852.xml b/sql/share/charsets/cp852.xml
index fff67d3509d..e0c574d2ea1 100644
--- a/sql/share/charsets/cp852.xml
+++ b/sql/share/charsets/cp852.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (c) 2003, 2004 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
diff --git a/sql/share/charsets/cp866.xml b/sql/share/charsets/cp866.xml
index d35f3d68b05..9cd8c8c504b 100644
--- a/sql/share/charsets/cp866.xml
+++ b/sql/share/charsets/cp866.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (C) 2003 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
diff --git a/sql/share/charsets/dec8.xml b/sql/share/charsets/dec8.xml
index 66bb421b674..68949309ced 100644
--- a/sql/share/charsets/dec8.xml
+++ b/sql/share/charsets/dec8.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (C) 2003 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
diff --git a/sql/share/charsets/geostd8.xml b/sql/share/charsets/geostd8.xml
index a789d07e6d8..822cc083724 100644
--- a/sql/share/charsets/geostd8.xml
+++ b/sql/share/charsets/geostd8.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (C) 2003 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
diff --git a/sql/share/charsets/greek.xml b/sql/share/charsets/greek.xml
index 5b66a7ab442..cbbe22e675a 100644
--- a/sql/share/charsets/greek.xml
+++ b/sql/share/charsets/greek.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (C) 2003 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
diff --git a/sql/share/charsets/hebrew.xml b/sql/share/charsets/hebrew.xml
index 0544b27ef4f..562fa4f4748 100644
--- a/sql/share/charsets/hebrew.xml
+++ b/sql/share/charsets/hebrew.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (c) 2003, 2006 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
diff --git a/sql/share/charsets/hp8.xml b/sql/share/charsets/hp8.xml
index 83a076237f7..b17f75ed73e 100644
--- a/sql/share/charsets/hp8.xml
+++ b/sql/share/charsets/hp8.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (C) 2003 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
diff --git a/sql/share/charsets/keybcs2.xml b/sql/share/charsets/keybcs2.xml
index a9f305deab8..7c2775ba5c3 100644
--- a/sql/share/charsets/keybcs2.xml
+++ b/sql/share/charsets/keybcs2.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (C) 2003 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
diff --git a/sql/share/charsets/koi8r.xml b/sql/share/charsets/koi8r.xml
index 21ebf78b79e..25264d4f9ce 100644
--- a/sql/share/charsets/koi8r.xml
+++ b/sql/share/charsets/koi8r.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (C) 2003 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
diff --git a/sql/share/charsets/koi8u.xml b/sql/share/charsets/koi8u.xml
index 65145c97593..a2f5de9feb2 100644
--- a/sql/share/charsets/koi8u.xml
+++ b/sql/share/charsets/koi8u.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (C) 2003 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
diff --git a/sql/share/charsets/languages.html b/sql/share/charsets/languages.html
index 6d1a8aafc5c..3263d6a2ae2 100644
--- a/sql/share/charsets/languages.html
+++ b/sql/share/charsets/languages.html
@@ -1,4 +1,21 @@
#!/bin/sh
+
+# Copyright (C) 2003 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 Street, Fifth Floor, Boston, MA 02110-1301, USA
+
#<pre>
(
echo "DROP TABLE lang;"
diff --git a/sql/share/charsets/latin1.xml b/sql/share/charsets/latin1.xml
index 4054eea8d33..68307847d91 100644
--- a/sql/share/charsets/latin1.xml
+++ b/sql/share/charsets/latin1.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (c) 2003, 2005 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
diff --git a/sql/share/charsets/latin2.xml b/sql/share/charsets/latin2.xml
index a44ec7e0ec6..29ff4cb974b 100644
--- a/sql/share/charsets/latin2.xml
+++ b/sql/share/charsets/latin2.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (c) 2003, 2005 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
diff --git a/sql/share/charsets/latin5.xml b/sql/share/charsets/latin5.xml
index 6b60e58cdda..ca7dd106de5 100644
--- a/sql/share/charsets/latin5.xml
+++ b/sql/share/charsets/latin5.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (c) 2003, 2005 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
diff --git a/sql/share/charsets/latin7.xml b/sql/share/charsets/latin7.xml
index fb384b3a5ff..81866c23bbd 100644
--- a/sql/share/charsets/latin7.xml
+++ b/sql/share/charsets/latin7.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (C) 2003 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
diff --git a/sql/share/charsets/macce.xml b/sql/share/charsets/macce.xml
index d7242f26297..4fa46301d2e 100644
--- a/sql/share/charsets/macce.xml
+++ b/sql/share/charsets/macce.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (C) 2003 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
diff --git a/sql/share/charsets/macroman.xml b/sql/share/charsets/macroman.xml
index a2485cf9379..4ee8dc1f952 100644
--- a/sql/share/charsets/macroman.xml
+++ b/sql/share/charsets/macroman.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (C) 2003 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
diff --git a/sql/share/charsets/swe7.xml b/sql/share/charsets/swe7.xml
index f12a2238718..d881f1e7d62 100644
--- a/sql/share/charsets/swe7.xml
+++ b/sql/share/charsets/swe7.xml
@@ -4,6 +4,7 @@
<copyright>
Copyright (C) 2003 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
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
new file mode 100644
index 00000000000..6fcb460e6c6
--- /dev/null
+++ b/sql/share/errmsg-utf8.txt
@@ -0,0 +1,6567 @@
+languages czech=cze latin2, danish=dan latin1, dutch=nla latin1, english=eng latin1, estonian=est latin7, french=fre latin1, german=ger latin1, greek=greek greek, hungarian=hun latin2, italian=ita latin1, japanese=jpn ujis, japanese-sjis=jps sjis, korean=kor euckr, norwegian-ny=norwegian-ny latin1, norwegian=nor latin1, polish=pol latin2, portuguese=por latin1, romanian=rum latin2, russian=rus koi8r, serbian=serbian cp1250, slovak=slo latin2, spanish=spa latin1, swedish=swe latin1, ukrainian=ukr koi8u;
+
+default-language eng
+
+start-error-number 1000
+
+ER_HASHCHK
+ eng "hashchk"
+ER_NISAMCHK
+ eng "isamchk"
+ER_NO
+ cze "NE"
+ dan "NEJ"
+ nla "NEE"
+ eng "NO"
+ est "EI"
+ fre "NON"
+ ger "Nein"
+ greek "ΟΧΙ"
+ hun "NEM"
+ kor "아니오"
+ nor "NEI"
+ norwegian-ny "NEI"
+ pol "NIE"
+ por "NÃO"
+ rum "NU"
+ rus "ÐЕТ"
+ serbian "NE"
+ slo "NIE"
+ ukr "ÐІ"
+ER_YES
+ cze "ANO"
+ dan "JA"
+ nla "JA"
+ eng "YES"
+ est "JAH"
+ fre "OUI"
+ ger "Ja"
+ greek "ÎΑΙ"
+ hun "IGEN"
+ ita "SI"
+ kor "예"
+ nor "JA"
+ norwegian-ny "JA"
+ pol "TAK"
+ por "SIM"
+ rum "DA"
+ rus "ДÐ"
+ serbian "DA"
+ slo "Ãno"
+ spa "SI"
+ ukr "ТÐК"
+ER_CANT_CREATE_FILE
+ cze "Nemohu vytvo-Břit soubor '%-.200s' (chybový kód: %d)"
+ dan "Kan ikke oprette filen '%-.200s' (Fejlkode: %d)"
+ nla "Kan file '%-.200s' niet aanmaken (Errcode: %d)"
+ eng "Can't create file '%-.200s' (errno: %d)"
+ est "Ei suuda luua faili '%-.200s' (veakood: %d)"
+ fre "Ne peut créer le fichier '%-.200s' (Errcode: %d)"
+ ger "Kann Datei '%-.200s' nicht erzeugen (Fehler: %d)"
+ greek "ΑδÏνατη η δημιουÏγία του αÏχείου '%-.200s' (κωδικός λάθους: %d)"
+ hun "A '%-.200s' file nem hozhato letre (hibakod: %d)"
+ ita "Impossibile creare il file '%-.200s' (errno: %d)"
+ jpn "'%-.200s' ファイルãŒä½œã‚Œã¾ã›ã‚“ (errno: %d)"
+ kor "í™”ì¼ '%-.200s'를 만들지 못했습니다. (ì—러번호: %d)"
+ nor "Kan ikke opprette fila '%-.200s' (Feilkode: %d)"
+ norwegian-ny "Kan ikkje opprette fila '%-.200s' (Feilkode: %d)"
+ pol "Nie można stworzyć pliku '%-.200s' (Kod błędu: %d)"
+ por "Não pode criar o arquivo '%-.200s' (erro no. %d)"
+ rum "Nu pot sa creez fisierul '%-.200s' (Eroare: %d)"
+ rus "Ðевозможно Ñоздать файл '%-.200s' (ошибка: %d)"
+ serbian "Ne mogu da kreiram file '%-.200s' (errno: %d)"
+ slo "Nemôžem vytvoriť súbor '%-.200s' (chybový kód: %d)"
+ spa "No puedo crear archivo '%-.200s' (Error: %d)"
+ swe "Kan inte skapa filen '%-.200s' (Felkod: %d)"
+ ukr "Ðе можу Ñтворити файл '%-.200s' (помилка: %d)"
+ER_CANT_CREATE_TABLE
+ cze "Nemohu vytvo-Břit tabulku '%-.200s' (chybový kód: %d)"
+ dan "Kan ikke oprette tabellen '%-.200s' (Fejlkode: %d)"
+ nla "Kan tabel '%-.200s' niet aanmaken (Errcode: %d)"
+ eng "Can't create table '%-.200s' (errno: %d)"
+ jps "'%-.200s' テーブルãŒä½œã‚Œã¾ã›ã‚“.(errno: %d)",
+ est "Ei suuda luua tabelit '%-.200s' (veakood: %d)"
+ fre "Ne peut créer la table '%-.200s' (Errcode: %d)"
+ ger "Kann Tabelle '%-.200s' nicht erzeugen (Fehler: %d)"
+ greek "ΑδÏνατη η δημιουÏγία του πίνακα '%-.200s' (κωδικός λάθους: %d)"
+ hun "A '%-.200s' tabla nem hozhato letre (hibakod: %d)"
+ ita "Impossibile creare la tabella '%-.200s' (errno: %d)"
+ jpn "'%-.200s' テーブルãŒä½œã‚Œã¾ã›ã‚“.(errno: %d)"
+ kor "í…Œì´ë¸” '%-.200s'를 만들지 못했습니다. (ì—러번호: %d)"
+ nor "Kan ikke opprette tabellen '%-.200s' (Feilkode: %d)"
+ norwegian-ny "Kan ikkje opprette tabellen '%-.200s' (Feilkode: %d)"
+ pol "Nie można stworzyć tabeli '%-.200s' (Kod błędu: %d)"
+ por "Não pode criar a tabela '%-.200s' (erro no. %d)"
+ rum "Nu pot sa creez tabla '%-.200s' (Eroare: %d)"
+ rus "Ðевозможно Ñоздать таблицу '%-.200s' (ошибка: %d)"
+ serbian "Ne mogu da kreiram tabelu '%-.200s' (errno: %d)"
+ slo "Nemôžem vytvoriť tabuľku '%-.200s' (chybový kód: %d)"
+ spa "No puedo crear tabla '%-.200s' (Error: %d)"
+ swe "Kan inte skapa tabellen '%-.200s' (Felkod: %d)"
+ ukr "Ðе можу Ñтворити таблицю '%-.200s' (помилка: %d)"
+ER_CANT_CREATE_DB
+ cze "Nemohu vytvo-Břit databázi '%-.192s' (chybový kód: %d)"
+ dan "Kan ikke oprette databasen '%-.192s' (Fejlkode: %d)"
+ nla "Kan database '%-.192s' niet aanmaken (Errcode: %d)"
+ eng "Can't create database '%-.192s' (errno: %d)"
+ jps "'%-.192s' データベースãŒä½œã‚Œã¾ã›ã‚“ (errno: %d)",
+ est "Ei suuda luua andmebaasi '%-.192s' (veakood: %d)"
+ fre "Ne peut créer la base '%-.192s' (Erreur %d)"
+ ger "Kann Datenbank '%-.192s' nicht erzeugen (Fehler: %d)"
+ greek "ΑδÏνατη η δημιουÏγία της βάσης δεδομένων '%-.192s' (κωδικός λάθους: %d)"
+ hun "Az '%-.192s' adatbazis nem hozhato letre (hibakod: %d)"
+ ita "Impossibile creare il database '%-.192s' (errno: %d)"
+ jpn "'%-.192s' データベースãŒä½œã‚Œã¾ã›ã‚“ (errno: %d)"
+ kor "ë°ì´íƒ€ë² ì´ìŠ¤ '%-.192s'를 만들지 못했습니다.. (ì—러번호: %d)"
+ nor "Kan ikke opprette databasen '%-.192s' (Feilkode: %d)"
+ norwegian-ny "Kan ikkje opprette databasen '%-.192s' (Feilkode: %d)"
+ pol "Nie można stworzyć bazy danych '%-.192s' (Kod błędu: %d)"
+ por "Não pode criar o banco de dados '%-.192s' (erro no. %d)"
+ rum "Nu pot sa creez baza de date '%-.192s' (Eroare: %d)"
+ rus "Ðевозможно Ñоздать базу данных '%-.192s' (ошибка: %d)"
+ serbian "Ne mogu da kreiram bazu '%-.192s' (errno: %d)"
+ slo "Nemôžem vytvoriť databázu '%-.192s' (chybový kód: %d)"
+ spa "No puedo crear base de datos '%-.192s' (Error: %d)"
+ swe "Kan inte skapa databasen '%-.192s' (Felkod: %d)"
+ ukr "Ðе можу Ñтворити базу данних '%-.192s' (помилка: %d)"
+ER_DB_CREATE_EXISTS
+ cze "Nemohu vytvo-Břit databázi '%-.192s'; databáze již existuje"
+ dan "Kan ikke oprette databasen '%-.192s'; databasen eksisterer"
+ nla "Kan database '%-.192s' niet aanmaken; database bestaat reeds"
+ eng "Can't create database '%-.192s'; database exists"
+ jps "'%-.192s' データベースãŒä½œã‚Œã¾ã›ã‚“.æ—¢ã«ãã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ãŒå­˜åœ¨ã—ã¾ã™",
+ est "Ei suuda luua andmebaasi '%-.192s': andmebaas juba eksisteerib"
+ fre "Ne peut créer la base '%-.192s'; elle existe déjà"
+ ger "Kann Datenbank '%-.192s' nicht erzeugen. Datenbank existiert bereits"
+ greek "ΑδÏνατη η δημιουÏγία της βάσης δεδομένων '%-.192s'; Η βάση δεδομένων υπάÏχει ήδη"
+ hun "Az '%-.192s' adatbazis nem hozhato letre Az adatbazis mar letezik"
+ ita "Impossibile creare il database '%-.192s'; il database esiste"
+ jpn "'%-.192s' データベースãŒä½œã‚Œã¾ã›ã‚“.æ—¢ã«ãã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ãŒå­˜åœ¨ã—ã¾ã™"
+ kor "ë°ì´íƒ€ë² ì´ìŠ¤ '%-.192s'를 만들지 못했습니다.. ë°ì´íƒ€ë² ì´ìŠ¤ê°€ 존재함"
+ nor "Kan ikke opprette databasen '%-.192s'; databasen eksisterer"
+ norwegian-ny "Kan ikkje opprette databasen '%-.192s'; databasen eksisterer"
+ pol "Nie można stworzyć bazy danych '%-.192s'; baza danych już istnieje"
+ por "Não pode criar o banco de dados '%-.192s'; este banco de dados já existe"
+ rum "Nu pot sa creez baza de date '%-.192s'; baza de date exista deja"
+ rus "Ðевозможно Ñоздать базу данных '%-.192s'. База данных уже ÑущеÑтвует"
+ serbian "Ne mogu da kreiram bazu '%-.192s'; baza već postoji."
+ slo "Nemôžem vytvoriť databázu '%-.192s'; databáza existuje"
+ spa "No puedo crear base de datos '%-.192s'; la base de datos ya existe"
+ swe "Databasen '%-.192s' existerar redan"
+ ukr "Ðе можу Ñтворити базу данних '%-.192s'. База данних Ñ–Ñнує"
+ER_DB_DROP_EXISTS
+ cze "Nemohu zru-Bšit databázi '%-.192s', databáze neexistuje"
+ dan "Kan ikke slette (droppe) '%-.192s'; databasen eksisterer ikke"
+ nla "Kan database '%-.192s' niet verwijderen; database bestaat niet"
+ eng "Can't drop database '%-.192s'; database doesn't exist"
+ jps "'%-.192s' データベースを破棄ã§ãã¾ã›ã‚“. ãã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ãŒãªã„ã®ã§ã™.",
+ est "Ei suuda kustutada andmebaasi '%-.192s': andmebaasi ei eksisteeri"
+ fre "Ne peut effacer la base '%-.192s'; elle n'existe pas"
+ ger "Kann Datenbank '%-.192s' nicht löschen; Datenbank nicht vorhanden"
+ greek "ΑδÏνατη η διαγÏαφή της βάσης δεδομένων '%-.192s'. Η βάση δεδομένων δεν υπάÏχει"
+ hun "A(z) '%-.192s' adatbazis nem szuntetheto meg. Az adatbazis nem letezik"
+ ita "Impossibile cancellare '%-.192s'; il database non esiste"
+ jpn "'%-.192s' データベースを破棄ã§ãã¾ã›ã‚“. ãã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ãŒãªã„ã®ã§ã™."
+ kor "ë°ì´íƒ€ë² ì´ìŠ¤ '%-.192s'를 제거하지 못했습니다. ë°ì´íƒ€ë² ì´ìŠ¤ê°€ 존재하지 ì•ŠìŒ "
+ nor "Kan ikke fjerne (drop) '%-.192s'; databasen eksisterer ikke"
+ norwegian-ny "Kan ikkje fjerne (drop) '%-.192s'; databasen eksisterer ikkje"
+ pol "Nie można usun?ć bazy danych '%-.192s'; baza danych nie istnieje"
+ por "Não pode eliminar o banco de dados '%-.192s'; este banco de dados não existe"
+ rum "Nu pot sa drop baza de date '%-.192s'; baza da date este inexistenta"
+ rus "Ðевозможно удалить базу данных '%-.192s'. Такой базы данных нет"
+ serbian "Ne mogu da izbrišem bazu '%-.192s'; baza ne postoji."
+ slo "Nemôžem zmazať databázu '%-.192s'; databáza neexistuje"
+ spa "No puedo eliminar base de datos '%-.192s'; la base de datos no existe"
+ swe "Kan inte radera databasen '%-.192s'; databasen finns inte"
+ ukr "Ðе можу видалити базу данних '%-.192s'. База данних не Ñ–Ñнує"
+ER_DB_DROP_DELETE
+ cze "Chyba p-Bři rušení databáze (nemohu vymazat '%-.192s', chyba %d)"
+ dan "Fejl ved sletning (drop) af databasen (kan ikke slette '%-.192s', Fejlkode %d)"
+ nla "Fout bij verwijderen database (kan '%-.192s' niet verwijderen, Errcode: %d)"
+ eng "Error dropping database (can't delete '%-.192s', errno: %d)"
+ jps "データベース破棄エラー ('%-.192s' を削除ã§ãã¾ã›ã‚“, errno: %d)",
+ est "Viga andmebaasi kustutamisel (ei suuda kustutada faili '%-.192s', veakood: %d)"
+ fre "Ne peut effacer la base '%-.192s' (erreur %d)"
+ ger "Fehler beim Löschen der Datenbank ('%-.192s' kann nicht gelöscht werden, Fehler: %d)"
+ greek "ΠαÏουσιάστηκε Ï€Ïόβλημα κατά τη διαγÏαφή της βάσης δεδομένων (αδÏνατη η διαγÏαφή '%-.192s', κωδικός λάθους: %d)"
+ hun "Adatbazis megszuntetesi hiba ('%-.192s' nem torolheto, hibakod: %d)"
+ ita "Errore durante la cancellazione del database (impossibile cancellare '%-.192s', errno: %d)"
+ jpn "データベース破棄エラー ('%-.192s' を削除ã§ãã¾ã›ã‚“, errno: %d)"
+ kor "ë°ì´íƒ€ë² ì´ìŠ¤ 제거 ì—러('%-.192s'를 삭제할 수 ì—†ì니다, ì—러번호: %d)"
+ nor "Feil ved fjerning (drop) av databasen (kan ikke slette '%-.192s', feil %d)"
+ norwegian-ny "Feil ved fjerning (drop) av databasen (kan ikkje slette '%-.192s', feil %d)"
+ pol "Bł?d podczas usuwania bazy danych (nie można usun?ć '%-.192s', bł?d %d)"
+ por "Erro ao eliminar banco de dados (não pode eliminar '%-.192s' - erro no. %d)"
+ rum "Eroare dropuind baza de date (nu pot sa sterg '%-.192s', Eroare: %d)"
+ rus "Ошибка при удалении базы данных (невозможно удалить '%-.192s', ошибка: %d)"
+ serbian "Ne mogu da izbrišem bazu (ne mogu da izbrišem '%-.192s', errno: %d)"
+ slo "Chyba pri mazaní databázy (nemôžem zmazať '%-.192s', chybový kód: %d)"
+ spa "Error eliminando la base de datos(no puedo borrar '%-.192s', error %d)"
+ swe "Fel vid radering av databasen (Kan inte radera '%-.192s'. Felkod: %d)"
+ ukr "Ðе можу видалити базу данних (Ðе можу видалити '%-.192s', помилка: %d)"
+ER_DB_DROP_RMDIR
+ cze "Chyba p-Bři rušení databáze (nemohu vymazat adresář '%-.192s', chyba %d)"
+ dan "Fejl ved sletting af database (kan ikke slette folderen '%-.192s', Fejlkode %d)"
+ nla "Fout bij verwijderen database (kan rmdir '%-.192s' niet uitvoeren, Errcode: %d)"
+ eng "Error dropping database (can't rmdir '%-.192s', errno: %d)"
+ jps "データベース破棄エラー ('%-.192s' ã‚’ rmdir ã§ãã¾ã›ã‚“, errno: %d)",
+ est "Viga andmebaasi kustutamisel (ei suuda kustutada kataloogi '%-.192s', veakood: %d)"
+ fre "Erreur en effaçant la base (rmdir '%-.192s', erreur %d)"
+ ger "Fehler beim Löschen der Datenbank (Verzeichnis '%-.192s' kann nicht gelöscht werden, Fehler: %d)"
+ greek "ΠαÏουσιάστηκε Ï€Ïόβλημα κατά τη διαγÏαφή της βάσης δεδομένων (αδÏνατη η διαγÏαφή του φακέλλου '%-.192s', κωδικός λάθους: %d)"
+ hun "Adatbazis megszuntetesi hiba ('%-.192s' nem szuntetheto meg, hibakod: %d)"
+ ita "Errore durante la cancellazione del database (impossibile rmdir '%-.192s', errno: %d)"
+ jpn "データベース破棄エラー ('%-.192s' ã‚’ rmdir ã§ãã¾ã›ã‚“, errno: %d)"
+ kor "ë°ì´íƒ€ë² ì´ìŠ¤ 제거 ì—러(rmdir '%-.192s'를 í•  수 ì—†ì니다, ì—러번호: %d)"
+ nor "Feil ved sletting av database (kan ikke slette katalogen '%-.192s', feil %d)"
+ norwegian-ny "Feil ved sletting av database (kan ikkje slette katalogen '%-.192s', feil %d)"
+ pol "Bł?d podczas usuwania bazy danych (nie można wykonać rmdir '%-.192s', bł?d %d)"
+ por "Erro ao eliminar banco de dados (não pode remover diretório '%-.192s' - erro no. %d)"
+ rum "Eroare dropuind baza de date (nu pot sa rmdir '%-.192s', Eroare: %d)"
+ rus "Ðевозможно удалить базу данных (невозможно удалить каталог '%-.192s', ошибка: %d)"
+ serbian "Ne mogu da izbrišem bazu (ne mogu da izbrišem direktorijum '%-.192s', errno: %d)"
+ slo "Chyba pri mazaní databázy (nemôžem vymazať adresár '%-.192s', chybový kód: %d)"
+ spa "Error eliminando la base de datos (No puedo borrar directorio '%-.192s', error %d)"
+ swe "Fel vid radering av databasen (Kan inte radera biblioteket '%-.192s'. Felkod: %d)"
+ ukr "Ðе можу видалити базу данних (Ðе можу видалити теку '%-.192s', помилка: %d)"
+ER_CANT_DELETE_FILE
+ cze "Chyba p-Bři výmazu '%-.192s' (chybový kód: %d)"
+ dan "Fejl ved sletning af '%-.192s' (Fejlkode: %d)"
+ nla "Fout bij het verwijderen van '%-.192s' (Errcode: %d)"
+ eng "Error on delete of '%-.192s' (errno: %d)"
+ jps "'%-.192s' ã®å‰Šé™¤ãŒã‚¨ãƒ©ãƒ¼ (errno: %d)",
+ est "Viga '%-.192s' kustutamisel (veakood: %d)"
+ fre "Erreur en effaçant '%-.192s' (Errcode: %d)"
+ ger "Fehler beim Löschen von '%-.192s' (Fehler: %d)"
+ greek "ΠαÏουσιάστηκε Ï€Ïόβλημα κατά τη διαγÏαφή '%-.192s' (κωδικός λάθους: %d)"
+ hun "Torlesi hiba: '%-.192s' (hibakod: %d)"
+ ita "Errore durante la cancellazione di '%-.192s' (errno: %d)"
+ jpn "'%-.192s' ã®å‰Šé™¤ãŒã‚¨ãƒ©ãƒ¼ (errno: %d)"
+ kor "'%-.192s' ì‚­ì œ 중 ì—러 (ì—러번호: %d)"
+ nor "Feil ved sletting av '%-.192s' (Feilkode: %d)"
+ norwegian-ny "Feil ved sletting av '%-.192s' (Feilkode: %d)"
+ pol "Bł?d podczas usuwania '%-.192s' (Kod błędu: %d)"
+ por "Erro na remoção de '%-.192s' (erro no. %d)"
+ rum "Eroare incercind sa delete '%-.192s' (Eroare: %d)"
+ rus "Ошибка при удалении '%-.192s' (ошибка: %d)"
+ serbian "Greška pri brisanju '%-.192s' (errno: %d)"
+ slo "Chyba pri mazaní '%-.192s' (chybový kód: %d)"
+ spa "Error en el borrado de '%-.192s' (Error: %d)"
+ swe "Kan inte radera filen '%-.192s' (Felkod: %d)"
+ ukr "Ðе можу видалити '%-.192s' (помилка: %d)"
+ER_CANT_FIND_SYSTEM_REC
+ cze "Nemohu -BÄíst záznam v systémové tabulce"
+ dan "Kan ikke læse posten i systemfolderen"
+ nla "Kan record niet lezen in de systeem tabel"
+ eng "Can't read record in system table"
+ jps "system table ã®ãƒ¬ã‚³ãƒ¼ãƒ‰ã‚’読む事ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ",
+ est "Ei suuda lugeda kirjet süsteemsest tabelist"
+ fre "Ne peut lire un enregistrement de la table 'system'"
+ ger "Datensatz in der Systemtabelle nicht lesbar"
+ greek "ΑδÏνατη η ανάγνωση εγγÏαφής από πίνακα του συστήματος"
+ hun "Nem olvashato rekord a rendszertablaban"
+ ita "Impossibile leggere il record dalla tabella di sistema"
+ jpn "system table ã®ãƒ¬ã‚³ãƒ¼ãƒ‰ã‚’読む事ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+ kor "system í…Œì´ë¸”ì—ì„œ 레코드를 ì½ì„ 수 없습니다."
+ nor "Kan ikke lese posten i systemkatalogen"
+ norwegian-ny "Kan ikkje lese posten i systemkatalogen"
+ pol "Nie można odczytać rekordu z tabeli systemowej"
+ por "Não pode ler um registro numa tabela do sistema"
+ rum "Nu pot sa citesc cimpurile in tabla de system (system table)"
+ rus "Ðевозможно прочитать запиÑÑŒ в ÑиÑтемной таблице"
+ serbian "Ne mogu da proÄitam slog iz sistemske tabele"
+ slo "Nemôžem ÄítaÅ¥ záznam v systémovej tabuľke"
+ spa "No puedo leer el registro en la tabla del sistema"
+ swe "Hittar inte posten i systemregistret"
+ ukr "Ðе можу зчитати Ð·Ð°Ð¿Ð¸Ñ Ð· ÑиÑтемної таблиці"
+ER_CANT_GET_STAT
+ cze "Nemohu z-Bískat stav '%-.200s' (chybový kód: %d)"
+ dan "Kan ikke læse status af '%-.200s' (Fejlkode: %d)"
+ nla "Kan de status niet krijgen van '%-.200s' (Errcode: %d)"
+ eng "Can't get status of '%-.200s' (errno: %d)"
+ jps "'%-.200s' ã®ã‚¹ãƒ†ã‚¤ã‚¿ã‚¹ãŒå¾—られã¾ã›ã‚“. (errno: %d)",
+ est "Ei suuda lugeda '%-.200s' olekut (veakood: %d)"
+ fre "Ne peut obtenir le status de '%-.200s' (Errcode: %d)"
+ ger "Kann Status von '%-.200s' nicht ermitteln (Fehler: %d)"
+ greek "ΑδÏνατη η λήψη πληÏοφοÏιών για την κατάσταση του '%-.200s' (κωδικός λάθους: %d)"
+ hun "A(z) '%-.200s' statusza nem allapithato meg (hibakod: %d)"
+ ita "Impossibile leggere lo stato di '%-.200s' (errno: %d)"
+ jpn "'%-.200s' ã®ã‚¹ãƒ†ã‚¤ã‚¿ã‚¹ãŒå¾—られã¾ã›ã‚“. (errno: %d)"
+ kor "'%-.200s'ì˜ ìƒíƒœë¥¼ 얻지 못했습니다. (ì—러번호: %d)"
+ nor "Kan ikke lese statusen til '%-.200s' (Feilkode: %d)"
+ norwegian-ny "Kan ikkje lese statusen til '%-.200s' (Feilkode: %d)"
+ pol "Nie można otrzymać statusu '%-.200s' (Kod błędu: %d)"
+ por "Não pode obter o status de '%-.200s' (erro no. %d)"
+ rum "Nu pot sa obtin statusul lui '%-.200s' (Eroare: %d)"
+ rus "Ðевозможно получить ÑтатуÑную информацию о '%-.200s' (ошибка: %d)"
+ serbian "Ne mogu da dobijem stanje file-a '%-.200s' (errno: %d)"
+ slo "Nemôžem zistiť stav '%-.200s' (chybový kód: %d)"
+ spa "No puedo obtener el estado de '%-.200s' (Error: %d)"
+ swe "Kan inte läsa filinformationen (stat) från '%-.200s' (Felkod: %d)"
+ ukr "Ðе можу отримати ÑÑ‚Ð°Ñ‚ÑƒÑ '%-.200s' (помилка: %d)"
+ER_CANT_GET_WD
+ cze "Chyba p-Bři zjišťování pracovní adresář (chybový kód: %d)"
+ dan "Kan ikke læse aktive folder (Fejlkode: %d)"
+ nla "Kan de werkdirectory niet krijgen (Errcode: %d)"
+ eng "Can't get working directory (errno: %d)"
+ jps "working directory を得る事ãŒã§ãã¾ã›ã‚“ã§ã—㟠(errno: %d)",
+ est "Ei suuda identifitseerida jooksvat kataloogi (veakood: %d)"
+ fre "Ne peut obtenir le répertoire de travail (Errcode: %d)"
+ ger "Kann Arbeitsverzeichnis nicht ermitteln (Fehler: %d)"
+ greek "Ο φάκελλος εÏγασίας δεν βÏέθηκε (κωδικός λάθους: %d)"
+ hun "A munkakonyvtar nem allapithato meg (hibakod: %d)"
+ ita "Impossibile leggere la directory di lavoro (errno: %d)"
+ jpn "working directory を得る事ãŒã§ãã¾ã›ã‚“ã§ã—㟠(errno: %d)"
+ kor "수행 디렉토리를 찾지 못했습니다. (ì—러번호: %d)"
+ nor "Kan ikke lese aktiv katalog(Feilkode: %d)"
+ norwegian-ny "Kan ikkje lese aktiv katalog(Feilkode: %d)"
+ pol "Nie można rozpoznać aktualnego katalogu (Kod błędu: %d)"
+ por "Não pode obter o diretório corrente (erro no. %d)"
+ rum "Nu pot sa obtin directorul current (working directory) (Eroare: %d)"
+ rus "Ðевозможно определить рабочий каталог (ошибка: %d)"
+ serbian "Ne mogu da dobijem trenutni direktorijum (errno: %d)"
+ slo "Nemôžem zistiť pracovný adresár (chybový kód: %d)"
+ spa "No puedo acceder al directorio (Error: %d)"
+ swe "Kan inte inte läsa aktivt bibliotek. (Felkod: %d)"
+ ukr "Ðе можу визначити робочу теку (помилка: %d)"
+ER_CANT_LOCK
+ cze "Nemohu uzamknout soubor (chybov-Bý kód: %d)"
+ dan "Kan ikke låse fil (Fejlkode: %d)"
+ nla "Kan de file niet blokeren (Errcode: %d)"
+ eng "Can't lock file (errno: %d)"
+ jps "ファイルをロックã§ãã¾ã›ã‚“ (errno: %d)",
+ est "Ei suuda lukustada faili (veakood: %d)"
+ fre "Ne peut verrouiller le fichier (Errcode: %d)"
+ ger "Datei kann nicht gesperrt werden (Fehler: %d)"
+ greek "Το αÏχείο δεν μποÏεί να κλειδωθεί (κωδικός λάθους: %d)"
+ hun "A file nem zarolhato. (hibakod: %d)"
+ ita "Impossibile il locking il file (errno: %d)"
+ jpn "ファイルをロックã§ãã¾ã›ã‚“ (errno: %d)"
+ kor "í™”ì¼ì„ 잠그지(lock) 못했습니다. (ì—러번호: %d)"
+ nor "Kan ikke låse fila (Feilkode: %d)"
+ norwegian-ny "Kan ikkje låse fila (Feilkode: %d)"
+ pol "Nie można zablokować pliku (Kod błędu: %d)"
+ por "Não pode travar o arquivo (erro no. %d)"
+ rum "Nu pot sa lock fisierul (Eroare: %d)"
+ rus "Ðевозможно поÑтавить блокировку на файле (ошибка: %d)"
+ serbian "Ne mogu da zakljuÄam file (errno: %d)"
+ slo "Nemôžem zamknúť súbor (chybový kód: %d)"
+ spa "No puedo bloquear archivo: (Error: %d)"
+ swe "Kan inte låsa filen. (Felkod: %d)"
+ ukr "Ðе можу заблокувати файл (помилка: %d)"
+ER_CANT_OPEN_FILE
+ cze "Nemohu otev-Břít soubor '%-.200s' (chybový kód: %d)"
+ dan "Kan ikke åbne fil: '%-.200s' (Fejlkode: %d)"
+ nla "Kan de file '%-.200s' niet openen (Errcode: %d)"
+ eng "Can't open file: '%-.200s' (errno: %d)"
+ jps "'%-.200s' ファイルを開ã事ãŒã§ãã¾ã›ã‚“ (errno: %d)",
+ est "Ei suuda avada faili '%-.200s' (veakood: %d)"
+ fre "Ne peut ouvrir le fichier: '%-.200s' (Errcode: %d)"
+ ger "Kann Datei '%-.200s' nicht öffnen (Fehler: %d)"
+ greek "Δεν είναι δυνατό να ανοιχτεί το αÏχείο: '%-.200s' (κωδικός λάθους: %d)"
+ hun "A '%-.200s' file nem nyithato meg (hibakod: %d)"
+ ita "Impossibile aprire il file: '%-.200s' (errno: %d)"
+ jpn "'%-.200s' ファイルを開ã事ãŒã§ãã¾ã›ã‚“ (errno: %d)"
+ kor "í™”ì¼ì„ 열지 못했습니다.: '%-.200s' (ì—러번호: %d)"
+ nor "Kan ikke åpne fila: '%-.200s' (Feilkode: %d)"
+ norwegian-ny "Kan ikkje åpne fila: '%-.200s' (Feilkode: %d)"
+ pol "Nie można otworzyć pliku: '%-.200s' (Kod błędu: %d)"
+ por "Não pode abrir o arquivo '%-.200s' (erro no. %d)"
+ rum "Nu pot sa deschid fisierul: '%-.200s' (Eroare: %d)"
+ rus "Ðевозможно открыть файл: '%-.200s' (ошибка: %d)"
+ serbian "Ne mogu da otvorim file: '%-.200s' (errno: %d)"
+ slo "Nemôžem otvoriť súbor: '%-.200s' (chybový kód: %d)"
+ spa "No puedo abrir archivo: '%-.200s' (Error: %d)"
+ swe "Kan inte använda '%-.200s' (Felkod: %d)"
+ ukr "Ðе можу відкрити файл: '%-.200s' (помилка: %d)"
+ER_FILE_NOT_FOUND
+ cze "Nemohu naj-Bít soubor '%-.200s' (chybový kód: %d)"
+ dan "Kan ikke finde fila: '%-.200s' (Fejlkode: %d)"
+ nla "Kan de file: '%-.200s' niet vinden (Errcode: %d)"
+ eng "Can't find file: '%-.200s' (errno: %d)"
+ jps "'%-.200s' ファイルを見付ã‘る事ãŒã§ãã¾ã›ã‚“.(errno: %d)",
+ est "Ei suuda leida faili '%-.200s' (veakood: %d)"
+ fre "Ne peut trouver le fichier: '%-.200s' (Errcode: %d)"
+ ger "Kann Datei '%-.200s' nicht finden (Fehler: %d)"
+ greek "Δεν βÏέθηκε το αÏχείο: '%-.200s' (κωδικός λάθους: %d)"
+ hun "A(z) '%-.200s' file nem talalhato (hibakod: %d)"
+ ita "Impossibile trovare il file: '%-.200s' (errno: %d)"
+ jpn "'%-.200s' ファイルを見付ã‘る事ãŒã§ãã¾ã›ã‚“.(errno: %d)"
+ kor "í™”ì¼ì„ 찾지 못했습니다.: '%-.200s' (ì—러번호: %d)"
+ nor "Kan ikke finne fila: '%-.200s' (Feilkode: %d)"
+ norwegian-ny "Kan ikkje finne fila: '%-.200s' (Feilkode: %d)"
+ pol "Nie można znaleĽć pliku: '%-.200s' (Kod błędu: %d)"
+ por "Não pode encontrar o arquivo '%-.200s' (erro no. %d)"
+ rum "Nu pot sa gasesc fisierul: '%-.200s' (Eroare: %d)"
+ rus "Ðевозможно найти файл: '%-.200s' (ошибка: %d)"
+ serbian "Ne mogu da pronađem file: '%-.200s' (errno: %d)"
+ slo "Nemôžem nájsť súbor: '%-.200s' (chybový kód: %d)"
+ spa "No puedo encontrar archivo: '%-.200s' (Error: %d)"
+ swe "Hittar inte filen '%-.200s' (Felkod: %d)"
+ ukr "Ðе можу знайти файл: '%-.200s' (помилка: %d)"
+ER_CANT_READ_DIR
+ cze "Nemohu -BÄíst adresář '%-.192s' (chybový kód: %d)"
+ dan "Kan ikke læse folder '%-.192s' (Fejlkode: %d)"
+ nla "Kan de directory niet lezen van '%-.192s' (Errcode: %d)"
+ eng "Can't read dir of '%-.192s' (errno: %d)"
+ jps "'%-.192s' ディレクトリãŒèª­ã‚ã¾ã›ã‚“.(errno: %d)",
+ est "Ei suuda lugeda kataloogi '%-.192s' (veakood: %d)"
+ fre "Ne peut lire le répertoire de '%-.192s' (Errcode: %d)"
+ ger "Verzeichnis von '%-.192s' nicht lesbar (Fehler: %d)"
+ greek "Δεν είναι δυνατό να διαβαστεί ο φάκελλος του '%-.192s' (κωδικός λάθους: %d)"
+ hun "A(z) '%-.192s' konyvtar nem olvashato. (hibakod: %d)"
+ ita "Impossibile leggere la directory di '%-.192s' (errno: %d)"
+ jpn "'%-.192s' ディレクトリãŒèª­ã‚ã¾ã›ã‚“.(errno: %d)"
+ kor "'%-.192s'디렉토리를 ì½ì§€ 못했습니다. (ì—러번호: %d)"
+ nor "Kan ikke lese katalogen '%-.192s' (Feilkode: %d)"
+ norwegian-ny "Kan ikkje lese katalogen '%-.192s' (Feilkode: %d)"
+ pol "Nie można odczytać katalogu '%-.192s' (Kod błędu: %d)"
+ por "Não pode ler o diretório de '%-.192s' (erro no. %d)"
+ rum "Nu pot sa citesc directorul '%-.192s' (Eroare: %d)"
+ rus "Ðевозможно прочитать каталог '%-.192s' (ошибка: %d)"
+ serbian "Ne mogu da proÄitam direktorijum '%-.192s' (errno: %d)"
+ slo "Nemôžem ÄítaÅ¥ adresár '%-.192s' (chybový kód: %d)"
+ spa "No puedo leer el directorio de '%-.192s' (Error: %d)"
+ swe "Kan inte läsa från bibliotek '%-.192s' (Felkod: %d)"
+ ukr "Ðе можу прочитати теку '%-.192s' (помилка: %d)"
+ER_CANT_SET_WD
+ cze "Nemohu zm-Běnit adresář na '%-.192s' (chybový kód: %d)"
+ dan "Kan ikke skifte folder til '%-.192s' (Fejlkode: %d)"
+ nla "Kan de directory niet veranderen naar '%-.192s' (Errcode: %d)"
+ eng "Can't change dir to '%-.192s' (errno: %d)"
+ jps "'%-.192s' ディレクトリ㫠chdir ã§ãã¾ã›ã‚“.(errno: %d)",
+ est "Ei suuda siseneda kataloogi '%-.192s' (veakood: %d)"
+ fre "Ne peut changer le répertoire pour '%-.192s' (Errcode: %d)"
+ ger "Kann nicht in das Verzeichnis '%-.192s' wechseln (Fehler: %d)"
+ greek "ΑδÏνατη η αλλαγή του Ï„Ïέχοντος καταλόγου σε '%-.192s' (κωδικός λάθους: %d)"
+ hun "Konyvtarvaltas nem lehetseges a(z) '%-.192s'-ba. (hibakod: %d)"
+ ita "Impossibile cambiare la directory in '%-.192s' (errno: %d)"
+ jpn "'%-.192s' ディレクトリ㫠chdir ã§ãã¾ã›ã‚“.(errno: %d)"
+ kor "'%-.192s'디렉토리로 ì´ë™í•  수 없었습니다. (ì—러번호: %d)"
+ nor "Kan ikke skifte katalog til '%-.192s' (Feilkode: %d)"
+ norwegian-ny "Kan ikkje skifte katalog til '%-.192s' (Feilkode: %d)"
+ pol "Nie można zmienić katalogu na '%-.192s' (Kod błędu: %d)"
+ por "Não pode mudar para o diretório '%-.192s' (erro no. %d)"
+ rum "Nu pot sa schimb directorul '%-.192s' (Eroare: %d)"
+ rus "Ðевозможно перейти в каталог '%-.192s' (ошибка: %d)"
+ serbian "Ne mogu da promenim direktorijum na '%-.192s' (errno: %d)"
+ slo "Nemôžem vojsť do adresára '%-.192s' (chybový kód: %d)"
+ spa "No puedo cambiar al directorio de '%-.192s' (Error: %d)"
+ swe "Kan inte byta till '%-.192s' (Felkod: %d)"
+ ukr "Ðе можу перейти у теку '%-.192s' (помилка: %d)"
+ER_CHECKREAD
+ cze "Z-Báznam byl zmÄ›nÄ›n od posledního Ätení v tabulce '%-.192s'"
+ dan "Posten er ændret siden sidste læsning '%-.192s'"
+ nla "Record is veranderd sinds de laatste lees activiteit in de tabel '%-.192s'"
+ eng "Record has changed since last read in table '%-.192s'"
+ est "Kirje tabelis '%-.192s' on muutunud viimasest lugemisest saadik"
+ fre "Enregistrement modifié depuis sa dernière lecture dans la table '%-.192s'"
+ ger "Datensatz hat sich seit dem letzten Zugriff auf Tabelle '%-.192s' geändert"
+ greek "Η εγγÏαφή έχει αλλάξει από την τελευταία φοÏά που ανασÏÏθηκε από τον πίνακα '%-.192s'"
+ hun "A(z) '%-.192s' tablaban talalhato rekord megvaltozott az utolso olvasas ota"
+ ita "Il record e` cambiato dall'ultima lettura della tabella '%-.192s'"
+ kor "í…Œì´ë¸” '%-.192s'ì—ì„œ 마지막으로 ì½ì€ 후 Recordê°€ 변경ë˜ì—ˆìŠµë‹ˆë‹¤."
+ nor "Posten har blitt endret siden den ble lest '%-.192s'"
+ norwegian-ny "Posten har vorte endra sidan den sist vart lesen '%-.192s'"
+ pol "Rekord został zmieniony od ostaniego odczytania z tabeli '%-.192s'"
+ por "Registro alterado desde a última leitura da tabela '%-.192s'"
+ rum "Cimpul a fost schimbat de la ultima citire a tabelei '%-.192s'"
+ rus "ЗапиÑÑŒ изменилаÑÑŒ Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ð° поÑледней выборки в таблице '%-.192s'"
+ serbian "Slog je promenjen od zadnjeg Äitanja tabele '%-.192s'"
+ slo "Záznam bol zmenený od posledného Äítania v tabuľke '%-.192s'"
+ spa "El registro ha cambiado desde la ultima lectura de la tabla '%-.192s'"
+ swe "Posten har förändrats sedan den lästes i register '%-.192s'"
+ ukr "Ð—Ð°Ð¿Ð¸Ñ Ð±ÑƒÐ»Ð¾ змінено з чаÑу оÑтаннього Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ð· таблиці '%-.192s'"
+ER_DISK_FULL
+ cze "Disk je pln-Bý (%s), Äekám na uvolnÄ›ní nÄ›jakého místa ..."
+ dan "Ikke mere diskplads (%s). Venter på at få frigjort plads..."
+ nla "Schijf vol (%s). Aan het wachten totdat er ruimte vrij wordt gemaakt..."
+ eng "Disk full (%s); waiting for someone to free some space..."
+ jps "Disk full (%s). 誰ã‹ãŒä½•ã‹ã‚’減らã™ã¾ã§ã¾ã£ã¦ãã ã•ã„...",
+ est "Ketas täis (%s). Ootame kuni tekib vaba ruumi..."
+ fre "Disque plein (%s). J'attend que quelqu'un libère de l'espace..."
+ ger "Festplatte voll (%s). Warte, bis jemand Platz schafft ..."
+ greek "Δεν υπάÏχει χώÏος στο δίσκο (%s). ΠαÏακαλώ, πεÏιμένετε να ελευθεÏωθεί χώÏος..."
+ hun "A lemez megtelt (%s)."
+ ita "Disco pieno (%s). In attesa che qualcuno liberi un po' di spazio..."
+ jpn "Disk full (%s). 誰ã‹ãŒä½•ã‹ã‚’減らã™ã¾ã§ã¾ã£ã¦ãã ã•ã„..."
+ kor "Disk full (%s). 다른 ì‚¬ëžŒì´ ì§€ìš¸ë•Œê¹Œì§€ 기다립니다..."
+ nor "Ikke mer diskplass (%s). Venter på å få frigjort plass..."
+ norwegian-ny "Ikkje meir diskplass (%s). Ventar på å få frigjort plass..."
+ pol "Dysk pełny (%s). Oczekiwanie na zwolnienie miejsca..."
+ por "Disco cheio (%s). Aguardando alguém liberar algum espaço..."
+ rum "Hard-disk-ul este plin (%s). Astept sa se elibereze ceva spatiu..."
+ rus "ДиÑк заполнен. (%s). Ожидаем, пока кто-то не уберет поÑле ÑÐµÐ±Ñ Ð¼ÑƒÑор..."
+ serbian "Disk je pun (%s). Čekam nekoga da dođe i oslobodi nešto mesta..."
+ slo "Disk je plný (%s), Äakám na uvoľnenie miesta..."
+ spa "Disco lleno (%s). Esperando para que se libere algo de espacio..."
+ swe "Disken är full (%s). Väntar tills det finns ledigt utrymme..."
+ ukr "ДиÑк заповнений (%s). Вичикую, доки звільнитьÑÑ Ñ‚Ñ€Ð¾Ñ…Ð¸ міÑцÑ..."
+ER_DUP_KEY 23000
+ cze "Nemohu zapsat, zdvojen-Bý klÃ­Ä v tabulce '%-.192s'"
+ dan "Kan ikke skrive, flere ens nøgler i tabellen '%-.192s'"
+ nla "Kan niet schrijven, dubbele zoeksleutel in tabel '%-.192s'"
+ eng "Can't write; duplicate key in table '%-.192s'"
+ jps "table '%-.192s' ã« key ãŒé‡è¤‡ã—ã¦ã„ã¦æ›¸ãã“ã‚ã¾ã›ã‚“",
+ est "Ei saa kirjutada, korduv võti tabelis '%-.192s'"
+ fre "Ecriture impossible, doublon dans une clé de la table '%-.192s'"
+ ger "Kann nicht speichern, Grund: doppelter Schlüssel in Tabelle '%-.192s'"
+ greek "Δεν είναι δυνατή η καταχώÏηση, η τιμή υπάÏχει ήδη στον πίνακα '%-.192s'"
+ hun "Irasi hiba, duplikalt kulcs a '%-.192s' tablaban."
+ ita "Scrittura impossibile: chiave duplicata nella tabella '%-.192s'"
+ jpn "table '%-.192s' ã« key ãŒé‡è¤‡ã—ã¦ã„ã¦æ›¸ãã“ã‚ã¾ã›ã‚“"
+ kor "기ë¡í•  수 ì—†ì니다., í…Œì´ë¸” '%-.192s'ì—ì„œ 중복 키"
+ nor "Kan ikke skrive, flere like nøkler i tabellen '%-.192s'"
+ norwegian-ny "Kan ikkje skrive, flere like nyklar i tabellen '%-.192s'"
+ pol "Nie można zapisać, powtórzone klucze w tabeli '%-.192s'"
+ por "Não pode gravar. Chave duplicada na tabela '%-.192s'"
+ rum "Nu pot sa scriu (can't write), cheie duplicata in tabela '%-.192s'"
+ rus "Ðевозможно произвеÑти запиÑÑŒ, дублирующийÑÑ ÐºÐ»ÑŽÑ‡ в таблице '%-.192s'"
+ serbian "Ne mogu da piÅ¡em poÅ¡to postoji duplirani kljuÄ u tabeli '%-.192s'"
+ slo "Nemôžem zapísaÅ¥, duplikát kľúÄa v tabuľke '%-.192s'"
+ spa "No puedo escribir, clave duplicada en la tabla '%-.192s'"
+ swe "Kan inte skriva, dubbel söknyckel i register '%-.192s'"
+ ukr "Ðе можу запиÑати, дублюючийÑÑ ÐºÐ»ÑŽÑ‡ в таблиці '%-.192s'"
+ER_ERROR_ON_CLOSE
+ cze "Chyba p-Bři zavírání '%-.192s' (chybový kód: %d)"
+ dan "Fejl ved lukning af '%-.192s' (Fejlkode: %d)"
+ nla "Fout bij het sluiten van '%-.192s' (Errcode: %d)"
+ eng "Error on close of '%-.192s' (errno: %d)"
+ est "Viga faili '%-.192s' sulgemisel (veakood: %d)"
+ fre "Erreur a la fermeture de '%-.192s' (Errcode: %d)"
+ ger "Fehler beim Schließen von '%-.192s' (Fehler: %d)"
+ greek "ΠαÏουσιάστηκε Ï€Ïόβλημα κλείνοντας το '%-.192s' (κωδικός λάθους: %d)"
+ hun "Hiba a(z) '%-.192s' zarasakor. (hibakod: %d)"
+ ita "Errore durante la chiusura di '%-.192s' (errno: %d)"
+ kor "'%-.192s'닫는 중 ì—러 (ì—러번호: %d)"
+ nor "Feil ved lukking av '%-.192s' (Feilkode: %d)"
+ norwegian-ny "Feil ved lukking av '%-.192s' (Feilkode: %d)"
+ pol "Bł?d podczas zamykania '%-.192s' (Kod błędu: %d)"
+ por "Erro ao fechar '%-.192s' (erro no. %d)"
+ rum "Eroare inchizind '%-.192s' (errno: %d)"
+ rus "Ошибка при закрытии '%-.192s' (ошибка: %d)"
+ serbian "Greška pri zatvaranju '%-.192s' (errno: %d)"
+ slo "Chyba pri zatváraní '%-.192s' (chybový kód: %d)"
+ spa "Error en el cierre de '%-.192s' (Error: %d)"
+ swe "Fick fel vid stängning av '%-.192s' (Felkod: %d)"
+ ukr "Ðе можу закрити '%-.192s' (помилка: %d)"
+ER_ERROR_ON_READ
+ cze "Chyba p-BÅ™i Ätení souboru '%-.200s' (chybový kód: %d)"
+ dan "Fejl ved læsning af '%-.200s' (Fejlkode: %d)"
+ nla "Fout bij het lezen van file '%-.200s' (Errcode: %d)"
+ eng "Error reading file '%-.200s' (errno: %d)"
+ jps "'%-.200s' ファイルã®èª­ã¿è¾¼ã¿ã‚¨ãƒ©ãƒ¼ (errno: %d)",
+ est "Viga faili '%-.200s' lugemisel (veakood: %d)"
+ fre "Erreur en lecture du fichier '%-.200s' (Errcode: %d)"
+ ger "Fehler beim Lesen der Datei '%-.200s' (Fehler: %d)"
+ greek "ΠÏόβλημα κατά την ανάγνωση του αÏχείου '%-.200s' (κωδικός λάθους: %d)"
+ hun "Hiba a '%-.200s'file olvasasakor. (hibakod: %d)"
+ ita "Errore durante la lettura del file '%-.200s' (errno: %d)"
+ jpn "'%-.200s' ファイルã®èª­ã¿è¾¼ã¿ã‚¨ãƒ©ãƒ¼ (errno: %d)"
+ kor "'%-.200s'í™”ì¼ ì½ê¸° ì—러 (ì—러번호: %d)"
+ nor "Feil ved lesing av '%-.200s' (Feilkode: %d)"
+ norwegian-ny "Feil ved lesing av '%-.200s' (Feilkode: %d)"
+ pol "Bł?d podczas odczytu pliku '%-.200s' (Kod błędu: %d)"
+ por "Erro ao ler arquivo '%-.200s' (erro no. %d)"
+ rum "Eroare citind fisierul '%-.200s' (errno: %d)"
+ rus "Ошибка Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð° '%-.200s' (ошибка: %d)"
+ serbian "GreÅ¡ka pri Äitanju file-a '%-.200s' (errno: %d)"
+ slo "Chyba pri Äítaní súboru '%-.200s' (chybový kód: %d)"
+ spa "Error leyendo el fichero '%-.200s' (Error: %d)"
+ swe "Fick fel vid läsning av '%-.200s' (Felkod %d)"
+ ukr "Ðе можу прочитати файл '%-.200s' (помилка: %d)"
+ER_ERROR_ON_RENAME
+ cze "Chyba p-Bři přejmenování '%-.210s' na '%-.210s' (chybový kód: %d)"
+ dan "Fejl ved omdøbning af '%-.210s' til '%-.210s' (Fejlkode: %d)"
+ nla "Fout bij het hernoemen van '%-.210s' naar '%-.210s' (Errcode: %d)"
+ eng "Error on rename of '%-.210s' to '%-.210s' (errno: %d)"
+ jps "'%-.210s' ã‚’ '%-.210s' ã« rename ã§ãã¾ã›ã‚“ (errno: %d)",
+ est "Viga faili '%-.210s' ümbernimetamisel '%-.210s'-ks (veakood: %d)"
+ fre "Erreur en renommant '%-.210s' en '%-.210s' (Errcode: %d)"
+ ger "Fehler beim Umbenennen von '%-.210s' in '%-.210s' (Fehler: %d)"
+ greek "ΠÏόβλημα κατά την μετονομασία του αÏχείου '%-.210s' to '%-.210s' (κωδικός λάθους: %d)"
+ hun "Hiba a '%-.210s' file atnevezesekor '%-.210s'. (hibakod: %d)"
+ ita "Errore durante la rinominazione da '%-.210s' a '%-.210s' (errno: %d)"
+ jpn "'%-.210s' ã‚’ '%-.210s' ã« rename ã§ãã¾ã›ã‚“ (errno: %d)"
+ kor "'%-.210s'를 '%-.210s'ë¡œ ì´ë¦„ 변경중 ì—러 (ì—러번호: %d)"
+ nor "Feil ved omdøping av '%-.210s' til '%-.210s' (Feilkode: %d)"
+ norwegian-ny "Feil ved omdøyping av '%-.210s' til '%-.210s' (Feilkode: %d)"
+ pol "Bł?d podczas zmieniania nazwy '%-.210s' na '%-.210s' (Kod błędu: %d)"
+ por "Erro ao renomear '%-.210s' para '%-.210s' (erro no. %d)"
+ rum "Eroare incercind sa renumesc '%-.210s' in '%-.210s' (errno: %d)"
+ rus "Ошибка при переименовании '%-.210s' в '%-.210s' (ошибка: %d)"
+ serbian "Greška pri promeni imena '%-.210s' na '%-.210s' (errno: %d)"
+ slo "Chyba pri premenovávaní '%-.210s' na '%-.210s' (chybový kód: %d)"
+ spa "Error en el renombrado de '%-.210s' a '%-.210s' (Error: %d)"
+ swe "Kan inte byta namn från '%-.210s' till '%-.210s' (Felkod: %d)"
+ ukr "Ðе можу перейменувати '%-.210s' у '%-.210s' (помилка: %d)"
+ER_ERROR_ON_WRITE
+ cze "Chyba p-Bři zápisu do souboru '%-.200s' (chybový kód: %d)"
+ dan "Fejl ved skriving av filen '%-.200s' (Fejlkode: %d)"
+ nla "Fout bij het wegschrijven van file '%-.200s' (Errcode: %d)"
+ eng "Error writing file '%-.200s' (errno: %d)"
+ jps "'%-.200s' ファイルを書ã事ãŒã§ãã¾ã›ã‚“ (errno: %d)",
+ est "Viga faili '%-.200s' kirjutamisel (veakood: %d)"
+ fre "Erreur d'écriture du fichier '%-.200s' (Errcode: %d)"
+ ger "Fehler beim Speichern der Datei '%-.200s' (Fehler: %d)"
+ greek "ΠÏόβλημα κατά την αποθήκευση του αÏχείου '%-.200s' (κωδικός λάθους: %d)"
+ hun "Hiba a '%-.200s' file irasakor. (hibakod: %d)"
+ ita "Errore durante la scrittura del file '%-.200s' (errno: %d)"
+ jpn "'%-.200s' ファイルを書ã事ãŒã§ãã¾ã›ã‚“ (errno: %d)"
+ kor "'%-.200s'í™”ì¼ ê¸°ë¡ ì¤‘ ì—러 (ì—러번호: %d)"
+ nor "Feil ved skriving av fila '%-.200s' (Feilkode: %d)"
+ norwegian-ny "Feil ved skriving av fila '%-.200s' (Feilkode: %d)"
+ pol "Bł?d podczas zapisywania pliku '%-.200s' (Kod błędu: %d)"
+ por "Erro ao gravar arquivo '%-.200s' (erro no. %d)"
+ rum "Eroare scriind fisierul '%-.200s' (errno: %d)"
+ rus "Ошибка запиÑи в файл '%-.200s' (ошибка: %d)"
+ serbian "Greška pri upisu '%-.200s' (errno: %d)"
+ slo "Chyba pri zápise do súboru '%-.200s' (chybový kód: %d)"
+ spa "Error escribiendo el archivo '%-.200s' (Error: %d)"
+ swe "Fick fel vid skrivning till '%-.200s' (Felkod %d)"
+ ukr "Ðе можу запиÑати файл '%-.200s' (помилка: %d)"
+ER_FILE_USED
+ cze "'%-.192s' je zam-BÄen proti zmÄ›nám"
+ dan "'%-.192s' er låst mod opdateringer"
+ nla "'%-.192s' is geblokeerd tegen veranderingen"
+ eng "'%-.192s' is locked against change"
+ jps "'%-.192s' ã¯ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã™",
+ est "'%-.192s' on lukustatud muudatuste vastu"
+ fre "'%-.192s' est verrouillé contre les modifications"
+ ger "'%-.192s' ist für Änderungen gesperrt"
+ greek "'%-.192s' δεν επιτÏέπονται αλλαγές"
+ hun "'%-.192s' a valtoztatas ellen zarolva"
+ ita "'%-.192s' e` soggetto a lock contro i cambiamenti"
+ jpn "'%-.192s' ã¯ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã™"
+ kor "'%-.192s'ê°€ 변경할 수 ì—†ë„ë¡ ìž ê²¨ìžˆì니다."
+ nor "'%-.192s' er låst mot oppdateringer"
+ norwegian-ny "'%-.192s' er låst mot oppdateringar"
+ pol "'%-.192s' jest zablokowany na wypadek zmian"
+ por "'%-.192s' está com travamento contra alterações"
+ rum "'%-.192s' este blocat pentry schimbari (loccked against change)"
+ rus "'%-.192s' заблокирован Ð´Ð»Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹"
+ serbian "'%-.192s' je zakljuÄan za upis"
+ slo "'%-.192s' je zamknutý proti zmenám"
+ spa "'%-.192s' esta bloqueado contra cambios"
+ swe "'%-.192s' är låst mot användning"
+ ukr "'%-.192s' заблокований на внеÑÐµÐ½Ð½Ñ Ð·Ð¼Ñ–Ð½"
+ER_FILSORT_ABORT
+ cze "T-Břídění přerušeno"
+ dan "Sortering afbrudt"
+ nla "Sorteren afgebroken"
+ eng "Sort aborted"
+ jps "Sort 中断",
+ est "Sorteerimine katkestatud"
+ fre "Tri alphabétique abandonné"
+ ger "Sortiervorgang abgebrochen"
+ greek "Η διαδικασία ταξινόμισης ακυÏώθηκε"
+ hun "Sikertelen rendezes"
+ ita "Operazione di ordinamento abbandonata"
+ jpn "Sort 中断"
+ kor "소트가 중단ë˜ì—ˆìŠµë‹ˆë‹¤."
+ nor "Sortering avbrutt"
+ norwegian-ny "Sortering avbrote"
+ pol "Sortowanie przerwane"
+ por "Ordenação abortada"
+ rum "Sortare intrerupta"
+ rus "Сортировка прервана"
+ serbian "Sortiranje je prekinuto"
+ slo "Triedenie prerušené"
+ spa "Ordeancion cancelada"
+ swe "Sorteringen avbruten"
+ ukr "Ð¡Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿ÐµÑ€ÐµÑ€Ð²Ð°Ð½Ð¾"
+ER_FORM_NOT_FOUND
+ cze "Pohled '%-.192s' pro '%-.192s' neexistuje"
+ dan "View '%-.192s' eksisterer ikke for '%-.192s'"
+ nla "View '%-.192s' bestaat niet voor '%-.192s'"
+ eng "View '%-.192s' doesn't exist for '%-.192s'"
+ jps "View '%-.192s' ㌠'%-.192s' ã«å®šç¾©ã•ã‚Œã¦ã„ã¾ã›ã‚“",
+ est "Vaade '%-.192s' ei eksisteeri '%-.192s' jaoks"
+ fre "La vue (View) '%-.192s' n'existe pas pour '%-.192s'"
+ ger "View '%-.192s' existiert für '%-.192s' nicht"
+ greek "Το View '%-.192s' δεν υπάÏχει για '%-.192s'"
+ hun "A(z) '%-.192s' nezet nem letezik a(z) '%-.192s'-hoz"
+ ita "La view '%-.192s' non esiste per '%-.192s'"
+ jpn "View '%-.192s' ㌠'%-.192s' ã«å®šç¾©ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+ kor "ë·° '%-.192s'ê°€ '%-.192s'ì—서는 존재하지 ì•Šì니다."
+ nor "View '%-.192s' eksisterer ikke for '%-.192s'"
+ norwegian-ny "View '%-.192s' eksisterar ikkje for '%-.192s'"
+ pol "Widok '%-.192s' nie istnieje dla '%-.192s'"
+ por "Visão '%-.192s' não existe para '%-.192s'"
+ rum "View '%-.192s' nu exista pentru '%-.192s'"
+ rus "ПредÑтавление '%-.192s' не ÑущеÑтвует Ð´Ð»Ñ '%-.192s'"
+ serbian "View '%-.192s' ne postoji za '%-.192s'"
+ slo "Pohľad '%-.192s' neexistuje pre '%-.192s'"
+ spa "La vista '%-.192s' no existe para '%-.192s'"
+ swe "Formulär '%-.192s' finns inte i '%-.192s'"
+ ukr "ВиглÑд '%-.192s' не Ñ–Ñнує Ð´Ð»Ñ '%-.192s'"
+ER_GET_ERRNO
+ cze "Obsluha tabulky vr-Bátila chybu %d"
+ dan "Modtog fejl %d fra tabel håndteringen"
+ nla "Fout %d van tabel handler"
+ eng "Got error %d from storage engine"
+ est "Tabeli handler tagastas vea %d"
+ fre "Reçu l'erreur %d du handler de la table"
+ ger "Fehler %d (Speicher-Engine)"
+ greek "Ελήφθη μήνυμα λάθους %d από τον χειÏιστή πίνακα (table handler)"
+ hun "%d hibajelzes a tablakezelotol"
+ ita "Rilevato l'errore %d dal gestore delle tabelle"
+ jpn "Got error %d from table handler"
+ kor "í…Œì´ë¸” handlerì—ì„œ %d ì—러가 ë°œìƒ í•˜ì˜€ìŠµë‹ˆë‹¤."
+ nor "Mottok feil %d fra tabell håndterer"
+ norwegian-ny "Mottok feil %d fra tabell handterar"
+ pol "Otrzymano bł?d %d z obsługi tabeli"
+ por "Obteve erro %d no manipulador de tabelas"
+ rum "Eroarea %d obtinuta din handlerul tabelei"
+ rus "Получена ошибка %d от обработчика таблиц"
+ serbian "Handler tabela je vratio grešku %d"
+ slo "Obsluha tabuľky vrátila chybu %d"
+ spa "Error %d desde el manejador de la tabla"
+ swe "Fick felkod %d från databashanteraren"
+ ukr "Отримано помилку %d від деÑкриптора таблиці"
+ER_ILLEGAL_HA
+ cze "Obsluha tabulky '%-.192s' nem-Bá tento parametr"
+ dan "Denne mulighed eksisterer ikke for tabeltypen '%-.192s'"
+ nla "Tabel handler voor '%-.192s' heeft deze optie niet"
+ eng "Table storage engine for '%-.192s' doesn't have this option"
+ est "Tabeli '%-.192s' handler ei toeta antud operatsiooni"
+ fre "Le handler de la table '%-.192s' n'a pas cette option"
+ ger "Diese Option gibt es nicht (Speicher-Engine für '%-.192s')"
+ greek "Ο χειÏιστής πίνακα (table handler) για '%-.192s' δεν διαθέτει αυτή την επιλογή"
+ hun "A(z) '%-.192s' tablakezelonek nincs ilyen opcioja"
+ ita "Il gestore delle tabelle per '%-.192s' non ha questa opzione"
+ jpn "Table handler for '%-.192s' doesn't have this option"
+ kor "'%-.192s'ì˜ í…Œì´ë¸” handler는 ì´ëŸ¬í•œ ì˜µì…˜ì„ ì œê³µí•˜ì§€ ì•Šì니다."
+ nor "Tabell håndtereren for '%-.192s' har ikke denne muligheten"
+ norwegian-ny "Tabell håndteraren for '%-.192s' har ikkje denne moglegheita"
+ pol "Obsługa tabeli '%-.192s' nie posiada tej opcji"
+ por "Manipulador de tabela para '%-.192s' não tem esta opção"
+ rum "Handlerul tabelei pentru '%-.192s' nu are aceasta optiune"
+ rus "Обработчик таблицы '%-.192s' не поддерживает Ñту возможноÑÑ‚ÑŒ"
+ serbian "Handler tabela za '%-.192s' nema ovu opciju"
+ slo "Obsluha tabuľky '%-.192s' nemá tento parameter"
+ spa "El manejador de la tabla de '%-.192s' no tiene esta opcion"
+ swe "Tabellhanteraren for tabell '%-.192s' stödjer ej detta"
+ ukr "ДеÑкриптор таблиці '%-.192s' не має цієї влаÑтивоÑÑ‚Ñ–"
+ER_KEY_NOT_FOUND
+ cze "Nemohu naj-Bít záznam v '%-.192s'"
+ dan "Kan ikke finde posten i '%-.192s'"
+ nla "Kan record niet vinden in '%-.192s'"
+ eng "Can't find record in '%-.192s'"
+ jps "'%-.192s'ã®ãªã‹ã«ãƒ¬ã‚³ãƒ¼ãƒ‰ãŒè¦‹ä»˜ã‹ã‚Šã¾ã›ã‚“",
+ est "Ei suuda leida kirjet '%-.192s'-s"
+ fre "Ne peut trouver l'enregistrement dans '%-.192s'"
+ ger "Kann Datensatz in '%-.192s' nicht finden"
+ greek "ΑδÏνατη η ανεÏÏεση εγγÏαφής στο '%-.192s'"
+ hun "Nem talalhato a rekord '%-.192s'-ben"
+ ita "Impossibile trovare il record in '%-.192s'"
+ jpn "'%-.192s'ã®ãªã‹ã«ãƒ¬ã‚³ãƒ¼ãƒ‰ãŒè¦‹ä»˜ã‹ã‚Šã¾ã›ã‚“"
+ kor "'%-.192s'ì—ì„œ 레코드를 ì°¾ì„ ìˆ˜ ì—†ì니다."
+ nor "Kan ikke finne posten i '%-.192s'"
+ norwegian-ny "Kan ikkje finne posten i '%-.192s'"
+ pol "Nie można znaleĽć rekordu w '%-.192s'"
+ por "Não pode encontrar registro em '%-.192s'"
+ rum "Nu pot sa gasesc recordul in '%-.192s'"
+ rus "Ðевозможно найти запиÑÑŒ в '%-.192s'"
+ serbian "Ne mogu da pronađem slog u '%-.192s'"
+ slo "Nemôžem nájsť záznam v '%-.192s'"
+ spa "No puedo encontrar el registro en '%-.192s'"
+ swe "Hittar inte posten '%-.192s'"
+ ukr "Ðе можу запиÑати у '%-.192s'"
+ER_NOT_FORM_FILE
+ cze "Nespr-Bávná informace v souboru '%-.200s'"
+ dan "Forkert indhold i: '%-.200s'"
+ nla "Verkeerde info in file: '%-.200s'"
+ eng "Incorrect information in file: '%-.200s'"
+ jps "ファイル '%-.200s' ã® info ãŒé–“é•ã£ã¦ã„るよã†ã§ã™",
+ est "Vigane informatsioon failis '%-.200s'"
+ fre "Information erronnée dans le fichier: '%-.200s'"
+ ger "Falsche Information in Datei '%-.200s'"
+ greek "Λάθος πληÏοφοÏίες στο αÏχείο: '%-.200s'"
+ hun "Ervenytelen info a file-ban: '%-.200s'"
+ ita "Informazione errata nel file: '%-.200s'"
+ jpn "ファイル '%-.200s' ã® info ãŒé–“é•ã£ã¦ã„るよã†ã§ã™"
+ kor "í™”ì¼ì˜ 부정확한 ì •ë³´: '%-.200s'"
+ nor "Feil informasjon i filen: '%-.200s'"
+ norwegian-ny "Feil informasjon i fila: '%-.200s'"
+ pol "Niewła?ciwa informacja w pliku: '%-.200s'"
+ por "Informação incorreta no arquivo '%-.200s'"
+ rum "Informatie incorecta in fisierul: '%-.200s'"
+ rus "ÐÐµÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð² файле '%-.200s'"
+ serbian "Pogrešna informacija u file-u: '%-.200s'"
+ slo "Nesprávna informácia v súbore: '%-.200s'"
+ spa "Informacion erronea en el archivo: '%-.200s'"
+ swe "Felaktig fil: '%-.200s'"
+ ukr "Хибна Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ñƒ файлі: '%-.200s'"
+ER_NOT_KEYFILE
+ cze "Nespr-Bávný klÃ­Ä pro tabulku '%-.200s'; pokuste se ho opravit"
+ dan "Fejl i indeksfilen til tabellen '%-.200s'; prøv at reparere den"
+ nla "Verkeerde zoeksleutel file voor tabel: '%-.200s'; probeer het te repareren"
+ eng "Incorrect key file for table '%-.200s'; try to repair it"
+ jps "'%-.200s' テーブル㮠key file ãŒé–“é•ã£ã¦ã„るよã†ã§ã™. 修復をã—ã¦ãã ã•ã„",
+ est "Tabeli '%-.200s' võtmefail on vigane; proovi seda parandada"
+ fre "Index corrompu dans la table: '%-.200s'; essayez de le réparer"
+ ger "Fehlerhafte Index-Datei für Tabelle '%-.200s'; versuche zu reparieren"
+ greek "Λάθος αÏχείο ταξινόμισης (key file) για τον πίνακα: '%-.200s'; ΠαÏακαλώ, διοÏθώστε το!"
+ hun "Ervenytelen kulcsfile a tablahoz: '%-.200s'; probalja kijavitani!"
+ ita "File chiave errato per la tabella : '%-.200s'; prova a riparalo"
+ jpn "'%-.200s' テーブル㮠key file ãŒé–“é•ã£ã¦ã„るよã†ã§ã™. 修復をã—ã¦ãã ã•ã„"
+ kor "'%-.200s' í…Œì´ë¸”ì˜ ë¶€ì •í™•í•œ 키 존재. 수정하시오!"
+ nor "Tabellen '%-.200s' har feil i nøkkelfilen; forsøk å reparer den"
+ norwegian-ny "Tabellen '%-.200s' har feil i nykkelfila; prøv å reparere den"
+ pol "Niewła?ciwy plik kluczy dla tabeli: '%-.200s'; spróbuj go naprawić"
+ por "Arquivo de índice incorreto para tabela '%-.200s'; tente repará-lo"
+ rum "Cheia fisierului incorecta pentru tabela: '%-.200s'; incearca s-o repari"
+ rus "Ðекорректный индекÑный файл Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹: '%-.200s'. Попробуйте воÑÑтановить его"
+ serbian "Pogrešan key file za tabelu: '%-.200s'; probajte da ga ispravite"
+ slo "Nesprávny kÄ¾ÃºÄ pre tabuľku '%-.200s'; pokúste sa ho opraviÅ¥"
+ spa "Clave de archivo erronea para la tabla: '%-.200s'; intente repararlo"
+ swe "Fatalt fel vid hantering av register '%-.200s'; kör en reparation"
+ ukr "Хибний файл ключей Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ–: '%-.200s'; Спробуйте його відновити"
+ER_OLD_KEYFILE
+ cze "Star-Bý klíÄový soubor pro '%-.192s'; opravte ho."
+ dan "Gammel indeksfil for tabellen '%-.192s'; reparer den"
+ nla "Oude zoeksleutel file voor tabel '%-.192s'; repareer het!"
+ eng "Old key file for table '%-.192s'; repair it!"
+ jps "'%-.192s' テーブルã¯å¤ã„å½¢å¼ã® key file ã®ã‚ˆã†ã§ã™; 修復をã—ã¦ãã ã•ã„",
+ est "Tabeli '%-.192s' võtmefail on aegunud; paranda see!"
+ fre "Vieux fichier d'index pour la table '%-.192s'; réparez le!"
+ ger "Alte Index-Datei für Tabelle '%-.192s'. Bitte reparieren"
+ greek "Παλαιό αÏχείο ταξινόμισης (key file) για τον πίνακα '%-.192s'; ΠαÏακαλώ, διοÏθώστε το!"
+ hun "Regi kulcsfile a '%-.192s'tablahoz; probalja kijavitani!"
+ ita "File chiave vecchio per la tabella '%-.192s'; riparalo!"
+ jpn "'%-.192s' テーブルã¯å¤ã„å½¢å¼ã® key file ã®ã‚ˆã†ã§ã™; 修復をã—ã¦ãã ã•ã„"
+ kor "'%-.192s' í…Œì´ë¸”ì˜ ì´ì „ë²„ì ¼ì˜ í‚¤ 존재. 수정하시오!"
+ nor "Gammel nøkkelfil for tabellen '%-.192s'; reparer den!"
+ norwegian-ny "Gammel nykkelfil for tabellen '%-.192s'; reparer den!"
+ pol "Plik kluczy dla tabeli '%-.192s' jest starego typu; napraw go!"
+ por "Arquivo de índice desatualizado para tabela '%-.192s'; repare-o!"
+ rum "Cheia fisierului e veche pentru tabela '%-.192s'; repar-o!"
+ rus "Старый индекÑный файл Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ '%-.192s'; отремонтируйте его!"
+ serbian "Zastareo key file za tabelu '%-.192s'; ispravite ga"
+ slo "Starý kľúÄový súbor pre '%-.192s'; opravte ho!"
+ spa "Clave de archivo antigua para la tabla '%-.192s'; reparelo!"
+ swe "Gammal nyckelfil '%-.192s'; reparera registret"
+ ukr "Старий файл ключей Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ– '%-.192s'; Відновіть його!"
+ER_OPEN_AS_READONLY
+ cze "'%-.192s' je jen pro -BÄtení"
+ dan "'%-.192s' er skrivebeskyttet"
+ nla "'%-.192s' is alleen leesbaar"
+ eng "Table '%-.192s' is read only"
+ jps "'%-.192s' ã¯èª­ã¿è¾¼ã¿å°‚用ã§ã™",
+ est "Tabel '%-.192s' on ainult lugemiseks"
+ fre "'%-.192s' est en lecture seulement"
+ ger "Tabelle '%-.192s' ist nur lesbar"
+ greek "'%-.192s' επιτÏέπεται μόνο η ανάγνωση"
+ hun "'%-.192s' irasvedett"
+ ita "'%-.192s' e` di sola lettura"
+ jpn "'%-.192s' ã¯èª­ã¿è¾¼ã¿å°‚用ã§ã™"
+ kor "í…Œì´ë¸” '%-.192s'는 ì½ê¸°ì „ìš© 입니다."
+ nor "'%-.192s' er skrivebeskyttet"
+ norwegian-ny "'%-.192s' er skrivetryggja"
+ pol "'%-.192s' jest tylko do odczytu"
+ por "Tabela '%-.192s' é somente para leitura"
+ rum "Tabela '%-.192s' e read-only"
+ rus "Таблица '%-.192s' предназначена только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ"
+ serbian "Tabelu '%-.192s' je dozvoljeno samo Äitati"
+ slo "'%-.192s' is ÄítaÅ¥ only"
+ spa "'%-.192s' es de solo lectura"
+ swe "'%-.192s' är skyddad mot förändring"
+ ukr "Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ '%-.192s' тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ"
+ER_OUTOFMEMORY HY001 S1001
+ cze "M-Bálo paměti. Přestartujte daemona a zkuste znovu (je potřeba %d bytů)"
+ dan "Ikke mere hukommelse. Genstart serveren og prøv igen (mangler %d bytes)"
+ nla "Geen geheugen meer. Herstart server en probeer opnieuw (%d bytes nodig)"
+ eng "Out of memory; restart server and try again (needed %d bytes)"
+ jps "Out of memory. デーモンをリスタートã—ã¦ã¿ã¦ãã ã•ã„ (%d bytes å¿…è¦)",
+ est "Mälu sai otsa. Proovi MariaDB uuesti käivitada (puudu jäi %d baiti)"
+ fre "Manque de mémoire. Redémarrez le démon et ré-essayez (%d octets nécessaires)"
+ ger "Kein Speicher vorhanden (%d Bytes benötigt). Bitte Server neu starten"
+ greek "Δεν υπάÏχει διαθέσιμη μνήμη. ΠÏοσπαθήστε πάλι, επανεκινώντας τη διαδικασία (demon) (χÏειάζονται %d bytes)"
+ hun "Nincs eleg memoria. Inditsa ujra a demont, es probalja ismet. (%d byte szukseges.)"
+ ita "Memoria esaurita. Fai ripartire il demone e riprova (richiesti %d bytes)"
+ jpn "Out of memory. デーモンをリスタートã—ã¦ã¿ã¦ãã ã•ã„ (%d bytes å¿…è¦)"
+ kor "Out of memory. ë°ëª¬ì„ 재 실행 후 다시 시작하시오 (needed %d bytes)"
+ nor "Ikke mer minne. Star på nytt tjenesten og prøv igjen (trengte %d byter)"
+ norwegian-ny "Ikkje meir minne. Start på nytt tenesten og prøv igjen (trengte %d bytar)"
+ pol "Zbyt mało pamięci. Uruchom ponownie demona i spróbuj ponownie (potrzeba %d bajtów)"
+ por "Sem memória. Reinicie o programa e tente novamente (necessita de %d bytes)"
+ rum "Out of memory. Porneste daemon-ul din nou si incearca inca o data (e nevoie de %d bytes)"
+ rus "ÐедоÑтаточно памÑти. ПерезапуÑтите Ñервер и попробуйте еще раз (нужно %d байт)"
+ serbian "Nema memorije. Restartujte MariaDB server i probajte ponovo (potrebno je %d byte-ova)"
+ slo "Málo pamäti. Reštartujte daemona a skúste znova (je potrebných %d bytov)"
+ spa "Memoria insuficiente. Reinicie el demonio e intentelo otra vez (necesita %d bytes)"
+ swe "Oväntat slut på minnet, starta om programmet och försök på nytt (Behövde %d bytes)"
+ ukr "Брак пам'ÑÑ‚Ñ–. РеÑтартуйте Ñервер та Ñпробуйте знову (потрібно %d байтів)"
+ER_OUT_OF_SORTMEMORY HY001 S1001
+ cze "M-Bálo paměti pro třídění. Zvyšte velikost třídícího bufferu"
+ dan "Ikke mere sorteringshukommelse. Øg sorteringshukommelse (sort buffer size) for serveren"
+ nla "Geen geheugen om te sorteren. Verhoog de server sort buffer size"
+ eng "Out of sort memory, consider increasing server sort buffer size"
+ jps "Out of sort memory. sort buffer size ãŒè¶³ã‚Šãªã„よã†ã§ã™.",
+ est "Mälu sai sorteerimisel otsa. Suurenda MariaDB-i sorteerimispuhvrit"
+ fre "Manque de mémoire pour le tri. Augmentez-la."
+ ger "Kein Speicher zum Sortieren vorhanden. sort_buffer_size sollte im Server erhöht werden"
+ greek "Δεν υπάÏχει διαθέσιμη μνήμη για ταξινόμιση. Αυξήστε το sort buffer size για τη διαδικασία (demon)"
+ hun "Nincs eleg memoria a rendezeshez. Novelje a rendezo demon puffermeretet"
+ ita "Memoria per gli ordinamenti esaurita. Incrementare il 'sort_buffer' al demone"
+ jpn "Out of sort memory. sort buffer size ãŒè¶³ã‚Šãªã„よã†ã§ã™."
+ kor "Out of sort memory. daemon sort bufferì˜ í¬ê¸°ë¥¼ ì¦ê°€ì‹œí‚¤ì„¸ìš”"
+ nor "Ikke mer sorteringsminne. Vurder å øke sorteringsminnet (sort buffer size) for tjenesten"
+ norwegian-ny "Ikkje meir sorteringsminne. Vurder å auke sorteringsminnet (sorteringsbuffer storleik) for tenesten"
+ pol "Zbyt mało pamięci dla sortowania. Zwiększ wielko?ć bufora demona dla sortowania"
+ por "Não há memória suficiente para ordenação. Considere aumentar o tamanho do retentor (buffer) de ordenação."
+ rum "Out of memory pentru sortare. Largeste marimea buffer-ului pentru sortare in daemon (sort buffer size)"
+ rus "ÐедоÑтаточно памÑти Ð´Ð»Ñ Ñортировки. Увеличьте размер буфера Ñортировки на Ñервере"
+ serbian "Nema memorije za sortiranje. Povećajte veliÄinu sort buffer-a MariaDB server-u"
+ slo "Málo pamäti pre triedenie, zvýšte veľkosť triediaceho bufferu"
+ spa "Memoria de ordenacion insuficiente. Incremente el tamano del buffer de ordenacion"
+ swe "Sorteringsbufferten räcker inte till. Kontrollera startparametrarna"
+ ukr "Брак пам'ÑÑ‚Ñ– Ð´Ð»Ñ ÑортуваннÑ. Треба збільшити розмір буфера ÑÐ¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñƒ Ñервера"
+ER_UNEXPECTED_EOF
+ cze "Neo-BÄekávaný konec souboru pÅ™i Ätení '%-.192s' (chybový kód: %d)"
+ dan "Uventet afslutning på fil (eof) ved læsning af filen '%-.192s' (Fejlkode: %d)"
+ nla "Onverwachte eof gevonden tijdens het lezen van file '%-.192s' (Errcode: %d)"
+ eng "Unexpected EOF found when reading file '%-.192s' (errno: %d)"
+ jps "'%-.192s' ファイルを読ã¿è¾¼ã¿ä¸­ã« EOF ãŒäºˆæœŸã›ã¬æ‰€ã§ç¾ã‚Œã¾ã—ãŸ. (errno: %d)",
+ est "Ootamatu faililõpumärgend faili '%-.192s' lugemisel (veakood: %d)"
+ fre "Fin de fichier inattendue en lisant '%-.192s' (Errcode: %d)"
+ ger "Unerwartetes Ende beim Lesen der Datei '%-.192s' (Fehler: %d)"
+ greek "Κατά τη διάÏκεια της ανάγνωσης, βÏέθηκε απÏοσδόκητα το τέλος του αÏχείου '%-.192s' (κωδικός λάθους: %d)"
+ hun "Varatlan filevege-jel a '%-.192s'olvasasakor. (hibakod: %d)"
+ ita "Fine del file inaspettata durante la lettura del file '%-.192s' (errno: %d)"
+ jpn "'%-.192s' ファイルを読ã¿è¾¼ã¿ä¸­ã« EOF ãŒäºˆæœŸã›ã¬æ‰€ã§ç¾ã‚Œã¾ã—ãŸ. (errno: %d)"
+ kor "'%-.192s' í™”ì¼ì„ ì½ëŠ” ë„중 ìž˜ëª»ëœ eofì„ ë°œê²¬ (ì—러번호: %d)"
+ nor "Uventet slutt på fil (eof) ved lesing av filen '%-.192s' (Feilkode: %d)"
+ norwegian-ny "Uventa slutt på fil (eof) ved lesing av fila '%-.192s' (Feilkode: %d)"
+ pol "Nieoczekiwany 'eof' napotkany podczas czytania z pliku '%-.192s' (Kod błędu: %d)"
+ por "Encontrado fim de arquivo inesperado ao ler arquivo '%-.192s' (erro no. %d)"
+ rum "Sfirsit de fisier neasteptat in citirea fisierului '%-.192s' (errno: %d)"
+ rus "Ðеожиданный конец файла '%-.192s' (ошибка: %d)"
+ serbian "NeoÄekivani kraj pri Äitanju file-a '%-.192s' (errno: %d)"
+ slo "NeoÄakávaný koniec súboru pri Äítaní '%-.192s' (chybový kód: %d)"
+ spa "Inesperado fin de ficheroU mientras leiamos el archivo '%-.192s' (Error: %d)"
+ swe "Oväntat filslut vid läsning från '%-.192s' (Felkod: %d)"
+ ukr "Хибний кінець файлу '%-.192s' (помилка: %d)"
+ER_CON_COUNT_ERROR 08004
+ cze "P-Bříliš mnoho spojení"
+ dan "For mange forbindelser (connections)"
+ nla "Te veel verbindingen"
+ eng "Too many connections"
+ jps "接続ãŒå¤šã™ãŽã¾ã™",
+ est "Liiga palju samaaegseid ühendusi"
+ fre "Trop de connexions"
+ ger "Zu viele Verbindungen"
+ greek "ΥπάÏχουν πολλές συνδέσεις..."
+ hun "Tul sok kapcsolat"
+ ita "Troppe connessioni"
+ jpn "接続ãŒå¤šã™ãŽã¾ã™"
+ kor "너무 ë§Žì€ ì—°ê²°... max_connectionì„ ì¦ê°€ 시키시오..."
+ nor "For mange tilkoblinger (connections)"
+ norwegian-ny "For mange tilkoplingar (connections)"
+ pol "Zbyt wiele poł?czeń"
+ por "Excesso de conexões"
+ rum "Prea multe conectiuni"
+ rus "Слишком много Ñоединений"
+ serbian "Previše konekcija"
+ slo "Príliš mnoho spojení"
+ spa "Demasiadas conexiones"
+ swe "För många anslutningar"
+ ukr "Забагато з'єднань"
+ER_OUT_OF_RESOURCES
+ cze "M-Bálo prostoru/paměti pro thread"
+ dan "Udgået for tråde/hukommelse"
+ nla "Geen thread geheugen meer; controleer of mysqld of andere processen al het beschikbare geheugen gebruikt. Zo niet, dan moet u wellicht 'ulimit' gebruiken om mysqld toe te laten meer geheugen te benutten, of u kunt extra swap ruimte toevoegen"
+ eng "Out of memory; check if mysqld or some other process uses all available memory; if not, you may have to use 'ulimit' to allow mysqld to use more memory or you can add more swap space"
+ jps "Out of memory; mysqld ã‹ãã®ä»–ã®ãƒ—ロセスãŒãƒ¡ãƒ¢ãƒªãƒ¼ã‚’å…¨ã¦ä½¿ã£ã¦ã„ã‚‹ã‹ç¢ºèªã—ã¦ãã ã•ã„. メモリーを使ã„切ã£ã¦ã„ãªã„å ´åˆã€'ulimit' を設定ã—㦠mysqld ã®ãƒ¡ãƒ¢ãƒªãƒ¼ä½¿ç”¨é™ç•Œé‡ã‚’多ãã™ã‚‹ã‹ã€swap space を増やã—ã¦ã¿ã¦ãã ã•ã„",
+ est "Mälu sai otsa. Võimalik, et aitab swap-i lisamine või käsu 'ulimit' abil MariaDB-le rohkema mälu kasutamise lubamine"
+ fre "Manque de 'threads'/mémoire"
+ ger "Kein Speicher mehr vorhanden. Prüfen Sie, ob mysqld oder ein anderer Prozess den gesamten Speicher verbraucht. Wenn nicht, sollten Sie mit 'ulimit' dafür sorgen, dass mysqld mehr Speicher benutzen darf, oder mehr Swap-Speicher einrichten"
+ greek "ΠÏόβλημα με τη διαθέσιμη μνήμη (Out of thread space/memory)"
+ hun "Elfogyott a thread-memoria"
+ ita "Fine dello spazio/memoria per i thread"
+ jpn "Out of memory; mysqld ã‹ãã®ä»–ã®ãƒ—ロセスãŒãƒ¡ãƒ¢ãƒªãƒ¼ã‚’å…¨ã¦ä½¿ã£ã¦ã„ã‚‹ã‹ç¢ºèªã—ã¦ãã ã•ã„. メモリーを使ã„切ã£ã¦ã„ãªã„å ´åˆã€'ulimit' を設定ã—㦠mysqld ã®ãƒ¡ãƒ¢ãƒªãƒ¼ä½¿ç”¨é™ç•Œé‡ã‚’多ãã™ã‚‹ã‹ã€swap space を増やã—ã¦ã¿ã¦ãã ã•ã„"
+# This message failed to convert from euc-kr, skipped
+ 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)"
+ 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 викориÑтовувати більше пам'ÑÑ‚Ñ– або ви можете додати більше міÑÑ†Ñ Ð¿Ñ–Ð´ Ñвап"
+ER_BAD_HOST_ERROR 08S01
+ cze "Nemohu zjistit jm-Béno stroje pro Vaši adresu"
+ dan "Kan ikke få værtsnavn for din adresse"
+ nla "Kan de hostname niet krijgen van uw adres"
+ eng "Can't get hostname for your address"
+ jps "ãã® address ã® hostname ãŒå¼•ã‘ã¾ã›ã‚“.",
+ est "Ei suuda lahendada IP aadressi masina nimeks"
+ fre "Ne peut obtenir de hostname pour votre adresse"
+ ger "Kann Hostnamen für diese Adresse nicht erhalten"
+ greek "Δεν έγινε γνωστό το hostname για την address σας"
+ hun "A gepnev nem allapithato meg a cimbol"
+ ita "Impossibile risalire al nome dell'host dall'indirizzo (risoluzione inversa)"
+ jpn "ãã® address ã® hostname ãŒå¼•ã‘ã¾ã›ã‚“."
+ kor "ë‹¹ì‹ ì˜ ì»´í“¨í„°ì˜ í˜¸ìŠ¤íŠ¸ì´ë¦„ì„ ì–»ì„ ìˆ˜ ì—†ì니다."
+ nor "Kan ikke få tak i vertsnavn for din adresse"
+ norwegian-ny "Kan ikkje få tak i vertsnavn for di adresse"
+ pol "Nie można otrzymać nazwy hosta dla twojego adresu"
+ por "Não pode obter nome do 'host' para seu endereço"
+ rum "Nu pot sa obtin hostname-ul adresei tale"
+ rus "Ðевозможно получить Ð¸Ð¼Ñ Ñ…Ð¾Ñта Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ³Ð¾ адреÑа"
+ serbian "Ne mogu da dobijem ime host-a za vašu IP adresu"
+ slo "Nemôžem zistiť meno hostiteľa pre vašu adresu"
+ spa "No puedo obtener el nombre de maquina de tu direccion"
+ swe "Kan inte hitta 'hostname' för din adress"
+ ukr "Ðе можу визначити ім'Ñ Ñ…Ð¾Ñту Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ñ— адреÑи"
+ER_HANDSHAKE_ERROR 08S01
+ cze "Chyba p-Bři ustavování spojení"
+ dan "Forkert håndtryk (handshake)"
+ nla "Verkeerde handshake"
+ eng "Bad handshake"
+ est "Väär handshake"
+ fre "Mauvais 'handshake'"
+ ger "Ungültiger Handshake"
+ greek "Η αναγνώÏιση (handshake) δεν έγινε σωστά"
+ hun "A kapcsolatfelvetel nem sikerult (Bad handshake)"
+ ita "Negoziazione impossibile"
+ nor "Feil håndtrykk (handshake)"
+ norwegian-ny "Feil handtrykk (handshake)"
+ pol "ZÅ‚y uchwyt(handshake)"
+ por "Negociação de acesso falhou"
+ rum "Prost inceput de conectie (bad handshake)"
+ rus "Ðекорректное приветÑтвие"
+ serbian "LoÅ¡ poÄetak komunikacije (handshake)"
+ slo "Chyba pri nadväzovaní spojenia"
+ spa "Protocolo erroneo"
+ swe "Fel vid initiering av kommunikationen med klienten"
+ ukr "Ðевірна уÑтановка зв'Ñзку"
+ER_DBACCESS_DENIED_ERROR 42000
+ cze "P-Břístup pro uživatele '%s'@'%s' k databázi '%-.192s' není povolen"
+ dan "Adgang nægtet bruger: '%s'@'%s' til databasen '%-.192s'"
+ nla "Toegang geweigerd voor gebruiker: '%s'@'%s' naar database '%-.192s'"
+ eng "Access denied for user '%s'@'%s' to database '%-.192s'"
+ jps "ユーザー '%s'@'%s' ã® '%-.192s' データベースã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’æ‹’å¦ã—ã¾ã™",
+ est "Ligipääs keelatud kasutajale '%s'@'%s' andmebaasile '%-.192s'"
+ fre "Accès refusé pour l'utilisateur: '%s'@'%s'. Base '%-.192s'"
+ ger "Benutzer '%s'@'%s' hat keine Zugriffsberechtigung für Datenbank '%-.192s'"
+ greek "Δεν επιτέÏεται η Ï€Ïόσβαση στο χÏήστη: '%s'@'%s' στη βάση δεδομένων '%-.192s'"
+ hun "A(z) '%s'@'%s' felhasznalo szamara tiltott eleres az '%-.192s' adabazishoz."
+ ita "Accesso non consentito per l'utente: '%s'@'%s' al database '%-.192s'"
+ jpn "ユーザー '%s'@'%s' ã® '%-.192s' データベースã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’æ‹’å¦ã—ã¾ã™"
+ kor "'%s'@'%s' 사용ìžëŠ” '%-.192s' ë°ì´íƒ€ë² ì´ìŠ¤ì— ì ‘ê·¼ì´ ê±°ë¶€ ë˜ì—ˆìŠµë‹ˆë‹¤."
+ nor "Tilgang nektet for bruker: '%s'@'%s' til databasen '%-.192s' nektet"
+ norwegian-ny "Tilgang ikkje tillate for brukar: '%s'@'%s' til databasen '%-.192s' nekta"
+ por "Acesso negado para o usuário '%s'@'%s' ao banco de dados '%-.192s'"
+ rum "Acces interzis pentru utilizatorul: '%s'@'%s' la baza de date '%-.192s'"
+ rus "Ð”Ð»Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ '%s'@'%s' доÑтуп к базе данных '%-.192s' закрыт"
+ serbian "Pristup je zabranjen korisniku '%s'@'%s' za bazu '%-.192s'"
+ slo "Zakázaný prístup pre užívateľa: '%s'@'%s' k databázi '%-.192s'"
+ spa "Acceso negado para usuario: '%s'@'%s' para la base de datos '%-.192s'"
+ swe "Användare '%s'@'%s' är ej berättigad att använda databasen %-.192s"
+ ukr "ДоÑтуп заборонено Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувача: '%s'@'%s' до бази данних '%-.192s'"
+ER_ACCESS_DENIED_ERROR 28000
+ cze "P-Břístup pro uživatele '%s'@'%s' (s heslem %s)"
+ dan "Adgang nægtet bruger: '%s'@'%s' (Bruger adgangskode: %s)"
+ nla "Toegang geweigerd voor gebruiker: '%s'@'%s' (Wachtwoord gebruikt: %s)"
+ eng "Access denied for user '%s'@'%s' (using password: %s)"
+ jps "ユーザー '%s'@'%s' ã‚’æ‹’å¦ã—ã¾ã™.uUsing password: %s)",
+ est "Ligipääs keelatud kasutajale '%s'@'%s' (kasutab parooli: %s)"
+ fre "Accès refusé pour l'utilisateur: '%s'@'%s' (mot de passe: %s)"
+ ger "Benutzer '%s'@'%s' hat keine Zugriffsberechtigung (verwendetes Passwort: %s)"
+ greek "Δεν επιτέÏεται η Ï€Ïόσβαση στο χÏήστη: '%s'@'%s' (χÏήση password: %s)"
+ hun "A(z) '%s'@'%s' felhasznalo szamara tiltott eleres. (Hasznalja a jelszot: %s)"
+ ita "Accesso non consentito per l'utente: '%s'@'%s' (Password: %s)"
+ jpn "ユーザー '%s'@'%s' ã‚’æ‹’å¦ã—ã¾ã™.uUsing password: %s)"
+ kor "'%s'@'%s' 사용ìžëŠ” ì ‘ê·¼ì´ ê±°ë¶€ ë˜ì—ˆìŠµë‹ˆë‹¤. (using password: %s)"
+ nor "Tilgang nektet for bruker: '%s'@'%s' (Bruker passord: %s)"
+ norwegian-ny "Tilgang ikke tillate for brukar: '%s'@'%s' (Brukar passord: %s)"
+ por "Acesso negado para o usuário '%s'@'%s' (senha usada: %s)"
+ rum "Acces interzis pentru utilizatorul: '%s'@'%s' (Folosind parola: %s)"
+ rus "ДоÑтуп закрыт Ð´Ð»Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ '%s'@'%s' (был иÑпользован пароль: %s)"
+ serbian "Pristup je zabranjen korisniku '%s'@'%s' (koristi lozinku: '%s')"
+ slo "Zakázaný prístup pre užívateľa: '%s'@'%s' (použitie hesla: %s)"
+ spa "Acceso negado para usuario: '%s'@'%s' (Usando clave: %s)"
+ swe "Användare '%s'@'%s' är ej berättigad att logga in (Använder lösen: %s)"
+ ukr "ДоÑтуп заборонено Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувача: '%s'@'%s' (ВикориÑтано пароль: %s)"
+ER_NO_DB_ERROR 3D000
+ cze "Nebyla vybr-Bána žádná databáze"
+ dan "Ingen database valgt"
+ nla "Geen database geselecteerd"
+ eng "No database selected"
+ jps "データベースãŒé¸æŠžã•ã‚Œã¦ã„ã¾ã›ã‚“.",
+ est "Andmebaasi ei ole valitud"
+ fre "Aucune base n'a été sélectionnée"
+ ger "Keine Datenbank ausgewählt"
+ greek "Δεν επιλέχθηκε βάση δεδομένων"
+ hun "Nincs kivalasztott adatbazis"
+ ita "Nessun database selezionato"
+ jpn "データベースãŒé¸æŠžã•ã‚Œã¦ã„ã¾ã›ã‚“."
+ kor "ì„ íƒëœ ë°ì´íƒ€ë² ì´ìŠ¤ê°€ 없습니다."
+ nor "Ingen database valgt"
+ norwegian-ny "Ingen database vald"
+ pol "Nie wybrano żadnej bazy danych"
+ por "Nenhum banco de dados foi selecionado"
+ rum "Nici o baza de data nu a fost selectata inca"
+ rus "База данных не выбрана"
+ serbian "Ni jedna baza nije selektovana"
+ slo "Nebola vybraná databáza"
+ spa "Base de datos no seleccionada"
+ swe "Ingen databas i användning"
+ ukr "Базу данних не вибрано"
+ER_UNKNOWN_COM_ERROR 08S01
+ cze "Nezn-Bámý příkaz"
+ dan "Ukendt kommando"
+ nla "Onbekend commando"
+ eng "Unknown command"
+ jps "ãã®ã‚³ãƒžãƒ³ãƒ‰ã¯ä½•ï¼Ÿ",
+ est "Tundmatu käsk"
+ fre "Commande inconnue"
+ ger "Unbekannter Befehl"
+ greek "Αγνωστη εντολή"
+ hun "Ervenytelen parancs"
+ ita "Comando sconosciuto"
+ jpn "ãã®ã‚³ãƒžãƒ³ãƒ‰ã¯ä½•ï¼Ÿ"
+ kor "명령어가 뭔지 모르겠어요..."
+ nor "Ukjent kommando"
+ norwegian-ny "Ukjent kommando"
+ pol "Nieznana komenda"
+ por "Comando desconhecido"
+ rum "Comanda invalida"
+ rus "ÐеизвеÑÑ‚Ð½Ð°Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° коммуникационного протокола"
+ serbian "Nepoznata komanda"
+ slo "Neznámy príkaz"
+ spa "Comando desconocido"
+ swe "Okänt commando"
+ ukr "Ðевідома команда"
+ER_BAD_NULL_ERROR 23000
+ cze "Sloupec '%-.192s' nem-Bůže být null"
+ dan "Kolonne '%-.192s' kan ikke være NULL"
+ nla "Kolom '%-.192s' kan niet null zijn"
+ eng "Column '%-.192s' cannot be null"
+ jps "Column '%-.192s' 㯠null ã«ã¯ã§ããªã„ã®ã§ã™",
+ est "Tulp '%-.192s' ei saa omada nullväärtust"
+ fre "Le champ '%-.192s' ne peut être vide (null)"
+ ger "Feld '%-.192s' darf nicht NULL sein"
+ greek "Το πεδίο '%-.192s' δεν μποÏεί να είναι κενό (null)"
+ hun "A(z) '%-.192s' oszlop erteke nem lehet nulla"
+ ita "La colonna '%-.192s' non puo` essere nulla"
+ jpn "Column '%-.192s' 㯠null ã«ã¯ã§ããªã„ã®ã§ã™"
+ kor "칼럼 '%-.192s'는 ë„(Null)ì´ ë˜ë©´ 안ë©ë‹ˆë‹¤. "
+ nor "Kolonne '%-.192s' kan ikke vere null"
+ norwegian-ny "Kolonne '%-.192s' kan ikkje vere null"
+ pol "Kolumna '%-.192s' nie może być null"
+ por "Coluna '%-.192s' não pode ser vazia"
+ rum "Coloana '%-.192s' nu poate sa fie null"
+ rus "Столбец '%-.192s' не может принимать величину NULL"
+ serbian "Kolona '%-.192s' ne može biti NULL"
+ slo "Pole '%-.192s' nemôže byť null"
+ spa "La columna '%-.192s' no puede ser nula"
+ swe "Kolumn '%-.192s' får inte vara NULL"
+ ukr "Стовбець '%-.192s' не може бути нульовим"
+ER_BAD_DB_ERROR 42000
+ cze "Nezn-Bámá databáze '%-.192s'"
+ dan "Ukendt database '%-.192s'"
+ nla "Onbekende database '%-.192s'"
+ eng "Unknown database '%-.192s'"
+ jps "'%-.192s' ãªã‚“ã¦ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã¯çŸ¥ã‚Šã¾ã›ã‚“.",
+ est "Tundmatu andmebaas '%-.192s'"
+ fre "Base '%-.192s' inconnue"
+ ger "Unbekannte Datenbank '%-.192s'"
+ greek "Αγνωστη βάση δεδομένων '%-.192s'"
+ hun "Ervenytelen adatbazis: '%-.192s'"
+ ita "Database '%-.192s' sconosciuto"
+ jpn "'%-.192s' ãªã‚“ã¦ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã¯çŸ¥ã‚Šã¾ã›ã‚“."
+ kor "ë°ì´íƒ€ë² ì´ìŠ¤ '%-.192s'는 알수 ì—†ìŒ"
+ nor "Ukjent database '%-.192s'"
+ norwegian-ny "Ukjent database '%-.192s'"
+ pol "Nieznana baza danych '%-.192s'"
+ por "Banco de dados '%-.192s' desconhecido"
+ rum "Baza de data invalida '%-.192s'"
+ rus "ÐеизвеÑÑ‚Ð½Ð°Ñ Ð±Ð°Ð·Ð° данных '%-.192s'"
+ serbian "Nepoznata baza '%-.192s'"
+ slo "Neznáma databáza '%-.192s'"
+ spa "Base de datos desconocida '%-.192s'"
+ swe "Okänd databas: '%-.192s'"
+ ukr "Ðевідома база данних '%-.192s'"
+ER_TABLE_EXISTS_ERROR 42S01
+ cze "Tabulka '%-.192s' ji-Bž existuje"
+ dan "Tabellen '%-.192s' findes allerede"
+ nla "Tabel '%-.192s' bestaat al"
+ eng "Table '%-.192s' already exists"
+ jps "Table '%-.192s' ã¯æ—¢ã«ã‚ã‚Šã¾ã™",
+ est "Tabel '%-.192s' juba eksisteerib"
+ fre "La table '%-.192s' existe déjà"
+ ger "Tabelle '%-.192s' bereits vorhanden"
+ greek "Ο πίνακας '%-.192s' υπάÏχει ήδη"
+ hun "A(z) '%-.192s' tabla mar letezik"
+ ita "La tabella '%-.192s' esiste gia`"
+ jpn "Table '%-.192s' ã¯æ—¢ã«ã‚ã‚Šã¾ã™"
+ kor "í…Œì´ë¸” '%-.192s'는 ì´ë¯¸ 존재함"
+ nor "Tabellen '%-.192s' eksisterer allerede"
+ norwegian-ny "Tabellen '%-.192s' eksisterar allereide"
+ pol "Tabela '%-.192s' już istnieje"
+ por "Tabela '%-.192s' já existe"
+ rum "Tabela '%-.192s' exista deja"
+ rus "Таблица '%-.192s' уже ÑущеÑтвует"
+ serbian "Tabela '%-.192s' već postoji"
+ slo "Tabuľka '%-.192s' už existuje"
+ spa "La tabla '%-.192s' ya existe"
+ swe "Tabellen '%-.192s' finns redan"
+ ukr "Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ '%-.192s' вже Ñ–Ñнує"
+ER_BAD_TABLE_ERROR 42S02
+ cze "Nezn-Bámá tabulka '%-.100s'"
+ dan "Ukendt tabel '%-.100s'"
+ nla "Onbekende tabel '%-.100s'"
+ eng "Unknown table '%-.100s'"
+ jps "table '%-.100s' ã¯ã‚ã‚Šã¾ã›ã‚“.",
+ est "Tundmatu tabel '%-.100s'"
+ fre "Table '%-.100s' inconnue"
+ ger "Unbekannte Tabelle '%-.100s'"
+ greek "Αγνωστος πίνακας '%-.100s'"
+ hun "Ervenytelen tabla: '%-.100s'"
+ ita "Tabella '%-.100s' sconosciuta"
+ jpn "table '%-.100s' ã¯ã‚ã‚Šã¾ã›ã‚“."
+ kor "í…Œì´ë¸” '%-.100s'는 알수 ì—†ìŒ"
+ nor "Ukjent tabell '%-.100s'"
+ norwegian-ny "Ukjent tabell '%-.100s'"
+ pol "Nieznana tabela '%-.100s'"
+ por "Tabela '%-.100s' desconhecida"
+ rum "Tabela '%-.100s' este invalida"
+ rus "ÐеизвеÑÑ‚Ð½Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð° '%-.100s'"
+ serbian "Nepoznata tabela '%-.100s'"
+ slo "Neznáma tabuľka '%-.100s'"
+ spa "Tabla '%-.100s' desconocida"
+ swe "Okänd tabell '%-.100s'"
+ ukr "Ðевідома Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ '%-.100s'"
+ER_NON_UNIQ_ERROR 23000
+ cze "Sloupec '%-.192s' v %-.192s nen-Bí zcela jasný"
+ dan "Felt: '%-.192s' i tabel %-.192s er ikke entydigt"
+ nla "Kolom: '%-.192s' in %-.192s is niet eenduidig"
+ eng "Column '%-.192s' in %-.192s is ambiguous"
+ est "Väli '%-.192s' %-.192s-s ei ole ühene"
+ fre "Champ: '%-.192s' dans %-.192s est ambigu"
+ ger "Feld '%-.192s' in %-.192s ist nicht eindeutig"
+ greek "Το πεδίο: '%-.192s' σε %-.192s δεν έχει καθοÏιστεί"
+ hun "A(z) '%-.192s' oszlop %-.192s-ben ketertelmu"
+ ita "Colonna: '%-.192s' di %-.192s e` ambigua"
+ jpn "Column: '%-.192s' in %-.192s is ambiguous"
+ kor "칼럼: '%-.192s' in '%-.192s' ì´ ëª¨í˜¸í•¨"
+ nor "Felt: '%-.192s' i tabell %-.192s er ikke entydig"
+ norwegian-ny "Kolonne: '%-.192s' i tabell %-.192s er ikkje eintydig"
+ pol "Kolumna: '%-.192s' w %-.192s jest dwuznaczna"
+ por "Coluna '%-.192s' em '%-.192s' é ambígua"
+ rum "Coloana: '%-.192s' in %-.192s este ambigua"
+ rus "Столбец '%-.192s' в %-.192s задан неоднозначно"
+ serbian "Kolona '%-.192s' u %-.192s nije jedinstvena u kontekstu"
+ slo "Pole: '%-.192s' v %-.192s je nejasné"
+ spa "La columna: '%-.192s' en %-.192s es ambigua"
+ swe "Kolumn '%-.192s' i %-.192s är inte unik"
+ ukr "Стовбець '%-.192s' у %-.192s визначений неоднозначно"
+ER_SERVER_SHUTDOWN 08S01
+ cze "Prob-Bíhá ukonÄování práce serveru"
+ dan "Database nedlukning er i gang"
+ nla "Bezig met het stoppen van de server"
+ eng "Server shutdown in progress"
+ jps "Server を shutdown 中...",
+ est "Serveri seiskamine käib"
+ fre "Arrêt du serveur en cours"
+ ger "Der Server wird heruntergefahren"
+ greek "ΕναÏξη διαδικασίας αποσÏνδεσης του εξυπηÏετητή (server shutdown)"
+ hun "A szerver leallitasa folyamatban"
+ ita "Shutdown del server in corso"
+ jpn "Server を shutdown 中..."
+ kor "Server가 셧다운 중입니다."
+ nor "Database nedkobling er i gang"
+ norwegian-ny "Tenar nedkopling er i gang"
+ pol "Trwa kończenie działania serwera"
+ por "'Shutdown' do servidor em andamento"
+ rum "Terminarea serverului este in desfasurare"
+ rus "Сервер находитÑÑ Ð² процеÑÑе оÑтановки"
+ serbian "Gašenje servera je u toku"
+ slo "Prebieha ukonÄovanie práce servera"
+ spa "Desconexion de servidor en proceso"
+ swe "Servern går nu ned"
+ ukr "ЗавершуєтьÑÑ Ñ€Ð°Ð±Ð¾Ñ‚Ð° Ñервера"
+ER_BAD_FIELD_ERROR 42S22 S0022
+ cze "Nezn-Bámý sloupec '%-.192s' v %-.192s"
+ dan "Ukendt kolonne '%-.192s' i tabel %-.192s"
+ nla "Onbekende kolom '%-.192s' in %-.192s"
+ eng "Unknown column '%-.192s' in '%-.192s'"
+ jps "'%-.192s' column 㯠'%-.192s' ã«ã¯ã‚ã‚Šã¾ã›ã‚“.",
+ est "Tundmatu tulp '%-.192s' '%-.192s'-s"
+ fre "Champ '%-.192s' inconnu dans %-.192s"
+ ger "Unbekanntes Tabellenfeld '%-.192s' in %-.192s"
+ greek "Αγνωστο πεδίο '%-.192s' σε '%-.192s'"
+ hun "A(z) '%-.192s' oszlop ervenytelen '%-.192s'-ben"
+ ita "Colonna sconosciuta '%-.192s' in '%-.192s'"
+ jpn "'%-.192s' column 㯠'%-.192s' ã«ã¯ã‚ã‚Šã¾ã›ã‚“."
+ kor "Unknown 칼럼 '%-.192s' in '%-.192s'"
+ nor "Ukjent kolonne '%-.192s' i tabell %-.192s"
+ norwegian-ny "Ukjent felt '%-.192s' i tabell %-.192s"
+ pol "Nieznana kolumna '%-.192s' w %-.192s"
+ por "Coluna '%-.192s' desconhecida em '%-.192s'"
+ rum "Coloana invalida '%-.192s' in '%-.192s'"
+ rus "ÐеизвеÑтный Ñтолбец '%-.192s' в '%-.192s'"
+ serbian "Nepoznata kolona '%-.192s' u '%-.192s'"
+ slo "Neznáme pole '%-.192s' v '%-.192s'"
+ spa "La columna '%-.192s' en %-.192s es desconocida"
+ swe "Okänd kolumn '%-.192s' i %-.192s"
+ ukr "Ðевідомий Ñтовбець '%-.192s' у '%-.192s'"
+ER_WRONG_FIELD_WITH_GROUP 42000 S1009
+ cze "Pou-Bžité '%-.192s' nebylo v group by"
+ dan "Brugte '%-.192s' som ikke var i group by"
+ nla "Opdracht gebruikt '%-.192s' dat niet in de GROUP BY voorkomt"
+ eng "'%-.192s' isn't in GROUP BY"
+ jps "'%-.192s' isn't in GROUP BY",
+ est "'%-.192s' puudub GROUP BY klauslis"
+ fre "'%-.192s' n'est pas dans 'group by'"
+ ger "'%-.192s' ist nicht in GROUP BY vorhanden"
+ greek "ΧÏησιμοποιήθηκε '%-.192s' που δεν υπήÏχε στο group by"
+ hun "Used '%-.192s' with wasn't in group by"
+ ita "Usato '%-.192s' che non e` nel GROUP BY"
+ kor "'%-.192s'ì€ GROUP BYì†ì— ì—†ìŒ"
+ nor "Brukte '%-.192s' som ikke var i group by"
+ norwegian-ny "Brukte '%-.192s' som ikkje var i group by"
+ pol "Użyto '%-.192s' bez umieszczenia w group by"
+ por "'%-.192s' não está em 'GROUP BY'"
+ rum "'%-.192s' nu exista in clauza GROUP BY"
+ rus "'%-.192s' не приÑутÑтвует в GROUP BY"
+ serbian "Entitet '%-.192s' nije naveden u komandi 'GROUP BY'"
+ slo "Použité '%-.192s' nebolo v 'group by'"
+ spa "Usado '%-.192s' el cual no esta group by"
+ swe "'%-.192s' finns inte i GROUP BY"
+ ukr "'%-.192s' не є у GROUP BY"
+ER_WRONG_GROUP_FIELD 42000 S1009
+ cze "Nemohu pou-Bžít group na '%-.192s'"
+ dan "Kan ikke gruppere på '%-.192s'"
+ nla "Kan '%-.192s' niet groeperen"
+ eng "Can't group on '%-.192s'"
+ est "Ei saa grupeerida '%-.192s' järgi"
+ fre "Ne peut regrouper '%-.192s'"
+ ger "Gruppierung über '%-.192s' nicht möglich"
+ greek "ΑδÏνατη η ομαδοποίηση (group on) '%-.192s'"
+ hun "A group nem hasznalhato: '%-.192s'"
+ ita "Impossibile raggruppare per '%-.192s'"
+ kor "'%-.192s'를 그룹할 수 ì—†ìŒ"
+ nor "Kan ikke gruppere på '%-.192s'"
+ norwegian-ny "Kan ikkje gruppere på '%-.192s'"
+ pol "Nie można grupować po '%-.192s'"
+ por "Não pode agrupar em '%-.192s'"
+ rum "Nu pot sa grupez pe (group on) '%-.192s'"
+ rus "Ðевозможно произвеÑти группировку по '%-.192s'"
+ serbian "Ne mogu da grupišem po '%-.192s'"
+ slo "Nemôžem použiť 'group' na '%-.192s'"
+ spa "No puedo agrupar por '%-.192s'"
+ swe "Kan inte använda GROUP BY med '%-.192s'"
+ ukr "Ðе можу групувати по '%-.192s'"
+ER_WRONG_SUM_SELECT 42000 S1009
+ cze "P-Bříkaz obsahuje zároveň funkci sum a sloupce"
+ dan "Udtrykket har summer (sum) funktioner og kolonner i samme udtryk"
+ nla "Opdracht heeft totaliseer functies en kolommen in dezelfde opdracht"
+ eng "Statement has sum functions and columns in same statement"
+ est "Lauses on korraga nii tulbad kui summeerimisfunktsioonid"
+ fre "Vous demandez la fonction sum() et des champs dans la même commande"
+ ger "Die Verwendung von Summierungsfunktionen und Spalten im selben Befehl ist nicht erlaubt"
+ greek "Η διατÏπωση πεÏιέχει sum functions και columns στην ίδια διατÏπωση"
+ ita "Il comando ha una funzione SUM e una colonna non specificata nella GROUP BY"
+ kor "Statement ê°€ sumê¸°ëŠ¥ì„ ë™ìž‘중ì´ê³  ì¹¼ëŸ¼ë„ ë™ì¼í•œ statement입니다."
+ nor "Uttrykket har summer (sum) funksjoner og kolonner i samme uttrykk"
+ norwegian-ny "Uttrykket har summer (sum) funksjoner og kolonner i same uttrykk"
+ pol "Zapytanie ma funkcje sumuj?ce i kolumny w tym samym zapytaniu"
+ por "Cláusula contém funções de soma e colunas juntas"
+ rum "Comanda are functii suma si coloane in aceeasi comanda"
+ rus "Выражение Ñодержит групповые функции и Ñтолбцы, но не включает GROUP BY. Ркак вы умудрилиÑÑŒ получить Ñто Ñообщение об ошибке?"
+ serbian "Izraz ima 'SUM' agregatnu funkciju i kolone u isto vreme"
+ slo "Príkaz obsahuje zároveň funkciu 'sum' a poľa"
+ spa "El estamento tiene funciones de suma y columnas en el mismo estamento"
+ swe "Kommandot har både sum functions och enkla funktioner"
+ ukr "У виразі викориÑтано підÑумовуючі функції порÑд з іменами Ñтовбців"
+ER_WRONG_VALUE_COUNT 21S01
+ cze "Po-BÄet sloupců neodpovídá zadané hodnotÄ›"
+ dan "Kolonne tæller stemmer ikke med antallet af værdier"
+ nla "Het aantal kolommen komt niet overeen met het aantal opgegeven waardes"
+ eng "Column count doesn't match value count"
+ est "Tulpade arv erineb väärtuste arvust"
+ ger "Die Anzahl der Spalten entspricht nicht der Anzahl der Werte"
+ greek "Το Column count δεν ταιÏιάζει με το value count"
+ hun "Az oszlopban levo ertek nem egyezik meg a szamitott ertekkel"
+ ita "Il numero delle colonne non e` uguale al numero dei valori"
+ kor "ì¹¼ëŸ¼ì˜ ì¹´ìš´íŠ¸ê°€ ê°’ì˜ ì¹´ìš´íŠ¸ì™€ ì¼ì¹˜í•˜ì§€ 않습니다."
+ nor "Felt telling stemmer verdi telling"
+ norwegian-ny "Kolonne telling stemmer verdi telling"
+ pol "Liczba kolumn nie odpowiada liczbie warto?ci"
+ por "Contagem de colunas não confere com a contagem de valores"
+ rum "Numarul de coloane nu este acelasi cu numarul valoarei"
+ rus "КоличеÑтво Ñтолбцов не Ñовпадает Ñ ÐºÐ¾Ð»Ð¸Ñ‡ÐµÑтвом значений"
+ serbian "Broj kolona ne odgovara broju vrednosti"
+ slo "PoÄet polí nezodpovedá zadanej hodnote"
+ spa "La columna con count no tiene valores para contar"
+ swe "Antalet kolumner motsvarar inte antalet värden"
+ ukr "КількіÑÑ‚ÑŒ Ñтовбців не Ñпівпадає з кількіÑÑ‚ÑŽ значень"
+ER_TOO_LONG_IDENT 42000 S1009
+ cze "Jm-Béno identifikátoru '%-.100s' je příliš dlouhé"
+ dan "Navnet '%-.100s' er for langt"
+ nla "Naam voor herkenning '%-.100s' is te lang"
+ eng "Identifier name '%-.100s' is too long"
+ jps "Identifier name '%-.100s' ã¯é•·ã™ãŽã¾ã™",
+ est "Identifikaatori '%-.100s' nimi on liiga pikk"
+ fre "Le nom de l'identificateur '%-.100s' est trop long"
+ ger "Name des Bezeichners '%-.100s' ist zu lang"
+ greek "Το identifier name '%-.100s' είναι Ï€Î¿Î»Ï Î¼ÎµÎ³Î¬Î»Î¿"
+ hun "A(z) '%-.100s' azonositonev tul hosszu."
+ ita "Il nome dell'identificatore '%-.100s' e` troppo lungo"
+ jpn "Identifier name '%-.100s' ã¯é•·ã™ãŽã¾ã™"
+ kor "Identifier '%-.100s'는 너무 길군요."
+ nor "Identifikator '%-.100s' er for lang"
+ norwegian-ny "Identifikator '%-.100s' er for lang"
+ pol "Nazwa identyfikatora '%-.100s' jest zbyt długa"
+ por "Nome identificador '%-.100s' é longo demais"
+ rum "Numele indentificatorului '%-.100s' este prea lung"
+ rus "Слишком длинный идентификатор '%-.100s'"
+ serbian "Ime '%-.100s' je predugaÄko"
+ slo "Meno identifikátora '%-.100s' je príliš dlhé"
+ spa "El nombre del identificador '%-.100s' es demasiado grande"
+ swe "Kolumnnamn '%-.100s' är för långt"
+ ukr "Ім'Ñ Ñ–Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ‚Ð¾Ñ€Ð° '%-.100s' задовге"
+ER_DUP_FIELDNAME 42S21 S1009
+ cze "Zdvojen-Bé jméno sloupce '%-.192s'"
+ dan "Feltnavnet '%-.192s' findes allerede"
+ nla "Dubbele kolom naam '%-.192s'"
+ eng "Duplicate column name '%-.192s'"
+ jps "'%-.192s' ã¨ã„ㆠcolumn åã¯é‡è¤‡ã—ã¦ã¾ã™",
+ est "Kattuv tulba nimi '%-.192s'"
+ fre "Nom du champ '%-.192s' déjà utilisé"
+ ger "Doppelter Spaltenname: '%-.192s'"
+ greek "Επανάληψη column name '%-.192s'"
+ hun "Duplikalt oszlopazonosito: '%-.192s'"
+ ita "Nome colonna duplicato '%-.192s'"
+ jpn "'%-.192s' ã¨ã„ㆠcolumn åã¯é‡è¤‡ã—ã¦ã¾ã™"
+ kor "ì¤‘ë³µëœ ì¹¼ëŸ¼ ì´ë¦„: '%-.192s'"
+ nor "Feltnavnet '%-.192s' eksisterte fra før"
+ norwegian-ny "Feltnamnet '%-.192s' eksisterte frå før"
+ pol "Powtórzona nazwa kolumny '%-.192s'"
+ por "Nome da coluna '%-.192s' duplicado"
+ rum "Numele coloanei '%-.192s' e duplicat"
+ rus "ДублирующееÑÑ Ð¸Ð¼Ñ Ñтолбца '%-.192s'"
+ serbian "Duplirano ime kolone '%-.192s'"
+ slo "Opakované meno poľa '%-.192s'"
+ spa "Nombre de columna duplicado '%-.192s'"
+ swe "Kolumnnamn '%-.192s finns flera gånger"
+ ukr "Дублююче ім'Ñ ÑÑ‚Ð¾Ð²Ð±Ñ†Ñ '%-.192s'"
+ER_DUP_KEYNAME 42000 S1009
+ cze "Zdvojen-Bé jméno klíÄe '%-.192s'"
+ dan "Indeksnavnet '%-.192s' findes allerede"
+ nla "Dubbele zoeksleutel naam '%-.192s'"
+ eng "Duplicate key name '%-.192s'"
+ jps "'%-.192s' ã¨ã„ㆠkey ã®åå‰ã¯é‡è¤‡ã—ã¦ã„ã¾ã™",
+ est "Kattuv võtme nimi '%-.192s'"
+ fre "Nom de clef '%-.192s' déjà utilisé"
+ ger "Doppelter Name für Schlüssel vorhanden: '%-.192s'"
+ greek "Επανάληψη key name '%-.192s'"
+ hun "Duplikalt kulcsazonosito: '%-.192s'"
+ ita "Nome chiave duplicato '%-.192s'"
+ jpn "'%-.192s' ã¨ã„ㆠkey ã®åå‰ã¯é‡è¤‡ã—ã¦ã„ã¾ã™"
+ kor "ì¤‘ë³µëœ í‚¤ ì´ë¦„ : '%-.192s'"
+ nor "Nøkkelnavnet '%-.192s' eksisterte fra før"
+ norwegian-ny "Nøkkelnamnet '%-.192s' eksisterte frå før"
+ pol "Powtórzony nazwa klucza '%-.192s'"
+ por "Nome da chave '%-.192s' duplicado"
+ rum "Numele cheiei '%-.192s' e duplicat"
+ rus "ДублирующееÑÑ Ð¸Ð¼Ñ ÐºÐ»ÑŽÑ‡Ð° '%-.192s'"
+ serbian "Duplirano ime kljuÄa '%-.192s'"
+ slo "Opakované meno kľúÄa '%-.192s'"
+ spa "Nombre de clave duplicado '%-.192s'"
+ swe "Nyckelnamn '%-.192s' finns flera gånger"
+ ukr "Дублююче ім'Ñ ÐºÐ»ÑŽÑ‡Ð° '%-.192s'"
+# When using this error code, please use ER(ER_DUP_ENTRY_WITH_KEY_NAME)
+# for the message string. See, for example, code in handler.cc.
+ER_DUP_ENTRY 23000 S1009
+ cze "Zdvojen-Bý klÃ­Ä '%-.192s' (Äíslo klíÄe %d)"
+ dan "Ens værdier '%-.192s' for indeks %d"
+ nla "Dubbele ingang '%-.192s' voor zoeksleutel %d"
+ eng "Duplicate entry '%-.192s' for key %d"
+ jps "'%-.192s' 㯠key %d ã«ãŠã„ã¦é‡è¤‡ã—ã¦ã„ã¾ã™",
+ est "Kattuv väärtus '%-.192s' võtmele %d"
+ fre "Duplicata du champ '%-.192s' pour la clef %d"
+ ger "Doppelter Eintrag '%-.192s' für Schlüssel %d"
+ greek "Διπλή εγγÏαφή '%-.192s' για το κλειδί %d"
+ hun "Duplikalt bejegyzes '%-.192s' a %d kulcs szerint."
+ ita "Valore duplicato '%-.192s' per la chiave %d"
+ jpn "'%-.192s' 㯠key %d ã«ãŠã„ã¦é‡è¤‡ã—ã¦ã„ã¾ã™"
+ kor "ì¤‘ë³µëœ ìž…ë ¥ ê°’ '%-.192s': key %d"
+ nor "Like verdier '%-.192s' for nøkkel %d"
+ norwegian-ny "Like verdiar '%-.192s' for nykkel %d"
+ pol "Powtórzone wyst?pienie '%-.192s' dla klucza %d"
+ por "Entrada '%-.192s' duplicada para a chave %d"
+ rum "Cimpul '%-.192s' e duplicat pentru cheia %d"
+ rus "ДублирующаÑÑÑ Ð·Ð°Ð¿Ð¸ÑÑŒ '%-.192s' по ключу %d"
+ serbian "Dupliran unos '%-.192s' za kljuÄ '%d'"
+ slo "Opakovaný kÄ¾ÃºÄ '%-.192s' (Äíslo kľúÄa %d)"
+ spa "Entrada duplicada '%-.192s' para la clave %d"
+ swe "Dubbel nyckel '%-.192s' för nyckel %d"
+ ukr "Дублюючий Ð·Ð°Ð¿Ð¸Ñ '%-.192s' Ð´Ð»Ñ ÐºÐ»ÑŽÑ‡Ð° %d"
+ER_WRONG_FIELD_SPEC 42000 S1009
+ cze "Chybn-Bá specifikace sloupce '%-.192s'"
+ dan "Forkert kolonnespecifikaton for felt '%-.192s'"
+ nla "Verkeerde kolom specificatie voor kolom '%-.192s'"
+ eng "Incorrect column specifier for column '%-.192s'"
+ est "Vigane tulba kirjeldus tulbale '%-.192s'"
+ fre "Mauvais paramètre de champ pour le champ '%-.192s'"
+ ger "Falsche Spezifikation für Feld '%-.192s'"
+ greek "Εσφαλμένο column specifier για το πεδίο '%-.192s'"
+ hun "Rossz oszlopazonosito: '%-.192s'"
+ ita "Specifica errata per la colonna '%-.192s'"
+ kor "칼럼 '%-.192s'ì˜ ë¶€ì •í™•í•œ 칼럼 ì •ì˜ìž"
+ nor "Feil kolonne spesifikator for felt '%-.192s'"
+ norwegian-ny "Feil kolonne spesifikator for kolonne '%-.192s'"
+ pol "Błędna specyfikacja kolumny dla kolumny '%-.192s'"
+ por "Especificador de coluna incorreto para a coluna '%-.192s'"
+ rum "Specificandul coloanei '%-.192s' este incorect"
+ rus "Ðекорректный определитель Ñтолбца Ð´Ð»Ñ Ñтолбца '%-.192s'"
+ serbian "Pogrešan naziv kolone za kolonu '%-.192s'"
+ slo "Chyba v špecifikácii poľa '%-.192s'"
+ spa "Especificador de columna erroneo para la columna '%-.192s'"
+ swe "Felaktigt kolumntyp för kolumn '%-.192s'"
+ ukr "Ðевірний Ñпецифікатор ÑÑ‚Ð¾Ð²Ð±Ñ†Ñ '%-.192s'"
+ER_PARSE_ERROR 42000 s1009
+ cze "%s bl-Bízko '%-.80s' na řádku %d"
+ dan "%s nær '%-.80s' på linje %d"
+ nla "%s bij '%-.80s' in regel %d"
+ eng "%s near '%-.80s' at line %d"
+ jps "%s : '%-.80s' 付近 : %d 行目",
+ est "%s '%-.80s' ligidal real %d"
+ fre "%s près de '%-.80s' à la ligne %d"
+ ger "%s bei '%-.80s' in Zeile %d"
+ greek "%s πλησίον '%-.80s' στη γÏαμμή %d"
+ hun "A %s a '%-.80s'-hez kozeli a %d sorban"
+ ita "%s vicino a '%-.80s' linea %d"
+ jpn "%s : '%-.80s' 付近 : %d 行目"
+ kor "'%s' ì—러 ê°™ì니다. ('%-.80s' 명령어 ë¼ì¸ %d)"
+ nor "%s nær '%-.80s' på linje %d"
+ norwegian-ny "%s attmed '%-.80s' på line %d"
+ pol "%s obok '%-.80s' w linii %d"
+ por "%s próximo a '%-.80s' na linha %d"
+ rum "%s linga '%-.80s' pe linia %d"
+ rus "%s около '%-.80s' на Ñтроке %d"
+ serbian "'%s' u iskazu '%-.80s' na liniji %d"
+ slo "%s blízko '%-.80s' na riadku %d"
+ spa "%s cerca '%-.80s' en la linea %d"
+ swe "%s nära '%-.80s' på rad %d"
+ ukr "%s Ð±Ñ–Ð»Ñ '%-.80s' в Ñтроці %d"
+ER_EMPTY_QUERY 42000
+ cze "V-Býsledek dotazu je prázdný"
+ dan "Forespørgsel var tom"
+ nla "Query was leeg"
+ eng "Query was empty"
+ jps "Query ãŒç©ºã§ã™.",
+ est "Tühi päring"
+ fre "Query est vide"
+ ger "Leere Abfrage"
+ greek "Το εÏώτημα (query) που θέσατε ήταν κενό"
+ hun "Ures lekerdezes."
+ ita "La query e` vuota"
+ jpn "Query ãŒç©ºã§ã™."
+ kor "쿼리결과가 없습니다."
+ nor "Forespørsel var tom"
+ norwegian-ny "Førespurnad var tom"
+ pol "Zapytanie było puste"
+ por "Consulta (query) estava vazia"
+ rum "Query-ul a fost gol"
+ rus "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð¾ÐºÐ°Ð·Ð°Ð»ÑÑ Ð¿ÑƒÑтым"
+ serbian "Upit je bio prazan"
+ slo "Výsledok požiadavky bol prázdny"
+ spa "La query estaba vacia"
+ swe "Frågan var tom"
+ ukr "ПуÑтий запит"
+ER_NONUNIQ_TABLE 42000 S1009
+ cze "Nejednozna-BÄná tabulka/alias: '%-.192s'"
+ dan "Tabellen/aliaset: '%-.192s' er ikke unikt"
+ nla "Niet unieke waarde tabel/alias: '%-.192s'"
+ eng "Not unique table/alias: '%-.192s'"
+ jps "'%-.192s' ã¯ä¸€æ„ã® table/alias åã§ã¯ã‚ã‚Šã¾ã›ã‚“",
+ est "Ei ole unikaalne tabel/alias '%-.192s'"
+ fre "Table/alias: '%-.192s' non unique"
+ ger "Tabellenname/Alias '%-.192s' nicht eindeutig"
+ greek "ΑδÏνατη η ανεÏÏεση unique table/alias: '%-.192s'"
+ hun "Nem egyedi tabla/alias: '%-.192s'"
+ ita "Tabella/alias non unico: '%-.192s'"
+ jpn "'%-.192s' ã¯ä¸€æ„ã® table/alias åã§ã¯ã‚ã‚Šã¾ã›ã‚“"
+ kor "Unique 하지 ì•Šì€ í…Œì´ë¸”/alias: '%-.192s'"
+ nor "Ikke unikt tabell/alias: '%-.192s'"
+ norwegian-ny "Ikkje unikt tabell/alias: '%-.192s'"
+ pol "Tabela/alias nie s? unikalne: '%-.192s'"
+ por "Tabela/alias '%-.192s' não única"
+ rum "Tabela/alias: '%-.192s' nu este unic"
+ rus "ПовторÑющаÑÑÑ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð°/пÑевдоним '%-.192s'"
+ serbian "Tabela ili alias nisu bili jedinstveni: '%-.192s'"
+ slo "Nie jednoznaÄná tabuľka/alias: '%-.192s'"
+ spa "Tabla/alias: '%-.192s' es no unica"
+ swe "Icke unikt tabell/alias: '%-.192s'"
+ ukr "Ðеунікальна таблицÑ/пÑевдонім: '%-.192s'"
+ER_INVALID_DEFAULT 42000 S1009
+ cze "Chybn-Bá defaultní hodnota pro '%-.192s'"
+ dan "Ugyldig standardværdi for '%-.192s'"
+ nla "Foutieve standaard waarde voor '%-.192s'"
+ eng "Invalid default value for '%-.192s'"
+ est "Vigane vaikeväärtus '%-.192s' jaoks"
+ fre "Valeur par défaut invalide pour '%-.192s'"
+ ger "Fehlerhafter Vorgabewert (DEFAULT) für '%-.192s'"
+ greek "Εσφαλμένη Ï€ÏοκαθοÏισμένη τιμή (default value) για '%-.192s'"
+ hun "Ervenytelen ertek: '%-.192s'"
+ ita "Valore di default non valido per '%-.192s'"
+ kor "'%-.192s'ì˜ ìœ íš¨í•˜ì§€ 못한 ë””í´íŠ¸ ê°’ì„ ì‚¬ìš©í•˜ì…¨ìŠµë‹ˆë‹¤."
+ nor "Ugyldig standardverdi for '%-.192s'"
+ norwegian-ny "Ugyldig standardverdi for '%-.192s'"
+ pol "Niewła?ciwa warto?ć domy?lna dla '%-.192s'"
+ por "Valor padrão (default) inválido para '%-.192s'"
+ rum "Valoarea de default este invalida pentru '%-.192s'"
+ rus "Ðекорректное значение по умолчанию Ð´Ð»Ñ '%-.192s'"
+ serbian "Loša default vrednost za '%-.192s'"
+ slo "Chybná implicitná hodnota pre '%-.192s'"
+ spa "Valor por defecto invalido para '%-.192s'"
+ swe "Ogiltigt DEFAULT värde för '%-.192s'"
+ ukr "Ðевірне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð¿Ð¾ замовчуванню Ð´Ð»Ñ '%-.192s'"
+ER_MULTIPLE_PRI_KEY 42000 S1009
+ cze "Definov-Báno více primárních klíÄů"
+ dan "Flere primærnøgler specificeret"
+ nla "Meerdere primaire zoeksleutels gedefinieerd"
+ eng "Multiple primary key defined"
+ jps "複数㮠primary key ãŒå®šç¾©ã•ã‚Œã¾ã—ãŸ",
+ est "Mitut primaarset võtit ei saa olla"
+ fre "Plusieurs clefs primaires définies"
+ ger "Mehrere Primärschlüssel (PRIMARY KEY) definiert"
+ greek "ΠεÏισσότεÏα από ένα primary key οÏίστηκαν"
+ hun "Tobbszoros elsodleges kulcs definialas."
+ ita "Definite piu` chiave primarie"
+ jpn "複数㮠primary key ãŒå®šç¾©ã•ã‚Œã¾ã—ãŸ"
+ kor "Multiple primary keyê°€ ì •ì˜ë˜ì–´ 있슴"
+ nor "Fleire primærnøkle spesifisert"
+ norwegian-ny "Fleire primærnyklar spesifisert"
+ pol "Zdefiniowano wiele kluczy podstawowych"
+ por "Definida mais de uma chave primária"
+ rum "Chei primare definite de mai multe ori"
+ rus "Указано неÑколько первичных ключей"
+ serbian "Definisani viÅ¡estruki primarni kljuÄevi"
+ slo "Zadefinovaných viac primárnych kľúÄov"
+ spa "Multiples claves primarias definidas"
+ swe "Flera PRIMARY KEY använda"
+ ukr "Первинного ключа визначено неодноразово"
+ER_TOO_MANY_KEYS 42000 S1009
+ cze "Zad-Báno příliÅ¡ mnoho klíÄů, je povoleno nejvíce %d klíÄů"
+ dan "For mange nøgler specificeret. Kun %d nøgler må bruges"
+ nla "Teveel zoeksleutels gedefinieerd. Maximaal zijn %d zoeksleutels toegestaan"
+ eng "Too many keys specified; max %d keys allowed"
+ jps "key ã®æŒ‡å®šãŒå¤šã™ãŽã¾ã™. key ã¯æœ€å¤§ %d ã¾ã§ã§ã™",
+ est "Liiga palju võtmeid. Maksimaalselt võib olla %d võtit"
+ fre "Trop de clefs sont définies. Maximum de %d clefs alloué"
+ ger "Zu viele Schlüssel definiert. Maximal %d Schlüssel erlaubt"
+ greek "ΠάÏα πολλά key οÏίσθηκαν. Το Ï€Î¿Î»Ï %d επιτÏέπονται"
+ hun "Tul sok kulcs. Maximum %d kulcs engedelyezett."
+ ita "Troppe chiavi. Sono ammesse max %d chiavi"
+ jpn "key ã®æŒ‡å®šãŒå¤šã™ãŽã¾ã™. key ã¯æœ€å¤§ %d ã¾ã§ã§ã™"
+ kor "너무 ë§Žì€ í‚¤ê°€ ì •ì˜ë˜ì–´ 있ì니다.. 최대 %dì˜ í‚¤ê°€ 가능함"
+ nor "For mange nøkler spesifisert. Maks %d nøkler tillatt"
+ norwegian-ny "For mange nykler spesifisert. Maks %d nyklar tillatt"
+ pol "Okre?lono zbyt wiele kluczy. Dostępnych jest maksymalnie %d kluczy"
+ por "Especificadas chaves demais. O máximo permitido são %d chaves"
+ rum "Prea multe chei. Numarul de chei maxim este %d"
+ rus "Указано Ñлишком много ключей. РазрешаетÑÑ ÑƒÐºÐ°Ð·Ñ‹Ð²Ð°Ñ‚ÑŒ не более %d ключей"
+ serbian "Navedeno je previÅ¡e kljuÄeva. Maksimum %d kljuÄeva je dozvoljeno"
+ slo "Zadaných ríliÅ¡ veľa kľúÄov. Najviac %d kľúÄov je povolených"
+ spa "Demasiadas claves primarias declaradas. Un maximo de %d claves son permitidas"
+ swe "För många nycklar använda. Man får ha högst %d nycklar"
+ ukr "Забагато ключів зазначено. Дозволено не більше %d ключів"
+ER_TOO_MANY_KEY_PARTS 42000 S1009
+ cze "Zad-Báno příliÅ¡ mnoho Äást klíÄů, je povoleno nejvíce %d Äástí"
+ dan "For mange nøgledele specificeret. Kun %d dele må bruges"
+ nla "Teveel zoeksleutel onderdelen gespecificeerd. Maximaal %d onderdelen toegestaan"
+ eng "Too many key parts specified; max %d parts allowed"
+ est "Võti koosneb liiga paljudest osadest. Maksimaalselt võib olla %d osa"
+ fre "Trop de parties specifiées dans la clef. Maximum de %d parties"
+ ger "Zu viele Teilschlüssel definiert. Maximal %d Teilschlüssel erlaubt"
+ greek "ΠάÏα πολλά key parts οÏίσθηκαν. Το Ï€Î¿Î»Ï %d επιτÏέπονται"
+ hun "Tul sok kulcsdarabot definialt. Maximum %d resz engedelyezett"
+ ita "Troppe parti di chiave specificate. Sono ammesse max %d parti"
+ kor "너무 ë§Žì€ í‚¤ 부분(parts)ë“¤ì´ ì •ì˜ë˜ì–´ 있ì니다.. 최대 %d ë¶€ë¶„ì´ ê°€ëŠ¥í•¨"
+ nor "For mange nøkkeldeler spesifisert. Maks %d deler tillatt"
+ norwegian-ny "For mange nykkeldelar spesifisert. Maks %d delar tillatt"
+ pol "Okre?lono zbyt wiele czę?ci klucza. Dostępnych jest maksymalnie %d czę?ci"
+ por "Especificadas partes de chave demais. O máximo permitido são %d partes"
+ rum "Prea multe chei. Numarul de chei maxim este %d"
+ rus "Указано Ñлишком много чаÑтей ÑоÑтавного ключа. РазрешаетÑÑ ÑƒÐºÐ°Ð·Ñ‹Ð²Ð°Ñ‚ÑŒ не более %d чаÑтей"
+ serbian "Navedeno je previÅ¡e delova kljuÄa. Maksimum %d delova je dozvoljeno"
+ slo "Zadaných ríliÅ¡ veľa Äastí kľúÄov. Je povolených najviac %d Äastí"
+ spa "Demasiadas partes de clave declaradas. Un maximo de %d partes son permitidas"
+ swe "För många nyckeldelar använda. Man får ha högst %d nyckeldelar"
+ ukr "Забагато чаÑтин ключа зазначено. Дозволено не більше %d чаÑтин"
+ER_TOO_LONG_KEY 42000 S1009
+ cze "Zadan-Bý klÃ­Ä byl příliÅ¡ dlouhý, nejvÄ›tší délka klíÄe je %d"
+ dan "Specificeret nøgle var for lang. Maksimal nøglelængde er %d"
+ nla "Gespecificeerde zoeksleutel was te lang. De maximale lengte is %d"
+ eng "Specified key was too long; max key length is %d bytes"
+ jps "key ãŒé•·ã™ãŽã¾ã™. key ã®é•·ã•ã¯æœ€å¤§ %d ã§ã™",
+ est "Võti on liiga pikk. Maksimaalne võtmepikkus on %d"
+ fre "La clé est trop longue. Longueur maximale: %d"
+ ger "Schlüssel ist zu lang. Die maximale Schlüssellänge beträgt %d"
+ greek "Το κλειδί που οÏίσθηκε είναι Ï€Î¿Î»Ï Î¼ÎµÎ³Î¬Î»Î¿. Το μέγιστο μήκος είναι %d"
+ hun "A megadott kulcs tul hosszu. Maximalis kulcshosszusag: %d"
+ ita "La chiave specificata e` troppo lunga. La max lunghezza della chiave e` %d"
+ jpn "key ãŒé•·ã™ãŽã¾ã™. key ã®é•·ã•ã¯æœ€å¤§ %d ã§ã™"
+ kor "ì •ì˜ëœ 키가 너무 ê¹ë‹ˆë‹¤. 최대 í‚¤ì˜ ê¸¸ì´ëŠ” %d입니다."
+ nor "Spesifisert nøkkel var for lang. Maks nøkkellengde er is %d"
+ norwegian-ny "Spesifisert nykkel var for lang. Maks nykkellengde er %d"
+ pol "Zdefinowany klucz jest zbyt długi. Maksymaln? długo?ci? klucza jest %d"
+ por "Chave especificada longa demais. O comprimento de chave máximo permitido é %d"
+ rum "Cheia specificata este prea lunga. Marimea maxima a unei chei este de %d"
+ rus "Указан Ñлишком длинный ключ. МакÑÐ¸Ð¼Ð°Ð»ÑŒÐ½Ð°Ñ Ð´Ð»Ð¸Ð½Ð° ключа ÑоÑтавлÑет %d байт"
+ serbian "Navedeni kljuÄ je predug. Maksimalna dužina kljuÄa je %d"
+ slo "Zadaný kÄ¾ÃºÄ je príliÅ¡ dlhý, najväÄÅ¡ia dĺžka kľúÄa je %d"
+ spa "Declaracion de clave demasiado larga. La maxima longitud de clave es %d"
+ swe "För lång nyckel. Högsta tillåtna nyckellängd är %d"
+ ukr "Зазначений ключ задовгий. Ðайбільша довжина ключа %d байтів"
+ER_KEY_COLUMN_DOES_NOT_EXITS 42000 S1009
+ cze "Kl-BíÄový sloupec '%-.192s' v tabulce neexistuje"
+ dan "Nøglefeltet '%-.192s' eksisterer ikke i tabellen"
+ nla "Zoeksleutel kolom '%-.192s' bestaat niet in tabel"
+ eng "Key column '%-.192s' doesn't exist in table"
+ jps "Key column '%-.192s' ãŒãƒ†ãƒ¼ãƒ–ルã«ã‚ã‚Šã¾ã›ã‚“.",
+ est "Võtme tulp '%-.192s' puudub tabelis"
+ fre "La clé '%-.192s' n'existe pas dans la table"
+ ger "In der Tabelle gibt es kein Schlüsselfeld '%-.192s'"
+ greek "Το πεδίο κλειδί '%-.192s' δεν υπάÏχει στον πίνακα"
+ hun "A(z) '%-.192s'kulcsoszlop nem letezik a tablaban"
+ ita "La colonna chiave '%-.192s' non esiste nella tabella"
+ jpn "Key column '%-.192s' ãŒãƒ†ãƒ¼ãƒ–ルã«ã‚ã‚Šã¾ã›ã‚“."
+ kor "Key 칼럼 '%-.192s'는 í…Œì´ë¸”ì— ì¡´ìž¬í•˜ì§€ 않습니다."
+ nor "Nøkkel felt '%-.192s' eksiterer ikke i tabellen"
+ norwegian-ny "Nykkel kolonne '%-.192s' eksiterar ikkje i tabellen"
+ pol "Kolumna '%-.192s' zdefiniowana w kluczu nie istnieje w tabeli"
+ por "Coluna chave '%-.192s' não existe na tabela"
+ rum "Coloana cheie '%-.192s' nu exista in tabela"
+ rus "Ключевой Ñтолбец '%-.192s' в таблице не ÑущеÑтвует"
+ serbian "KljuÄna kolona '%-.192s' ne postoji u tabeli"
+ slo "KľúÄový stĺpec '%-.192s' v tabuľke neexistuje"
+ spa "La columna clave '%-.192s' no existe en la tabla"
+ swe "Nyckelkolumn '%-.192s' finns inte"
+ ukr "Ключовий Ñтовбець '%-.192s' не Ñ–Ñнує у таблиці"
+ER_BLOB_USED_AS_KEY 42000 S1009
+ cze "Blob sloupec '%-.192s' nem-Bůže být použit jako klíÄ"
+ dan "BLOB feltet '%-.192s' kan ikke bruges ved specifikation af indeks"
+ nla "BLOB kolom '%-.192s' kan niet gebruikt worden bij zoeksleutel specificatie"
+ eng "BLOB column '%-.192s' can't be used in key specification with the used table type"
+ est "BLOB-tüüpi tulpa '%-.192s' ei saa kasutada võtmena"
+ fre "Champ BLOB '%-.192s' ne peut être utilisé dans une clé"
+ ger "BLOB-Feld '%-.192s' kann beim verwendeten Tabellentyp nicht als Schlüssel verwendet werden"
+ greek "Πεδίο Ï„Ïπου Blob '%-.192s' δεν μποÏεί να χÏησιμοποιηθεί στον οÏισμό ενός ÎºÎ»ÎµÎ¹Î´Î¹Î¿Ï (key specification)"
+ hun "Blob objektum '%-.192s' nem hasznalhato kulcskent"
+ ita "La colonna BLOB '%-.192s' non puo` essere usata nella specifica della chiave"
+ kor "BLOB 칼럼 '%-.192s'는 키 ì •ì˜ì—ì„œ ì‚¬ìš©ë  ìˆ˜ 없습니다."
+ nor "Blob felt '%-.192s' kan ikke brukes ved spesifikasjon av nøkler"
+ norwegian-ny "Blob kolonne '%-.192s' kan ikkje brukast ved spesifikasjon av nyklar"
+ pol "Kolumna typu Blob '%-.192s' nie może być użyta w specyfikacji klucza"
+ por "Coluna BLOB '%-.192s' não pode ser utilizada na especificação de chave para o tipo de tabela usado"
+ rum "Coloana de tip BLOB '%-.192s' nu poate fi folosita in specificarea cheii cu tipul de tabla folosit"
+ rus "Столбец типа BLOB '%-.192s' не может быть иÑпользован как значение ключа в таблице такого типа"
+ serbian "BLOB kolona '%-.192s' ne može biti upotrebljena za navoÄ‘enje kljuÄa sa tipom tabele koji se trenutno koristi"
+ slo "Blob pole '%-.192s' nemôže byÅ¥ použité ako kľúÄ"
+ spa "La columna Blob '%-.192s' no puede ser usada en una declaracion de clave"
+ swe "En BLOB '%-.192s' kan inte vara nyckel med den använda tabelltypen"
+ ukr "BLOB Ñтовбець '%-.192s' не може бути викориÑтаний у визначенні ключа в цьому типі таблиці"
+ER_TOO_BIG_FIELDLENGTH 42000 S1009
+ cze "P-Bříliš velká délka sloupce '%-.192s' (nejvíce %lu). Použijte BLOB"
+ dan "For stor feltlængde for kolonne '%-.192s' (maks = %lu). Brug BLOB i stedet"
+ nla "Te grote kolomlengte voor '%-.192s' (max = %lu). Maak hiervoor gebruik van het type BLOB"
+ eng "Column length too big for column '%-.192s' (max = %lu); use BLOB or TEXT instead"
+ jps "column '%-.192s' ã¯,確ä¿ã™ã‚‹ column ã®å¤§ãã•ãŒå¤šã™ãŽã¾ã™. (最大 %lu ã¾ã§). BLOB ã‚’ã‹ã‚ã‚Šã«ä½¿ç”¨ã—ã¦ãã ã•ã„."
+ est "Tulba '%-.192s' pikkus on liiga pikk (maksimaalne pikkus: %lu). Kasuta BLOB väljatüüpi"
+ fre "Champ '%-.192s' trop long (max = %lu). Utilisez un BLOB"
+ ger "Feldlänge für Feld '%-.192s' zu groß (maximal %lu). BLOB- oder TEXT-Spaltentyp verwenden!"
+ greek "Î Î¿Î»Ï Î¼ÎµÎ³Î¬Î»Î¿ μήκος για το πεδίο '%-.192s' (max = %lu). ΠαÏακαλώ χÏησιμοποιείστε τον Ï„Ïπο BLOB"
+ hun "A(z) '%-.192s' oszlop tul hosszu. (maximum = %lu). Hasznaljon BLOB tipust inkabb."
+ ita "La colonna '%-.192s' e` troppo grande (max=%lu). Utilizza un BLOB."
+ jpn "column '%-.192s' ã¯,確ä¿ã™ã‚‹ column ã®å¤§ãã•ãŒå¤šã™ãŽã¾ã™. (最大 %lu ã¾ã§). BLOB ã‚’ã‹ã‚ã‚Šã«ä½¿ç”¨ã—ã¦ãã ã•ã„."
+ kor "칼럼 '%-.192s'ì˜ ì¹¼ëŸ¼ 길ì´ê°€ 너무 ê¹ë‹ˆë‹¤ (최대 = %lu). ëŒ€ì‹ ì— BLOB를 사용하세요."
+ nor "For stor nøkkellengde for kolonne '%-.192s' (maks = %lu). Bruk BLOB istedenfor"
+ norwegian-ny "For stor nykkellengde for felt '%-.192s' (maks = %lu). Bruk BLOB istadenfor"
+ pol "Zbyt duża długo?ć kolumny '%-.192s' (maks. = %lu). W zamian użyj typu BLOB"
+ por "Comprimento da coluna '%-.192s' grande demais (max = %lu); use BLOB em seu lugar"
+ rum "Lungimea coloanei '%-.192s' este prea lunga (maximum = %lu). Foloseste BLOB mai bine"
+ rus "Слишком Ð±Ð¾Ð»ÑŒÑˆÐ°Ñ Ð´Ð»Ð¸Ð½Ð° Ñтолбца '%-.192s' (макÑимум = %lu). ИÑпользуйте тип BLOB или TEXT вмеÑто текущего"
+ serbian "Previše podataka za kolonu '%-.192s' (maksimum je %lu). Upotrebite BLOB polje"
+ slo "Príliš veľká dĺžka pre pole '%-.192s' (maximum = %lu). Použite BLOB"
+ spa "Longitud de columna demasiado grande para la columna '%-.192s' (maximo = %lu).Usar BLOB en su lugar"
+ swe "För stor kolumnlängd angiven för '%-.192s' (max= %lu). Använd en BLOB instället"
+ ukr "Задовга довжина ÑÑ‚Ð¾Ð²Ð±Ñ†Ñ '%-.192s' (max = %lu). ВикориÑтайте тип BLOB"
+ER_WRONG_AUTO_KEY 42000 S1009
+ cze "M-Bůžete mít pouze jedno AUTO pole a to musí být definováno jako klíÄ"
+ dan "Der kan kun specificeres eet AUTO_INCREMENT-felt, og det skal være indekseret"
+ nla "Er kan slechts 1 autofield zijn en deze moet als zoeksleutel worden gedefinieerd."
+ eng "Incorrect table definition; there can be only one auto column and it must be defined as a key"
+ jps "テーブルã®å®šç¾©ãŒé•ã„ã¾ã™; there can be only one auto column and it must be defined as a key",
+ est "Vigane tabelikirjeldus; Tabelis tohib olla üks auto_increment tüüpi tulp ning see peab olema defineeritud võtmena"
+ fre "Un seul champ automatique est permis et il doit être indexé"
+ ger "Falsche Tabellendefinition. Es darf nur eine AUTO_INCREMENT-Spalte geben, und diese muss als Schlüssel definiert werden"
+ greek "ΜποÏεί να υπάÏχει μόνο ένα auto field και Ï€Ïέπει να έχει οÏισθεί σαν key"
+ hun "Csak egy auto mezo lehetseges, es azt kulcskent kell definialni."
+ ita "Puo` esserci solo un campo AUTO e deve essere definito come chiave"
+ jpn "テーブルã®å®šç¾©ãŒé•ã„ã¾ã™; there can be only one auto column and it must be defined as a key"
+ kor "부정확한 í…Œì´ë¸” ì •ì˜; í…Œì´ë¸”ì€ í•˜ë‚˜ì˜ auto ì¹¼ëŸ¼ì´ ì¡´ìž¬í•˜ê³  키로 ì •ì˜ë˜ì–´ì ¸ì•¼ 합니다."
+ nor "Bare ett auto felt kan være definert som nøkkel."
+ norwegian-ny "Bare eitt auto felt kan være definert som nøkkel."
+ pol "W tabeli może być tylko jedno pole auto i musi ono być zdefiniowane jako klucz"
+ por "Definição incorreta de tabela. Somente é permitido um único campo auto-incrementado e ele tem que ser definido como chave"
+ rum "Definitia tabelei este incorecta; Nu pot fi mai mult de o singura coloana de tip auto si aceasta trebuie definita ca cheie"
+ rus "Ðекорректное определение таблицы: может ÑущеÑтвовать только один автоинкрементный Ñтолбец, и он должен быть определен как ключ"
+ serbian "PogreÅ¡na definicija tabele; U tabeli može postojati samo jedna 'AUTO' kolona i ona mora biti istovremeno definisana kao kolona kljuÄa"
+ slo "Môžete maÅ¥ iba jedno AUTO pole a to musí byÅ¥ definované ako kľúÄ"
+ spa "Puede ser solamente un campo automatico y este debe ser definido como una clave"
+ swe "Det får finnas endast ett AUTO_INCREMENT-fält och detta måste vara en nyckel"
+ ukr "Ðевірне Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ–; Може бути лише один автоматичний Ñтовбець, що повинен бути визначений Ñк ключ"
+ER_UNUSED_2
+ eng "You should never see it"
+ER_NORMAL_SHUTDOWN
+ cze "%s: norm-Bální ukonÄení\n"
+ dan "%s: Normal nedlukning\n"
+ nla "%s: Normaal afgesloten \n"
+ eng "%s: Normal shutdown\n"
+ est "%s: MariaDB lõpetas\n"
+ fre "%s: Arrêt normal du serveur\n"
+ ger "%s: Normal heruntergefahren\n"
+ greek "%s: Φυσιολογική διαδικασία shutdown\n"
+ hun "%s: Normal leallitas\n"
+ ita "%s: Shutdown normale\n"
+ kor "%s: ì •ìƒì ì¸ shutdown\n"
+ nor "%s: Normal avslutning\n"
+ norwegian-ny "%s: Normal nedkopling\n"
+ pol "%s: Standardowe zakończenie działania\n"
+ por "%s: 'Shutdown' normal\n"
+ rum "%s: Terminare normala\n"
+ rus "%s: ÐšÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ð°Ñ Ð¾Ñтановка\n"
+ serbian "%s: Normalno gašenje\n"
+ slo "%s: normálne ukonÄenie\n"
+ spa "%s: Apagado normal\n"
+ swe "%s: Normal avslutning\n"
+ ukr "%s: Ðормальне завершеннÑ\n"
+ER_GOT_SIGNAL
+ cze "%s: p-BÅ™ijat signal %d, konÄím\n"
+ dan "%s: Fangede signal %d. Afslutter!!\n"
+ nla "%s: Signaal %d. Systeem breekt af!\n"
+ eng "%s: Got signal %d. Aborting!\n"
+ jps "%s: Got signal %d. 中断!¥n",
+ est "%s: sain signaali %d. Lõpetan!\n"
+ fre "%s: Reçu le signal %d. Abandonne!\n"
+ ger "%s: Signal %d erhalten. Abbruch!\n"
+ greek "%s: Ελήφθη το μήνυμα %d. Η διαδικασία εγκαταλείπεται!\n"
+ hun "%s: %d jelzes. Megszakitva!\n"
+ ita "%s: Ricevuto segnale %d. Interruzione!\n"
+ jpn "%s: Got signal %d. 中断!\n"
+ kor "%s: %d 신호가 들어왔ìŒ. 중지!\n"
+ nor "%s: Oppdaget signal %d. Avslutter!\n"
+ norwegian-ny "%s: Oppdaga signal %d. Avsluttar!\n"
+ pol "%s: Otrzymano sygnał %d. Kończenie działania!\n"
+ por "%s: Obteve sinal %d. Abortando!\n"
+ rum "%s: Semnal %d obtinut. Aborting!\n"
+ rus "%s: Получен Ñигнал %d. Прекращаем!\n"
+ serbian "%s: Dobio signal %d. Prekidam!\n"
+ slo "%s: prijatý signál %d, ukonÄenie (Abort)!\n"
+ spa "%s: Recibiendo signal %d. Abortando!\n"
+ swe "%s: Fick signal %d. Avslutar!\n"
+ ukr "%s: Отримано Ñигнал %d. ПерериваюÑÑŒ!\n"
+ER_SHUTDOWN_COMPLETE
+ cze "%s: ukon-BÄení práce hotovo\n"
+ dan "%s: Server lukket\n"
+ nla "%s: Afsluiten afgerond\n"
+ eng "%s: Shutdown complete\n"
+ jps "%s: Shutdown 完了¥n",
+ est "%s: Lõpp\n"
+ fre "%s: Arrêt du serveur terminé\n"
+ ger "%s: Herunterfahren beendet\n"
+ greek "%s: Η διαδικασία Shutdown ολοκληÏώθηκε\n"
+ hun "%s: A leallitas kesz\n"
+ ita "%s: Shutdown completato\n"
+ jpn "%s: Shutdown 完了\n"
+ kor "%s: Shutdown ì´ ì™„ë£Œë¨!\n"
+ nor "%s: Avslutning komplett\n"
+ norwegian-ny "%s: Nedkopling komplett\n"
+ pol "%s: Zakończenie działania wykonane\n"
+ por "%s: 'Shutdown' completo\n"
+ rum "%s: Terminare completa\n"
+ rus "%s: ОÑтановка завершена\n"
+ serbian "%s: Gašenje završeno\n"
+ slo "%s: práca ukonÄená\n"
+ spa "%s: Apagado completado\n"
+ swe "%s: Avslutning klar\n"
+ ukr "%s: Роботу завершено\n"
+ER_FORCING_CLOSE 08S01
+ cze "%s: n-Básilné uzavření threadu %ld uživatele '%-.48s'\n"
+ dan "%s: Forceret nedlukning af tråd: %ld bruger: '%-.48s'\n"
+ nla "%s: Afsluiten afgedwongen van thread %ld gebruiker: '%-.48s'\n"
+ eng "%s: Forcing close of thread %ld user: '%-.48s'\n"
+ jps "%s: スレッド %ld 強制終了 user: '%-.48s'¥n",
+ est "%s: Sulgen jõuga lõime %ld kasutaja: '%-.48s'\n"
+ fre "%s: Arrêt forcé de la tâche (thread) %ld utilisateur: '%-.48s'\n"
+ ger "%s: Thread %ld zwangsweise beendet. Benutzer: '%-.48s'\n"
+ greek "%s: Το thread θα κλείσει %ld user: '%-.48s'\n"
+ hun "%s: A(z) %ld thread kenyszeritett zarasa. Felhasznalo: '%-.48s'\n"
+ ita "%s: Forzata la chiusura del thread %ld utente: '%-.48s'\n"
+ jpn "%s: スレッド %ld 強制終了 user: '%-.48s'\n"
+ kor "%s: thread %ldì˜ ê°•ì œ 종료 user: '%-.48s'\n"
+ nor "%s: Påtvinget avslutning av tråd %ld bruker: '%-.48s'\n"
+ norwegian-ny "%s: Påtvinga avslutning av tråd %ld brukar: '%-.48s'\n"
+ pol "%s: Wymuszenie zamknięcia w?tku %ld użytkownik: '%-.48s'\n"
+ por "%s: Forçando finalização da 'thread' %ld - usuário '%-.48s'\n"
+ rum "%s: Terminare fortata a thread-ului %ld utilizatorului: '%-.48s'\n"
+ rus "%s: Принудительно закрываем поток %ld пользователÑ: '%-.48s'\n"
+ serbian "%s: Usiljeno gašenje thread-a %ld koji pripada korisniku: '%-.48s'\n"
+ slo "%s: násilné ukonÄenie vlákna %ld užívateľa '%-.48s'\n"
+ spa "%s: Forzando a cerrar el thread %ld usuario: '%-.48s'\n"
+ swe "%s: Stänger av tråd %ld; användare: '%-.48s'\n"
+ ukr "%s: ПриÑкорюю Ð·Ð°ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ð³Ñ–Ð»ÐºÐ¸ %ld кориÑтувача: '%-.48s'\n"
+ER_IPSOCK_ERROR 08S01
+ cze "Nemohu vytvo-Břit IP socket"
+ dan "Kan ikke oprette IP socket"
+ nla "Kan IP-socket niet openen"
+ eng "Can't create IP socket"
+ jps "IP socket ãŒä½œã‚Œã¾ã›ã‚“",
+ est "Ei suuda luua IP socketit"
+ fre "Ne peut créer la connexion IP (socket)"
+ ger "Kann IP-Socket nicht erzeugen"
+ greek "Δεν είναι δυνατή η δημιουÏγία IP socket"
+ hun "Az IP socket nem hozhato letre"
+ ita "Impossibile creare il socket IP"
+ jpn "IP socket ãŒä½œã‚Œã¾ã›ã‚“"
+ kor "IP ì†Œì¼“ì„ ë§Œë“¤ì§€ 못했습니다."
+ nor "Kan ikke opprette IP socket"
+ norwegian-ny "Kan ikkje opprette IP socket"
+ pol "Nie można stworzyć socket'u IP"
+ por "Não pode criar o soquete IP"
+ rum "Nu pot crea IP socket"
+ rus "Ðевозможно Ñоздать IP-Ñокет"
+ serbian "Ne mogu da kreiram IP socket"
+ slo "Nemôžem vytvoriť IP socket"
+ spa "No puedo crear IP socket"
+ swe "Kan inte skapa IP-socket"
+ ukr "Ðе можу Ñтворити IP роз'єм"
+ER_NO_SUCH_INDEX 42S12 S1009
+ cze "Tabulka '%-.192s' nem-Bá index odpovídající CREATE INDEX. Vytvořte tabulku znovu"
+ dan "Tabellen '%-.192s' har ikke den nøgle, som blev brugt i CREATE INDEX. Genopret tabellen"
+ nla "Tabel '%-.192s' heeft geen INDEX zoals deze gemaakt worden met CREATE INDEX. Maak de tabel opnieuw"
+ eng "Table '%-.192s' has no index like the one used in CREATE INDEX; recreate the table"
+ jps "Table '%-.192s' ã¯ãã®ã‚ˆã†ãª index ã‚’æŒã£ã¦ã„ã¾ã›ã‚“(CREATE INDEX 実行時ã«æŒ‡å®šã•ã‚Œã¦ã„ã¾ã›ã‚“). テーブルを作り直ã—ã¦ãã ã•ã„",
+ est "Tabelil '%-.192s' puuduvad võtmed. Loo tabel uuesti"
+ fre "La table '%-.192s' n'a pas d'index comme celle utilisée dans CREATE INDEX. Recréez la table"
+ ger "Tabelle '%-.192s' besitzt keinen wie den in CREATE INDEX verwendeten Index. Tabelle neu anlegen"
+ greek "Ο πίνακας '%-.192s' δεν έχει ευÏετήÏιο (index) σαν αυτό που χÏησιμοποιείτε στην CREATE INDEX. ΠαÏακαλώ, ξαναδημιουÏγήστε τον πίνακα"
+ hun "A(z) '%-.192s' tablahoz nincs meg a CREATE INDEX altal hasznalt index. Alakitsa at a tablat"
+ ita "La tabella '%-.192s' non ha nessun indice come quello specificatato dalla CREATE INDEX. Ricrea la tabella"
+ jpn "Table '%-.192s' ã¯ãã®ã‚ˆã†ãª index ã‚’æŒã£ã¦ã„ã¾ã›ã‚“(CREATE INDEX 実行時ã«æŒ‡å®šã•ã‚Œã¦ã„ã¾ã›ã‚“). テーブルを作り直ã—ã¦ãã ã•ã„"
+ kor "í…Œì´ë¸” '%-.192s'는 ì¸ë±ìŠ¤ë¥¼ 만들지 않았습니다. alter í…Œì´ë¸”ëª…ë ¹ì„ ì´ìš©í•˜ì—¬ í…Œì´ë¸”ì„ ìˆ˜ì •í•˜ì„¸ìš”..."
+ nor "Tabellen '%-.192s' har ingen index som den som er brukt i CREATE INDEX. Gjenopprett tabellen"
+ norwegian-ny "Tabellen '%-.192s' har ingen index som den som er brukt i CREATE INDEX. Oprett tabellen på nytt"
+ pol "Tabela '%-.192s' nie ma indeksu takiego jak w CREATE INDEX. Stwórz tabelę"
+ por "Tabela '%-.192s' não possui um índice como o usado em CREATE INDEX. Recrie a tabela"
+ rum "Tabela '%-.192s' nu are un index ca acela folosit in CREATE INDEX. Re-creeaza tabela"
+ rus "Ð’ таблице '%-.192s' нет такого индекÑа, как в CREATE INDEX. Создайте таблицу заново"
+ serbian "Tabela '%-.192s' nema isti indeks kao onaj upotrebljen pri komandi 'CREATE INDEX'. Napravite tabelu ponovo"
+ slo "Tabuľka '%-.192s' nemá index zodpovedajúci CREATE INDEX. Vytvorte tabulku znova"
+ spa "La tabla '%-.192s' no tiene indice como el usado en CREATE INDEX. Crea de nuevo la tabla"
+ swe "Tabellen '%-.192s' har inget index som motsvarar det angivna i CREATE INDEX. Skapa om tabellen"
+ ukr "Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ '%-.192s' має індекÑ, що не Ñпівпадає з вказанним у CREATE INDEX. Створіть таблицю знову"
+ER_WRONG_FIELD_TERMINATORS 42000 S1009
+ cze "Argument separ-Bátoru položek nebyl oÄekáván. PÅ™eÄtÄ›te si manuál"
+ dan "Felt adskiller er ikke som forventet, se dokumentationen"
+ nla "De argumenten om velden te scheiden zijn anders dan verwacht. Raadpleeg de handleiding"
+ eng "Field separator argument is not what is expected; check the manual"
+ est "Väljade eraldaja erineb oodatust. Tutvu kasutajajuhendiga"
+ fre "Séparateur de champs inconnu. Vérifiez dans le manuel"
+ ger "Feldbegrenzer-Argument ist nicht in der erwarteten Form. Bitte im Handbuch nachlesen"
+ greek "Ο διαχωÏιστής πεδίων δεν είναι αυτός που αναμενόταν. ΠαÏακαλώ ανατÏέξτε στο manual"
+ hun "A mezoelvalaszto argumentumok nem egyeznek meg a varttal. Nezze meg a kezikonyvben!"
+ ita "L'argomento 'Field separator' non e` quello atteso. Controlla il manuale"
+ kor "í•„ë“œ êµ¬ë¶„ìž ì¸ìˆ˜ë“¤ì´ 완전하지 않습니다. ë©”ë‰´ì–¼ì„ ì°¾ì•„ 보세요."
+ nor "Felt skiller argumentene er ikke som forventet, se dokumentasjonen"
+ norwegian-ny "Felt skiljer argumenta er ikkje som venta, sjå dokumentasjonen"
+ pol "Nie oczekiwano separatora. SprawdĽ podręcznik"
+ por "Argumento separador de campos não é o esperado. Cheque o manual"
+ rum "Argumentul pentru separatorul de cimpuri este diferit de ce ma asteptam. Verifica manualul"
+ rus "Ðргумент Ñ€Ð°Ð·Ð´ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ Ð¿Ð¾Ð»ÐµÐ¹ - не тот, который ожидалÑÑ. ОбращайтеÑÑŒ к документации"
+ serbian "Argument separatora polja nije ono Å¡to se oÄekivalo. Proverite uputstvo MariaDB server-a"
+ slo "Argument oddeľovaÄ polí nezodpovedá požiadavkám. Skontrolujte v manuáli"
+ spa "Los separadores de argumentos del campo no son los especificados. Comprueba el manual"
+ swe "Fältseparatorerna är vad som förväntades. Kontrollera mot manualen"
+ ukr "Хибний розділювач полів. Почитайте документацію"
+ER_BLOBS_AND_NO_TERMINATED 42000 S1009
+ cze "Nen-Bí možné použít pevný rowlength s BLOBem. Použijte 'fields terminated by'."
+ dan "Man kan ikke bruge faste feltlængder med BLOB. Brug i stedet 'fields terminated by'."
+ nla "Bij het gebruik van BLOBs is het niet mogelijk om vaste rijlengte te gebruiken. Maak s.v.p. gebruik van 'fields terminated by'."
+ eng "You can't use fixed rowlength with BLOBs; please use 'fields terminated by'"
+ est "BLOB-tüüpi väljade olemasolul ei saa kasutada fikseeritud väljapikkust. Vajalik 'fields terminated by' määrang."
+ fre "Vous ne pouvez utiliser des lignes de longueur fixe avec des BLOBs. Utiliser 'fields terminated by'."
+ ger "Eine feste Zeilenlänge kann für BLOB-Felder nicht verwendet werden. Bitte 'fields terminated by' verwenden"
+ greek "Δεν μποÏείτε να χÏησιμοποιήσετε fixed rowlength σε BLOBs. ΠαÏακαλώ χÏησιμοποιείστε 'fields terminated by'."
+ hun "Fix hosszusagu BLOB-ok nem hasznalhatok. Hasznalja a 'mezoelvalaszto jelet' ."
+ ita "Non possono essere usate righe a lunghezza fissa con i BLOB. Usa 'FIELDS TERMINATED BY'."
+ jpn "You can't use fixed rowlength with BLOBs; please use 'fields terminated by'."
+ kor "BLOB로는 고정길ì´ì˜ lowlength를 사용할 수 없습니다. 'fields terminated by'를 사용하세요."
+ nor "En kan ikke bruke faste feltlengder med BLOB. Vennlisgt bruk 'fields terminated by'."
+ norwegian-ny "Ein kan ikkje bruke faste feltlengder med BLOB. Vennlisgt bruk 'fields terminated by'."
+ pol "Nie można użyć stałej długo?ci wiersza z polami typu BLOB. Użyj 'fields terminated by'."
+ por "Você não pode usar comprimento de linha fixo com BLOBs. Por favor, use campos com comprimento limitado."
+ rum "Nu poti folosi lungime de cimp fix pentru BLOB-uri. Foloseste 'fields terminated by'."
+ rus "ФикÑированный размер запиÑи Ñ Ð¿Ð¾Ð»Ñми типа BLOB иÑпользовать нельзÑ, применÑйте 'fields terminated by'"
+ serbian "Ne možete koristiti fiksnu veliÄinu sloga kada imate BLOB polja. Molim koristite 'fields terminated by' opciju."
+ slo "Nie je možné použiť fixnú dĺžku s BLOBom. Použite 'fields terminated by'."
+ spa "No puedes usar longitudes de filas fijos con BLOBs. Por favor usa 'campos terminados por '."
+ swe "Man kan inte använda fast radlängd med blobs. Använd 'fields terminated by'"
+ ukr "Ðе можна викориÑтовувати Ñталу довжину Ñтроки з BLOB. ЗкориÑтайтеÑÑ 'fields terminated by'"
+ER_TEXTFILE_NOT_READABLE
+ cze "Soubor '%-.128s' mus-Bí být v adresáři databáze nebo Äitelný pro vÅ¡echny"
+ dan "Filen '%-.128s' skal være i database-folderen, eller kunne læses af alle"
+ nla "Het bestand '%-.128s' dient in de database directory voor the komen of leesbaar voor iedereen te zijn."
+ eng "The file '%-.128s' must be in the database directory or be readable by all"
+ jps "ファイル '%-.128s' 㯠databse ã® directory ã«ã‚ã‚‹ã‹å…¨ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒèª­ã‚るよã†ã«è¨±å¯ã•ã‚Œã¦ã„ãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“.",
+ est "Fail '%-.128s' peab asuma andmebaasi kataloogis või olema kõigile loetav"
+ fre "Le fichier '%-.128s' doit être dans le répertoire de la base et lisible par tous"
+ ger "Datei '%-.128s' muss im Datenbank-Verzeichnis vorhanden oder lesbar für alle sein"
+ greek "Το αÏχείο '%-.128s' Ï€Ïέπει να υπάÏχει στο database directory ή να μποÏεί να διαβαστεί από όλους"
+ hun "A(z) '%-.128s'-nak az adatbazis konyvtarban kell lennie, vagy mindenki szamara olvashatonak"
+ ita "Il file '%-.128s' deve essere nella directory del database e deve essere leggibile da tutti"
+ jpn "ファイル '%-.128s' 㯠databse ã® directory ã«ã‚ã‚‹ã‹å…¨ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒèª­ã‚るよã†ã«è¨±å¯ã•ã‚Œã¦ã„ãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“."
+ kor "'%-.128s' í™”ì¼ëŠ” ë°ì´íƒ€ë² ì´ìŠ¤ ë””ë ‰í† ë¦¬ì— ì¡´ìž¬í•˜ê±°ë‚˜ 모ë‘ì—게 ì½ê¸° 가능하여야 합니다."
+ nor "Filen '%-.128s' må være i database-katalogen for å være lesbar for alle"
+ norwegian-ny "Filen '%-.128s' må være i database-katalogen for å være lesbar for alle"
+ pol "Plik '%-.128s' musi znajdować sie w katalogu bazy danych lub mieć prawa czytania przez wszystkich"
+ por "Arquivo '%-.128s' tem que estar no diretório do banco de dados ou ter leitura possível para todos"
+ rum "Fisierul '%-.128s' trebuie sa fie in directorul bazei de data sau trebuie sa poata sa fie citit de catre toata lumea (verifica permisiile)"
+ rus "Файл '%-.128s' должен находитьÑÑ Ð² том же каталоге, что и база данных, или быть общедоÑтупным Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ"
+ serbian "File '%-.128s' mora biti u direktorijumu gde su file-ovi baze i mora imati odgovarajuća prava pristupa"
+ slo "Súbor '%-.128s' musí byÅ¥ v adresári databázy, alebo Äitateľný pre vÅ¡etkých"
+ spa "El archivo '%-.128s' debe estar en el directorio de la base de datos o ser de lectura por todos"
+ swe "Textfilen '%-.128s' måste finnas i databasbiblioteket eller vara läsbar för alla"
+ ukr "Файл '%-.128s' повинен бути у теці бази данних або мати вÑтановлене право на Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ð´Ð»Ñ ÑƒÑÑ–Ñ…"
+ER_FILE_EXISTS_ERROR
+ cze "Soubor '%-.200s' ji-Bž existuje"
+ dan "Filen '%-.200s' eksisterer allerede"
+ nla "Het bestand '%-.200s' bestaat reeds"
+ eng "File '%-.200s' already exists"
+ jps "File '%-.200s' ã¯æ—¢ã«å­˜åœ¨ã—ã¾ã™",
+ est "Fail '%-.200s' juba eksisteerib"
+ fre "Le fichier '%-.200s' existe déjà"
+ ger "Datei '%-.200s' bereits vorhanden"
+ greek "Το αÏχείο '%-.200s' υπάÏχει ήδη"
+ hun "A '%-.200s' file mar letezik."
+ ita "Il file '%-.200s' esiste gia`"
+ jpn "File '%-.200s' ã¯æ—¢ã«å­˜åœ¨ã—ã¾ã™"
+ kor "'%-.200s' í™”ì¼ì€ ì´ë¯¸ 존재합니다."
+ nor "Filen '%-.200s' eksisterte allerede"
+ norwegian-ny "Filen '%-.200s' eksisterte allereide"
+ pol "Plik '%-.200s' już istnieje"
+ por "Arquivo '%-.200s' já existe"
+ rum "Fisierul '%-.200s' exista deja"
+ rus "Файл '%-.200s' уже ÑущеÑтвует"
+ serbian "File '%-.200s' već postoji"
+ slo "Súbor '%-.200s' už existuje"
+ spa "El archivo '%-.200s' ya existe"
+ swe "Filen '%-.200s' existerar redan"
+ ukr "Файл '%-.200s' вже Ñ–Ñнує"
+ER_LOAD_INFO
+ cze "Z-Báznamů: %ld Vymazáno: %ld PÅ™eskoÄeno: %ld Varování: %ld"
+ dan "Poster: %ld Fjernet: %ld Sprunget over: %ld Advarsler: %ld"
+ nla "Records: %ld Verwijderd: %ld Overgeslagen: %ld Waarschuwingen: %ld"
+ eng "Records: %ld Deleted: %ld Skipped: %ld Warnings: %ld"
+ jps "レコード数: %ld 削除: %ld Skipped: %ld Warnings: %ld",
+ est "Kirjeid: %ld Kustutatud: %ld Vahele jäetud: %ld Hoiatusi: %ld"
+ fre "Enregistrements: %ld Effacés: %ld Non traités: %ld Avertissements: %ld"
+ ger "Datensätze: %ld Gelöscht: %ld Ausgelassen: %ld Warnungen: %ld"
+ greek "ΕγγÏαφές: %ld ΔιαγÏαφές: %ld ΠαÏεκάμφθησαν: %ld ΠÏοειδοποιήσεις: %ld"
+ hun "Rekordok: %ld Torolve: %ld Skipped: %ld Warnings: %ld"
+ ita "Records: %ld Cancellati: %ld Saltati: %ld Avvertimenti: %ld"
+ jpn "レコード数: %ld 削除: %ld Skipped: %ld Warnings: %ld"
+ kor "레코드: %ld개 삭제: %ld개 스킵: %ld개 경고: %ld개"
+ nor "Poster: %ld Fjernet: %ld Hoppet over: %ld Advarsler: %ld"
+ norwegian-ny "Poster: %ld Fjerna: %ld Hoppa over: %ld Ã…tvaringar: %ld"
+ pol "Recordów: %ld Usuniętych: %ld Pominiętych: %ld Ostrzeżeń: %ld"
+ por "Registros: %ld - Deletados: %ld - Ignorados: %ld - Avisos: %ld"
+ rum "Recorduri: %ld Sterse: %ld Sarite (skipped): %ld Atentionari (warnings): %ld"
+ rus "ЗапиÑей: %ld Удалено: %ld Пропущено: %ld Предупреждений: %ld"
+ serbian "Slogova: %ld Izbrisano: %ld PreskoÄeno: %ld Upozorenja: %ld"
+ slo "Záznamov: %ld Zmazaných: %ld PreskoÄených: %ld Varovania: %ld"
+ spa "Registros: %ld Borrados: %ld Saltados: %ld Peligros: %ld"
+ swe "Rader: %ld Bortagna: %ld Dubletter: %ld Varningar: %ld"
+ ukr "ЗапиÑів: %ld Видалено: %ld Пропущено: %ld ЗаÑтережень: %ld"
+ER_ALTER_INFO
+ cze "Z-Báznamů: %ld Zdvojených: %ld"
+ dan "Poster: %ld Ens: %ld"
+ nla "Records: %ld Dubbel: %ld"
+ eng "Records: %ld Duplicates: %ld"
+ jps "レコード数: %ld é‡è¤‡: %ld",
+ est "Kirjeid: %ld Kattuvaid: %ld"
+ fre "Enregistrements: %ld Doublons: %ld"
+ ger "Datensätze: %ld Duplikate: %ld"
+ greek "ΕγγÏαφές: %ld Επαναλήψεις: %ld"
+ hun "Rekordok: %ld Duplikalva: %ld"
+ ita "Records: %ld Duplicati: %ld"
+ jpn "レコード数: %ld é‡è¤‡: %ld"
+ kor "레코드: %ld개 중복: %ld개"
+ nor "Poster: %ld Like: %ld"
+ norwegian-ny "Poster: %ld Like: %ld"
+ pol "Rekordów: %ld Duplikatów: %ld"
+ por "Registros: %ld - Duplicados: %ld"
+ rum "Recorduri: %ld Duplicate: %ld"
+ rus "ЗапиÑей: %ld Дубликатов: %ld"
+ serbian "Slogova: %ld Duplikata: %ld"
+ slo "Záznamov: %ld Opakovaných: %ld"
+ spa "Registros: %ld Duplicados: %ld"
+ swe "Rader: %ld Dubletter: %ld"
+ ukr "ЗапиÑів: %ld Дублікатів: %ld"
+ER_WRONG_SUB_KEY
+ cze "Chybn-Bá podÄást klíÄe -- není to Å™etÄ›zec nebo je delší než délka Äásti klíÄe"
+ dan "Forkert indeksdel. Den anvendte nøgledel er ikke en streng eller længden er større end nøglelængden"
+ nla "Foutief sub-gedeelte van de zoeksleutel. De gebruikte zoeksleutel is geen onderdeel van een string of of de gebruikte lengte is langer dan de zoeksleutel"
+ eng "Incorrect prefix key; the used key part isn't a string, the used length is longer than the key part, or the storage engine doesn't support unique prefix keys"
+ est "Vigane võtme osa. Kasutatud võtmeosa ei ole string tüüpi, määratud pikkus on pikem kui võtmeosa või tabelihandler ei toeta seda tüüpi võtmeid"
+ fre "Mauvaise sous-clef. Ce n'est pas un 'string' ou la longueur dépasse celle définie dans la clef"
+ ger "Falscher Unterteilschlüssel. Der verwendete Schlüsselteil ist entweder kein String, die verwendete Länge ist länger als der Teilschlüssel oder die Speicher-Engine unterstützt keine Unterteilschlüssel"
+ greek "Εσφαλμένο sub part key. Το χÏησιμοποιοÏμενο key part δεν είναι string ή το μήκος του είναι μεγαλÏτεÏο"
+ hun "Rossz alkulcs. A hasznalt kulcsresz nem karaktersorozat vagy hosszabb, mint a kulcsresz"
+ ita "Sotto-parte della chiave errata. La parte di chiave utilizzata non e` una stringa o la lunghezza e` maggiore della parte di chiave."
+ jpn "Incorrect prefix key; the used key part isn't a string or the used length is longer than the key part"
+ kor "부정확한 서버 파트 키. ì‚¬ìš©ëœ í‚¤ 파트가 스트ë§ì´ 아니거나 키 íŒŒíŠ¸ì˜ ê¸¸ì´ê°€ 너무 ê¹ë‹ˆë‹¤."
+ nor "Feil delnøkkel. Den brukte delnøkkelen er ikke en streng eller den oppgitte lengde er lengre enn nøkkel lengden"
+ norwegian-ny "Feil delnykkel. Den brukte delnykkelen er ikkje ein streng eller den oppgitte lengda er lengre enn nykkellengden"
+ pol "Błędna podczę?ć klucza. Użyta czę?ć klucza nie jest łańcuchem lub użyta długo?ć jest większa niż czę?ć klucza"
+ por "Sub parte da chave incorreta. A parte da chave usada não é uma 'string' ou o comprimento usado é maior que parte da chave ou o manipulador de tabelas não suporta sub chaves únicas"
+ rum "Componentul cheii este incorrect. Componentul folosit al cheii nu este un sir sau lungimea folosita este mai lunga decit lungimea cheii"
+ rus "ÐÐµÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ð°Ñ Ñ‡Ð°ÑÑ‚ÑŒ ключа. ИÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÐµÐ¼Ð°Ñ Ñ‡Ð°ÑÑ‚ÑŒ ключа не ÑвлÑетÑÑ Ñтрокой, ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ð°Ñ Ð´Ð»Ð¸Ð½Ð° больше, чем длина чаÑти ключа, или обработчик таблицы не поддерживает уникальные чаÑти ключа"
+ serbian "PogreÅ¡an pod-kljuÄ dela kljuÄa. Upotrebljeni deo kljuÄa nije string, upotrebljena dužina je veća od dela kljuÄa ili handler tabela ne podržava jedinstvene pod-kljuÄeve"
+ slo "Incorrect prefix key; the used key part isn't a string or the used length is longer than the key part"
+ spa "Parte de la clave es erronea. Una parte de la clave no es una cadena o la longitud usada es tan grande como la parte de la clave"
+ swe "Felaktig delnyckel. Nyckeldelen är inte en sträng eller den angivna längden är längre än kolumnlängden"
+ ukr "Ðевірна чаÑтина ключа. ВикориÑтана чаÑтина ключа не Ñ” Ñтрокою, задовга або вказівник таблиці не підтримує унікальних чаÑтин ключей"
+ER_CANT_REMOVE_ALL_FIELDS 42000
+ cze "Nen-Bí možné vymazat všechny položky s ALTER TABLE. Použijte DROP TABLE"
+ dan "Man kan ikke slette alle felter med ALTER TABLE. Brug DROP TABLE i stedet."
+ nla "Het is niet mogelijk alle velden te verwijderen met ALTER TABLE. Gebruik a.u.b. DROP TABLE hiervoor!"
+ eng "You can't delete all columns with ALTER TABLE; use DROP TABLE instead"
+ jps "ALTER TABLE ã§å…¨ã¦ã® column ã¯å‰Šé™¤ã§ãã¾ã›ã‚“. DROP TABLE を使用ã—ã¦ãã ã•ã„",
+ est "ALTER TABLE kasutades ei saa kustutada kõiki tulpasid. Kustuta tabel DROP TABLE abil"
+ fre "Vous ne pouvez effacer tous les champs avec ALTER TABLE. Utilisez DROP TABLE"
+ ger "Mit ALTER TABLE können nicht alle Felder auf einmal gelöscht werden. Dafür DROP TABLE verwenden"
+ greek "Δεν είναι δυνατή η διαγÏαφή όλων των πεδίων με ALTER TABLE. ΠαÏακαλώ χÏησιμοποιείστε DROP TABLE"
+ hun "Az osszes mezo nem torolheto az ALTER TABLE-lel. Hasznalja a DROP TABLE-t helyette"
+ ita "Non si possono cancellare tutti i campi con una ALTER TABLE. Utilizzare DROP TABLE"
+ jpn "ALTER TABLE ã§å…¨ã¦ã® column ã¯å‰Šé™¤ã§ãã¾ã›ã‚“. DROP TABLE を使用ã—ã¦ãã ã•ã„"
+ kor "ALTER TABLE 명령으로는 모든 ì¹¼ëŸ¼ì„ ì§€ìš¸ 수 없습니다. DROP TABLE ëª…ë ¹ì„ ì´ìš©í•˜ì„¸ìš”."
+ nor "En kan ikke slette alle felt med ALTER TABLE. Bruk DROP TABLE isteden."
+ norwegian-ny "Ein kan ikkje slette alle felt med ALTER TABLE. Bruk DROP TABLE istadenfor."
+ pol "Nie można usun?ć wszystkich pól wykorzystuj?c ALTER TABLE. W zamian użyj DROP TABLE"
+ por "Você não pode deletar todas as colunas com ALTER TABLE; use DROP TABLE em seu lugar"
+ rum "Nu poti sterge toate coloanele cu ALTER TABLE. Foloseste DROP TABLE in schimb"
+ rus "ÐÐµÐ»ÑŒÐ·Ñ ÑƒÐ´Ð°Ð»Ð¸Ñ‚ÑŒ вÑе Ñтолбцы Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ ALTER TABLE. ИÑпользуйте DROP TABLE"
+ serbian "Ne možete da izbrišete sve kolone pomoću komande 'ALTER TABLE'. Upotrebite komandu 'DROP TABLE' ako želite to da uradite"
+ slo "One nemôžem zmazať all fields with ALTER TABLE; use DROP TABLE instead"
+ spa "No puede borrar todos los campos con ALTER TABLE. Usa DROP TABLE para hacerlo"
+ swe "Man kan inte radera alla fält med ALTER TABLE. Använd DROP TABLE istället"
+ ukr "Ðе можливо видалити вÑÑ– Ñтовбці за допомогою ALTER TABLE. Ð”Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ ÑкориÑтайтеÑÑ DROP TABLE"
+ER_CANT_DROP_FIELD_OR_KEY 42000
+ cze "Nemohu zru-BÅ¡it '%-.192s' (provést DROP). Zkontrolujte, zda neexistují záznamy/klíÄe"
+ dan "Kan ikke udføre DROP '%-.192s'. Undersøg om feltet/nøglen eksisterer."
+ nla "Kan '%-.192s' niet weggooien. Controleer of het veld of de zoeksleutel daadwerkelijk bestaat."
+ eng "Can't DROP '%-.192s'; check that column/key exists"
+ jps "'%-.192s' を破棄ã§ãã¾ã›ã‚“ã§ã—ãŸ; check that column/key exists",
+ est "Ei suuda kustutada '%-.192s'. Kontrolli kas tulp/võti eksisteerib"
+ fre "Ne peut effacer (DROP) '%-.192s'. Vérifiez s'il existe"
+ ger "Kann '%-.192s' nicht löschen. Existiert die Spalte oder der Schlüssel?"
+ greek "ΑδÏνατη η διαγÏαφή (DROP) '%-.192s'. ΠαÏακαλώ ελέγξτε αν το πεδίο/κλειδί υπάÏχει"
+ hun "A DROP '%-.192s' nem lehetseges. Ellenorizze, hogy a mezo/kulcs letezik-e"
+ ita "Impossibile cancellare '%-.192s'. Controllare che il campo chiave esista"
+ jpn "'%-.192s' を破棄ã§ãã¾ã›ã‚“ã§ã—ãŸ; check that column/key exists"
+ kor "'%-.192s'를 DROPí•  수 없습니다. 칼럼ì´ë‚˜ 키가 존재하는지 채í¬í•˜ì„¸ìš”."
+ nor "Kan ikke DROP '%-.192s'. Undersøk om felt/nøkkel eksisterer."
+ norwegian-ny "Kan ikkje DROP '%-.192s'. Undersøk om felt/nøkkel eksisterar."
+ pol "Nie można wykonać operacji DROP '%-.192s'. SprawdĽ, czy to pole/klucz istnieje"
+ por "Não se pode fazer DROP '%-.192s'. Confira se esta coluna/chave existe"
+ rum "Nu pot sa DROP '%-.192s'. Verifica daca coloana/cheia exista"
+ rus "Ðевозможно удалить (DROP) '%-.192s'. УбедитеÑÑŒ что Ñтолбец/ключ дейÑтвительно ÑущеÑтвует"
+ serbian "Ne mogu da izvrÅ¡im komandu drop 'DROP' na '%-.192s'. Proverite da li ta kolona (odnosno kljuÄ) postoji"
+ slo "Nemôžem zruÅ¡iÅ¥ (DROP) '%-.192s'. Skontrolujte, Äi neexistujú záznamy/kľúÄe"
+ spa "No puedo ELIMINAR '%-.192s'. compuebe que el campo/clave existe"
+ swe "Kan inte ta bort '%-.192s'. Kontrollera att fältet/nyckel finns"
+ ukr "Ðе можу DROP '%-.192s'. Перевірте, чи цей Ñтовбець/ключ Ñ–Ñнує"
+ER_INSERT_INFO
+ cze "Z-Báznamů: %ld Zdvojených: %ld Varování: %ld"
+ dan "Poster: %ld Ens: %ld Advarsler: %ld"
+ nla "Records: %ld Dubbel: %ld Waarschuwing: %ld"
+ eng "Records: %ld Duplicates: %ld Warnings: %ld"
+ jps "レコード数: %ld é‡è¤‡æ•°: %ld Warnings: %ld",
+ est "Kirjeid: %ld Kattuvaid: %ld Hoiatusi: %ld"
+ fre "Enregistrements: %ld Doublons: %ld Avertissements: %ld"
+ ger "Datensätze: %ld Duplikate: %ld Warnungen: %ld"
+ greek "ΕγγÏαφές: %ld Επαναλήψεις: %ld ΠÏοειδοποιήσεις: %ld"
+ hun "Rekordok: %ld Duplikalva: %ld Warnings: %ld"
+ ita "Records: %ld Duplicati: %ld Avvertimenti: %ld"
+ jpn "レコード数: %ld é‡è¤‡æ•°: %ld Warnings: %ld"
+ kor "레코드: %ld개 중복: %ld개 경고: %ld개"
+ nor "Poster: %ld Like: %ld Advarsler: %ld"
+ norwegian-ny "Postar: %ld Like: %ld Ã…tvaringar: %ld"
+ pol "Rekordów: %ld Duplikatów: %ld Ostrzeżeń: %ld"
+ por "Registros: %ld - Duplicados: %ld - Avisos: %ld"
+ rum "Recorduri: %ld Duplicate: %ld Atentionari (warnings): %ld"
+ rus "ЗапиÑей: %ld Дубликатов: %ld Предупреждений: %ld"
+ serbian "Slogova: %ld Duplikata: %ld Upozorenja: %ld"
+ slo "Záznamov: %ld Opakovaných: %ld Varovania: %ld"
+ spa "Registros: %ld Duplicados: %ld Peligros: %ld"
+ swe "Rader: %ld Dubletter: %ld Varningar: %ld"
+ ukr "ЗапиÑів: %ld Дублікатів: %ld ЗаÑтережень: %ld"
+ER_UPDATE_TABLE_USED
+ eng "You can't specify target table '%-.192s' for update in FROM clause"
+ ger "Die Verwendung der zu aktualisierenden Zieltabelle '%-.192s' ist in der FROM-Klausel nicht zulässig."
+ rus "Ðе допуÑкаетÑÑ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ðµ таблицы '%-.192s' в ÑпиÑке таблиц FROM Ð´Ð»Ñ Ð²Ð½ÐµÑÐµÐ½Ð¸Ñ Ð² нее изменений"
+ swe "INSERT-table '%-.192s' får inte finnas i FROM tabell-listan"
+ ukr "Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ '%-.192s' що змінюєтьÑÑ Ð½Ðµ дозволена у переліку таблиць FROM"
+ER_NO_SUCH_THREAD
+ cze "Nezn-Bámá identifikace threadu: %lu"
+ dan "Ukendt tråd id: %lu"
+ nla "Onbekend thread id: %lu"
+ eng "Unknown thread id: %lu"
+ jps "thread id: %lu ã¯ã‚ã‚Šã¾ã›ã‚“",
+ est "Tundmatu lõim: %lu"
+ fre "Numéro de tâche inconnu: %lu"
+ ger "Unbekannte Thread-ID: %lu"
+ greek "Αγνωστο thread id: %lu"
+ hun "Ervenytelen szal (thread) id: %lu"
+ ita "Thread id: %lu sconosciuto"
+ jpn "thread id: %lu ã¯ã‚ã‚Šã¾ã›ã‚“"
+ kor "알수 없는 쓰레드 id: %lu"
+ nor "Ukjent tråd id: %lu"
+ norwegian-ny "Ukjent tråd id: %lu"
+ pol "Nieznany identyfikator w?tku: %lu"
+ por "'Id' de 'thread' %lu desconhecido"
+ rum "Id-ul: %lu thread-ului este necunoscut"
+ rus "ÐеизвеÑтный номер потока: %lu"
+ serbian "Nepoznat thread identifikator: %lu"
+ slo "Neznáma identifikácia vlákna: %lu"
+ spa "Identificador del thread: %lu desconocido"
+ swe "Finns ingen tråd med id %lu"
+ ukr "Ðевідомий ідентифікатор гілки: %lu"
+ER_KILL_DENIED_ERROR
+ cze "Nejste vlastn-Bíkem threadu %lu"
+ dan "Du er ikke ejer af tråden %lu"
+ nla "U bent geen bezitter van thread %lu"
+ eng "You are not owner of thread %lu"
+ jps "thread %lu ã®ã‚ªãƒ¼ãƒŠãƒ¼ã§ã¯ã‚ã‚Šã¾ã›ã‚“",
+ est "Ei ole lõime %lu omanik"
+ fre "Vous n'êtes pas propriétaire de la tâche no: %lu"
+ ger "Sie sind nicht Eigentümer von Thread %lu"
+ greek "Δεν είσθε owner του thread %lu"
+ hun "A %lu thread-nek mas a tulajdonosa"
+ ita "Utente non proprietario del thread %lu"
+ jpn "thread %lu ã®ã‚ªãƒ¼ãƒŠãƒ¼ã§ã¯ã‚ã‚Šã¾ã›ã‚“"
+ kor "쓰레드(Thread) %luì˜ ì†Œìœ ìžê°€ 아닙니다."
+ nor "Du er ikke eier av tråden %lu"
+ norwegian-ny "Du er ikkje eigar av tråd %lu"
+ pol "Nie jeste? wła?cicielem w?tku %lu"
+ por "Você não é proprietário da 'thread' %lu"
+ rum "Nu sinteti proprietarul threadului %lu"
+ rus "Ð’Ñ‹ не ÑвлÑетеÑÑŒ владельцем потока %lu"
+ serbian "Vi niste vlasnik thread-a %lu"
+ slo "Nie ste vlastníkom vlákna %lu"
+ spa "Tu no eres el propietario del thread%lu"
+ swe "Du är inte ägare till tråd %lu"
+ ukr "Ви не володар гілки %lu"
+ER_NO_TABLES_USED
+ cze "Nejsou pou-Bžity žádné tabulky"
+ dan "Ingen tabeller i brug"
+ nla "Geen tabellen gebruikt."
+ eng "No tables used"
+ est "Ãœhtegi tabelit pole kasutusel"
+ fre "Aucune table utilisée"
+ ger "Keine Tabellen verwendet"
+ greek "Δεν χÏησιμοποιήθηκαν πίνακες"
+ hun "Nincs hasznalt tabla"
+ ita "Nessuna tabella usata"
+ kor "ì–´ë–¤ í…Œì´ë¸”ë„ ì‚¬ìš©ë˜ì§€ 않았습니다."
+ nor "Ingen tabeller i bruk"
+ norwegian-ny "Ingen tabellar i bruk"
+ pol "Nie ma żadej użytej tabeli"
+ por "Nenhuma tabela usada"
+ rum "Nici o tabela folosita"
+ rus "Ðикакие таблицы не иÑпользованы"
+ serbian "Nema upotrebljenih tabela"
+ slo "Nie je použitá žiadna tabuľka"
+ spa "No ha tablas usadas"
+ swe "Inga tabeller angivna"
+ ukr "Ðе викориÑтано таблиць"
+ER_TOO_BIG_SET
+ cze "P-Bříliš mnoho řetězců pro sloupec %-.192s a SET"
+ dan "For mange tekststrenge til specifikationen af SET i kolonne %-.192s"
+ nla "Teveel strings voor kolom %-.192s en SET"
+ eng "Too many strings for column %-.192s and SET"
+ est "Liiga palju string tulbale %-.192s tüübile SET"
+ fre "Trop de chaînes dans la colonne %-.192s avec SET"
+ ger "Zu viele Strings für Feld %-.192s und SET angegeben"
+ greek "ΠάÏα πολλά strings για το πεδίο %-.192s και SET"
+ hun "Tul sok karakter: %-.192s es SET"
+ ita "Troppe stringhe per la colonna %-.192s e la SET"
+ kor "칼럼 %-.192s와 SETì—ì„œ 스트ë§ì´ 너무 많습니다."
+ nor "For mange tekststrenger kolonne %-.192s og SET"
+ norwegian-ny "For mange tekststrengar felt %-.192s og SET"
+ pol "Zbyt wiele łańcuchów dla kolumny %-.192s i polecenia SET"
+ por "'Strings' demais para coluna '%-.192s' e SET"
+ rum "Prea multe siruri pentru coloana %-.192s si SET"
+ rus "Слишком много значений Ð´Ð»Ñ Ñтолбца %-.192s в SET"
+ serbian "Previše string-ova za kolonu '%-.192s' i komandu 'SET'"
+ slo "Príliš mnoho reťazcov pre pole %-.192s a SET"
+ spa "Muchas strings para columna %-.192s y SET"
+ swe "För många alternativ till kolumn %-.192s för SET"
+ ukr "Забагато Ñтрок Ð´Ð»Ñ ÑÑ‚Ð¾Ð²Ð±Ñ†Ñ %-.192s та SET"
+ER_NO_UNIQUE_LOGFILE
+ cze "Nemohu vytvo-BÅ™it jednoznaÄné jméno logovacího souboru %-.200s.(1-999)\n"
+ dan "Kan ikke lave unikt log-filnavn %-.200s.(1-999)\n"
+ nla "Het is niet mogelijk een unieke naam te maken voor de logfile %-.200s.(1-999)\n"
+ eng "Can't generate a unique log-filename %-.200s.(1-999)\n"
+ est "Ei suuda luua unikaalset logifaili nime %-.200s.(1-999)\n"
+ fre "Ne peut générer un unique nom de journal %-.200s.(1-999)\n"
+ ger "Kann keinen eindeutigen Dateinamen für die Logdatei %-.200s(1-999) erzeugen\n"
+ greek "ΑδÏνατη η δημιουÏγία unique log-filename %-.200s.(1-999)\n"
+ hun "Egyedi log-filenev nem generalhato: %-.200s.(1-999)\n"
+ ita "Impossibile generare un nome del file log unico %-.200s.(1-999)\n"
+ kor "Unique ë¡œê·¸í™”ì¼ '%-.200s'를 만들수 없습니다.(1-999)\n"
+ nor "Kan ikke lage unikt loggfilnavn %-.200s.(1-999)\n"
+ norwegian-ny "Kan ikkje lage unikt loggfilnavn %-.200s.(1-999)\n"
+ pol "Nie można stworzyć unikalnej nazwy pliku z logiem %-.200s.(1-999)\n"
+ por "Não pode gerar um nome de arquivo de 'log' único '%-.200s'.(1-999)\n"
+ rum "Nu pot sa generez un nume de log unic %-.200s.(1-999)\n"
+ rus "Ðевозможно Ñоздать уникальное Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° журнала %-.200s.(1-999)\n"
+ serbian "Ne mogu da generišem jedinstveno ime log-file-a: '%-.200s.(1-999)'\n"
+ slo "Nemôžem vytvoriť unikátne meno log-súboru %-.200s.(1-999)\n"
+ spa "No puede crear un unico archivo log %-.200s.(1-999)\n"
+ swe "Kan inte generera ett unikt filnamn %-.200s.(1-999)\n"
+ ukr "Ðе можу згенерувати унікальне ім'Ñ log-файлу %-.200s.(1-999)\n"
+ER_TABLE_NOT_LOCKED_FOR_WRITE
+ cze "Tabulka '%-.192s' byla zam-BÄena s READ a nemůže být zmÄ›nÄ›na"
+ dan "Tabellen '%-.192s' var låst med READ lås og kan ikke opdateres"
+ nla "Tabel '%-.192s' was gelocked met een lock om te lezen. Derhalve kunnen geen wijzigingen worden opgeslagen."
+ eng "Table '%-.192s' was locked with a READ lock and can't be updated"
+ jps "Table '%-.192s' 㯠READ lock ã«ãªã£ã¦ã„ã¦ã€æ›´æ–°ã¯ã§ãã¾ã›ã‚“",
+ est "Tabel '%-.192s' on lukustatud READ lukuga ning ei ole muudetav"
+ fre "Table '%-.192s' verrouillée lecture (READ): modification impossible"
+ ger "Tabelle '%-.192s' ist mit Lesesperre versehen und kann nicht aktualisiert werden"
+ greek "Ο πίνακας '%-.192s' έχει κλειδωθεί με READ lock και δεν επιτÏέπονται αλλαγές"
+ hun "A(z) '%-.192s' tabla zarolva lett (READ lock) es nem lehet frissiteni"
+ ita "La tabella '%-.192s' e` soggetta a lock in lettura e non puo` essere aggiornata"
+ jpn "Table '%-.192s' 㯠READ lock ã«ãªã£ã¦ã„ã¦ã€æ›´æ–°ã¯ã§ãã¾ã›ã‚“"
+ kor "í…Œì´ë¸” '%-.192s'는 READ ë½ì´ 잠겨있어서 갱신할 수 없습니다."
+ nor "Tabellen '%-.192s' var låst med READ lås og kan ikke oppdateres"
+ norwegian-ny "Tabellen '%-.192s' var låst med READ lås og kan ikkje oppdaterast"
+ pol "Tabela '%-.192s' została zablokowana przez READ i nie może zostać zaktualizowana"
+ por "Tabela '%-.192s' foi travada com trava de leitura e não pode ser atualizada"
+ rum "Tabela '%-.192s' a fost locked cu un READ lock si nu poate fi actualizata"
+ rus "Таблица '%-.192s' заблокирована уровнем READ lock и не может быть изменена"
+ serbian "Tabela '%-.192s' je zakljuÄana READ lock-om; iz nje se može samo Äitati ali u nju se ne može pisati"
+ slo "Tabuľka '%-.192s' bola zamknutá s READ a nemôže byť zmenená"
+ spa "Tabla '%-.192s' fue trabada con un READ lock y no puede ser actualizada"
+ swe "Tabell '%-.192s' kan inte uppdateras emedan den är låst för läsning"
+ ukr "Таблицю '%-.192s' заблоковано тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ, тому Ñ—Ñ— не можна оновити"
+ER_TABLE_NOT_LOCKED
+ cze "Tabulka '%-.192s' nebyla zam-BÄena s LOCK TABLES"
+ dan "Tabellen '%-.192s' var ikke låst med LOCK TABLES"
+ nla "Tabel '%-.192s' was niet gelocked met LOCK TABLES"
+ eng "Table '%-.192s' was not locked with LOCK TABLES"
+ jps "Table '%-.192s' 㯠LOCK TABLES ã«ã‚ˆã£ã¦ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã›ã‚“",
+ est "Tabel '%-.192s' ei ole lukustatud käsuga LOCK TABLES"
+ fre "Table '%-.192s' non verrouillée: utilisez LOCK TABLES"
+ ger "Tabelle '%-.192s' wurde nicht mit LOCK TABLES gesperrt"
+ greek "Ο πίνακας '%-.192s' δεν έχει κλειδωθεί με LOCK TABLES"
+ hun "A(z) '%-.192s' tabla nincs zarolva a LOCK TABLES-szel"
+ ita "Non e` stato impostato il lock per la tabella '%-.192s' con LOCK TABLES"
+ jpn "Table '%-.192s' 㯠LOCK TABLES ã«ã‚ˆã£ã¦ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+ kor "í…Œì´ë¸” '%-.192s'는 LOCK TABLES 명령으로 잠기지 않았습니다."
+ nor "Tabellen '%-.192s' var ikke låst med LOCK TABLES"
+ norwegian-ny "Tabellen '%-.192s' var ikkje låst med LOCK TABLES"
+ pol "Tabela '%-.192s' nie została zablokowana poleceniem LOCK TABLES"
+ por "Tabela '%-.192s' não foi travada com LOCK TABLES"
+ rum "Tabela '%-.192s' nu a fost locked cu LOCK TABLES"
+ rus "Таблица '%-.192s' не была заблокирована Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ LOCK TABLES"
+ serbian "Tabela '%-.192s' nije bila zakljuÄana komandom 'LOCK TABLES'"
+ slo "Tabuľka '%-.192s' nebola zamknutá s LOCK TABLES"
+ spa "Tabla '%-.192s' no fue trabada con LOCK TABLES"
+ swe "Tabell '%-.192s' är inte låst med LOCK TABLES"
+ ukr "Таблицю '%-.192s' не було блоковано з LOCK TABLES"
+ER_BLOB_CANT_HAVE_DEFAULT 42000
+ cze "Blob polo-Bžka '%-.192s' nemůže mít defaultní hodnotu"
+ dan "BLOB feltet '%-.192s' kan ikke have en standard værdi"
+ nla "Blob veld '%-.192s' can geen standaardwaarde bevatten"
+ eng "BLOB/TEXT column '%-.192s' can't have a default value"
+ est "BLOB-tüüpi tulp '%-.192s' ei saa omada vaikeväärtust"
+ fre "BLOB '%-.192s' ne peut avoir de valeur par défaut"
+ ger "BLOB/TEXT-Feld '%-.192s' darf keinen Vorgabewert (DEFAULT) haben"
+ greek "Τα Blob πεδία '%-.192s' δεν μποÏοÏν να έχουν Ï€ÏοκαθοÏισμένες τιμές (default value)"
+ hun "A(z) '%-.192s' blob objektumnak nem lehet alapertelmezett erteke"
+ ita "Il campo BLOB '%-.192s' non puo` avere un valore di default"
+ jpn "BLOB column '%-.192s' can't have a default value"
+ kor "BLOB 칼럼 '%-.192s' 는 ë””í´íŠ¸ ê°’ì„ ê°€ì§ˆ 수 없습니다."
+ nor "Blob feltet '%-.192s' kan ikke ha en standard verdi"
+ norwegian-ny "Blob feltet '%-.192s' kan ikkje ha ein standard verdi"
+ pol "Pole typu blob '%-.192s' nie może mieć domy?lnej warto?ci"
+ por "Coluna BLOB '%-.192s' não pode ter um valor padrão (default)"
+ rum "Coloana BLOB '%-.192s' nu poate avea o valoare default"
+ rus "Ðевозможно указывать значение по умолчанию Ð´Ð»Ñ Ñтолбца BLOB '%-.192s'"
+ serbian "BLOB kolona '%-.192s' ne može imati default vrednost"
+ slo "Pole BLOB '%-.192s' nemôže mať implicitnú hodnotu"
+ spa "Campo Blob '%-.192s' no puede tener valores patron"
+ swe "BLOB fält '%-.192s' kan inte ha ett DEFAULT-värde"
+ ukr "Стовбець BLOB '%-.192s' не може мати Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð¿Ð¾ замовчуванню"
+ER_WRONG_DB_NAME 42000
+ cze "Nep-Břípustné jméno databáze '%-.100s'"
+ dan "Ugyldigt database navn '%-.100s'"
+ nla "Databasenaam '%-.100s' is niet getoegestaan"
+ eng "Incorrect database name '%-.100s'"
+ jps "指定ã—㟠database å '%-.100s' ãŒé–“é•ã£ã¦ã„ã¾ã™",
+ est "Vigane andmebaasi nimi '%-.100s'"
+ fre "Nom de base de donnée illégal: '%-.100s'"
+ ger "Unerlaubter Datenbankname '%-.100s'"
+ greek "Λάθος όνομα βάσης δεδομένων '%-.100s'"
+ hun "Hibas adatbazisnev: '%-.100s'"
+ ita "Nome database errato '%-.100s'"
+ jpn "指定ã—㟠database å '%-.100s' ãŒé–“é•ã£ã¦ã„ã¾ã™"
+ kor "'%-.100s' ë°ì´íƒ€ë² ì´ìŠ¤ì˜ ì´ë¦„ì´ ë¶€ì •í™•í•©ë‹ˆë‹¤."
+ nor "Ugyldig database navn '%-.100s'"
+ norwegian-ny "Ugyldig database namn '%-.100s'"
+ pol "Niedozwolona nazwa bazy danych '%-.100s'"
+ por "Nome de banco de dados '%-.100s' incorreto"
+ rum "Numele bazei de date este incorect '%-.100s'"
+ rus "Ðекорректное Ð¸Ð¼Ñ Ð±Ð°Ð·Ñ‹ данных '%-.100s'"
+ serbian "Pogrešno ime baze '%-.100s'"
+ slo "Neprípustné meno databázy '%-.100s'"
+ spa "Nombre de base de datos ilegal '%-.100s'"
+ swe "Felaktigt databasnamn '%-.100s'"
+ ukr "Ðевірне ім'Ñ Ð±Ð°Ð·Ð¸ данних '%-.100s'"
+ER_WRONG_TABLE_NAME 42000
+ cze "Nep-Břípustné jméno tabulky '%-.100s'"
+ dan "Ugyldigt tabel navn '%-.100s'"
+ nla "Niet toegestane tabelnaam '%-.100s'"
+ eng "Incorrect table name '%-.100s'"
+ jps "指定ã—㟠table å '%-.100s' ã¯ã¾ã¡ãŒã£ã¦ã„ã¾ã™",
+ est "Vigane tabeli nimi '%-.100s'"
+ fre "Nom de table illégal: '%-.100s'"
+ ger "Unerlaubter Tabellenname '%-.100s'"
+ greek "Λάθος όνομα πίνακα '%-.100s'"
+ hun "Hibas tablanev: '%-.100s'"
+ ita "Nome tabella errato '%-.100s'"
+ jpn "指定ã—㟠table å '%-.100s' ã¯ã¾ã¡ãŒã£ã¦ã„ã¾ã™"
+ kor "'%-.100s' í…Œì´ë¸” ì´ë¦„ì´ ë¶€ì •í™•í•©ë‹ˆë‹¤."
+ nor "Ugyldig tabell navn '%-.100s'"
+ norwegian-ny "Ugyldig tabell namn '%-.100s'"
+ pol "Niedozwolona nazwa tabeli '%-.100s'..."
+ por "Nome de tabela '%-.100s' incorreto"
+ rum "Numele tabelei este incorect '%-.100s'"
+ rus "Ðекорректное Ð¸Ð¼Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ '%-.100s'"
+ serbian "Pogrešno ime tabele '%-.100s'"
+ slo "Neprípustné meno tabuľky '%-.100s'"
+ spa "Nombre de tabla ilegal '%-.100s'"
+ swe "Felaktigt tabellnamn '%-.100s'"
+ ukr "Ðевірне ім'Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ– '%-.100s'"
+ER_TOO_BIG_SELECT 42000
+ cze "Zadan-Bý SELECT by procházel příliš mnoho záznamů a trval velmi dlouho. Zkontrolujte tvar WHERE a je-li SELECT v pořádku, použijte SET SQL_BIG_SELECTS=1"
+ dan "SELECT ville undersøge for mange poster og ville sandsynligvis tage meget lang tid. Undersøg WHERE delen og brug SET SQL_BIG_SELECTS=1 hvis udtrykket er korrekt"
+ nla "Het SELECT-statement zou te veel records analyseren en dus veel tijd in beslagnemen. Kijk het WHERE-gedeelte van de query na en kies SET SQL_BIG_SELECTS=1 als het stament in orde is."
+ eng "The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET MAX_JOIN_SIZE=# if the SELECT is okay"
+ est "SELECT lause peab läbi vaatama suure hulga kirjeid ja võtaks tõenäoliselt liiga kaua aega. Tasub kontrollida WHERE klauslit ja vajadusel kasutada käsku SET SQL_BIG_SELECTS=1"
+ fre "SELECT va devoir examiner beaucoup d'enregistrements ce qui va prendre du temps. Vérifiez la clause WHERE et utilisez SET SQL_BIG_SELECTS=1 si SELECT se passe bien"
+ ger "Die Ausführung des SELECT würde zu viele Datensätze untersuchen und wahrscheinlich sehr lange dauern. Bitte WHERE-Klausel überprüfen und gegebenenfalls SET SQL_BIG_SELECTS=1 oder SET MAX_JOIN_SIZE=# verwenden"
+ greek "Το SELECT θα εξετάσει μεγάλο αÏιθμό εγγÏαφών και πιθανώς θα καθυστεÏήσει. ΠαÏακαλώ εξετάστε τις παÏαμέτÏους του WHERE και χÏησιμοποιείστε SET SQL_BIG_SELECTS=1 αν το SELECT είναι σωστό"
+ hun "A SELECT tul sok rekordot fog megvizsgalni es nagyon sokaig fog tartani. Ellenorizze a WHERE-t es hasznalja a SET SQL_BIG_SELECTS=1 beallitast, ha a SELECT okay"
+ ita "La SELECT dovrebbe esaminare troppi record e usare troppo tempo. Controllare la WHERE e usa SET SQL_BIG_SELECTS=1 se e` tutto a posto."
+ kor "SELECT 명령ì—ì„œ 너무 ë§Žì€ ë ˆì½”ë“œë¥¼ 찾기 ë•Œë¬¸ì— ë§Žì€ ì‹œê°„ì´ ì†Œìš”ë©ë‹ˆë‹¤. ë”°ë¼ì„œ WHERE ë¬¸ì„ ì ê²€í•˜ê±°ë‚˜, 만약 SELECTê°€ okë˜ë©´ SET SQL_BIG_SELECTS=1 ì˜µì…˜ì„ ì‚¬ìš©í•˜ì„¸ìš”."
+ nor "SELECT ville undersøke for mange poster og ville sannsynligvis ta veldig lang tid. Undersøk WHERE klausulen og bruk SET SQL_BIG_SELECTS=1 om SELECTen er korrekt"
+ norwegian-ny "SELECT ville undersøkje for mange postar og ville sannsynligvis ta veldig lang tid. Undersøk WHERE klausulen og bruk SET SQL_BIG_SELECTS=1 om SELECTen er korrekt"
+ pol "Operacja SELECT będzie dotyczyła zbyt wielu rekordów i prawdopodobnie zajmie bardzo dużo czasu. SprawdĽ warunek WHERE i użyj SQL_OPTION BIG_SELECTS=1 je?li operacja SELECT jest poprawna"
+ por "O SELECT examinaria registros demais e provavelmente levaria muito tempo. Cheque sua cláusula WHERE e use SET SQL_BIG_SELECTS=1, se o SELECT estiver correto"
+ rum "SELECT-ul ar examina prea multe cimpuri si probabil ar lua prea mult timp; verifica clauza WHERE si foloseste SET SQL_BIG_SELECTS=1 daca SELECT-ul e okay"
+ rus "Ð”Ð»Ñ Ñ‚Ð°ÐºÐ¾Ð¹ выборки SELECT должен будет проÑмотреть Ñлишком много запиÑей и, видимо, Ñто займет очень много времени. Проверьте ваше указание WHERE, и, еÑли в нем вÑе в порÑдке, укажите SET SQL_BIG_SELECTS=1"
+ serbian "Komanda 'SELECT' će ispitati previše slogova i potrošiti previše vremena. Proverite vaš 'WHERE' filter i upotrebite 'SET OPTION SQL_BIG_SELECTS=1' ako želite baš ovakvu komandu"
+ slo "Zadaná požiadavka SELECT by prechádzala príliš mnoho záznamov a trvala by príliš dlho. Skontrolujte tvar WHERE a ak je v poriadku, použite SET SQL_BIG_SELECTS=1"
+ spa "El SELECT puede examinar muchos registros y probablemente con mucho tiempo. Verifique tu WHERE y usa SET SQL_BIG_SELECTS=1 si el SELECT esta correcto"
+ swe "Den angivna frågan skulle läsa mer än MAX_JOIN_SIZE rader. Kontrollera din WHERE och använd SET SQL_BIG_SELECTS=1 eller SET MAX_JOIN_SIZE=# ifall du vill hantera stora joins"
+ ukr "Запиту SELECT потрібно обробити багато запиÑів, що, певне, займе дуже багато чаÑу. Перевірте ваше WHERE та викориÑтовуйте SET SQL_BIG_SELECTS=1, Ñкщо цей запит SELECT Ñ” вірним"
+ER_UNKNOWN_ERROR
+ cze "Nezn-Bámá chyba"
+ dan "Ukendt fejl"
+ nla "Onbekende Fout"
+ eng "Unknown error"
+ est "Tundmatu viga"
+ fre "Erreur inconnue"
+ ger "Unbekannter Fehler"
+ greek "ΠÏοέκυψε άγνωστο λάθος"
+ hun "Ismeretlen hiba"
+ ita "Errore sconosciuto"
+ kor "알수 없는 ì—러입니다."
+ nor "Ukjent feil"
+ norwegian-ny "Ukjend feil"
+ por "Erro desconhecido"
+ rum "Eroare unknown"
+ rus "ÐеизвеÑÑ‚Ð½Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°"
+ serbian "Nepoznata greška"
+ slo "Neznámá chyba"
+ spa "Error desconocido"
+ swe "Oidentifierat fel"
+ ukr "Ðевідома помилка"
+ER_UNKNOWN_PROCEDURE 42000
+ cze "Nezn-Bámá procedura %-.192s"
+ dan "Ukendt procedure %-.192s"
+ nla "Onbekende procedure %-.192s"
+ eng "Unknown procedure '%-.192s'"
+ est "Tundmatu protseduur '%-.192s'"
+ fre "Procédure %-.192s inconnue"
+ ger "Unbekannte Prozedur '%-.192s'"
+ greek "Αγνωστη διαδικασία '%-.192s'"
+ hun "Ismeretlen eljaras: '%-.192s'"
+ ita "Procedura '%-.192s' sconosciuta"
+ kor "알수 없는 수행문 : '%-.192s'"
+ nor "Ukjent prosedyre %-.192s"
+ norwegian-ny "Ukjend prosedyre %-.192s"
+ pol "Unkown procedure %-.192s"
+ por "'Procedure' '%-.192s' desconhecida"
+ rum "Procedura unknown '%-.192s'"
+ rus "ÐеизвеÑÑ‚Ð½Ð°Ñ Ð¿Ñ€Ð¾Ñ†ÐµÐ´ÑƒÑ€Ð° '%-.192s'"
+ serbian "Nepoznata procedura '%-.192s'"
+ slo "Neznámá procedúra '%-.192s'"
+ spa "Procedimiento desconocido %-.192s"
+ swe "Okänd procedur: %-.192s"
+ ukr "Ðевідома процедура '%-.192s'"
+ER_WRONG_PARAMCOUNT_TO_PROCEDURE 42000
+ cze "Chybn-Bý poÄet parametrů procedury %-.192s"
+ dan "Forkert antal parametre til proceduren %-.192s"
+ nla "Foutief aantal parameters doorgegeven aan procedure %-.192s"
+ eng "Incorrect parameter count to procedure '%-.192s'"
+ est "Vale parameetrite hulk protseduurile '%-.192s'"
+ fre "Mauvais nombre de paramètres pour la procedure %-.192s"
+ ger "Falsche Parameterzahl für Prozedur '%-.192s'"
+ greek "Λάθος αÏιθμός παÏαμέτÏων στη διαδικασία '%-.192s'"
+ hun "Rossz parameter a(z) '%-.192s'eljaras szamitasanal"
+ ita "Numero di parametri errato per la procedura '%-.192s'"
+ kor "'%-.192s' ìˆ˜í–‰ë¬¸ì— ëŒ€í•œ 부정확한 파ë¼ë©”í„°"
+ nor "Feil parameter antall til prosedyren %-.192s"
+ norwegian-ny "Feil parameter tal til prosedyra %-.192s"
+ pol "Incorrect parameter count to procedure %-.192s"
+ por "Número de parâmetros incorreto para a 'procedure' '%-.192s'"
+ rum "Procedura '%-.192s' are un numar incorect de parametri"
+ rus "Ðекорректное количеÑтво параметров Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñ†ÐµÐ´ÑƒÑ€Ñ‹ '%-.192s'"
+ serbian "Pogrešan broj parametara za proceduru '%-.192s'"
+ slo "Chybný poÄet parametrov procedúry '%-.192s'"
+ spa "Equivocado parametro count para procedimiento %-.192s"
+ swe "Felaktigt antal parametrar till procedur %-.192s"
+ ukr "Хибна кількіÑÑ‚ÑŒ параметрів процедури '%-.192s'"
+ER_WRONG_PARAMETERS_TO_PROCEDURE
+ cze "Chybn-Bé parametry procedury %-.192s"
+ dan "Forkert(e) parametre til proceduren %-.192s"
+ nla "Foutieve parameters voor procedure %-.192s"
+ eng "Incorrect parameters to procedure '%-.192s'"
+ est "Vigased parameetrid protseduurile '%-.192s'"
+ fre "Paramètre erroné pour la procedure %-.192s"
+ ger "Falsche Parameter für Prozedur '%-.192s'"
+ greek "Λάθος παÏάμετÏοι στην διαδικασία '%-.192s'"
+ hun "Rossz parameter a(z) '%-.192s' eljarasban"
+ ita "Parametri errati per la procedura '%-.192s'"
+ kor "'%-.192s' ìˆ˜í–‰ë¬¸ì— ëŒ€í•œ 부정확한 파ë¼ë©”í„°"
+ nor "Feil parametre til prosedyren %-.192s"
+ norwegian-ny "Feil parameter til prosedyra %-.192s"
+ pol "Incorrect parameters to procedure %-.192s"
+ por "Parâmetros incorretos para a 'procedure' '%-.192s'"
+ rum "Procedura '%-.192s' are parametrii incorecti"
+ rus "Ðекорректные параметры Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñ†ÐµÐ´ÑƒÑ€Ñ‹ '%-.192s'"
+ serbian "Pogrešni parametri prosleđeni proceduri '%-.192s'"
+ slo "Chybné parametre procedúry '%-.192s'"
+ spa "Equivocados parametros para procedimiento %-.192s"
+ swe "Felaktiga parametrar till procedur %-.192s"
+ ukr "Хибний параметер процедури '%-.192s'"
+ER_UNKNOWN_TABLE 42S02
+ cze "Nezn-Bámá tabulka '%-.192s' v %-.32s"
+ dan "Ukendt tabel '%-.192s' i %-.32s"
+ nla "Onbekende tabel '%-.192s' in %-.32s"
+ eng "Unknown table '%-.192s' in %-.32s"
+ est "Tundmatu tabel '%-.192s' %-.32s-s"
+ fre "Table inconnue '%-.192s' dans %-.32s"
+ ger "Unbekannte Tabelle '%-.192s' in '%-.32s'"
+ greek "Αγνωστος πίνακας '%-.192s' σε %-.32s"
+ hun "Ismeretlen tabla: '%-.192s' %-.32s-ban"
+ ita "Tabella '%-.192s' sconosciuta in %-.32s"
+ jpn "Unknown table '%-.192s' in %-.32s"
+ kor "알수 없는 í…Œì´ë¸” '%-.192s' (ë°ì´íƒ€ë² ì´ìŠ¤ %-.32s)"
+ nor "Ukjent tabell '%-.192s' i %-.32s"
+ norwegian-ny "Ukjend tabell '%-.192s' i %-.32s"
+ pol "Unknown table '%-.192s' in %-.32s"
+ por "Tabela '%-.192s' desconhecida em '%-.32s'"
+ rum "Tabla '%-.192s' invalida in %-.32s"
+ rus "ÐеизвеÑÑ‚Ð½Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð° '%-.192s' в %-.32s"
+ serbian "Nepoznata tabela '%-.192s' u '%-.32s'"
+ slo "Neznáma tabuľka '%-.192s' v %-.32s"
+ spa "Tabla desconocida '%-.192s' in %-.32s"
+ swe "Okänd tabell '%-.192s' i '%-.32s'"
+ ukr "Ðевідома Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ '%-.192s' у %-.32s"
+ER_FIELD_SPECIFIED_TWICE 42000
+ cze "Polo-Bžka '%-.192s' je zadána dvakrát"
+ dan "Feltet '%-.192s' er anvendt to gange"
+ nla "Veld '%-.192s' is dubbel gespecificeerd"
+ eng "Column '%-.192s' specified twice"
+ est "Tulp '%-.192s' on määratletud topelt"
+ fre "Champ '%-.192s' spécifié deux fois"
+ ger "Feld '%-.192s' wurde zweimal angegeben"
+ greek "Το πεδίο '%-.192s' έχει οÏισθεί δÏο φοÏές"
+ hun "A(z) '%-.192s' mezot ketszer definialta"
+ ita "Campo '%-.192s' specificato 2 volte"
+ kor "칼럼 '%-.192s'는 ë‘번 ì •ì˜ë˜ì–´ 있ì니다."
+ nor "Feltet '%-.192s' er spesifisert to ganger"
+ norwegian-ny "Feltet '%-.192s' er spesifisert to gangar"
+ pol "Field '%-.192s' specified twice"
+ por "Coluna '%-.192s' especificada duas vezes"
+ rum "Coloana '%-.192s' specificata de doua ori"
+ rus "Столбец '%-.192s' указан дважды"
+ serbian "Kolona '%-.192s' je navedena dva puta"
+ slo "Pole '%-.192s' je zadané dvakrát"
+ spa "Campo '%-.192s' especificado dos veces"
+ swe "Fält '%-.192s' är redan använt"
+ ukr "Стовбець '%-.192s' зазначено двічі"
+ER_INVALID_GROUP_FUNC_USE
+ cze "Nespr-Bávné použití funkce group"
+ dan "Forkert brug af grupperings-funktion"
+ nla "Ongeldig gebruik van GROUP-functie"
+ eng "Invalid use of group function"
+ est "Vigane grupeerimisfunktsiooni kasutus"
+ fre "Utilisation invalide de la clause GROUP"
+ ger "Falsche Verwendung einer Gruppierungsfunktion"
+ greek "Εσφαλμένη χÏήση της group function"
+ hun "A group funkcio ervenytelen hasznalata"
+ ita "Uso non valido di una funzione di raggruppamento"
+ kor "ìž˜ëª»ëœ ê·¸ë£¹ 함수를 사용하였습니다."
+ por "Uso inválido de função de agrupamento (GROUP)"
+ rum "Folosire incorecta a functiei group"
+ rus "Ðеправильное иÑпользование групповых функций"
+ serbian "Pogrešna upotreba 'GROUP' funkcije"
+ slo "Nesprávne použitie funkcie GROUP"
+ spa "Invalido uso de función en grupo"
+ swe "Felaktig användning av SQL grupp function"
+ ukr "Хибне викориÑÑ‚Ð°Ð½Ð½Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ— групуваннÑ"
+ER_UNSUPPORTED_EXTENSION 42000
+ cze "Tabulka '%-.192s' pou-Bžívá rozšíření, které v této verzi MariaDB není"
+ 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"
+ est "Tabel '%-.192s' kasutab laiendust, mis ei eksisteeri antud MariaDB versioonis"
+ fre "Table '%-.192s' : utilise une extension invalide pour cette version de MariaDB"
+ ger "Tabelle '%-.192s' verwendet eine Erweiterung, die in dieser MariaDB-Version nicht verfügbar ist"
+ greek "Ο πίνακς '%-.192s' χÏησιμοποιεί κάποιο extension που δεν υπάÏχει στην έκδοση αυτή της MariaDB"
+ hun "A(z) '%-.192s' tabla olyan bovitest hasznal, amely nem letezik ebben a MariaDB versioban."
+ ita "La tabella '%-.192s' usa un'estensione che non esiste in questa versione di MariaDB"
+ 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"
+ pol "Table '%-.192s' uses a extension that doesn't exist in this MariaDB version"
+ por "Tabela '%-.192s' usa uma extensão que não existe nesta versão do MariaDB"
+ rum "Tabela '%-.192s' foloseste o extensire inexistenta in versiunea curenta de MariaDB"
+ rus "Ð’ таблице '%-.192s' иÑпользуютÑÑ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾Ñти, не поддерживаемые в Ñтой верÑии MariaDB"
+ serbian "Tabela '%-.192s' koristi ekstenziju koje ne postoji u ovoj verziji MariaDB-a"
+ slo "Tabuľka '%-.192s' používa rozšírenie, ktoré v tejto verzii MariaDB nie je"
+ spa "Tabla '%-.192s' usa una extensión que no existe en esta MariaDB versión"
+ swe "Tabell '%-.192s' har en extension som inte finns i denna version av MariaDB"
+ ukr "Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ '%-.192s' викориÑтовує розширеннÑ, що не Ñ–Ñнує у цій верÑÑ–Ñ— MariaDB"
+ER_TABLE_MUST_HAVE_COLUMNS 42000
+ cze "Tabulka mus-Bí mít alespoň jeden sloupec"
+ dan "En tabel skal have mindst een kolonne"
+ nla "Een tabel moet minstens 1 kolom bevatten"
+ eng "A table must have at least 1 column"
+ jps "テーブルã¯æœ€ä½Ž 1 個㮠column ãŒå¿…è¦ã§ã™",
+ est "Tabelis peab olema vähemalt üks tulp"
+ fre "Une table doit comporter au moins une colonne"
+ ger "Eine Tabelle muss mindestens eine Spalte besitzen"
+ greek "Ενας πίνακας Ï€Ïέπει να έχει τουλάχιστον ένα πεδίο"
+ hun "A tablanak legalabb egy oszlopot tartalmazni kell"
+ ita "Una tabella deve avere almeno 1 colonna"
+ jpn "テーブルã¯æœ€ä½Ž 1 個㮠column ãŒå¿…è¦ã§ã™"
+ kor "í•˜ë‚˜ì˜ í…Œì´ë¸”ì—서는 ì ì–´ë„ í•˜ë‚˜ì˜ ì¹¼ëŸ¼ì´ ì¡´ìž¬í•˜ì—¬ì•¼ 합니다."
+ por "Uma tabela tem que ter pelo menos uma (1) coluna"
+ rum "O tabela trebuie sa aiba cel putin o coloana"
+ rus "Ð’ таблице должен быть как минимум один Ñтолбец"
+ serbian "Tabela mora imati najmanje jednu kolonu"
+ slo "Tabuľka musí mať aspoň 1 pole"
+ spa "Una tabla debe tener al menos 1 columna"
+ swe "Tabeller måste ha minst 1 kolumn"
+ ukr "Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ Ð¿Ð¾Ð²Ð¸Ð½Ð½Ð° мати хочаб один Ñтовбець"
+ER_RECORD_FILE_FULL
+ cze "Tabulka '%-.192s' je pln-Bá"
+ dan "Tabellen '%-.192s' er fuld"
+ nla "De tabel '%-.192s' is vol"
+ eng "The table '%-.192s' is full"
+ jps "table '%-.192s' ã¯ã„ã£ã±ã„ã§ã™",
+ est "Tabel '%-.192s' on täis"
+ fre "La table '%-.192s' est pleine"
+ ger "Tabelle '%-.192s' ist voll"
+ greek "Ο πίνακας '%-.192s' είναι γεμάτος"
+ hun "A '%-.192s' tabla megtelt"
+ ita "La tabella '%-.192s' e` piena"
+ jpn "table '%-.192s' ã¯ã„ã£ã±ã„ã§ã™"
+ kor "í…Œì´ë¸” '%-.192s'ê°€ full났습니다. "
+ por "Tabela '%-.192s' está cheia"
+ rum "Tabela '%-.192s' e plina"
+ rus "Таблица '%-.192s' переполнена"
+ serbian "Tabela '%-.192s' je popunjena do kraja"
+ slo "Tabuľka '%-.192s' je plná"
+ spa "La tabla '%-.192s' está llena"
+ swe "Tabellen '%-.192s' är full"
+ ukr "Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ '%-.192s' заповнена"
+ER_UNKNOWN_CHARACTER_SET 42000
+ cze "Nezn-Bámá znaková sada: '%-.64s'"
+ dan "Ukendt tegnsæt: '%-.64s'"
+ nla "Onbekende character set: '%-.64s'"
+ eng "Unknown character set: '%-.64s'"
+ jps "character set '%-.64s' ã¯ã‚µãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“",
+ est "Vigane kooditabel '%-.64s'"
+ fre "Jeu de caractères inconnu: '%-.64s'"
+ ger "Unbekannter Zeichensatz: '%-.64s'"
+ greek "Αγνωστο character set: '%-.64s'"
+ hun "Ervenytelen karakterkeszlet: '%-.64s'"
+ ita "Set di caratteri '%-.64s' sconosciuto"
+ jpn "character set '%-.64s' ã¯ã‚µãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“"
+ kor "알수없는 언어 Set: '%-.64s'"
+ por "Conjunto de caracteres '%-.64s' desconhecido"
+ rum "Set de caractere invalid: '%-.64s'"
+ rus "ÐеизвеÑÑ‚Ð½Ð°Ñ ÐºÐ¾Ð´Ð¸Ñ€Ð¾Ð²ÐºÐ° '%-.64s'"
+ serbian "Nepoznati karakter-set: '%-.64s'"
+ slo "Neznáma znaková sada: '%-.64s'"
+ spa "Juego de caracteres desconocido: '%-.64s'"
+ swe "Okänd teckenuppsättning: '%-.64s'"
+ ukr "Ðевідома кодова таблицÑ: '%-.64s'"
+ER_TOO_MANY_TABLES
+ cze "P-Bříliš mnoho tabulek, MariaDB jich může mít v joinu jen %d"
+ dan "For mange tabeller. MariaDB kan kun bruge %d tabeller i et join"
+ nla "Teveel tabellen. MariaDB kan slechts %d tabellen in een join bevatten"
+ eng "Too many tables; MariaDB can only use %d tables in a join"
+ jps "テーブルãŒå¤šã™ãŽã¾ã™; MariaDB can only use %d tables in a join",
+ est "Liiga palju tabeleid. MariaDB suudab JOINiga ühendada kuni %d tabelit"
+ fre "Trop de tables. MariaDB ne peut utiliser que %d tables dans un JOIN"
+ ger "Zu viele Tabellen. MariaDB kann in einem Join maximal %d Tabellen verwenden"
+ greek "Î Î¿Î»Ï Î¼ÎµÎ³Î¬Î»Î¿Ï‚ αÏιθμός πινάκων. Η MariaDB μποÏεί να χÏησιμοποιήσει %d πίνακες σε διαδικασία join"
+ hun "Tul sok tabla. A MariaDB csak %d tablat tud kezelni osszefuzeskor"
+ ita "Troppe tabelle. MariaDB puo` usare solo %d tabelle in una join"
+ jpn "テーブルãŒå¤šã™ãŽã¾ã™; MariaDB can only use %d tables in a join"
+ 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"
+ rus "Слишком много таблиц. MariaDB может иÑпользовать только %d таблиц в Ñоединении"
+ serbian "Previše tabela. MariaDB može upotrebiti maksimum %d tabela pri 'JOIN' operaciji"
+ slo "Príliš mnoho tabuliek. MariaDB môže použiť len %d v JOIN-e"
+ spa "Muchas tablas. MariaDB solamente puede usar %d tablas en un join"
+ swe "För många tabeller. MariaDB can ha högst %d tabeller i en och samma join"
+ ukr "Забагато таблиць. MariaDB може викориÑтовувати лише %d таблиць у об'єднанні"
+ER_TOO_MANY_FIELDS
+ cze "P-Bříliš mnoho položek"
+ dan "For mange felter"
+ nla "Te veel velden"
+ eng "Too many columns"
+ jps "column ãŒå¤šã™ãŽã¾ã™",
+ est "Liiga palju tulpasid"
+ fre "Trop de champs"
+ ger "Zu viele Felder"
+ greek "Î Î¿Î»Ï Î¼ÎµÎ³Î¬Î»Î¿Ï‚ αÏιθμός πεδίων"
+ hun "Tul sok mezo"
+ ita "Troppi campi"
+ jpn "column ãŒå¤šã™ãŽã¾ã™"
+ kor "ì¹¼ëŸ¼ì´ ë„ˆë¬´ 많습니다."
+ por "Colunas demais"
+ rum "Prea multe coloane"
+ rus "Слишком много Ñтолбцов"
+ serbian "Previše kolona"
+ slo "Príliš mnoho polí"
+ spa "Muchos campos"
+ swe "För många fält"
+ ukr "Забагато Ñтовбців"
+ER_TOO_BIG_ROWSIZE 42000
+ cze "-BŘádek je příliÅ¡ velký. Maximální velikost řádku, nepoÄítaje položky blob, je %ld. Musíte zmÄ›nit nÄ›které položky na blob"
+ dan "For store poster. Max post størrelse, uden BLOB's, er %ld. Du må lave nogle felter til BLOB's"
+ nla "Rij-grootte is groter dan toegestaan. Maximale rij grootte, blobs niet meegeteld, is %ld. U dient sommige velden in blobs te veranderen."
+ eng "Row size too large. The maximum row size for the used table type, not counting BLOBs, is %ld. You have to change some columns to TEXT or BLOBs"
+ jps "row size ãŒå¤§ãã™ãŽã¾ã™. BLOB ã‚’å«ã¾ãªã„å ´åˆã® row size ã®æœ€å¤§ã¯ %ld ã§ã™. ã„ãã¤ã‹ã® field ã‚’ BLOB ã«å¤‰ãˆã¦ãã ã•ã„.",
+ est "Liiga pikk kirje. Kirje maksimumpikkus arvestamata BLOB-tüüpi välju on %ld. Muuda mõned väljad BLOB-tüüpi väljadeks"
+ fre "Ligne trop grande. Le taille maximale d'une ligne, sauf les BLOBs, est %ld. Changez le type de quelques colonnes en BLOB"
+ ger "Zeilenlänge zu groß. Die maximale Zeilenlänge für den verwendeten Tabellentyp (ohne BLOB-Felder) beträgt %ld. Einige Felder müssen in BLOB oder TEXT umgewandelt werden"
+ greek "Î Î¿Î»Ï Î¼ÎµÎ³Î¬Î»Î¿ μέγεθος εγγÏαφής. Το μέγιστο μέγεθος εγγÏαφής, χωÏίς να υπολογίζονται τα blobs, είναι %ld. ΠÏέπει να οÏίσετε κάποια πεδία σαν blobs"
+ hun "Tul nagy sormeret. A maximalis sormeret (nem szamolva a blob objektumokat) %ld. Nehany mezot meg kell valtoztatnia"
+ ita "Riga troppo grande. La massima grandezza di una riga, non contando i BLOB, e` %ld. Devi cambiare alcuni campi in BLOB"
+ jpn "row size ãŒå¤§ãã™ãŽã¾ã™. BLOB ã‚’å«ã¾ãªã„å ´åˆã® row size ã®æœ€å¤§ã¯ %ld ã§ã™. ã„ãã¤ã‹ã® field ã‚’ BLOB ã«å¤‰ãˆã¦ãã ã•ã„."
+ kor "너무 í° row 사ì´ì¦ˆìž…니다. BLOB를 계산하지 ì•Šê³  최대 row 사ì´ì¦ˆëŠ” %ld입니다. ì–¼ë§ˆê°„ì˜ í•„ë“œë“¤ì„ BLOBë¡œ 바꾸셔야 ê² êµ°ìš”.."
+ por "Tamanho de linha grande demais. O máximo tamanho de linha, não contando BLOBs, é %ld. Você tem que mudar alguns campos para BLOBs"
+ rum "Marimea liniei (row) prea mare. Marimea maxima a liniei, excluzind BLOB-urile este de %ld. Trebuie sa schimbati unele cimpuri in BLOB-uri"
+ rus "Слишком большой размер запиÑи. МакÑимальный размер Ñтроки, иÑÐºÐ»ÑŽÑ‡Ð°Ñ Ð¿Ð¾Ð»Ñ BLOB, - %ld. Возможно, вам Ñледует изменить тип некоторых полей на BLOB"
+ serbian "Prevelik slog. Maksimalna veliÄina sloga, ne raÄunajući BLOB polja, je %ld. Trebali bi da promenite tip nekih polja u BLOB"
+ slo "Riadok je príliš veľký. Maximálna veľkosť riadku, okrem 'BLOB', je %ld. Musíte zmeniť niektoré položky na BLOB"
+ spa "Tamaño de línea muy grande. Máximo tamaño de línea, no contando blob, es %ld. Tu tienes que cambiar algunos campos para blob"
+ swe "För stor total radlängd. Den högst tillåtna radlängden, förutom BLOBs, är %ld. Ändra några av dina fält till BLOB"
+ ukr "Задовга Ñтрока. Ðайбільшою довжиною Ñтроки, не рахуючи BLOB, Ñ” %ld. Вам потрібно привеÑти деÑкі Ñтовбці до типу BLOB"
+ER_STACK_OVERRUN
+ cze "P-BÅ™eteÄení zásobníku threadu: použito %ld z %ld. Použijte 'mysqld --thread_stack=#' k zadání vÄ›tšího zásobníku"
+ dan "Thread stack brugt: Brugt: %ld af en %ld stak. Brug 'mysqld --thread_stack=#' for at allokere en større stak om nødvendigt"
+ nla "Thread stapel overrun: Gebruikte: %ld van een %ld stack. Gebruik 'mysqld --thread_stack=#' om een grotere stapel te definieren (indien noodzakelijk)."
+ eng "Thread stack overrun: Used: %ld of a %ld stack. Use 'mysqld --thread_stack=#' to specify a bigger stack if needed"
+ jps "Thread stack overrun: Used: %ld of a %ld stack. スタック領域を多ãã¨ã‚ŠãŸã„å ´åˆã€'mysqld --thread_stack=#' ã¨æŒ‡å®šã—ã¦ãã ã•ã„",
+ fre "Débordement de la pile des tâches (Thread stack). Utilisées: %ld pour une pile de %ld. Essayez 'mysqld --thread_stack=#' pour indiquer une plus grande valeur"
+ ger "Thread-Stack-Überlauf. Benutzt: %ld von %ld Stack. 'mysqld --thread_stack=#' verwenden, um bei Bedarf einen größeren Stack anzulegen"
+ greek "Stack overrun στο thread: Used: %ld of a %ld stack. ΠαÏακαλώ χÏησιμοποιείστε 'mysqld --thread_stack=#' για να οÏίσετε ένα μεγαλÏτεÏο stack αν χÏειάζεται"
+ hun "Thread verem tullepes: Used: %ld of a %ld stack. Hasznalja a 'mysqld --thread_stack=#' nagyobb verem definialasahoz"
+ ita "Thread stack overrun: Usati: %ld di uno stack di %ld. Usa 'mysqld --thread_stack=#' per specificare uno stack piu` grande."
+ jpn "Thread stack overrun: Used: %ld of a %ld stack. スタック領域を多ãã¨ã‚ŠãŸã„å ´åˆã€'mysqld --thread_stack=#' ã¨æŒ‡å®šã—ã¦ãã ã•ã„"
+ kor "쓰레드 스íƒì´ 넘쳤습니다. 사용: %ldê°œ 스íƒ: %ldê°œ. 만약 필요시 ë”í° ìŠ¤íƒì„ ì›í• ë•Œì—는 'mysqld --thread_stack=#' 를 ì •ì˜í•˜ì„¸ìš”"
+ por "Estouro da pilha do 'thread'. Usados %ld de uma pilha de %ld. Use 'mysqld --thread_stack=#' para especificar uma pilha maior, se necessário"
+ rum "Stack-ul thread-ului a fost depasit (prea mic): Folositi: %ld intr-un stack de %ld. Folositi 'mysqld --thread_stack=#' ca sa specifici un stack mai mare"
+ rus "Стек потоков переполнен: иÑпользовано: %ld из %ld Ñтека. ПрименÑйте 'mysqld --thread_stack=#' Ð´Ð»Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð±Ð¾Ð»ÑŒÑˆÐµÐ³Ð¾ размера Ñтека, еÑли необходимо"
+ serbian "Prepisivanje thread stack-a: Upotrebljeno: %ld od %ld stack memorije. Upotrebite 'mysqld --thread_stack=#' da navedete veći stack ako je potrebno"
+ slo "PreteÄenie zásobníku vlákna: použité: %ld z %ld. Použite 'mysqld --thread_stack=#' k zadaniu väÄÅ¡ieho zásobníka"
+ spa "Sobrecarga de la pila de thread: Usada: %ld de una %ld pila. Use 'mysqld --thread_stack=#' para especificar una mayor pila si necesario"
+ swe "Trådstacken tog slut: Har använt %ld av %ld bytes. Använd 'mysqld --thread_stack=#' ifall du behöver en större stack"
+ ukr "Стек гілок переповнено: ВикориÑтано: %ld з %ld. ВикориÑтовуйте 'mysqld --thread_stack=#' аби зазначити більший Ñтек, Ñкщо необхідно"
+ER_WRONG_OUTER_JOIN 42000
+ cze "V OUTER JOIN byl nalezen k-Břížový odkaz. Prověřte ON podmínky"
+ dan "Krydsreferencer fundet i OUTER JOIN; check dine ON conditions"
+ nla "Gekruiste afhankelijkheid gevonden in OUTER JOIN. Controleer uw ON-conditions"
+ eng "Cross dependency found in OUTER JOIN; examine your ON conditions"
+ est "Ristsõltuvus OUTER JOIN klauslis. Kontrolli oma ON tingimusi"
+ fre "Dépendance croisée dans une clause OUTER JOIN. Vérifiez la condition ON"
+ ger "OUTER JOIN enthält fehlerhafte Abhängigkeiten. In ON verwendete Bedingungen überprüfen"
+ greek "Cross dependency βÏέθηκε σε OUTER JOIN. ΠαÏακαλώ εξετάστε τις συνθήκες που θέσατε στο ON"
+ hun "Keresztfuggoseg van az OUTER JOIN-ban. Ellenorizze az ON felteteleket"
+ ita "Trovata una dipendenza incrociata nella OUTER JOIN. Controlla le condizioni ON"
+ por "Dependência cruzada encontrada em junção externa (OUTER JOIN); examine as condições utilizadas nas cláusulas 'ON'"
+ rum "Dependinta incrucisata (cross dependency) gasita in OUTER JOIN. Examinati conditiile ON"
+ rus "Ð’ OUTER JOIN обнаружена перекреÑÑ‚Ð½Ð°Ñ Ð·Ð°Ð²Ð¸ÑимоÑÑ‚ÑŒ. Внимательно проанализируйте Ñвои уÑÐ»Ð¾Ð²Ð¸Ñ ON"
+ serbian "Unakrsna zavisnost pronađena u komandi 'OUTER JOIN'. Istražite vaše 'ON' uslove"
+ slo "V OUTER JOIN bol nájdený krížový odkaz. Skontrolujte podmienky ON"
+ spa "Dependencia cruzada encontrada en OUTER JOIN. Examine su condición ON"
+ swe "Felaktigt referens i OUTER JOIN. Kontrollera ON-uttrycket"
+ ukr "ПерехреÑна залежніÑÑ‚ÑŒ у OUTER JOIN. Перевірте умову ON"
+ER_NULL_COLUMN_IN_INDEX 42000
+ eng "Table handler doesn't support NULL in given index. Please change column '%-.192s' to be NOT NULL or use another handler"
+ swe "Tabell hanteraren kan inte indexera NULL kolumner för den givna index typen. Ändra '%-.192s' till NOT NULL eller använd en annan hanterare"
+ER_CANT_FIND_UDF
+ cze "Nemohu na-BÄíst funkci '%-.192s'"
+ dan "Kan ikke læse funktionen '%-.192s'"
+ nla "Kan functie '%-.192s' niet laden"
+ eng "Can't load function '%-.192s'"
+ jps "function '%-.192s' ã‚’ ロードã§ãã¾ã›ã‚“",
+ est "Ei suuda avada funktsiooni '%-.192s'"
+ fre "Imposible de charger la fonction '%-.192s'"
+ ger "Kann Funktion '%-.192s' nicht laden"
+ greek "Δεν είναι δυνατή η διαδικασία load για τη συνάÏτηση '%-.192s'"
+ hun "A(z) '%-.192s' fuggveny nem toltheto be"
+ ita "Impossibile caricare la funzione '%-.192s'"
+ jpn "function '%-.192s' ã‚’ ロードã§ãã¾ã›ã‚“"
+ kor "'%-.192s' 함수를 로드하지 못했습니다."
+ por "Não pode carregar a função '%-.192s'"
+ rum "Nu pot incarca functia '%-.192s'"
+ rus "Ðевозможно загрузить функцию '%-.192s'"
+ serbian "Ne mogu da uÄitam funkciju '%-.192s'"
+ slo "Nemôžem naÄítaÅ¥ funkciu '%-.192s'"
+ spa "No puedo cargar función '%-.192s'"
+ swe "Kan inte ladda funktionen '%-.192s'"
+ ukr "Ðе можу завантажити функцію '%-.192s'"
+ER_CANT_INITIALIZE_UDF
+ cze "Nemohu inicializovat funkci '%-.192s'; %-.80s"
+ dan "Kan ikke starte funktionen '%-.192s'; %-.80s"
+ nla "Kan functie '%-.192s' niet initialiseren; %-.80s"
+ eng "Can't initialize function '%-.192s'; %-.80s"
+ jps "function '%-.192s' ã‚’åˆæœŸåŒ–ã§ãã¾ã›ã‚“; %-.80s",
+ est "Ei suuda algväärtustada funktsiooni '%-.192s'; %-.80s"
+ fre "Impossible d'initialiser la fonction '%-.192s'; %-.80s"
+ ger "Kann Funktion '%-.192s' nicht initialisieren: %-.80s"
+ greek "Δεν είναι δυνατή η έναÏξη της συνάÏτησης '%-.192s'; %-.80s"
+ hun "A(z) '%-.192s' fuggveny nem inicializalhato; %-.80s"
+ ita "Impossibile inizializzare la funzione '%-.192s'; %-.80s"
+ jpn "function '%-.192s' ã‚’åˆæœŸåŒ–ã§ãã¾ã›ã‚“; %-.80s"
+ kor "'%-.192s' 함수를 초기화 하지 못했습니다.; %-.80s"
+ por "Não pode inicializar a função '%-.192s' - '%-.80s'"
+ rum "Nu pot initializa functia '%-.192s'; %-.80s"
+ rus "Ðевозможно инициализировать функцию '%-.192s'; %-.80s"
+ serbian "Ne mogu da inicijalizujem funkciju '%-.192s'; %-.80s"
+ slo "Nemôžem inicializovať funkciu '%-.192s'; %-.80s"
+ spa "No puedo inicializar función '%-.192s'; %-.80s"
+ swe "Kan inte initialisera funktionen '%-.192s'; '%-.80s'"
+ ukr "Ðе можу ініціалізувати функцію '%-.192s'; %-.80s"
+ER_UDF_NO_PATHS
+ cze "Pro sd-Bílenou knihovnu nejsou povoleny cesty"
+ dan "Angivelse af sti ikke tilladt for delt bibliotek"
+ nla "Geen pad toegestaan voor shared library"
+ eng "No paths allowed for shared library"
+ jps "shared library ã¸ã®ãƒ‘スãŒé€šã£ã¦ã„ã¾ã›ã‚“",
+ est "Teegi nimes ei tohi olla kataloogi"
+ fre "Chemin interdit pour les bibliothèques partagées"
+ ger "Keine Pfade gestattet für Shared Library"
+ greek "Δεν βÏέθηκαν paths για την shared library"
+ hun "Nincs ut a megosztott konyvtarakhoz (shared library)"
+ ita "Non sono ammessi path per le librerie condivisa"
+ jpn "shared library ã¸ã®ãƒ‘スãŒé€šã£ã¦ã„ã¾ã›ã‚“"
+ kor "공유 ë¼ì´ë²„러리를 위한 패스가 ì •ì˜ë˜ì–´ 있지 않습니다."
+ por "Não há caminhos (paths) permitidos para biblioteca compartilhada"
+ rum "Nici un paths nu e permis pentru o librarie shared"
+ rus "ÐедопуÑтимо указывать пути Ð´Ð»Ñ Ð´Ð¸Ð½Ð°Ð¼Ð¸Ñ‡ÐµÑких библиотек"
+ serbian "Ne postoje dozvoljene putanje do share-ovane biblioteke"
+ slo "Neprípustné žiadne cesty k zdieľanej knižnici"
+ spa "No pasos permitidos para librarias conjugadas"
+ swe "Man får inte ange sökväg för dynamiska bibliotek"
+ ukr "Ðе дозволено викориÑтовувати путі Ð´Ð»Ñ Ñ€Ð¾Ð·Ð´Ñ–Ð»ÑŽÐ²Ð°Ð½Ð¸Ñ… бібліотек"
+ER_UDF_EXISTS
+ cze "Funkce '%-.192s' ji-Bž existuje"
+ dan "Funktionen '%-.192s' findes allerede"
+ nla "Functie '%-.192s' bestaat reeds"
+ eng "Function '%-.192s' already exists"
+ jps "Function '%-.192s' ã¯æ—¢ã«å®šç¾©ã•ã‚Œã¦ã„ã¾ã™",
+ est "Funktsioon '%-.192s' juba eksisteerib"
+ fre "La fonction '%-.192s' existe déjà"
+ ger "Funktion '%-.192s' existiert schon"
+ greek "Η συνάÏτηση '%-.192s' υπάÏχει ήδη"
+ hun "A '%-.192s' fuggveny mar letezik"
+ ita "La funzione '%-.192s' esiste gia`"
+ jpn "Function '%-.192s' ã¯æ—¢ã«å®šç¾©ã•ã‚Œã¦ã„ã¾ã™"
+ kor "'%-.192s' 함수는 ì´ë¯¸ 존재합니다."
+ por "Função '%-.192s' já existe"
+ rum "Functia '%-.192s' exista deja"
+ rus "Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ '%-.192s' уже ÑущеÑтвует"
+ serbian "Funkcija '%-.192s' već postoji"
+ slo "Funkcia '%-.192s' už existuje"
+ spa "Función '%-.192s' ya existe"
+ swe "Funktionen '%-.192s' finns redan"
+ ukr "Ð¤ÑƒÐ½ÐºÑ†Ñ–Ñ '%-.192s' вже Ñ–Ñнує"
+ER_CANT_OPEN_LIBRARY
+ cze "Nemohu otev-Břít sdílenou knihovnu '%-.192s' (errno: %d, %-.128s)"
+ dan "Kan ikke åbne delt bibliotek '%-.192s' (errno: %d, %-.128s)"
+ nla "Kan shared library '%-.192s' niet openen (Errcode: %d, %-.128s)"
+ eng "Can't open shared library '%-.192s' (errno: %d, %-.128s)"
+ jps "shared library '%-.192s' ã‚’é–‹ã事ãŒã§ãã¾ã›ã‚“ (errno: %d, %-.128s)",
+ est "Ei suuda avada jagatud teeki '%-.192s' (veakood: %d, %-.128s)"
+ fre "Impossible d'ouvrir la bibliothèque partagée '%-.192s' (errno: %d, %-.128s)"
+ ger "Kann Shared Library '%-.192s' nicht öffnen (Fehler: %d, %-.128s)"
+ greek "Δεν είναι δυνατή η ανάγνωση της shared library '%-.192s' (κωδικός λάθους: %d, %-.128s)"
+ hun "A(z) '%-.192s' megosztott konyvtar nem hasznalhato (hibakod: %d, %-.128s)"
+ ita "Impossibile aprire la libreria condivisa '%-.192s' (errno: %d, %-.128s)"
+ jpn "shared library '%-.192s' ã‚’é–‹ã事ãŒã§ãã¾ã›ã‚“ (errno: %d, %-.128s)"
+ kor "'%-.192s' 공유 ë¼ì´ë²„러리를 열수 없습니다.(ì—러번호: %d, %-.128s)"
+ nor "Can't open shared library '%-.192s' (errno: %d, %-.128s)"
+ norwegian-ny "Can't open shared library '%-.192s' (errno: %d, %-.128s)"
+ pol "Can't open shared library '%-.192s' (errno: %d, %-.128s)"
+ por "Não pode abrir biblioteca compartilhada '%-.192s' (erro no. %d, %-.128s)"
+ rum "Nu pot deschide libraria shared '%-.192s' (Eroare: %d, %-.128s)"
+ rus "Ðевозможно открыть динамичеÑкую библиотеку '%-.192s' (ошибка: %d, %-.128s)"
+ serbian "Ne mogu da otvorim share-ovanu biblioteku '%-.192s' (errno: %d, %-.128s)"
+ slo "Nemôžem otvoriť zdieľanú knižnicu '%-.192s' (chybový kód: %d, %-.128s)"
+ spa "No puedo abrir libraria conjugada '%-.192s' (errno: %d, %-.128s)"
+ swe "Kan inte öppna det dynamiska biblioteket '%-.192s' (Felkod: %d, %-.128s)"
+ ukr "Ðе можу відкрити розділювану бібліотеку '%-.192s' (помилка: %d, %-.128s)"
+ER_CANT_FIND_DL_ENTRY
+ cze "Nemohu naj-Bít funkci '%-.128s' v knihovně"
+ dan "Kan ikke finde funktionen '%-.128s' i bibliotek"
+ nla "Kan functie '%-.128s' niet in library vinden"
+ eng "Can't find symbol '%-.128s' in library"
+ jps "function '%-.128s' をライブラリー中ã«è¦‹ä»˜ã‘る事ãŒã§ãã¾ã›ã‚“",
+ est "Ei leia funktsiooni '%-.128s' antud teegis"
+ fre "Impossible de trouver la fonction '%-.128s' dans la bibliothèque"
+ ger "Kann Funktion '%-.128s' in der Library nicht finden"
+ greek "Δεν είναι δυνατή η ανεÏÏεση της συνάÏτησης '%-.128s' στην βιβλιοθήκη"
+ hun "A(z) '%-.128s' fuggveny nem talalhato a konyvtarban"
+ ita "Impossibile trovare la funzione '%-.128s' nella libreria"
+ jpn "function '%-.128s' をライブラリー中ã«è¦‹ä»˜ã‘る事ãŒã§ãã¾ã›ã‚“"
+ kor "ë¼ì´ë²„러리ì—ì„œ '%-.128s' 함수를 ì°¾ì„ ìˆ˜ 없습니다."
+ por "Não pode encontrar a função '%-.128s' na biblioteca"
+ rum "Nu pot gasi functia '%-.128s' in libraria"
+ rus "Ðевозможно отыÑкать Ñимвол '%-.128s' в библиотеке"
+ serbian "Ne mogu da pronadjem funkciju '%-.128s' u biblioteci"
+ slo "Nemôžem nájsť funkciu '%-.128s' v knižnici"
+ spa "No puedo encontrar función '%-.128s' en libraria"
+ swe "Hittar inte funktionen '%-.128s' in det dynamiska biblioteket"
+ ukr "Ðе можу знайти функцію '%-.128s' у бібліотеці"
+ER_FUNCTION_NOT_DEFINED
+ cze "Funkce '%-.192s' nen-Bí definována"
+ dan "Funktionen '%-.192s' er ikke defineret"
+ nla "Functie '%-.192s' is niet gedefinieerd"
+ eng "Function '%-.192s' is not defined"
+ jps "Function '%-.192s' ã¯å®šç¾©ã•ã‚Œã¦ã„ã¾ã›ã‚“",
+ est "Funktsioon '%-.192s' ei ole defineeritud"
+ fre "La fonction '%-.192s' n'est pas définie"
+ ger "Funktion '%-.192s' ist nicht definiert"
+ greek "Η συνάÏτηση '%-.192s' δεν έχει οÏισθεί"
+ hun "A '%-.192s' fuggveny nem definialt"
+ ita "La funzione '%-.192s' non e` definita"
+ jpn "Function '%-.192s' ã¯å®šç¾©ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+ kor "'%-.192s' 함수가 ì •ì˜ë˜ì–´ 있지 않습니다."
+ por "Função '%-.192s' não está definida"
+ rum "Functia '%-.192s' nu e definita"
+ rus "Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ '%-.192s' не определена"
+ serbian "Funkcija '%-.192s' nije definisana"
+ slo "Funkcia '%-.192s' nie je definovaná"
+ spa "Función '%-.192s' no está definida"
+ swe "Funktionen '%-.192s' är inte definierad"
+ ukr "Функцію '%-.192s' не визначено"
+ER_HOST_IS_BLOCKED
+ cze "Stroj '%-.64s' je zablokov-Bán kvůli mnoha chybám při připojování. Odblokujete použitím 'mysqladmin flush-hosts'"
+ dan "Værten '%-.64s' er blokeret på grund af mange fejlforespørgsler. Lås op med 'mysqladmin flush-hosts'"
+ nla "Host '%-.64s' is geblokkeeerd vanwege te veel verbindings fouten. Deblokkeer met 'mysqladmin flush-hosts'"
+ eng "Host '%-.64s' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'"
+ jps "Host '%-.64s' 㯠many connection error ã®ãŸã‚ã€æ‹’å¦ã•ã‚Œã¾ã—ãŸ. 'mysqladmin flush-hosts' ã§è§£é™¤ã—ã¦ãã ã•ã„",
+ est "Masin '%-.64s' on blokeeritud hulgaliste ühendusvigade tõttu. Blokeeringu saab tühistada 'mysqladmin flush-hosts' käsuga"
+ fre "L'hôte '%-.64s' est bloqué à cause d'un trop grand nombre d'erreur de connexion. Débloquer le par 'mysqladmin flush-hosts'"
+ ger "Host '%-.64s' blockiert wegen zu vieler Verbindungsfehler. Aufheben der Blockierung mit 'mysqladmin flush-hosts'"
+ greek "Ο υπολογιστής '%-.64s' έχει αποκλεισθεί λόγω πολλαπλών λαθών σÏνδεσης. ΠÏοσπαθήστε να διοÏώσετε με 'mysqladmin flush-hosts'"
+ hun "A '%-.64s' host blokkolodott, tul sok kapcsolodasi hiba miatt. Hasznalja a 'mysqladmin flush-hosts' parancsot"
+ ita "Sistema '%-.64s' bloccato a causa di troppi errori di connessione. Per sbloccarlo: 'mysqladmin flush-hosts'"
+ jpn "Host '%-.64s' 㯠many connection error ã®ãŸã‚ã€æ‹’å¦ã•ã‚Œã¾ã—ãŸ. 'mysqladmin flush-hosts' ã§è§£é™¤ã—ã¦ãã ã•ã„"
+ kor "너무 ë§Žì€ ì—°ê²°ì˜¤ë¥˜ë¡œ ì¸í•˜ì—¬ 호스트 '%-.64s'는 블ë½ë˜ì—ˆìŠµë‹ˆë‹¤. 'mysqladmin flush-hosts'를 ì´ìš©í•˜ì—¬ 블ë½ì„ 해제하세요"
+ por "'Host' '%-.64s' está bloqueado devido a muitos erros de conexão. Desbloqueie com 'mysqladmin flush-hosts'"
+ rum "Host-ul '%-.64s' e blocat din cauza multelor erori de conectie. Poti deploca folosind 'mysqladmin flush-hosts'"
+ rus "ХоÑÑ‚ '%-.64s' заблокирован из-за Ñлишком большого количеÑтва ошибок ÑоединениÑ. Разблокировать его можно Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ 'mysqladmin flush-hosts'"
+ serbian "Host '%-.64s' je blokiran zbog previše grešaka u konekciji. Možete ga odblokirati pomoću komande 'mysqladmin flush-hosts'"
+ spa "Servidor '%-.64s' está bloqueado por muchos errores de conexión. Desbloquear con 'mysqladmin flush-hosts'"
+ swe "Denna dator, '%-.64s', är blockerad pga många felaktig paket. Gör 'mysqladmin flush-hosts' för att ta bort alla blockeringarna"
+ ukr "ХоÑÑ‚ '%-.64s' заблоковано з причини великої кількоÑÑ‚Ñ– помилок з'єднаннÑ. Ð”Ð»Ñ Ñ€Ð¾Ð·Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸Ñтовуйте 'mysqladmin flush-hosts'"
+ER_HOST_NOT_PRIVILEGED
+ cze "Stroj '%-.64s' nem-Bá 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"
+ jps "Host '%-.64s' 㯠MariaDB server ã«æŽ¥ç¶šã‚’許å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“",
+ est "Masinal '%-.64s' puudub ligipääs sellele MariaDB serverile"
+ fre "Le hôte '%-.64s' n'est pas authorisé à se connecter à ce serveur MariaDB"
+ ger "Host '%-.64s' hat keine Berechtigung, sich mit diesem MariaDB-Server zu verbinden"
+ greek "Ο υπολογιστής '%-.64s' δεν έχει δικαίωμα σÏνδεσης με τον MariaDB server"
+ hun "A '%-.64s' host szamara nem engedelyezett a kapcsolodas ehhez a MariaDB szerverhez"
+ ita "Al sistema '%-.64s' non e` consentita la connessione a questo server MariaDB"
+ jpn "Host '%-.64s' 㯠MariaDB server ã«æŽ¥ç¶šã‚’許å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+ 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"
+ rus "ХоÑту '%-.64s' не разрешаетÑÑ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡Ð°Ñ‚ÑŒÑÑ Ðº Ñтому Ñерверу MariaDB"
+ serbian "Host-u '%-.64s' nije dozvoljeno da se konektuje na ovaj MariaDB server"
+ spa "Servidor '%-.64s' no está permitido para conectar con este servidor MariaDB"
+ swe "Denna dator, '%-.64s', har inte privileger att använda denna MariaDB server"
+ ukr "ХоÑту '%-.64s' не доволено зв'ÑзуватиÑÑŒ з цим Ñервером MariaDB"
+ER_PASSWORD_ANONYMOUS_USER 42000
+ cze "Pou-Bžíváte MariaDB jako anonymní uživatel a anonymní uživatelé nemají povoleno měnit hesla"
+ dan "Du bruger MariaDB som anonym bruger. Anonyme brugere må ikke ændre adgangskoder"
+ nla "U gebruikt MariaDB als anonieme gebruiker en deze mogen geen wachtwoorden wijzigen"
+ eng "You are using MariaDB as an anonymous user and anonymous users are not allowed to change passwords"
+ jps "MariaDB ã‚’ anonymous users ã§ä½¿ç”¨ã—ã¦ã„る状態ã§ã¯ã€ãƒ‘スワードã®å¤‰æ›´ã¯ã§ãã¾ã›ã‚“",
+ est "Te kasutate MariaDB-i anonüümse kasutajana, kelledel pole parooli muutmise õigust"
+ fre "Vous utilisez un utilisateur anonyme et les utilisateurs anonymes ne sont pas autorisés à changer les mots de passe"
+ ger "Sie benutzen MariaDB als anonymer Benutzer und dürfen daher keine Passwörter ändern"
+ greek "ΧÏησιμοποιείτε την MariaDB σαν anonymous user και έτσι δεν μποÏείτε να αλλάξετε τα passwords άλλων χÏηστών"
+ hun "Nevtelen (anonymous) felhasznalokent nem negedelyezett a jelszovaltoztatas"
+ ita "Impossibile cambiare la password usando MariaDB come utente anonimo"
+ jpn "MariaDB ã‚’ anonymous users ã§ä½¿ç”¨ã—ã¦ã„る状態ã§ã¯ã€ãƒ‘スワードã®å¤‰æ›´ã¯ã§ãã¾ã›ã‚“"
+ kor "ë‹¹ì‹ ì€ MariaDBì„œë²„ì— ìµëª…ì˜ ì‚¬ìš©ìžë¡œ ì ‘ì†ì„ 하셨습니다.ìµëª…ì˜ ì‚¬ìš©ìžëŠ” 암호를 변경할 수 없습니다."
+ por "Você está usando o MariaDB como usuário anônimo e usuários anônimos não têm permissão para mudar senhas"
+ rum "Dumneavoastra folositi MariaDB ca un utilizator anonim si utilizatorii anonimi nu au voie sa schime parolele"
+ rus "Ð’Ñ‹ иÑпользуете MariaDB от имени анонимного пользователÑ, а анонимным пользователÑм не разрешаетÑÑ Ð¼ÐµÐ½ÑÑ‚ÑŒ пароли"
+ serbian "Vi koristite MariaDB kao anonimni korisnik a anonimnim korisnicima nije dozvoljeno da menjaju lozinke"
+ spa "Tu estás usando MariaDB como un usuario anonimo y usuarios anonimos no tienen permiso para cambiar las claves"
+ swe "Du använder MariaDB som en anonym användare och som sådan får du inte ändra ditt lösenord"
+ ukr "Ви викориÑтовуєте MariaDB Ñк анонімний кориÑтувач, тому вам не дозволено змінювати паролі"
+ER_PASSWORD_NOT_ALLOWED 42000
+ cze "Na zm-Běnu hesel ostatním musíte mít právo provést update tabulek v databázi mysql"
+ dan "Du skal have tilladelse til at opdatere tabeller i MariaDB databasen for at ændre andres adgangskoder"
+ nla "U moet tabel update priveleges hebben in de mysql database om wachtwoorden voor anderen te mogen wijzigen"
+ eng "You must have privileges to update tables in the mysql database to be able to change passwords for others"
+ jps "ä»–ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ‘スワードを変更ã™ã‚‹ãŸã‚ã«ã¯, mysql データベースã«å¯¾ã—㦠update ã®è¨±å¯ãŒãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“.",
+ est "Teiste paroolide muutmiseks on nõutav tabelite muutmisõigus 'mysql' andmebaasis"
+ fre "Vous devez avoir le privilège update sur les tables de la base de donnée mysql pour pouvoir changer les mots de passe des autres"
+ ger "Sie benötigen die Berechtigung zum Aktualisieren von Tabellen in der Datenbank 'mysql', um die Passwörter anderer Benutzer ändern zu können"
+ greek "ΠÏέπει να έχετε δικαίωμα διόÏθωσης πινάκων (update) στη βάση δεδομένων mysql για να μποÏείτε να αλλάξετε τα passwords άλλων χÏηστών"
+ hun "Onnek tabla-update joggal kell rendelkeznie a mysql adatbazisban masok jelszavanak megvaltoztatasahoz"
+ ita "E` necessario il privilegio di update sulle tabelle del database mysql per cambiare le password per gli altri utenti"
+ jpn "ä»–ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ‘スワードを変更ã™ã‚‹ãŸã‚ã«ã¯, mysql データベースã«å¯¾ã—㦠update ã®è¨±å¯ãŒãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“."
+ kor "ë‹¹ì‹ ì€ ë‹¤ë¥¸ì‚¬ìš©ìžë“¤ì˜ 암호를 변경할 수 있ë„ë¡ ë°ì´íƒ€ë² ì´ìŠ¤ ë³€ê²½ê¶Œí•œì„ ê°€ì ¸ì•¼ 합니다."
+ por "Você deve ter privilégios para atualizar tabelas no banco de dados mysql para ser capaz de mudar a senha de outros"
+ rum "Trebuie sa aveti privilegii sa actualizati tabelele in bazele de date mysql ca sa puteti sa schimati parolele altora"
+ rus "Ð”Ð»Ñ Ñ‚Ð¾Ð³Ð¾ чтобы изменÑÑ‚ÑŒ пароли других пользователей, у Ð²Ð°Ñ Ð´Ð¾Ð»Ð¶Ð½Ñ‹ быть привилегии на изменение таблиц в базе данных mysql"
+ serbian "Morate imati privilegije da možete da update-ujete određene tabele ako želite da menjate lozinke za druge korisnike"
+ spa "Tu debes de tener permiso para actualizar tablas en la base de datos mysql para cambiar las claves para otros"
+ swe "För att ändra lösenord för andra måste du ha rättigheter att uppdatera mysql-databasen"
+ ukr "Ви повині мати право на Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†ÑŒ у базі данних mysql, аби мати можливіÑÑ‚ÑŒ змінювати пароль іншим"
+ER_PASSWORD_NO_MATCH 42000
+ cze "V tabulce user nen-Bí žádný odpovídající řádek"
+ dan "Kan ikke finde nogen tilsvarende poster i bruger tabellen"
+ nla "Kan geen enkele passende rij vinden in de gebruikers tabel"
+ eng "Can't find any matching row in the user table"
+ est "Ei leia vastavat kirjet kasutajate tabelis"
+ fre "Impossible de trouver un enregistrement correspondant dans la table user"
+ ger "Kann keinen passenden Datensatz in Tabelle 'user' finden"
+ greek "Δεν είναι δυνατή η ανεÏÏεση της αντίστοιχης εγγÏαφής στον πίνακα των χÏηστών"
+ hun "Nincs megegyezo sor a user tablaban"
+ ita "Impossibile trovare la riga corrispondente nella tabella user"
+ kor "ì‚¬ìš©ìž í…Œì´ë¸”ì—ì„œ ì¼ì¹˜í•˜ëŠ” ê²ƒì„ ì°¾ì„ ìˆ˜ ì—†ì니다."
+ por "Não pode encontrar nenhuma linha que combine na tabela usuário (user table)"
+ rum "Nu pot gasi nici o linie corespunzatoare in tabela utilizatorului"
+ rus "Ðевозможно отыÑкать подходÑщую запиÑÑŒ в таблице пользователей"
+ serbian "Ne mogu da pronađem odgovarajući slog u 'user' tabeli"
+ spa "No puedo encontrar una línea correponsdiente en la tabla user"
+ swe "Hittade inte användaren i 'user'-tabellen"
+ ukr "Ðе можу знайти відповідних запиÑів у таблиці кориÑтувача"
+ER_UPDATE_INFO
+ cze "Nalezen-Bých řádků: %ld Změněno: %ld Varování: %ld"
+ dan "Poster fundet: %ld Ændret: %ld Advarsler: %ld"
+ nla "Passende rijen: %ld Gewijzigd: %ld Waarschuwingen: %ld"
+ eng "Rows matched: %ld Changed: %ld Warnings: %ld"
+ jps "一致数(Rows matched): %ld 変更: %ld Warnings: %ld",
+ est "Sobinud kirjeid: %ld Muudetud: %ld Hoiatusi: %ld"
+ fre "Enregistrements correspondants: %ld Modifiés: %ld Warnings: %ld"
+ ger "Datensätze gefunden: %ld Geändert: %ld Warnungen: %ld"
+ hun "Megegyezo sorok szama: %ld Valtozott: %ld Warnings: %ld"
+ ita "Rows riconosciute: %ld Cambiate: %ld Warnings: %ld"
+ jpn "一致数(Rows matched): %ld 変更: %ld Warnings: %ld"
+ kor "ì¼ì¹˜í•˜ëŠ” Rows : %ldê°œ 변경ë¨: %ldê°œ 경고: %ldê°œ"
+ por "Linhas que combinaram: %ld - Alteradas: %ld - Avisos: %ld"
+ rum "Linii identificate (matched): %ld Schimbate: %ld Atentionari (warnings): %ld"
+ rus "Совпало запиÑей: %ld Изменено: %ld Предупреждений: %ld"
+ serbian "Odgovarajućih slogova: %ld Promenjeno: %ld Upozorenja: %ld"
+ spa "Líneas correspondientes: %ld Cambiadas: %ld Avisos: %ld"
+ swe "Rader: %ld Uppdaterade: %ld Varningar: %ld"
+ ukr "ЗапиÑів відповідає: %ld Змінено: %ld ЗаÑтережень: %ld"
+ER_CANT_CREATE_THREAD
+ cze "Nemohu vytvo-BÅ™it nový thread (errno %d). Pokud je jeÅ¡tÄ› nÄ›jaká volná paměť, podívejte se do manuálu na Äást o chybách specifických pro jednotlivé operaÄní systémy"
+ dan "Kan ikke danne en ny tråd (fejl nr. %d). Hvis computeren ikke er løbet tør for hukommelse, kan du se i brugervejledningen for en mulig operativ-system - afhængig fejl"
+ nla "Kan geen nieuwe thread aanmaken (Errcode: %d). Indien er geen tekort aan geheugen is kunt u de handleiding consulteren over een mogelijke OS afhankelijke fout"
+ eng "Can't create a new thread (errno %d); if you are not out of available memory, you can consult the manual for a possible OS-dependent bug"
+ jps "æ–°è¦ã«ã‚¹ãƒ¬ãƒƒãƒ‰ãŒä½œã‚Œã¾ã›ã‚“ã§ã—㟠(errno %d). ã‚‚ã—最大使用許å¯ãƒ¡ãƒ¢ãƒªãƒ¼æ•°ã‚’越ãˆã¦ã„ãªã„ã®ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¦ã„ã‚‹ãªã‚‰, マニュアルã®ä¸­ã‹ã‚‰ 'possible OS-dependent bug' ã¨ã„ã†æ–‡å­—を探ã—ã¦ãã¿ã¦ã ã•ã„.",
+ est "Ei suuda luua uut lõime (veakood %d). Kui mälu ei ole otsas, on tõenäoliselt tegemist operatsioonisüsteemispetsiifilise veaga"
+ fre "Impossible de créer une nouvelle tâche (errno %d). S'il reste de la mémoire libre, consultez le manual pour trouver un éventuel bug dépendant de l'OS"
+ ger "Kann keinen neuen Thread erzeugen (Fehler: %d). Sollte noch Speicher verfügbar sein, bitte im Handbuch wegen möglicher Fehler im Betriebssystem nachschlagen"
+ hun "Uj thread letrehozasa nem lehetseges (Hibakod: %d). Amenyiben van meg szabad memoria, olvassa el a kezikonyv operacios rendszerfuggo hibalehetosegekrol szolo reszet"
+ ita "Impossibile creare un nuovo thread (errno %d). Se non ci sono problemi di memoria disponibile puoi consultare il manuale per controllare possibili problemi dipendenti dal SO"
+ jpn "æ–°è¦ã«ã‚¹ãƒ¬ãƒƒãƒ‰ãŒä½œã‚Œã¾ã›ã‚“ã§ã—㟠(errno %d). ã‚‚ã—最大使用許å¯ãƒ¡ãƒ¢ãƒªãƒ¼æ•°ã‚’越ãˆã¦ã„ãªã„ã®ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¦ã„ã‚‹ãªã‚‰, マニュアルã®ä¸­ã‹ã‚‰ 'possible OS-dependent bug' ã¨ã„ã†æ–‡å­—を探ã—ã¦ãã¿ã¦ã ã•ã„."
+ kor "새로운 쓰레드를 만들 수 없습니다.(ì—러번호 %d). 만약 여유메모리가 있다면 OS-dependent버그 ì˜ ë©”ë‰´ì–¼ ë¶€ë¶„ì„ ì°¾ì•„ë³´ì‹œì˜¤."
+ nor "Can't create a new thread (errno %d); if you are not out of available memory you can consult the manual for any possible OS dependent bug"
+ norwegian-ny "Can't create a new thread (errno %d); if you are not out of available memory you can consult the manual for any possible OS dependent bug"
+ pol "Can't create a new thread (errno %d); if you are not out of available memory you can consult the manual for any possible OS dependent bug"
+ por "Não pode criar uma nova 'thread' (erro no. %d). Se você não estiver sem memória disponível, você pode consultar o manual sobre um possível 'bug' dependente do sistema operacional"
+ rum "Nu pot crea un thread nou (Eroare %d). Daca mai aveti memorie disponibila in sistem, puteti consulta manualul - ar putea exista un potential bug in legatura cu sistemul de operare"
+ rus "Ðевозможно Ñоздать новый поток (ошибка %d). ЕÑли Ñто не ÑитуациÑ, ÑвÑÐ·Ð°Ð½Ð½Ð°Ñ Ñ Ð½ÐµÑ…Ð²Ð°Ñ‚ÐºÐ¾Ð¹ памÑти, то вам Ñледует изучить документацию на предмет опиÑÐ°Ð½Ð¸Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾Ð¹ ошибки работы в конкретной ОС"
+ serbian "Ne mogu da kreiram novi thread (errno %d). Ako imate joÅ¡ slobodne memorije, trebali biste da pogledate u priruÄniku da li je ovo specifiÄna greÅ¡ka vaÅ¡eg operativnog sistema"
+ spa "No puedo crear un nuevo thread (errno %d). Si tu está con falta de memoria disponible, tu puedes consultar el Manual para posibles problemas con SO"
+ swe "Kan inte skapa en ny tråd (errno %d)"
+ ukr "Ðе можу Ñтворити нову гілку (помилка %d). Якщо ви не викориÑтали уÑÑŽ пам'ÑÑ‚ÑŒ, то прочитайте документацію до вашої ОС - можливо це помилка ОС"
+ER_WRONG_VALUE_COUNT_ON_ROW 21S01
+ cze "Po-BÄet sloupců neodpovídá poÄtu hodnot na řádku %lu"
+ dan "Kolonne antallet stemmer ikke overens med antallet af værdier i post %lu"
+ nla "Kolom aantal komt niet overeen met waarde aantal in rij %lu"
+ eng "Column count doesn't match value count at row %lu"
+ est "Tulpade hulk erineb väärtuste hulgast real %lu"
+ ger "Anzahl der Felder stimmt nicht mit der Anzahl der Werte in Zeile %lu überein"
+ hun "Az oszlopban talalhato ertek nem egyezik meg a %lu sorban szamitott ertekkel"
+ ita "Il numero delle colonne non corrisponde al conteggio alla riga %lu"
+ kor "Row %luì—ì„œ 칼럼 카운트와 value 카운터와 ì¼ì¹˜í•˜ì§€ 않습니다."
+ por "Contagem de colunas não confere com a contagem de valores na linha %lu"
+ rum "Numarul de coloane nu corespunde cu numarul de valori la linia %lu"
+ rus "КоличеÑтво Ñтолбцов не Ñовпадает Ñ ÐºÐ¾Ð»Ð¸Ñ‡ÐµÑтвом значений в запиÑи %lu"
+ serbian "Broj kolona ne odgovara broju vrednosti u slogu %lu"
+ spa "El número de columnas no corresponde al número en la línea %lu"
+ swe "Antalet kolumner motsvarar inte antalet värden på rad: %lu"
+ ukr "КількіÑÑ‚ÑŒ Ñтовбців не Ñпівпадає з кількіÑÑ‚ÑŽ значень у Ñтроці %lu"
+ER_CANT_REOPEN_TABLE
+ cze "Nemohu znovuotev-Břít tabulku: '%-.192s"
+ dan "Kan ikke genåbne tabel '%-.192s"
+ nla "Kan tabel niet opnieuw openen: '%-.192s"
+ eng "Can't reopen table: '%-.192s'"
+ est "Ei suuda taasavada tabelit '%-.192s'"
+ fre "Impossible de réouvrir la table: '%-.192s"
+ ger "Kann Tabelle'%-.192s' nicht erneut öffnen"
+ hun "Nem lehet ujra-megnyitni a tablat: '%-.192s"
+ ita "Impossibile riaprire la tabella: '%-.192s'"
+ kor "í…Œì´ë¸”ì„ ë‹¤ì‹œ 열수 없군요: '%-.192s"
+ nor "Can't reopen table: '%-.192s"
+ norwegian-ny "Can't reopen table: '%-.192s"
+ pol "Can't reopen table: '%-.192s"
+ por "Não pode reabrir a tabela '%-.192s"
+ rum "Nu pot redeschide tabela: '%-.192s'"
+ rus "Ðевозможно заново открыть таблицу '%-.192s'"
+ serbian "Ne mogu da ponovo otvorim tabelu '%-.192s'"
+ slo "Can't reopen table: '%-.192s"
+ spa "No puedo reabrir tabla: '%-.192s"
+ swe "Kunde inte stänga och öppna tabell '%-.192s"
+ ukr "Ðе можу перевідкрити таблицю: '%-.192s'"
+ER_INVALID_USE_OF_NULL 22004
+ cze "Neplatn-Bé užití hodnoty NULL"
+ dan "Forkert brug af nulværdi (NULL)"
+ nla "Foutief gebruik van de NULL waarde"
+ eng "Invalid use of NULL value"
+ jps "NULL 値ã®ä½¿ç”¨æ–¹æ³•ãŒä¸é©åˆ‡ã§ã™",
+ est "NULL väärtuse väärkasutus"
+ fre "Utilisation incorrecte de la valeur NULL"
+ ger "Unerlaubte Verwendung eines NULL-Werts"
+ hun "A NULL ervenytelen hasznalata"
+ ita "Uso scorretto del valore NULL"
+ jpn "NULL 値ã®ä½¿ç”¨æ–¹æ³•ãŒä¸é©åˆ‡ã§ã™"
+ kor "NULL ê°’ì„ ìž˜ëª» 사용하셨군요..."
+ por "Uso inválido do valor NULL"
+ rum "Folosirea unei value NULL e invalida"
+ rus "Ðеправильное иÑпользование величины NULL"
+ serbian "Pogrešna upotreba vrednosti NULL"
+ spa "Invalido uso de valor NULL"
+ swe "Felaktig använding av NULL"
+ ukr "Хибне викориÑÑ‚Ð°Ð½Ð½Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ NULL"
+ER_REGEXP_ERROR 42000
+ cze "Regul-Bární výraz vrátil chybu '%-.64s'"
+ dan "Fik fejl '%-.64s' fra regexp"
+ nla "Fout '%-.64s' ontvangen van regexp"
+ eng "Got error '%-.64s' from regexp"
+ est "regexp tagastas vea '%-.64s'"
+ fre "Erreur '%-.64s' provenant de regexp"
+ ger "regexp lieferte Fehler '%-.64s'"
+ hun "'%-.64s' hiba a regularis kifejezes hasznalata soran (regexp)"
+ ita "Errore '%-.64s' da regexp"
+ kor "regexpì—ì„œ '%-.64s'ê°€ 났습니다."
+ por "Obteve erro '%-.64s' em regexp"
+ rum "Eroarea '%-.64s' obtinuta din expresia regulara (regexp)"
+ rus "Получена ошибка '%-.64s' от регулÑрного выражениÑ"
+ serbian "Funkcija regexp je vratila grešku '%-.64s'"
+ spa "Obtenido error '%-.64s' de regexp"
+ swe "Fick fel '%-.64s' från REGEXP"
+ ukr "Отримано помилку '%-.64s' від регулÑрного виразу"
+ER_MIX_OF_GROUP_FUNC_AND_FIELDS 42000
+ cze "Pokud nen-Bí žádná GROUP BY klauzule, není dovoleno souÄasné použití GROUP položek (MIN(),MAX(),COUNT()...) s ne GROUP položkami"
+ dan "Sammenblanding af GROUP kolonner (MIN(),MAX(),COUNT()...) uden GROUP kolonner er ikke tilladt, hvis der ikke er noget GROUP BY prædikat"
+ nla "Het mixen van GROUP kolommen (MIN(),MAX(),COUNT()...) met no-GROUP kolommen is foutief indien er geen GROUP BY clausule is"
+ eng "Mixing of GROUP columns (MIN(),MAX(),COUNT(),...) with no GROUP columns is illegal if there is no GROUP BY clause"
+ est "GROUP tulpade (MIN(),MAX(),COUNT()...) kooskasutamine tavaliste tulpadega ilma GROUP BY klauslita ei ole lubatud"
+ fre "Mélanger les colonnes GROUP (MIN(),MAX(),COUNT()...) avec des colonnes normales est interdit s'il n'y a pas de clause GROUP BY"
+ ger "Das Vermischen von GROUP-Feldern (MIN(),MAX(),COUNT()...) mit Nicht-GROUP-Feldern ist nicht zulässig, wenn keine GROUP-BY-Klausel vorhanden ist"
+ hun "A GROUP mezok (MIN(),MAX(),COUNT()...) kevert hasznalata nem lehetseges GROUP BY hivatkozas nelkul"
+ ita "Il mescolare funzioni di aggregazione (MIN(),MAX(),COUNT()...) e non e` illegale se non c'e` una clausula GROUP BY"
+ kor "Mixing of GROUP 칼럼s (MIN(),MAX(),COUNT(),...) with no GROUP 칼럼s is illegal if there is no GROUP BY clause"
+ por "Mistura de colunas agrupadas (com MIN(), MAX(), COUNT(), ...) com colunas não agrupadas é ilegal, se não existir uma cláusula de agrupamento (cláusula GROUP BY)"
+ rum "Amestecarea de coloane GROUP (MIN(),MAX(),COUNT()...) fara coloane GROUP este ilegala daca nu exista o clauza GROUP BY"
+ rus "Одновременное иÑпользование Ñгруппированных (GROUP) Ñтолбцов (MIN(),MAX(),COUNT(),...) Ñ Ð½ÐµÑгруппированными Ñтолбцами ÑвлÑетÑÑ Ð½ÐµÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ñ‹Ð¼, еÑли в выражении еÑÑ‚ÑŒ GROUP BY"
+ serbian "Upotreba agregatnih funkcija (MIN(),MAX(),COUNT()...) bez 'GROUP' kolona je pogrešna ako ne postoji 'GROUP BY' iskaz"
+ spa "Mezcla de columnas GROUP (MIN(),MAX(),COUNT()...) con no GROUP columnas es ilegal si no hat la clausula GROUP BY"
+ swe "Man får ha både GROUP-kolumner (MIN(),MAX(),COUNT()...) och fält i en fråga om man inte har en GROUP BY-del"
+ ukr "Ð—Ð¼Ñ–ÑˆÑƒÐ²Ð°Ð½Ð½Ñ GROUP Ñтовбців (MIN(),MAX(),COUNT()...) з не GROUP ÑтовбцÑми Ñ” забороненим, Ñкщо не має GROUP BY"
+ER_NONEXISTING_GRANT 42000
+ cze "Neexistuje odpov-Bídající grant pro uživatele '%-.48s' na stroji '%-.64s'"
+ dan "Denne tilladelse findes ikke for brugeren '%-.48s' på vært '%-.64s'"
+ nla "Deze toegang (GRANT) is niet toegekend voor gebruiker '%-.48s' op host '%-.64s'"
+ eng "There is no such grant defined for user '%-.48s' on host '%-.64s'"
+ jps "ユーザー '%-.48s' (ホスト '%-.64s' ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼) ã¯è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“",
+ est "Sellist õigust ei ole defineeritud kasutajale '%-.48s' masinast '%-.64s'"
+ fre "Un tel droit n'est pas défini pour l'utilisateur '%-.48s' sur l'hôte '%-.64s'"
+ ger "Für Benutzer '%-.48s' auf Host '%-.64s' gibt es keine solche Berechtigung"
+ hun "A '%-.48s' felhasznalonak nincs ilyen joga a '%-.64s' host-on"
+ ita "GRANT non definita per l'utente '%-.48s' dalla macchina '%-.64s'"
+ jpn "ユーザー '%-.48s' (ホスト '%-.64s' ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼) ã¯è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+ kor "ì‚¬ìš©ìž '%-.48s' (호스트 '%-.64s')를 위하여 ì •ì˜ëœ 그런 승ì¸ì€ 없습니다."
+ por "Não existe tal permissão (grant) definida para o usuário '%-.48s' no 'host' '%-.64s'"
+ rum "Nu exista un astfel de grant definit pentru utilzatorul '%-.48s' de pe host-ul '%-.64s'"
+ rus "Такие права не определены Ð´Ð»Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ '%-.48s' на хоÑте '%-.64s'"
+ serbian "Ne postoji odobrenje za pristup korisniku '%-.48s' na host-u '%-.64s'"
+ spa "No existe permiso definido para usuario '%-.48s' en el servidor '%-.64s'"
+ swe "Det finns inget privilegium definierat för användare '%-.48s' på '%-.64s'"
+ ukr "Повноважень не визначено Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувача '%-.48s' з хоÑту '%-.64s'"
+ER_TABLEACCESS_DENIED_ERROR 42000
+ cze "%-.16s p-Bříkaz nepřístupný pro uživatele: '%s'@'%s' pro tabulku '%-.192s'"
+ dan "%-.16s-kommandoen er ikke tilladt for brugeren '%s'@'%s' for tabellen '%-.192s'"
+ nla "%-.16s commando geweigerd voor gebruiker: '%s'@'%s' voor tabel '%-.192s'"
+ eng "%-.16s command denied to user '%s'@'%s' for table '%-.192s'"
+ jps "コマンド %-.16s 㯠ユーザー '%s'@'%s' ,テーブル '%-.192s' ã«å¯¾ã—ã¦è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“",
+ est "%-.16s käsk ei ole lubatud kasutajale '%s'@'%s' tabelis '%-.192s'"
+ fre "La commande '%-.16s' est interdite à l'utilisateur: '%s'@'%s' sur la table '%-.192s'"
+ ger "%-.16s Befehl nicht erlaubt für Benutzer '%s'@'%s' auf Tabelle '%-.192s'"
+ hun "%-.16s parancs a '%s'@'%s' felhasznalo szamara nem engedelyezett a '%-.192s' tablaban"
+ ita "Comando %-.16s negato per l'utente: '%s'@'%s' sulla tabella '%-.192s'"
+ jpn "コマンド %-.16s 㯠ユーザー '%s'@'%s' ,テーブル '%-.192s' ã«å¯¾ã—ã¦è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+ kor "'%-.16s' ëª…ë ¹ì€ ë‹¤ìŒ ì‚¬ìš©ìžì—게 거부ë˜ì—ˆìŠµë‹ˆë‹¤. : '%s'@'%s' for í…Œì´ë¸” '%-.192s'"
+ por "Comando '%-.16s' negado para o usuário '%s'@'%s' na tabela '%-.192s'"
+ rum "Comanda %-.16s interzisa utilizatorului: '%s'@'%s' pentru tabela '%-.192s'"
+ rus "Команда %-.16s запрещена пользователю '%s'@'%s' Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ '%-.192s'"
+ serbian "%-.16s komanda zabranjena za korisnika '%s'@'%s' za tabelu '%-.192s'"
+ spa "%-.16s comando negado para usuario: '%s'@'%s' para tabla '%-.192s'"
+ swe "%-.16s ej tillåtet för '%s'@'%s' för tabell '%-.192s'"
+ ukr "%-.16s команда заборонена кориÑтувачу: '%s'@'%s' у таблиці '%-.192s'"
+ER_COLUMNACCESS_DENIED_ERROR 42000
+ cze "%-.16s p-Bříkaz nepřístupný pro uživatele: '%s'@'%s' pro sloupec '%-.192s' v tabulce '%-.192s'"
+ dan "%-.16s-kommandoen er ikke tilladt for brugeren '%s'@'%s' for kolonne '%-.192s' in tabellen '%-.192s'"
+ nla "%-.16s commando geweigerd voor gebruiker: '%s'@'%s' voor kolom '%-.192s' in tabel '%-.192s'"
+ eng "%-.16s command denied to user '%s'@'%s' for column '%-.192s' in table '%-.192s'"
+ jps "コマンド %-.16s 㯠ユーザー '%s'@'%s'Â¥n カラム '%-.192s' テーブル '%-.192s' ã«å¯¾ã—ã¦è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“",
+ est "%-.16s käsk ei ole lubatud kasutajale '%s'@'%s' tulbale '%-.192s' tabelis '%-.192s'"
+ fre "La commande '%-.16s' est interdite à l'utilisateur: '%s'@'%s' sur la colonne '%-.192s' de la table '%-.192s'"
+ ger "%-.16s Befehl nicht erlaubt für Benutzer '%s'@'%s' und Feld '%-.192s' in Tabelle '%-.192s'"
+ hun "%-.16s parancs a '%s'@'%s' felhasznalo szamara nem engedelyezett a '%-.192s' mezo eseten a '%-.192s' tablaban"
+ ita "Comando %-.16s negato per l'utente: '%s'@'%s' sulla colonna '%-.192s' della tabella '%-.192s'"
+ jpn "コマンド %-.16s 㯠ユーザー '%s'@'%s'\n カラム '%-.192s' テーブル '%-.192s' ã«å¯¾ã—ã¦è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+ kor "'%-.16s' ëª…ë ¹ì€ ë‹¤ìŒ ì‚¬ìš©ìžì—게 거부ë˜ì—ˆìŠµë‹ˆë‹¤. : '%s'@'%s' for 칼럼 '%-.192s' in í…Œì´ë¸” '%-.192s'"
+ por "Comando '%-.16s' negado para o usuário '%s'@'%s' na coluna '%-.192s', na tabela '%-.192s'"
+ rum "Comanda %-.16s interzisa utilizatorului: '%s'@'%s' pentru coloana '%-.192s' in tabela '%-.192s'"
+ rus "Команда %-.16s запрещена пользователю '%s'@'%s' Ð´Ð»Ñ Ñтолбца '%-.192s' в таблице '%-.192s'"
+ serbian "%-.16s komanda zabranjena za korisnika '%s'@'%s' za kolonu '%-.192s' iz tabele '%-.192s'"
+ spa "%-.16s comando negado para usuario: '%s'@'%s' para columna '%-.192s' en la tabla '%-.192s'"
+ swe "%-.16s ej tillåtet för '%s'@'%s' för kolumn '%-.192s' i tabell '%-.192s'"
+ ukr "%-.16s команда заборонена кориÑтувачу: '%s'@'%s' Ð´Ð»Ñ ÑÑ‚Ð¾Ð²Ð±Ñ†Ñ '%-.192s' у таблиці '%-.192s'"
+ER_ILLEGAL_GRANT_FOR_TABLE 42000
+ cze "Neplatn-Bý příkaz GRANT/REVOKE. Prosím, pÅ™eÄtÄ›te si v manuálu, jaká privilegia je možné použít."
+ dan "Forkert GRANT/REVOKE kommando. Se i brugervejledningen hvilke privilegier der kan specificeres."
+ nla "Foutief GRANT/REVOKE commando. Raadpleeg de handleiding welke priveleges gebruikt kunnen worden."
+ eng "Illegal GRANT/REVOKE command; please consult the manual to see which privileges can be used"
+ est "Vigane GRANT/REVOKE käsk. Tutvu kasutajajuhendiga"
+ fre "Commande GRANT/REVOKE incorrecte. Consultez le manuel."
+ ger "Unzulässiger GRANT- oder REVOKE-Befehl. Verfügbare Berechtigungen sind im Handbuch aufgeführt"
+ greek "Illegal GRANT/REVOKE command; please consult the manual to see which privileges can be used."
+ hun "Ervenytelen GRANT/REVOKE parancs. Kerem, nezze meg a kezikonyvben, milyen jogok lehetsegesek"
+ ita "Comando GRANT/REVOKE illegale. Prego consultare il manuale per sapere quali privilegi possono essere usati."
+ jpn "Illegal GRANT/REVOKE command; please consult the manual to see which privleges can be used."
+ kor "ìž˜ëª»ëœ GRANT/REVOKE 명령. ì–´ë–¤ 권리와 승ì¸ì´ 사용ë˜ì–´ 질 수 있는지 ë©”ë‰´ì–¼ì„ ë³´ì‹œì˜¤."
+ nor "Illegal GRANT/REVOKE command; please consult the manual to see which privleges can be used."
+ norwegian-ny "Illegal GRANT/REVOKE command; please consult the manual to see which privleges can be used."
+ pol "Illegal GRANT/REVOKE command; please consult the manual to see which privleges can be used."
+ por "Comando GRANT/REVOKE ilegal. Por favor consulte no manual quais privilégios podem ser usados."
+ rum "Comanda GRANT/REVOKE ilegala. Consultati manualul in privinta privilegiilor ce pot fi folosite."
+ rus "ÐÐµÐ²ÐµÑ€Ð½Ð°Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° GRANT или REVOKE. ОбратитеÑÑŒ к документации, чтобы выÑÑнить, какие привилегии можно иÑпользовать"
+ serbian "PogreÅ¡na 'GRANT' odnosno 'REVOKE' komanda. Molim Vas pogledajte u priruÄniku koje vrednosti mogu biti upotrebljene."
+ slo "Illegal GRANT/REVOKE command; please consult the manual to see which privleges can be used."
+ spa "Ilegal comando GRANT/REVOKE. Por favor consulte el manual para cuales permisos pueden ser usados."
+ swe "Felaktigt GRANT-privilegium använt"
+ ukr "Хибна GRANT/REVOKE команда; прочитайте документацію ÑтоÑовно того, Ñкі права можна викориÑтовувати"
+ER_GRANT_WRONG_HOST_OR_USER 42000
+ cze "Argument p-Bříkazu GRANT uživatel nebo stroj je příliš dlouhý"
+ dan "Værts- eller brugernavn for langt til GRANT"
+ nla "De host of gebruiker parameter voor GRANT is te lang"
+ eng "The host or user argument to GRANT is too long"
+ est "Masina või kasutaja nimi GRANT lauses on liiga pikk"
+ fre "L'hôte ou l'utilisateur donné en argument à GRANT est trop long"
+ ger "Das Host- oder User-Argument für GRANT ist zu lang"
+ hun "A host vagy felhasznalo argumentuma tul hosszu a GRANT parancsban"
+ ita "L'argomento host o utente per la GRANT e` troppo lungo"
+ kor "승ì¸(GRANT)ì„ ìœ„í•˜ì—¬ 사용한 사용ìžë‚˜ í˜¸ìŠ¤íŠ¸ì˜ ê°’ë“¤ì´ ë„ˆë¬´ ê¹ë‹ˆë‹¤."
+ por "Argumento de 'host' ou de usuário para o GRANT é longo demais"
+ rum "Argumentul host-ului sau utilizatorului pentru GRANT e prea lung"
+ rus "Слишком длинное Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ/хоÑта Ð´Ð»Ñ GRANT"
+ serbian "Argument 'host' ili 'korisnik' prosleÄ‘en komandi 'GRANT' je predugaÄak"
+ spa "El argumento para servidor o usuario para GRANT es demasiado grande"
+ swe "Felaktigt maskinnamn eller användarnamn använt med GRANT"
+ ukr "Ðргумент host або user Ð´Ð»Ñ GRANT задовгий"
+ER_NO_SUCH_TABLE 42S02
+ cze "Tabulka '%-.192s.%-.192s' neexistuje"
+ dan "Tabellen '%-.192s.%-.192s' eksisterer ikke"
+ nla "Tabel '%-.192s.%-.192s' bestaat niet"
+ eng "Table '%-.192s.%-.192s' doesn't exist"
+ est "Tabelit '%-.192s.%-.192s' ei eksisteeri"
+ fre "La table '%-.192s.%-.192s' n'existe pas"
+ ger "Tabelle '%-.192s.%-.192s' existiert nicht"
+ hun "A '%-.192s.%-.192s' tabla nem letezik"
+ ita "La tabella '%-.192s.%-.192s' non esiste"
+ jpn "Table '%-.192s.%-.192s' doesn't exist"
+ kor "í…Œì´ë¸” '%-.192s.%-.192s' 는 존재하지 않습니다."
+ nor "Table '%-.192s.%-.192s' doesn't exist"
+ norwegian-ny "Table '%-.192s.%-.192s' doesn't exist"
+ pol "Table '%-.192s.%-.192s' doesn't exist"
+ por "Tabela '%-.192s.%-.192s' não existe"
+ rum "Tabela '%-.192s.%-.192s' nu exista"
+ rus "Таблица '%-.192s.%-.192s' не ÑущеÑтвует"
+ serbian "Tabela '%-.192s.%-.192s' ne postoji"
+ slo "Table '%-.192s.%-.192s' doesn't exist"
+ spa "Tabla '%-.192s.%-.192s' no existe"
+ swe "Det finns ingen tabell som heter '%-.192s.%-.192s'"
+ ukr "Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ '%-.192s.%-.192s' не Ñ–Ñнує"
+ER_NONEXISTING_TABLE_GRANT 42000
+ cze "Neexistuje odpov-Bídající grant pro uživatele '%-.48s' na stroji '%-.64s' pro tabulku '%-.192s'"
+ dan "Denne tilladelse eksisterer ikke for brugeren '%-.48s' på vært '%-.64s' for tabellen '%-.192s'"
+ nla "Deze toegang (GRANT) is niet toegekend voor gebruiker '%-.48s' op host '%-.64s' op tabel '%-.192s'"
+ eng "There is no such grant defined for user '%-.48s' on host '%-.64s' on table '%-.192s'"
+ est "Sellist õigust ei ole defineeritud kasutajale '%-.48s' masinast '%-.64s' tabelile '%-.192s'"
+ fre "Un tel droit n'est pas défini pour l'utilisateur '%-.48s' sur l'hôte '%-.64s' sur la table '%-.192s'"
+ ger "Eine solche Berechtigung ist für User '%-.48s' auf Host '%-.64s' an Tabelle '%-.192s' nicht definiert"
+ hun "A '%-.48s' felhasznalo szamara a '%-.64s' host '%-.192s' tablajaban ez a parancs nem engedelyezett"
+ ita "GRANT non definita per l'utente '%-.48s' dalla macchina '%-.64s' sulla tabella '%-.192s'"
+ kor "ì‚¬ìš©ìž '%-.48s'(호스트 '%-.64s')는 í…Œì´ë¸” '%-.192s'를 사용하기 위하여 ì •ì˜ëœ 승ì¸ì€ 없습니다. "
+ por "Não existe tal permissão (grant) definido para o usuário '%-.48s' no 'host' '%-.64s', na tabela '%-.192s'"
+ rum "Nu exista un astfel de privilegiu (grant) definit pentru utilizatorul '%-.48s' de pe host-ul '%-.64s' pentru tabela '%-.192s'"
+ rus "Такие права не определены Ð´Ð»Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ '%-.48s' на компьютере '%-.64s' Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ '%-.192s'"
+ serbian "Ne postoji odobrenje za pristup korisniku '%-.48s' na host-u '%-.64s' tabeli '%-.192s'"
+ spa "No existe tal permiso definido para usuario '%-.48s' en el servidor '%-.64s' en la tabla '%-.192s'"
+ swe "Det finns inget privilegium definierat för användare '%-.48s' på '%-.64s' för tabell '%-.192s'"
+ ukr "Повноважень не визначено Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувача '%-.48s' з хоÑту '%-.64s' Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ– '%-.192s'"
+ER_NOT_ALLOWED_COMMAND 42000
+ cze "Pou-Bžitý příkaz není v této verzi MariaDB povolen"
+ 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"
+ est "Antud käsk ei ole lubatud käesolevas MariaDB versioonis"
+ fre "Cette commande n'existe pas dans cette version de MariaDB"
+ ger "Der verwendete Befehl ist in dieser MariaDB-Version nicht zulässig"
+ hun "A hasznalt parancs nem engedelyezett ebben a MariaDB verzioban"
+ ita "Il comando utilizzato non e` supportato in questa versione di MariaDB"
+ 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"
+ rus "Эта команда не допуÑкаетÑÑ Ð² данной верÑии MariaDB"
+ serbian "Upotrebljena komanda nije dozvoljena sa ovom verzijom MariaDB servera"
+ spa "El comando usado no es permitido con esta versión de MariaDB"
+ swe "Du kan inte använda detta kommando med denna MariaDB version"
+ ukr "ВикориÑтовувана команда не дозволена у цій верÑÑ–Ñ— MariaDB"
+ER_SYNTAX_ERROR 42000
+ cze "Va-Bše syntaxe je nějaká divná"
+ dan "Der er en fejl i SQL syntaksen"
+ nla "Er is iets fout in de gebruikte syntax"
+ eng "You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use"
+ est "Viga SQL süntaksis"
+ fre "Erreur de syntaxe"
+ ger "Fehler in der SQL-Syntax. Bitte die korrekte Syntax im Handbuch nachschlagen"
+ greek "You have an error in your SQL syntax"
+ hun "Szintaktikai hiba"
+ ita "Errore di sintassi nella query SQL"
+ jpn "Something is wrong in your syntax"
+ kor "SQL êµ¬ë¬¸ì— ì˜¤ë¥˜ê°€ 있습니다."
+ nor "Something is wrong in your syntax"
+ norwegian-ny "Something is wrong in your syntax"
+ pol "Something is wrong in your syntax"
+ por "Você tem um erro de sintaxe no seu SQL"
+ rum "Aveti o eroare in sintaxa RSQL"
+ rus "У Ð²Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ° в запроÑе. Изучите документацию по иÑпользуемой верÑии MariaDB на предмет корректного ÑинтакÑиÑа"
+ serbian "Imate grešku u vašoj SQL sintaksi"
+ slo "Something is wrong in your syntax"
+ spa "Algo está equivocado en su sintax"
+ swe "Du har något fel i din syntax"
+ ukr "У Ð²Ð°Ñ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° у ÑинтакÑиÑÑ– SQL"
+ER_DELAYED_CANT_CHANGE_LOCK
+ cze "Zpo-Bžděný insert threadu nebyl schopen získat požadovaný zámek pro tabulku %-.192s"
+ dan "Forsinket indsættelse tråden (delayed insert thread) kunne ikke opnå lås på tabellen %-.192s"
+ nla "'Delayed insert' thread kon de aangevraagde 'lock' niet krijgen voor tabel %-.192s"
+ eng "Delayed insert thread couldn't get requested lock for table %-.192s"
+ est "INSERT DELAYED lõim ei suutnud saada soovitud lukku tabelile %-.192s"
+ fre "La tâche 'delayed insert' n'a pas pu obtenir le verrou démandé sur la table %-.192s"
+ ger "Verzögerter (DELAYED) Einfüge-Thread konnte die angeforderte Sperre für Tabelle '%-.192s' nicht erhalten"
+ hun "A kesleltetett beillesztes (delayed insert) thread nem kapott zatolast a %-.192s tablahoz"
+ ita "Il thread di inserimento ritardato non riesce ad ottenere il lock per la tabella %-.192s"
+ kor "ì§€ì—°ëœ insert 쓰레드가 í…Œì´ë¸” %-.192sì˜ ìš”êµ¬ëœ ë½í‚¹ì„ 처리할 수 없었습니다."
+ por "'Thread' de inserção retardada (atrasada) pois não conseguiu obter a trava solicitada para tabela '%-.192s'"
+ rum "Thread-ul pentru inserarea aminata nu a putut obtine lacatul (lock) pentru tabela %-.192s"
+ rus "Поток, обÑлуживающий отложенную вÑтавку (delayed insert), не Ñмог получить запрашиваемую блокировку на таблицу %-.192s"
+ serbian "Prolongirani 'INSERT' thread nije mogao da dobije traženo zakljuÄavanje tabele '%-.192s'"
+ spa "Thread de inserción retarda no pudiendo bloquear para la tabla %-.192s"
+ swe "DELAYED INSERT-tråden kunde inte låsa tabell '%-.192s'"
+ ukr "Гілка Ð´Ð»Ñ INSERT DELAYED не може отримати Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ– %-.192s"
+ER_TOO_MANY_DELAYED_THREADS
+ cze "P-Bříliš mnoho zpožděných threadů"
+ dan "For mange slettede tråde (threads) i brug"
+ nla "Te veel 'delayed' threads in gebruik"
+ eng "Too many delayed threads in use"
+ est "Liiga palju DELAYED lõimesid kasutusel"
+ fre "Trop de tâche 'delayed' en cours"
+ ger "Zu viele verzögerte (DELAYED) Threads in Verwendung"
+ hun "Tul sok kesletetett thread (delayed)"
+ ita "Troppi threads ritardati in uso"
+ kor "너무 ë§Žì€ ì§€ì—° 쓰레드를 사용하고 있습니다."
+ por "Excesso de 'threads' retardadas (atrasadas) em uso"
+ rum "Prea multe threaduri aminate care sint in uz"
+ rus "Слишком много потоков, обÑлуживающих отложенную вÑтавку (delayed insert)"
+ serbian "Previše prolongiranih thread-ova je u upotrebi"
+ spa "Muchos threads retardados en uso"
+ swe "Det finns redan 'max_delayed_threads' trådar i använding"
+ ukr "Забагато затриманих гілок викориÑтовуєтьÑÑ"
+ER_ABORTING_CONNECTION 08S01
+ cze "Zru-Bšeno spojení %ld do databáze: '%-.192s' uživatel: '%-.48s' (%-.64s)"
+ dan "Afbrudt forbindelse %ld til database: '%-.192s' bruger: '%-.48s' (%-.64s)"
+ nla "Afgebroken verbinding %ld naar db: '%-.192s' gebruiker: '%-.48s' (%-.64s)"
+ eng "Aborted connection %ld to db: '%-.192s' user: '%-.48s' (%-.64s)"
+ est "Ãœhendus katkestatud %ld andmebaasile: '%-.192s' kasutajale: '%-.48s' (%-.64s)"
+ fre "Connection %ld avortée vers la bd: '%-.192s' utilisateur: '%-.48s' (%-.64s)"
+ ger "Abbruch der Verbindung %ld zur Datenbank '%-.192s'. Benutzer: '%-.48s' (%-.64s)"
+ hun "Megszakitott kapcsolat %ld db: '%-.192s' adatbazishoz, felhasznalo: '%-.48s' (%-.64s)"
+ ita "Interrotta la connessione %ld al db: '%-.192s' utente: '%-.48s' (%-.64s)"
+ jpn "Aborted connection %ld to db: '%-.192s' user: '%-.48s' (%-.64s)"
+ kor "ë°ì´íƒ€ë² ì´ìŠ¤ ì ‘ì†ì„ 위한 ì—°ê²° %ldê°€ ì¤‘ë‹¨ë¨ : '%-.192s' 사용ìž: '%-.48s' (%-.64s)"
+ nor "Aborted connection %ld to db: '%-.192s' user: '%-.48s' (%-.64s)"
+ norwegian-ny "Aborted connection %ld to db: '%-.192s' user: '%-.48s' (%-.64s)"
+ pol "Aborted connection %ld to db: '%-.192s' user: '%-.48s' (%-.64s)"
+ por "Conexão %ld abortou para o banco de dados '%-.192s' - usuário '%-.48s' (%-.64s)"
+ rum "Conectie terminata %ld la baza de date: '%-.192s' utilizator: '%-.48s' (%-.64s)"
+ rus "Прервано Ñоединение %ld к базе данных '%-.192s' Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ '%-.48s' (%-.64s)"
+ serbian "Prekinuta konekcija broj %ld ka bazi: '%-.192s' korisnik je bio: '%-.48s' (%-.64s)"
+ slo "Aborted connection %ld to db: '%-.192s' user: '%-.48s' (%-.64s)"
+ spa "Conexión abortada %ld para db: '%-.192s' usuario: '%-.48s' (%-.64s)"
+ swe "Avbröt länken för tråd %ld till db '%-.192s', användare '%-.48s' (%-.64s)"
+ ukr "Перервано з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ %ld до бази данних: '%-.192s' кориÑтувача: '%-.48s' (%-.64s)"
+ER_NET_PACKET_TOO_LARGE 08S01
+ cze "Zji-Bštěn příchozí packet delší než 'max_allowed_packet'"
+ dan "Modtog en datapakke som var større end 'max_allowed_packet'"
+ nla "Groter pakket ontvangen dan 'max_allowed_packet'"
+ eng "Got a packet bigger than 'max_allowed_packet' bytes"
+ est "Saabus suurem pakett kui lubatud 'max_allowed_packet' muutujaga"
+ fre "Paquet plus grand que 'max_allowed_packet' reçu"
+ ger "Empfangenes Paket ist größer als 'max_allowed_packet' Bytes"
+ hun "A kapott csomag nagyobb, mint a maximalisan engedelyezett: 'max_allowed_packet'"
+ ita "Ricevuto un pacchetto piu` grande di 'max_allowed_packet'"
+ kor "'max_allowed_packet'보다 ë”í° íŒ¨í‚·ì„ ë°›ì•˜ìŠµë‹ˆë‹¤."
+ por "Obteve um pacote maior do que a taxa máxima de pacotes definida (max_allowed_packet)"
+ rum "Un packet mai mare decit 'max_allowed_packet' a fost primit"
+ rus "Полученный пакет больше, чем 'max_allowed_packet'"
+ serbian "Primio sam mrežni paket veći od definisane vrednosti 'max_allowed_packet'"
+ spa "Obtenido un paquete mayor que 'max_allowed_packet'"
+ swe "Kommunkationspaketet är större än 'max_allowed_packet'"
+ ukr "Отримано пакет більший ніж max_allowed_packet"
+ER_NET_READ_ERROR_FROM_PIPE 08S01
+ cze "Zji-BÅ¡tÄ›na chyba pÅ™i Ätení z roury spojení"
+ dan "Fik læsefejl fra forbindelse (connection pipe)"
+ nla "Kreeg leesfout van de verbindings pipe"
+ eng "Got a read error from the connection pipe"
+ est "Viga ühendustoru lugemisel"
+ fre "Erreur de lecture reçue du pipe de connexion"
+ ger "Lese-Fehler bei einer Verbindungs-Pipe"
+ hun "Olvasasi hiba a kapcsolat soran"
+ ita "Rilevato un errore di lettura dalla pipe di connessione"
+ kor "ì—°ê²° 파ì´í”„로부터 ì—러가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
+ por "Obteve um erro de leitura no 'pipe' da conexão"
+ rum "Eroare la citire din cauza lui 'connection pipe'"
+ rus "Получена ошибка Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð¾Ñ‚ потока ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ (connection pipe)"
+ serbian "GreÅ¡ka pri Äitanju podataka sa pipe-a"
+ spa "Obtenido un error de lectura de la conexión pipe"
+ swe "Fick läsfel från klienten vid läsning från 'PIPE'"
+ ukr "Отримано помилку Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ð· комунікаційного каналу"
+ER_NET_FCNTL_ERROR 08S01
+ cze "Zji-Bštěna chyba fcntl()"
+ dan "Fik fejlmeddelelse fra fcntl()"
+ nla "Kreeg fout van fcntl()"
+ eng "Got an error from fcntl()"
+ est "fcntl() tagastas vea"
+ fre "Erreur reçue de fcntl() "
+ ger "fcntl() lieferte einen Fehler"
+ hun "Hiba a fcntl() fuggvenyben"
+ ita "Rilevato un errore da fcntl()"
+ kor "fcntl() 함수로부터 ì—러가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
+ por "Obteve um erro em fcntl()"
+ rum "Eroare obtinuta de la fcntl()"
+ rus "Получена ошибка от fcntl()"
+ serbian "Greška pri izvršavanju funkcije fcntl()"
+ spa "Obtenido un error de fcntl()"
+ swe "Fick fatalt fel från 'fcntl()'"
+ ukr "Отримано помилкку від fcntl()"
+ER_NET_PACKETS_OUT_OF_ORDER 08S01
+ cze "P-Bříchozí packety v chybném pořadí"
+ dan "Modtog ikke datapakker i korrekt rækkefølge"
+ nla "Pakketten in verkeerde volgorde ontvangen"
+ eng "Got packets out of order"
+ est "Paketid saabusid vales järjekorras"
+ fre "Paquets reçus dans le désordre"
+ ger "Pakete nicht in der richtigen Reihenfolge empfangen"
+ hun "Helytelen sorrendben erkezett adatcsomagok"
+ ita "Ricevuti pacchetti non in ordine"
+ kor "순서가 맞지않는 íŒ¨í‚·ì„ ë°›ì•˜ìŠµë‹ˆë‹¤."
+ por "Obteve pacotes fora de ordem"
+ rum "Packets care nu sint ordonati au fost gasiti"
+ rus "Пакеты получены в неверном порÑдке"
+ serbian "Primio sam mrežne pakete van reda"
+ spa "Obtenido paquetes desordenados"
+ swe "Kommunikationspaketen kom i fel ordning"
+ ukr "Отримано пакети у неналежному порÑдку"
+ER_NET_UNCOMPRESS_ERROR 08S01
+ cze "Nemohu rozkomprimovat komunika-BÄní packet"
+ dan "Kunne ikke dekomprimere kommunikations-pakke (communication packet)"
+ nla "Communicatiepakket kon niet worden gedecomprimeerd"
+ eng "Couldn't uncompress communication packet"
+ est "Viga andmepaketi lahtipakkimisel"
+ fre "Impossible de décompresser le paquet reçu"
+ ger "Kommunikationspaket lässt sich nicht entpacken"
+ hun "A kommunikacios adatcsomagok nem tomorithetok ki"
+ ita "Impossibile scompattare i pacchetti di comunicazione"
+ kor "통신 íŒ¨í‚·ì˜ ì••ì¶•í•´ì œë¥¼ í•  수 없었습니다."
+ por "Não conseguiu descomprimir pacote de comunicação"
+ rum "Nu s-a putut decompresa pachetul de comunicatie (communication packet)"
+ rus "Ðевозможно раÑпаковать пакет, полученный через коммуникационный протокол"
+ serbian "Ne mogu da dekompresujem mrežne pakete"
+ spa "No puedo descomprimir paquetes de comunicación"
+ swe "Kunde inte packa up kommunikationspaketet"
+ ukr "Ðе можу декомпреÑувати комунікаційний пакет"
+ER_NET_READ_ERROR 08S01
+ cze "Zji-BÅ¡tÄ›na chyba pÅ™i Ätení komunikaÄního packetu"
+ dan "Fik fejlmeddelelse ved læsning af kommunikations-pakker (communication packets)"
+ nla "Fout bij het lezen van communicatiepakketten"
+ eng "Got an error reading communication packets"
+ est "Viga andmepaketi lugemisel"
+ fre "Erreur de lecture des paquets reçus"
+ ger "Fehler beim Lesen eines Kommunikationspakets"
+ hun "HIba a kommunikacios adatcsomagok olvasasa soran"
+ ita "Rilevato un errore ricevendo i pacchetti di comunicazione"
+ kor "통신 íŒ¨í‚·ì„ ì½ëŠ” 중 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
+ por "Obteve um erro na leitura de pacotes de comunicação"
+ rum "Eroare obtinuta citind pachetele de comunicatie (communication packets)"
+ rus "Получена ошибка в процеÑÑе Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¿Ð°ÐºÐµÑ‚Ð° через коммуникационный протокол "
+ serbian "Greška pri primanju mrežnih paketa"
+ spa "Obtenido un error leyendo paquetes de comunicación"
+ swe "Fick ett fel vid läsning från klienten"
+ ukr "Отримано помилку Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ ÐºÐ¾Ð¼ÑƒÐ½Ñ–ÐºÐ°Ñ†Ñ–Ð¹Ð½Ð¸Ñ… пакетів"
+ER_NET_READ_INTERRUPTED 08S01
+ cze "Zji-BÅ¡tÄ›n timeout pÅ™i Ätení komunikaÄního packetu"
+ dan "Timeout-fejl ved læsning af kommunukations-pakker (communication packets)"
+ nla "Timeout bij het lezen van communicatiepakketten"
+ eng "Got timeout reading communication packets"
+ est "Kontrollaja ületamine andmepakettide lugemisel"
+ fre "Timeout en lecture des paquets reçus"
+ ger "Zeitüberschreitung beim Lesen eines Kommunikationspakets"
+ hun "Idotullepes a kommunikacios adatcsomagok olvasasa soran"
+ ita "Rilevato un timeout ricevendo i pacchetti di comunicazione"
+ kor "통신 íŒ¨í‚·ì„ ì½ëŠ” 중 timeoutì´ ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
+ por "Obteve expiração de tempo (timeout) na leitura de pacotes de comunicação"
+ rum "Timeout obtinut citind pachetele de comunicatie (communication packets)"
+ rus "Получен таймаут Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð¿Ð°ÐºÐµÑ‚Ð° через коммуникационный протокол "
+ serbian "Vremenski limit za Äitanje mrežnih paketa je istekao"
+ spa "Obtenido timeout leyendo paquetes de comunicación"
+ swe "Fick 'timeout' vid läsning från klienten"
+ ukr "Отримано затримку Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ ÐºÐ¾Ð¼ÑƒÐ½Ñ–ÐºÐ°Ñ†Ñ–Ð¹Ð½Ð¸Ñ… пакетів"
+ER_NET_ERROR_ON_WRITE 08S01
+ cze "Zji-BÅ¡tÄ›na chyba pÅ™i zápisu komunikaÄního packetu"
+ dan "Fik fejlmeddelelse ved skrivning af kommunukations-pakker (communication packets)"
+ nla "Fout bij het schrijven van communicatiepakketten"
+ eng "Got an error writing communication packets"
+ est "Viga andmepaketi kirjutamisel"
+ fre "Erreur d'écriture des paquets envoyés"
+ ger "Fehler beim Schreiben eines Kommunikationspakets"
+ hun "Hiba a kommunikacios csomagok irasa soran"
+ ita "Rilevato un errore inviando i pacchetti di comunicazione"
+ kor "통신 íŒ¨í‚·ì„ ê¸°ë¡í•˜ëŠ” 중 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
+ por "Obteve um erro na escrita de pacotes de comunicação"
+ rum "Eroare in scrierea pachetelor de comunicatie (communication packets)"
+ rus "Получена ошибка при передаче пакета через коммуникационный протокол "
+ serbian "Greška pri slanju mrežnih paketa"
+ spa "Obtenido un error de escribiendo paquetes de comunicación"
+ swe "Fick ett fel vid skrivning till klienten"
+ ukr "Отримано помилку запиÑу комунікаційних пакетів"
+ER_NET_WRITE_INTERRUPTED 08S01
+ cze "Zji-BÅ¡tÄ›n timeout pÅ™i zápisu komunikaÄního packetu"
+ dan "Timeout-fejl ved skrivning af kommunukations-pakker (communication packets)"
+ nla "Timeout bij het schrijven van communicatiepakketten"
+ eng "Got timeout writing communication packets"
+ est "Kontrollaja ületamine andmepakettide kirjutamisel"
+ fre "Timeout d'écriture des paquets envoyés"
+ ger "Zeitüberschreitung beim Schreiben eines Kommunikationspakets"
+ hun "Idotullepes a kommunikacios csomagok irasa soran"
+ ita "Rilevato un timeout inviando i pacchetti di comunicazione"
+ kor "통신 íŒ¨íŒƒì„ ê¸°ë¡í•˜ëŠ” 중 timeoutì´ ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
+ por "Obteve expiração de tempo ('timeout') na escrita de pacotes de comunicação"
+ rum "Timeout obtinut scriind pachetele de comunicatie (communication packets)"
+ rus "Получен таймаут в процеÑÑе передачи пакета через коммуникационный протокол "
+ serbian "Vremenski limit za slanje mrežnih paketa je istekao"
+ spa "Obtenido timeout escribiendo paquetes de comunicación"
+ swe "Fick 'timeout' vid skrivning till klienten"
+ ukr "Отримано затримку запиÑу комунікаційних пакетів"
+ER_TOO_LONG_STRING 42000
+ cze "V-Býsledný řetězec je delší než 'max_allowed_packet'"
+ dan "Strengen med resultater er større end 'max_allowed_packet'"
+ nla "Resultaat string is langer dan 'max_allowed_packet'"
+ eng "Result string is longer than 'max_allowed_packet' bytes"
+ est "Tulemus on pikem kui lubatud 'max_allowed_packet' muutujaga"
+ fre "La chaîne résultat est plus grande que 'max_allowed_packet'"
+ ger "Ergebnis-String ist länger als 'max_allowed_packet' Bytes"
+ hun "Ez eredmeny sztring nagyobb, mint a lehetseges maximum: 'max_allowed_packet'"
+ ita "La stringa di risposta e` piu` lunga di 'max_allowed_packet'"
+ por "'String' resultante é mais longa do que 'max_allowed_packet'"
+ rum "Sirul rezultat este mai lung decit 'max_allowed_packet'"
+ rus "Ð ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð¸Ñ€ÑƒÑŽÑ‰Ð°Ñ Ñтрока больше, чем 'max_allowed_packet'"
+ serbian "RezultujuÄi string je duži nego Å¡to to dozvoljava parametar servera 'max_allowed_packet'"
+ spa "La string resultante es mayor que max_allowed_packet"
+ swe "Resultatsträngen är längre än max_allowed_packet"
+ ukr "Строка результату довша ніж max_allowed_packet"
+ER_TABLE_CANT_HANDLE_BLOB 42000
+ cze "Typ pou-Bžité tabulky nepodporuje BLOB/TEXT sloupce"
+ dan "Denne tabeltype understøtter ikke brug af BLOB og TEXT kolonner"
+ nla "Het gebruikte tabel type ondersteunt geen BLOB/TEXT kolommen"
+ eng "The used table type doesn't support BLOB/TEXT columns"
+ est "Valitud tabelitüüp ei toeta BLOB/TEXT tüüpi välju"
+ fre "Ce type de table ne supporte pas les colonnes BLOB/TEXT"
+ ger "Der verwendete Tabellentyp unterstützt keine BLOB- und TEXT-Felder"
+ hun "A hasznalt tabla tipus nem tamogatja a BLOB/TEXT mezoket"
+ ita "Il tipo di tabella usata non supporta colonne di tipo BLOB/TEXT"
+ por "Tipo de tabela usado não permite colunas BLOB/TEXT"
+ rum "Tipul de tabela folosit nu suporta coloane de tip BLOB/TEXT"
+ rus "ИÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÐµÐ¼Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð° не поддерживает типы BLOB/TEXT"
+ serbian "Iskorišteni tip tabele ne podržava kolone tipa 'BLOB' odnosno 'TEXT'"
+ spa "El tipo de tabla usada no permite soporte para columnas BLOB/TEXT"
+ swe "Den använda tabelltypen kan inte hantera BLOB/TEXT-kolumner"
+ ukr "ВикориÑтаний тип таблиці не підтримує BLOB/TEXT Ñтовбці"
+ER_TABLE_CANT_HANDLE_AUTO_INCREMENT 42000
+ cze "Typ pou-Bžité tabulky nepodporuje AUTO_INCREMENT sloupce"
+ dan "Denne tabeltype understøtter ikke brug af AUTO_INCREMENT kolonner"
+ nla "Het gebruikte tabel type ondersteunt geen AUTO_INCREMENT kolommen"
+ eng "The used table type doesn't support AUTO_INCREMENT columns"
+ est "Valitud tabelitüüp ei toeta AUTO_INCREMENT tüüpi välju"
+ fre "Ce type de table ne supporte pas les colonnes AUTO_INCREMENT"
+ ger "Der verwendete Tabellentyp unterstützt keine AUTO_INCREMENT-Felder"
+ hun "A hasznalt tabla tipus nem tamogatja az AUTO_INCREMENT tipusu mezoket"
+ ita "Il tipo di tabella usata non supporta colonne di tipo AUTO_INCREMENT"
+ por "Tipo de tabela usado não permite colunas AUTO_INCREMENT"
+ rum "Tipul de tabela folosit nu suporta coloane de tip AUTO_INCREMENT"
+ rus "ИÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÐµÐ¼Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð° не поддерживает автоинкрементные Ñтолбцы"
+ serbian "Iskorišteni tip tabele ne podržava kolone tipa 'AUTO_INCREMENT'"
+ spa "El tipo de tabla usada no permite soporte para columnas AUTO_INCREMENT"
+ swe "Den använda tabelltypen kan inte hantera AUTO_INCREMENT-kolumner"
+ ukr "ВикориÑтаний тип таблиці не підтримує AUTO_INCREMENT Ñтовбці"
+ER_DELAYED_INSERT_TABLE_LOCKED
+ cze "INSERT DELAYED nen-Bí možno s tabulkou '%-.192s' použít, protože je zamÄená pomocí LOCK TABLES"
+ dan "INSERT DELAYED kan ikke bruges med tabellen '%-.192s', fordi tabellen er låst med LOCK TABLES"
+ nla "INSERT DELAYED kan niet worden gebruikt bij table '%-.192s', vanwege een 'lock met LOCK TABLES"
+ eng "INSERT DELAYED can't be used with table '%-.192s' because it is locked with LOCK TABLES"
+ est "INSERT DELAYED ei saa kasutada tabeli '%-.192s' peal, kuna see on lukustatud LOCK TABLES käsuga"
+ fre "INSERT DELAYED ne peut être utilisé avec la table '%-.192s', car elle est verrouée avec LOCK TABLES"
+ ger "INSERT DELAYED kann für Tabelle '%-.192s' nicht verwendet werden, da sie mit LOCK TABLES gesperrt ist"
+ greek "INSERT DELAYED can't be used with table '%-.192s', because it is locked with LOCK TABLES"
+ hun "Az INSERT DELAYED nem hasznalhato a '%-.192s' tablahoz, mert a tabla zarolt (LOCK TABLES)"
+ ita "L'inserimento ritardato (INSERT DELAYED) non puo` essere usato con la tabella '%-.192s', perche` soggetta a lock da 'LOCK TABLES'"
+ jpn "INSERT DELAYED can't be used with table '%-.192s', because it is locked with LOCK TABLES"
+ kor "INSERT DELAYED can't be used with table '%-.192s', because it is locked with LOCK TABLES"
+ nor "INSERT DELAYED can't be used with table '%-.192s', because it is locked with LOCK TABLES"
+ norwegian-ny "INSERT DELAYED can't be used with table '%-.192s', because it is locked with LOCK TABLES"
+ pol "INSERT DELAYED can't be used with table '%-.192s', because it is locked with LOCK TABLES"
+ por "INSERT DELAYED não pode ser usado com a tabela '%-.192s', porque ela está travada com LOCK TABLES"
+ rum "INSERT DELAYED nu poate fi folosit cu tabela '%-.192s', deoarece este locked folosing LOCK TABLES"
+ rus "ÐÐµÐ»ÑŒÐ·Ñ Ð¸Ñпользовать INSERT DELAYED Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ '%-.192s', потому что она заблокирована Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ LOCK TABLES"
+ serbian "Komanda 'INSERT DELAYED' ne može biti iskoriÅ¡tena u tabeli '%-.192s', zbog toga Å¡to je zakljuÄana komandom 'LOCK TABLES'"
+ slo "INSERT DELAYED can't be used with table '%-.192s', because it is locked with LOCK TABLES"
+ spa "INSERT DELAYED no puede ser usado con tablas '%-.192s', porque esta bloqueada con LOCK TABLES"
+ swe "INSERT DELAYED kan inte användas med tabell '%-.192s', emedan den är låst med LOCK TABLES"
+ ukr "INSERT DELAYED не може бути викориÑтано з таблицею '%-.192s', тому що Ñ—Ñ— заблоковано з LOCK TABLES"
+ER_WRONG_COLUMN_NAME 42000
+ cze "Nespr-Bávné jméno sloupce '%-.100s'"
+ dan "Forkert kolonnenavn '%-.100s'"
+ nla "Incorrecte kolom naam '%-.100s'"
+ eng "Incorrect column name '%-.100s'"
+ est "Vigane tulba nimi '%-.100s'"
+ fre "Nom de colonne '%-.100s' incorrect"
+ ger "Falscher Spaltenname '%-.100s'"
+ hun "Ervenytelen mezonev: '%-.100s'"
+ ita "Nome colonna '%-.100s' non corretto"
+ por "Nome de coluna '%-.100s' incorreto"
+ rum "Nume increct de coloana '%-.100s'"
+ rus "Ðеверное Ð¸Ð¼Ñ Ñтолбца '%-.100s'"
+ serbian "Pogrešno ime kolone '%-.100s'"
+ spa "Incorrecto nombre de columna '%-.100s'"
+ swe "Felaktigt kolumnnamn '%-.100s'"
+ ukr "Ðевірне ім'Ñ ÑÑ‚Ð¾Ð²Ð±Ñ†Ñ '%-.100s'"
+ER_WRONG_KEY_COLUMN 42000
+ cze "Handler pou-Bžité tabulky neumí indexovat sloupce '%-.192s'"
+ dan "Den brugte tabeltype kan ikke indeksere kolonnen '%-.192s'"
+ nla "De gebruikte tabel 'handler' kan kolom '%-.192s' niet indexeren"
+ eng "The used storage engine can't index column '%-.192s'"
+ est "Tabelihandler ei oska indekseerida tulpa '%-.192s'"
+ fre "Le handler de la table ne peut indexé la colonne '%-.192s'"
+ ger "Die verwendete Speicher-Engine kann die Spalte '%-.192s' nicht indizieren"
+ greek "The used table handler can't index column '%-.192s'"
+ hun "A hasznalt tablakezelo nem tudja a '%-.192s' mezot indexelni"
+ ita "Il gestore delle tabelle non puo` indicizzare la colonna '%-.192s'"
+ jpn "The used table handler can't index column '%-.192s'"
+ kor "The used table handler can't index column '%-.192s'"
+ nor "The used table handler can't index column '%-.192s'"
+ norwegian-ny "The used table handler can't index column '%-.192s'"
+ pol "The used table handler can't index column '%-.192s'"
+ por "O manipulador de tabela usado não pode indexar a coluna '%-.192s'"
+ rum "Handler-ul tabelei folosite nu poate indexa coloana '%-.192s'"
+ rus "ИÑпользованный обработчик таблицы не может проиндекÑировать Ñтолбец '%-.192s'"
+ serbian "Handler tabele ne može da indeksira kolonu '%-.192s'"
+ slo "The used table handler can't index column '%-.192s'"
+ spa "El manipulador de tabla usado no puede indexar columna '%-.192s'"
+ swe "Den använda tabelltypen kan inte indexera kolumn '%-.192s'"
+ ukr "ВикориÑтаний вказівник таблиці не може індекÑувати Ñтовбець '%-.192s'"
+ER_WRONG_MRG_TABLE
+ cze "V-Bšechny tabulky v MERGE tabulce nejsou definovány stejně"
+ dan "Tabellerne i MERGE er ikke defineret ens"
+ nla "Niet alle tabellen in de MERGE tabel hebben identieke gedefinities"
+ eng "Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist"
+ est "Kõik tabelid MERGE tabeli määratluses ei ole identsed"
+ fre "Toutes les tables de la table de type MERGE n'ont pas la même définition"
+ ger "Nicht alle Tabellen in der MERGE-Tabelle sind gleich definiert"
+ hun "A MERGE tablaban talalhato tablak definicioja nem azonos"
+ ita "Non tutte le tabelle nella tabella di MERGE sono definite in maniera identica"
+ jpn "All tables in the MERGE table are not defined identically"
+ kor "All tables in the MERGE table are not defined identically"
+ nor "All tables in the MERGE table are not defined identically"
+ norwegian-ny "All tables in the MERGE table are not defined identically"
+ pol "All tables in the MERGE table are not defined identically"
+ por "Todas as tabelas contidas na tabela fundida (MERGE) não estão definidas identicamente"
+ rum "Toate tabelele din tabela MERGE nu sint definite identic"
+ rus "Ðе вÑе таблицы в MERGE определены одинаково"
+ serbian "Tabele iskoriÅ¡tene u 'MERGE' tabeli nisu definisane na isti naÄin"
+ slo "All tables in the MERGE table are not defined identically"
+ spa "Todas las tablas en la MERGE tabla no estan definidas identicamente"
+ swe "Tabellerna i MERGE-tabellen är inte identiskt definierade"
+ ukr "Таблиці у MERGE TABLE мають різну Ñтруктуру"
+ER_DUP_UNIQUE 23000
+ cze "Kv-Bůli unique constraintu nemozu zapsat do tabulky '%-.192s'"
+ dan "Kan ikke skrive til tabellen '%-.192s' fordi det vil bryde CONSTRAINT regler"
+ nla "Kan niet opslaan naar table '%-.192s' vanwege 'unique' beperking"
+ eng "Can't write, because of unique constraint, to table '%-.192s'"
+ est "Ei suuda kirjutada tabelisse '%-.192s', kuna see rikub ühesuse kitsendust"
+ fre "Écriture impossible à cause d'un index UNIQUE sur la table '%-.192s'"
+ ger "Schreiben in Tabelle '%-.192s' nicht möglich wegen einer Eindeutigkeitsbeschränkung (unique constraint)"
+ hun "A '%-.192s' nem irhato, az egyedi mezok miatt"
+ ita "Impossibile scrivere nella tabella '%-.192s' per limitazione di unicita`"
+ por "Não pode gravar, devido à restrição UNIQUE, na tabela '%-.192s'"
+ rum "Nu pot scrie pe hard-drive, din cauza constraintului unic (unique constraint) pentru tabela '%-.192s'"
+ rus "Ðевозможно запиÑать в таблицу '%-.192s' из-за ограничений уникального ключа"
+ serbian "Zbog provere jedinstvenosti ne mogu da upišem podatke u tabelu '%-.192s'"
+ spa "No puedo escribir, debido al único constraint, para tabla '%-.192s'"
+ swe "Kan inte skriva till tabell '%-.192s'; UNIQUE-test"
+ ukr "Ðе можу запиÑати до таблиці '%-.192s', з причини вимог унікальноÑÑ‚Ñ–"
+ER_BLOB_KEY_WITHOUT_LENGTH 42000
+ cze "BLOB sloupec '%-.192s' je pou-Bžit ve specifikaci klíÄe bez délky"
+ dan "BLOB kolonnen '%-.192s' brugt i nøglespecifikation uden nøglelængde"
+ nla "BLOB kolom '%-.192s' gebruikt in zoeksleutel specificatie zonder zoeksleutel lengte"
+ eng "BLOB/TEXT column '%-.192s' used in key specification without a key length"
+ est "BLOB-tüüpi tulp '%-.192s' on kasutusel võtmes ilma pikkust määratlemata"
+ fre "La colonne '%-.192s' de type BLOB est utilisée dans une définition d'index sans longueur d'index"
+ ger "BLOB- oder TEXT-Spalte '%-.192s' wird in der Schlüsseldefinition ohne Schlüssellängenangabe verwendet"
+ greek "BLOB column '%-.192s' used in key specification without a key length"
+ hun "BLOB mezo '%-.192s' hasznalt a mezo specifikacioban, a mezohossz megadasa nelkul"
+ ita "La colonna '%-.192s' di tipo BLOB e` usata in una chiave senza specificarne la lunghezza"
+ jpn "BLOB column '%-.192s' used in key specification without a key length"
+ kor "BLOB column '%-.192s' used in key specification without a key length"
+ nor "BLOB column '%-.192s' used in key specification without a key length"
+ norwegian-ny "BLOB column '%-.192s' used in key specification without a key length"
+ pol "BLOB column '%-.192s' used in key specification without a key length"
+ por "Coluna BLOB '%-.192s' usada na especificação de chave sem o comprimento da chave"
+ rum "Coloana BLOB '%-.192s' este folosita in specificarea unei chei fara ca o lungime de cheie sa fie folosita"
+ rus "Столбец типа BLOB '%-.192s' был указан в определении ключа без ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð´Ð»Ð¸Ð½Ñ‹ ключа"
+ serbian "BLOB kolona '%-.192s' je upotrebljena u specifikaciji kljuÄa bez navoÄ‘enja dužine kljuÄa"
+ slo "BLOB column '%-.192s' used in key specification without a key length"
+ spa "Columna BLOB column '%-.192s' usada en especificación de clave sin tamaño de la clave"
+ swe "Du har inte angett någon nyckellängd för BLOB '%-.192s'"
+ ukr "Стовбець BLOB '%-.192s' викориÑтано у визначенні ключа без Ð²ÐºÐ°Ð·Ð°Ð½Ð½Ñ Ð´Ð¾Ð²Ð¶Ð¸Ð½Ð¸ ключа"
+ER_PRIMARY_CANT_HAVE_NULL 42000
+ cze "V-BÅ¡echny Äásti primárního klíÄe musejí být NOT NULL; pokud potÅ™ebujete NULL, použijte UNIQUE"
+ dan "Alle dele af en PRIMARY KEY skal være NOT NULL; Hvis du skal bruge NULL i nøglen, brug UNIQUE istedet"
+ nla "Alle delen van een PRIMARY KEY moeten NOT NULL zijn; Indien u NULL in een zoeksleutel nodig heeft kunt u UNIQUE gebruiken"
+ eng "All parts of a PRIMARY KEY must be NOT NULL; if you need NULL in a key, use UNIQUE instead"
+ est "Kõik PRIMARY KEY peavad olema määratletud NOT NULL piiranguga; vajadusel kasuta UNIQUE tüüpi võtit"
+ fre "Toutes les parties d'un index PRIMARY KEY doivent être NOT NULL; Si vous avez besoin d'un NULL dans l'index, utilisez un index UNIQUE"
+ ger "Alle Teile eines PRIMARY KEY müssen als NOT NULL definiert sein. Wenn NULL in einem Schlüssel benötigt wird, muss ein UNIQUE-Schlüssel verwendet werden"
+ hun "Az elsodleges kulcs teljes egeszeben csak NOT NULL tipusu lehet; Ha NULL mezot szeretne a kulcskent, hasznalja inkabb a UNIQUE-ot"
+ ita "Tutte le parti di una chiave primaria devono essere dichiarate NOT NULL; se necessitano valori NULL nelle chiavi utilizzare UNIQUE"
+ por "Todas as partes de uma chave primária devem ser não-nulas. Se você precisou usar um valor nulo (NULL) em uma chave, use a cláusula UNIQUE em seu lugar"
+ rum "Toate partile unei chei primare (PRIMARY KEY) trebuie sa fie NOT NULL; Daca aveti nevoie de NULL in vreo cheie, folositi UNIQUE in schimb"
+ rus "Ð’Ñе чаÑти первичного ключа (PRIMARY KEY) должны быть определены как NOT NULL; ЕÑли вам нужна поддержка величин NULL в ключе, воÑпользуйтеÑÑŒ индекÑом UNIQUE"
+ serbian "Svi delovi primarnog kljuÄa moraju biti razliÄiti od NULL; Ako Vam ipak treba NULL vrednost u kljuÄu, upotrebite 'UNIQUE'"
+ spa "Todas las partes de un PRIMARY KEY deben ser NOT NULL; Si necesitas NULL en una clave, use UNIQUE"
+ swe "Alla delar av en PRIMARY KEY måste vara NOT NULL; Om du vill ha en nyckel med NULL, använd UNIQUE istället"
+ ukr "УÑÑ– чаÑтини PRIMARY KEY повинні бути NOT NULL; Якщо ви потребуєте NULL у ключі, ÑкориÑтайтеÑÑ UNIQUE"
+ER_TOO_MANY_ROWS 42000
+ cze "V-Býsledek obsahuje více než jeden řádek"
+ dan "Resultatet bestod af mere end een række"
+ nla "Resultaat bevatte meer dan een rij"
+ eng "Result consisted of more than one row"
+ est "Tulemis oli rohkem kui üks kirje"
+ fre "Le résultat contient plus d'un enregistrement"
+ ger "Ergebnis besteht aus mehr als einer Zeile"
+ hun "Az eredmeny tobb, mint egy sort tartalmaz"
+ ita "Il risultato consiste di piu` di una riga"
+ por "O resultado consistiu em mais do que uma linha"
+ rum "Resultatul constista din mai multe linii"
+ rus "Ð’ результате возвращена более чем одна Ñтрока"
+ serbian "Rezultat je saÄinjen od viÅ¡e slogova"
+ spa "Resultado compuesto de mas que una línea"
+ swe "Resultet bestod av mera än en rad"
+ ukr "Результат знаходитьÑÑ Ñƒ більше ніж одній Ñтроці"
+ER_REQUIRES_PRIMARY_KEY 42000
+ cze "Tento typ tabulky vy-Bžaduje primární klíÄ"
+ dan "Denne tabeltype kræver en primærnøgle"
+ nla "Dit tabel type heeft een primaire zoeksleutel nodig"
+ eng "This table type requires a primary key"
+ est "Antud tabelitüüp nõuab primaarset võtit"
+ fre "Ce type de table nécessite une clé primaire (PRIMARY KEY)"
+ ger "Dieser Tabellentyp benötigt einen Primärschlüssel (PRIMARY KEY)"
+ hun "Az adott tablatipushoz elsodleges kulcs hasznalata kotelezo"
+ ita "Questo tipo di tabella richiede una chiave primaria"
+ por "Este tipo de tabela requer uma chave primária"
+ rum "Aceast tip de tabela are nevoie de o cheie primara"
+ rus "Этот тип таблицы требует Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ð¿ÐµÑ€Ð²Ð¸Ñ‡Ð½Ð¾Ð³Ð¾ ключа"
+ serbian "Ovaj tip tabele zahteva da imate definisan primarni kljuÄ"
+ spa "Este tipo de tabla necesita de una primary key"
+ swe "Denna tabelltyp kräver en PRIMARY KEY"
+ ukr "Цей тип таблиці потребує первинного ключа"
+ER_NO_RAID_COMPILED
+ cze "Tato verze MariaDB nen-Bí 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"
+ est "Antud MariaDB versioon on kompileeritud ilma RAID toeta"
+ fre "Cette version de MariaDB n'est pas compilée avec le support RAID"
+ ger "Diese MariaDB-Version ist nicht mit RAID-Unterstützung kompiliert"
+ hun "Ezen leforditott MariaDB verzio nem tartalmaz RAID support-ot"
+ ita "Questa versione di MYSQL non e` compilata con il supporto RAID"
+ 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"
+ serbian "Ova verzija MariaDB servera nije kompajlirana sa podrškom za RAID uređaje"
+ spa "Esta versión de MariaDB no es compilada con soporte RAID"
+ swe "Denna version av MariaDB är inte kompilerad med RAID"
+ ukr "Ð¦Ñ Ð²ÐµÑ€ÑÑ–Ñ MariaDB не зкомпільована з підтримкою RAID"
+ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE
+ cze "Update tabulky bez WHERE s kl-BíÄem není v módu bezpeÄných update dovoleno"
+ dan "Du bruger sikker opdaterings modus ('safe update mode') og du forsøgte at opdatere en tabel uden en WHERE klausul, der gør brug af et KEY felt"
+ nla "U gebruikt 'safe update mode' en u probeerde een tabel te updaten zonder een WHERE met een KEY kolom"
+ eng "You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column"
+ est "Katse muuta tabelit turvalises rezhiimis ilma WHERE klauslita"
+ fre "Vous êtes en mode 'safe update' et vous essayez de faire un UPDATE sans clause WHERE utilisant un index"
+ ger "MariaDB läuft im sicheren Aktualisierungsmodus (safe update mode). Sie haben versucht, eine Tabelle zu aktualisieren, ohne in der WHERE-Klausel ein KEY-Feld anzugeben"
+ hun "On a biztonsagos update modot hasznalja, es WHERE that uses a KEY column"
+ ita "In modalita` 'safe update' si e` cercato di aggiornare una tabella senza clausola WHERE su una chiave"
+ por "Você está usando modo de atualização seguro e tentou atualizar uma tabela sem uma cláusula WHERE que use uma coluna chave"
+ rus "Ð’Ñ‹ работаете в режиме безопаÑных обновлений (safe update mode) и попробовали изменить таблицу без иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ ÐºÐ»ÑŽÑ‡ÐµÐ²Ð¾Ð³Ð¾ Ñтолбца в чаÑти WHERE"
+ serbian "Vi koristite safe update mod servera, a probali ste da promenite podatke bez 'WHERE' komande koja koristi kolonu kljuÄa"
+ spa "Tu estás usando modo de actualización segura y tentado actualizar una tabla sin un WHERE que usa una KEY columna"
+ swe "Du använder 'säker uppdateringsmod' och försökte uppdatera en tabell utan en WHERE-sats som använder sig av en nyckel"
+ ukr "Ви у режимі безпечного Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ‚Ð° намагаєтеÑÑŒ оновити таблицю без оператора WHERE, що викориÑтовує KEY Ñтовбець"
+ER_KEY_DOES_NOT_EXITS 42000 S1009
+ cze "Kl-BÃ­Ä '%-.192s' v tabulce '%-.192s' neexistuje"
+ dan "Nøglen '%-.192s' eksisterer ikke i tabellen '%-.192s'"
+ nla "Zoeksleutel '%-.192s' bestaat niet in tabel '%-.192s'"
+ eng "Key '%-.192s' doesn't exist in table '%-.192s'"
+ est "Võti '%-.192s' ei eksisteeri tabelis '%-.192s'"
+ fre "L'index '%-.192s' n'existe pas sur la table '%-.192s'"
+ ger "Schlüssel '%-.192s' existiert in der Tabelle '%-.192s' nicht"
+ hun "A '%-.192s' kulcs nem letezik a '%-.192s' tablaban"
+ ita "La chiave '%-.192s' non esiste nella tabella '%-.192s'"
+ por "Chave '%-.192s' não existe na tabela '%-.192s'"
+ rus "Ключ '%-.192s' не ÑущеÑтвует в таблице '%-.192s'"
+ serbian "KljuÄ '%-.192s' ne postoji u tabeli '%-.192s'"
+ spa "Clave '%-.192s' no existe en la tabla '%-.192s'"
+ swe "Nyckel '%-.192s' finns inte in tabell '%-.192s'"
+ ukr "Ключ '%-.192s' не Ñ–Ñнує в таблиці '%-.192s'"
+ER_CHECK_NO_SUCH_TABLE 42000
+ cze "Nemohu otev-Břít tabulku"
+ dan "Kan ikke åbne tabellen"
+ nla "Kan tabel niet openen"
+ eng "Can't open table"
+ est "Ei suuda avada tabelit"
+ fre "Impossible d'ouvrir la table"
+ ger "Kann Tabelle nicht öffnen"
+ hun "Nem tudom megnyitni a tablat"
+ ita "Impossibile aprire la tabella"
+ por "Não pode abrir a tabela"
+ rus "Ðевозможно открыть таблицу"
+ serbian "Ne mogu da otvorim tabelu"
+ spa "No puedo abrir tabla"
+ swe "Kan inte öppna tabellen"
+ ukr "Ðе можу відкрити таблицю"
+ER_CHECK_NOT_IMPLEMENTED 42000
+ cze "Handler tabulky nepodporuje %s"
+ dan "Denne tabeltype understøtter ikke %s"
+ nla "De 'handler' voor de tabel ondersteund geen %s"
+ eng "The storage engine for the table doesn't support %s"
+ est "Antud tabelitüüp ei toeta %s käske"
+ fre "Ce type de table ne supporte pas les %s"
+ ger "Die Speicher-Engine für diese Tabelle unterstützt kein %s"
+ greek "The handler for the table doesn't support %s"
+ hun "A tabla kezeloje (handler) nem tamogatja az %s"
+ ita "Il gestore per la tabella non supporta il %s"
+ jpn "The handler for the table doesn't support %s"
+ kor "The handler for the table doesn't support %s"
+ nor "The handler for the table doesn't support %s"
+ norwegian-ny "The handler for the table doesn't support %s"
+ pol "The handler for the table doesn't support %s"
+ por "O manipulador de tabela não suporta %s"
+ rum "The handler for the table doesn't support %s"
+ rus "Обработчик таблицы не поддерживает Ñтого: %s"
+ serbian "Handler za ovu tabelu ne dozvoljava %s komande"
+ slo "The handler for the table doesn't support %s"
+ spa "El manipulador de la tabla no permite soporte para %s"
+ swe "Tabellhanteraren för denna tabell kan inte göra %s"
+ ukr "Вказівник таблиці не підтримуе %s"
+ER_CANT_DO_THIS_DURING_AN_TRANSACTION 25000
+ cze "Proveden-Bí tohoto příkazu není v transakci dovoleno"
+ dan "Du må ikke bruge denne kommando i en transaktion"
+ nla "Het is u niet toegestaan dit commando uit te voeren binnen een transactie"
+ eng "You are not allowed to execute this command in a transaction"
+ est "Seda käsku ei saa kasutada transaktsiooni sees"
+ fre "Vous n'êtes pas autorisé à exécute cette commande dans une transaction"
+ ger "Sie dürfen diesen Befehl nicht in einer Transaktion ausführen"
+ hun "Az On szamara nem engedelyezett a parancs vegrehajtasa a tranzakcioban"
+ ita "Non puoi eseguire questo comando in una transazione"
+ por "Não lhe é permitido executar este comando em uma transação"
+ rus "Вам не разрешено выполнÑÑ‚ÑŒ Ñту команду в транзакции"
+ serbian "Nije Vam dozvoljeno da izvršite ovu komandu u transakciji"
+ spa "No tienes el permiso para ejecutar este comando en una transición"
+ swe "Du får inte utföra detta kommando i en transaktion"
+ ukr "Вам не дозволено виконувати цю команду в транзакції"
+ER_ERROR_DURING_COMMIT
+ cze "Chyba %d p-Bři COMMIT"
+ dan "Modtog fejl %d mens kommandoen COMMIT blev udført"
+ nla "Kreeg fout %d tijdens COMMIT"
+ eng "Got error %d during COMMIT"
+ est "Viga %d käsu COMMIT täitmisel"
+ fre "Erreur %d lors du COMMIT"
+ ger "Fehler %d beim COMMIT"
+ hun "%d hiba a COMMIT vegrehajtasa soran"
+ ita "Rilevato l'errore %d durante il COMMIT"
+ por "Obteve erro %d durante COMMIT"
+ rus "Получена ошибка %d в процеÑÑе COMMIT"
+ serbian "Greška %d za vreme izvršavanja komande 'COMMIT'"
+ spa "Obtenido error %d durante COMMIT"
+ swe "Fick fel %d vid COMMIT"
+ ukr "Отримано помилку %d під Ñ‡Ð°Ñ COMMIT"
+ER_ERROR_DURING_ROLLBACK
+ cze "Chyba %d p-Bři ROLLBACK"
+ dan "Modtog fejl %d mens kommandoen ROLLBACK blev udført"
+ nla "Kreeg fout %d tijdens ROLLBACK"
+ eng "Got error %d during ROLLBACK"
+ est "Viga %d käsu ROLLBACK täitmisel"
+ fre "Erreur %d lors du ROLLBACK"
+ ger "Fehler %d beim ROLLBACK"
+ hun "%d hiba a ROLLBACK vegrehajtasa soran"
+ ita "Rilevato l'errore %d durante il ROLLBACK"
+ por "Obteve erro %d durante ROLLBACK"
+ rus "Получена ошибка %d в процеÑÑе ROLLBACK"
+ serbian "Greška %d za vreme izvršavanja komande 'ROLLBACK'"
+ spa "Obtenido error %d durante ROLLBACK"
+ swe "Fick fel %d vid ROLLBACK"
+ ukr "Отримано помилку %d під Ñ‡Ð°Ñ ROLLBACK"
+ER_ERROR_DURING_FLUSH_LOGS
+ cze "Chyba %d p-Bři FLUSH_LOGS"
+ dan "Modtog fejl %d mens kommandoen FLUSH_LOGS blev udført"
+ nla "Kreeg fout %d tijdens FLUSH_LOGS"
+ eng "Got error %d during FLUSH_LOGS"
+ est "Viga %d käsu FLUSH_LOGS täitmisel"
+ fre "Erreur %d lors du FLUSH_LOGS"
+ ger "Fehler %d bei FLUSH_LOGS"
+ hun "%d hiba a FLUSH_LOGS vegrehajtasa soran"
+ ita "Rilevato l'errore %d durante il FLUSH_LOGS"
+ por "Obteve erro %d durante FLUSH_LOGS"
+ rus "Получена ошибка %d в процеÑÑе FLUSH_LOGS"
+ serbian "Greška %d za vreme izvršavanja komande 'FLUSH_LOGS'"
+ spa "Obtenido error %d durante FLUSH_LOGS"
+ swe "Fick fel %d vid FLUSH_LOGS"
+ ukr "Отримано помилку %d під Ñ‡Ð°Ñ FLUSH_LOGS"
+ER_ERROR_DURING_CHECKPOINT
+ cze "Chyba %d p-Bři CHECKPOINT"
+ dan "Modtog fejl %d mens kommandoen CHECKPOINT blev udført"
+ nla "Kreeg fout %d tijdens CHECKPOINT"
+ eng "Got error %d during CHECKPOINT"
+ est "Viga %d käsu CHECKPOINT täitmisel"
+ fre "Erreur %d lors du CHECKPOINT"
+ ger "Fehler %d bei CHECKPOINT"
+ hun "%d hiba a CHECKPOINT vegrehajtasa soran"
+ ita "Rilevato l'errore %d durante il CHECKPOINT"
+ por "Obteve erro %d durante CHECKPOINT"
+ rus "Получена ошибка %d в процеÑÑе CHECKPOINT"
+ serbian "Greška %d za vreme izvršavanja komande 'CHECKPOINT'"
+ spa "Obtenido error %d durante CHECKPOINT"
+ swe "Fick fel %d vid CHECKPOINT"
+ ukr "Отримано помилку %d під Ñ‡Ð°Ñ CHECKPOINT"
+ER_NEW_ABORTING_CONNECTION 08S01
+ cze "Spojen-Bí %ld do databáze: '%-.192s' uživatel: '%-.48s' stroj: '%-.64s' (%-.64s) bylo přerušeno"
+ dan "Afbrød forbindelsen %ld til databasen '%-.192s' bruger: '%-.48s' vært: '%-.64s' (%-.64s)"
+ nla "Afgebroken verbinding %ld naar db: '%-.192s' gebruiker: '%-.48s' host: '%-.64s' (%-.64s)"
+ eng "Aborted connection %ld to db: '%-.192s' user: '%-.48s' host: '%-.64s' (%-.64s)"
+ est "Ãœhendus katkestatud %ld andmebaas: '%-.192s' kasutaja: '%-.48s' masin: '%-.64s' (%-.64s)"
+ fre "Connection %ld avortée vers la bd: '%-.192s' utilisateur: '%-.48s' hôte: '%-.64s' (%-.64s)"
+ ger "Abbruch der Verbindung %ld zur Datenbank '%-.192s'. Benutzer: '%-.48s', Host: '%-.64s' (%-.64s)"
+ ita "Interrotta la connessione %ld al db: ''%-.192s' utente: '%-.48s' host: '%-.64s' (%-.64s)"
+ por "Conexão %ld abortada para banco de dados '%-.192s' - usuário '%-.48s' - 'host' '%-.64s' ('%-.64s')"
+ rus "Прервано Ñоединение %ld к базе данных '%-.192s' Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ '%-.48s' Ñ Ñ…Ð¾Ñта '%-.64s' (%-.64s)"
+ serbian "Prekinuta konekcija broj %ld ka bazi: '%-.192s' korisnik je bio: '%-.48s' a host: '%-.64s' (%-.64s)"
+ spa "Abortada conexión %ld para db: '%-.192s' usuario: '%-.48s' servidor: '%-.64s' (%-.64s)"
+ swe "Avbröt länken för tråd %ld till db '%-.192s', användare '%-.48s', host '%-.64s' (%-.64s)"
+ ukr "Перервано з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ %ld до бази данних: '%-.192s' кориÑтувач: '%-.48s' хоÑÑ‚: '%-.64s' (%-.64s)"
+ER_DUMP_NOT_IMPLEMENTED
+ cze "Handler tabulky nepodporuje bin-Bární dump"
+ dan "Denne tabeltype unserstøtter ikke binært tabeldump"
+ nla "De 'handler' voor de tabel ondersteund geen binaire tabel dump"
+ eng "The storage engine for the table does not support binary table dump"
+ fre "Ce type de table ne supporte pas les copies binaires"
+ ger "Die Speicher-Engine für die Tabelle unterstützt keinen binären Tabellen-Dump"
+ ita "Il gestore per la tabella non supporta il dump binario"
+ jpn "The handler for the table does not support binary table dump"
+ por "O manipulador de tabela não suporta 'dump' binário de tabela"
+ rum "The handler for the table does not support binary table dump"
+ rus "Обработчик Ñтой таблицы не поддерживает двоичного ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¾Ð±Ñ€Ð°Ð·Ð° таблицы (dump)"
+ serbian "Handler tabele ne podržava binarni dump tabele"
+ spa "El manipulador de tabla no soporta dump para tabla binaria"
+ swe "Tabellhanteraren klarar inte en binär kopiering av tabellen"
+ ukr "Цей тип таблиці не підтримує бінарну передачу таблиці"
+ER_FLUSH_MASTER_BINLOG_CLOSED
+ eng "Binlog closed, cannot RESET MASTER"
+ ger "Binlog geschlossen. Kann RESET MASTER nicht ausführen"
+ por "Binlog fechado. Não pode fazer RESET MASTER"
+ rus "Двоичный журнал Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð·Ð°ÐºÑ€Ñ‹Ñ‚, невозможно выполнить RESET MASTER"
+ serbian "Binarni log file zatvoren, ne mogu da izvršim komandu 'RESET MASTER'"
+ ukr "Реплікаційний лог закрито, не можу виконати RESET MASTER"
+ER_INDEX_REBUILD
+ cze "P-Břebudování indexu dumpnuté tabulky '%-.192s' nebylo úspěšné"
+ dan "Kunne ikke genopbygge indekset for den dumpede tabel '%-.192s'"
+ nla "Gefaald tijdens heropbouw index van gedumpte tabel '%-.192s'"
+ eng "Failed rebuilding the index of dumped table '%-.192s'"
+ fre "La reconstruction de l'index de la table copiée '%-.192s' a échoué"
+ ger "Neuerstellung des Index der Dump-Tabelle '%-.192s' fehlgeschlagen"
+ greek "Failed rebuilding the index of dumped table '%-.192s'"
+ hun "Failed rebuilding the index of dumped table '%-.192s'"
+ ita "Fallita la ricostruzione dell'indice della tabella copiata '%-.192s'"
+ por "Falhou na reconstrução do índice da tabela 'dumped' '%-.192s'"
+ rus "Ошибка переÑтройки индекÑа Ñохраненной таблицы '%-.192s'"
+ serbian "Izgradnja indeksa dump-ovane tabele '%-.192s' nije uspela"
+ spa "Falla reconstruyendo el indice de la tabla dumped '%-.192s'"
+ ukr "Ðевдале Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ–Ð½Ð´ÐµÐºÑа переданої таблиці '%-.192s'"
+ER_MASTER
+ cze "Chyba masteru: '%-.64s'"
+ dan "Fejl fra master: '%-.64s'"
+ nla "Fout van master: '%-.64s'"
+ eng "Error from master: '%-.64s'"
+ fre "Erreur reçue du maître: '%-.64s'"
+ ger "Fehler vom Master: '%-.64s'"
+ ita "Errore dal master: '%-.64s"
+ por "Erro no 'master' '%-.64s'"
+ rus "Ошибка от головного Ñервера: '%-.64s'"
+ serbian "Greška iz glavnog servera '%-.64s' u klasteru"
+ spa "Error del master: '%-.64s'"
+ swe "Fick en master: '%-.64s'"
+ ukr "Помилка від головного: '%-.64s'"
+ER_MASTER_NET_READ 08S01
+ cze "S-Bíťová chyba pÅ™i Ätení z masteru"
+ dan "Netværksfejl ved læsning fra master"
+ nla "Net fout tijdens lezen van master"
+ eng "Net error reading from master"
+ fre "Erreur de lecture réseau reçue du maître"
+ ger "Netzfehler beim Lesen vom Master"
+ ita "Errore di rete durante la ricezione dal master"
+ por "Erro de rede lendo do 'master'"
+ rus "Возникла ошибка Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð² процеÑÑе коммуникации Ñ Ð³Ð¾Ð»Ð¾Ð²Ð½Ñ‹Ð¼ Ñервером"
+ serbian "Greška u primanju mrežnih paketa sa glavnog servera u klasteru"
+ spa "Error de red leyendo del master"
+ swe "Fick nätverksfel vid läsning från master"
+ ukr "Мережева помилка Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ð²Ñ–Ð´ головного"
+ER_MASTER_NET_WRITE 08S01
+ cze "S-Bíťová chyba při zápisu na master"
+ dan "Netværksfejl ved skrivning til master"
+ nla "Net fout tijdens schrijven naar master"
+ eng "Net error writing to master"
+ fre "Erreur d'écriture réseau reçue du maître"
+ ger "Netzfehler beim Schreiben zum Master"
+ ita "Errore di rete durante l'invio al master"
+ por "Erro de rede gravando no 'master'"
+ rus "Возникла ошибка запиÑи в процеÑÑе коммуникации Ñ Ð³Ð¾Ð»Ð¾Ð²Ð½Ñ‹Ð¼ Ñервером"
+ serbian "Greška u slanju mrežnih paketa na glavni server u klasteru"
+ spa "Error de red escribiendo para el master"
+ swe "Fick nätverksfel vid skrivning till master"
+ ukr "Мережева помилка запиÑу до головного"
+ER_FT_MATCHING_KEY_NOT_FOUND
+ cze "-BŽádný sloupec nemá vytvořen fulltextový index"
+ dan "Kan ikke finde en FULLTEXT nøgle som svarer til kolonne listen"
+ nla "Kan geen FULLTEXT index vinden passend bij de kolom lijst"
+ eng "Can't find FULLTEXT index matching the column list"
+ est "Ei suutnud leida FULLTEXT indeksit, mis kattuks kasutatud tulpadega"
+ fre "Impossible de trouver un index FULLTEXT correspondant à cette liste de colonnes"
+ ger "Kann keinen FULLTEXT-Index finden, der der Feldliste entspricht"
+ ita "Impossibile trovare un indice FULLTEXT che corrisponda all'elenco delle colonne"
+ por "Não pode encontrar um índice para o texto todo que combine com a lista de colunas"
+ rus "Ðевозможно отыÑкать полнотекÑтовый (FULLTEXT) индекÑ, ÑоответÑтвующий ÑпиÑку Ñтолбцов"
+ serbian "Ne mogu da pronađem 'FULLTEXT' indeks koli odgovara listi kolona"
+ spa "No puedo encontrar índice FULLTEXT correspondiendo a la lista de columnas"
+ swe "Hittar inte ett FULLTEXT-index i kolumnlistan"
+ ukr "Ðе можу знайти FULLTEXT індекÑ, що відповідає переліку Ñтовбців"
+ER_LOCK_OR_ACTIVE_TRANSACTION
+ cze "Nemohu prov-Bést zadaný příkaz, protože existují aktivní zamÄené tabulky nebo aktivní transakce"
+ dan "Kan ikke udføre den givne kommando fordi der findes aktive, låste tabeller eller fordi der udføres en transaktion"
+ nla "Kan het gegeven commando niet uitvoeren, want u heeft actieve gelockte tabellen of een actieve transactie"
+ eng "Can't execute the given command because you have active locked tables or an active transaction"
+ est "Ei suuda täita antud käsku kuna on aktiivseid lukke või käimasolev transaktsioon"
+ fre "Impossible d'exécuter la commande car vous avez des tables verrouillées ou une transaction active"
+ ger "Kann den angegebenen Befehl wegen einer aktiven Tabellensperre oder einer aktiven Transaktion nicht ausführen"
+ ita "Impossibile eseguire il comando richiesto: tabelle sotto lock o transazione in atto"
+ por "Não pode executar o comando dado porque você tem tabelas ativas travadas ou uma transação ativa"
+ rus "Ðевозможно выполнить указанную команду, поÑкольку у Ð²Ð°Ñ Ð¿Ñ€Ð¸ÑутÑтвуют активно заблокированные таблица или Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ð°Ñ Ñ‚Ñ€Ð°Ð½Ð·Ð°ÐºÑ†Ð¸Ñ"
+ serbian "Ne mogu da izvrÅ¡im datu komandu zbog toga Å¡to su tabele zakljuÄane ili je transakcija u toku"
+ spa "No puedo ejecutar el comando dado porque tienes tablas bloqueadas o una transición activa"
+ swe "Kan inte utföra kommandot emedan du har en låst tabell eller an aktiv transaktion"
+ ukr "Ðе можу виконати подану команду тому, що Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð° або виконуєтьÑÑ Ñ‚Ñ€Ð°Ð½Ð·Ð°ÐºÑ†Ñ–Ñ"
+ER_UNKNOWN_SYSTEM_VARIABLE
+ cze "Nezn-Bámá systémová proměnná '%-.64s'"
+ dan "Ukendt systemvariabel '%-.64s'"
+ nla "Onbekende systeem variabele '%-.64s'"
+ eng "Unknown system variable '%-.64s'"
+ est "Tundmatu süsteemne muutuja '%-.64s'"
+ fre "Variable système '%-.64s' inconnue"
+ ger "Unbekannte Systemvariable '%-.64s'"
+ ita "Variabile di sistema '%-.64s' sconosciuta"
+ por "Variável de sistema '%-.64s' desconhecida"
+ rus "ÐеизвеÑÑ‚Ð½Ð°Ñ ÑиÑÑ‚ÐµÐ¼Ð½Ð°Ñ Ð¿ÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ '%-.64s'"
+ serbian "Nepoznata sistemska promenljiva '%-.64s'"
+ spa "Desconocida variable de sistema '%-.64s'"
+ swe "Okänd systemvariabel: '%-.64s'"
+ ukr "Ðевідома ÑиÑтемна змінна '%-.64s'"
+ER_CRASHED_ON_USAGE
+ cze "Tabulka '%-.192s' je ozna-BÄena jako poruÅ¡ená a mÄ›la by být opravena"
+ dan "Tabellen '%-.192s' er markeret med fejl og bør repareres"
+ nla "Tabel '%-.192s' staat als gecrashed gemarkeerd en dient te worden gerepareerd"
+ eng "Table '%-.192s' is marked as crashed and should be repaired"
+ est "Tabel '%-.192s' on märgitud vigaseks ja tuleb parandada"
+ fre "La table '%-.192s' est marquée 'crashed' et devrait être réparée"
+ ger "Tabelle '%-.192s' ist als defekt markiert und sollte repariert werden"
+ ita "La tabella '%-.192s' e` segnalata come corrotta e deve essere riparata"
+ por "Tabela '%-.192s' está marcada como danificada e deve ser reparada"
+ rus "Таблица '%-.192s' помечена как иÑÐ¿Ð¾Ñ€Ñ‡ÐµÐ½Ð½Ð°Ñ Ð¸ должна пройти проверку и ремонт"
+ serbian "Tabela '%-.192s' je markirana kao oštećena i trebala bi biti popravljena"
+ spa "Tabla '%-.192s' está marcada como crashed y debe ser reparada"
+ swe "Tabell '%-.192s' är trasig och bör repareras med REPAIR TABLE"
+ ukr "Таблицю '%-.192s' марковано Ñк зіпÑовану та Ñ—Ñ— потрібно відновити"
+ER_CRASHED_ON_REPAIR
+ cze "Tabulka '%-.192s' je ozna-BÄena jako poruÅ¡ená a poslední (automatická?) oprava se nezdaÅ™ila"
+ dan "Tabellen '%-.192s' er markeret med fejl og sidste (automatiske?) REPAIR fejlede"
+ nla "Tabel '%-.192s' staat als gecrashed gemarkeerd en de laatste (automatische?) reparatie poging mislukte"
+ eng "Table '%-.192s' is marked as crashed and last (automatic?) repair failed"
+ est "Tabel '%-.192s' on märgitud vigaseks ja viimane (automaatne?) parandus ebaõnnestus"
+ fre "La table '%-.192s' est marquée 'crashed' et le dernier 'repair' a échoué"
+ ger "Tabelle '%-.192s' ist als defekt markiert und der letzte (automatische?) Reparaturversuch schlug fehl"
+ ita "La tabella '%-.192s' e` segnalata come corrotta e l'ultima ricostruzione (automatica?) e` fallita"
+ por "Tabela '%-.192s' está marcada como danificada e a última reparação (automática?) falhou"
+ rus "Таблица '%-.192s' помечена как иÑÐ¿Ð¾Ñ€Ñ‡ÐµÐ½Ð½Ð°Ñ Ð¸ поÑледний (автоматичеÑкий?) ремонт не был уÑпешным"
+ serbian "Tabela '%-.192s' je markirana kao oštećena, a zadnja (automatska?) popravka je bila neuspela"
+ spa "Tabla '%-.192s' está marcada como crashed y la última reparación (automactica?) falló"
+ swe "Tabell '%-.192s' är trasig och senast (automatiska?) reparation misslyckades"
+ ukr "Таблицю '%-.192s' марковано Ñк зіпÑовану та оÑтаннє (автоматичне?) Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½Ðµ вдалоÑÑ"
+ER_WARNING_NOT_COMPLETE_ROLLBACK
+ dan "Advarsel: Visse data i tabeller der ikke understøtter transaktioner kunne ikke tilbagestilles"
+ nla "Waarschuwing: Roll back mislukt voor sommige buiten transacties gewijzigde tabellen"
+ eng "Some non-transactional changed tables couldn't be rolled back"
+ est "Hoiatus: mõnesid transaktsioone mittetoetavaid tabeleid ei suudetud tagasi kerida"
+ fre "Attention: certaines tables ne supportant pas les transactions ont été changées et elles ne pourront pas être restituées"
+ ger "Änderungen an einigen nicht transaktionalen Tabellen konnten nicht zurückgerollt werden"
+ ita "Attenzione: Alcune delle modifiche alle tabelle non transazionali non possono essere ripristinate (roll back impossibile)"
+ por "Aviso: Algumas tabelas não-transacionais alteradas não puderam ser reconstituídas (rolled back)"
+ rus "Внимание: по некоторым измененным нетранзакционным таблицам невозможно будет произвеÑти откат транзакции"
+ serbian "Upozorenje: Neke izmenjene tabele ne podržavaju komandu 'ROLLBACK'"
+ spa "Aviso: Algunas tablas no transancionales no pueden tener rolled back"
+ swe "Warning: Några icke transaktionella tabeller kunde inte återställas vid ROLLBACK"
+ ukr "ЗаÑтереженнÑ: ДеÑкі нетранзакційні зміни таблиць не можна буде повернути"
+ER_TRANS_CACHE_FULL
+ dan "Fler-udtryks transaktion krævede mere plads en 'max_binlog_cache_size' bytes. Forhøj værdien af denne variabel og prøv igen"
+ nla "Multi-statement transactie vereist meer dan 'max_binlog_cache_size' bytes opslag. Verhoog deze mysqld variabele en probeer opnieuw"
+ eng "Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage; increase this mysqld variable and try again"
+ est "Mitme lausendiga transaktsioon nõudis rohkem ruumi kui lubatud 'max_binlog_cache_size' muutujaga. Suurenda muutuja väärtust ja proovi uuesti"
+ fre "Cette transaction à commandes multiples nécessite plus de 'max_binlog_cache_size' octets de stockage, augmentez cette variable de mysqld et réessayez"
+ ger "Transaktionen, die aus mehreren Befehlen bestehen, benötigten mehr als 'max_binlog_cache_size' Bytes an Speicher. Btte vergrössern Sie diese Server-Variable versuchen Sie es noch einmal"
+ ita "La transazione a comandi multipli (multi-statement) ha richiesto piu` di 'max_binlog_cache_size' bytes di disco: aumentare questa variabile di mysqld e riprovare"
+ por "Transações multi-declaradas (multi-statement transactions) requeriram mais do que o valor limite (max_binlog_cache_size) de bytes para armazenagem. Aumente o valor desta variável do mysqld e tente novamente"
+ rus "Транзакции, включающей большое количеÑтво команд, потребовалоÑÑŒ более чем 'max_binlog_cache_size' байт. Увеличьте Ñту переменную Ñервера mysqld и попробуйте еще раз"
+ spa "Multipla transición necesita mas que 'max_binlog_cache_size' bytes de almacenamiento. Aumente esta variable mysqld y tente de nuevo"
+ swe "Transaktionen krävde mera än 'max_binlog_cache_size' minne. Öka denna mysqld-variabel och försök på nytt"
+ ukr "Ð¢Ñ€Ð°Ð½Ð·Ð°ÐºÑ†Ñ–Ñ Ð· багатьма виразами вимагає більше ніж 'max_binlog_cache_size' байтів Ð´Ð»Ñ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ. Збільште цю змінну mysqld та Ñпробуйте знову"
+ER_SLAVE_MUST_STOP
+ dan "Denne handling kunne ikke udføres med kørende slave, brug først kommandoen STOP SLAVE"
+ nla "Deze operatie kan niet worden uitgevoerd met een actieve slave, doe eerst STOP SLAVE"
+ eng "This operation cannot be performed with a running slave; run STOP SLAVE first"
+ fre "Cette opération ne peut être réalisée avec un esclave actif, faites STOP SLAVE d'abord"
+ ger "Diese Operation kann bei einem aktiven Slave nicht durchgeführt werden. Bitte zuerst STOP SLAVE ausführen"
+ ita "Questa operazione non puo' essere eseguita con un database 'slave' che gira, lanciare prima STOP SLAVE"
+ por "Esta operação não pode ser realizada com um 'slave' em execução. Execute STOP SLAVE primeiro"
+ rus "Эту операцию невозможно выполнить при работающем потоке подчиненного Ñервера. Сначала выполните STOP SLAVE"
+ serbian "Ova operacija ne može biti izvršena dok je aktivan podređeni server. Zadajte prvo komandu 'STOP SLAVE' da zaustavite podređeni server."
+ spa "Esta operación no puede ser hecha con el esclavo funcionando, primero use STOP SLAVE"
+ swe "Denna operation kan inte göras under replikering; Gör STOP SLAVE först"
+ ukr "ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ðµ може бути виконана з запущеним підлеглим, Ñпочатку виконайте STOP SLAVE"
+ER_SLAVE_NOT_RUNNING
+ dan "Denne handling kræver en kørende slave. Konfigurer en slave og brug kommandoen START SLAVE"
+ nla "Deze operatie vereist een actieve slave, configureer slave en doe dan START SLAVE"
+ eng "This operation requires a running slave; configure slave and do START SLAVE"
+ fre "Cette opération nécessite un esclave actif, configurez les esclaves et faites START SLAVE"
+ ger "Diese Operation benötigt einen aktiven Slave. Bitte Slave konfigurieren und mittels START SLAVE aktivieren"
+ ita "Questa operaione richiede un database 'slave', configurarlo ed eseguire START SLAVE"
+ por "Esta operação requer um 'slave' em execução. Configure o 'slave' e execute START SLAVE"
+ rus "Ð”Ð»Ñ Ñтой операции требуетÑÑ Ñ€Ð°Ð±Ð¾Ñ‚Ð°ÑŽÑ‰Ð¸Ð¹ подчиненный Ñервер. Сначала выполните START SLAVE"
+ serbian "Ova operacija zahteva da je aktivan podređeni server. Konfigurišite prvo podređeni server i onda izvršite komandu 'START SLAVE'"
+ spa "Esta operación necesita el esclavo funcionando, configure esclavo y haga el START SLAVE"
+ swe "Denna operation kan endast göras under replikering; Konfigurera slaven och gör START SLAVE"
+ ukr "ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð²Ð¸Ð¼Ð°Ð³Ð°Ñ” запущеного підлеглого, зконфігуруйте підлеглого та виконайте START SLAVE"
+ER_BAD_SLAVE
+ dan "Denne server er ikke konfigureret som slave. Ret in config-filen eller brug kommandoen CHANGE MASTER TO"
+ nla "De server is niet geconfigureerd als slave, fix in configuratie bestand of met CHANGE MASTER TO"
+ eng "The server is not configured as slave; fix in config file or with CHANGE MASTER TO"
+ fre "Le server n'est pas configuré comme un esclave, changez le fichier de configuration ou utilisez CHANGE MASTER TO"
+ ger "Der Server ist nicht als Slave konfiguriert. Bitte in der Konfigurationsdatei oder mittels CHANGE MASTER TO beheben"
+ ita "Il server non e' configurato come 'slave', correggere il file di configurazione cambiando CHANGE MASTER TO"
+ por "O servidor não está configurado como 'slave'. Acerte o arquivo de configuração ou use CHANGE MASTER TO"
+ rus "Этот Ñервер не наÑтроен как подчиненный. ВнеÑите иÑÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð² конфигурационном файле или Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ CHANGE MASTER TO"
+ serbian "Server nije konfigurisan kao podređeni server, ispravite konfiguracioni file ili na njemu izvršite komandu 'CHANGE MASTER TO'"
+ spa "El servidor no está configurado como esclavo, edite el archivo config file o con CHANGE MASTER TO"
+ swe "Servern är inte konfigurerade som en replikationsslav. Ändra konfigurationsfilen eller gör CHANGE MASTER TO"
+ ukr "Сервер не зконфігуровано Ñк підлеглий, виправте це у файлі конфігурації або з CHANGE MASTER TO"
+ER_MASTER_INFO
+ eng "Could not initialize master info structure; more error messages can be found in the MariaDB error log"
+ fre "Impossible d'initialiser les structures d'information de maître, vous trouverez des messages d'erreur supplémentaires dans le journal des erreurs de MariaDB"
+ ger "Konnte Master-Info-Struktur nicht initialisieren. Weitere Fehlermeldungen können im MariaDB-Error-Log eingesehen werden"
+ serbian "Nisam mogao da inicijalizujem informacionu strukturu glavnog servera, proverite da li imam privilegije potrebne za pristup file-u 'master.info'"
+ swe "Kunde inte initialisera replikationsstrukturerna. See MariaDB fel fil för mera information"
+ER_SLAVE_THREAD
+ dan "Kunne ikke danne en slave-tråd; check systemressourcerne"
+ nla "Kon slave thread niet aanmaken, controleer systeem resources"
+ eng "Could not create slave thread; check system resources"
+ fre "Impossible de créer une tâche esclave, vérifiez les ressources système"
+ ger "Konnte Slave-Thread nicht starten. Bitte System-Ressourcen überprüfen"
+ ita "Impossibile creare il thread 'slave', controllare le risorse di sistema"
+ por "Não conseguiu criar 'thread' de 'slave'. Verifique os recursos do sistema"
+ rus "Ðевозможно Ñоздать поток подчиненного Ñервера. Проверьте ÑиÑтемные реÑурÑÑ‹"
+ serbian "Nisam mogao da startujem thread za podređeni server, proverite sistemske resurse"
+ spa "No puedo crear el thread esclavo, verifique recursos del sistema"
+ swe "Kunde inte starta en tråd för replikering"
+ ukr "Ðе можу Ñтворити підлеглу гілку, перевірте ÑиÑтемні реÑурÑи"
+ER_TOO_MANY_USER_CONNECTIONS 42000
+ dan "Brugeren %-.64s har allerede mere end 'max_user_connections' aktive forbindelser"
+ nla "Gebruiker %-.64s heeft reeds meer dan 'max_user_connections' actieve verbindingen"
+ eng "User %-.64s already has more than 'max_user_connections' active connections"
+ est "Kasutajal %-.64s on juba rohkem ühendusi kui lubatud 'max_user_connections' muutujaga"
+ fre "L'utilisateur %-.64s possède déjà plus de 'max_user_connections' connexions actives"
+ ger "Benutzer '%-.64s' hat mehr als 'max_user_connections' aktive Verbindungen"
+ ita "L'utente %-.64s ha gia' piu' di 'max_user_connections' connessioni attive"
+ por "Usuário '%-.64s' já possui mais que o valor máximo de conexões (max_user_connections) ativas"
+ rus "У Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ %-.64s уже больше чем 'max_user_connections' активных Ñоединений"
+ serbian "Korisnik %-.64s već ima više aktivnih konekcija nego što je to određeno 'max_user_connections' promenljivom"
+ spa "Usario %-.64s ya tiene mas que 'max_user_connections' conexiones activas"
+ swe "Användare '%-.64s' har redan 'max_user_connections' aktiva inloggningar"
+ ukr "КориÑтувач %-.64s вже має більше ніж 'max_user_connections' активних з'єднань"
+ER_SET_CONSTANTS_ONLY
+ dan "Du må kun bruge konstantudtryk med SET"
+ nla "U mag alleen constante expressies gebruiken bij SET"
+ eng "You may only use constant expressions with SET"
+ est "Ainult konstantsed suurused on lubatud SET klauslis"
+ fre "Seules les expressions constantes sont autorisées avec SET"
+ ger "Bei SET dürfen nur konstante Ausdrücke verwendet werden"
+ ita "Si possono usare solo espressioni costanti con SET"
+ por "Você pode usar apenas expressões constantes com SET"
+ rus "Ð’Ñ‹ можете иÑпользовать в SET только конÑтантные выражениÑ"
+ serbian "Možete upotrebiti samo konstantan iskaz sa komandom 'SET'"
+ spa "Tu solo debes usar expresiones constantes con SET"
+ swe "Man kan endast använda konstantuttryck med SET"
+ ukr "Можна викориÑтовувати лише вирази зі Ñталими у SET"
+ER_LOCK_WAIT_TIMEOUT
+ dan "Lock wait timeout overskredet"
+ nla "Lock wacht tijd overschreden"
+ eng "Lock wait timeout exceeded; try restarting transaction"
+ est "Kontrollaeg ületatud luku järel ootamisel; Proovi transaktsiooni otsast alata"
+ fre "Timeout sur l'obtention du verrou"
+ ger "Beim Warten auf eine Sperre wurde die zulässige Wartezeit überschritten. Bitte versuchen Sie, die Transaktion neu zu starten"
+ ita "E' scaduto il timeout per l'attesa del lock"
+ por "Tempo de espera (timeout) de travamento excedido. Tente reiniciar a transação."
+ rus "Таймаут Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²ÐºÐ¸ иÑтек; попробуйте перезапуÑтить транзакцию"
+ serbian "Vremenski limit za zakljuÄavanje tabele je istekao; Probajte da ponovo startujete transakciju"
+ spa "Tiempo de bloqueo de espera excedido"
+ swe "Fick inte ett lås i tid ; Försök att starta om transaktionen"
+ ukr "Затримку Ð¾Ñ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð¸Ñ‡ÐµÑ€Ð¿Ð°Ð½Ð¾"
+ER_LOCK_TABLE_FULL
+ dan "Det totale antal låse overstiger størrelsen på låse-tabellen"
+ nla "Het totale aantal locks overschrijdt de lock tabel grootte"
+ eng "The total number of locks exceeds the lock table size"
+ est "Lukkude koguarv ületab lukutabeli suuruse"
+ fre "Le nombre total de verrou dépasse la taille de la table des verrous"
+ ger "Die Gesamtzahl der Sperren überschreitet die Größe der Sperrtabelle"
+ ita "Il numero totale di lock e' maggiore della grandezza della tabella di lock"
+ por "O número total de travamentos excede o tamanho da tabela de travamentos"
+ rus "Общее количеÑтво блокировок превыÑило размеры таблицы блокировок"
+ serbian "Broj totalnih zakljuÄavanja tabele premaÅ¡uje veliÄinu tabele zakljuÄavanja"
+ spa "El número total de bloqueos excede el tamaño de bloqueo de la tabla"
+ swe "Antal lås överskrider antalet reserverade lås"
+ ukr "Загальна кількіÑÑ‚ÑŒ блокувань перевищила розмір блокувань Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ–"
+ER_READ_ONLY_TRANSACTION 25000
+ dan "Update lås kan ikke opnås under en READ UNCOMMITTED transaktion"
+ nla "Update locks kunnen niet worden verkregen tijdens een READ UNCOMMITTED transactie"
+ eng "Update locks cannot be acquired during a READ UNCOMMITTED transaction"
+ est "Uuenduslukke ei saa kasutada READ UNCOMMITTED transaktsiooni käigus"
+ fre "Un verrou en update ne peut être acquit pendant une transaction READ UNCOMMITTED"
+ ger "Während einer READ-UNCOMMITTED-Transaktion können keine UPDATE-Sperren angefordert werden"
+ ita "I lock di aggiornamento non possono essere acquisiti durante una transazione 'READ UNCOMMITTED'"
+ por "Travamentos de atualização não podem ser obtidos durante uma transação de tipo READ UNCOMMITTED"
+ rus "Блокировки обновлений Ð½ÐµÐ»ÑŒÐ·Ñ Ð¿Ð¾Ð»ÑƒÑ‡Ð¸Ñ‚ÑŒ в процеÑÑе Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð½Ðµ принÑтой (в режиме READ UNCOMMITTED) транзакции"
+ serbian "ZakljuÄavanja izmena ne mogu biti realizovana sve dok traje 'READ UNCOMMITTED' transakcija"
+ spa "Bloqueos de actualización no pueden ser adqueridos durante una transición READ UNCOMMITTED"
+ swe "Updateringslås kan inte göras när man använder READ UNCOMMITTED"
+ ukr "Оновити Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ðµ можливо на протÑзі транзакції READ UNCOMMITTED"
+ER_DROP_DB_WITH_READ_LOCK
+ dan "DROP DATABASE er ikke tilladt mens en tråd holder på globalt read lock"
+ nla "DROP DATABASE niet toegestaan terwijl thread een globale 'read lock' bezit"
+ eng "DROP DATABASE not allowed while thread is holding global read lock"
+ est "DROP DATABASE ei ole lubatud kui lõim omab globaalset READ lukku"
+ fre "DROP DATABASE n'est pas autorisée pendant qu'une tâche possède un verrou global en lecture"
+ ger "DROP DATABASE ist nicht erlaubt, solange der Thread eine globale Lesesperre hält"
+ ita "DROP DATABASE non e' permesso mentre il thread ha un lock globale di lettura"
+ por "DROP DATABASE não permitido enquanto uma 'thread' está mantendo um travamento global de leitura"
+ rus "Ðе допуÑкаетÑÑ DROP DATABASE, пока поток держит глобальную блокировку чтениÑ"
+ serbian "Komanda 'DROP DATABASE' nije dozvoljena dok thread globalno zakljuÄava Äitanje podataka"
+ spa "DROP DATABASE no permitido mientras un thread está ejerciendo un bloqueo de lectura global"
+ swe "DROP DATABASE är inte tillåtet när man har ett globalt läslås"
+ ukr "DROP DATABASE не дозволено доки гілка перебуває під загальним блокуваннÑм читаннÑ"
+ER_CREATE_DB_WITH_READ_LOCK
+ dan "CREATE DATABASE er ikke tilladt mens en tråd holder på globalt read lock"
+ nla "CREATE DATABASE niet toegestaan terwijl thread een globale 'read lock' bezit"
+ eng "CREATE DATABASE not allowed while thread is holding global read lock"
+ est "CREATE DATABASE ei ole lubatud kui lõim omab globaalset READ lukku"
+ fre "CREATE DATABASE n'est pas autorisée pendant qu'une tâche possède un verrou global en lecture"
+ ger "CREATE DATABASE ist nicht erlaubt, solange der Thread eine globale Lesesperre hält"
+ ita "CREATE DATABASE non e' permesso mentre il thread ha un lock globale di lettura"
+ por "CREATE DATABASE não permitido enquanto uma 'thread' está mantendo um travamento global de leitura"
+ rus "Ðе допуÑкаетÑÑ CREATE DATABASE, пока поток держит глобальную блокировку чтениÑ"
+ serbian "Komanda 'CREATE DATABASE' nije dozvoljena dok thread globalno zakljuÄava Äitanje podataka"
+ spa "CREATE DATABASE no permitido mientras un thread está ejerciendo un bloqueo de lectura global"
+ swe "CREATE DATABASE är inte tillåtet när man har ett globalt läslås"
+ ukr "CREATE DATABASE не дозволено доки гілка перебуває під загальним блокуваннÑм читаннÑ"
+ER_WRONG_ARGUMENTS
+ nla "Foutieve parameters voor %s"
+ eng "Incorrect arguments to %s"
+ est "Vigased parameetrid %s-le"
+ fre "Mauvais arguments à %s"
+ ger "Falsche Argumente für %s"
+ ita "Argomenti errati a %s"
+ por "Argumentos errados para %s"
+ rus "Ðеверные параметры Ð´Ð»Ñ %s"
+ serbian "Pogrešni argumenti prosleđeni na %s"
+ spa "Argumentos errados para %s"
+ swe "Felaktiga argument till %s"
+ ukr "Хибний аргумент Ð´Ð»Ñ %s"
+ER_NO_PERMISSION_TO_CREATE_USER 42000
+ nla "'%s'@'%s' mag geen nieuwe gebruikers creeren"
+ eng "'%s'@'%s' is not allowed to create new users"
+ est "Kasutajal '%s'@'%s' ei ole lubatud luua uusi kasutajaid"
+ fre "'%s'@'%s' n'est pas autorisé à créer de nouveaux utilisateurs"
+ ger "'%s'@'%s' ist nicht berechtigt, neue Benutzer hinzuzufügen"
+ ita "A '%s'@'%s' non e' permesso creare nuovi utenti"
+ por "Não é permitido a '%s'@'%s' criar novos usuários"
+ rus "'%s'@'%s' не разрешаетÑÑ Ñоздавать новых пользователей"
+ serbian "Korisniku '%s'@'%s' nije dozvoljeno da kreira nove korisnike"
+ spa "'%s'@'%s' no es permitido para crear nuevos usuarios"
+ swe "'%s'@'%s' har inte rättighet att skapa nya användare"
+ ukr "КориÑтувачу '%s'@'%s' не дозволено Ñтворювати нових кориÑтувачів"
+ER_UNION_TABLES_IN_DIFFERENT_DIR
+ nla "Incorrecte tabel definitie; alle MERGE tabellen moeten tot dezelfde database behoren"
+ eng "Incorrect table definition; all MERGE tables must be in the same database"
+ est "Vigane tabelimääratlus; kõik MERGE tabeli liikmed peavad asuma samas andmebaasis"
+ fre "Définition de table incorrecte; toutes les tables MERGE doivent être dans la même base de donnée"
+ ger "Falsche Tabellendefinition. Alle MERGE-Tabellen müssen sich in derselben Datenbank befinden"
+ ita "Definizione della tabella errata; tutte le tabelle di tipo MERGE devono essere nello stesso database"
+ por "Definição incorreta da tabela. Todas as tabelas contidas na junção devem estar no mesmo banco de dados."
+ rus "Ðеверное определение таблицы; Ð’Ñе таблицы в MERGE должны принадлежать одной и той же базе данных"
+ serbian "Pogrešna definicija tabele; sve 'MERGE' tabele moraju biti u istoj bazi podataka"
+ spa "Incorrecta definición de la tabla; Todas las tablas MERGE deben estar en el mismo banco de datos"
+ swe "Felaktig tabelldefinition; alla tabeller i en MERGE-tabell måste vara i samma databas"
+ER_LOCK_DEADLOCK 40001
+ nla "Deadlock gevonden tijdens lock-aanvraag poging; Probeer herstart van de transactie"
+ eng "Deadlock found when trying to get lock; try restarting transaction"
+ est "Lukustamisel tekkis tupik (deadlock); alusta transaktsiooni otsast"
+ fre "Deadlock découvert en essayant d'obtenir les verrous : essayez de redémarrer la transaction"
+ ger "Beim Versuch, eine Sperre anzufordern, ist ein Deadlock aufgetreten. Versuchen Sie, die Transaktion neu zu starten"
+ ita "Trovato deadlock durante il lock; Provare a far ripartire la transazione"
+ por "Encontrado um travamento fatal (deadlock) quando tentava obter uma trava. Tente reiniciar a transação."
+ rus "Возникла Ñ‚ÑƒÐ¿Ð¸ÐºÐ¾Ð²Ð°Ñ ÑÐ¸Ñ‚ÑƒÐ°Ñ†Ð¸Ñ Ð² процеÑÑе Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²ÐºÐ¸; Попробуйте перезапуÑтить транзакцию"
+ serbian "Unakrsno zakljuÄavanje pronaÄ‘eno kada sam pokuÅ¡ao da dobijem pravo na zakljuÄavanje; Probajte da restartujete transakciju"
+ spa "Encontrado deadlock cuando tentando obtener el bloqueo; Tente recomenzar la transición"
+ swe "Fick 'DEADLOCK' vid låsförsök av block/rad. Försök att starta om transaktionen"
+ER_TABLE_CANT_HANDLE_FT
+ nla "Het gebruikte tabel type ondersteund geen FULLTEXT indexen"
+ eng "The used table type doesn't support FULLTEXT indexes"
+ est "Antud tabelitüüp ei toeta FULLTEXT indekseid"
+ fre "Le type de table utilisé ne supporte pas les index FULLTEXT"
+ ger "Der verwendete Tabellentyp unterstützt keine FULLTEXT-Indizes"
+ ita "La tabella usata non supporta gli indici FULLTEXT"
+ por "O tipo de tabela utilizado não suporta índices de texto completo (fulltext indexes)"
+ rus "ИÑпользуемый тип таблиц не поддерживает полнотекÑтовых индекÑов"
+ serbian "Upotrebljeni tip tabele ne podržava 'FULLTEXT' indekse"
+ spa "El tipo de tabla usada no soporta índices FULLTEXT"
+ swe "Tabelltypen har inte hantering av FULLTEXT-index"
+ ukr "ВикориÑтаний тип таблиці не підтримує FULLTEXT індекÑів"
+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)"
+ 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_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"
+ fre "Impossible d'ajouter un enregistrement fils : une constrainte externe l'empèche"
+ ger "Hinzufügen oder Aktualisieren eines Kind-Datensatzes schlug aufgrund einer Fremdschlüssel-Beschränkung fehl"
+ greek "Cannot add a child row: a foreign key constraint fails"
+ hun "Cannot add a child row: a foreign key constraint fails"
+ ita "Impossibile aggiungere la riga: un vincolo d'integrita' referenziale non e' soddisfatto"
+ norwegian-ny "Cannot add a child row: a foreign key constraint fails"
+ por "Não pode acrescentar uma linha filha: uma restrição de chave estrangeira falhou"
+ rus "Ðевозможно добавить или обновить дочернюю Ñтроку: проверка ограничений внешнего ключа не выполнÑетÑÑ"
+ spa "No puede adicionar una línea hijo: falla de clave extranjera constraint"
+ swe "FOREIGN KEY-konflikt: Kan inte skriva barn"
+ER_ROW_IS_REFERENCED 23000
+ eng "Cannot delete or update a parent row: a foreign key constraint fails"
+ fre "Impossible de supprimer un enregistrement père : une constrainte externe l'empèche"
+ ger "Löschen oder Aktualisieren eines Eltern-Datensatzes schlug aufgrund einer Fremdschlüssel-Beschränkung fehl"
+ greek "Cannot delete a parent row: a foreign key constraint fails"
+ hun "Cannot delete a parent row: a foreign key constraint fails"
+ ita "Impossibile cancellare la riga: un vincolo d'integrita' referenziale non e' soddisfatto"
+ por "Não pode apagar uma linha pai: uma restrição de chave estrangeira falhou"
+ rus "Ðевозможно удалить или обновить родительÑкую Ñтроку: проверка ограничений внешнего ключа не выполнÑетÑÑ"
+ serbian "Ne mogu da izbriÅ¡em roditeljski slog: provera spoljnog kljuÄa je neuspela"
+ spa "No puede deletar una línea padre: falla de clave extranjera constraint"
+ swe "FOREIGN KEY-konflikt: Kan inte radera fader"
+ER_CONNECT_TO_MASTER 08S01
+ nla "Fout bij opbouwen verbinding naar master: %-.128s"
+ eng "Error connecting to master: %-.128s"
+ ger "Fehler bei der Verbindung zum Master: %-.128s"
+ ita "Errore durante la connessione al master: %-.128s"
+ por "Erro conectando com o master: %-.128s"
+ rus "Ошибка ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ñ Ð³Ð¾Ð»Ð¾Ð²Ð½Ñ‹Ð¼ Ñервером: %-.128s"
+ spa "Error de coneccion a master: %-.128s"
+ swe "Fick fel vid anslutning till master: %-.128s"
+ER_QUERY_ON_MASTER
+ nla "Fout bij uitvoeren query op master: %-.128s"
+ eng "Error running query on master: %-.128s"
+ ger "Beim Ausführen einer Abfrage auf dem Master trat ein Fehler auf: %-.128s"
+ ita "Errore eseguendo una query sul master: %-.128s"
+ por "Erro rodando consulta no master: %-.128s"
+ rus "Ошибка Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа на головном Ñервере: %-.128s"
+ spa "Error executando el query en master: %-.128s"
+ swe "Fick fel vid utförande av command på mastern: %-.128s"
+ER_ERROR_WHEN_EXECUTING_COMMAND
+ nla "Fout tijdens uitvoeren van commando %s: %-.128s"
+ eng "Error when executing command %s: %-.128s"
+ est "Viga käsu %s täitmisel: %-.128s"
+ ger "Fehler beim Ausführen des Befehls %s: %-.128s"
+ ita "Errore durante l'esecuzione del comando %s: %-.128s"
+ por "Erro quando executando comando %s: %-.128s"
+ rus "Ошибка при выполнении команды %s: %-.128s"
+ serbian "Greška pri izvršavanju komande %s: %-.128s"
+ spa "Error de %s: %-.128s"
+ swe "Fick fel vid utförande av %s: %-.128s"
+ER_WRONG_USAGE
+ nla "Foutief gebruik van %s en %s"
+ eng "Incorrect usage of %s and %s"
+ est "Vigane %s ja %s kasutus"
+ ger "Falsche Verwendung von %s und %s"
+ ita "Uso errato di %s e %s"
+ por "Uso errado de %s e %s"
+ rus "Ðеверное иÑпользование %s и %s"
+ serbian "Pogrešna upotreba %s i %s"
+ spa "Equivocado uso de %s y %s"
+ swe "Felaktig använding av %s and %s"
+ ukr "Wrong usage of %s and %s"
+ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT 21000
+ nla "De gebruikte SELECT commando's hebben een verschillend aantal kolommen"
+ eng "The used SELECT statements have a different number of columns"
+ est "Tulpade arv kasutatud SELECT lausetes ei kattu"
+ ger "Die verwendeten SELECT-Befehle liefern unterschiedliche Anzahlen von Feldern zurück"
+ ita "La SELECT utilizzata ha un numero di colonne differente"
+ por "Os comandos SELECT usados têm diferente número de colunas"
+ rus "ИÑпользованные операторы выборки (SELECT) дают разное количеÑтво Ñтолбцов"
+ serbian "Upotrebljene 'SELECT' komande adresiraju razliÄit broj kolona"
+ spa "El comando SELECT usado tiene diferente número de columnas"
+ swe "SELECT-kommandona har olika antal kolumner"
+ER_CANT_UPDATE_WITH_READLOCK
+ nla "Kan de query niet uitvoeren vanwege een conflicterende read lock"
+ eng "Can't execute the query because you have a conflicting read lock"
+ est "Ei suuda täita päringut konfliktse luku tõttu"
+ ger "Augrund eines READ-LOCK-Konflikts kann die Abfrage nicht ausgeführt werden"
+ ita "Impossibile eseguire la query perche' c'e' un conflitto con in lock di lettura"
+ por "Não posso executar a consulta porque você tem um conflito de travamento de leitura"
+ rus "Ðевозможно иÑполнить запроÑ, поÑкольку у Ð²Ð°Ñ ÑƒÑтановлены конфликтующие блокировки чтениÑ"
+ serbian "Ne mogu da izvrÅ¡im upit zbog toga Å¡to imate zakljuÄavanja Äitanja podataka u konfliktu"
+ spa "No puedo ejecutar el query porque usted tiene conflicto de traba de lectura"
+ swe "Kan inte utföra kommandot emedan du har ett READ-lås"
+ER_MIXING_NOT_ALLOWED
+ nla "Het combineren van transactionele en niet-transactionele tabellen is uitgeschakeld."
+ eng "Mixing of transactional and non-transactional tables is disabled"
+ est "Transaktsioone toetavate ning mittetoetavate tabelite kooskasutamine ei ole lubatud"
+ ger "Die gleichzeitige Verwendung von Tabellen mit und ohne Transaktionsunterstützung ist deaktiviert"
+ ita "E' disabilitata la possibilita' di mischiare tabelle transazionali e non-transazionali"
+ por "Mistura de tabelas transacional e não-transacional está desabilitada"
+ rus "ИÑпользование транзакционных таблиц нарÑду Ñ Ð½ÐµÑ‚Ñ€Ð°Ð½Ð·Ð°ÐºÑ†Ð¸Ð¾Ð½Ð½Ñ‹Ð¼Ð¸ запрещено"
+ serbian "MeÅ¡anje tabela koje podržavaju transakcije i onih koje ne podržavaju transakcije je iskljuÄeno"
+ spa "Mezla de transancional y no-transancional tablas está deshabilitada"
+ swe "Blandning av transaktionella och icke-transaktionella tabeller är inaktiverat"
+ER_DUP_ARGUMENT
+ nla "Optie '%s' tweemaal gebruikt in opdracht"
+ eng "Option '%s' used twice in statement"
+ est "Määrangut '%s' on lauses kasutatud topelt"
+ ger "Option '%s' wird im Befehl zweimal verwendet"
+ ita "L'opzione '%s' e' stata usata due volte nel comando"
+ por "Opção '%s' usada duas vezes no comando"
+ rus "ÐžÐ¿Ñ†Ð¸Ñ '%s' дважды иÑпользована в выражении"
+ spa "Opción '%s' usada dos veces en el comando"
+ swe "Option '%s' användes två gånger"
+ER_USER_LIMIT_REACHED 42000
+ nla "Gebruiker '%-.64s' heeft het maximale gebruik van de '%s' faciliteit overschreden (huidige waarde: %ld)"
+ eng "User '%-.64s' has exceeded the '%s' resource (current value: %ld)"
+ ger "Benutzer '%-.64s' hat die Ressourcenbeschränkung '%s' überschritten (aktueller Wert: %ld)"
+ ita "L'utente '%-.64s' ha ecceduto la risorsa '%s' (valore corrente: %ld)"
+ por "Usuário '%-.64s' tem excedido o '%s' recurso (atual valor: %ld)"
+ rus "Пользователь '%-.64s' превыÑил иÑпользование реÑурÑа '%s' (текущее значение: %ld)"
+ spa "Usuario '%-.64s' ha excedido el recurso '%s' (actual valor: %ld)"
+ swe "Användare '%-.64s' har överskridit '%s' (nuvarande värde: %ld)"
+ER_SPECIFIC_ACCESS_DENIED_ERROR 42000
+ nla "Toegang geweigerd. U moet het %-.128s privilege hebben voor deze operatie"
+ eng "Access denied; you need (at least one of) the %-.128s privilege(s) for this operation"
+ ger "Kein Zugriff. Hierfür wird die Berechtigung %-.128s benötigt"
+ ita "Accesso non consentito. Serve il privilegio %-.128s per questa operazione"
+ por "Acesso negado. Você precisa o privilégio %-.128s para essa operação"
+ rus "Ð’ доÑтупе отказано. Вам нужны привилегии %-.128s Ð´Ð»Ñ Ñтой операции"
+ spa "Acceso negado. Usted necesita el privilegio %-.128s para esta operación"
+ swe "Du har inte privlegiet '%-.128s' som behövs för denna operation"
+ ukr "Access denied. You need the %-.128s privilege for this operation"
+ER_LOCAL_VARIABLE
+ nla "Variabele '%-.64s' is SESSION en kan niet worden gebruikt met SET GLOBAL"
+ eng "Variable '%-.64s' is a SESSION variable and can't be used with SET GLOBAL"
+ ger "Variable '%-.64s' ist eine lokale Variable und kann nicht mit SET GLOBAL verändert werden"
+ ita "La variabile '%-.64s' e' una variabile locale ( SESSION ) e non puo' essere cambiata usando SET GLOBAL"
+ por "Variável '%-.64s' é uma SESSION variável e não pode ser usada com SET GLOBAL"
+ rus "ÐŸÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ '%-.64s' ÑвлÑетÑÑ Ð¿Ð¾Ñ‚Ð¾ÐºÐ¾Ð²Ð¾Ð¹ (SESSION) переменной и не может быть изменена Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ SET GLOBAL"
+ spa "Variable '%-.64s' es una SESSION variable y no puede ser usada con SET GLOBAL"
+ swe "Variabel '%-.64s' är en SESSION variabel och kan inte ändrad med SET GLOBAL"
+ER_GLOBAL_VARIABLE
+ nla "Variabele '%-.64s' is GLOBAL en dient te worden gewijzigd met SET GLOBAL"
+ eng "Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL"
+ ger "Variable '%-.64s' ist eine globale Variable und muss mit SET GLOBAL verändert werden"
+ ita "La variabile '%-.64s' e' una variabile globale ( GLOBAL ) e deve essere cambiata usando SET GLOBAL"
+ por "Variável '%-.64s' é uma GLOBAL variável e deve ser configurada com SET GLOBAL"
+ rus "ÐŸÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ '%-.64s' ÑвлÑетÑÑ Ð³Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ð¾Ð¹ (GLOBAL) переменной, и ее Ñледует изменÑÑ‚ÑŒ Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ SET GLOBAL"
+ spa "Variable '%-.64s' es una GLOBAL variable y no puede ser configurada con SET GLOBAL"
+ swe "Variabel '%-.64s' är en GLOBAL variabel och bör sättas med SET GLOBAL"
+ER_NO_DEFAULT 42000
+ nla "Variabele '%-.64s' heeft geen standaard waarde"
+ eng "Variable '%-.64s' doesn't have a default value"
+ ger "Variable '%-.64s' hat keinen Vorgabewert"
+ ita "La variabile '%-.64s' non ha un valore di default"
+ por "Variável '%-.64s' não tem um valor padrão"
+ rus "ÐŸÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ '%-.64s' не имеет Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾ умолчанию"
+ spa "Variable '%-.64s' no tiene un valor patrón"
+ swe "Variabel '%-.64s' har inte ett DEFAULT-värde"
+ER_WRONG_VALUE_FOR_VAR 42000
+ nla "Variabele '%-.64s' kan niet worden gewijzigd naar de waarde '%-.200s'"
+ eng "Variable '%-.64s' can't be set to the value of '%-.200s'"
+ ger "Variable '%-.64s' kann nicht auf '%-.200s' gesetzt werden"
+ ita "Alla variabile '%-.64s' non puo' essere assegato il valore '%-.200s'"
+ por "Variável '%-.64s' não pode ser configurada para o valor de '%-.200s'"
+ rus "ÐŸÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ '%-.64s' не может быть уÑтановлена в значение '%-.200s'"
+ spa "Variable '%-.64s' no puede ser configurada para el valor de '%-.200s'"
+ swe "Variabel '%-.64s' kan inte sättas till '%-.200s'"
+ER_WRONG_TYPE_FOR_VAR 42000
+ nla "Foutief argumenttype voor variabele '%-.64s'"
+ eng "Incorrect argument type to variable '%-.64s'"
+ ger "Falscher Argumenttyp für Variable '%-.64s'"
+ ita "Tipo di valore errato per la variabile '%-.64s'"
+ por "Tipo errado de argumento para variável '%-.64s'"
+ rus "Ðеверный тип аргумента Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð¾Ð¹ '%-.64s'"
+ spa "Tipo de argumento equivocado para variable '%-.64s'"
+ swe "Fel typ av argument till variabel '%-.64s'"
+ER_VAR_CANT_BE_READ
+ nla "Variabele '%-.64s' kan alleen worden gewijzigd, niet gelezen"
+ eng "Variable '%-.64s' can only be set, not read"
+ ger "Variable '%-.64s' kann nur verändert, nicht gelesen werden"
+ ita "Alla variabile '%-.64s' e' di sola scrittura quindi puo' essere solo assegnato un valore, non letto"
+ por "Variável '%-.64s' somente pode ser configurada, não lida"
+ rus "ÐŸÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ '%-.64s' может быть только уÑтановлена, но не Ñчитана"
+ spa "Variable '%-.64s' solamente puede ser configurada, no leída"
+ swe "Variabeln '%-.64s' kan endast sättas, inte läsas"
+ER_CANT_USE_OPTION_HERE 42000
+ nla "Foutieve toepassing/plaatsing van '%s'"
+ eng "Incorrect usage/placement of '%s'"
+ ger "Falsche Verwendung oder Platzierung von '%s'"
+ ita "Uso/posizione di '%s' sbagliato"
+ por "Errado uso/colocação de '%s'"
+ rus "Ðеверное иÑпользование или в неверном меÑте указан '%s'"
+ spa "Equivocado uso/colocación de '%s'"
+ swe "Fel använding/placering av '%s'"
+ER_NOT_SUPPORTED_YET 42000
+ nla "Deze versie van MariaDB ondersteunt nog geen '%s'"
+ eng "This version of MariaDB doesn't yet support '%s'"
+ ger "Diese MariaDB-Version unterstützt '%s' nicht"
+ ita "Questa versione di MariaDB non supporta ancora '%s'"
+ por "Esta versão de MariaDB não suporta ainda '%s'"
+ rus "Эта верÑÐ¸Ñ MariaDB пока еще не поддерживает '%s'"
+ spa "Esta versión de MariaDB no soporta todavia '%s'"
+ swe "Denna version av MariaDB kan ännu inte utföra '%s'"
+ER_MASTER_FATAL_ERROR_READING_BINLOG
+ nla "Kreeg fatale fout %d: '%-.320s' van master tijdens lezen van data uit binaire log"
+ eng "Got fatal error %d from master when reading data from binary log: '%-.320s'"
+ ger "Schwerer Fehler %d: '%-.320s vom Master beim Lesen des binären Logs"
+ ita "Errore fatale %d: '%-.320s' dal master leggendo i dati dal log binario"
+ por "Obteve fatal erro %d: '%-.320s' do master quando lendo dados do binary log"
+ rus "Получена неиÑÐ¿Ñ€Ð°Ð²Ð¸Ð¼Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ° %d: '%-.320s' от головного Ñервера в процеÑÑе выборки данных из двоичного журнала"
+ spa "Recibió fatal error %d: '%-.320s' del master cuando leyendo datos del binary log"
+ swe "Fick fatalt fel %d: '%-.320s' från master vid läsning av binärloggen"
+ER_SLAVE_IGNORED_TABLE
+ eng "Slave SQL thread ignored the query because of replicate-*-table rules"
+ ger "Slave-SQL-Thread hat die Abfrage aufgrund von replicate-*-table-Regeln ignoriert"
+ nla "Slave SQL thread negeerde de query vanwege replicate-*-table opties"
+ por "Slave SQL thread ignorado a consulta devido às normas de replicação-*-tabela"
+ spa "Slave SQL thread ignorado el query debido a las reglas de replicación-*-tabla"
+ swe "Slav SQL tråden ignorerade frågan pga en replicate-*-table regel"
+ER_INCORRECT_GLOBAL_LOCAL_VAR
+ eng "Variable '%-.192s' is a %s variable"
+ serbian "Promenljiva '%-.192s' je %s promenljiva"
+ ger "Variable '%-.192s' ist eine %s-Variable"
+ nla "Variabele '%-.192s' is geen %s variabele"
+ spa "Variable '%-.192s' es una %s variable"
+ swe "Variabel '%-.192s' är av typ %s"
+ER_WRONG_FK_DEF 42000
+ eng "Incorrect foreign key definition for '%-.192s': %s"
+ ger "Falsche Fremdschlüssel-Definition für '%-.192s': %s"
+ nla "Incorrecte foreign key definitie voor '%-.192s': %s"
+ por "Definição errada da chave estrangeira para '%-.192s': %s"
+ spa "Equivocada definición de llave extranjera para '%-.192s': %s"
+ swe "Felaktig FOREIGN KEY-definition för '%-.192s': %s"
+ER_KEY_REF_DO_NOT_MATCH_TABLE_REF
+ eng "Key reference and table reference don't match"
+ ger "Schlüssel- und Tabellenverweis passen nicht zusammen"
+ nla "Sleutel- en tabelreferentie komen niet overeen"
+ por "Referência da chave e referência da tabela não coincidem"
+ spa "Referencia de llave y referencia de tabla no coinciden"
+ swe "Nyckelreferensen och tabellreferensen stämmer inte överens"
+ER_OPERAND_COLUMNS 21000
+ eng "Operand should contain %d column(s)"
+ ger "Operand sollte %d Spalte(n) enthalten"
+ nla "Operand behoort %d kolommen te bevatten"
+ rus "Операнд должен Ñодержать %d колонок"
+ spa "Operando debe tener %d columna(s)"
+ ukr "Операнд має ÑкладатиÑÑ Ð· %d Ñтовбців"
+ER_SUBQUERY_NO_1_ROW 21000
+ eng "Subquery returns more than 1 row"
+ ger "Unterabfrage lieferte mehr als einen Datensatz zurück"
+ nla "Subquery retourneert meer dan 1 rij"
+ por "Subconsulta retorna mais que 1 registro"
+ rus "ÐŸÐ¾Ð´Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ более одной запиÑи"
+ spa "Subconsulta retorna mas que 1 línea"
+ swe "Subquery returnerade mer än 1 rad"
+ ukr "Підзапит повертає більш нiж 1 запиÑ"
+ER_UNKNOWN_STMT_HANDLER
+ dan "Unknown prepared statement handler (%.*s) given to %s"
+ eng "Unknown prepared statement handler (%.*s) given to %s"
+ ger "Unbekannter Prepared-Statement-Handler (%.*s) für %s angegeben"
+ nla "Onebekende prepared statement handler (%.*s) voor %s aangegeven"
+ por "Desconhecido manipulador de declaração preparado (%.*s) determinado para %s"
+ spa "Desconocido preparado comando handler (%.*s) dado para %s"
+ swe "Okänd PREPARED STATEMENT id (%.*s) var given till %s"
+ ukr "Unknown prepared statement handler (%.*s) given to %s"
+ER_CORRUPT_HELP_DB
+ eng "Help database is corrupt or does not exist"
+ ger "Die Hilfe-Datenbank ist beschädigt oder existiert nicht"
+ nla "Help database is beschadigd of bestaat niet"
+ por "Banco de dado de ajuda corrupto ou não existente"
+ spa "Base de datos Help está corrupto o no existe"
+ swe "Hjälpdatabasen finns inte eller är skadad"
+ER_CYCLIC_REFERENCE
+ eng "Cyclic reference on subqueries"
+ ger "Zyklischer Verweis in Unterabfragen"
+ nla "Cyclische verwijzing in subqueries"
+ por "Referência cíclica em subconsultas"
+ rus "ЦикличеÑÐºÐ°Ñ ÑÑылка на подзапроÑ"
+ spa "Cíclica referencia en subconsultas"
+ swe "Cyklisk referens i subqueries"
+ ukr "Циклічне поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° підзапит"
+ER_AUTO_CONVERT
+ eng "Converting column '%s' from %s to %s"
+ ger "Feld '%s' wird von %s nach %s umgewandelt"
+ nla "Veld '%s' wordt van %s naar %s geconverteerd"
+ por "Convertendo coluna '%s' de %s para %s"
+ rus "Преобразование Ð¿Ð¾Ð»Ñ '%s' из %s в %s"
+ spa "Convirtiendo columna '%s' de %s para %s"
+ swe "Konvertar kolumn '%s' från %s till %s"
+ ukr "ÐŸÐµÑ€ÐµÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñтовбца '%s' з %s у %s"
+ER_ILLEGAL_REFERENCE 42S22
+ eng "Reference '%-.64s' not supported (%s)"
+ ger "Verweis '%-.64s' wird nicht unterstützt (%s)"
+ nla "Verwijzing '%-.64s' niet ondersteund (%s)"
+ por "Referência '%-.64s' não suportada (%s)"
+ rus "СÑылка '%-.64s' не поддерживаетÑÑ (%s)"
+ spa "Referencia '%-.64s' no soportada (%s)"
+ swe "Referens '%-.64s' stöds inte (%s)"
+ ukr "ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ '%-.64s' не пiдтримуетÑÑ (%s)"
+ER_DERIVED_MUST_HAVE_ALIAS 42000
+ eng "Every derived table must have its own alias"
+ ger "Für jede abgeleitete Tabelle muss ein eigener Alias angegeben werden"
+ nla "Voor elke afgeleide tabel moet een unieke alias worden gebruikt"
+ por "Cada tabela derivada deve ter seu próprio alias"
+ spa "Cada tabla derivada debe tener su propio alias"
+ swe "Varje 'derived table' måste ha sitt eget alias"
+ER_SELECT_REDUCED 01000
+ eng "Select %u was reduced during optimization"
+ ger "Select %u wurde während der Optimierung reduziert"
+ nla "Select %u werd geredureerd tijdens optimtalisatie"
+ por "Select %u foi reduzido durante otimização"
+ rus "Select %u был упразднен в процеÑÑе оптимизации"
+ spa "Select %u fué reducido durante optimización"
+ swe "Select %u reducerades vid optimiering"
+ ukr "Select %u was ÑкаÑовано при оптимiзацii"
+ER_TABLENAME_NOT_ALLOWED_HERE 42000
+ eng "Table '%-.192s' from one of the SELECTs cannot be used in %-.32s"
+ ger "Tabelle '%-.192s', die in einem der SELECT-Befehle verwendet wurde, kann nicht in %-.32s verwendet werden"
+ nla "Tabel '%-.192s' uit een van de SELECTS kan niet in %-.32s gebruikt worden"
+ por "Tabela '%-.192s' de um dos SELECTs não pode ser usada em %-.32s"
+ spa "Tabla '%-.192s' de uno de los SELECT no puede ser usada en %-.32s"
+ swe "Tabell '%-.192s' från en SELECT kan inte användas i %-.32s"
+ER_NOT_SUPPORTED_AUTH_MODE 08004
+ eng "Client does not support authentication protocol requested by server; consider upgrading MariaDB client"
+ ger "Client unterstützt das vom Server erwartete Authentifizierungsprotokoll nicht. Bitte aktualisieren Sie Ihren MariaDB-Client"
+ nla "Client ondersteunt het door de server verwachtte authenticatieprotocol niet. Overweeg een nieuwere MariaDB client te gebruiken"
+ por "Cliente não suporta o protocolo de autenticação exigido pelo servidor; considere a atualização do cliente MariaDB"
+ spa "Cliente no soporta protocolo de autenticación solicitado por el servidor; considere actualizar el cliente MariaDB"
+ swe "Klienten stöder inte autentiseringsprotokollet som begärts av servern; överväg uppgradering av klientprogrammet."
+ER_SPATIAL_CANT_HAVE_NULL 42000
+ eng "All parts of a SPATIAL index must be NOT NULL"
+ ger "Alle Teile eines SPATIAL-Index müssen als NOT NULL deklariert sein"
+ nla "Alle delete van een SPATIAL index dienen als NOT NULL gedeclareerd te worden"
+ por "Todas as partes de uma SPATIAL index devem ser NOT NULL"
+ spa "Todas las partes de una SPATIAL index deben ser NOT NULL"
+ swe "Alla delar av en SPATIAL index måste vara NOT NULL"
+ER_COLLATION_CHARSET_MISMATCH 42000
+ eng "COLLATION '%s' is not valid for CHARACTER SET '%s'"
+ ger "COLLATION '%s' ist für CHARACTER SET '%s' ungültig"
+ nla "COLLATION '%s' is niet geldig voor CHARACTER SET '%s'"
+ por "COLLATION '%s' não é válida para CHARACTER SET '%s'"
+ spa "COLLATION '%s' no es válido para CHARACTER SET '%s'"
+ swe "COLLATION '%s' är inte tillåtet för CHARACTER SET '%s'"
+ER_SLAVE_WAS_RUNNING
+ eng "Slave is already running"
+ ger "Slave läuft bereits"
+ nla "Slave is reeds actief"
+ por "O slave já está rodando"
+ spa "Slave ya está funcionando"
+ swe "Slaven har redan startat"
+ER_SLAVE_WAS_NOT_RUNNING
+ eng "Slave already has been stopped"
+ ger "Slave wurde bereits angehalten"
+ nla "Slave is reeds gestopt"
+ por "O slave já está parado"
+ spa "Slave ya fué parado"
+ swe "Slaven har redan stoppat"
+ER_TOO_BIG_FOR_UNCOMPRESS
+ eng "Uncompressed data size too large; the maximum size is %d (probably, length of uncompressed data was corrupted)"
+ ger "Unkomprimierte Daten sind zu groß. Die maximale Größe beträgt %d (wahrscheinlich wurde die Länge der unkomprimierten Daten beschädigt)"
+ nla "Ongecomprimeerder data is te groot; de maximum lengte is %d (waarschijnlijk, de lengte van de gecomprimeerde data was beschadigd)"
+ por "Tamanho muito grande dos dados des comprimidos. O máximo tamanho é %d. (provavelmente, o comprimento dos dados descomprimidos está corrupto)"
+ spa "Tamaño demasiado grande para datos descomprimidos. El máximo tamaño es %d. (probablemente, extensión de datos descomprimidos fué corrompida)"
+ER_ZLIB_Z_MEM_ERROR
+ eng "ZLIB: Not enough memory"
+ ger "ZLIB: Nicht genug Speicher"
+ nla "ZLIB: Onvoldoende geheugen"
+ por "ZLIB: Não suficiente memória disponível"
+ spa "Z_MEM_ERROR: No suficiente memoria para zlib"
+ER_ZLIB_Z_BUF_ERROR
+ eng "ZLIB: Not enough room in the output buffer (probably, length of uncompressed data was corrupted)"
+ ger "ZLIB: Im Ausgabepuffer ist nicht genug Platz vorhanden (wahrscheinlich wurde die Länge der unkomprimierten Daten beschädigt)"
+ nla "ZLIB: Onvoldoende ruimte in uitgaande buffer (waarschijnlijk, de lengte van de ongecomprimeerde data was beschadigd)"
+ por "ZLIB: Não suficiente espaço no buffer emissor (provavelmente, o comprimento dos dados descomprimidos está corrupto)"
+ spa "Z_BUF_ERROR: No suficiente espacio en el búfer de salida para zlib (probablemente, extensión de datos descomprimidos fué corrompida)"
+ER_ZLIB_Z_DATA_ERROR
+ eng "ZLIB: Input data corrupted"
+ ger "ZLIB: Eingabedaten beschädigt"
+ nla "ZLIB: Invoer data beschadigd"
+ por "ZLIB: Dados de entrada está corrupto"
+ spa "ZLIB: Dato de entrada fué corrompido para zlib"
+ER_CUT_VALUE_GROUP_CONCAT
+ eng "Row %u was cut by GROUP_CONCAT()"
+ER_WARN_TOO_FEW_RECORDS 01000
+ eng "Row %lu doesn't contain data for all columns"
+ ger "Zeile %lu enthält nicht für alle Felder Daten"
+ nla "Rij %lu bevat niet de data voor alle kolommen"
+ por "Conta de registro é menor que a conta de coluna na linha %lu"
+ spa "Línea %lu no contiene datos para todas las columnas"
+ER_WARN_TOO_MANY_RECORDS 01000
+ eng "Row %lu was truncated; it contained more data than there were input columns"
+ ger "Zeile %lu gekürzt, die Zeile enthielt mehr Daten, als es Eingabefelder gibt"
+ nla "Regel %lu ingekort, bevatte meer data dan invoer kolommen"
+ por "Conta de registro é maior que a conta de coluna na linha %lu"
+ spa "Línea %lu fué truncada; La misma contine mas datos que las que existen en las columnas de entrada"
+ER_WARN_NULL_TO_NOTNULL 22004
+ eng "Column set to default value; NULL supplied to NOT NULL column '%s' at row %lu"
+ ger "Feld auf Vorgabewert gesetzt, da NULL für NOT-NULL-Feld '%s' in Zeile %lu angegeben"
+ por "Dado truncado, NULL fornecido para NOT NULL coluna '%s' na linha %lu"
+ spa "Datos truncado, NULL suministrado para NOT NULL columna '%s' en la línea %lu"
+ER_WARN_DATA_OUT_OF_RANGE 22003
+ eng "Out of range value for column '%s' at row %lu"
+WARN_DATA_TRUNCATED 01000
+ eng "Data truncated for column '%s' at row %lu"
+ ger "Daten abgeschnitten für Feld '%s' in Zeile %lu"
+ por "Dado truncado para coluna '%s' na linha %lu"
+ spa "Datos truncados para columna '%s' en la línea %lu"
+ER_WARN_USING_OTHER_HANDLER
+ eng "Using storage engine %s for table '%s'"
+ ger "Für Tabelle '%s' wird Speicher-Engine %s benutzt"
+ por "Usando engine de armazenamento %s para tabela '%s'"
+ spa "Usando motor de almacenamiento %s para tabla '%s'"
+ swe "Använder handler %s för tabell '%s'"
+ER_CANT_AGGREGATE_2COLLATIONS
+ eng "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'"
+ ger "Unerlaubte Mischung von Sortierreihenfolgen (%s, %s) und (%s, %s) für Operation '%s'"
+ por "Combinação ilegal de collations (%s,%s) e (%s,%s) para operação '%s'"
+ spa "Ilegal mezcla de collations (%s,%s) y (%s,%s) para operación '%s'"
+ER_DROP_USER
+ eng "Cannot drop one or more of the requested users"
+ ger "Kann einen oder mehrere der angegebenen Benutzer nicht löschen"
+ER_REVOKE_GRANTS
+ eng "Can't revoke all privileges for one or more of the requested users"
+ ger "Kann nicht alle Berechtigungen widerrufen, die für einen oder mehrere Benutzer gewährt wurden"
+ por "Não pode revocar todos os privilégios, grant para um ou mais dos usuários pedidos"
+ spa "No puede revocar todos los privilegios, derecho para uno o mas de los usuarios solicitados"
+ER_CANT_AGGREGATE_3COLLATIONS
+ eng "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'"
+ ger "Unerlaubte Mischung von Sortierreihenfolgen (%s, %s), (%s, %s), (%s, %s) für Operation '%s'"
+ por "Ilegal combinação de collations (%s,%s), (%s,%s), (%s,%s) para operação '%s'"
+ spa "Ilegal mezcla de collations (%s,%s), (%s,%s), (%s,%s) para operación '%s'"
+ER_CANT_AGGREGATE_NCOLLATIONS
+ eng "Illegal mix of collations for operation '%s'"
+ ger "Unerlaubte Mischung von Sortierreihenfolgen für Operation '%s'"
+ por "Ilegal combinação de collations para operação '%s'"
+ spa "Ilegal mezcla de collations para operación '%s'"
+ER_VARIABLE_IS_NOT_STRUCT
+ eng "Variable '%-.64s' is not a variable component (can't be used as XXXX.variable_name)"
+ ger "Variable '%-.64s' ist keine Variablen-Komponente (kann nicht als XXXX.variablen_name verwendet werden)"
+ por "Variável '%-.64s' não é uma variável componente (Não pode ser usada como XXXX.variável_nome)"
+ spa "Variable '%-.64s' no es una variable componente (No puede ser usada como XXXX.variable_name)"
+ER_UNKNOWN_COLLATION
+ eng "Unknown collation: '%-.64s'"
+ ger "Unbekannte Sortierreihenfolge: '%-.64s'"
+ por "Collation desconhecida: '%-.64s'"
+ spa "Collation desconocida: '%-.64s'"
+ER_SLAVE_IGNORED_SSL_PARAMS
+ eng "SSL parameters in CHANGE MASTER are ignored because this MariaDB slave was compiled without SSL support; they can be used later if MariaDB slave with SSL is started"
+ ger "SSL-Parameter in CHANGE MASTER werden ignoriert, weil dieser MariaDB-Slave ohne SSL-Unterstützung kompiliert wurde. Sie können aber später verwendet werden, wenn ein MariaDB-Slave mit SSL gestartet wird"
+ por "SSL parâmetros em CHANGE MASTER são ignorados porque este escravo MariaDB foi compilado sem o SSL suporte. Os mesmos podem ser usados mais tarde quando o escravo MariaDB com SSL seja iniciado."
+ spa "Parametros SSL en CHANGE MASTER son ignorados porque este slave MariaDB fue compilado sin soporte SSL; pueden ser usados despues cuando el slave MariaDB con SSL sea inicializado"
+ER_SERVER_IS_IN_SECURE_AUTH_MODE
+ eng "Server is running in --secure-auth mode, but '%s'@'%s' has a password in the old format; please change the password to the new format"
+ ger "Server läuft im Modus --secure-auth, aber '%s'@'%s' hat ein Passwort im alten Format. Bitte Passwort ins neue Format ändern"
+ por "Servidor está rodando em --secure-auth modo, porêm '%s'@'%s' tem senha no formato antigo; por favor troque a senha para o novo formato"
+ rus "Сервер запущен в режиме --secure-auth (безопаÑной авторизации), но Ð´Ð»Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ '%s'@'%s' пароль Ñохранён в Ñтаром формате; необходимо обновить формат паролÑ"
+ spa "Servidor está rodando en modo --secure-auth, pero '%s'@'%s' tiene clave en el antiguo formato; por favor cambie la clave para el nuevo formato"
+ER_WARN_FIELD_RESOLVED
+ eng "Field or reference '%-.192s%s%-.192s%s%-.192s' of SELECT #%d was resolved in SELECT #%d"
+ ger "Feld oder Verweis '%-.192s%s%-.192s%s%-.192s' im SELECT-Befehl Nr. %d wurde im SELECT-Befehl Nr. %d aufgelöst"
+ por "Campo ou referência '%-.192s%s%-.192s%s%-.192s' de SELECT #%d foi resolvido em SELECT #%d"
+ rus "Поле или ÑÑылка '%-.192s%s%-.192s%s%-.192s' из SELECTа #%d была найдена в SELECTе #%d"
+ spa "Campo o referencia '%-.192s%s%-.192s%s%-.192s' de SELECT #%d fue resolvido en SELECT #%d"
+ ukr "Стовбець або поÑÐ¸Ð»Ð°Ð½Ð½Ñ '%-.192s%s%-.192s%s%-.192s' із SELECTу #%d було знайдене у SELECTÑ– #%d"
+ER_BAD_SLAVE_UNTIL_COND
+ eng "Incorrect parameter or combination of parameters for START SLAVE UNTIL"
+ ger "Falscher Parameter oder falsche Kombination von Parametern für START SLAVE UNTIL"
+ por "Parâmetro ou combinação de parâmetros errado para START SLAVE UNTIL"
+ spa "Parametro equivocado o combinación de parametros para START SLAVE UNTIL"
+ER_MISSING_SKIP_SLAVE
+ eng "It is recommended to use --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL; otherwise, you will get problems if you get an unexpected slave's mysqld restart"
+ ger "Es wird empfohlen, mit --skip-slave-start zu starten, wenn mit START SLAVE UNTIL eine Schritt-für-Schritt-Replikation ausgeführt wird. Ansonsten gibt es Probleme, wenn ein Slave-Server unerwartet neu startet"
+ por "É recomendado para rodar com --skip-slave-start quando fazendo replicação passo-por-passo com START SLAVE UNTIL, de outra forma você não está seguro em caso de inesperada reinicialição do mysqld escravo"
+ spa "Es recomendado rodar con --skip-slave-start cuando haciendo replicación step-by-step con START SLAVE UNTIL, a menos que usted no esté seguro en caso de inesperada reinicialización del mysqld slave"
+ER_UNTIL_COND_IGNORED
+ eng "SQL thread is not to be started so UNTIL options are ignored"
+ ger "SQL-Thread soll nicht gestartet werden. Daher werden UNTIL-Optionen ignoriert"
+ por "Thread SQL não pode ser inicializado tal que opções UNTIL são ignoradas"
+ spa "SQL thread no es inicializado tal que opciones UNTIL son ignoradas"
+ER_WRONG_NAME_FOR_INDEX 42000
+ eng "Incorrect index name '%-.100s'"
+ ger "Falscher Indexname '%-.100s'"
+ por "Incorreto nome de índice '%-.100s'"
+ spa "Nombre de índice incorrecto '%-.100s'"
+ swe "Felaktigt index namn '%-.100s'"
+ER_WRONG_NAME_FOR_CATALOG 42000
+ eng "Incorrect catalog name '%-.100s'"
+ ger "Falscher Katalogname '%-.100s'"
+ por "Incorreto nome de catálogo '%-.100s'"
+ spa "Nombre de catalog incorrecto '%-.100s'"
+ swe "Felaktigt katalog namn '%-.100s'"
+ER_WARN_QC_RESIZE
+ eng "Query cache failed to set size %llu; new query cache size is %lu"
+ ger "Änderung der Query-Cache-Größe auf %llu fehlgeschlagen; neue Query-Cache-Größe ist %lu"
+ por "Falha em Query cache para configurar tamanho %llu, novo tamanho de query cache é %lu"
+ rus "Кеш запроÑов не может уÑтановить размер %llu, новый размер кеша зпроÑов - %lu"
+ spa "Query cache fallada para configurar tamaño %llu, nuevo tamaño de query cache es %lu"
+ swe "Storleken av "Query cache" kunde inte sättas till %llu, ny storlek är %lu"
+ ukr "Кеш запитів неÑпроможен вÑтановити розмір %llu, новий розмір кеша запитів - %lu"
+ER_BAD_FT_COLUMN
+ eng "Column '%-.192s' cannot be part of FULLTEXT index"
+ ger "Feld '%-.192s' kann nicht Teil eines FULLTEXT-Index sein"
+ por "Coluna '%-.192s' não pode ser parte de índice FULLTEXT"
+ spa "Columna '%-.192s' no puede ser parte de FULLTEXT index"
+ swe "Kolumn '%-.192s' kan inte vara del av ett FULLTEXT index"
+ER_UNKNOWN_KEY_CACHE
+ eng "Unknown key cache '%-.100s'"
+ ger "Unbekannter Schlüssel-Cache '%-.100s'"
+ por "Key cache desconhecida '%-.100s'"
+ spa "Desconocida key cache '%-.100s'"
+ swe "Okänd nyckel cache '%-.100s'"
+ER_WARN_HOSTNAME_WONT_WORK
+ eng "MariaDB is started in --skip-name-resolve mode; you must restart it without this switch for this grant to work"
+ ger "MariaDB wurde mit --skip-name-resolve gestartet. Diese Option darf nicht verwendet werden, damit diese Rechtevergabe möglich ist"
+ por "MariaDB foi inicializado em modo --skip-name-resolve. Você necesita reincializá-lo sem esta opção para este grant funcionar"
+ spa "MariaDB esta inicializado en modo --skip-name-resolve. Usted necesita reinicializarlo sin esta opción para este derecho funcionar"
+ER_UNKNOWN_STORAGE_ENGINE 42000
+ eng "Unknown storage engine '%s'"
+ ger "Unbekannte Speicher-Engine '%s'"
+ por "Motor de tabela desconhecido '%s'"
+ spa "Desconocido motor de tabla '%s'"
+ER_WARN_DEPRECATED_SYNTAX
+ eng "'%s' is deprecated and will be removed in a future release. Please use %s instead"
+ ger "'%s' ist veraltet. Bitte benutzen Sie '%s'"
+ por "'%s' é desatualizado. Use '%s' em seu lugar"
+ spa "'%s' está desaprobado, use '%s' en su lugar"
+ER_NON_UPDATABLE_TABLE
+ eng "The target table %-.100s of the %s is not updatable"
+ ger "Die Zieltabelle %-.100s von %s ist nicht aktualisierbar"
+ por "A tabela destino %-.100s do %s não é atualizável"
+ rus "Таблица %-.100s в %s не может изменÑÑ‚ÑÑ"
+ spa "La tabla destino %-.100s del %s no es actualizable"
+ swe "Tabell %-.100s använd med '%s' är inte uppdateringsbar"
+ ukr "Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ %-.100s у %s не може оновлюватиÑÑŒ"
+ER_FEATURE_DISABLED
+ eng "The '%s' feature is disabled; you need MariaDB built with '%s' to have it working"
+ ger "Das Feature '%s' ist ausgeschaltet, Sie müssen MariaDB mit '%s' übersetzen, damit es verfügbar ist"
+ por "O recurso '%s' foi desativado; você necessita MariaDB construído com '%s' para ter isto funcionando"
+ spa "El recurso '%s' fue deshabilitado; usted necesita construir MariaDB con '%s' para tener eso funcionando"
+ swe "'%s' är inte aktiverad; För att aktivera detta måste du bygga om MariaDB med '%s' definierad"
+ER_OPTION_PREVENTS_STATEMENT
+ eng "The MariaDB server is running with the %s option so it cannot execute this statement"
+ ger "Der MariaDB-Server läuft mit der Option %s und kann diese Anweisung deswegen nicht ausführen"
+ por "O servidor MariaDB está rodando com a opção %s razão pela qual não pode executar esse commando"
+ spa "El servidor MariaDB está rodando con la opción %s tal que no puede ejecutar este comando"
+ swe "MariaDB är startad med %s. Pga av detta kan du inte använda detta kommando"
+ER_DUPLICATED_VALUE_IN_TYPE
+ eng "Column '%-.100s' has duplicated value '%-.64s' in %s"
+ ger "Feld '%-.100s' hat doppelten Wert '%-.64s' in %s"
+ por "Coluna '%-.100s' tem valor duplicado '%-.64s' em %s"
+ spa "Columna '%-.100s' tiene valor doblado '%-.64s' en %s"
+ER_TRUNCATED_WRONG_VALUE 22007
+ eng "Truncated incorrect %-.32s value: '%-.128s'"
+ ger "Falscher %-.32s-Wert gekürzt: '%-.128s'"
+ por "Truncado errado %-.32s valor: '%-.128s'"
+ spa "Equivocado truncado %-.32s valor: '%-.128s'"
+ER_TOO_MUCH_AUTO_TIMESTAMP_COLS
+ eng "Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause"
+ ger "Fehlerhafte Tabellendefinition. Es kann nur eine einzige TIMESTAMP-Spalte mit CURRENT_TIMESTAMP als DEFAULT oder in einer ON-UPDATE-Klausel geben"
+ por "Incorreta definição de tabela; Pode ter somente uma coluna TIMESTAMP com CURRENT_TIMESTAMP em DEFAULT ou ON UPDATE cláusula"
+ spa "Incorrecta definición de tabla; Solamente debe haber una columna TIMESTAMP con CURRENT_TIMESTAMP en DEFAULT o ON UPDATE cláusula"
+ER_INVALID_ON_UPDATE
+ eng "Invalid ON UPDATE clause for '%-.192s' column"
+ ger "Ungültige ON-UPDATE-Klausel für Spalte '%-.192s'"
+ por "Inválida cláusula ON UPDATE para campo '%-.192s'"
+ spa "Inválido ON UPDATE cláusula para campo '%-.192s'"
+ER_UNSUPPORTED_PS
+ eng "This command is not supported in the prepared statement protocol yet"
+ ger "Dieser Befehl wird im Protokoll für vorbereitete Anweisungen noch nicht unterstützt"
+ER_GET_ERRMSG
+ dan "Modtog fejl %d '%-.200s' fra %s"
+ eng "Got error %d '%-.200s' from %s"
+ ger "Fehler %d '%-.200s' von %s"
+ nor "Mottok feil %d '%-.200s' fa %s"
+ norwegian-ny "Mottok feil %d '%-.200s' fra %s"
+ER_GET_TEMPORARY_ERRMSG
+ dan "Modtog temporary fejl %d '%-.200s' fra %s"
+ eng "Got temporary error %d '%-.200s' from %s"
+ ger "Temporärer Fehler %d '%-.200s' von %s"
+ nor "Mottok temporary feil %d '%-.200s' fra %s"
+ norwegian-ny "Mottok temporary feil %d '%-.200s' fra %s"
+ER_UNKNOWN_TIME_ZONE
+ eng "Unknown or incorrect time zone: '%-.64s'"
+ ger "Unbekannte oder falsche Zeitzone: '%-.64s'"
+ER_WARN_INVALID_TIMESTAMP
+ eng "Invalid TIMESTAMP value in column '%s' at row %lu"
+ ger "Ungültiger TIMESTAMP-Wert in Feld '%s', Zeile %lu"
+ER_INVALID_CHARACTER_STRING
+ eng "Invalid %s character string: '%.64s'"
+ ger "Ungültiger %s-Zeichen-String: '%.64s'"
+ER_WARN_ALLOWED_PACKET_OVERFLOWED
+ eng "Result of %s() was larger than max_allowed_packet (%ld) - truncated"
+ ger "Ergebnis von %s() war größer als max_allowed_packet (%ld) Bytes und wurde deshalb gekürzt"
+ER_CONFLICTING_DECLARATIONS
+ eng "Conflicting declarations: '%s%s' and '%s%s'"
+ ger "Widersprüchliche Deklarationen: '%s%s' und '%s%s'"
+ER_SP_NO_RECURSIVE_CREATE 2F003
+ eng "Can't create a %s from within another stored routine"
+ ger "Kann kein %s innerhalb einer anderen gespeicherten Routine erzeugen"
+ER_SP_ALREADY_EXISTS 42000
+ eng "%s %s already exists"
+ ger "%s %s existiert bereits"
+ER_SP_DOES_NOT_EXIST 42000
+ eng "%s %s does not exist"
+ ger "%s %s existiert nicht"
+ER_SP_DROP_FAILED
+ eng "Failed to DROP %s %s"
+ ger "DROP %s %s ist fehlgeschlagen"
+ER_SP_STORE_FAILED
+ eng "Failed to CREATE %s %s"
+ ger "CREATE %s %s ist fehlgeschlagen"
+ER_SP_LILABEL_MISMATCH 42000
+ eng "%s with no matching label: %s"
+ ger "%s ohne passende Marke: %s"
+ER_SP_LABEL_REDEFINE 42000
+ eng "Redefining label %s"
+ ger "Neudefinition der Marke %s"
+ER_SP_LABEL_MISMATCH 42000
+ eng "End-label %s without match"
+ ger "Ende-Marke %s ohne zugehörigen Anfang"
+ER_SP_UNINIT_VAR 01000
+ eng "Referring to uninitialized variable %s"
+ ger "Zugriff auf nichtinitialisierte Variable %s"
+ER_SP_BADSELECT 0A000
+ eng "PROCEDURE %s can't return a result set in the given context"
+ ger "PROCEDURE %s kann im gegebenen Kontext keine Ergebnismenge zurückgeben"
+ER_SP_BADRETURN 42000
+ eng "RETURN is only allowed in a FUNCTION"
+ ger "RETURN ist nur innerhalb einer FUNCTION erlaubt"
+ER_SP_BADSTATEMENT 0A000
+ eng "%s is not allowed in stored procedures"
+ ger "%s ist in gespeicherten Prozeduren nicht erlaubt"
+ER_UPDATE_LOG_DEPRECATED_IGNORED 42000
+ eng "The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored. This option will be removed in MariaDB 5.6."
+ ger "Das Update-Log ist veraltet und wurde durch das Binär-Log ersetzt. SET SQL_LOG_UPDATE wird ignoriert. Diese Option wird in MariaDB 5.6 entfernt."
+ER_UPDATE_LOG_DEPRECATED_TRANSLATED 42000
+ eng "The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN. This option will be removed in MariaDB 5.6."
+ ger "Das Update-Log ist veraltet und wurde durch das Binär-Log ersetzt. SET SQL_LOG_UPDATE wurde in SET SQL_LOG_BIN übersetzt. Diese Option wird in MariaDB 5.6 entfernt."
+ER_QUERY_INTERRUPTED 70100
+ eng "Query execution was interrupted"
+ ger "Ausführung der Abfrage wurde unterbrochen"
+ER_SP_WRONG_NO_OF_ARGS 42000
+ eng "Incorrect number of arguments for %s %s; expected %u, got %u"
+ ger "Falsche Anzahl von Argumenten für %s %s; erwarte %u, erhalte %u"
+ER_SP_COND_MISMATCH 42000
+ eng "Undefined CONDITION: %s"
+ ger "Undefinierte CONDITION: %s"
+ER_SP_NORETURN 42000
+ eng "No RETURN found in FUNCTION %s"
+ ger "Kein RETURN in FUNCTION %s gefunden"
+ER_SP_NORETURNEND 2F005
+ eng "FUNCTION %s ended without RETURN"
+ ger "FUNCTION %s endete ohne RETURN"
+ER_SP_BAD_CURSOR_QUERY 42000
+ eng "Cursor statement must be a SELECT"
+ ger "Cursor-Anweisung muss ein SELECT sein"
+ER_SP_BAD_CURSOR_SELECT 42000
+ eng "Cursor SELECT must not have INTO"
+ ger "Cursor-SELECT darf kein INTO haben"
+ER_SP_CURSOR_MISMATCH 42000
+ eng "Undefined CURSOR: %s"
+ ger "Undefinierter CURSOR: %s"
+ER_SP_CURSOR_ALREADY_OPEN 24000
+ eng "Cursor is already open"
+ ger "Cursor ist schon geöffnet"
+ER_SP_CURSOR_NOT_OPEN 24000
+ eng "Cursor is not open"
+ ger "Cursor ist nicht geöffnet"
+ER_SP_UNDECLARED_VAR 42000
+ eng "Undeclared variable: %s"
+ ger "Nicht deklarierte Variable: %s"
+ER_SP_WRONG_NO_OF_FETCH_ARGS
+ eng "Incorrect number of FETCH variables"
+ ger "Falsche Anzahl von FETCH-Variablen"
+ER_SP_FETCH_NO_DATA 02000
+ eng "No data - zero rows fetched, selected, or processed"
+ ger "Keine Daten - null Zeilen geholt (fetch), ausgewählt oder verarbeitet"
+ER_SP_DUP_PARAM 42000
+ eng "Duplicate parameter: %s"
+ ger "Doppelter Parameter: %s"
+ER_SP_DUP_VAR 42000
+ eng "Duplicate variable: %s"
+ ger "Doppelte Variable: %s"
+ER_SP_DUP_COND 42000
+ eng "Duplicate condition: %s"
+ ger "Doppelte Bedingung: %s"
+ER_SP_DUP_CURS 42000
+ eng "Duplicate cursor: %s"
+ ger "Doppelter Cursor: %s"
+ER_SP_CANT_ALTER
+ eng "Failed to ALTER %s %s"
+ ger "ALTER %s %s fehlgeschlagen"
+ER_SP_SUBSELECT_NYI 0A000
+ eng "Subquery value not supported"
+ ger "Subquery-Wert wird nicht unterstützt"
+ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG 0A000
+ eng "%s is not allowed in stored function or trigger"
+ ger "%s ist in gespeicherten Funktionen und in Triggern nicht erlaubt"
+ER_SP_VARCOND_AFTER_CURSHNDLR 42000
+ eng "Variable or condition declaration after cursor or handler declaration"
+ ger "Deklaration einer Variablen oder einer Bedingung nach der Deklaration eines Cursors oder eines Handlers"
+ER_SP_CURSOR_AFTER_HANDLER 42000
+ eng "Cursor declaration after handler declaration"
+ ger "Deklaration eines Cursors nach der Deklaration eines Handlers"
+ER_SP_CASE_NOT_FOUND 20000
+ eng "Case not found for CASE statement"
+ ger "Fall für CASE-Anweisung nicht gefunden"
+ER_FPARSER_TOO_BIG_FILE
+ eng "Configuration file '%-.192s' is too big"
+ ger "Konfigurationsdatei '%-.192s' ist zu groß"
+ rus "Слишком большой конфигурационный файл '%-.192s'"
+ ukr "Занадто великий конфігураційний файл '%-.192s'"
+ER_FPARSER_BAD_HEADER
+ eng "Malformed file type header in file '%-.192s'"
+ ger "Nicht wohlgeformter Dateityp-Header in Datei '%-.192s'"
+ rus "Ðеверный заголовок типа файла '%-.192s'"
+ ukr "Ðевірний заголовок типу у файлі '%-.192s'"
+ER_FPARSER_EOF_IN_COMMENT
+ eng "Unexpected end of file while parsing comment '%-.200s'"
+ ger "Unerwartetes Dateiende beim Parsen des Kommentars '%-.200s'"
+ rus "Ðеожиданный конец файла в коментарии '%-.200s'"
+ ukr "ÐеÑподіванний кінець файлу у коментарі '%-.200s'"
+ER_FPARSER_ERROR_IN_PARAMETER
+ eng "Error while parsing parameter '%-.192s' (line: '%-.192s')"
+ ger "Fehler beim Parsen des Parameters '%-.192s' (Zeile: '%-.192s')"
+ rus "Ошибка при раÑпознавании параметра '%-.192s' (Ñтрока: '%-.192s')"
+ ukr "Помилка в роÑпізнаванні параметру '%-.192s' (Ñ€Ñдок: '%-.192s')"
+ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER
+ eng "Unexpected end of file while skipping unknown parameter '%-.192s'"
+ ger "Unerwartetes Dateiende beim Ãœberspringen des unbekannten Parameters '%-.192s'"
+ rus "Ðеожиданный конец файла при пропуÑке неизвеÑтного параметра '%-.192s'"
+ ukr "ÐеÑподіванний кінець файлу у Ñпробі проминути невідомий параметр '%-.192s'"
+ER_VIEW_NO_EXPLAIN
+ eng "EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+ ger "EXPLAIN/SHOW kann nicht verlangt werden. Rechte für zugrunde liegende Tabelle fehlen"
+ rus "EXPLAIN/SHOW не может быть выполненно; недоÑтаточно прав на таблицы запроÑа"
+ ukr "EXPLAIN/SHOW не може бути виконано; немає прав на тиблиці запиту"
+ER_FRM_UNKNOWN_TYPE
+ eng "File '%-.192s' has unknown type '%-.64s' in its header"
+ ger "Datei '%-.192s' hat unbekannten Typ '%-.64s' im Header"
+ rus "Файл '%-.192s' Ñодержит неизвеÑтный тип '%-.64s' в заголовке"
+ ukr "Файл '%-.192s' має невідомий тип '%-.64s' у заголовку"
+ER_WRONG_OBJECT
+ eng "'%-.192s.%-.192s' is not %s"
+ ger "'%-.192s.%-.192s' ist nicht %s"
+ rus "'%-.192s.%-.192s' - не %s"
+ ukr "'%-.192s.%-.192s' не є %s"
+ER_NONUPDATEABLE_COLUMN
+ eng "Column '%-.192s' is not updatable"
+ ger "Feld '%-.192s' ist nicht aktualisierbar"
+ rus "Столбец '%-.192s' не обновлÑемый"
+ ukr "Стовбець '%-.192s' не може бути зминений"
+ER_VIEW_SELECT_DERIVED
+ eng "View's SELECT contains a subquery in the FROM clause"
+ ger "SELECT der View enthält eine Subquery in der FROM-Klausel"
+ rus "View SELECT Ñодержит Ð¿Ð¾Ð´Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð² конÑтрукции FROM"
+ ukr "View SELECT має підзапит у конÑтрукції FROM"
+ER_VIEW_SELECT_CLAUSE
+ eng "View's SELECT contains a '%s' clause"
+ ger "SELECT der View enthält eine '%s'-Klausel"
+ rus "View SELECT Ñодержит конÑтрукцию '%s'"
+ ukr "View SELECT має конÑтрукцію '%s'"
+ER_VIEW_SELECT_VARIABLE
+ eng "View's SELECT contains a variable or parameter"
+ ger "SELECT der View enthält eine Variable oder einen Parameter"
+ rus "View SELECT Ñодержит переменную или параметр"
+ ukr "View SELECT має зминну або параметер"
+ER_VIEW_SELECT_TMPTABLE
+ eng "View's SELECT refers to a temporary table '%-.192s'"
+ ger "SELECT der View verweist auf eine temporäre Tabelle '%-.192s'"
+ rus "View SELECT Ñодержит ÑÑылку на временную таблицу '%-.192s'"
+ ukr "View SELECT викориÑтовує тимчаÑову таблицю '%-.192s'"
+ER_VIEW_WRONG_LIST
+ eng "View's SELECT and view's field list have different column counts"
+ ger "SELECT- und Feldliste der Views haben unterschiedliche Anzahlen von Spalten"
+ rus "View SELECT и ÑпиÑок полей view имеют разное количеÑтво Ñтолбцов"
+ ukr "View SELECT Ñ– перелік Ñтовбців view мають різну кількіÑÑ‚ÑŒ Ñковбців"
+ER_WARN_VIEW_MERGE
+ eng "View merge algorithm can't be used here for now (assumed undefined algorithm)"
+ ger "View-Merge-Algorithmus kann hier momentan nicht verwendet werden (undefinierter Algorithmus wird angenommen)"
+ rus "Ðлгоритм ÑлиÑÐ½Ð¸Ñ view не может быть иÑпользован ÑÐµÐ¹Ñ‡Ð°Ñ (алгоритм будет неопеределенным)"
+ ukr "Ðлгоритм Ð·Ð»Ð¸Ð²Ð°Ð½Ð½Ñ view не може бути викориÑтаний зараз (алгоритм буде невизначений)"
+ER_WARN_VIEW_WITHOUT_KEY
+ eng "View being updated does not have complete key of underlying table in it"
+ ger "Die aktualisierte View enthält nicht den vollständigen Schlüssel der zugrunde liegenden Tabelle"
+ rus "ОбновлÑемый view не Ñодержит ключа иÑпользованных(ой) в нем таблиц(Ñ‹)"
+ ukr "View, що оновлюетьÑÑ, не міÑтить повного ключа таблиці(ÑŒ), що викоріÑтана в ньюому"
+ER_VIEW_INVALID
+ eng "View '%-.192s.%-.192s' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them"
+ER_SP_NO_DROP_SP
+ eng "Can't drop or alter a %s from within another stored routine"
+ ger "Kann eine %s nicht von innerhalb einer anderen gespeicherten Routine löschen oder ändern"
+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"
+ER_TRG_DOES_NOT_EXIST
+ eng "Trigger does not exist"
+ ger "Trigger existiert nicht"
+ER_TRG_ON_VIEW_OR_TEMP_TABLE
+ eng "Trigger's '%-.192s' is view or temporary table"
+ ger "'%-.192s' des Triggers ist View oder temporäre Tabelle"
+ER_TRG_CANT_CHANGE_ROW
+ eng "Updating of %s row is not allowed in %strigger"
+ ger "Aktualisieren einer %s-Zeile ist in einem %s-Trigger nicht erlaubt"
+ER_TRG_NO_SUCH_ROW_IN_TRG
+ eng "There is no %s row in %s trigger"
+ ger "Es gibt keine %s-Zeile im %s-Trigger"
+ER_NO_DEFAULT_FOR_FIELD
+ eng "Field '%-.192s' doesn't have a default value"
+ ger "Feld '%-.192s' hat keinen Vorgabewert"
+ER_DIVISION_BY_ZERO 22012
+ eng "Division by 0"
+ ger "Division durch 0"
+ER_TRUNCATED_WRONG_VALUE_FOR_FIELD 22007
+ eng "Incorrect %-.32s value: '%-.128s' for column '%.192s' at row %lu"
+ ger "Falscher %-.32s-Wert: '%-.128s' für Feld '%.192s' in Zeile %lu"
+ER_ILLEGAL_VALUE_FOR_TYPE 22007
+ eng "Illegal %s '%-.192s' value found during parsing"
+ ger "Nicht zulässiger %s-Wert '%-.192s' beim Parsen gefunden"
+ER_VIEW_NONUPD_CHECK
+ eng "CHECK OPTION on non-updatable view '%-.192s.%-.192s'"
+ ger "CHECK OPTION auf nicht-aktualisierbarem View '%-.192s.%-.192s'"
+ rus "CHECK OPTION Ð´Ð»Ñ Ð½ÐµÐ¾Ð±Ð½Ð¾Ð²Ð»Ñемого VIEW '%-.192s.%-.192s'"
+ ukr "CHECK OPTION Ð´Ð»Ñ VIEW '%-.192s.%-.192s' що не може бути оновленним"
+ER_VIEW_CHECK_FAILED
+ eng "CHECK OPTION failed '%-.192s.%-.192s'"
+ ger "CHECK OPTION fehlgeschlagen: '%-.192s.%-.192s'"
+ rus "проверка CHECK OPTION Ð´Ð»Ñ VIEW '%-.192s.%-.192s' провалилаÑÑŒ"
+ ukr "Перевірка CHECK OPTION Ð´Ð»Ñ VIEW '%-.192s.%-.192s' не пройшла"
+ER_PROCACCESS_DENIED_ERROR 42000
+ eng "%-.16s command denied to user '%s'@'%s' for routine '%-.192s'"
+ ger "Befehl %-.16s nicht zulässig für Benutzer '%s'@'%s' in Routine '%-.192s'"
+ER_RELAY_LOG_FAIL
+ eng "Failed purging old relay logs: %s"
+ ger "Bereinigen alter Relais-Logs fehlgeschlagen: %s"
+ER_PASSWD_LENGTH
+ eng "Password hash should be a %d-digit hexadecimal number"
+ ger "Passwort-Hash sollte eine Hexdaezimalzahl mit %d Stellen sein"
+ER_UNKNOWN_TARGET_BINLOG
+ eng "Target log not found in binlog index"
+ ger "Ziel-Log im Binlog-Index nicht gefunden"
+ER_IO_ERR_LOG_INDEX_READ
+ eng "I/O error reading log index file"
+ ger "Fehler beim Lesen der Log-Index-Datei"
+ER_BINLOG_PURGE_PROHIBITED
+ eng "Server configuration does not permit binlog purge"
+ ger "Server-Konfiguration erlaubt keine Binlog-Bereinigung"
+ER_FSEEK_FAIL
+ eng "Failed on fseek()"
+ ger "fseek() fehlgeschlagen"
+ER_BINLOG_PURGE_FATAL_ERR
+ eng "Fatal error during log purge"
+ ger "Schwerwiegender Fehler bei der Log-Bereinigung"
+ER_LOG_IN_USE
+ eng "A purgeable log is in use, will not purge"
+ ger "Ein zu bereinigendes Log wird gerade benutzt, daher keine Bereinigung"
+ER_LOG_PURGE_UNKNOWN_ERR
+ eng "Unknown error during log purge"
+ ger "Unbekannter Fehler bei Log-Bereinigung"
+ER_RELAY_LOG_INIT
+ eng "Failed initializing relay log position: %s"
+ ger "Initialisierung der Relais-Log-Position fehlgeschlagen: %s"
+ER_NO_BINARY_LOGGING
+ eng "You are not using binary logging"
+ ger "Sie verwenden keine Binärlogs"
+ER_RESERVED_SYNTAX
+ eng "The '%-.64s' syntax is reserved for purposes internal to the MariaDB server"
+ ger "Die Schreibweise '%-.64s' ist für interne Zwecke des MariaDB-Servers reserviert"
+ER_WSAS_FAILED
+ eng "WSAStartup Failed"
+ ger "WSAStartup fehlgeschlagen"
+ER_DIFF_GROUPS_PROC
+ eng "Can't handle procedures with different groups yet"
+ ger "Kann Prozeduren mit unterschiedlichen Gruppen noch nicht verarbeiten"
+ER_NO_GROUP_FOR_PROC
+ eng "Select must have a group with this procedure"
+ ger "SELECT muss bei dieser Prozedur ein GROUP BY haben"
+ER_ORDER_WITH_PROC
+ eng "Can't use ORDER clause with this procedure"
+ ger "Kann bei dieser Prozedur keine ORDER-BY-Klausel verwenden"
+ER_LOGGING_PROHIBIT_CHANGING_OF
+ eng "Binary logging and replication forbid changing the global server %s"
+ ger "Binärlogs und Replikation verhindern Wechsel des globalen Servers %s"
+ER_NO_FILE_MAPPING
+ eng "Can't map file: %-.200s, errno: %d"
+ ger "Kann Datei nicht abbilden: %-.200s, Fehler: %d"
+ER_WRONG_MAGIC
+ eng "Wrong magic in %-.64s"
+ ger "Falsche magische Zahlen in %-.64s"
+ER_PS_MANY_PARAM
+ eng "Prepared statement contains too many placeholders"
+ ger "Vorbereitete Anweisung enthält zu viele Platzhalter"
+ER_KEY_PART_0
+ eng "Key part '%-.192s' length cannot be 0"
+ ger "Länge des Schlüsselteils '%-.192s' kann nicht 0 sein"
+ER_VIEW_CHECKSUM
+ eng "View text checksum failed"
+ ger "View-Text-Prüfsumme fehlgeschlagen"
+ rus "Проверка контрольной Ñуммы текÑта VIEW провалилаÑÑŒ"
+ ukr "Перевірка контрольної Ñуми текÑту VIEW не пройшла"
+ER_VIEW_MULTIUPDATE
+ eng "Can not modify more than one base table through a join view '%-.192s.%-.192s'"
+ ger "Kann nicht mehr als eine Basistabelle über Join-View '%-.192s.%-.192s' ändern"
+ rus "ÐÐµÐ»ÑŒÐ·Ñ Ð¸Ð·Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ больше чем одну базовую таблицу иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð¼Ð½Ð¾Ð³Ð¾Ñ‚Ð°Ð±Ð»Ð¸Ñ‡Ð½Ñ‹Ð¹ VIEW '%-.192s.%-.192s'"
+ ukr "Ðеможливо оновити більш ниж одну базову таблицю выкориÑтовуючи VIEW '%-.192s.%-.192s', що міÑÑ‚Ñ–Ñ‚ÑŒ декілька таблиць"
+ER_VIEW_NO_INSERT_FIELD_LIST
+ eng "Can not insert into join view '%-.192s.%-.192s' without fields list"
+ ger "Kann nicht ohne Feldliste in Join-View '%-.192s.%-.192s' einfügen"
+ rus "ÐÐµÐ»ÑŒÐ·Ñ Ð²ÑтавлÑÑ‚ÑŒ запиÑи в многотабличный VIEW '%-.192s.%-.192s' без ÑпиÑка полей"
+ ukr "Ðеможливо уÑтавити Ñ€Ñдки у VIEW '%-.192s.%-.192s', що міÑтить декілька таблиць, без ÑпиÑку Ñтовбців"
+ER_VIEW_DELETE_MERGE_VIEW
+ eng "Can not delete from join view '%-.192s.%-.192s'"
+ ger "Kann nicht aus Join-View '%-.192s.%-.192s' löschen"
+ rus "ÐÐµÐ»ÑŒÐ·Ñ ÑƒÐ´Ð°Ð»ÑÑ‚ÑŒ из многотабличного VIEW '%-.192s.%-.192s'"
+ ukr "Ðеможливо видалити Ñ€Ñдки у VIEW '%-.192s.%-.192s', що міÑтить декілька таблиць"
+ER_CANNOT_USER
+ eng "Operation %s failed for %.256s"
+ ger "Operation %s schlug fehl für %.256s"
+ norwegian-ny "Operation %s failed for '%.256s'"
+ER_XAER_NOTA XAE04
+ eng "XAER_NOTA: Unknown XID"
+ ger "XAER_NOTA: Unbekannte XID"
+ER_XAER_INVAL XAE05
+ eng "XAER_INVAL: Invalid arguments (or unsupported command)"
+ ger "XAER_INVAL: Ungültige Argumente (oder nicht unterstützter Befehl)"
+ER_XAER_RMFAIL XAE07
+ eng "XAER_RMFAIL: The command cannot be executed when global transaction is in the %.64s state"
+ ger "XAER_RMFAIL: DEr Befehl kann nicht ausgeführt werden, wenn die globale Transaktion im Zustand %.64s ist"
+ rus "XAER_RMFAIL: Ñту команду Ð½ÐµÐ»ÑŒÐ·Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÑÑ‚ÑŒ когда Ð³Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ð°Ñ Ñ‚Ñ€Ð°Ð½Ð·Ð°ÐºÑ†Ð¸Ñ Ð½Ð°Ñ…Ð¾Ð´Ð¸Ñ‚ÑÑ Ð² ÑоÑтоÑнии '%.64s'"
+ER_XAER_OUTSIDE XAE09
+ eng "XAER_OUTSIDE: Some work is done outside global transaction"
+ ger "XAER_OUTSIDE: Einige Arbeiten werden außerhalb der globalen Transaktion verrichtet"
+ER_XAER_RMERR XAE03
+ eng "XAER_RMERR: Fatal error occurred in the transaction branch - check your data for consistency"
+ ger "XAER_RMERR: Schwerwiegender Fehler im Transaktionszweig - prüfen Sie Ihre Daten auf Konsistenz"
+ER_XA_RBROLLBACK XA100
+ eng "XA_RBROLLBACK: Transaction branch was rolled back"
+ ger "XA_RBROLLBACK: Transaktionszweig wurde zurückgerollt"
+ER_NONEXISTING_PROC_GRANT 42000
+ eng "There is no such grant defined for user '%-.48s' on host '%-.64s' on routine '%-.192s'"
+ ger "Es gibt diese Berechtigung für Benutzer '%-.48s' auf Host '%-.64s' für Routine '%-.192s' nicht"
+ER_PROC_AUTO_GRANT_FAIL
+ eng "Failed to grant EXECUTE and ALTER ROUTINE privileges"
+ ger "Gewährung von EXECUTE- und ALTER-ROUTINE-Rechten fehlgeschlagen"
+ER_PROC_AUTO_REVOKE_FAIL
+ eng "Failed to revoke all privileges to dropped routine"
+ ger "Rücknahme aller Rechte für die gelöschte Routine fehlgeschlagen"
+ER_DATA_TOO_LONG 22001
+ eng "Data too long for column '%s' at row %lu"
+ ger "Daten zu lang für Feld '%s' in Zeile %lu"
+ER_SP_BAD_SQLSTATE 42000
+ eng "Bad SQLSTATE: '%s'"
+ ger "Ungültiger SQLSTATE: '%s'"
+ER_STARTUP
+ eng "%s: ready for connections.\nVersion: '%s' socket: '%s' port: %d %s"
+ ger "%s: bereit für Verbindungen.\nVersion: '%s' Socket: '%s' Port: %d %s"
+ER_LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR
+ eng "Can't load value from file with fixed size rows to variable"
+ ger "Kann Wert aus Datei mit Zeilen fester Größe nicht in Variable laden"
+ER_CANT_CREATE_USER_WITH_GRANT 42000
+ eng "You are not allowed to create a user with GRANT"
+ ger "Sie dürfen keinen Benutzer mit GRANT anlegen"
+ER_WRONG_VALUE_FOR_TYPE
+ eng "Incorrect %-.32s value: '%-.128s' for function %-.32s"
+ ger "Falscher %-.32s-Wert: '%-.128s' für Funktion %-.32s"
+ER_TABLE_DEF_CHANGED
+ eng "Table definition has changed, please retry transaction"
+ ger "Tabellendefinition wurde geändert, bitte starten Sie die Transaktion neu"
+ER_SP_DUP_HANDLER 42000
+ eng "Duplicate handler declared in the same block"
+ ger "Doppelter Handler im selben Block deklariert"
+ER_SP_NOT_VAR_ARG 42000
+ eng "OUT or INOUT argument %d for routine %s is not a variable or NEW pseudo-variable in BEFORE trigger"
+ ger "OUT- oder INOUT-Argument %d für Routine %s ist keine Variable"
+ER_SP_NO_RETSET 0A000
+ eng "Not allowed to return a result set from a %s"
+ ger "Rückgabe einer Ergebnismenge aus einer %s ist nicht erlaubt"
+ER_CANT_CREATE_GEOMETRY_OBJECT 22003
+ eng "Cannot get geometry object from data you send to the GEOMETRY field"
+ ger "Kann kein Geometrieobjekt aus den Daten machen, die Sie dem GEOMETRY-Feld übergeben haben"
+ER_FAILED_ROUTINE_BREAK_BINLOG
+ eng "A routine failed and has neither NO SQL nor READS SQL DATA in its declaration and binary logging is enabled; if non-transactional tables were updated, the binary log will miss their changes"
+ ger "Eine Routine, die weder NO SQL noch READS SQL DATA in der Deklaration hat, schlug fehl und Binärlogging ist aktiv. Wenn Nicht-Transaktions-Tabellen aktualisiert wurden, enthält das Binärlog ihre Änderungen nicht"
+ER_BINLOG_UNSAFE_ROUTINE
+ eng "This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)"
+ ger "Diese Routine hat weder DETERMINISTIC, NO SQL noch READS SQL DATA in der Deklaration und Binärlogging ist aktiv (*vielleicht* sollten Sie die weniger sichere Variable log_bin_trust_function_creators verwenden)"
+ER_BINLOG_CREATE_ROUTINE_NEED_SUPER
+ eng "You do not have the SUPER privilege and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)"
+ ger "Sie haben keine SUPER-Berechtigung und Binärlogging ist aktiv (*vielleicht* sollten Sie die weniger sichere Variable log_bin_trust_function_creators verwenden)"
+ER_EXEC_STMT_WITH_OPEN_CURSOR
+ eng "You can't execute a prepared statement which has an open cursor associated with it. Reset the statement to re-execute it."
+ ger "Sie können keine vorbereitete Anweisung ausführen, die mit einem geöffneten Cursor verknüpft ist. Setzen Sie die Anweisung zurück, um sie neu auszuführen"
+ER_STMT_HAS_NO_OPEN_CURSOR
+ eng "The statement (%lu) has no open cursor."
+ ger "Die Anweisung (%lu) hat keinen geöffneten Cursor"
+ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG
+ eng "Explicit or implicit commit is not allowed in stored function or trigger."
+ ger "Explizites oder implizites Commit ist in gespeicherten Funktionen und in Triggern nicht erlaubt"
+ER_NO_DEFAULT_FOR_VIEW_FIELD
+ eng "Field of view '%-.192s.%-.192s' underlying table doesn't have a default value"
+ ger "Ein Feld der dem View '%-.192s.%-.192s' zugrundeliegenden Tabelle hat keinen Vorgabewert"
+ER_SP_NO_RECURSION
+ eng "Recursive stored functions and triggers are not allowed."
+ ger "Rekursive gespeicherte Routinen und Triggers sind nicht erlaubt"
+ER_TOO_BIG_SCALE 42000 S1009
+ eng "Too big scale %u specified for '%-.192s'. Maximum is %lu."
+ ger "Zu großer Skalierungsfaktor %u für '%-.192s' angegeben. Maximum ist %lu"
+ER_TOO_BIG_PRECISION 42000 S1009
+ eng "Too big precision %u specified for '%-.192s'. Maximum is %lu."
+ ger "Zu große Genauigkeit %u für '%-.192s' angegeben. Maximum ist %lu"
+ER_M_BIGGER_THAN_D 42000 S1009
+ eng "For float(M,D), double(M,D) or decimal(M,D), M must be >= D (column '%-.192s')."
+ ger "Für FLOAT(M,D), DOUBLE(M,D) oder DECIMAL(M,D) muss M >= D sein (Feld '%-.192s')"
+ER_WRONG_LOCK_OF_SYSTEM_TABLE
+ eng "You can't combine write-locking of system tables with other tables or lock types"
+ ger "Sie können Schreibsperren auf der Systemtabelle nicht mit anderen Tabellen kombinieren"
+ER_CONNECT_TO_FOREIGN_DATA_SOURCE
+ eng "Unable to connect to foreign data source: %.64s"
+ ger "Kann nicht mit Fremddatenquelle verbinden: %.64s"
+ER_QUERY_ON_FOREIGN_DATA_SOURCE
+ eng "There was a problem processing the query on the foreign data source. Data source error: %-.64s"
+ ger "Bei der Verarbeitung der Abfrage ist in der Fremddatenquelle ein Problem aufgetreten. Datenquellenfehlermeldung: %-.64s"
+ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST
+ eng "The foreign data source you are trying to reference does not exist. Data source error: %-.64s"
+ ger "Die Fremddatenquelle, auf die Sie zugreifen wollen, existiert nicht. Datenquellenfehlermeldung: %-.64s"
+ER_FOREIGN_DATA_STRING_INVALID_CANT_CREATE
+ eng "Can't create federated table. The data source connection string '%-.64s' is not in the correct format"
+ ger "Kann föderierte Tabelle nicht erzeugen. Der Datenquellen-Verbindungsstring '%-.64s' hat kein korrektes Format"
+ER_FOREIGN_DATA_STRING_INVALID
+ eng "The data source connection string '%-.64s' is not in the correct format"
+ ger "Der Datenquellen-Verbindungsstring '%-.64s' hat kein korrektes Format"
+ER_CANT_CREATE_FEDERATED_TABLE
+ eng "Can't create federated table. Foreign data src error: %-.64s"
+ ger "Kann föderierte Tabelle nicht erzeugen. Fremddatenquellenfehlermeldung: %-.64s"
+ER_TRG_IN_WRONG_SCHEMA
+ eng "Trigger in wrong schema"
+ ger "Trigger im falschen Schema"
+ER_STACK_OVERRUN_NEED_MORE
+ eng "Thread stack overrun: %ld bytes used of a %ld byte stack, and %ld bytes needed. Use 'mysqld --thread_stack=#' to specify a bigger stack."
+ ger "Thread-Stack-Überlauf: %ld Bytes eines %ld-Byte-Stacks in Verwendung, und %ld Bytes benötigt. Verwenden Sie 'mysqld --thread_stack=#', um einen größeren Stack anzugeben"
+ER_TOO_LONG_BODY 42000 S1009
+ eng "Routine body for '%-.100s' is too long"
+ ger "Routinen-Body für '%-.100s' ist zu lang"
+ER_WARN_CANT_DROP_DEFAULT_KEYCACHE
+ eng "Cannot drop default keycache"
+ ger "Der vorgabemäßige Schlüssel-Cache kann nicht gelöscht werden"
+ER_TOO_BIG_DISPLAYWIDTH 42000 S1009
+ eng "Display width out of range for '%-.192s' (max = %lu)"
+ ger "Anzeigebreite außerhalb des zulässigen Bereichs für '%-.192s' (Maximum: %lu)"
+ER_XAER_DUPID XAE08
+ eng "XAER_DUPID: The XID already exists"
+ ger "XAER_DUPID: Die XID existiert bereits"
+ER_DATETIME_FUNCTION_OVERFLOW 22008
+ eng "Datetime function: %-.32s field overflow"
+ ger "Datetime-Funktion: %-.32s Feldüberlauf"
+ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG
+ eng "Can't update table '%-.192s' in stored function/trigger because it is already used by statement which invoked this stored function/trigger."
+ ger "Kann Tabelle '%-.192s' in gespeicherter Funktion oder Trigger nicht aktualisieren, weil sie bereits von der Anweisung verwendet wird, die diese gespeicherte Funktion oder den Trigger aufrief"
+ER_VIEW_PREVENT_UPDATE
+ eng "The definition of table '%-.192s' prevents operation %.192s on table '%-.192s'."
+ ger "Die Definition der Tabelle '%-.192s' verhindert die Operation %.192s auf Tabelle '%-.192s'"
+ER_PS_NO_RECURSION
+ eng "The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner"
+ ger "Die vorbereitete Anweisung enthält einen Aufruf einer gespeicherten Routine, die auf eben dieselbe Anweisung verweist. Es ist nicht erlaubt, eine vorbereitete Anweisung in solch rekursiver Weise auszuführen"
+ER_SP_CANT_SET_AUTOCOMMIT
+ eng "Not allowed to set autocommit from a stored function or trigger"
+ ger "Es ist nicht erlaubt, innerhalb einer gespeicherten Funktion oder eines Triggers AUTOCOMMIT zu setzen"
+ER_MALFORMED_DEFINER
+ eng "Definer is not fully qualified"
+ ger "Definierer des View ist nicht vollständig spezifiziert"
+ER_VIEW_FRM_NO_USER
+ eng "View '%-.192s'.'%-.192s' has no definer information (old table format). Current user is used as definer. Please recreate the view!"
+ ger "View '%-.192s'.'%-.192s' hat keine Definierer-Information (altes Tabellenformat). Der aktuelle Benutzer wird als Definierer verwendet. Bitte erstellen Sie den View neu"
+ER_VIEW_OTHER_USER
+ eng "You need the SUPER privilege for creation view with '%-.192s'@'%-.192s' definer"
+ ger "Sie brauchen die SUPER-Berechtigung, um einen View mit dem Definierer '%-.192s'@'%-.192s' zu erzeugen"
+ER_NO_SUCH_USER
+ eng "The user specified as a definer ('%-.64s'@'%-.64s') does not exist"
+ ger "Der als Definierer angegebene Benutzer ('%-.64s'@'%-.64s') existiert nicht"
+ER_FORBID_SCHEMA_CHANGE
+ eng "Changing schema from '%-.192s' to '%-.192s' is not allowed."
+ ger "Wechsel des Schemas von '%-.192s' auf '%-.192s' ist nicht erlaubt"
+ER_ROW_IS_REFERENCED_2 23000
+ eng "Cannot delete or update a parent row: a foreign key constraint fails (%.192s)"
+ ger "Kann Eltern-Zeile nicht löschen oder aktualisieren: eine Fremdschlüsselbedingung schlägt fehl (%.192s)"
+ER_NO_REFERENCED_ROW_2 23000
+ eng "Cannot add or update a child row: a foreign key constraint fails (%.192s)"
+ ger "Kann Kind-Zeile nicht hinzufügen oder aktualisieren: eine Fremdschlüsselbedingung schlägt fehl (%.192s)"
+ER_SP_BAD_VAR_SHADOW 42000
+ eng "Variable '%-.64s' must be quoted with `...`, or renamed"
+ ger "Variable '%-.64s' muss mit `...` geschützt oder aber umbenannt werden"
+ER_TRG_NO_DEFINER
+ eng "No definer attribute for trigger '%-.192s'.'%-.192s'. The trigger will be activated under the authorization of the invoker, which may have insufficient privileges. Please recreate the trigger."
+ ger "Kein Definierer-Attribut für Trigger '%-.192s'.'%-.192s'. Der Trigger wird mit der Autorisierung des Aufrufers aktiviert, der möglicherweise keine zureichenden Berechtigungen hat. Bitte legen Sie den Trigger neu an."
+ER_OLD_FILE_FORMAT
+ eng "'%-.192s' has an old format, you should re-create the '%s' object(s)"
+ ger "'%-.192s' hat altes Format, Sie sollten die '%s'-Objekt(e) neu erzeugen"
+ER_SP_RECURSION_LIMIT
+ eng "Recursive limit %d (as set by the max_sp_recursion_depth variable) was exceeded for routine %.192s"
+ ger "Rekursionsgrenze %d (durch Variable max_sp_recursion_depth gegeben) wurde für Routine %.192s überschritten"
+ER_SP_PROC_TABLE_CORRUPT
+ eng "Failed to load routine %-.192s. The table mysql.proc is missing, corrupt, or contains bad data (internal code %d)"
+ ger "Routine %-.192s konnte nicht geladen werden. Die Tabelle mysql.proc fehlt, ist beschädigt, oder enthält fehlerhaften Daten (interner Code: %d)"
+ER_SP_WRONG_NAME 42000
+ eng "Incorrect routine name '%-.192s'"
+ ger "Ungültiger Routinenname '%-.192s'"
+ER_TABLE_NEEDS_UPGRADE
+ eng "Table upgrade required. Please do \"REPAIR TABLE `%-.32s`\" or dump/reload to fix it!"
+ ger "Tabellenaktualisierung erforderlich. Bitte zum Reparieren \"REPAIR TABLE `%-.32s`\" eingeben!"
+ER_SP_NO_AGGREGATE 42000
+ eng "AGGREGATE is not supported for stored functions"
+ ger "AGGREGATE wird bei gespeicherten Funktionen nicht unterstützt"
+ER_MAX_PREPARED_STMT_COUNT_REACHED 42000
+ eng "Can't create more than max_prepared_stmt_count statements (current value: %lu)"
+ ger "Kann nicht mehr Anweisungen als max_prepared_stmt_count erzeugen (aktueller Wert: %lu)"
+ER_VIEW_RECURSIVE
+ eng "`%-.192s`.`%-.192s` contains view recursion"
+ ger "`%-.192s`.`%-.192s` 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"
+ER_TABLE_CANT_HANDLE_SPKEYS
+ eng "The used table type doesn't support SPATIAL indexes"
+ ger "Der verwendete Tabellentyp unterstützt keine SPATIAL-Indizes"
+ER_NO_TRIGGERS_ON_SYSTEM_SCHEMA
+ eng "Triggers can not be created on system tables"
+ ger "Trigger können nicht auf Systemtabellen erzeugt werden"
+ER_REMOVED_SPACES
+ eng "Leading spaces are removed from name '%s'"
+ ger "Führende Leerzeichen werden aus dem Namen '%s' entfernt"
+ER_AUTOINC_READ_FAILED
+ eng "Failed to read auto-increment value from storage engine"
+ ger "Lesen des Autoincrement-Werts von der Speicher-Engine fehlgeschlagen"
+ER_USERNAME
+ eng "user name"
+ ger "Benutzername"
+ER_HOSTNAME
+ eng "host name"
+ ger "Hostname"
+ER_WRONG_STRING_LENGTH
+ eng "String '%-.70s' is too long for %s (should be no longer than %d)"
+ ger "String '%-.70s' ist zu lang für %s (sollte nicht länger sein als %d)"
+ER_NON_INSERTABLE_TABLE
+ eng "The target table %-.100s of the %s is not insertable-into"
+ ger "Die Zieltabelle %-.100s von %s ist nicht einfügbar"
+ER_ADMIN_WRONG_MRG_TABLE
+ eng "Table '%-.64s' is differently defined or of non-MyISAM type or doesn't exist"
+ ger "Tabelle '%-.64s' ist unterschiedlich definiert, nicht vom Typ MyISAM oder existiert nicht"
+ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT
+ eng "Too high level of nesting for select"
+ ger "Zu tief verschachtelte SELECT-Anweisungen"
+ER_NAME_BECOMES_EMPTY
+ eng "Name '%-.64s' has become ''"
+ ger "Name '%-.64s' wurde zu ''"
+ER_AMBIGUOUS_FIELD_TERM
+ eng "First character of the FIELDS TERMINATED string is ambiguous; please use non-optional and non-empty FIELDS ENCLOSED BY"
+ ger "Das erste Zeichen der Zeichenkette FIELDS TERMINATED ist mehrdeutig; bitte benutzen Sie nicht optionale und nicht leere FIELDS ENCLOSED BY"
+ER_FOREIGN_SERVER_EXISTS
+ eng "The foreign server, %s, you are trying to create already exists."
+ ger "Der entfernte Server %s, den Sie versuchen zu erzeugen, existiert schon."
+ER_FOREIGN_SERVER_DOESNT_EXIST
+ eng "The foreign server name you are trying to reference does not exist. Data source error: %-.64s"
+ ger "Die externe Verbindung, auf die Sie zugreifen wollen, existiert nicht. Datenquellenfehlermeldung: %-.64s"
+ER_ILLEGAL_HA_CREATE_OPTION
+ eng "Table storage engine '%-.64s' does not support the create option '%.64s'"
+ ger "Speicher-Engine '%-.64s' der Tabelle unterstützt die Option '%.64s' nicht"
+ER_PARTITION_REQUIRES_VALUES_ERROR
+ eng "Syntax error: %-.64s PARTITIONING requires definition of VALUES %-.64s for each partition"
+ ger "Fehler in der SQL-Syntax: %-.64s-PARTITIONierung erfordert Definition von VALUES %-.64s für jede Partition"
+ swe "Syntaxfel: %-.64s PARTITIONering kräver definition av VALUES %-.64s för varje partition"
+ER_PARTITION_WRONG_VALUES_ERROR
+ eng "Only %-.64s PARTITIONING can use VALUES %-.64s in partition definition"
+ ger "Nur %-.64s-PARTITIONierung kann VALUES %-.64s in der Partitionsdefinition verwenden"
+ swe "Endast %-.64s partitionering kan använda VALUES %-.64s i definition av partitionen"
+ER_PARTITION_MAXVALUE_ERROR
+ eng "MAXVALUE can only be used in last partition definition"
+ ger "MAXVALUE kann nur für die Definition der letzten Partition verwendet werden"
+ swe "MAXVALUE kan bara användas i definitionen av den sista partitionen"
+ER_PARTITION_SUBPARTITION_ERROR
+ eng "Subpartitions can only be hash partitions and by key"
+ ger "Unterpartitionen dürfen nur HASH- oder KEY-Partitionen sein"
+ swe "Subpartitioner kan bara vara hash och key partitioner"
+ER_PARTITION_SUBPART_MIX_ERROR
+ eng "Must define subpartitions on all partitions if on one partition"
+ ger "Wenn Sie Unterpartitionen auf einer Partition definieren, müssen Sie das für alle Partitionen tun"
+ swe "Subpartitioner måste definieras på alla partitioner om på en"
+ER_PARTITION_WRONG_NO_PART_ERROR
+ eng "Wrong number of partitions defined, mismatch with previous setting"
+ ger "Falsche Anzahl von Partitionen definiert, stimmt nicht mit vorherigen Einstellungen überein"
+ swe "Antal partitioner definierade och antal partitioner är inte lika"
+ER_PARTITION_WRONG_NO_SUBPART_ERROR
+ eng "Wrong number of subpartitions defined, mismatch with previous setting"
+ ger "Falsche Anzahl von Unterpartitionen definiert, stimmt nicht mit vorherigen Einstellungen überein"
+ swe "Antal subpartitioner definierade och antal subpartitioner är inte lika"
+ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR
+ eng "Constant, random or timezone-dependent expressions in (sub)partitioning function are not allowed"
+ ger "Konstante oder Random-Ausdrücke in (Unter-)Partitionsfunktionen sind nicht erlaubt"
+ swe "Konstanta uttryck eller slumpmässiga uttryck är inte tillåtna (sub)partitioneringsfunktioner"
+ER_NO_CONST_EXPR_IN_RANGE_OR_LIST_ERROR
+ eng "Expression in RANGE/LIST VALUES must be constant"
+ ger "Ausdrücke in RANGE/LIST VALUES müssen konstant sein"
+ swe "Uttryck i RANGE/LIST VALUES måste vara ett konstant uttryck"
+ER_FIELD_NOT_FOUND_PART_ERROR
+ eng "Field in list of fields for partition function not found in table"
+ ger "Felder in der Feldliste der Partitionierungsfunktion wurden in der Tabelle nicht gefunden"
+ swe "Fält i listan av fält för partitionering med key inte funnen i tabellen"
+ER_LIST_OF_FIELDS_ONLY_IN_HASH_ERROR
+ eng "List of fields is only allowed in KEY partitions"
+ ger "Eine Feldliste ist nur in KEY-Partitionen erlaubt"
+ swe "En lista av fält är endast tillåtet för KEY partitioner"
+ER_INCONSISTENT_PARTITION_INFO_ERROR
+ eng "The partition info in the frm file is not consistent with what can be written into the frm file"
+ ger "Die Partitionierungsinformationen in der frm-Datei stimmen nicht mit dem überein, was in die frm-Datei geschrieben werden kann"
+ swe "Partitioneringsinformationen i frm-filen är inte konsistent med vad som kan skrivas i frm-filen"
+ER_PARTITION_FUNC_NOT_ALLOWED_ERROR
+ eng "The %-.192s function returns the wrong type"
+ ger "Die %-.192s-Funktion gibt einen falschen Typ zurück"
+ swe "%-.192s-funktionen returnerar felaktig typ"
+ER_PARTITIONS_MUST_BE_DEFINED_ERROR
+ eng "For %-.64s partitions each partition must be defined"
+ ger "Für %-.64s-Partitionen muss jede Partition definiert sein"
+ swe "För %-.64s partitionering så måste varje partition definieras"
+ER_RANGE_NOT_INCREASING_ERROR
+ eng "VALUES LESS THAN value must be strictly increasing for each partition"
+ ger "Werte in VALUES LESS THAN müssen für jede Partition strikt aufsteigend sein"
+ swe "Värden i VALUES LESS THAN måste vara strikt växande för varje partition"
+ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR
+ eng "VALUES value must be of same type as partition function"
+ ger "VALUES-Werte müssen vom selben Typ wie die Partitionierungsfunktion sein"
+ swe "Värden i VALUES måste vara av samma typ som partitioneringsfunktionen"
+ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR
+ eng "Multiple definition of same constant in list partitioning"
+ ger "Mehrfachdefinition derselben Konstante bei Listen-Partitionierung"
+ swe "Multipel definition av samma konstant i list partitionering"
+ER_PARTITION_ENTRY_ERROR
+ eng "Partitioning can not be used stand-alone in query"
+ ger "Partitionierung kann in einer Abfrage nicht alleinstehend benutzt werden"
+ swe "Partitioneringssyntax kan inte användas på egen hand i en SQL-fråga"
+ER_MIX_HANDLER_ERROR
+ eng "The mix of handlers in the partitions is not allowed in this version of MariaDB"
+ ger "Das Vermischen von Handlern in Partitionen ist in dieser Version von MariaDB nicht erlaubt"
+ swe "Denna mix av lagringsmotorer är inte tillåten i denna version av MariaDB"
+ER_PARTITION_NOT_DEFINED_ERROR
+ eng "For the partitioned engine it is necessary to define all %-.64s"
+ ger "Für die partitionierte Engine müssen alle %-.64s definiert sein"
+ swe "För partitioneringsmotorn så är det nödvändigt att definiera alla %-.64s"
+ER_TOO_MANY_PARTITIONS_ERROR
+ eng "Too many partitions (including subpartitions) were defined"
+ ger "Es wurden zu vielen Partitionen (einschließlich Unterpartitionen) definiert"
+ swe "För många partitioner (inkluderande subpartitioner) definierades"
+ER_SUBPARTITION_ERROR
+ eng "It is only possible to mix RANGE/LIST partitioning with HASH/KEY partitioning for subpartitioning"
+ ger "RANGE/LIST-Partitionierung kann bei Unterpartitionen nur zusammen mit HASH/KEY-Partitionierung verwendet werden"
+ swe "Det är endast möjligt att blanda RANGE/LIST partitionering med HASH/KEY partitionering för subpartitionering"
+ER_CANT_CREATE_HANDLER_FILE
+ eng "Failed to create specific handler file"
+ ger "Erzeugen einer spezifischen Handler-Datei fehlgeschlagen"
+ swe "Misslyckades med att skapa specifik fil i lagringsmotor"
+ER_BLOB_FIELD_IN_PART_FUNC_ERROR
+ eng "A BLOB field is not allowed in partition function"
+ ger "In der Partitionierungsfunktion sind BLOB-Spalten nicht erlaubt"
+ swe "Ett BLOB-fält är inte tillåtet i partitioneringsfunktioner"
+ER_UNIQUE_KEY_NEED_ALL_FIELDS_IN_PF
+ eng "A %-.192s must include all columns in the table's partitioning function"
+ER_NO_PARTS_ERROR
+ eng "Number of %-.64s = 0 is not an allowed value"
+ ger "Eine Anzahl von %-.64s = 0 ist kein erlaubter Wert"
+ swe "Antal %-.64s = 0 är inte ett tillåten värde"
+ER_PARTITION_MGMT_ON_NONPARTITIONED
+ eng "Partition management on a not partitioned table is not possible"
+ ger "Partitionsverwaltung einer nicht partitionierten Tabelle ist nicht möglich"
+ swe "Partitioneringskommando på en opartitionerad tabell är inte möjligt"
+ER_FOREIGN_KEY_ON_PARTITIONED
+ eng "Foreign key clause is not yet supported in conjunction with partitioning"
+ ger "Fremdschlüssel-Beschränkungen sind im Zusammenhang mit Partitionierung nicht zulässig"
+ swe "Foreign key klausul är inte ännu implementerad i kombination med partitionering"
+ER_DROP_PARTITION_NON_EXISTENT
+ eng "Error in list of partitions to %-.64s"
+ ger "Fehler in der Partitionsliste bei %-.64s"
+ swe "Fel i listan av partitioner att %-.64s"
+ER_DROP_LAST_PARTITION
+ eng "Cannot remove all partitions, use DROP TABLE instead"
+ ger "Es lassen sich nicht sämtliche Partitionen löschen, benutzen Sie statt dessen DROP TABLE"
+ swe "Det är inte tillåtet att ta bort alla partitioner, använd DROP TABLE istället"
+ER_COALESCE_ONLY_ON_HASH_PARTITION
+ eng "COALESCE PARTITION can only be used on HASH/KEY partitions"
+ ger "COALESCE PARTITION kann nur auf HASH- oder KEY-Partitionen benutzt werden"
+ swe "COALESCE PARTITION kan bara användas på HASH/KEY partitioner"
+ER_REORG_HASH_ONLY_ON_SAME_NO
+ eng "REORGANIZE PARTITION can only be used to reorganize partitions not to change their numbers"
+ ger "REORGANIZE PARTITION kann nur zur Reorganisation von Partitionen verwendet werden, nicht, um ihre Nummern zu ändern"
+ swe "REORGANIZE PARTITION kan bara användas för att omorganisera partitioner, inte för att ändra deras antal"
+ER_REORG_NO_PARAM_ERROR
+ eng "REORGANIZE PARTITION without parameters can only be used on auto-partitioned tables using HASH PARTITIONs"
+ ger "REORGANIZE PARTITION ohne Parameter kann nur für auto-partitionierte Tabellen verwendet werden, die HASH-Partitionierung benutzen"
+ swe "REORGANIZE PARTITION utan parametrar kan bara användas på auto-partitionerade tabeller som använder HASH partitionering"
+ER_ONLY_ON_RANGE_LIST_PARTITION
+ eng "%-.64s PARTITION can only be used on RANGE/LIST partitions"
+ ger "%-.64s PARTITION kann nur für RANGE- oder LIST-Partitionen verwendet werden"
+ swe "%-.64s PARTITION kan bara användas på RANGE/LIST-partitioner"
+ER_ADD_PARTITION_SUBPART_ERROR
+ eng "Trying to Add partition(s) with wrong number of subpartitions"
+ ger "Es wurde versucht, eine oder mehrere Partitionen mit der falschen Anzahl von Unterpartitionen hinzuzufügen"
+ swe "ADD PARTITION med fel antal subpartitioner"
+ER_ADD_PARTITION_NO_NEW_PARTITION
+ eng "At least one partition must be added"
+ ger "Es muss zumindest eine Partition hinzugefügt werden"
+ swe "Åtminstone en partition måste läggas till vid ADD PARTITION"
+ER_COALESCE_PARTITION_NO_PARTITION
+ eng "At least one partition must be coalesced"
+ ger "Zumindest eine Partition muss mit COALESCE PARTITION zusammengefügt werden"
+ swe "Åtminstone en partition måste slås ihop vid COALESCE PARTITION"
+ER_REORG_PARTITION_NOT_EXIST
+ eng "More partitions to reorganize than there are partitions"
+ ger "Es wurde versucht, mehr Partitionen als vorhanden zu reorganisieren"
+ swe "Fler partitioner att reorganisera än det finns partitioner"
+ER_SAME_NAME_PARTITION
+ eng "Duplicate partition name %-.192s"
+ ger "Doppelter Partitionsname: %-.192s"
+ swe "Duplicerat partitionsnamn %-.192s"
+ER_NO_BINLOG_ERROR
+ eng "It is not allowed to shut off binlog on this command"
+ ger "Es es nicht erlaubt, bei diesem Befehl binlog abzuschalten"
+ swe "Det är inte tillåtet att stänga av binlog på detta kommando"
+ER_CONSECUTIVE_REORG_PARTITIONS
+ eng "When reorganizing a set of partitions they must be in consecutive order"
+ ger "Bei der Reorganisation eines Satzes von Partitionen müssen diese in geordneter Reihenfolge vorliegen"
+ swe "När ett antal partitioner omorganiseras måste de vara i konsekutiv ordning"
+ER_REORG_OUTSIDE_RANGE
+ eng "Reorganize of range partitions cannot change total ranges except for last partition where it can extend the range"
+ ger "Die Reorganisation von RANGE-Partitionen kann Gesamtbereiche nicht verändern, mit Ausnahme der letzten Partition, die den Bereich erweitern kann"
+ swe "Reorganisering av rangepartitioner kan inte ändra den totala intervallet utom för den sista partitionen där intervallet kan utökas"
+ER_PARTITION_FUNCTION_FAILURE
+ eng "Partition function not supported in this version for this handler"
+ ger "Partitionsfunktion in dieser Version dieses Handlers nicht unterstützt"
+ER_PART_STATE_ERROR
+ eng "Partition state cannot be defined from CREATE/ALTER TABLE"
+ ger "Partitionszustand kann nicht von CREATE oder ALTER TABLE aus definiert werden"
+ swe "Partition state kan inte definieras från CREATE/ALTER TABLE"
+ER_LIMITED_PART_RANGE
+ eng "The %-.64s handler only supports 32 bit integers in VALUES"
+ ger "Der Handler %-.64s unterstützt in VALUES nur 32-Bit-Integers"
+ swe "%-.64s stödjer endast 32 bitar i integers i VALUES"
+ER_PLUGIN_IS_NOT_LOADED
+ eng "Plugin '%-.192s' is not loaded"
+ ger "Plugin '%-.192s' ist nicht geladen"
+ER_WRONG_VALUE
+ eng "Incorrect %-.32s value: '%-.128s'"
+ ger "Falscher %-.32s-Wert: '%-.128s'"
+ER_NO_PARTITION_FOR_GIVEN_VALUE
+ eng "Table has no partition for value %-.64s"
+ ger "Tabelle hat für den Wert %-.64s keine Partition"
+ER_FILEGROUP_OPTION_ONLY_ONCE
+ eng "It is not allowed to specify %s more than once"
+ ger "%s darf nicht mehr als einmal angegegeben werden"
+ER_CREATE_FILEGROUP_FAILED
+ eng "Failed to create %s"
+ ger "Anlegen von %s fehlgeschlagen"
+ER_DROP_FILEGROUP_FAILED
+ eng "Failed to drop %s"
+ ger "Löschen von %s fehlgeschlagen"
+ER_TABLESPACE_AUTO_EXTEND_ERROR
+ eng "The handler doesn't support autoextend of tablespaces"
+ ger "Der Handler unterstützt keine automatische Erweiterung (Autoextend) von Tablespaces"
+ER_WRONG_SIZE_NUMBER
+ eng "A size parameter was incorrectly specified, either number or on the form 10M"
+ ger "Ein Größen-Parameter wurde unkorrekt angegeben, muss entweder Zahl sein oder im Format 10M"
+ER_SIZE_OVERFLOW_ERROR
+ eng "The size number was correct but we don't allow the digit part to be more than 2 billion"
+ ger "Die Zahl für die Größe war korrekt, aber der Zahlanteil darf nicht größer als 2 Milliarden sein"
+ER_ALTER_FILEGROUP_FAILED
+ eng "Failed to alter: %s"
+ ger "Änderung von %s fehlgeschlagen"
+ER_BINLOG_ROW_LOGGING_FAILED
+ eng "Writing one row to the row-based binary log failed"
+ ger "Schreiben einer Zeilen ins zeilenbasierte Binärlog fehlgeschlagen"
+ER_BINLOG_ROW_WRONG_TABLE_DEF
+ eng "Table definition on master and slave does not match: %s"
+ ger "Tabellendefinition auf Master und Slave stimmt nicht überein: %s"
+ER_BINLOG_ROW_RBR_TO_SBR
+ eng "Slave running with --log-slave-updates must use row-based binary logging to be able to replicate row-based binary log events"
+ ger "Slave, die mit --log-slave-updates laufen, müssen zeilenbasiertes Loggen verwenden, um zeilenbasierte Binärlog-Ereignisse loggen zu können"
+ER_EVENT_ALREADY_EXISTS
+ eng "Event '%-.192s' already exists"
+ ger "Event '%-.192s' existiert bereits"
+ER_EVENT_STORE_FAILED
+ eng "Failed to store event %s. Error code %d from storage engine."
+ ger "Speichern von Event %s fehlgeschlagen. Fehlercode der Speicher-Engine: %d"
+ER_EVENT_DOES_NOT_EXIST
+ eng "Unknown event '%-.192s'"
+ ger "Unbekanntes Event '%-.192s'"
+ER_EVENT_CANT_ALTER
+ eng "Failed to alter event '%-.192s'"
+ ger "Ändern des Events '%-.192s' fehlgeschlagen"
+ER_EVENT_DROP_FAILED
+ eng "Failed to drop %s"
+ ger "Löschen von %s fehlgeschlagen"
+ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG
+ eng "INTERVAL is either not positive or too big"
+ ger "INTERVAL ist entweder nicht positiv oder zu groß"
+ER_EVENT_ENDS_BEFORE_STARTS
+ eng "ENDS is either invalid or before STARTS"
+ ger "ENDS ist entweder ungültig oder liegt vor STARTS"
+ER_EVENT_EXEC_TIME_IN_THE_PAST
+ eng "Event execution time is in the past. Event has been disabled"
+ ger "Ausführungszeit des Events liegt in der Vergangenheit. Event wurde deaktiviert"
+ER_EVENT_OPEN_TABLE_FAILED
+ eng "Failed to open mysql.event"
+ ger "Öffnen von mysql.event fehlgeschlagen"
+ER_EVENT_NEITHER_M_EXPR_NOR_M_AT
+ eng "No datetime expression provided"
+ ger "Kein DATETIME-Ausdruck angegeben"
+ER_COL_COUNT_DOESNT_MATCH_CORRUPTED
+ eng "Column count of mysql.%s is wrong. Expected %d, found %d. The table is probably corrupted"
+ ger "Spaltenanzahl von mysql.%s falsch. %d erwartet, aber %d gefunden. Tabelle ist wahrscheinlich beschädigt"
+ER_CANNOT_LOAD_FROM_TABLE
+ eng "Cannot load from mysql.%s. The table is probably corrupted"
+ ger "Kann mysql.%s nicht einlesen. Tabelle ist wahrscheinlich beschädigt"
+ER_EVENT_CANNOT_DELETE
+ eng "Failed to delete the event from mysql.event"
+ ger "Löschen des Events aus mysql.event fehlgeschlagen"
+ER_EVENT_COMPILE_ERROR
+ eng "Error during compilation of event's body"
+ ger "Fehler beim Kompilieren des Event-Bodys"
+ER_EVENT_SAME_NAME
+ eng "Same old and new event name"
+ ger "Alter und neuer Event-Name sind gleich"
+ER_EVENT_DATA_TOO_LONG
+ eng "Data for column '%s' too long"
+ ger "Daten der Spalte '%s' zu lang"
+ER_DROP_INDEX_FK
+ eng "Cannot drop index '%-.192s': needed in a foreign key constraint"
+ ger "Kann Index '%-.192s' nicht löschen: wird für eine Fremdschlüsselbeschränkung benötigt"
+# When using this error message, use the ER_WARN_DEPRECATED_SYNTAX error
+# code.
+ER_WARN_DEPRECATED_SYNTAX_WITH_VER
+ eng "The syntax '%s' is deprecated and will be removed in MariaDB %s. Please use %s instead"
+ ger "Die Syntax '%s' ist veraltet und wird in MariaDB %s entfernt. Bitte benutzen Sie statt dessen %s"
+ER_CANT_WRITE_LOCK_LOG_TABLE
+ eng "You can't write-lock a log table. Only read access is possible"
+ ger "Eine Log-Tabelle kann nicht schreibgesperrt werden. Es ist ohnehin nur Lesezugriff möglich"
+ER_CANT_LOCK_LOG_TABLE
+ eng "You can't use locks with log tables."
+ ger "Log-Tabellen können nicht gesperrt werden."
+ER_FOREIGN_DUPLICATE_KEY 23000 S1009
+ eng "Upholding foreign key constraints for table '%.192s', entry '%-.192s', key %d would lead to a duplicate entry"
+ ger "Aufrechterhalten der Fremdschlüssel-Beschränkungen für Tabelle '%.192s', Eintrag '%-.192s', Schlüssel %d würde zu einem doppelten Eintrag führen"
+ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE
+ eng "Column count of mysql.%s is wrong. Expected %d, found %d. Created with MariaDB %d, now running %d. Please use mysql_upgrade to fix this error."
+ ger "Spaltenanzahl von mysql.%s falsch. %d erwartet, aber %d erhalten. Erzeugt mit MariaDB %d, jetzt unter %d. Bitte benutzen Sie mysql_upgrade, um den Fehler zu beheben"
+ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR
+ eng "Cannot switch out of the row-based binary log format when the session has open temporary tables"
+ ger "Kann nicht aus dem zeilenbasierten Binärlog-Format herauswechseln, wenn die Sitzung offene temporäre Tabellen hat"
+ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT
+ eng "Cannot change the binary logging format inside a stored function or trigger"
+ ger "Das Binärlog-Format kann innerhalb einer gespeicherten Funktion oder eines Triggers nicht geändert werden"
+ER_NDB_CANT_SWITCH_BINLOG_FORMAT
+ eng "The NDB cluster engine does not support changing the binlog format on the fly yet"
+ ger "Die Speicher-Engine NDB Cluster unterstützt das Ändern des Binärlog-Formats zur Laufzeit noch nicht"
+ER_PARTITION_NO_TEMPORARY
+ eng "Cannot create temporary table with partitions"
+ ger "Anlegen temporärer Tabellen mit Partitionen nicht möglich"
+ER_PARTITION_CONST_DOMAIN_ERROR
+ eng "Partition constant is out of partition function domain"
+ ger "Partitionskonstante liegt außerhalb der Partitionsfunktionsdomäne"
+ swe "Partitionskonstanten är utanför partitioneringsfunktionens domän"
+ER_PARTITION_FUNCTION_IS_NOT_ALLOWED
+ eng "This partition function is not allowed"
+ ger "Diese Partitionierungsfunktion ist nicht erlaubt"
+ swe "Denna partitioneringsfunktion är inte tillåten"
+ER_DDL_LOG_ERROR
+ eng "Error in DDL log"
+ ger "Fehler im DDL-Log"
+ER_NULL_IN_VALUES_LESS_THAN
+ eng "Not allowed to use NULL value in VALUES LESS THAN"
+ ger "In VALUES LESS THAN dürfen keine NULL-Werte verwendet werden"
+ swe "Det är inte tillåtet att använda NULL-värden i VALUES LESS THAN"
+ER_WRONG_PARTITION_NAME
+ eng "Incorrect partition name"
+ ger "Falscher Partitionsname"
+ swe "Felaktigt partitionsnamn"
+ER_CANT_CHANGE_TX_ISOLATION 25001
+ eng "Transaction isolation level can't be changed while a transaction is in progress"
+ ger "Transaktionsisolationsebene kann während einer laufenden Transaktion nicht geändert werden"
+ER_DUP_ENTRY_AUTOINCREMENT_CASE
+ eng "ALTER TABLE causes auto_increment resequencing, resulting in duplicate entry '%-.192s' for key '%-.192s'"
+ ger "ALTER TABLE führt zur Neusequenzierung von auto_increment, wodurch der doppelte Eintrag '%-.192s' für Schlüssel '%-.192s' auftritt"
+ER_EVENT_MODIFY_QUEUE_ERROR
+ eng "Internal scheduler error %d"
+ ger "Interner Scheduler-Fehler %d"
+ER_EVENT_SET_VAR_ERROR
+ eng "Error during starting/stopping of the scheduler. Error code %u"
+ ger "Fehler während des Startens oder Anhalten des Schedulers. Fehlercode %u"
+ER_PARTITION_MERGE_ERROR
+ eng "Engine cannot be used in partitioned tables"
+ ger "Engine kann in partitionierten Tabellen nicht verwendet werden"
+ swe "Engine inte användas i en partitionerad tabell"
+ER_CANT_ACTIVATE_LOG
+ eng "Cannot activate '%-.64s' log"
+ ger "Kann Logdatei '%-.64s' nicht aktivieren"
+ER_RBR_NOT_AVAILABLE
+ eng "The server was not built with row-based replication"
+ ger "Der Server wurde nicht mit zeilenbasierter Replikation gebaut"
+ER_BASE64_DECODE_ERROR
+ eng "Decoding of base64 string failed"
+ swe "Avkodning av base64 sträng misslyckades"
+ ger "Der Server hat keine zeilenbasierte Replikation"
+ER_EVENT_RECURSION_FORBIDDEN
+ eng "Recursion of EVENT DDL statements is forbidden when body is present"
+ ger "Rekursivität von EVENT-DDL-Anweisungen ist unzulässig wenn ein Hauptteil (Body) existiert"
+ER_EVENTS_DB_ERROR
+ eng "Cannot proceed because system tables used by Event Scheduler were found damaged at server start"
+ ger "Kann nicht weitermachen, weil die Tabellen, die von Events verwendet werden, beim Serverstart als beschädigt markiert wurden"
+ER_ONLY_INTEGERS_ALLOWED
+ eng "Only integers allowed as number here"
+ ger "An dieser Stelle sind nur Ganzzahlen zulässig"
+ER_UNSUPORTED_LOG_ENGINE
+ eng "This storage engine cannot be used for log tables"
+ ger "Diese Speicher-Engine kann für Logtabellen nicht verwendet werden"
+ER_BAD_LOG_STATEMENT
+ eng "You cannot '%s' a log table if logging is enabled"
+ ger "Sie können eine Logtabelle nicht '%s', wenn Loggen angeschaltet ist"
+ER_CANT_RENAME_LOG_TABLE
+ eng "Cannot rename '%s'. When logging enabled, rename to/from log table must rename two tables: the log table to an archive table and another table back to '%s'"
+ ger "Kann '%s' nicht umbenennen. Wenn Loggen angeschaltet ist, müssen zwei Tabellen umbenannt werden: die Logtabelle zu einer Archivtabelle, und eine weitere Tabelle zu '%s'"
+ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT 42000
+ eng "Incorrect parameter count in the call to native function '%-.192s'"
+ ger "Falsche Anzahl von Parametern beim Aufruf der nativen Funktion '%-.192s'"
+ER_WRONG_PARAMETERS_TO_NATIVE_FCT 42000
+ eng "Incorrect parameters in the call to native function '%-.192s'"
+ ger "Falscher Parameter beim Aufruf der nativen Funktion '%-.192s'"
+ER_WRONG_PARAMETERS_TO_STORED_FCT 42000
+ eng "Incorrect parameters in the call to stored function '%-.192s'"
+ ger "Falsche Parameter beim Aufruf der gespeicherten Funktion '%-.192s'"
+ER_NATIVE_FCT_NAME_COLLISION
+ eng "This function '%-.192s' has the same name as a native function"
+ ger "Die Funktion '%-.192s' hat denselben Namen wie eine native Funktion"
+# When using this error message, use the ER_DUP_ENTRY error code. See, for
+# example, code in handler.cc.
+ER_DUP_ENTRY_WITH_KEY_NAME 23000 S1009
+ cze "Zvojen-Bý klÃ­Ä '%-.64s' (Äíslo klíÄe '%-.192s')"
+ dan "Ens værdier '%-.64s' for indeks '%-.192s'"
+ nla "Dubbele ingang '%-.64s' voor zoeksleutel '%-.192s'"
+ eng "Duplicate entry '%-.64s' for key '%-.192s'"
+ jps "'%-.64s' 㯠key '%-.192s' ã«ãŠã„ã¦é‡è¤‡ã—ã¦ã„ã¾ã™",
+ est "Kattuv väärtus '%-.64s' võtmele '%-.192s'"
+ fre "Duplicata du champ '%-.64s' pour la clef '%-.192s'"
+ ger "Doppelter Eintrag '%-.64s' für Schlüssel '%-.192s'"
+ greek "Διπλή εγγÏαφή '%-.64s' για το κλειδί '%-.192s'"
+ hun "Duplikalt bejegyzes '%-.64s' a '%-.192s' kulcs szerint."
+ ita "Valore duplicato '%-.64s' per la chiave '%-.192s'"
+ jpn "'%-.64s' 㯠key '%-.192s' ã«ãŠã„ã¦é‡è¤‡ã—ã¦ã„ã¾ã™"
+ kor "ì¤‘ë³µëœ ìž…ë ¥ ê°’ '%-.64s': key '%-.192s'"
+ nor "Like verdier '%-.64s' for nøkkel '%-.192s'"
+ norwegian-ny "Like verdiar '%-.64s' for nykkel '%-.192s'"
+ pol "Powtórzone wyst?pienie '%-.64s' dla klucza '%-.192s'"
+ por "Entrada '%-.64s' duplicada para a chave '%-.192s'"
+ rum "Cimpul '%-.64s' e duplicat pentru cheia '%-.192s'"
+ rus "ДублирующаÑÑÑ Ð·Ð°Ð¿Ð¸ÑÑŒ '%-.64s' по ключу '%-.192s'"
+ serbian "Dupliran unos '%-.64s' za kljuÄ '%-.192s'"
+ slo "Opakovaný kÄ¾ÃºÄ '%-.64s' (Äíslo kľúÄa '%-.192s')"
+ spa "Entrada duplicada '%-.64s' para la clave '%-.192s'"
+ swe "Dubbel nyckel '%-.64s' för nyckel '%-.192s'"
+ ukr "Дублюючий Ð·Ð°Ð¿Ð¸Ñ '%-.64s' Ð´Ð»Ñ ÐºÐ»ÑŽÑ‡Ð° '%-.192s'"
+ER_BINLOG_PURGE_EMFILE
+ eng "Too many files opened, please execute the command again"
+ ger "Zu viele offene Dateien, bitte führen Sie den Befehl noch einmal aus"
+ER_EVENT_CANNOT_CREATE_IN_THE_PAST
+ eng "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was dropped immediately after creation."
+ ger "Ausführungszeit des Events liegt in der Vergangenheit, und es wurde ON COMPLETION NOT PRESERVE gesetzt. Das Event wurde unmittelbar nach Erzeugung gelöscht."
+ER_EVENT_CANNOT_ALTER_IN_THE_PAST
+ eng "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was dropped immediately after creation."
+ ger "Ausführungszeit des Events liegt in der Vergangenheit, und es wurde ON COMPLETION NOT PRESERVE gesetzt. Das Event wurde unmittelbar nach Erzeugung gelöscht."
+ER_SLAVE_INCIDENT
+ eng "The incident %s occured on the master. Message: %-.64s"
+ ger "Der Vorfall %s passierte auf dem Master. Meldung: %-.64s"
+ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT
+ eng "Table has no partition for some existing values"
+ ger "Tabelle hat für einige bestehende Werte keine Partition"
+ER_BINLOG_UNSAFE_STATEMENT
+ eng "Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. %s"
+ swe "Detta är inte säkert att logga i statement-format, för BINLOG_FORMAT = STATEMENT. %s"
+ ger "Unsichere Anweisung ins Binärlog geschrieben, weil Anweisungsformat BINLOG_FORMAT = STATEMENT. %s"
+ER_SLAVE_FATAL_ERROR
+ eng "Fatal error: %s"
+ ger "Fataler Fehler: %s"
+ER_SLAVE_RELAY_LOG_READ_FAILURE
+ eng "Relay log read failure: %s"
+ ger "Relaylog-Lesefehler: %s"
+ER_SLAVE_RELAY_LOG_WRITE_FAILURE
+ eng "Relay log write failure: %s"
+ ger "Relaylog-Schreibfehler: %s"
+ER_SLAVE_CREATE_EVENT_FAILURE
+ eng "Failed to create %s"
+ ger "Erzeugen von %s fehlgeschlagen"
+ER_SLAVE_MASTER_COM_FAILURE
+ eng "Master command %s failed: %s"
+ ger "Master-Befehl %s fehlgeschlagen: %s"
+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"
+ER_VIEW_INVALID_CREATION_CTX
+ eng "Creation context of view `%-.64s`.`%-.64s' is invalid"
+ ger "Erzeugungskontext des Views`%-.64s`.`%-.64s' 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"
+ER_TRG_CORRUPTED_FILE
+ eng "Corrupted TRG file for table `%-.64s`.`%-.64s`"
+ ger "Beschädigte TRG-Datei für Tabelle `%-.64s`.`%-.64s`"
+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"
+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"
+ER_EVENT_INVALID_CREATION_CTX
+ eng "Creation context of event `%-.64s`.`%-.64s` is invalid"
+ ger "Erzeugungskontext des Events `%-.64s`.`%-.64s` 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"
+ER_CANT_CREATE_SROUTINE
+ eng "Cannot create stored routine `%-.64s`. Check warnings"
+ ger "Kann gespeicherte Routine `%-.64s` nicht erzeugen. Beachten Sie die Warnungen"
+ER_NEVER_USED
+ eng "Ambiguous slave modes combination. %s"
+ ger "Mehrdeutige Kombination von Slave-Modi. %s"
+ER_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."
+ER_SLAVE_CORRUPT_EVENT
+ eng "Corrupted replication event was detected"
+ ger "Beschädigtes Replikationsereignis entdeckt"
+ER_LOAD_DATA_INVALID_COLUMN
+ eng "Invalid column reference (%-.64s) in LOAD DATA"
+ ger "Ungültige Spaltenreferenz (%-.64s) bei LOAD DATA"
+ER_LOG_PURGE_NO_FILE
+ eng "Being purged log %s was not found"
+ ger "Zu bereinigende Logdatei %s wurde nicht gefunden"
+ER_XA_RBTIMEOUT XA106
+ eng "XA_RBTIMEOUT: Transaction branch was rolled back: took too long"
+ ger "XA_RBTIMEOUT: Transaktionszweig wurde zurückgerollt: Zeitüberschreitung"
+ER_XA_RBDEADLOCK XA102
+ eng "XA_RBDEADLOCK: Transaction branch was rolled back: deadlock was detected"
+ ger "XA_RBDEADLOCK: Transaktionszweig wurde zurückgerollt: Deadlock entdeckt"
+ER_NEED_REPREPARE
+ eng "Prepared statement needs to be re-prepared"
+ ger "Vorbereitete Anweisungen müssen noch einmal vorbereitet werden"
+ER_DELAYED_NOT_SUPPORTED
+ eng "DELAYED option not supported for table '%-.192s'"
+ ger "Die DELAYED-Option wird für Tabelle '%-.192s' nicht unterstützt"
+WARN_NO_MASTER_INFO
+ eng "The master info structure does not exist"
+ ger "Die Master-Info-Struktur existiert nicht"
+WARN_OPTION_IGNORED
+ eng "<%-.64s> option ignored"
+ ger "Option <%-.64s> ignoriert"
+ER_PLUGIN_DELETE_BUILTIN
+ eng "Built-in plugins cannot be deleted"
+ ger "Eingebaute Plugins können nicht gelöscht werden"
+WARN_PLUGIN_BUSY
+ eng "Plugin is busy and will be uninstalled on shutdown"
+ ger "Plugin wird verwendet und wird erst beim Herunterfahren deinstalliert"
+ER_VARIABLE_IS_READONLY
+ eng "%s variable '%s' is read-only. Use SET %s to assign the value"
+ ger "%s Variable '%s' ist nur lesbar. Benutzen Sie SET %s, um einen Wert zuzuweisen"
+ER_WARN_ENGINE_TRANSACTION_ROLLBACK
+ eng "Storage engine %s does not support rollback for this statement. Transaction rolled back and must be restarted"
+ ger "Speicher-Engine %s unterstützt für diese Anweisung kein Rollback. Transaktion wurde zurückgerollt und muss neu gestartet werden"
+ER_SLAVE_HEARTBEAT_FAILURE
+ eng "Unexpected master's heartbeat data: %s"
+ ger "Unerwartete Daten vom Heartbeat des Masters: %s"
+ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE
+ eng "The requested value for the heartbeat period is either negative or exceeds the maximum allowed (%s seconds)."
+ER_NDB_REPLICATION_SCHEMA_ERROR
+ eng "Bad schema for mysql.ndb_replication table. Message: %-.64s"
+ ger "Fehlerhaftes Schema für mysql.ndb_replication table. Meldung: %-.64s"
+ER_CONFLICT_FN_PARSE_ERROR
+ eng "Error in parsing conflict function. Message: %-.64s"
+ ger "Fehler beim Parsen einer Konflikt-Funktion. Meldung: %-.64s"
+ER_EXCEPTIONS_WRITE_ERROR
+ eng "Write to exceptions table failed. Message: %-.128s""
+ ger "Schreiben in Ausnahme-Tabelle fehlgeschlagen. Meldung: %-.128s""
+ER_TOO_LONG_TABLE_COMMENT
+ eng "Comment for table '%-.64s' is too long (max = %lu)"
+ por "Comentário para a tabela '%-.64s' é longo demais (max = %lu)"
+ ger "Kommentar für Tabelle '%-.64s' ist zu lang (max = %lu)"
+ER_TOO_LONG_FIELD_COMMENT
+ eng "Comment for field '%-.64s' is too long (max = %lu)"
+ por "Comentário para o campo '%-.64s' é longo demais (max = %lu)"
+ ger "Kommentar für Feld '%-.64s' ist zu lang (max = %lu)"
+ER_FUNC_INEXISTENT_NAME_COLLISION 42000
+ eng "FUNCTION %s does not exist. Check the 'Function Name Parsing and Resolution' section in the Reference Manual"
+ ger "FUNCTION %s existiert nicht. Erläuterungen im Abschnitt 'Function Name Parsing and Resolution' im Referenzhandbuch"
+# When updating these, please update EXPLAIN_FILENAME_MAX_EXTRA_LENGTH in
+# sql_table.h with the new maximal additional length for explain_filename.
+ER_DATABASE_NAME
+ eng "Database"
+ swe "Databas"
+ ger "Datenbank"
+ER_TABLE_NAME
+ eng "Table"
+ swe "Tabell"
+ ger "Tabelle"
+ER_PARTITION_NAME
+ eng "Partition"
+ swe "Partition"
+ ger "Partition"
+ER_SUBPARTITION_NAME
+ eng "Subpartition"
+ swe "Subpartition"
+ ger "Unterpartition"
+ER_TEMPORARY_NAME
+ eng "Temporary"
+ swe "Temporär"
+ ger "Temporär"
+ER_RENAMED_NAME
+ eng "Renamed"
+ swe "Namnändrad"
+ ger "Umbenannt"
+ER_TOO_MANY_CONCURRENT_TRXS
+ eng "Too many active concurrent transactions"
+ ger "Zu viele aktive simultane Transaktionen"
+WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED
+ eng "Non-ASCII separator arguments are not fully supported"
+ ger "Nicht-ASCII-Trennargumente werden nicht vollständig unterstützt"
+ER_DEBUG_SYNC_TIMEOUT
+ eng "debug sync point wait timed out"
+ ger "Debug Sync Point Wartezeit überschritten"
+ER_DEBUG_SYNC_HIT_LIMIT
+ eng "debug sync point hit limit reached"
+ ger "Debug Sync Point Hit Limit erreicht"
+ER_DUP_SIGNAL_SET 42000
+ eng "Duplicate condition information item '%s'"
+ ger "Informationselement '%s' für Duplikatbedingung"
+# Note that the SQLSTATE is not 01000, it is provided by SIGNAL/RESIGNAL
+ER_SIGNAL_WARN 01000
+ eng "Unhandled user-defined warning condition"
+ ger "Unbehandelte benutzerdefinierte Warnbedingung"
+# Note that the SQLSTATE is not 02000, it is provided by SIGNAL/RESIGNAL
+ER_SIGNAL_NOT_FOUND 02000
+ eng "Unhandled user-defined not found condition"
+ ger "Unbehandelte benutzerdefinierte Nicht-gefunden-Bedingung"
+# Note that the SQLSTATE is not HY000, it is provided by SIGNAL/RESIGNAL
+ER_SIGNAL_EXCEPTION HY000
+ eng "Unhandled user-defined exception condition"
+ ger "Unbehandelte benutzerdefinierte Ausnahmebedingung"
+ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER 0K000
+ eng "RESIGNAL when handler not active"
+ ger "RESIGNAL bei nicht aktivem Handler"
+ER_SIGNAL_BAD_CONDITION_TYPE
+ eng "SIGNAL/RESIGNAL can only use a CONDITION defined with SQLSTATE"
+ ger "SIGNAL/RESIGNAL kann nur mit einer Bedingung (CONDITION) benutzt werden, die bei SQLSTATE definiert wurde"
+WARN_COND_ITEM_TRUNCATED
+ eng "Data truncated for condition item '%s'"
+ ger "Daten gekürzt für Bedingungselement '%s'"
+ER_COND_ITEM_TOO_LONG
+ eng "Data too long for condition item '%s'"
+ ger "Daten zu lang für Bedingungselement '%s'"
+ER_UNKNOWN_LOCALE
+ eng "Unknown locale: '%-.64s'"
+ ger "Unbekannte Locale: '%-.64s'"
+ER_SLAVE_IGNORE_SERVER_IDS
+ eng "The requested server id %d clashes with the slave startup option --replicate-same-server-id"
+ ger "Die angeforderte Server-ID %d steht im Konflikt mit der Startoption --replicate-same-server-id für den Slave"
+ER_QUERY_CACHE_DISABLED
+ eng "Query cache is disabled; set query_cache_type to ON or DEMAND to enable it"
+ER_SAME_NAME_PARTITION_FIELD
+ eng "Duplicate partition field name '%-.192s'"
+ ger "Partitionsfeld '%-.192s' ist ein Duplikat"
+ER_PARTITION_COLUMN_LIST_ERROR
+ eng "Inconsistency in usage of column lists for partitioning"
+ ger "Inkonsistenz bei der Benutzung von Spaltenlisten für Partitionierung"
+ER_WRONG_TYPE_COLUMN_VALUE_ERROR
+ eng "Partition column values of incorrect type"
+ ger "Partitionsspaltenwerte sind vom falschen Typ"
+ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR
+ eng "Too many fields in '%-.192s'"
+ ger "Zu viele Felder in '%-.192s'"
+ER_MAXVALUE_IN_VALUES_IN
+ eng "Cannot use MAXVALUE as value in VALUES IN"
+ ger "MAXVALUE kann nicht als Wert in VALUES IN verwendet werden"
+ER_TOO_MANY_VALUES_ERROR
+ eng "Cannot have more than one value for this type of %-.64s partitioning"
+ ger "Für den Partionierungstyp %-.64s darf es nicht mehr als einen Wert geben"
+ER_ROW_SINGLE_PARTITION_FIELD_ERROR
+ eng "Row expressions in VALUES IN only allowed for multi-field column partitioning"
+ ger "Zeilenausdrücke in VALUES IN sind nur für Mehrfeld-Spaltenpartionierung erlaubt"
+ER_FIELD_TYPE_NOT_ALLOWED_AS_PARTITION_FIELD
+ eng "Field '%-.192s' is of a not allowed type for this type of partitioning"
+ ger "Feld '%-.192s' ist für diese Art von Partitionierung von einem nicht zulässigen Typ"
+ER_PARTITION_FIELDS_TOO_LONG
+ eng "The total length of the partitioning fields is too large"
+ ger "Die Gesamtlänge der Partitionsfelder ist zu groß"
+ER_BINLOG_ROW_ENGINE_AND_STMT_ENGINE
+ eng "Cannot execute statement: impossible to write to binary log since both row-incapable engines and statement-incapable engines are involved."
+ER_BINLOG_ROW_MODE_AND_STMT_ENGINE
+ eng "Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = ROW and at least one table uses a storage engine limited to statement-based logging."
+ER_BINLOG_UNSAFE_AND_STMT_ENGINE
+ eng "Cannot execute statement: impossible to write to binary log since statement is unsafe, storage engine is limited to statement-based logging, and BINLOG_FORMAT = MIXED. %s"
+ER_BINLOG_ROW_INJECTION_AND_STMT_ENGINE
+ eng "Cannot execute statement: impossible to write to binary log since statement is in row format and at least one table uses a storage engine limited to statement-based logging."
+ER_BINLOG_STMT_MODE_AND_ROW_ENGINE
+ eng "Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT and at least one table uses a storage engine limited to row-based logging.%s"
+ER_BINLOG_ROW_INJECTION_AND_STMT_MODE
+ eng "Cannot execute statement: impossible to write to binary log since statement is in row format and BINLOG_FORMAT = STATEMENT."
+ER_BINLOG_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE
+ eng "Cannot execute statement: impossible to write to binary log since more than one engine is involved and at least one engine is self-logging."
+
+ER_BINLOG_UNSAFE_LIMIT
+ eng "The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted."
+ER_BINLOG_UNSAFE_INSERT_DELAYED
+ eng "The statement is unsafe because it uses INSERT DELAYED. This is unsafe because the times when rows are inserted cannot be predicted."
+ER_BINLOG_UNSAFE_SYSTEM_TABLE
+ eng "The statement is unsafe because it uses the general log, slow query log, or performance_schema table(s). This is unsafe because system tables may differ on slaves."
+ER_BINLOG_UNSAFE_AUTOINC_COLUMNS
+ eng "Statement is unsafe because it invokes a trigger or a stored function that inserts into an AUTO_INCREMENT column. Inserted values cannot be logged correctly."
+ER_BINLOG_UNSAFE_UDF
+ eng "Statement is unsafe because it uses a UDF which may not return the same value on the slave."
+ER_BINLOG_UNSAFE_SYSTEM_VARIABLE
+ eng "Statement is unsafe because it uses a system variable that may have a different value on the slave."
+ER_BINLOG_UNSAFE_SYSTEM_FUNCTION
+ eng "Statement is unsafe because it uses a system function that may return a different value on the slave."
+ER_BINLOG_UNSAFE_NONTRANS_AFTER_TRANS
+ eng "Statement is unsafe because it accesses a non-transactional table after accessing a transactional table within the same transaction."
+
+ER_MESSAGE_AND_STATEMENT
+ eng "%s Statement: %s"
+
+ER_SLAVE_CONVERSION_FAILED
+ eng "Column %d of table '%-.192s.%-.192s' cannot be converted from type '%-.32s' to type '%-.32s'"
+ER_SLAVE_CANT_CREATE_CONVERSION
+ eng "Can't create conversion table for table '%-.192s.%-.192s'"
+ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT
+ eng "Cannot modify @@session.binlog_format inside a transaction"
+ER_PATH_LENGTH
+ eng "The path specified for %.64s is too long."
+ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT
+ eng "'%s' is deprecated and will be removed in a future release."
+ ger "'%s' ist veraltet und wird in einer zukünftigen Version entfernt werden."
+
+ER_WRONG_NATIVE_TABLE_STRUCTURE
+ eng "Native table '%-.64s'.'%-.64s' has the wrong structure"
+
+ER_WRONG_PERFSCHEMA_USAGE
+ eng "Invalid performance_schema usage."
+ER_WARN_I_S_SKIPPED_TABLE
+ eng "Table '%s'.'%s' was skipped since its definition is being modified by concurrent DDL statement"
+
+ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_DIRECT
+ eng "Cannot modify @@session.binlog_direct_non_transactional_updates inside a transaction"
+ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_DIRECT
+ eng "Cannot change the binlog direct flag inside a stored function or trigger"
+ER_SPATIAL_MUST_HAVE_GEOM_COL 42000
+ eng "A SPATIAL index may only contain a geometrical type column"
+ ger "Ein raumbezogener Index (SPATIAL) darf nur Spalten geometrischen Typs enthalten"
+ER_TOO_LONG_INDEX_COMMENT
+ eng "Comment for index '%-.64s' is too long (max = %lu)"
+
+ER_LOCK_ABORTED
+ eng "Wait on a lock was aborted due to a pending exclusive lock"
+
+ER_DATA_OUT_OF_RANGE 22003
+ eng "%s value is out of range in '%s'"
+
+ER_WRONG_SPVAR_TYPE_IN_LIMIT
+ eng "A variable of a non-integer based type in LIMIT clause"
+
+ER_BINLOG_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE
+ eng "Mixing self-logging and non-self-logging engines in a statement is unsafe."
+
+ER_BINLOG_UNSAFE_MIXED_STATEMENT
+ eng "Statement accesses nontransactional table as well as transactional or temporary table, and writes to any of them."
+
+ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_SQL_LOG_BIN
+ eng "Cannot modify @@session.sql_log_bin inside a transaction"
+
+ER_STORED_FUNCTION_PREVENTS_SWITCH_SQL_LOG_BIN
+ eng "Cannot change the sql_log_bin inside a stored function or trigger"
+
+ER_FAILED_READ_FROM_PAR_FILE
+ eng "Failed to read from the .par file"
+ swe "Misslyckades läsa från .par filen"
+
+ER_VALUES_IS_NOT_INT_TYPE_ERROR
+ eng "VALUES value for partition '%-.64s' must have type INT"
+ swe "Värden i VALUES för partition '%-.64s' måste ha typen INT"
+
+ER_ACCESS_DENIED_NO_PASSWORD_ERROR 28000
+ cze "P-Břístup pro uživatele '%s'@'%s'"
+ dan "Adgang nægtet bruger: '%s'@'%s'"
+ nla "Toegang geweigerd voor gebruiker: '%s'@'%s'"
+ eng "Access denied for user '%s'@'%s'"
+ est "Ligipääs keelatud kasutajale '%s'@'%s'"
+ fre "Accès refusé pour l'utilisateur: '%s'@'%s'"
+ ger "Benutzer '%s'@'%s' hat keine Zugriffsberechtigung"
+ greek "Δεν επιτέÏεται η Ï€Ïόσβαση στο χÏήστη: '%s'@'%s'"
+ hun "A(z) '%s'@'%s' felhasznalo szamara tiltott eleres."
+ ita "Accesso non consentito per l'utente: '%s'@'%s'"
+ kor "'%s'@'%s' 사용ìžëŠ” ì ‘ê·¼ì´ ê±°ë¶€ ë˜ì—ˆìŠµë‹ˆë‹¤."
+ nor "Tilgang nektet for bruker: '%s'@'%s'"
+ norwegian-ny "Tilgang ikke tillate for brukar: '%s'@'%s'"
+ por "Acesso negado para o usuário '%s'@'%s'"
+ rum "Acces interzis pentru utilizatorul: '%s'@'%s'"
+ rus "ДоÑтуп закрыт Ð´Ð»Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ '%s'@'%s'"
+ serbian "Pristup je zabranjen korisniku '%s'@'%s'"
+ slo "Zakázaný prístup pre užívateľa: '%s'@'%s'"
+ spa "Acceso negado para usuario: '%s'@'%s'"
+ swe "Användare '%s'@'%s' är ej berättigad att logga in"
+ ukr "ДоÑтуп заборонено Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувача: '%s'@'%s'"
+
+ER_SET_PASSWORD_AUTH_PLUGIN
+ eng "SET PASSWORD has no significance for users authenticating via plugins"
+
+ER_GRANT_PLUGIN_USER_EXISTS
+ eng "GRANT with IDENTIFIED WITH is illegal because the user %-.*s already exists"
+
+ER_TRUNCATE_ILLEGAL_FK 42000
+ eng "Cannot truncate a table referenced in a foreign key constraint (%.192s)"
+
+ER_PLUGIN_IS_PERMANENT
+ eng "Plugin '%s' is force_plus_permanent and can not be unloaded"
+
+ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN
+ eng "The requested value for the heartbeat period is less than 1 millisecond. The value is reset to 0, meaning that heartbeating will effectively be disabled."
+
+ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX
+ eng "The requested value for the heartbeat period exceeds the value of `slave_net_timeout' seconds. A sensible value for the period should be less than the timeout."
+
+ER_STMT_CACHE_FULL
+ eng "Multi-row statements required more than 'max_binlog_stmt_cache_size' bytes of storage; increase this mysqld variable and try again"
+
+ER_MULTI_UPDATE_KEY_CONFLICT
+ eng "Primary key/partition key update is not allowed since the table is updated both as '%-.192s' and '%-.192s'."
+
+# When translating this error message make sure to include "ALTER TABLE" in the
+# message as mysqlcheck parses the error message looking for ALTER TABLE.
+ER_TABLE_NEEDS_REBUILD
+ eng "Table rebuild required. Please do \"ALTER TABLE `%-.32s` 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'"
+
+ER_INDEX_COLUMN_TOO_LONG
+ eng "Index column size too large. The maximum column size is %lu bytes."
+
+ER_ERROR_IN_TRIGGER_BODY
+ eng "Trigger '%-.64s' has an error in its body: '%-.256s'"
+
+ER_ERROR_IN_UNKNOWN_TRIGGER_BODY
+ eng "Unknown trigger has an error in its body: '%-.256s'"
+
+ER_INDEX_CORRUPT
+ eng "Index %s is corrupted"
+
+ER_UNDO_RECORD_TOO_BIG
+ eng "Undo log record is too big."
+
+ER_BINLOG_UNSAFE_INSERT_IGNORE_SELECT
+ eng "INSERT IGNORE... SELECT is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are ignored. This order cannot be predicted and may differ on master and the slave."
+
+ER_BINLOG_UNSAFE_INSERT_SELECT_UPDATE
+ eng "INSERT... SELECT... ON DUPLICATE KEY UPDATE is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are updated. This order cannot be predicted and may differ on master and the slave."
+
+ER_BINLOG_UNSAFE_REPLACE_SELECT
+ eng "REPLACE... SELECT is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are replaced. This order cannot be predicted and may differ on master and the slave."
+
+ER_BINLOG_UNSAFE_CREATE_IGNORE_SELECT
+ eng "CREATE... IGNORE SELECT is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are ignored. This order cannot be predicted and may differ on master and the slave."
+
+ER_BINLOG_UNSAFE_CREATE_REPLACE_SELECT
+ eng "CREATE... REPLACE SELECT is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are replaced. This order cannot be predicted and may differ on master and the slave."
+
+ER_BINLOG_UNSAFE_UPDATE_IGNORE
+ eng "UPDATE IGNORE is unsafe because the order in which rows are updated determines which (if any) rows are ignored. This order cannot be predicted and may differ on master and the slave."
+
+ER_PLUGIN_NO_UNINSTALL
+ eng "Plugin '%s' is marked as not dynamically uninstallable. You have to stop the server to uninstall it."
+
+ER_PLUGIN_NO_INSTALL
+ eng "Plugin '%s' is marked as not dynamically installable. You have to stop the server to install it."
+
+ER_BINLOG_UNSAFE_WRITE_AUTOINC_SELECT
+ eng "Statements writing to a table with an auto-increment column after selecting from another table are unsafe because the order in which rows are retrieved determines what (if any) rows will be written. This order cannot be predicted and may differ on master and the slave."
+
+ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC
+ eng "CREATE TABLE... SELECT... on a table with an auto-increment column is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are inserted. This order cannot be predicted and may differ on master and the slave."
+
+ER_BINLOG_UNSAFE_INSERT_TWO_KEYS
+ eng "INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe"
+
+ER_TABLE_IN_FK_CHECK
+ eng "Table is being used in foreign key check."
+
+ER_UNUSED_1
+ eng "You should never see it"
+
+ER_BINLOG_UNSAFE_AUTOINC_NOT_FIRST
+ eng "INSERT into autoincrement field which is not the first part in the composed primary key is unsafe."
+
+#
+# End of 5.5 error messages.
+#
+
+#
+# MariaDB error messages section starts here
+#
+
+# The following is here to allow us to detect if there was missing
+# error messages in the errmsg.sys file
+
+ER_LAST_MYSQL_ERROR_MESSAGE
+ eng ""
+
+# MariaDB error numbers starts from 1900
+start-error-number 1900
+
+ER_VCOL_BASED_ON_VCOL
+ eng "A computed column cannot be based on a computed column"
+ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED
+ eng "Function or expression is not allowed for column '%s'"
+ER_DATA_CONVERSION_ERROR_FOR_VIRTUAL_COLUMN
+ eng "Generated value for computed column '%s' cannot be converted to type '%s'"
+ER_PRIMARY_KEY_BASED_ON_VIRTUAL_COLUMN
+ eng "Primary key cannot be defined upon a computed column"
+ER_KEY_BASED_ON_GENERATED_VIRTUAL_COLUMN
+ eng "Key/Index cannot be defined on a non-stored computed column"
+ER_WRONG_FK_OPTION_FOR_VIRTUAL_COLUMN
+ eng "Cannot define foreign key with %s clause on a computed column"
+ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN
+ eng "The value specified for computed column '%s' in table '%s' ignored"
+ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN
+ eng "This is not yet supported for computed columns"
+ER_CONST_EXPR_IN_VCOL
+ eng "Constant expression in computed column function is not allowed"
+ER_ROW_EXPR_FOR_VCOL
+ eng "Expression for computed column cannot return a row"
+ER_UNSUPPORTED_ENGINE_FOR_VIRTUAL_COLUMNS
+ eng "%s storage engine does not support computed columns"
+ER_UNKNOWN_OPTION
+ eng "Unknown option '%-.64s'"
+ER_BAD_OPTION_VALUE
+ eng "Incorrect value '%-.64s' for option '%-.64s'"
+ER_NETWORK_READ_EVENT_CHECKSUM_FAILURE
+ eng "Replication event checksum verification failed while reading from network."
+ER_BINLOG_READ_EVENT_CHECKSUM_FAILURE
+ eng "Replication event checksum verification failed while reading from a log file."
+ER_CANT_DO_ONLINE
+ eng "Can't execute the given '%s' command as online"
+ER_DATA_OVERFLOW 22003
+ eng "Got overflow when converting '%-.128s' to %-.32s. Value truncated."
+ER_DATA_TRUNCATED 22003
+ eng "Truncated value '%-.128s' when converting to %-.32s"
+ER_BAD_DATA 22007
+ eng "Encountered illegal value '%-.128s' when converting to %-.32s"
+ER_DYN_COL_WRONG_FORMAT
+ eng "Encountered illegal format of dynamic column string"
+ER_DYN_COL_IMPLEMENTATION_LIMIT
+ eng "Dynamic column implementation limit reached"
+ER_DYN_COL_DATA 22007
+ eng "Illegal value used as argument of dynamic column function"
+ER_DYN_COL_WRONG_CHARSET
+ eng "Dynamic column contains unknown character set"
+ER_ILLEGAL_SUBQUERY_OPTIMIZER_SWITCHES
+ eng "At least one of the 'in_to_exists' or 'materialization' optimizer_switch flags must be 'on'."
+ER_QUERY_CACHE_IS_DISABLED
+ eng "Query cache is disabled (resize or similar command in progress); repeat this command later"
+ER_QUERY_CACHE_IS_GLOBALY_DISABLED
+ eng "Query cache is globally disabled and you can't enable it only for this session"
+ER_VIEW_ORDERBY_IGNORED
+ eng "View '%-.192s'.'%-.192s' ORDER BY clause ignored because there is other ORDER BY clause already."
+ER_CONNECTION_KILLED 70100
+ eng "Connection was killed"
+ER_INTERNAL_ERROR
+ eng "Internal error: '%-.192s'"
+ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_SKIP_REPLICATION
+ eng "Cannot modify @@session.skip_replication inside a transaction"
+ER_STORED_FUNCTION_PREVENTS_SWITCH_SKIP_REPLICATION
+ eng "Cannot modify @@session.skip_replication inside a stored function or trigger"
+ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT
+ eng "Query execution was interrupted. The query examined at least %llu rows, which exceeds LIMIT ROWS EXAMINED (%llu). The query result may be incomplete."
+ER_NO_SUCH_TABLE_IN_ENGINE 42S02
+ eng "Table '%-.192s.%-.192s' doesn't exist in engine"
+ swe "Det finns ingen tabell som heter '%-.192s.%-.192s' i handlern"
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
deleted file mode 100644
index e8d9f8b8e01..00000000000
--- a/sql/share/errmsg.txt
+++ /dev/null
@@ -1,6299 +0,0 @@
-languages czech=cze latin2, danish=dan latin1, dutch=nla latin1, english=eng latin1, estonian=est latin7, french=fre latin1, german=ger latin1, greek=greek greek, hungarian=hun latin2, italian=ita latin1, japanese=jpn ujis, japanese-sjis=jps sjis, korean=kor euckr, norwegian-ny=norwegian-ny latin1, norwegian=nor latin1, polish=pol latin2, portuguese=por latin1, romanian=rum latin2, russian=rus koi8r, serbian=serbian cp1250, slovak=slo latin2, spanish=spa latin1, swedish=swe latin1, ukrainian=ukr koi8u;
-
-default-language eng
-
-start-error-number 1000
-
-ER_HASHCHK
- eng "hashchk"
-ER_NISAMCHK
- eng "isamchk"
-ER_NO
- cze "NE"
- dan "NEJ"
- nla "NEE"
- eng "NO"
- est "EI"
- fre "NON"
- ger "Nein"
- greek "Ï×É"
- hun "NEM"
- kor "¾Æ´Ï¿À"
- nor "NEI"
- norwegian-ny "NEI"
- pol "NIE"
- por "NÃO"
- rum "NU"
- rus "îåô"
- serbian "NE"
- slo "NIE"
- ukr "î¶"
-ER_YES
- cze "ANO"
- dan "JA"
- nla "JA"
- eng "YES"
- est "JAH"
- fre "OUI"
- ger "Ja"
- greek "ÍÁÉ"
- hun "IGEN"
- ita "SI"
- kor "¿¹"
- nor "JA"
- norwegian-ny "JA"
- pol "TAK"
- por "SIM"
- rum "DA"
- rus "äá"
- serbian "DA"
- slo "Áno"
- spa "SI"
- ukr "ôáë"
-ER_CANT_CREATE_FILE
- cze "Nemohu vytvo-Bøit soubor '%-.200s' (chybový kód: %d)"
- dan "Kan ikke oprette filen '%-.200s' (Fejlkode: %d)"
- nla "Kan file '%-.200s' niet aanmaken (Errcode: %d)"
- eng "Can't create file '%-.200s' (errno: %d)"
- est "Ei suuda luua faili '%-.200s' (veakood: %d)"
- fre "Ne peut créer le fichier '%-.200s' (Errcode: %d)"
- ger "Kann Datei '%-.200s' nicht erzeugen (Fehler: %d)"
- greek "Áäýíáôç ç äçìéïõñãßá ôïõ áñ÷åßïõ '%-.200s' (êùäéêüò ëÜèïõò: %d)"
- hun "A '%-.200s' file nem hozhato letre (hibakod: %d)"
- ita "Impossibile creare il file '%-.200s' (errno: %d)"
- jpn "'%-.200s' ¥Õ¥¡¥¤¥ë¤¬ºî¤ì¤Þ¤»¤ó (errno: %d)"
- kor "È­ÀÏ '%-.200s'¸¦ ¸¸µéÁö ¸øÇß½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)"
- nor "Kan ikke opprette fila '%-.200s' (Feilkode: %d)"
- norwegian-ny "Kan ikkje opprette fila '%-.200s' (Feilkode: %d)"
- pol "Nie mo¿na stworzyæ pliku '%-.200s' (Kod b³êdu: %d)"
- por "Não pode criar o arquivo '%-.200s' (erro no. %d)"
- rum "Nu pot sa creez fisierul '%-.200s' (Eroare: %d)"
- rus "îÅ×ÏÚÍÏÖÎÏ ÓÏÚÄÁÔØ ÆÁÊÌ '%-.200s' (ÏÛÉÂËÁ: %d)"
- serbian "Ne mogu da kreiram file '%-.200s' (errno: %d)"
- slo "Nemô¾em vytvori» súbor '%-.200s' (chybový kód: %d)"
- spa "No puedo crear archivo '%-.200s' (Error: %d)"
- swe "Kan inte skapa filen '%-.200s' (Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ÓÔ×ÏÒÉÔÉ ÆÁÊÌ '%-.200s' (ÐÏÍÉÌËÁ: %d)"
-ER_CANT_CREATE_TABLE
- cze "Nemohu vytvo-Bøit tabulku '%-.200s' (chybový kód: %d)"
- dan "Kan ikke oprette tabellen '%-.200s' (Fejlkode: %d)"
- nla "Kan tabel '%-.200s' niet aanmaken (Errcode: %d)"
- eng "Can't create table '%-.200s' (errno: %d)"
- jps "'%-.200s' ƒe[ƒuƒ‹‚ªì‚ê‚Ü‚¹‚ñ.(errno: %d)",
- est "Ei suuda luua tabelit '%-.200s' (veakood: %d)"
- fre "Ne peut créer la table '%-.200s' (Errcode: %d)"
- ger "Kann Tabelle '%-.200s' nicht erzeugen (Fehler: %d)"
- greek "Áäýíáôç ç äçìéïõñãßá ôïõ ðßíáêá '%-.200s' (êùäéêüò ëÜèïõò: %d)"
- hun "A '%-.200s' tabla nem hozhato letre (hibakod: %d)"
- ita "Impossibile creare la tabella '%-.200s' (errno: %d)"
- jpn "'%-.200s' ¥Æ¡¼¥Ö¥ë¤¬ºî¤ì¤Þ¤»¤ó.(errno: %d)"
- kor "Å×À̺í '%-.200s'¸¦ ¸¸µéÁö ¸øÇß½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)"
- nor "Kan ikke opprette tabellen '%-.200s' (Feilkode: %d)"
- norwegian-ny "Kan ikkje opprette tabellen '%-.200s' (Feilkode: %d)"
- pol "Nie mo¿na stworzyæ tabeli '%-.200s' (Kod b³êdu: %d)"
- por "Não pode criar a tabela '%-.200s' (erro no. %d)"
- rum "Nu pot sa creez tabla '%-.200s' (Eroare: %d)"
- rus "îÅ×ÏÚÍÏÖÎÏ ÓÏÚÄÁÔØ ÔÁÂÌÉÃÕ '%-.200s' (ÏÛÉÂËÁ: %d)"
- serbian "Ne mogu da kreiram tabelu '%-.200s' (errno: %d)"
- slo "Nemô¾em vytvori» tabuµku '%-.200s' (chybový kód: %d)"
- spa "No puedo crear tabla '%-.200s' (Error: %d)"
- swe "Kan inte skapa tabellen '%-.200s' (Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ÓÔ×ÏÒÉÔÉ ÔÁÂÌÉÃÀ '%-.200s' (ÐÏÍÉÌËÁ: %d)"
-ER_CANT_CREATE_DB
- cze "Nemohu vytvo-Bøit databázi '%-.192s' (chybový kód: %d)"
- dan "Kan ikke oprette databasen '%-.192s' (Fejlkode: %d)"
- nla "Kan database '%-.192s' niet aanmaken (Errcode: %d)"
- eng "Can't create database '%-.192s' (errno: %d)"
- jps "'%-.192s' ƒf[ƒ^ƒx[ƒX‚ªì‚ê‚Ü‚¹‚ñ (errno: %d)",
- est "Ei suuda luua andmebaasi '%-.192s' (veakood: %d)"
- fre "Ne peut créer la base '%-.192s' (Erreur %d)"
- ger "Kann Datenbank '%-.192s' nicht erzeugen (Fehler: %d)"
- greek "Áäýíáôç ç äçìéïõñãßá ôçò âÜóçò äåäïìÝíùí '%-.192s' (êùäéêüò ëÜèïõò: %d)"
- hun "Az '%-.192s' adatbazis nem hozhato letre (hibakod: %d)"
- ita "Impossibile creare il database '%-.192s' (errno: %d)"
- jpn "'%-.192s' ¥Ç¡¼¥¿¥Ù¡¼¥¹¤¬ºî¤ì¤Þ¤»¤ó (errno: %d)"
- kor "µ¥ÀÌŸº£À̽º '%-.192s'¸¦ ¸¸µéÁö ¸øÇß½À´Ï´Ù.. (¿¡·¯¹øÈ£: %d)"
- nor "Kan ikke opprette databasen '%-.192s' (Feilkode: %d)"
- norwegian-ny "Kan ikkje opprette databasen '%-.192s' (Feilkode: %d)"
- pol "Nie mo¿na stworzyæ bazy danych '%-.192s' (Kod b³êdu: %d)"
- por "Não pode criar o banco de dados '%-.192s' (erro no. %d)"
- rum "Nu pot sa creez baza de date '%-.192s' (Eroare: %d)"
- rus "îÅ×ÏÚÍÏÖÎÏ ÓÏÚÄÁÔØ ÂÁÚÕ ÄÁÎÎÙÈ '%-.192s' (ÏÛÉÂËÁ: %d)"
- serbian "Ne mogu da kreiram bazu '%-.192s' (errno: %d)"
- slo "Nemô¾em vytvori» databázu '%-.192s' (chybový kód: %d)"
- spa "No puedo crear base de datos '%-.192s' (Error: %d)"
- swe "Kan inte skapa databasen '%-.192s' (Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ÓÔ×ÏÒÉÔÉ ÂÁÚÕ ÄÁÎÎÉÈ '%-.192s' (ÐÏÍÉÌËÁ: %d)"
-ER_DB_CREATE_EXISTS
- cze "Nemohu vytvo-Bøit databázi '%-.192s'; databáze ji¾ existuje"
- dan "Kan ikke oprette databasen '%-.192s'; databasen eksisterer"
- nla "Kan database '%-.192s' niet aanmaken; database bestaat reeds"
- eng "Can't create database '%-.192s'; database exists"
- jps "'%-.192s' ƒf[ƒ^ƒx[ƒX‚ªì‚ê‚Ü‚¹‚ñ.Šù‚É‚»‚̃f[ƒ^ƒx[ƒX‚ª‘¶Ý‚µ‚Ü‚·",
- est "Ei suuda luua andmebaasi '%-.192s': andmebaas juba eksisteerib"
- fre "Ne peut créer la base '%-.192s'; elle existe déjà"
- ger "Kann Datenbank '%-.192s' nicht erzeugen. Datenbank existiert bereits"
- greek "Áäýíáôç ç äçìéïõñãßá ôçò âÜóçò äåäïìÝíùí '%-.192s'; Ç âÜóç äåäïìÝíùí õðÜñ÷åé Þäç"
- hun "Az '%-.192s' adatbazis nem hozhato letre Az adatbazis mar letezik"
- ita "Impossibile creare il database '%-.192s'; il database esiste"
- jpn "'%-.192s' ¥Ç¡¼¥¿¥Ù¡¼¥¹¤¬ºî¤ì¤Þ¤»¤ó.´û¤Ë¤½¤Î¥Ç¡¼¥¿¥Ù¡¼¥¹¤¬Â¸ºß¤·¤Þ¤¹"
- kor "µ¥ÀÌŸº£À̽º '%-.192s'¸¦ ¸¸µéÁö ¸øÇß½À´Ï´Ù.. µ¥ÀÌŸº£À̽º°¡ Á¸ÀçÇÔ"
- nor "Kan ikke opprette databasen '%-.192s'; databasen eksisterer"
- norwegian-ny "Kan ikkje opprette databasen '%-.192s'; databasen eksisterer"
- pol "Nie mo¿na stworzyæ bazy danych '%-.192s'; baza danych ju¿ istnieje"
- por "Não pode criar o banco de dados '%-.192s'; este banco de dados já existe"
- rum "Nu pot sa creez baza de date '%-.192s'; baza de date exista deja"
- rus "îÅ×ÏÚÍÏÖÎÏ ÓÏÚÄÁÔØ ÂÁÚÕ ÄÁÎÎÙÈ '%-.192s'. âÁÚÁ ÄÁÎÎÙÈ ÕÖÅ ÓÕÝÅÓÔ×ÕÅÔ"
- serbian "Ne mogu da kreiram bazu '%-.192s'; baza veæ postoji."
- slo "Nemô¾em vytvori» databázu '%-.192s'; databáza existuje"
- spa "No puedo crear base de datos '%-.192s'; la base de datos ya existe"
- swe "Databasen '%-.192s' existerar redan"
- ukr "îÅ ÍÏÖÕ ÓÔ×ÏÒÉÔÉ ÂÁÚÕ ÄÁÎÎÉÈ '%-.192s'. âÁÚÁ ÄÁÎÎÉÈ ¦ÓÎÕ¤"
-ER_DB_DROP_EXISTS
- cze "Nemohu zru-B¹it databázi '%-.192s', databáze neexistuje"
- dan "Kan ikke slette (droppe) '%-.192s'; databasen eksisterer ikke"
- nla "Kan database '%-.192s' niet verwijderen; database bestaat niet"
- eng "Can't drop database '%-.192s'; database doesn't exist"
- jps "'%-.192s' ƒf[ƒ^ƒx[ƒX‚ð”jŠü‚Å‚«‚Ü‚¹‚ñ. ‚»‚̃f[ƒ^ƒx[ƒX‚ª‚È‚¢‚Ì‚Å‚·.",
- est "Ei suuda kustutada andmebaasi '%-.192s': andmebaasi ei eksisteeri"
- fre "Ne peut effacer la base '%-.192s'; elle n'existe pas"
- ger "Kann Datenbank '%-.192s' nicht löschen; Datenbank nicht vorhanden"
- greek "Áäýíáôç ç äéáãñáöÞ ôçò âÜóçò äåäïìÝíùí '%-.192s'. Ç âÜóç äåäïìÝíùí äåí õðÜñ÷åé"
- hun "A(z) '%-.192s' adatbazis nem szuntetheto meg. Az adatbazis nem letezik"
- ita "Impossibile cancellare '%-.192s'; il database non esiste"
- jpn "'%-.192s' ¥Ç¡¼¥¿¥Ù¡¼¥¹¤òÇË´þ¤Ç¤­¤Þ¤»¤ó. ¤½¤Î¥Ç¡¼¥¿¥Ù¡¼¥¹¤¬¤Ê¤¤¤Î¤Ç¤¹."
- kor "µ¥ÀÌŸº£À̽º '%-.192s'¸¦ Á¦°ÅÇÏÁö ¸øÇß½À´Ï´Ù. µ¥ÀÌŸº£À̽º°¡ Á¸ÀçÇÏÁö ¾ÊÀ½ "
- nor "Kan ikke fjerne (drop) '%-.192s'; databasen eksisterer ikke"
- norwegian-ny "Kan ikkje fjerne (drop) '%-.192s'; databasen eksisterer ikkje"
- pol "Nie mo¿na usun?æ bazy danych '%-.192s'; baza danych nie istnieje"
- por "Não pode eliminar o banco de dados '%-.192s'; este banco de dados não existe"
- rum "Nu pot sa drop baza de date '%-.192s'; baza da date este inexistenta"
- rus "îÅ×ÏÚÍÏÖÎÏ ÕÄÁÌÉÔØ ÂÁÚÕ ÄÁÎÎÙÈ '%-.192s'. ôÁËÏÊ ÂÁÚÙ ÄÁÎÎÙÈ ÎÅÔ"
- serbian "Ne mogu da izbrišem bazu '%-.192s'; baza ne postoji."
- slo "Nemô¾em zmaza» databázu '%-.192s'; databáza neexistuje"
- spa "No puedo eliminar base de datos '%-.192s'; la base de datos no existe"
- swe "Kan inte radera databasen '%-.192s'; databasen finns inte"
- ukr "îÅ ÍÏÖÕ ×ÉÄÁÌÉÔÉ ÂÁÚÕ ÄÁÎÎÉÈ '%-.192s'. âÁÚÁ ÄÁÎÎÉÈ ÎÅ ¦ÓÎÕ¤"
-ER_DB_DROP_DELETE
- cze "Chyba p-Bøi ru¹ení databáze (nemohu vymazat '%-.192s', chyba %d)"
- dan "Fejl ved sletning (drop) af databasen (kan ikke slette '%-.192s', Fejlkode %d)"
- nla "Fout bij verwijderen database (kan '%-.192s' niet verwijderen, Errcode: %d)"
- eng "Error dropping database (can't delete '%-.192s', errno: %d)"
- jps "ƒf[ƒ^ƒx[ƒX”jŠüƒGƒ‰[ ('%-.192s' ‚ð휂ł«‚Ü‚¹‚ñ, errno: %d)",
- est "Viga andmebaasi kustutamisel (ei suuda kustutada faili '%-.192s', veakood: %d)"
- fre "Ne peut effacer la base '%-.192s' (erreur %d)"
- ger "Fehler beim Löschen der Datenbank ('%-.192s' kann nicht gelöscht werden, Fehler: %d)"
- greek "ÐáñïõóéÜóôçêå ðñüâëçìá êáôÜ ôç äéáãñáöÞ ôçò âÜóçò äåäïìÝíùí (áäýíáôç ç äéáãñáöÞ '%-.192s', êùäéêüò ëÜèïõò: %d)"
- hun "Adatbazis megszuntetesi hiba ('%-.192s' nem torolheto, hibakod: %d)"
- ita "Errore durante la cancellazione del database (impossibile cancellare '%-.192s', errno: %d)"
- jpn "¥Ç¡¼¥¿¥Ù¡¼¥¹ÇË´þ¥¨¥é¡¼ ('%-.192s' ¤òºï½ü¤Ç¤­¤Þ¤»¤ó, errno: %d)"
- kor "µ¥ÀÌŸº£À̽º Á¦°Å ¿¡·¯('%-.192s'¸¦ »èÁ¦ÇÒ ¼ö ¾øÀ¾´Ï´Ù, ¿¡·¯¹øÈ£: %d)"
- nor "Feil ved fjerning (drop) av databasen (kan ikke slette '%-.192s', feil %d)"
- norwegian-ny "Feil ved fjerning (drop) av databasen (kan ikkje slette '%-.192s', feil %d)"
- pol "B³?d podczas usuwania bazy danych (nie mo¿na usun?æ '%-.192s', b³?d %d)"
- por "Erro ao eliminar banco de dados (não pode eliminar '%-.192s' - erro no. %d)"
- rum "Eroare dropuind baza de date (nu pot sa sterg '%-.192s', Eroare: %d)"
- rus "ïÛÉÂËÁ ÐÒÉ ÕÄÁÌÅÎÉÉ ÂÁÚÙ ÄÁÎÎÙÈ (ÎÅ×ÏÚÍÏÖÎÏ ÕÄÁÌÉÔØ '%-.192s', ÏÛÉÂËÁ: %d)"
- serbian "Ne mogu da izbrišem bazu (ne mogu da izbrišem '%-.192s', errno: %d)"
- slo "Chyba pri mazaní databázy (nemô¾em zmaza» '%-.192s', chybový kód: %d)"
- spa "Error eliminando la base de datos(no puedo borrar '%-.192s', error %d)"
- swe "Fel vid radering av databasen (Kan inte radera '%-.192s'. Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ×ÉÄÁÌÉÔÉ ÂÁÚÕ ÄÁÎÎÉÈ (îÅ ÍÏÖÕ ×ÉÄÁÌÉÔÉ '%-.192s', ÐÏÍÉÌËÁ: %d)"
-ER_DB_DROP_RMDIR
- cze "Chyba p-Bøi ru¹ení databáze (nemohu vymazat adresáø '%-.192s', chyba %d)"
- dan "Fejl ved sletting af database (kan ikke slette folderen '%-.192s', Fejlkode %d)"
- nla "Fout bij verwijderen database (kan rmdir '%-.192s' niet uitvoeren, Errcode: %d)"
- eng "Error dropping database (can't rmdir '%-.192s', errno: %d)"
- jps "ƒf[ƒ^ƒx[ƒX”jŠüƒGƒ‰[ ('%-.192s' ‚ð rmdir ‚Å‚«‚Ü‚¹‚ñ, errno: %d)",
- est "Viga andmebaasi kustutamisel (ei suuda kustutada kataloogi '%-.192s', veakood: %d)"
- fre "Erreur en effaçant la base (rmdir '%-.192s', erreur %d)"
- ger "Fehler beim Löschen der Datenbank (Verzeichnis '%-.192s' kann nicht gelöscht werden, Fehler: %d)"
- greek "ÐáñïõóéÜóôçêå ðñüâëçìá êáôÜ ôç äéáãñáöÞ ôçò âÜóçò äåäïìÝíùí (áäýíáôç ç äéáãñáöÞ ôïõ öáêÝëëïõ '%-.192s', êùäéêüò ëÜèïõò: %d)"
- hun "Adatbazis megszuntetesi hiba ('%-.192s' nem szuntetheto meg, hibakod: %d)"
- ita "Errore durante la cancellazione del database (impossibile rmdir '%-.192s', errno: %d)"
- jpn "¥Ç¡¼¥¿¥Ù¡¼¥¹ÇË´þ¥¨¥é¡¼ ('%-.192s' ¤ò rmdir ¤Ç¤­¤Þ¤»¤ó, errno: %d)"
- kor "µ¥ÀÌŸº£À̽º Á¦°Å ¿¡·¯(rmdir '%-.192s'¸¦ ÇÒ ¼ö ¾øÀ¾´Ï´Ù, ¿¡·¯¹øÈ£: %d)"
- nor "Feil ved sletting av database (kan ikke slette katalogen '%-.192s', feil %d)"
- norwegian-ny "Feil ved sletting av database (kan ikkje slette katalogen '%-.192s', feil %d)"
- pol "B³?d podczas usuwania bazy danych (nie mo¿na wykonaæ rmdir '%-.192s', b³?d %d)"
- por "Erro ao eliminar banco de dados (não pode remover diretório '%-.192s' - erro no. %d)"
- rum "Eroare dropuind baza de date (nu pot sa rmdir '%-.192s', Eroare: %d)"
- rus "îÅ×ÏÚÍÏÖÎÏ ÕÄÁÌÉÔØ ÂÁÚÕ ÄÁÎÎÙÈ (ÎÅ×ÏÚÍÏÖÎÏ ÕÄÁÌÉÔØ ËÁÔÁÌÏÇ '%-.192s', ÏÛÉÂËÁ: %d)"
- serbian "Ne mogu da izbrišem bazu (ne mogu da izbrišem direktorijum '%-.192s', errno: %d)"
- slo "Chyba pri mazaní databázy (nemô¾em vymaza» adresár '%-.192s', chybový kód: %d)"
- spa "Error eliminando la base de datos (No puedo borrar directorio '%-.192s', error %d)"
- swe "Fel vid radering av databasen (Kan inte radera biblioteket '%-.192s'. Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ×ÉÄÁÌÉÔÉ ÂÁÚÕ ÄÁÎÎÉÈ (îÅ ÍÏÖÕ ×ÉÄÁÌÉÔÉ ÔÅËÕ '%-.192s', ÐÏÍÉÌËÁ: %d)"
-ER_CANT_DELETE_FILE
- cze "Chyba p-Bøi výmazu '%-.192s' (chybový kód: %d)"
- dan "Fejl ved sletning af '%-.192s' (Fejlkode: %d)"
- nla "Fout bij het verwijderen van '%-.192s' (Errcode: %d)"
- eng "Error on delete of '%-.192s' (errno: %d)"
- jps "'%-.192s' ‚Ì휂ªƒGƒ‰[ (errno: %d)",
- est "Viga '%-.192s' kustutamisel (veakood: %d)"
- fre "Erreur en effaçant '%-.192s' (Errcode: %d)"
- ger "Fehler beim Löschen von '%-.192s' (Fehler: %d)"
- greek "ÐáñïõóéÜóôçêå ðñüâëçìá êáôÜ ôç äéáãñáöÞ '%-.192s' (êùäéêüò ëÜèïõò: %d)"
- hun "Torlesi hiba: '%-.192s' (hibakod: %d)"
- ita "Errore durante la cancellazione di '%-.192s' (errno: %d)"
- jpn "'%-.192s' ¤Îºï½ü¤¬¥¨¥é¡¼ (errno: %d)"
- kor "'%-.192s' »èÁ¦ Áß ¿¡·¯ (¿¡·¯¹øÈ£: %d)"
- nor "Feil ved sletting av '%-.192s' (Feilkode: %d)"
- norwegian-ny "Feil ved sletting av '%-.192s' (Feilkode: %d)"
- pol "B³?d podczas usuwania '%-.192s' (Kod b³êdu: %d)"
- por "Erro na remoção de '%-.192s' (erro no. %d)"
- rum "Eroare incercind sa delete '%-.192s' (Eroare: %d)"
- rus "ïÛÉÂËÁ ÐÒÉ ÕÄÁÌÅÎÉÉ '%-.192s' (ÏÛÉÂËÁ: %d)"
- serbian "Greška pri brisanju '%-.192s' (errno: %d)"
- slo "Chyba pri mazaní '%-.192s' (chybový kód: %d)"
- spa "Error en el borrado de '%-.192s' (Error: %d)"
- swe "Kan inte radera filen '%-.192s' (Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ×ÉÄÁÌÉÔÉ '%-.192s' (ÐÏÍÉÌËÁ: %d)"
-ER_CANT_FIND_SYSTEM_REC
- cze "Nemohu -Bèíst záznam v systémové tabulce"
- dan "Kan ikke læse posten i systemfolderen"
- nla "Kan record niet lezen in de systeem tabel"
- eng "Can't read record in system table"
- jps "system table ‚̃ŒƒR[ƒh‚ð“Ç‚ÞŽ–‚ª‚Å‚«‚Ü‚¹‚ñ‚Å‚µ‚½",
- est "Ei suuda lugeda kirjet süsteemsest tabelist"
- fre "Ne peut lire un enregistrement de la table 'system'"
- ger "Datensatz in der Systemtabelle nicht lesbar"
- greek "Áäýíáôç ç áíÜãíùóç åããñáöÞò áðü ðßíáêá ôïõ óõóôÞìáôïò"
- hun "Nem olvashato rekord a rendszertablaban"
- ita "Impossibile leggere il record dalla tabella di sistema"
- jpn "system table ¤Î¥ì¥³¡¼¥É¤òÆɤà»ö¤¬¤Ç¤­¤Þ¤»¤ó¤Ç¤·¤¿"
- kor "system Å×ÀÌºí¿¡¼­ ·¹Äڵ带 ÀÐÀ» ¼ö ¾ø½À´Ï´Ù."
- nor "Kan ikke lese posten i systemkatalogen"
- norwegian-ny "Kan ikkje lese posten i systemkatalogen"
- pol "Nie mo¿na odczytaæ rekordu z tabeli systemowej"
- por "Não pode ler um registro numa tabela do sistema"
- rum "Nu pot sa citesc cimpurile in tabla de system (system table)"
- rus "îÅ×ÏÚÍÏÖÎÏ ÐÒÏÞÉÔÁÔØ ÚÁÐÉÓØ × ÓÉÓÔÅÍÎÏÊ ÔÁÂÌÉÃÅ"
- serbian "Ne mogu da proèitam slog iz sistemske tabele"
- slo "Nemô¾em èíta» záznam v systémovej tabuµke"
- spa "No puedo leer el registro en la tabla del sistema"
- swe "Hittar inte posten i systemregistret"
- ukr "îÅ ÍÏÖÕ ÚÞÉÔÁÔÉ ÚÁÐÉÓ Ú ÓÉÓÔÅÍÎϧ ÔÁÂÌÉæ"
-ER_CANT_GET_STAT
- cze "Nemohu z-Bískat stav '%-.200s' (chybový kód: %d)"
- dan "Kan ikke læse status af '%-.200s' (Fejlkode: %d)"
- nla "Kan de status niet krijgen van '%-.200s' (Errcode: %d)"
- eng "Can't get status of '%-.200s' (errno: %d)"
- jps "'%-.200s' ‚̃XƒeƒCƒ^ƒX‚ª“¾‚ç‚ê‚Ü‚¹‚ñ. (errno: %d)",
- est "Ei suuda lugeda '%-.200s' olekut (veakood: %d)"
- fre "Ne peut obtenir le status de '%-.200s' (Errcode: %d)"
- ger "Kann Status von '%-.200s' nicht ermitteln (Fehler: %d)"
- greek "Áäýíáôç ç ëÞøç ðëçñïöïñéþí ãéá ôçí êáôÜóôáóç ôïõ '%-.200s' (êùäéêüò ëÜèïõò: %d)"
- hun "A(z) '%-.200s' statusza nem allapithato meg (hibakod: %d)"
- ita "Impossibile leggere lo stato di '%-.200s' (errno: %d)"
- jpn "'%-.200s' ¤Î¥¹¥Æ¥¤¥¿¥¹¤¬ÆÀ¤é¤ì¤Þ¤»¤ó. (errno: %d)"
- kor "'%-.200s'ÀÇ »óŸ¦ ¾òÁö ¸øÇß½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)"
- nor "Kan ikke lese statusen til '%-.200s' (Feilkode: %d)"
- norwegian-ny "Kan ikkje lese statusen til '%-.200s' (Feilkode: %d)"
- pol "Nie mo¿na otrzymaæ statusu '%-.200s' (Kod b³êdu: %d)"
- por "Não pode obter o status de '%-.200s' (erro no. %d)"
- rum "Nu pot sa obtin statusul lui '%-.200s' (Eroare: %d)"
- rus "îÅ×ÏÚÍÏÖÎÏ ÐÏÌÕÞÉÔØ ÓÔÁÔÕÓÎÕÀ ÉÎÆÏÒÍÁÃÉÀ Ï '%-.200s' (ÏÛÉÂËÁ: %d)"
- serbian "Ne mogu da dobijem stanje file-a '%-.200s' (errno: %d)"
- slo "Nemô¾em zisti» stav '%-.200s' (chybový kód: %d)"
- spa "No puedo obtener el estado de '%-.200s' (Error: %d)"
- swe "Kan inte läsa filinformationen (stat) från '%-.200s' (Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ÏÔÒÉÍÁÔÉ ÓÔÁÔÕÓ '%-.200s' (ÐÏÍÉÌËÁ: %d)"
-ER_CANT_GET_WD
- cze "Chyba p-Bøi zji¹»ování pracovní adresáø (chybový kód: %d)"
- dan "Kan ikke læse aktive folder (Fejlkode: %d)"
- nla "Kan de werkdirectory niet krijgen (Errcode: %d)"
- eng "Can't get working directory (errno: %d)"
- jps "working directory ‚𓾂鎖‚ª‚Å‚«‚Ü‚¹‚ñ‚Å‚µ‚½ (errno: %d)",
- est "Ei suuda identifitseerida jooksvat kataloogi (veakood: %d)"
- fre "Ne peut obtenir le répertoire de travail (Errcode: %d)"
- ger "Kann Arbeitsverzeichnis nicht ermitteln (Fehler: %d)"
- greek "Ï öÜêåëëïò åñãáóßáò äåí âñÝèçêå (êùäéêüò ëÜèïõò: %d)"
- hun "A munkakonyvtar nem allapithato meg (hibakod: %d)"
- ita "Impossibile leggere la directory di lavoro (errno: %d)"
- jpn "working directory ¤òÆÀ¤ë»ö¤¬¤Ç¤­¤Þ¤»¤ó¤Ç¤·¤¿ (errno: %d)"
- kor "¼öÇà µð·ºÅ丮¸¦ ãÁö ¸øÇß½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)"
- nor "Kan ikke lese aktiv katalog(Feilkode: %d)"
- norwegian-ny "Kan ikkje lese aktiv katalog(Feilkode: %d)"
- pol "Nie mo¿na rozpoznaæ aktualnego katalogu (Kod b³êdu: %d)"
- por "Não pode obter o diretório corrente (erro no. %d)"
- rum "Nu pot sa obtin directorul current (working directory) (Eroare: %d)"
- rus "îÅ×ÏÚÍÏÖÎÏ ÏÐÒÅÄÅÌÉÔØ ÒÁÂÏÞÉÊ ËÁÔÁÌÏÇ (ÏÛÉÂËÁ: %d)"
- serbian "Ne mogu da dobijem trenutni direktorijum (errno: %d)"
- slo "Nemô¾em zisti» pracovný adresár (chybový kód: %d)"
- spa "No puedo acceder al directorio (Error: %d)"
- swe "Kan inte inte läsa aktivt bibliotek. (Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ×ÉÚÎÁÞÉÔÉ ÒÏÂÏÞÕ ÔÅËÕ (ÐÏÍÉÌËÁ: %d)"
-ER_CANT_LOCK
- cze "Nemohu uzamknout soubor (chybov-Bý kód: %d)"
- dan "Kan ikke låse fil (Fejlkode: %d)"
- nla "Kan de file niet blokeren (Errcode: %d)"
- eng "Can't lock file (errno: %d)"
- jps "ƒtƒ@ƒCƒ‹‚ðƒƒbƒN‚Å‚«‚Ü‚¹‚ñ (errno: %d)",
- est "Ei suuda lukustada faili (veakood: %d)"
- fre "Ne peut verrouiller le fichier (Errcode: %d)"
- ger "Datei kann nicht gesperrt werden (Fehler: %d)"
- greek "Ôï áñ÷åßï äåí ìðïñåß íá êëåéäùèåß (êùäéêüò ëÜèïõò: %d)"
- hun "A file nem zarolhato. (hibakod: %d)"
- ita "Impossibile il locking il file (errno: %d)"
- jpn "¥Õ¥¡¥¤¥ë¤ò¥í¥Ã¥¯¤Ç¤­¤Þ¤»¤ó (errno: %d)"
- kor "È­ÀÏÀ» Àá±×Áö(lock) ¸øÇß½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)"
- nor "Kan ikke låse fila (Feilkode: %d)"
- norwegian-ny "Kan ikkje låse fila (Feilkode: %d)"
- pol "Nie mo¿na zablokowaæ pliku (Kod b³êdu: %d)"
- por "Não pode travar o arquivo (erro no. %d)"
- rum "Nu pot sa lock fisierul (Eroare: %d)"
- rus "îÅ×ÏÚÍÏÖÎÏ ÐÏÓÔÁ×ÉÔØ ÂÌÏËÉÒÏ×ËÕ ÎÁ ÆÁÊÌÅ (ÏÛÉÂËÁ: %d)"
- serbian "Ne mogu da zakljuèam file (errno: %d)"
- slo "Nemô¾em zamknú» súbor (chybový kód: %d)"
- spa "No puedo bloquear archivo: (Error: %d)"
- swe "Kan inte låsa filen. (Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ÚÁÂÌÏËÕ×ÁÔÉ ÆÁÊÌ (ÐÏÍÉÌËÁ: %d)"
-ER_CANT_OPEN_FILE
- cze "Nemohu otev-Bøít soubor '%-.200s' (chybový kód: %d)"
- dan "Kan ikke åbne fil: '%-.200s' (Fejlkode: %d)"
- nla "Kan de file '%-.200s' niet openen (Errcode: %d)"
- eng "Can't open file: '%-.200s' (errno: %d)"
- jps "'%-.200s' ƒtƒ@ƒCƒ‹‚ðŠJ‚­Ž–‚ª‚Å‚«‚Ü‚¹‚ñ (errno: %d)",
- est "Ei suuda avada faili '%-.200s' (veakood: %d)"
- fre "Ne peut ouvrir le fichier: '%-.200s' (Errcode: %d)"
- ger "Kann Datei '%-.200s' nicht öffnen (Fehler: %d)"
- greek "Äåí åßíáé äõíáôü íá áíïé÷ôåß ôï áñ÷åßï: '%-.200s' (êùäéêüò ëÜèïõò: %d)"
- hun "A '%-.200s' file nem nyithato meg (hibakod: %d)"
- ita "Impossibile aprire il file: '%-.200s' (errno: %d)"
- jpn "'%-.200s' ¥Õ¥¡¥¤¥ë¤ò³«¤¯»ö¤¬¤Ç¤­¤Þ¤»¤ó (errno: %d)"
- kor "È­ÀÏÀ» ¿­Áö ¸øÇß½À´Ï´Ù.: '%-.200s' (¿¡·¯¹øÈ£: %d)"
- nor "Kan ikke åpne fila: '%-.200s' (Feilkode: %d)"
- norwegian-ny "Kan ikkje åpne fila: '%-.200s' (Feilkode: %d)"
- pol "Nie mo¿na otworzyæ pliku: '%-.200s' (Kod b³êdu: %d)"
- por "Não pode abrir o arquivo '%-.200s' (erro no. %d)"
- rum "Nu pot sa deschid fisierul: '%-.200s' (Eroare: %d)"
- rus "îÅ×ÏÚÍÏÖÎÏ ÏÔËÒÙÔØ ÆÁÊÌ: '%-.200s' (ÏÛÉÂËÁ: %d)"
- serbian "Ne mogu da otvorim file: '%-.200s' (errno: %d)"
- slo "Nemô¾em otvori» súbor: '%-.200s' (chybový kód: %d)"
- spa "No puedo abrir archivo: '%-.200s' (Error: %d)"
- swe "Kan inte använda '%-.200s' (Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ×¦ÄËÒÉÔÉ ÆÁÊÌ: '%-.200s' (ÐÏÍÉÌËÁ: %d)"
-ER_FILE_NOT_FOUND
- cze "Nemohu naj-Bít soubor '%-.200s' (chybový kód: %d)"
- dan "Kan ikke finde fila: '%-.200s' (Fejlkode: %d)"
- nla "Kan de file: '%-.200s' niet vinden (Errcode: %d)"
- eng "Can't find file: '%-.200s' (errno: %d)"
- jps "'%-.200s' ƒtƒ@ƒCƒ‹‚ðŒ©•t‚¯‚鎖‚ª‚Å‚«‚Ü‚¹‚ñ.(errno: %d)",
- est "Ei suuda leida faili '%-.200s' (veakood: %d)"
- fre "Ne peut trouver le fichier: '%-.200s' (Errcode: %d)"
- ger "Kann Datei '%-.200s' nicht finden (Fehler: %d)"
- greek "Äåí âñÝèçêå ôï áñ÷åßï: '%-.200s' (êùäéêüò ëÜèïõò: %d)"
- hun "A(z) '%-.200s' file nem talalhato (hibakod: %d)"
- ita "Impossibile trovare il file: '%-.200s' (errno: %d)"
- jpn "'%-.200s' ¥Õ¥¡¥¤¥ë¤ò¸«ÉÕ¤±¤ë»ö¤¬¤Ç¤­¤Þ¤»¤ó.(errno: %d)"
- kor "È­ÀÏÀ» ãÁö ¸øÇß½À´Ï´Ù.: '%-.200s' (¿¡·¯¹øÈ£: %d)"
- nor "Kan ikke finne fila: '%-.200s' (Feilkode: %d)"
- norwegian-ny "Kan ikkje finne fila: '%-.200s' (Feilkode: %d)"
- pol "Nie mo¿na znale¥æ pliku: '%-.200s' (Kod b³êdu: %d)"
- por "Não pode encontrar o arquivo '%-.200s' (erro no. %d)"
- rum "Nu pot sa gasesc fisierul: '%-.200s' (Eroare: %d)"
- rus "îÅ×ÏÚÍÏÖÎÏ ÎÁÊÔÉ ÆÁÊÌ: '%-.200s' (ÏÛÉÂËÁ: %d)"
- serbian "Ne mogu da pronaðem file: '%-.200s' (errno: %d)"
- slo "Nemô¾em nájs» súbor: '%-.200s' (chybový kód: %d)"
- spa "No puedo encontrar archivo: '%-.200s' (Error: %d)"
- swe "Hittar inte filen '%-.200s' (Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ÚÎÁÊÔÉ ÆÁÊÌ: '%-.200s' (ÐÏÍÉÌËÁ: %d)"
-ER_CANT_READ_DIR
- cze "Nemohu -Bèíst adresáø '%-.192s' (chybový kód: %d)"
- dan "Kan ikke læse folder '%-.192s' (Fejlkode: %d)"
- nla "Kan de directory niet lezen van '%-.192s' (Errcode: %d)"
- eng "Can't read dir of '%-.192s' (errno: %d)"
- jps "'%-.192s' ƒfƒBƒŒƒNƒgƒŠ‚ª“Ç‚ß‚Ü‚¹‚ñ.(errno: %d)",
- est "Ei suuda lugeda kataloogi '%-.192s' (veakood: %d)"
- fre "Ne peut lire le répertoire de '%-.192s' (Errcode: %d)"
- ger "Verzeichnis von '%-.192s' nicht lesbar (Fehler: %d)"
- greek "Äåí åßíáé äõíáôü íá äéáâáóôåß ï öÜêåëëïò ôïõ '%-.192s' (êùäéêüò ëÜèïõò: %d)"
- hun "A(z) '%-.192s' konyvtar nem olvashato. (hibakod: %d)"
- ita "Impossibile leggere la directory di '%-.192s' (errno: %d)"
- jpn "'%-.192s' ¥Ç¥£¥ì¥¯¥È¥ê¤¬Æɤá¤Þ¤»¤ó.(errno: %d)"
- kor "'%-.192s'µð·ºÅ丮¸¦ ÀÐÁö ¸øÇß½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)"
- nor "Kan ikke lese katalogen '%-.192s' (Feilkode: %d)"
- norwegian-ny "Kan ikkje lese katalogen '%-.192s' (Feilkode: %d)"
- pol "Nie mo¿na odczytaæ katalogu '%-.192s' (Kod b³êdu: %d)"
- por "Não pode ler o diretório de '%-.192s' (erro no. %d)"
- rum "Nu pot sa citesc directorul '%-.192s' (Eroare: %d)"
- rus "îÅ×ÏÚÍÏÖÎÏ ÐÒÏÞÉÔÁÔØ ËÁÔÁÌÏÇ '%-.192s' (ÏÛÉÂËÁ: %d)"
- serbian "Ne mogu da proèitam direktorijum '%-.192s' (errno: %d)"
- slo "Nemô¾em èíta» adresár '%-.192s' (chybový kód: %d)"
- spa "No puedo leer el directorio de '%-.192s' (Error: %d)"
- swe "Kan inte läsa från bibliotek '%-.192s' (Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ÐÒÏÞÉÔÁÔÉ ÔÅËÕ '%-.192s' (ÐÏÍÉÌËÁ: %d)"
-ER_CANT_SET_WD
- cze "Nemohu zm-Bìnit adresáø na '%-.192s' (chybový kód: %d)"
- dan "Kan ikke skifte folder til '%-.192s' (Fejlkode: %d)"
- nla "Kan de directory niet veranderen naar '%-.192s' (Errcode: %d)"
- eng "Can't change dir to '%-.192s' (errno: %d)"
- jps "'%-.192s' ƒfƒBƒŒƒNƒgƒŠ‚É chdir ‚Å‚«‚Ü‚¹‚ñ.(errno: %d)",
- est "Ei suuda siseneda kataloogi '%-.192s' (veakood: %d)"
- fre "Ne peut changer le répertoire pour '%-.192s' (Errcode: %d)"
- ger "Kann nicht in das Verzeichnis '%-.192s' wechseln (Fehler: %d)"
- greek "Áäýíáôç ç áëëáãÞ ôïõ ôñÝ÷ïíôïò êáôáëüãïõ óå '%-.192s' (êùäéêüò ëÜèïõò: %d)"
- hun "Konyvtarvaltas nem lehetseges a(z) '%-.192s'-ba. (hibakod: %d)"
- ita "Impossibile cambiare la directory in '%-.192s' (errno: %d)"
- jpn "'%-.192s' ¥Ç¥£¥ì¥¯¥È¥ê¤Ë chdir ¤Ç¤­¤Þ¤»¤ó.(errno: %d)"
- kor "'%-.192s'µð·ºÅ丮·Î À̵¿ÇÒ ¼ö ¾ø¾ú½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)"
- nor "Kan ikke skifte katalog til '%-.192s' (Feilkode: %d)"
- norwegian-ny "Kan ikkje skifte katalog til '%-.192s' (Feilkode: %d)"
- pol "Nie mo¿na zmieniæ katalogu na '%-.192s' (Kod b³êdu: %d)"
- por "Não pode mudar para o diretório '%-.192s' (erro no. %d)"
- rum "Nu pot sa schimb directorul '%-.192s' (Eroare: %d)"
- rus "îÅ×ÏÚÍÏÖÎÏ ÐÅÒÅÊÔÉ × ËÁÔÁÌÏÇ '%-.192s' (ÏÛÉÂËÁ: %d)"
- serbian "Ne mogu da promenim direktorijum na '%-.192s' (errno: %d)"
- slo "Nemô¾em vojs» do adresára '%-.192s' (chybový kód: %d)"
- spa "No puedo cambiar al directorio de '%-.192s' (Error: %d)"
- swe "Kan inte byta till '%-.192s' (Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ÐÅÒÅÊÔÉ Õ ÔÅËÕ '%-.192s' (ÐÏÍÉÌËÁ: %d)"
-ER_CHECKREAD
- cze "Z-Báznam byl zmìnìn od posledního ètení v tabulce '%-.192s'"
- dan "Posten er ændret siden sidste læsning '%-.192s'"
- nla "Record is veranderd sinds de laatste lees activiteit in de tabel '%-.192s'"
- eng "Record has changed since last read in table '%-.192s'"
- est "Kirje tabelis '%-.192s' on muutunud viimasest lugemisest saadik"
- fre "Enregistrement modifié depuis sa dernière lecture dans la table '%-.192s'"
- ger "Datensatz hat sich seit dem letzten Zugriff auf Tabelle '%-.192s' geändert"
- greek "Ç åããñáöÞ Ý÷åé áëëÜîåé áðü ôçí ôåëåõôáßá öïñÜ ðïõ áíáóýñèçêå áðü ôïí ðßíáêá '%-.192s'"
- hun "A(z) '%-.192s' tablaban talalhato rekord megvaltozott az utolso olvasas ota"
- ita "Il record e` cambiato dall'ultima lettura della tabella '%-.192s'"
- kor "Å×À̺í '%-.192s'¿¡¼­ ¸¶Áö¸·À¸·Î ÀÐÀº ÈÄ Record°¡ º¯°æµÇ¾ú½À´Ï´Ù."
- nor "Posten har blitt endret siden den ble lest '%-.192s'"
- norwegian-ny "Posten har vorte endra sidan den sist vart lesen '%-.192s'"
- pol "Rekord zosta³ zmieniony od ostaniego odczytania z tabeli '%-.192s'"
- por "Registro alterado desde a última leitura da tabela '%-.192s'"
- rum "Cimpul a fost schimbat de la ultima citire a tabelei '%-.192s'"
- rus "úÁÐÉÓØ ÉÚÍÅÎÉÌÁÓØ Ó ÍÏÍÅÎÔÁ ÐÏÓÌÅÄÎÅÊ ×ÙÂÏÒËÉ × ÔÁÂÌÉÃÅ '%-.192s'"
- serbian "Slog je promenjen od zadnjeg èitanja tabele '%-.192s'"
- slo "Záznam bol zmenený od posledného èítania v tabuµke '%-.192s'"
- spa "El registro ha cambiado desde la ultima lectura de la tabla '%-.192s'"
- swe "Posten har förändrats sedan den lästes i register '%-.192s'"
- ukr "úÁÐÉÓ ÂÕÌÏ ÚͦÎÅÎÏ Ú ÞÁÓÕ ÏÓÔÁÎÎØÏÇÏ ÞÉÔÁÎÎÑ Ú ÔÁÂÌÉæ '%-.192s'"
-ER_DISK_FULL
- cze "Disk je pln-Bý (%s), èekám na uvolnìní nìjakého místa ..."
- dan "Ikke mere diskplads (%s). Venter på at få frigjort plads..."
- nla "Schijf vol (%s). Aan het wachten totdat er ruimte vrij wordt gemaakt..."
- eng "Disk full (%s); waiting for someone to free some space..."
- jps "Disk full (%s). ’N‚©‚ª‰½‚©‚ðŒ¸‚ç‚·‚Ü‚Å‚Ü‚Á‚Ä‚­‚¾‚³‚¢...",
- est "Ketas täis (%s). Ootame kuni tekib vaba ruumi..."
- fre "Disque plein (%s). J'attend que quelqu'un libère de l'espace..."
- ger "Festplatte voll (%s). Warte, bis jemand Platz schafft ..."
- greek "Äåí õðÜñ÷åé ÷þñïò óôï äßóêï (%s). Ðáñáêáëþ, ðåñéìÝíåôå íá åëåõèåñùèåß ÷þñïò..."
- hun "A lemez megtelt (%s)."
- ita "Disco pieno (%s). In attesa che qualcuno liberi un po' di spazio..."
- jpn "Disk full (%s). 狼¤¬²¿¤«¤ò¸º¤é¤¹¤Þ¤Ç¤Þ¤Ã¤Æ¤¯¤À¤µ¤¤..."
- kor "Disk full (%s). ´Ù¸¥ »ç¶÷ÀÌ Áö¿ï¶§±îÁö ±â´Ù¸³´Ï´Ù..."
- nor "Ikke mer diskplass (%s). Venter på å få frigjort plass..."
- norwegian-ny "Ikkje meir diskplass (%s). Ventar på å få frigjort plass..."
- pol "Dysk pe³ny (%s). Oczekiwanie na zwolnienie miejsca..."
- por "Disco cheio (%s). Aguardando alguém liberar algum espaço..."
- rum "Hard-disk-ul este plin (%s). Astept sa se elibereze ceva spatiu..."
- rus "äÉÓË ÚÁÐÏÌÎÅÎ. (%s). ïÖÉÄÁÅÍ, ÐÏËÁ ËÔÏ-ÔÏ ÎÅ ÕÂÅÒÅÔ ÐÏÓÌÅ ÓÅÂÑ ÍÕÓÏÒ..."
- serbian "Disk je pun (%s). Èekam nekoga da doðe i oslobodi nešto mesta..."
- slo "Disk je plný (%s), èakám na uvoµnenie miesta..."
- spa "Disco lleno (%s). Esperando para que se libere algo de espacio..."
- swe "Disken är full (%s). Väntar tills det finns ledigt utrymme..."
- ukr "äÉÓË ÚÁÐÏ×ÎÅÎÉÊ (%s). ÷ÉÞÉËÕÀ, ÄÏËÉ ÚצÌØÎÉÔØÓÑ ÔÒÏÈÉ Í¦ÓÃÑ..."
-ER_DUP_KEY 23000
- cze "Nemohu zapsat, zdvojen-Bý klíè v tabulce '%-.192s'"
- dan "Kan ikke skrive, flere ens nøgler i tabellen '%-.192s'"
- nla "Kan niet schrijven, dubbele zoeksleutel in tabel '%-.192s'"
- eng "Can't write; duplicate key in table '%-.192s'"
- jps "table '%-.192s' ‚É key ‚ªd•¡‚µ‚Ä‚¢‚Ä‘‚«‚±‚ß‚Ü‚¹‚ñ",
- est "Ei saa kirjutada, korduv võti tabelis '%-.192s'"
- fre "Ecriture impossible, doublon dans une clé de la table '%-.192s'"
- ger "Kann nicht speichern, Grund: doppelter Schlüssel in Tabelle '%-.192s'"
- greek "Äåí åßíáé äõíáôÞ ç êáôá÷þñçóç, ç ôéìÞ õðÜñ÷åé Þäç óôïí ðßíáêá '%-.192s'"
- hun "Irasi hiba, duplikalt kulcs a '%-.192s' tablaban."
- ita "Scrittura impossibile: chiave duplicata nella tabella '%-.192s'"
- jpn "table '%-.192s' ¤Ë key ¤¬½ÅÊ£¤·¤Æ¤¤¤Æ½ñ¤­¤³¤á¤Þ¤»¤ó"
- kor "±â·ÏÇÒ ¼ö ¾øÀ¾´Ï´Ù., Å×À̺í '%-.192s'¿¡¼­ Áߺ¹ Å°"
- nor "Kan ikke skrive, flere like nøkler i tabellen '%-.192s'"
- norwegian-ny "Kan ikkje skrive, flere like nyklar i tabellen '%-.192s'"
- pol "Nie mo¿na zapisaæ, powtórzone klucze w tabeli '%-.192s'"
- por "Não pode gravar. Chave duplicada na tabela '%-.192s'"
- rum "Nu pot sa scriu (can't write), cheie duplicata in tabela '%-.192s'"
- rus "îÅ×ÏÚÍÏÖÎÏ ÐÒÏÉÚ×ÅÓÔÉ ÚÁÐÉÓØ, ÄÕÂÌÉÒÕÀÝÉÊÓÑ ËÌÀÞ × ÔÁÂÌÉÃÅ '%-.192s'"
- serbian "Ne mogu da pišem pošto postoji duplirani kljuè u tabeli '%-.192s'"
- slo "Nemô¾em zapísa», duplikát kµúèa v tabuµke '%-.192s'"
- spa "No puedo escribir, clave duplicada en la tabla '%-.192s'"
- swe "Kan inte skriva, dubbel söknyckel i register '%-.192s'"
- ukr "îÅ ÍÏÖÕ ÚÁÐÉÓÁÔÉ, ÄÕÂÌÀÀÞÉÊÓÑ ËÌÀÞ × ÔÁÂÌÉæ '%-.192s'"
-ER_ERROR_ON_CLOSE
- cze "Chyba p-Bøi zavírání '%-.192s' (chybový kód: %d)"
- dan "Fejl ved lukning af '%-.192s' (Fejlkode: %d)"
- nla "Fout bij het sluiten van '%-.192s' (Errcode: %d)"
- eng "Error on close of '%-.192s' (errno: %d)"
- est "Viga faili '%-.192s' sulgemisel (veakood: %d)"
- fre "Erreur a la fermeture de '%-.192s' (Errcode: %d)"
- ger "Fehler beim Schließen von '%-.192s' (Fehler: %d)"
- greek "ÐáñïõóéÜóôçêå ðñüâëçìá êëåßíïíôáò ôï '%-.192s' (êùäéêüò ëÜèïõò: %d)"
- hun "Hiba a(z) '%-.192s' zarasakor. (hibakod: %d)"
- ita "Errore durante la chiusura di '%-.192s' (errno: %d)"
- kor "'%-.192s'´Ý´Â Áß ¿¡·¯ (¿¡·¯¹øÈ£: %d)"
- nor "Feil ved lukking av '%-.192s' (Feilkode: %d)"
- norwegian-ny "Feil ved lukking av '%-.192s' (Feilkode: %d)"
- pol "B³?d podczas zamykania '%-.192s' (Kod b³êdu: %d)"
- por "Erro ao fechar '%-.192s' (erro no. %d)"
- rum "Eroare inchizind '%-.192s' (errno: %d)"
- rus "ïÛÉÂËÁ ÐÒÉ ÚÁËÒÙÔÉÉ '%-.192s' (ÏÛÉÂËÁ: %d)"
- serbian "Greška pri zatvaranju '%-.192s' (errno: %d)"
- slo "Chyba pri zatváraní '%-.192s' (chybový kód: %d)"
- spa "Error en el cierre de '%-.192s' (Error: %d)"
- swe "Fick fel vid stängning av '%-.192s' (Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ÚÁËÒÉÔÉ '%-.192s' (ÐÏÍÉÌËÁ: %d)"
-ER_ERROR_ON_READ
- cze "Chyba p-Bøi ètení souboru '%-.200s' (chybový kód: %d)"
- dan "Fejl ved læsning af '%-.200s' (Fejlkode: %d)"
- nla "Fout bij het lezen van file '%-.200s' (Errcode: %d)"
- eng "Error reading file '%-.200s' (errno: %d)"
- jps "'%-.200s' ƒtƒ@ƒCƒ‹‚Ì“Ç‚Ýž‚݃Gƒ‰[ (errno: %d)",
- est "Viga faili '%-.200s' lugemisel (veakood: %d)"
- fre "Erreur en lecture du fichier '%-.200s' (Errcode: %d)"
- ger "Fehler beim Lesen der Datei '%-.200s' (Fehler: %d)"
- greek "Ðñüâëçìá êáôÜ ôçí áíÜãíùóç ôïõ áñ÷åßïõ '%-.200s' (êùäéêüò ëÜèïõò: %d)"
- hun "Hiba a '%-.200s'file olvasasakor. (hibakod: %d)"
- ita "Errore durante la lettura del file '%-.200s' (errno: %d)"
- jpn "'%-.200s' ¥Õ¥¡¥¤¥ë¤ÎÆɤ߹þ¤ß¥¨¥é¡¼ (errno: %d)"
- kor "'%-.200s'È­ÀÏ Àб⠿¡·¯ (¿¡·¯¹øÈ£: %d)"
- nor "Feil ved lesing av '%-.200s' (Feilkode: %d)"
- norwegian-ny "Feil ved lesing av '%-.200s' (Feilkode: %d)"
- pol "B³?d podczas odczytu pliku '%-.200s' (Kod b³êdu: %d)"
- por "Erro ao ler arquivo '%-.200s' (erro no. %d)"
- rum "Eroare citind fisierul '%-.200s' (errno: %d)"
- rus "ïÛÉÂËÁ ÞÔÅÎÉÑ ÆÁÊÌÁ '%-.200s' (ÏÛÉÂËÁ: %d)"
- serbian "Greška pri èitanju file-a '%-.200s' (errno: %d)"
- slo "Chyba pri èítaní súboru '%-.200s' (chybový kód: %d)"
- spa "Error leyendo el fichero '%-.200s' (Error: %d)"
- swe "Fick fel vid läsning av '%-.200s' (Felkod %d)"
- ukr "îÅ ÍÏÖÕ ÐÒÏÞÉÔÁÔÉ ÆÁÊÌ '%-.200s' (ÐÏÍÉÌËÁ: %d)"
-ER_ERROR_ON_RENAME
- cze "Chyba p-Bøi pøejmenování '%-.210s' na '%-.210s' (chybový kód: %d)"
- dan "Fejl ved omdøbning af '%-.210s' til '%-.210s' (Fejlkode: %d)"
- nla "Fout bij het hernoemen van '%-.210s' naar '%-.210s' (Errcode: %d)"
- eng "Error on rename of '%-.210s' to '%-.210s' (errno: %d)"
- jps "'%-.210s' ‚ð '%-.210s' ‚É rename ‚Å‚«‚Ü‚¹‚ñ (errno: %d)",
- est "Viga faili '%-.210s' ümbernimetamisel '%-.210s'-ks (veakood: %d)"
- fre "Erreur en renommant '%-.210s' en '%-.210s' (Errcode: %d)"
- ger "Fehler beim Umbenennen von '%-.210s' in '%-.210s' (Fehler: %d)"
- greek "Ðñüâëçìá êáôÜ ôçí ìåôïíïìáóßá ôïõ áñ÷åßïõ '%-.210s' to '%-.210s' (êùäéêüò ëÜèïõò: %d)"
- hun "Hiba a '%-.210s' file atnevezesekor '%-.210s'. (hibakod: %d)"
- ita "Errore durante la rinominazione da '%-.210s' a '%-.210s' (errno: %d)"
- jpn "'%-.210s' ¤ò '%-.210s' ¤Ë rename ¤Ç¤­¤Þ¤»¤ó (errno: %d)"
- kor "'%-.210s'¸¦ '%-.210s'·Î À̸§ º¯°æÁß ¿¡·¯ (¿¡·¯¹øÈ£: %d)"
- nor "Feil ved omdøping av '%-.210s' til '%-.210s' (Feilkode: %d)"
- norwegian-ny "Feil ved omdøyping av '%-.210s' til '%-.210s' (Feilkode: %d)"
- pol "B³?d podczas zmieniania nazwy '%-.210s' na '%-.210s' (Kod b³êdu: %d)"
- por "Erro ao renomear '%-.210s' para '%-.210s' (erro no. %d)"
- rum "Eroare incercind sa renumesc '%-.210s' in '%-.210s' (errno: %d)"
- rus "ïÛÉÂËÁ ÐÒÉ ÐÅÒÅÉÍÅÎÏ×ÁÎÉÉ '%-.210s' × '%-.210s' (ÏÛÉÂËÁ: %d)"
- serbian "Greška pri promeni imena '%-.210s' na '%-.210s' (errno: %d)"
- slo "Chyba pri premenovávaní '%-.210s' na '%-.210s' (chybový kód: %d)"
- spa "Error en el renombrado de '%-.210s' a '%-.210s' (Error: %d)"
- swe "Kan inte byta namn från '%-.210s' till '%-.210s' (Felkod: %d)"
- ukr "îÅ ÍÏÖÕ ÐÅÒÅÊÍÅÎÕ×ÁÔÉ '%-.210s' Õ '%-.210s' (ÐÏÍÉÌËÁ: %d)"
-ER_ERROR_ON_WRITE
- cze "Chyba p-Bøi zápisu do souboru '%-.200s' (chybový kód: %d)"
- dan "Fejl ved skriving av filen '%-.200s' (Fejlkode: %d)"
- nla "Fout bij het wegschrijven van file '%-.200s' (Errcode: %d)"
- eng "Error writing file '%-.200s' (errno: %d)"
- jps "'%-.200s' ƒtƒ@ƒCƒ‹‚ð‘‚­Ž–‚ª‚Å‚«‚Ü‚¹‚ñ (errno: %d)",
- est "Viga faili '%-.200s' kirjutamisel (veakood: %d)"
- fre "Erreur d'écriture du fichier '%-.200s' (Errcode: %d)"
- ger "Fehler beim Speichern der Datei '%-.200s' (Fehler: %d)"
- greek "Ðñüâëçìá êáôÜ ôçí áðïèÞêåõóç ôïõ áñ÷åßïõ '%-.200s' (êùäéêüò ëÜèïõò: %d)"
- hun "Hiba a '%-.200s' file irasakor. (hibakod: %d)"
- ita "Errore durante la scrittura del file '%-.200s' (errno: %d)"
- jpn "'%-.200s' ¥Õ¥¡¥¤¥ë¤ò½ñ¤¯»ö¤¬¤Ç¤­¤Þ¤»¤ó (errno: %d)"
- kor "'%-.200s'È­ÀÏ ±â·Ï Áß ¿¡·¯ (¿¡·¯¹øÈ£: %d)"
- nor "Feil ved skriving av fila '%-.200s' (Feilkode: %d)"
- norwegian-ny "Feil ved skriving av fila '%-.200s' (Feilkode: %d)"
- pol "B³?d podczas zapisywania pliku '%-.200s' (Kod b³êdu: %d)"
- por "Erro ao gravar arquivo '%-.200s' (erro no. %d)"
- rum "Eroare scriind fisierul '%-.200s' (errno: %d)"
- rus "ïÛÉÂËÁ ÚÁÐÉÓÉ × ÆÁÊÌ '%-.200s' (ÏÛÉÂËÁ: %d)"
- serbian "Greška pri upisu '%-.200s' (errno: %d)"
- slo "Chyba pri zápise do súboru '%-.200s' (chybový kód: %d)"
- spa "Error escribiendo el archivo '%-.200s' (Error: %d)"
- swe "Fick fel vid skrivning till '%-.200s' (Felkod %d)"
- ukr "îÅ ÍÏÖÕ ÚÁÐÉÓÁÔÉ ÆÁÊÌ '%-.200s' (ÐÏÍÉÌËÁ: %d)"
-ER_FILE_USED
- cze "'%-.192s' je zam-Bèen proti zmìnám"
- dan "'%-.192s' er låst mod opdateringer"
- nla "'%-.192s' is geblokeerd tegen veranderingen"
- eng "'%-.192s' is locked against change"
- jps "'%-.192s' ‚̓ƒbƒN‚³‚ê‚Ä‚¢‚Ü‚·",
- est "'%-.192s' on lukustatud muudatuste vastu"
- fre "'%-.192s' est verrouillé contre les modifications"
- ger "'%-.192s' ist für Änderungen gesperrt"
- greek "'%-.192s' äåí åðéôñÝðïíôáé áëëáãÝò"
- hun "'%-.192s' a valtoztatas ellen zarolva"
- ita "'%-.192s' e` soggetto a lock contro i cambiamenti"
- jpn "'%-.192s' ¤Ï¥í¥Ã¥¯¤µ¤ì¤Æ¤¤¤Þ¤¹"
- kor "'%-.192s'°¡ º¯°æÇÒ ¼ö ¾øµµ·Ï Àá°ÜÀÖÀ¾´Ï´Ù."
- nor "'%-.192s' er låst mot oppdateringer"
- norwegian-ny "'%-.192s' er låst mot oppdateringar"
- pol "'%-.192s' jest zablokowany na wypadek zmian"
- por "'%-.192s' está com travamento contra alterações"
- rum "'%-.192s' este blocat pentry schimbari (loccked against change)"
- rus "'%-.192s' ÚÁÂÌÏËÉÒÏ×ÁÎ ÄÌÑ ÉÚÍÅÎÅÎÉÊ"
- serbian "'%-.192s' je zakljuèan za upis"
- slo "'%-.192s' je zamknutý proti zmenám"
- spa "'%-.192s' esta bloqueado contra cambios"
- swe "'%-.192s' är låst mot användning"
- ukr "'%-.192s' ÚÁÂÌÏËÏ×ÁÎÉÊ ÎÁ ×ÎÅÓÅÎÎÑ ÚͦÎ"
-ER_FILSORT_ABORT
- cze "T-Bøídìní pøeru¹eno"
- dan "Sortering afbrudt"
- nla "Sorteren afgebroken"
- eng "Sort aborted"
- jps "Sort ’†’f",
- est "Sorteerimine katkestatud"
- fre "Tri alphabétique abandonné"
- ger "Sortiervorgang abgebrochen"
- greek "Ç äéáäéêáóßá ôáîéíüìéóçò áêõñþèçêå"
- hun "Sikertelen rendezes"
- ita "Operazione di ordinamento abbandonata"
- jpn "Sort ̾̂"
- kor "¼ÒÆ®°¡ ÁߴܵǾú½À´Ï´Ù."
- nor "Sortering avbrutt"
- norwegian-ny "Sortering avbrote"
- pol "Sortowanie przerwane"
- por "Ordenação abortada"
- rum "Sortare intrerupta"
- rus "óÏÒÔÉÒÏ×ËÁ ÐÒÅÒ×ÁÎÁ"
- serbian "Sortiranje je prekinuto"
- slo "Triedenie preru¹ené"
- spa "Ordeancion cancelada"
- swe "Sorteringen avbruten"
- ukr "óÏÒÔÕ×ÁÎÎÑ ÐÅÒÅÒ×ÁÎÏ"
-ER_FORM_NOT_FOUND
- cze "Pohled '%-.192s' pro '%-.192s' neexistuje"
- dan "View '%-.192s' eksisterer ikke for '%-.192s'"
- nla "View '%-.192s' bestaat niet voor '%-.192s'"
- eng "View '%-.192s' doesn't exist for '%-.192s'"
- jps "View '%-.192s' ‚ª '%-.192s' ‚É’è‹`‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ",
- est "Vaade '%-.192s' ei eksisteeri '%-.192s' jaoks"
- fre "La vue (View) '%-.192s' n'existe pas pour '%-.192s'"
- ger "View '%-.192s' existiert für '%-.192s' nicht"
- greek "Ôï View '%-.192s' äåí õðÜñ÷åé ãéá '%-.192s'"
- hun "A(z) '%-.192s' nezet nem letezik a(z) '%-.192s'-hoz"
- ita "La view '%-.192s' non esiste per '%-.192s'"
- jpn "View '%-.192s' ¤¬ '%-.192s' ¤ËÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó"
- kor "ºä '%-.192s'°¡ '%-.192s'¿¡¼­´Â Á¸ÀçÇÏÁö ¾ÊÀ¾´Ï´Ù."
- nor "View '%-.192s' eksisterer ikke for '%-.192s'"
- norwegian-ny "View '%-.192s' eksisterar ikkje for '%-.192s'"
- pol "Widok '%-.192s' nie istnieje dla '%-.192s'"
- por "Visão '%-.192s' não existe para '%-.192s'"
- rum "View '%-.192s' nu exista pentru '%-.192s'"
- rus "ðÒÅÄÓÔÁ×ÌÅÎÉÅ '%-.192s' ÎÅ ÓÕÝÅÓÔ×ÕÅÔ ÄÌÑ '%-.192s'"
- serbian "View '%-.192s' ne postoji za '%-.192s'"
- slo "Pohµad '%-.192s' neexistuje pre '%-.192s'"
- spa "La vista '%-.192s' no existe para '%-.192s'"
- swe "Formulär '%-.192s' finns inte i '%-.192s'"
- ukr "÷ÉÇÌÑÄ '%-.192s' ÎÅ ¦ÓÎÕ¤ ÄÌÑ '%-.192s'"
-ER_GET_ERRNO
- cze "Obsluha tabulky vr-Bátila chybu %d"
- dan "Modtog fejl %d fra tabel håndteringen"
- nla "Fout %d van tabel handler"
- eng "Got error %d from storage engine"
- est "Tabeli handler tagastas vea %d"
- fre "Reçu l'erreur %d du handler de la table"
- ger "Fehler %d (Speicher-Engine)"
- greek "ÅëÞöèç ìÞíõìá ëÜèïõò %d áðü ôïí ÷åéñéóôÞ ðßíáêá (table handler)"
- hun "%d hibajelzes a tablakezelotol"
- ita "Rilevato l'errore %d dal gestore delle tabelle"
- jpn "Got error %d from table handler"
- kor "Å×À̺í handler¿¡¼­ %d ¿¡·¯°¡ ¹ß»ý ÇÏ¿´½À´Ï´Ù."
- nor "Mottok feil %d fra tabell håndterer"
- norwegian-ny "Mottok feil %d fra tabell handterar"
- pol "Otrzymano b³?d %d z obs³ugi tabeli"
- por "Obteve erro %d no manipulador de tabelas"
- rum "Eroarea %d obtinuta din handlerul tabelei"
- rus "ðÏÌÕÞÅÎÁ ÏÛÉÂËÁ %d ÏÔ ÏÂÒÁÂÏÔÞÉËÁ ÔÁÂÌÉÃ"
- serbian "Handler tabela je vratio grešku %d"
- slo "Obsluha tabuµky vrátila chybu %d"
- spa "Error %d desde el manejador de la tabla"
- swe "Fick felkod %d från databashanteraren"
- ukr "ïÔÒÉÍÁÎÏ ÐÏÍÉÌËÕ %d ×¦Ä ÄÅÓËÒÉÐÔÏÒÁ ÔÁÂÌÉæ"
-ER_ILLEGAL_HA
- cze "Obsluha tabulky '%-.192s' nem-Bá tento parametr"
- dan "Denne mulighed eksisterer ikke for tabeltypen '%-.192s'"
- nla "Tabel handler voor '%-.192s' heeft deze optie niet"
- eng "Table storage engine for '%-.192s' doesn't have this option"
- est "Tabeli '%-.192s' handler ei toeta antud operatsiooni"
- fre "Le handler de la table '%-.192s' n'a pas cette option"
- ger "Diese Option gibt es nicht (Speicher-Engine für '%-.192s')"
- greek "Ï ÷åéñéóôÞò ðßíáêá (table handler) ãéá '%-.192s' äåí äéáèÝôåé áõôÞ ôçí åðéëïãÞ"
- hun "A(z) '%-.192s' tablakezelonek nincs ilyen opcioja"
- ita "Il gestore delle tabelle per '%-.192s' non ha questa opzione"
- jpn "Table handler for '%-.192s' doesn't have this option"
- kor "'%-.192s'ÀÇ Å×À̺í handler´Â ÀÌ·¯ÇÑ ¿É¼ÇÀ» Á¦°øÇÏÁö ¾ÊÀ¾´Ï´Ù."
- nor "Tabell håndtereren for '%-.192s' har ikke denne muligheten"
- norwegian-ny "Tabell håndteraren for '%-.192s' har ikkje denne moglegheita"
- pol "Obs³uga tabeli '%-.192s' nie posiada tej opcji"
- por "Manipulador de tabela para '%-.192s' não tem esta opção"
- rum "Handlerul tabelei pentru '%-.192s' nu are aceasta optiune"
- rus "ïÂÒÁÂÏÔÞÉË ÔÁÂÌÉÃÙ '%-.192s' ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÜÔÕ ×ÏÚÍÏÖÎÏÓÔØ"
- serbian "Handler tabela za '%-.192s' nema ovu opciju"
- slo "Obsluha tabuµky '%-.192s' nemá tento parameter"
- spa "El manejador de la tabla de '%-.192s' no tiene esta opcion"
- swe "Tabellhanteraren for tabell '%-.192s' stödjer ej detta"
- ukr "äÅÓËÒÉÐÔÏÒ ÔÁÂÌÉæ '%-.192s' ÎÅ ÍÁ¤ 椧 ×ÌÁÓÔÉ×ÏÓÔ¦"
-ER_KEY_NOT_FOUND
- cze "Nemohu naj-Bít záznam v '%-.192s'"
- dan "Kan ikke finde posten i '%-.192s'"
- nla "Kan record niet vinden in '%-.192s'"
- eng "Can't find record in '%-.192s'"
- jps "'%-.192s'‚Ì‚È‚©‚ɃŒƒR[ƒh‚ªŒ©•t‚©‚è‚Ü‚¹‚ñ",
- est "Ei suuda leida kirjet '%-.192s'-s"
- fre "Ne peut trouver l'enregistrement dans '%-.192s'"
- ger "Kann Datensatz in '%-.192s' nicht finden"
- greek "Áäýíáôç ç áíåýñåóç åããñáöÞò óôï '%-.192s'"
- hun "Nem talalhato a rekord '%-.192s'-ben"
- ita "Impossibile trovare il record in '%-.192s'"
- jpn "'%-.192s'¤Î¤Ê¤«¤Ë¥ì¥³¡¼¥É¤¬¸«ÉÕ¤«¤ê¤Þ¤»¤ó"
- kor "'%-.192s'¿¡¼­ ·¹Äڵ带 ãÀ» ¼ö ¾øÀ¾´Ï´Ù."
- nor "Kan ikke finne posten i '%-.192s'"
- norwegian-ny "Kan ikkje finne posten i '%-.192s'"
- pol "Nie mo¿na znale¥æ rekordu w '%-.192s'"
- por "Não pode encontrar registro em '%-.192s'"
- rum "Nu pot sa gasesc recordul in '%-.192s'"
- rus "îÅ×ÏÚÍÏÖÎÏ ÎÁÊÔÉ ÚÁÐÉÓØ × '%-.192s'"
- serbian "Ne mogu da pronaðem slog u '%-.192s'"
- slo "Nemô¾em nájs» záznam v '%-.192s'"
- spa "No puedo encontrar el registro en '%-.192s'"
- swe "Hittar inte posten '%-.192s'"
- ukr "îÅ ÍÏÖÕ ÚÁÐÉÓÁÔÉ Õ '%-.192s'"
-ER_NOT_FORM_FILE
- cze "Nespr-Bávná informace v souboru '%-.200s'"
- dan "Forkert indhold i: '%-.200s'"
- nla "Verkeerde info in file: '%-.200s'"
- eng "Incorrect information in file: '%-.200s'"
- jps "ƒtƒ@ƒCƒ‹ '%-.200s' ‚Ì info ‚ªŠÔˆá‚Á‚Ä‚¢‚é‚悤‚Å‚·",
- est "Vigane informatsioon failis '%-.200s'"
- fre "Information erronnée dans le fichier: '%-.200s'"
- ger "Falsche Information in Datei '%-.200s'"
- greek "ËÜèïò ðëçñïöïñßåò óôï áñ÷åßï: '%-.200s'"
- hun "Ervenytelen info a file-ban: '%-.200s'"
- ita "Informazione errata nel file: '%-.200s'"
- jpn "¥Õ¥¡¥¤¥ë '%-.200s' ¤Î info ¤¬´Ö°ã¤Ã¤Æ¤¤¤ë¤è¤¦¤Ç¤¹"
- kor "È­ÀÏÀÇ ºÎÁ¤È®ÇÑ Á¤º¸: '%-.200s'"
- nor "Feil informasjon i filen: '%-.200s'"
- norwegian-ny "Feil informasjon i fila: '%-.200s'"
- pol "Niew³a?ciwa informacja w pliku: '%-.200s'"
- por "Informação incorreta no arquivo '%-.200s'"
- rum "Informatie incorecta in fisierul: '%-.200s'"
- rus "îÅËÏÒÒÅËÔÎÁÑ ÉÎÆÏÒÍÁÃÉÑ × ÆÁÊÌÅ '%-.200s'"
- serbian "Pogrešna informacija u file-u: '%-.200s'"
- slo "Nesprávna informácia v súbore: '%-.200s'"
- spa "Informacion erronea en el archivo: '%-.200s'"
- swe "Felaktig fil: '%-.200s'"
- ukr "èÉÂÎÁ ¦ÎÆÏÒÍÁÃ¦Ñ Õ ÆÁÊ̦: '%-.200s'"
-ER_NOT_KEYFILE
- cze "Nespr-Bávný klíè pro tabulku '%-.200s'; pokuste se ho opravit"
- dan "Fejl i indeksfilen til tabellen '%-.200s'; prøv at reparere den"
- nla "Verkeerde zoeksleutel file voor tabel: '%-.200s'; probeer het te repareren"
- eng "Incorrect key file for table '%-.200s'; try to repair it"
- jps "'%-.200s' ƒe[ƒuƒ‹‚Ì key file ‚ªŠÔˆá‚Á‚Ä‚¢‚é‚悤‚Å‚·. C•œ‚ð‚µ‚Ä‚­‚¾‚³‚¢",
- est "Tabeli '%-.200s' võtmefail on vigane; proovi seda parandada"
- fre "Index corrompu dans la table: '%-.200s'; essayez de le réparer"
- ger "Fehlerhafte Index-Datei für Tabelle '%-.200s'; versuche zu reparieren"
- greek "ËÜèïò áñ÷åßï ôáîéíüìéóçò (key file) ãéá ôïí ðßíáêá: '%-.200s'; Ðáñáêáëþ, äéïñèþóôå ôï!"
- hun "Ervenytelen kulcsfile a tablahoz: '%-.200s'; probalja kijavitani!"
- ita "File chiave errato per la tabella : '%-.200s'; prova a riparalo"
- jpn "'%-.200s' ¥Æ¡¼¥Ö¥ë¤Î key file ¤¬´Ö°ã¤Ã¤Æ¤¤¤ë¤è¤¦¤Ç¤¹. ½¤Éü¤ò¤·¤Æ¤¯¤À¤µ¤¤"
- kor "'%-.200s' Å×À̺íÀÇ ºÎÁ¤È®ÇÑ Å° Á¸Àç. ¼öÁ¤ÇϽÿÀ!"
- nor "Tabellen '%-.200s' har feil i nøkkelfilen; forsøk å reparer den"
- norwegian-ny "Tabellen '%-.200s' har feil i nykkelfila; prøv å reparere den"
- pol "Niew³a?ciwy plik kluczy dla tabeli: '%-.200s'; spróbuj go naprawiæ"
- por "Arquivo de índice incorreto para tabela '%-.200s'; tente repará-lo"
- rum "Cheia fisierului incorecta pentru tabela: '%-.200s'; incearca s-o repari"
- rus "îÅËÏÒÒÅËÔÎÙÊ ÉÎÄÅËÓÎÙÊ ÆÁÊÌ ÄÌÑ ÔÁÂÌÉÃÙ: '%-.200s'. ðÏÐÒÏÂÕÊÔÅ ×ÏÓÓÔÁÎÏ×ÉÔØ ÅÇÏ"
- serbian "Pogrešan key file za tabelu: '%-.200s'; probajte da ga ispravite"
- slo "Nesprávny kµúè pre tabuµku '%-.200s'; pokúste sa ho opravi»"
- spa "Clave de archivo erronea para la tabla: '%-.200s'; intente repararlo"
- swe "Fatalt fel vid hantering av register '%-.200s'; kör en reparation"
- ukr "èÉÂÎÉÊ ÆÁÊÌ ËÌÀÞÅÊ ÄÌÑ ÔÁÂÌÉæ: '%-.200s'; óÐÒÏÂÕÊÔÅ ÊÏÇÏ ×¦ÄÎÏ×ÉÔÉ"
-ER_OLD_KEYFILE
- cze "Star-Bý klíèový soubor pro '%-.192s'; opravte ho."
- dan "Gammel indeksfil for tabellen '%-.192s'; reparer den"
- nla "Oude zoeksleutel file voor tabel '%-.192s'; repareer het!"
- eng "Old key file for table '%-.192s'; repair it!"
- jps "'%-.192s' ƒe[ƒuƒ‹‚͌¢Œ`Ž®‚Ì key file ‚̂悤‚Å‚·; C•œ‚ð‚µ‚Ä‚­‚¾‚³‚¢",
- est "Tabeli '%-.192s' võtmefail on aegunud; paranda see!"
- fre "Vieux fichier d'index pour la table '%-.192s'; réparez le!"
- ger "Alte Index-Datei für Tabelle '%-.192s'. Bitte reparieren"
- greek "Ðáëáéü áñ÷åßï ôáîéíüìéóçò (key file) ãéá ôïí ðßíáêá '%-.192s'; Ðáñáêáëþ, äéïñèþóôå ôï!"
- hun "Regi kulcsfile a '%-.192s'tablahoz; probalja kijavitani!"
- ita "File chiave vecchio per la tabella '%-.192s'; riparalo!"
- jpn "'%-.192s' ¥Æ¡¼¥Ö¥ë¤Ï¸Å¤¤·Á¼°¤Î key file ¤Î¤è¤¦¤Ç¤¹; ½¤Éü¤ò¤·¤Æ¤¯¤À¤µ¤¤"
- kor "'%-.192s' Å×À̺íÀÇ ÀÌÀü¹öÁ¯ÀÇ Å° Á¸Àç. ¼öÁ¤ÇϽÿÀ!"
- nor "Gammel nøkkelfil for tabellen '%-.192s'; reparer den!"
- norwegian-ny "Gammel nykkelfil for tabellen '%-.192s'; reparer den!"
- pol "Plik kluczy dla tabeli '%-.192s' jest starego typu; napraw go!"
- por "Arquivo de índice desatualizado para tabela '%-.192s'; repare-o!"
- rum "Cheia fisierului e veche pentru tabela '%-.192s'; repar-o!"
- rus "óÔÁÒÙÊ ÉÎÄÅËÓÎÙÊ ÆÁÊÌ ÄÌÑ ÔÁÂÌÉÃÙ '%-.192s'; ÏÔÒÅÍÏÎÔÉÒÕÊÔÅ ÅÇÏ!"
- serbian "Zastareo key file za tabelu '%-.192s'; ispravite ga"
- slo "Starý kµúèový súbor pre '%-.192s'; opravte ho!"
- spa "Clave de archivo antigua para la tabla '%-.192s'; reparelo!"
- swe "Gammal nyckelfil '%-.192s'; reparera registret"
- ukr "óÔÁÒÉÊ ÆÁÊÌ ËÌÀÞÅÊ ÄÌÑ ÔÁÂÌÉæ '%-.192s'; ÷¦ÄÎÏצÔØ ÊÏÇÏ!"
-ER_OPEN_AS_READONLY
- cze "'%-.192s' je jen pro -Bètení"
- dan "'%-.192s' er skrivebeskyttet"
- nla "'%-.192s' is alleen leesbaar"
- eng "Table '%-.192s' is read only"
- jps "'%-.192s' ‚Í“Ç‚Ýž‚Ýê—p‚Å‚·",
- est "Tabel '%-.192s' on ainult lugemiseks"
- fre "'%-.192s' est en lecture seulement"
- ger "Tabelle '%-.192s' ist nur lesbar"
- greek "'%-.192s' åðéôñÝðåôáé ìüíï ç áíÜãíùóç"
- hun "'%-.192s' irasvedett"
- ita "'%-.192s' e` di sola lettura"
- jpn "'%-.192s' ¤ÏÆɤ߹þ¤ßÀìÍѤǤ¹"
- kor "Å×À̺í '%-.192s'´Â ÀбâÀü¿ë ÀÔ´Ï´Ù."
- nor "'%-.192s' er skrivebeskyttet"
- norwegian-ny "'%-.192s' er skrivetryggja"
- pol "'%-.192s' jest tylko do odczytu"
- por "Tabela '%-.192s' é somente para leitura"
- rum "Tabela '%-.192s' e read-only"
- rus "ôÁÂÌÉÃÁ '%-.192s' ÐÒÅÄÎÁÚÎÁÞÅÎÁ ÔÏÌØËÏ ÄÌÑ ÞÔÅÎÉÑ"
- serbian "Tabelu '%-.192s' je dozvoljeno samo èitati"
- slo "'%-.192s' is èíta» only"
- spa "'%-.192s' es de solo lectura"
- swe "'%-.192s' är skyddad mot förändring"
- ukr "ôÁÂÌÉÃÑ '%-.192s' Ô¦ÌØËÉ ÄÌÑ ÞÉÔÁÎÎÑ"
-ER_OUTOFMEMORY HY001 S1001
- cze "M-Bálo pamìti. Pøestartujte daemona a zkuste znovu (je potøeba %d bytù)"
- dan "Ikke mere hukommelse. Genstart serveren og prøv igen (mangler %d bytes)"
- nla "Geen geheugen meer. Herstart server en probeer opnieuw (%d bytes nodig)"
- eng "Out of memory; restart server and try again (needed %d bytes)"
- jps "Out of memory. ƒf[ƒ‚ƒ“‚ðƒŠƒXƒ^[ƒg‚µ‚Ä‚Ý‚Ä‚­‚¾‚³‚¢ (%d bytes •K—v)",
- est "Mälu sai otsa. Proovi MySQL uuesti käivitada (puudu jäi %d baiti)"
- fre "Manque de mémoire. Redémarrez le démon et ré-essayez (%d octets nécessaires)"
- ger "Kein Speicher vorhanden (%d Bytes benötigt). Bitte Server neu starten"
- greek "Äåí õðÜñ÷åé äéáèÝóéìç ìíÞìç. ÐñïóðáèÞóôå ðÜëé, åðáíåêéíþíôáò ôç äéáäéêáóßá (demon) (÷ñåéÜæïíôáé %d bytes)"
- hun "Nincs eleg memoria. Inditsa ujra a demont, es probalja ismet. (%d byte szukseges.)"
- ita "Memoria esaurita. Fai ripartire il demone e riprova (richiesti %d bytes)"
- jpn "Out of memory. ¥Ç¡¼¥â¥ó¤ò¥ê¥¹¥¿¡¼¥È¤·¤Æ¤ß¤Æ¤¯¤À¤µ¤¤ (%d bytes ɬÍ×)"
- kor "Out of memory. µ¥¸óÀ» Àç ½ÇÇà ÈÄ ´Ù½Ã ½ÃÀÛÇϽÿÀ (needed %d bytes)"
- nor "Ikke mer minne. Star på nytt tjenesten og prøv igjen (trengte %d byter)"
- norwegian-ny "Ikkje meir minne. Start på nytt tenesten og prøv igjen (trengte %d bytar)"
- pol "Zbyt ma³o pamiêci. Uruchom ponownie demona i spróbuj ponownie (potrzeba %d bajtów)"
- por "Sem memória. Reinicie o programa e tente novamente (necessita de %d bytes)"
- rum "Out of memory. Porneste daemon-ul din nou si incearca inca o data (e nevoie de %d bytes)"
- rus "îÅÄÏÓÔÁÔÏÞÎÏ ÐÁÍÑÔÉ. ðÅÒÅÚÁÐÕÓÔÉÔÅ ÓÅÒ×ÅÒ É ÐÏÐÒÏÂÕÊÔÅ ÅÝÅ ÒÁÚ (ÎÕÖÎÏ %d ÂÁÊÔ)"
- serbian "Nema memorije. Restartujte MySQL server i probajte ponovo (potrebno je %d byte-ova)"
- slo "Málo pamäti. Re¹tartujte daemona a skúste znova (je potrebných %d bytov)"
- spa "Memoria insuficiente. Reinicie el demonio e intentelo otra vez (necesita %d bytes)"
- swe "Oväntat slut på minnet, starta om programmet och försök på nytt (Behövde %d bytes)"
- ukr "âÒÁË ÐÁÍ'ÑÔ¦. òÅÓÔÁÒÔÕÊÔÅ ÓÅÒ×ÅÒ ÔÁ ÓÐÒÏÂÕÊÔÅ ÚÎÏ×Õ (ÐÏÔÒ¦ÂÎÏ %d ÂÁÊÔ¦×)"
-ER_OUT_OF_SORTMEMORY HY001 S1001
- cze "M-Bálo pamìti pro tøídìní. Zvy¹te velikost tøídícího bufferu"
- dan "Ikke mere sorteringshukommelse. Øg sorteringshukommelse (sort buffer size) for serveren"
- nla "Geen geheugen om te sorteren. Verhoog de server sort buffer size"
- eng "Out of sort memory; increase server sort buffer size"
- jps "Out of sort memory. sort buffer size ‚ª‘«‚è‚È‚¢‚悤‚Å‚·.",
- est "Mälu sai sorteerimisel otsa. Suurenda MySQL-i sorteerimispuhvrit"
- fre "Manque de mémoire pour le tri. Augmentez-la."
- ger "Kein Speicher zum Sortieren vorhanden. sort_buffer_size sollte im Server erhöht werden"
- greek "Äåí õðÜñ÷åé äéáèÝóéìç ìíÞìç ãéá ôáîéíüìéóç. ÁõîÞóôå ôï sort buffer size ãéá ôç äéáäéêáóßá (demon)"
- hun "Nincs eleg memoria a rendezeshez. Novelje a rendezo demon puffermeretet"
- ita "Memoria per gli ordinamenti esaurita. Incrementare il 'sort_buffer' al demone"
- jpn "Out of sort memory. sort buffer size ¤¬Â­¤ê¤Ê¤¤¤è¤¦¤Ç¤¹."
- kor "Out of sort memory. daemon sort bufferÀÇ Å©±â¸¦ Áõ°¡½ÃÅ°¼¼¿ä"
- nor "Ikke mer sorteringsminne. Øk sorteringsminnet (sort buffer size) for tjenesten"
- norwegian-ny "Ikkje meir sorteringsminne. Auk sorteringsminnet (sorteringsbffer storleik) for tenesten"
- pol "Zbyt ma³o pamiêci dla sortowania. Zwiêksz wielko?æ bufora demona dla sortowania"
- por "Sem memória para ordenação. Aumente tamanho do 'buffer' de ordenação"
- rum "Out of memory pentru sortare. Largeste marimea buffer-ului pentru sortare in daemon (sort buffer size)"
- rus "îÅÄÏÓÔÁÔÏÞÎÏ ÐÁÍÑÔÉ ÄÌÑ ÓÏÒÔÉÒÏ×ËÉ. õ×ÅÌÉÞØÔÅ ÒÁÚÍÅÒ ÂÕÆÅÒÁ ÓÏÒÔÉÒÏ×ËÉ ÎÁ ÓÅÒ×ÅÒÅ"
- serbian "Nema memorije za sortiranje. Poveæajte velièinu sort buffer-a MySQL server-u"
- slo "Málo pamäti pre triedenie, zvý¹te veµkos» triediaceho bufferu"
- spa "Memoria de ordenacion insuficiente. Incremente el tamano del buffer de ordenacion"
- swe "Sorteringsbufferten räcker inte till. Kontrollera startparametrarna"
- ukr "âÒÁË ÐÁÍ'ÑÔ¦ ÄÌÑ ÓÏÒÔÕ×ÁÎÎÑ. ôÒÅÂÁ Ú¦ÌØÛÉÔÉ ÒÏÚÍ¦Ò ÂÕÆÅÒÁ ÓÏÒÔÕ×ÁÎÎÑ Õ ÓÅÒ×ÅÒÁ"
-ER_UNEXPECTED_EOF
- cze "Neo-Bèekávaný konec souboru pøi ètení '%-.192s' (chybový kód: %d)"
- dan "Uventet afslutning på fil (eof) ved læsning af filen '%-.192s' (Fejlkode: %d)"
- nla "Onverwachte eof gevonden tijdens het lezen van file '%-.192s' (Errcode: %d)"
- eng "Unexpected EOF found when reading file '%-.192s' (errno: %d)"
- jps "'%-.192s' ƒtƒ@ƒCƒ‹‚ð“Ç‚Ýž‚Ý’†‚É EOF ‚ª—\Šú‚¹‚ÊŠ‚ÅŒ»‚ê‚Ü‚µ‚½. (errno: %d)",
- est "Ootamatu faililõpumärgend faili '%-.192s' lugemisel (veakood: %d)"
- fre "Fin de fichier inattendue en lisant '%-.192s' (Errcode: %d)"
- ger "Unerwartetes Ende beim Lesen der Datei '%-.192s' (Fehler: %d)"
- greek "ÊáôÜ ôç äéÜñêåéá ôçò áíÜãíùóçò, âñÝèçêå áðñïóäüêçôá ôï ôÝëïò ôïõ áñ÷åßïõ '%-.192s' (êùäéêüò ëÜèïõò: %d)"
- hun "Varatlan filevege-jel a '%-.192s'olvasasakor. (hibakod: %d)"
- ita "Fine del file inaspettata durante la lettura del file '%-.192s' (errno: %d)"
- jpn "'%-.192s' ¥Õ¥¡¥¤¥ë¤òÆɤ߹þ¤ßÃæ¤Ë EOF ¤¬Í½´ü¤»¤Ì½ê¤Ç¸½¤ì¤Þ¤·¤¿. (errno: %d)"
- kor "'%-.192s' È­ÀÏÀ» Àд µµÁß À߸øµÈ eofÀ» ¹ß°ß (¿¡·¯¹øÈ£: %d)"
- nor "Uventet slutt på fil (eof) ved lesing av filen '%-.192s' (Feilkode: %d)"
- norwegian-ny "Uventa slutt på fil (eof) ved lesing av fila '%-.192s' (Feilkode: %d)"
- pol "Nieoczekiwany 'eof' napotkany podczas czytania z pliku '%-.192s' (Kod b³êdu: %d)"
- por "Encontrado fim de arquivo inesperado ao ler arquivo '%-.192s' (erro no. %d)"
- rum "Sfirsit de fisier neasteptat in citirea fisierului '%-.192s' (errno: %d)"
- rus "îÅÏÖÉÄÁÎÎÙÊ ËÏÎÅà ÆÁÊÌÁ '%-.192s' (ÏÛÉÂËÁ: %d)"
- serbian "Neoèekivani kraj pri èitanju file-a '%-.192s' (errno: %d)"
- slo "Neoèakávaný koniec súboru pri èítaní '%-.192s' (chybový kód: %d)"
- spa "Inesperado fin de ficheroU mientras leiamos el archivo '%-.192s' (Error: %d)"
- swe "Oväntat filslut vid läsning från '%-.192s' (Felkod: %d)"
- ukr "èÉÂÎÉÊ Ë¦ÎÅÃØ ÆÁÊÌÕ '%-.192s' (ÐÏÍÉÌËÁ: %d)"
-ER_CON_COUNT_ERROR 08004
- cze "P-Bøíli¹ mnoho spojení"
- dan "For mange forbindelser (connections)"
- nla "Te veel verbindingen"
- eng "Too many connections"
- jps "Ú‘±‚ª‘½‚·‚¬‚Ü‚·",
- est "Liiga palju samaaegseid ühendusi"
- fre "Trop de connexions"
- ger "Zu viele Verbindungen"
- greek "ÕðÜñ÷ïõí ðïëëÝò óõíäÝóåéò..."
- hun "Tul sok kapcsolat"
- ita "Troppe connessioni"
- jpn "Àܳ¤¬Â¿¤¹¤®¤Þ¤¹"
- kor "³Ê¹« ¸¹Àº ¿¬°á... max_connectionÀ» Áõ°¡ ½ÃÅ°½Ã¿À..."
- nor "For mange tilkoblinger (connections)"
- norwegian-ny "For mange tilkoplingar (connections)"
- pol "Zbyt wiele po³?czeñ"
- por "Excesso de conexões"
- rum "Prea multe conectiuni"
- rus "óÌÉÛËÏÍ ÍÎÏÇÏ ÓÏÅÄÉÎÅÎÉÊ"
- serbian "Previše konekcija"
- slo "Príli¹ mnoho spojení"
- spa "Demasiadas conexiones"
- swe "För många anslutningar"
- ukr "úÁÂÁÇÁÔÏ Ú'¤ÄÎÁÎØ"
-ER_OUT_OF_RESOURCES
- cze "M-Bálo prostoru/pamìti pro thread"
- dan "Udgået for tråde/hukommelse"
- nla "Geen thread geheugen meer; controleer of mysqld of andere processen al het beschikbare geheugen gebruikt. Zo niet, dan moet u wellicht 'ulimit' gebruiken om mysqld toe te laten meer geheugen te benutten, of u kunt extra swap ruimte toevoegen"
- eng "Out of memory; check if mysqld or some other process uses all available memory; if not, you may have to use 'ulimit' to allow mysqld to use more memory or you can add more swap space"
- jps "Out of memory; mysqld ‚©‚»‚Ì‘¼‚̃vƒƒZƒX‚ªƒƒ‚ƒŠ[‚ð‘S‚ÄŽg‚Á‚Ä‚¢‚é‚©Šm”F‚µ‚Ä‚­‚¾‚³‚¢. ƒƒ‚ƒŠ[‚ðŽg‚¢Ø‚Á‚Ä‚¢‚È‚¢ê‡A'ulimit' ‚ðݒ肵‚Ä mysqld ‚̃ƒ‚ƒŠ[Žg—pŒÀŠE—ʂ𑽂­‚·‚é‚©Aswap space ‚ð‘‚₵‚Ä‚Ý‚Ä‚­‚¾‚³‚¢",
- est "Mälu sai otsa. Võimalik, et aitab swap-i lisamine või käsu 'ulimit' abil MySQL-le rohkema mälu kasutamise lubamine"
- fre "Manque de 'threads'/mémoire"
- ger "Kein Speicher mehr vorhanden. Prüfen Sie, ob mysqld oder ein anderer Prozess den gesamten Speicher verbraucht. Wenn nicht, sollten Sie mit 'ulimit' dafür sorgen, dass mysqld mehr Speicher benutzen darf, oder mehr Swap-Speicher einrichten"
- greek "Ðñüâëçìá ìå ôç äéáèÝóéìç ìíÞìç (Out of thread space/memory)"
- hun "Elfogyott a thread-memoria"
- ita "Fine dello spazio/memoria per i thread"
- jpn "Out of memory; mysqld ¤«¤½¤Î¾¤Î¥×¥í¥»¥¹¤¬¥á¥â¥ê¡¼¤òÁ´¤Æ»È¤Ã¤Æ¤¤¤ë¤«³Îǧ¤·¤Æ¤¯¤À¤µ¤¤. ¥á¥â¥ê¡¼¤ò»È¤¤ÀڤäƤ¤¤Ê¤¤¾ì¹ç¡¢'ulimit' ¤òÀßÄꤷ¤Æ mysqld ¤Î¥á¥â¥ê¡¼»ÈÍѸ³¦Î̤ò¿¤¯¤¹¤ë¤«¡¢swap space ¤òÁý¤ä¤·¤Æ¤ß¤Æ¤¯¤À¤µ¤¤"
- kor "Out of memory; mysqld³ª ¶Ç´Ù¸¥ ÇÁ·Î¼¼¼­¿¡¼­ »ç¿ë°¡´ÉÇÑ ¸Þ¸ð¸®¸¦ »ç¿ëÇÑÁö äũÇϽÿÀ. ¸¸¾à ±×·¸Áö ¾Ê´Ù¸é ulimit ¸í·ÉÀ» ÀÌ¿¿ëÇÏ¿© ´õ¸¹Àº ¸Þ¸ð¸®¸¦ »ç¿ëÇÒ ¼ö ÀÖµµ·Ï Çϰųª ½º¿Ò ½ºÆÐÀ̽º¸¦ Áõ°¡½ÃÅ°½Ã¿À"
- 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 MySQL 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)"
- 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 ×ÉËÏÒÉÓÔÏ×Õ×ÁÔÉ Â¦ÌØÛÅ ÐÁÍ'ÑÔ¦ ÁÂÏ ×É ÍÏÖÅÔÅ ÄÏÄÁÔÉ Â¦ÌØÛŠͦÓÃÑ Ð¦Ä Ó×ÁÐ"
-ER_BAD_HOST_ERROR 08S01
- cze "Nemohu zjistit jm-Béno stroje pro Va¹i adresu"
- dan "Kan ikke få værtsnavn for din adresse"
- nla "Kan de hostname niet krijgen van uw adres"
- eng "Can't get hostname for your address"
- jps "‚»‚Ì address ‚Ì hostname ‚ªˆø‚¯‚Ü‚¹‚ñ.",
- est "Ei suuda lahendada IP aadressi masina nimeks"
- fre "Ne peut obtenir de hostname pour votre adresse"
- ger "Kann Hostnamen für diese Adresse nicht erhalten"
- greek "Äåí Ýãéíå ãíùóôü ôï hostname ãéá ôçí address óáò"
- hun "A gepnev nem allapithato meg a cimbol"
- ita "Impossibile risalire al nome dell'host dall'indirizzo (risoluzione inversa)"
- jpn "¤½¤Î address ¤Î hostname ¤¬°ú¤±¤Þ¤»¤ó."
- kor "´ç½ÅÀÇ ÄÄÇ»ÅÍÀÇ È£½ºÆ®À̸§À» ¾òÀ» ¼ö ¾øÀ¾´Ï´Ù."
- nor "Kan ikke få tak i vertsnavn for din adresse"
- norwegian-ny "Kan ikkje få tak i vertsnavn for di adresse"
- pol "Nie mo¿na otrzymaæ nazwy hosta dla twojego adresu"
- por "Não pode obter nome do 'host' para seu endereço"
- rum "Nu pot sa obtin hostname-ul adresei tale"
- rus "îÅ×ÏÚÍÏÖÎÏ ÐÏÌÕÞÉÔØ ÉÍÑ ÈÏÓÔÁ ÄÌÑ ×ÁÛÅÇÏ ÁÄÒÅÓÁ"
- serbian "Ne mogu da dobijem ime host-a za vašu IP adresu"
- slo "Nemô¾em zisti» meno hostiteµa pre va¹u adresu"
- spa "No puedo obtener el nombre de maquina de tu direccion"
- swe "Kan inte hitta 'hostname' för din adress"
- ukr "îÅ ÍÏÖÕ ×ÉÚÎÁÞÉÔÉ ¦Í'Ñ ÈÏÓÔÕ ÄÌÑ ×ÁÛϧ ÁÄÒÅÓÉ"
-ER_HANDSHAKE_ERROR 08S01
- cze "Chyba p-Bøi ustavování spojení"
- dan "Forkert håndtryk (handshake)"
- nla "Verkeerde handshake"
- eng "Bad handshake"
- est "Väär handshake"
- fre "Mauvais 'handshake'"
- ger "Ungültiger Handshake"
- greek "Ç áíáãíþñéóç (handshake) äåí Ýãéíå óùóôÜ"
- hun "A kapcsolatfelvetel nem sikerult (Bad handshake)"
- ita "Negoziazione impossibile"
- nor "Feil håndtrykk (handshake)"
- norwegian-ny "Feil handtrykk (handshake)"
- pol "Z³y uchwyt(handshake)"
- por "Negociação de acesso falhou"
- rum "Prost inceput de conectie (bad handshake)"
- rus "îÅËÏÒÒÅËÔÎÏÅ ÐÒÉ×ÅÔÓÔ×ÉÅ"
- serbian "Loš poèetak komunikacije (handshake)"
- slo "Chyba pri nadväzovaní spojenia"
- spa "Protocolo erroneo"
- swe "Fel vid initiering av kommunikationen med klienten"
- ukr "îÅצÒÎÁ ÕÓÔÁÎÏ×ËÁ Ú×'ÑÚËÕ"
-ER_DBACCESS_DENIED_ERROR 42000
- cze "P-Bøístup pro u¾ivatele '%-.48s'@'%-.64s' k databázi '%-.192s' není povolen"
- dan "Adgang nægtet bruger: '%-.48s'@'%-.64s' til databasen '%-.192s'"
- nla "Toegang geweigerd voor gebruiker: '%-.48s'@'%-.64s' naar database '%-.192s'"
- eng "Access denied for user '%-.48s'@'%-.64s' to database '%-.192s'"
- jps "ƒ†[ƒU[ '%-.48s'@'%-.64s' ‚Ì '%-.192s' ƒf[ƒ^ƒx[ƒX‚ւ̃AƒNƒZƒX‚ð‹‘”Û‚µ‚Ü‚·",
- est "Ligipääs keelatud kasutajale '%-.48s'@'%-.64s' andmebaasile '%-.192s'"
- fre "Accès refusé pour l'utilisateur: '%-.48s'@'@%-.64s'. Base '%-.192s'"
- ger "Benutzer '%-.48s'@'%-.64s' hat keine Zugriffsberechtigung für Datenbank '%-.192s'"
- greek "Äåí åðéôÝñåôáé ç ðñüóâáóç óôï ÷ñÞóôç: '%-.48s'@'%-.64s' óôç âÜóç äåäïìÝíùí '%-.192s'"
- hun "A(z) '%-.48s'@'%-.64s' felhasznalo szamara tiltott eleres az '%-.192s' adabazishoz."
- ita "Accesso non consentito per l'utente: '%-.48s'@'%-.64s' al database '%-.192s'"
- jpn "¥æ¡¼¥¶¡¼ '%-.48s'@'%-.64s' ¤Î '%-.192s' ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ø¤Î¥¢¥¯¥»¥¹¤òµñÈݤ·¤Þ¤¹"
- kor "'%-.48s'@'%-.64s' »ç¿ëÀÚ´Â '%-.192s' µ¥ÀÌŸº£À̽º¿¡ Á¢±ÙÀÌ °ÅºÎ µÇ¾ú½À´Ï´Ù."
- nor "Tilgang nektet for bruker: '%-.48s'@'%-.64s' til databasen '%-.192s' nektet"
- norwegian-ny "Tilgang ikkje tillate for brukar: '%-.48s'@'%-.64s' til databasen '%-.192s' nekta"
- por "Acesso negado para o usuário '%-.48s'@'%-.64s' ao banco de dados '%-.192s'"
- rum "Acces interzis pentru utilizatorul: '%-.48s'@'%-.64s' la baza de date '%-.192s'"
- rus "äÌÑ ÐÏÌØÚÏ×ÁÔÅÌÑ '%-.48s'@'%-.64s' ÄÏÓÔÕÐ Ë ÂÁÚÅ ÄÁÎÎÙÈ '%-.192s' ÚÁËÒÙÔ"
- serbian "Pristup je zabranjen korisniku '%-.48s'@'%-.64s' za bazu '%-.192s'"
- slo "Zakázaný prístup pre u¾ívateµa: '%-.48s'@'%-.64s' k databázi '%-.192s'"
- spa "Acceso negado para usuario: '%-.48s'@'%-.64s' para la base de datos '%-.192s'"
- swe "Användare '%-.48s'@'%-.64s' är ej berättigad att använda databasen %-.192s"
- ukr "äÏÓÔÕÐ ÚÁÂÏÒÏÎÅÎÏ ÄÌÑ ËÏÒÉÓÔÕ×ÁÞÁ: '%-.48s'@'%-.64s' ÄÏ ÂÁÚÉ ÄÁÎÎÉÈ '%-.192s'"
-ER_ACCESS_DENIED_ERROR 28000
- cze "P-Bøístup pro u¾ivatele '%-.48s'@'%-.64s' (s heslem %s)"
- dan "Adgang nægtet bruger: '%-.48s'@'%-.64s' (Bruger adgangskode: %s)"
- nla "Toegang geweigerd voor gebruiker: '%-.48s'@'%-.64s' (Wachtwoord gebruikt: %s)"
- eng "Access denied for user '%-.48s'@'%-.64s' (using password: %s)"
- jps "ƒ†[ƒU[ '%-.48s'@'%-.64s' ‚ð‹‘”Û‚µ‚Ü‚·.uUsing password: %s)",
- est "Ligipääs keelatud kasutajale '%-.48s'@'%-.64s' (kasutab parooli: %s)"
- fre "Accès refusé pour l'utilisateur: '%-.48s'@'@%-.64s' (mot de passe: %s)"
- ger "Benutzer '%-.48s'@'%-.64s' hat keine Zugriffsberechtigung (verwendetes Passwort: %s)"
- greek "Äåí åðéôÝñåôáé ç ðñüóâáóç óôï ÷ñÞóôç: '%-.48s'@'%-.64s' (÷ñÞóç password: %s)"
- hun "A(z) '%-.48s'@'%-.64s' felhasznalo szamara tiltott eleres. (Hasznalja a jelszot: %s)"
- ita "Accesso non consentito per l'utente: '%-.48s'@'%-.64s' (Password: %s)"
- jpn "¥æ¡¼¥¶¡¼ '%-.48s'@'%-.64s' ¤òµñÈݤ·¤Þ¤¹.uUsing password: %s)"
- kor "'%-.48s'@'%-.64s' »ç¿ëÀÚ´Â Á¢±ÙÀÌ °ÅºÎ µÇ¾ú½À´Ï´Ù. (using password: %s)"
- nor "Tilgang nektet for bruker: '%-.48s'@'%-.64s' (Bruker passord: %s)"
- norwegian-ny "Tilgang ikke tillate for brukar: '%-.48s'@'%-.64s' (Brukar passord: %s)"
- por "Acesso negado para o usuário '%-.48s'@'%-.64s' (senha usada: %s)"
- rum "Acces interzis pentru utilizatorul: '%-.48s'@'%-.64s' (Folosind parola: %s)"
- rus "äÏÓÔÕÐ ÚÁËÒÙÔ ÄÌÑ ÐÏÌØÚÏ×ÁÔÅÌÑ '%-.48s'@'%-.64s' (ÂÙÌ ÉÓÐÏÌØÚÏ×ÁÎ ÐÁÒÏÌØ: %s)"
- serbian "Pristup je zabranjen korisniku '%-.48s'@'%-.64s' (koristi lozinku: '%s')"
- slo "Zakázaný prístup pre u¾ívateµa: '%-.48s'@'%-.64s' (pou¾itie hesla: %s)"
- spa "Acceso negado para usuario: '%-.48s'@'%-.64s' (Usando clave: %s)"
- swe "Användare '%-.48s'@'%-.64s' är ej berättigad att logga in (Använder lösen: %s)"
- ukr "äÏÓÔÕÐ ÚÁÂÏÒÏÎÅÎÏ ÄÌÑ ËÏÒÉÓÔÕ×ÁÞÁ: '%-.48s'@'%-.64s' (÷ÉËÏÒÉÓÔÁÎÏ ÐÁÒÏÌØ: %s)"
-ER_NO_DB_ERROR 3D000
- cze "Nebyla vybr-Bána ¾ádná databáze"
- dan "Ingen database valgt"
- nla "Geen database geselecteerd"
- eng "No database selected"
- jps "ƒf[ƒ^ƒx[ƒX‚ª‘I‘ð‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ.",
- est "Andmebaasi ei ole valitud"
- fre "Aucune base n'a été sélectionnée"
- ger "Keine Datenbank ausgewählt"
- greek "Äåí åðéëÝ÷èçêå âÜóç äåäïìÝíùí"
- hun "Nincs kivalasztott adatbazis"
- ita "Nessun database selezionato"
- jpn "¥Ç¡¼¥¿¥Ù¡¼¥¹¤¬ÁªÂò¤µ¤ì¤Æ¤¤¤Þ¤»¤ó."
- kor "¼±ÅÃµÈ µ¥ÀÌŸº£À̽º°¡ ¾ø½À´Ï´Ù."
- nor "Ingen database valgt"
- norwegian-ny "Ingen database vald"
- pol "Nie wybrano ¿adnej bazy danych"
- por "Nenhum banco de dados foi selecionado"
- rum "Nici o baza de data nu a fost selectata inca"
- rus "âÁÚÁ ÄÁÎÎÙÈ ÎÅ ×ÙÂÒÁÎÁ"
- serbian "Ni jedna baza nije selektovana"
- slo "Nebola vybraná databáza"
- spa "Base de datos no seleccionada"
- swe "Ingen databas i användning"
- ukr "âÁÚÕ ÄÁÎÎÉÈ ÎÅ ×ÉÂÒÁÎÏ"
-ER_UNKNOWN_COM_ERROR 08S01
- cze "Nezn-Bámý pøíkaz"
- dan "Ukendt kommando"
- nla "Onbekend commando"
- eng "Unknown command"
- jps "‚»‚̃Rƒ}ƒ“ƒh‚͉½H",
- est "Tundmatu käsk"
- fre "Commande inconnue"
- ger "Unbekannter Befehl"
- greek "Áãíùóôç åíôïëÞ"
- hun "Ervenytelen parancs"
- ita "Comando sconosciuto"
- jpn "¤½¤Î¥³¥Þ¥ó¥É¤Ï²¿¡©"
- kor "¸í·É¾î°¡ ¹ºÁö ¸ð¸£°Ú¾î¿ä..."
- nor "Ukjent kommando"
- norwegian-ny "Ukjent kommando"
- pol "Nieznana komenda"
- por "Comando desconhecido"
- rum "Comanda invalida"
- rus "îÅÉÚ×ÅÓÔÎÁÑ ËÏÍÁÎÄÁ ËÏÍÍÕÎÉËÁÃÉÏÎÎÏÇÏ ÐÒÏÔÏËÏÌÁ"
- serbian "Nepoznata komanda"
- slo "Neznámy príkaz"
- spa "Comando desconocido"
- swe "Okänt commando"
- ukr "îÅצÄÏÍÁ ËÏÍÁÎÄÁ"
-ER_BAD_NULL_ERROR 23000
- cze "Sloupec '%-.192s' nem-Bù¾e být null"
- dan "Kolonne '%-.192s' kan ikke være NULL"
- nla "Kolom '%-.192s' kan niet null zijn"
- eng "Column '%-.192s' cannot be null"
- jps "Column '%-.192s' ‚Í null ‚É‚Í‚Å‚«‚È‚¢‚Ì‚Å‚·",
- est "Tulp '%-.192s' ei saa omada nullväärtust"
- fre "Le champ '%-.192s' ne peut être vide (null)"
- ger "Feld '%-.192s' darf nicht NULL sein"
- greek "Ôï ðåäßï '%-.192s' äåí ìðïñåß íá åßíáé êåíü (null)"
- hun "A(z) '%-.192s' oszlop erteke nem lehet nulla"
- ita "La colonna '%-.192s' non puo` essere nulla"
- jpn "Column '%-.192s' ¤Ï null ¤Ë¤Ï¤Ç¤­¤Ê¤¤¤Î¤Ç¤¹"
- kor "Ä®·³ '%-.192s'´Â ³Î(Null)ÀÌ µÇ¸é ¾ÈµË´Ï´Ù. "
- nor "Kolonne '%-.192s' kan ikke vere null"
- norwegian-ny "Kolonne '%-.192s' kan ikkje vere null"
- pol "Kolumna '%-.192s' nie mo¿e byæ null"
- por "Coluna '%-.192s' não pode ser vazia"
- rum "Coloana '%-.192s' nu poate sa fie null"
- rus "óÔÏÌÂÅà '%-.192s' ÎÅ ÍÏÖÅÔ ÐÒÉÎÉÍÁÔØ ×ÅÌÉÞÉÎÕ NULL"
- serbian "Kolona '%-.192s' ne može biti NULL"
- slo "Pole '%-.192s' nemô¾e by» null"
- spa "La columna '%-.192s' no puede ser nula"
- swe "Kolumn '%-.192s' får inte vara NULL"
- ukr "óÔÏ×ÂÅÃØ '%-.192s' ÎÅ ÍÏÖÅ ÂÕÔÉ ÎÕÌØÏ×ÉÍ"
-ER_BAD_DB_ERROR 42000
- cze "Nezn-Bámá databáze '%-.192s'"
- dan "Ukendt database '%-.192s'"
- nla "Onbekende database '%-.192s'"
- eng "Unknown database '%-.192s'"
- jps "'%-.192s' ‚È‚ñ‚ăf[ƒ^ƒx[ƒX‚Í’m‚è‚Ü‚¹‚ñ.",
- est "Tundmatu andmebaas '%-.192s'"
- fre "Base '%-.192s' inconnue"
- ger "Unbekannte Datenbank '%-.192s'"
- greek "Áãíùóôç âÜóç äåäïìÝíùí '%-.192s'"
- hun "Ervenytelen adatbazis: '%-.192s'"
- ita "Database '%-.192s' sconosciuto"
- jpn "'%-.192s' ¤Ê¤ó¤Æ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ÏÃΤê¤Þ¤»¤ó."
- kor "µ¥ÀÌŸº£À̽º '%-.192s'´Â ¾Ë¼ö ¾øÀ½"
- nor "Ukjent database '%-.192s'"
- norwegian-ny "Ukjent database '%-.192s'"
- pol "Nieznana baza danych '%-.192s'"
- por "Banco de dados '%-.192s' desconhecido"
- rum "Baza de data invalida '%-.192s'"
- rus "îÅÉÚ×ÅÓÔÎÁÑ ÂÁÚÁ ÄÁÎÎÙÈ '%-.192s'"
- serbian "Nepoznata baza '%-.192s'"
- slo "Neznáma databáza '%-.192s'"
- spa "Base de datos desconocida '%-.192s'"
- swe "Okänd databas: '%-.192s'"
- ukr "îÅצÄÏÍÁ ÂÁÚÁ ÄÁÎÎÉÈ '%-.192s'"
-ER_TABLE_EXISTS_ERROR 42S01
- cze "Tabulka '%-.192s' ji-B¾ existuje"
- dan "Tabellen '%-.192s' findes allerede"
- nla "Tabel '%-.192s' bestaat al"
- eng "Table '%-.192s' already exists"
- jps "Table '%-.192s' ‚ÍŠù‚É‚ ‚è‚Ü‚·",
- est "Tabel '%-.192s' juba eksisteerib"
- fre "La table '%-.192s' existe déjà"
- ger "Tabelle '%-.192s' bereits vorhanden"
- greek "Ï ðßíáêáò '%-.192s' õðÜñ÷åé Þäç"
- hun "A(z) '%-.192s' tabla mar letezik"
- ita "La tabella '%-.192s' esiste gia`"
- jpn "Table '%-.192s' ¤Ï´û¤Ë¤¢¤ê¤Þ¤¹"
- kor "Å×À̺í '%-.192s'´Â ÀÌ¹Ì Á¸ÀçÇÔ"
- nor "Tabellen '%-.192s' eksisterer allerede"
- norwegian-ny "Tabellen '%-.192s' eksisterar allereide"
- pol "Tabela '%-.192s' ju¿ istnieje"
- por "Tabela '%-.192s' já existe"
- rum "Tabela '%-.192s' exista deja"
- rus "ôÁÂÌÉÃÁ '%-.192s' ÕÖÅ ÓÕÝÅÓÔ×ÕÅÔ"
- serbian "Tabela '%-.192s' veæ postoji"
- slo "Tabuµka '%-.192s' u¾ existuje"
- spa "La tabla '%-.192s' ya existe"
- swe "Tabellen '%-.192s' finns redan"
- ukr "ôÁÂÌÉÃÑ '%-.192s' ×ÖÅ ¦ÓÎÕ¤"
-ER_BAD_TABLE_ERROR 42S02
- cze "Nezn-Bámá tabulka '%-.100s'"
- dan "Ukendt tabel '%-.100s'"
- nla "Onbekende tabel '%-.100s'"
- eng "Unknown table '%-.100s'"
- jps "table '%-.100s' ‚Í‚ ‚è‚Ü‚¹‚ñ.",
- est "Tundmatu tabel '%-.100s'"
- fre "Table '%-.100s' inconnue"
- ger "Unbekannte Tabelle '%-.100s'"
- greek "Áãíùóôïò ðßíáêáò '%-.100s'"
- hun "Ervenytelen tabla: '%-.100s'"
- ita "Tabella '%-.100s' sconosciuta"
- jpn "table '%-.100s' ¤Ï¤¢¤ê¤Þ¤»¤ó."
- kor "Å×À̺í '%-.100s'´Â ¾Ë¼ö ¾øÀ½"
- nor "Ukjent tabell '%-.100s'"
- norwegian-ny "Ukjent tabell '%-.100s'"
- pol "Nieznana tabela '%-.100s'"
- por "Tabela '%-.100s' desconhecida"
- rum "Tabela '%-.100s' este invalida"
- rus "îÅÉÚ×ÅÓÔÎÁÑ ÔÁÂÌÉÃÁ '%-.100s'"
- serbian "Nepoznata tabela '%-.100s'"
- slo "Neznáma tabuµka '%-.100s'"
- spa "Tabla '%-.100s' desconocida"
- swe "Okänd tabell '%-.100s'"
- ukr "îÅצÄÏÍÁ ÔÁÂÌÉÃÑ '%-.100s'"
-ER_NON_UNIQ_ERROR 23000
- cze "Sloupec '%-.192s' v %-.192s nen-Bí zcela jasný"
- dan "Felt: '%-.192s' i tabel %-.192s er ikke entydigt"
- nla "Kolom: '%-.192s' in %-.192s is niet eenduidig"
- eng "Column '%-.192s' in %-.192s is ambiguous"
- est "Väli '%-.192s' %-.192s-s ei ole ühene"
- fre "Champ: '%-.192s' dans %-.192s est ambigu"
- ger "Feld '%-.192s' in %-.192s ist nicht eindeutig"
- greek "Ôï ðåäßï: '%-.192s' óå %-.192s äåí Ý÷åé êáèïñéóôåß"
- hun "A(z) '%-.192s' oszlop %-.192s-ben ketertelmu"
- ita "Colonna: '%-.192s' di %-.192s e` ambigua"
- jpn "Column: '%-.192s' in %-.192s is ambiguous"
- kor "Ä®·³: '%-.192s' in '%-.192s' ÀÌ ¸ðÈ£ÇÔ"
- nor "Felt: '%-.192s' i tabell %-.192s er ikke entydig"
- norwegian-ny "Kolonne: '%-.192s' i tabell %-.192s er ikkje eintydig"
- pol "Kolumna: '%-.192s' w %-.192s jest dwuznaczna"
- por "Coluna '%-.192s' em '%-.192s' é ambígua"
- rum "Coloana: '%-.192s' in %-.192s este ambigua"
- rus "óÔÏÌÂÅÃ '%-.192s' × %-.192s ÚÁÄÁÎ ÎÅÏÄÎÏÚÎÁÞÎÏ"
- serbian "Kolona '%-.192s' u %-.192s nije jedinstvena u kontekstu"
- slo "Pole: '%-.192s' v %-.192s je nejasné"
- spa "La columna: '%-.192s' en %-.192s es ambigua"
- swe "Kolumn '%-.192s' i %-.192s är inte unik"
- ukr "óÔÏ×ÂÅÃØ '%-.192s' Õ %-.192s ×ÉÚÎÁÞÅÎÉÊ ÎÅÏÄÎÏÚÎÁÞÎÏ"
-ER_SERVER_SHUTDOWN 08S01
- cze "Prob-Bíhá ukonèování práce serveru"
- dan "Database nedlukning er i gang"
- nla "Bezig met het stoppen van de server"
- eng "Server shutdown in progress"
- jps "Server ‚ð shutdown ’†...",
- est "Serveri seiskamine käib"
- fre "Arrêt du serveur en cours"
- ger "Der Server wird heruntergefahren"
- greek "Åíáñîç äéáäéêáóßáò áðïóýíäåóçò ôïõ åîõðçñåôçôÞ (server shutdown)"
- hun "A szerver leallitasa folyamatban"
- ita "Shutdown del server in corso"
- jpn "Server ¤ò shutdown Ãæ..."
- kor "Server°¡ ¼Ë´Ù¿î ÁßÀÔ´Ï´Ù."
- nor "Database nedkobling er i gang"
- norwegian-ny "Tenar nedkopling er i gang"
- pol "Trwa koñczenie dzia³ania serwera"
- por "'Shutdown' do servidor em andamento"
- rum "Terminarea serverului este in desfasurare"
- rus "óÅÒ×ÅÒ ÎÁÈÏÄÉÔÓÑ × ÐÒÏÃÅÓÓÅ ÏÓÔÁÎÏ×ËÉ"
- serbian "Gašenje servera je u toku"
- slo "Prebieha ukonèovanie práce servera"
- spa "Desconexion de servidor en proceso"
- swe "Servern går nu ned"
- ukr "úÁ×ÅÒÛÕ¤ÔØÓÑ ÒÁÂÏÔÁ ÓÅÒ×ÅÒÁ"
-ER_BAD_FIELD_ERROR 42S22 S0022
- cze "Nezn-Bámý sloupec '%-.192s' v %-.192s"
- dan "Ukendt kolonne '%-.192s' i tabel %-.192s"
- nla "Onbekende kolom '%-.192s' in %-.192s"
- eng "Unknown column '%-.192s' in '%-.192s'"
- jps "'%-.192s' column ‚Í '%-.192s' ‚É‚Í‚ ‚è‚Ü‚¹‚ñ.",
- est "Tundmatu tulp '%-.192s' '%-.192s'-s"
- fre "Champ '%-.192s' inconnu dans %-.192s"
- ger "Unbekanntes Tabellenfeld '%-.192s' in %-.192s"
- greek "Áãíùóôï ðåäßï '%-.192s' óå '%-.192s'"
- hun "A(z) '%-.192s' oszlop ervenytelen '%-.192s'-ben"
- ita "Colonna sconosciuta '%-.192s' in '%-.192s'"
- jpn "'%-.192s' column ¤Ï '%-.192s' ¤Ë¤Ï¤¢¤ê¤Þ¤»¤ó."
- kor "Unknown Ä®·³ '%-.192s' in '%-.192s'"
- nor "Ukjent kolonne '%-.192s' i tabell %-.192s"
- norwegian-ny "Ukjent felt '%-.192s' i tabell %-.192s"
- pol "Nieznana kolumna '%-.192s' w %-.192s"
- por "Coluna '%-.192s' desconhecida em '%-.192s'"
- rum "Coloana invalida '%-.192s' in '%-.192s'"
- rus "îÅÉÚ×ÅÓÔÎÙÊ ÓÔÏÌÂÅà '%-.192s' × '%-.192s'"
- serbian "Nepoznata kolona '%-.192s' u '%-.192s'"
- slo "Neznáme pole '%-.192s' v '%-.192s'"
- spa "La columna '%-.192s' en %-.192s es desconocida"
- swe "Okänd kolumn '%-.192s' i %-.192s"
- ukr "îÅצÄÏÍÉÊ ÓÔÏ×ÂÅÃØ '%-.192s' Õ '%-.192s'"
-ER_WRONG_FIELD_WITH_GROUP 42000 S1009
- cze "Pou-B¾ité '%-.192s' nebylo v group by"
- dan "Brugte '%-.192s' som ikke var i group by"
- nla "Opdracht gebruikt '%-.192s' dat niet in de GROUP BY voorkomt"
- eng "'%-.192s' isn't in GROUP BY"
- jps "'%-.192s' isn't in GROUP BY",
- est "'%-.192s' puudub GROUP BY klauslis"
- fre "'%-.192s' n'est pas dans 'group by'"
- ger "'%-.192s' ist nicht in GROUP BY vorhanden"
- greek "×ñçóéìïðïéÞèçêå '%-.192s' ðïõ äåí õðÞñ÷å óôï group by"
- hun "Used '%-.192s' with wasn't in group by"
- ita "Usato '%-.192s' che non e` nel GROUP BY"
- kor "'%-.192s'Àº GROUP BY¼Ó¿¡ ¾øÀ½"
- nor "Brukte '%-.192s' som ikke var i group by"
- norwegian-ny "Brukte '%-.192s' som ikkje var i group by"
- pol "U¿yto '%-.192s' bez umieszczenia w group by"
- por "'%-.192s' não está em 'GROUP BY'"
- rum "'%-.192s' nu exista in clauza GROUP BY"
- rus "'%-.192s' ÎÅ ÐÒÉÓÕÔÓÔ×ÕÅÔ × GROUP BY"
- serbian "Entitet '%-.192s' nije naveden u komandi 'GROUP BY'"
- slo "Pou¾ité '%-.192s' nebolo v 'group by'"
- spa "Usado '%-.192s' el cual no esta group by"
- swe "'%-.192s' finns inte i GROUP BY"
- ukr "'%-.192s' ÎÅ ¤ Õ GROUP BY"
-ER_WRONG_GROUP_FIELD 42000 S1009
- cze "Nemohu pou-B¾ít group na '%-.192s'"
- dan "Kan ikke gruppere på '%-.192s'"
- nla "Kan '%-.192s' niet groeperen"
- eng "Can't group on '%-.192s'"
- est "Ei saa grupeerida '%-.192s' järgi"
- fre "Ne peut regrouper '%-.192s'"
- ger "Gruppierung über '%-.192s' nicht möglich"
- greek "Áäýíáôç ç ïìáäïðïßçóç (group on) '%-.192s'"
- hun "A group nem hasznalhato: '%-.192s'"
- ita "Impossibile raggruppare per '%-.192s'"
- kor "'%-.192s'¸¦ ±×·ìÇÒ ¼ö ¾øÀ½"
- nor "Kan ikke gruppere på '%-.192s'"
- norwegian-ny "Kan ikkje gruppere på '%-.192s'"
- pol "Nie mo¿na grupowaæ po '%-.192s'"
- por "Não pode agrupar em '%-.192s'"
- rum "Nu pot sa grupez pe (group on) '%-.192s'"
- rus "îÅ×ÏÚÍÏÖÎÏ ÐÒÏÉÚ×ÅÓÔÉ ÇÒÕÐÐÉÒÏ×ËÕ ÐÏ '%-.192s'"
- serbian "Ne mogu da grupišem po '%-.192s'"
- slo "Nemô¾em pou¾i» 'group' na '%-.192s'"
- spa "No puedo agrupar por '%-.192s'"
- swe "Kan inte använda GROUP BY med '%-.192s'"
- ukr "îÅ ÍÏÖÕ ÇÒÕÐÕ×ÁÔÉ ÐÏ '%-.192s'"
-ER_WRONG_SUM_SELECT 42000 S1009
- cze "P-Bøíkaz obsahuje zároveò funkci sum a sloupce"
- dan "Udtrykket har summer (sum) funktioner og kolonner i samme udtryk"
- nla "Opdracht heeft totaliseer functies en kolommen in dezelfde opdracht"
- eng "Statement has sum functions and columns in same statement"
- est "Lauses on korraga nii tulbad kui summeerimisfunktsioonid"
- fre "Vous demandez la fonction sum() et des champs dans la même commande"
- ger "Die Verwendung von Summierungsfunktionen und Spalten im selben Befehl ist nicht erlaubt"
- greek "Ç äéáôýðùóç ðåñéÝ÷åé sum functions êáé columns óôçí ßäéá äéáôýðùóç"
- ita "Il comando ha una funzione SUM e una colonna non specificata nella GROUP BY"
- kor "Statement °¡ sum±â´ÉÀ» µ¿ÀÛÁßÀÌ°í Ä®·³µµ µ¿ÀÏÇÑ statementÀÔ´Ï´Ù."
- nor "Uttrykket har summer (sum) funksjoner og kolonner i samme uttrykk"
- norwegian-ny "Uttrykket har summer (sum) funksjoner og kolonner i same uttrykk"
- pol "Zapytanie ma funkcje sumuj?ce i kolumny w tym samym zapytaniu"
- por "Cláusula contém funções de soma e colunas juntas"
- rum "Comanda are functii suma si coloane in aceeasi comanda"
- rus "÷ÙÒÁÖÅÎÉÅ ÓÏÄÅÒÖÉÔ ÇÒÕÐÐÏ×ÙÅ ÆÕÎËÃÉÉ É ÓÔÏÌÂÃÙ, ÎÏ ÎÅ ×ËÌÀÞÁÅÔ GROUP BY. á ËÁË ×Ù ÕÍÕÄÒÉÌÉÓØ ÐÏÌÕÞÉÔØ ÜÔÏ ÓÏÏÂÝÅÎÉÅ Ï ÏÛÉÂËÅ?"
- serbian "Izraz ima 'SUM' agregatnu funkciju i kolone u isto vreme"
- slo "Príkaz obsahuje zároveò funkciu 'sum' a poµa"
- spa "El estamento tiene funciones de suma y columnas en el mismo estamento"
- swe "Kommandot har både sum functions och enkla funktioner"
- ukr "õ ×ÉÒÁÚ¦ ×ÉËÏÒÉÓÔÁÎÏ Ð¦ÄÓÕÍÏ×ÕÀÞ¦ ÆÕÎËæ§ ÐÏÒÑÄ Ú ¦ÍÅÎÁÍÉ ÓÔÏ×Âæ×"
-ER_WRONG_VALUE_COUNT 21S01
- cze "Po-Bèet sloupcù neodpovídá zadané hodnotì"
- dan "Kolonne tæller stemmer ikke med antallet af værdier"
- nla "Het aantal kolommen komt niet overeen met het aantal opgegeven waardes"
- eng "Column count doesn't match value count"
- est "Tulpade arv erineb väärtuste arvust"
- ger "Die Anzahl der Spalten entspricht nicht der Anzahl der Werte"
- greek "Ôï Column count äåí ôáéñéÜæåé ìå ôï value count"
- hun "Az oszlopban levo ertek nem egyezik meg a szamitott ertekkel"
- ita "Il numero delle colonne non e` uguale al numero dei valori"
- kor "Ä®·³ÀÇ Ä«¿îÆ®°¡ °ªÀÇ Ä«¿îÆ®¿Í ÀÏÄ¡ÇÏÁö ¾Ê½À´Ï´Ù."
- nor "Felt telling stemmer verdi telling"
- norwegian-ny "Kolonne telling stemmer verdi telling"
- pol "Liczba kolumn nie odpowiada liczbie warto?ci"
- por "Contagem de colunas não confere com a contagem de valores"
- rum "Numarul de coloane nu este acelasi cu numarul valoarei"
- rus "ëÏÌÉÞÅÓÔ×Ï ÓÔÏÌÂÃÏ× ÎÅ ÓÏ×ÐÁÄÁÅÔ Ó ËÏÌÉÞÅÓÔ×ÏÍ ÚÎÁÞÅÎÉÊ"
- serbian "Broj kolona ne odgovara broju vrednosti"
- slo "Poèet polí nezodpovedá zadanej hodnote"
- spa "La columna con count no tiene valores para contar"
- swe "Antalet kolumner motsvarar inte antalet värden"
- ukr "ë¦ÌØ˦ÓÔØ ÓÔÏ×ÂÃ¦× ÎÅ ÓЦ×ÐÁÄÁ¤ Ú Ë¦ÌØ˦ÓÔÀ ÚÎÁÞÅÎØ"
-ER_TOO_LONG_IDENT 42000 S1009
- cze "Jm-Béno identifikátoru '%-.100s' je pøíli¹ dlouhé"
- dan "Navnet '%-.100s' er for langt"
- nla "Naam voor herkenning '%-.100s' is te lang"
- eng "Identifier name '%-.100s' is too long"
- jps "Identifier name '%-.100s' ‚Í’·‚·‚¬‚Ü‚·",
- est "Identifikaatori '%-.100s' nimi on liiga pikk"
- fre "Le nom de l'identificateur '%-.100s' est trop long"
- ger "Name des Bezeichners '%-.100s' ist zu lang"
- greek "Ôï identifier name '%-.100s' åßíáé ðïëý ìåãÜëï"
- hun "A(z) '%-.100s' azonositonev tul hosszu."
- ita "Il nome dell'identificatore '%-.100s' e` troppo lungo"
- jpn "Identifier name '%-.100s' ¤ÏŤ¹¤®¤Þ¤¹"
- kor "Identifier '%-.100s'´Â ³Ê¹« ±æ±º¿ä."
- nor "Identifikator '%-.100s' er for lang"
- norwegian-ny "Identifikator '%-.100s' er for lang"
- pol "Nazwa identyfikatora '%-.100s' jest zbyt d³uga"
- por "Nome identificador '%-.100s' é longo demais"
- rum "Numele indentificatorului '%-.100s' este prea lung"
- rus "óÌÉÛËÏÍ ÄÌÉÎÎÙÊ ÉÄÅÎÔÉÆÉËÁÔÏÒ '%-.100s'"
- serbian "Ime '%-.100s' je predugaèko"
- slo "Meno identifikátora '%-.100s' je príli¹ dlhé"
- spa "El nombre del identificador '%-.100s' es demasiado grande"
- swe "Kolumnnamn '%-.100s' är för långt"
- ukr "¶Í'Ñ ¦ÄÅÎÔÉƦËÁÔÏÒÁ '%-.100s' ÚÁÄÏ×ÇÅ"
-ER_DUP_FIELDNAME 42S21 S1009
- cze "Zdvojen-Bé jméno sloupce '%-.192s'"
- dan "Feltnavnet '%-.192s' findes allerede"
- nla "Dubbele kolom naam '%-.192s'"
- eng "Duplicate column name '%-.192s'"
- jps "'%-.192s' ‚Æ‚¢‚¤ column –¼‚Íd•¡‚µ‚Ä‚Ü‚·",
- est "Kattuv tulba nimi '%-.192s'"
- fre "Nom du champ '%-.192s' déjà utilisé"
- ger "Doppelter Spaltenname: '%-.192s'"
- greek "ÅðáíÜëçøç column name '%-.192s'"
- hun "Duplikalt oszlopazonosito: '%-.192s'"
- ita "Nome colonna duplicato '%-.192s'"
- jpn "'%-.192s' ¤È¤¤¤¦ column ̾¤Ï½ÅÊ£¤·¤Æ¤Þ¤¹"
- kor "Áߺ¹µÈ Ä®·³ À̸§: '%-.192s'"
- nor "Feltnavnet '%-.192s' eksisterte fra før"
- norwegian-ny "Feltnamnet '%-.192s' eksisterte frå før"
- pol "Powtórzona nazwa kolumny '%-.192s'"
- por "Nome da coluna '%-.192s' duplicado"
- rum "Numele coloanei '%-.192s' e duplicat"
- rus "äÕÂÌÉÒÕÀÝÅÅÓÑ ÉÍÑ ÓÔÏÌÂÃÁ '%-.192s'"
- serbian "Duplirano ime kolone '%-.192s'"
- slo "Opakované meno poµa '%-.192s'"
- spa "Nombre de columna duplicado '%-.192s'"
- swe "Kolumnnamn '%-.192s finns flera gånger"
- ukr "äÕÂÌÀÀÞÅ ¦Í'Ñ ÓÔÏ×ÂÃÑ '%-.192s'"
-ER_DUP_KEYNAME 42000 S1009
- cze "Zdvojen-Bé jméno klíèe '%-.192s'"
- dan "Indeksnavnet '%-.192s' findes allerede"
- nla "Dubbele zoeksleutel naam '%-.192s'"
- eng "Duplicate key name '%-.192s'"
- jps "'%-.192s' ‚Æ‚¢‚¤ key ‚Ì–¼‘O‚Íd•¡‚µ‚Ä‚¢‚Ü‚·",
- est "Kattuv võtme nimi '%-.192s'"
- fre "Nom de clef '%-.192s' déjà utilisé"
- ger "Doppelter Name für Schlüssel vorhanden: '%-.192s'"
- greek "ÅðáíÜëçøç key name '%-.192s'"
- hun "Duplikalt kulcsazonosito: '%-.192s'"
- ita "Nome chiave duplicato '%-.192s'"
- jpn "'%-.192s' ¤È¤¤¤¦ key ¤Î̾Á°¤Ï½ÅÊ£¤·¤Æ¤¤¤Þ¤¹"
- kor "Áߺ¹µÈ Å° À̸§ : '%-.192s'"
- nor "Nøkkelnavnet '%-.192s' eksisterte fra før"
- norwegian-ny "Nøkkelnamnet '%-.192s' eksisterte frå før"
- pol "Powtórzony nazwa klucza '%-.192s'"
- por "Nome da chave '%-.192s' duplicado"
- rum "Numele cheiei '%-.192s' e duplicat"
- rus "äÕÂÌÉÒÕÀÝÅÅÓÑ ÉÍÑ ËÌÀÞÁ '%-.192s'"
- serbian "Duplirano ime kljuèa '%-.192s'"
- slo "Opakované meno kµúèa '%-.192s'"
- spa "Nombre de clave duplicado '%-.192s'"
- swe "Nyckelnamn '%-.192s' finns flera gånger"
- ukr "äÕÂÌÀÀÞÅ ¦Í'Ñ ËÌÀÞÁ '%-.192s'"
-# When using this error code, please use ER(ER_DUP_ENTRY_WITH_KEY_NAME)
-# for the message string. See, for example, code in handler.cc.
-ER_DUP_ENTRY 23000 S1009
- cze "Zdvojen-Bý klíè '%-.192s' (èíslo klíèe %d)"
- dan "Ens værdier '%-.192s' for indeks %d"
- nla "Dubbele ingang '%-.192s' voor zoeksleutel %d"
- eng "Duplicate entry '%-.192s' for key %d"
- jps "'%-.192s' ‚Í key %d ‚É‚¨‚¢‚Äd•¡‚µ‚Ä‚¢‚Ü‚·",
- est "Kattuv väärtus '%-.192s' võtmele %d"
- fre "Duplicata du champ '%-.192s' pour la clef %d"
- ger "Doppelter Eintrag '%-.192s' für Schlüssel %d"
- greek "ÄéðëÞ åããñáöÞ '%-.192s' ãéá ôï êëåéäß %d"
- hun "Duplikalt bejegyzes '%-.192s' a %d kulcs szerint."
- ita "Valore duplicato '%-.192s' per la chiave %d"
- jpn "'%-.192s' ¤Ï key %d ¤Ë¤ª¤¤¤Æ½ÅÊ£¤·¤Æ¤¤¤Þ¤¹"
- kor "Áߺ¹µÈ ÀÔ·Â °ª '%-.192s': key %d"
- nor "Like verdier '%-.192s' for nøkkel %d"
- norwegian-ny "Like verdiar '%-.192s' for nykkel %d"
- pol "Powtórzone wyst?pienie '%-.192s' dla klucza %d"
- por "Entrada '%-.192s' duplicada para a chave %d"
- rum "Cimpul '%-.192s' e duplicat pentru cheia %d"
- rus "äÕÂÌÉÒÕÀÝÁÑÓÑ ÚÁÐÉÓØ '%-.192s' ÐÏ ËÌÀÞÕ %d"
- serbian "Dupliran unos '%-.192s' za kljuè '%d'"
- slo "Opakovaný kµúè '%-.192s' (èíslo kµúèa %d)"
- spa "Entrada duplicada '%-.192s' para la clave %d"
- swe "Dubbel nyckel '%-.192s' för nyckel %d"
- ukr "äÕÂÌÀÀÞÉÊ ÚÁÐÉÓ '%-.192s' ÄÌÑ ËÌÀÞÁ %d"
-ER_WRONG_FIELD_SPEC 42000 S1009
- cze "Chybn-Bá specifikace sloupce '%-.192s'"
- dan "Forkert kolonnespecifikaton for felt '%-.192s'"
- nla "Verkeerde kolom specificatie voor kolom '%-.192s'"
- eng "Incorrect column specifier for column '%-.192s'"
- est "Vigane tulba kirjeldus tulbale '%-.192s'"
- fre "Mauvais paramètre de champ pour le champ '%-.192s'"
- ger "Falsche Spezifikation für Feld '%-.192s'"
- greek "ÅóöáëìÝíï column specifier ãéá ôï ðåäßï '%-.192s'"
- hun "Rossz oszlopazonosito: '%-.192s'"
- ita "Specifica errata per la colonna '%-.192s'"
- kor "Ä®·³ '%-.192s'ÀÇ ºÎÁ¤È®ÇÑ Ä®·³ Á¤ÀÇÀÚ"
- nor "Feil kolonne spesifikator for felt '%-.192s'"
- norwegian-ny "Feil kolonne spesifikator for kolonne '%-.192s'"
- pol "B³êdna specyfikacja kolumny dla kolumny '%-.192s'"
- por "Especificador de coluna incorreto para a coluna '%-.192s'"
- rum "Specificandul coloanei '%-.192s' este incorect"
- rus "îÅËÏÒÒÅËÔÎÙÊ ÏÐÒÅÄÅÌÉÔÅÌØ ÓÔÏÌÂÃÁ ÄÌÑ ÓÔÏÌÂÃÁ '%-.192s'"
- serbian "Pogrešan naziv kolone za kolonu '%-.192s'"
- slo "Chyba v ¹pecifikácii poµa '%-.192s'"
- spa "Especificador de columna erroneo para la columna '%-.192s'"
- swe "Felaktigt kolumntyp för kolumn '%-.192s'"
- ukr "îÅצÒÎÉÊ ÓÐÅÃÉƦËÁÔÏÒ ÓÔÏ×ÂÃÑ '%-.192s'"
-ER_PARSE_ERROR 42000 s1009
- cze "%s bl-Bízko '%-.80s' na øádku %d"
- dan "%s nær '%-.80s' på linje %d"
- nla "%s bij '%-.80s' in regel %d"
- eng "%s near '%-.80s' at line %d"
- jps "%s : '%-.80s' •t‹ß : %d s–Ú",
- est "%s '%-.80s' ligidal real %d"
- fre "%s près de '%-.80s' à la ligne %d"
- ger "%s bei '%-.80s' in Zeile %d"
- greek "%s ðëçóßïí '%-.80s' óôç ãñáììÞ %d"
- hun "A %s a '%-.80s'-hez kozeli a %d sorban"
- ita "%s vicino a '%-.80s' linea %d"
- jpn "%s : '%-.80s' ÉÕ¶á : %d ¹ÔÌÜ"
- kor "'%s' ¿¡·¯ °°À¾´Ï´Ù. ('%-.80s' ¸í·É¾î ¶óÀÎ %d)"
- nor "%s nær '%-.80s' på linje %d"
- norwegian-ny "%s attmed '%-.80s' på line %d"
- pol "%s obok '%-.80s' w linii %d"
- por "%s próximo a '%-.80s' na linha %d"
- rum "%s linga '%-.80s' pe linia %d"
- rus "%s ÏËÏÌÏ '%-.80s' ÎÁ ÓÔÒÏËÅ %d"
- serbian "'%s' u iskazu '%-.80s' na liniji %d"
- slo "%s blízko '%-.80s' na riadku %d"
- spa "%s cerca '%-.80s' en la linea %d"
- swe "%s nära '%-.80s' på rad %d"
- ukr "%s ¦ÌÑ '%-.80s' × ÓÔÒÏæ %d"
-ER_EMPTY_QUERY 42000
- cze "V-Býsledek dotazu je prázdný"
- dan "Forespørgsel var tom"
- nla "Query was leeg"
- eng "Query was empty"
- jps "Query ‚ª‹ó‚Å‚·.",
- est "Tühi päring"
- fre "Query est vide"
- ger "Leere Abfrage"
- greek "Ôï åñþôçìá (query) ðïõ èÝóáôå Þôáí êåíü"
- hun "Ures lekerdezes."
- ita "La query e` vuota"
- jpn "Query ¤¬¶õ¤Ç¤¹."
- kor "Äõ¸®°á°ú°¡ ¾ø½À´Ï´Ù."
- nor "Forespørsel var tom"
- norwegian-ny "Førespurnad var tom"
- pol "Zapytanie by³o puste"
- por "Consulta (query) estava vazia"
- rum "Query-ul a fost gol"
- rus "úÁÐÒÏÓ ÏËÁÚÁÌÓÑ ÐÕÓÔÙÍ"
- serbian "Upit je bio prazan"
- slo "Výsledok po¾iadavky bol prázdny"
- spa "La query estaba vacia"
- swe "Frågan var tom"
- ukr "ðÕÓÔÉÊ ÚÁÐÉÔ"
-ER_NONUNIQ_TABLE 42000 S1009
- cze "Nejednozna-Bèná tabulka/alias: '%-.192s'"
- dan "Tabellen/aliaset: '%-.192s' er ikke unikt"
- nla "Niet unieke waarde tabel/alias: '%-.192s'"
- eng "Not unique table/alias: '%-.192s'"
- jps "'%-.192s' ‚͈êˆÓ‚Ì table/alias –¼‚Å‚Í‚ ‚è‚Ü‚¹‚ñ",
- est "Ei ole unikaalne tabel/alias '%-.192s'"
- fre "Table/alias: '%-.192s' non unique"
- ger "Tabellenname/Alias '%-.192s' nicht eindeutig"
- greek "Áäýíáôç ç áíåýñåóç unique table/alias: '%-.192s'"
- hun "Nem egyedi tabla/alias: '%-.192s'"
- ita "Tabella/alias non unico: '%-.192s'"
- jpn "'%-.192s' ¤Ï°ì°Õ¤Î table/alias ̾¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó"
- kor "Unique ÇÏÁö ¾ÊÀº Å×À̺í/alias: '%-.192s'"
- nor "Ikke unikt tabell/alias: '%-.192s'"
- norwegian-ny "Ikkje unikt tabell/alias: '%-.192s'"
- pol "Tabela/alias nie s? unikalne: '%-.192s'"
- por "Tabela/alias '%-.192s' não única"
- rum "Tabela/alias: '%-.192s' nu este unic"
- rus "ðÏ×ÔÏÒÑÀÝÁÑÓÑ ÔÁÂÌÉÃÁ/ÐÓÅ×ÄÏÎÉÍ '%-.192s'"
- serbian "Tabela ili alias nisu bili jedinstveni: '%-.192s'"
- slo "Nie jednoznaèná tabuµka/alias: '%-.192s'"
- spa "Tabla/alias: '%-.192s' es no unica"
- swe "Icke unikt tabell/alias: '%-.192s'"
- ukr "îÅÕΦËÁÌØÎÁ ÔÁÂÌÉÃÑ/ÐÓÅ×ÄÏΦÍ: '%-.192s'"
-ER_INVALID_DEFAULT 42000 S1009
- cze "Chybn-Bá defaultní hodnota pro '%-.192s'"
- dan "Ugyldig standardværdi for '%-.192s'"
- nla "Foutieve standaard waarde voor '%-.192s'"
- eng "Invalid default value for '%-.192s'"
- est "Vigane vaikeväärtus '%-.192s' jaoks"
- fre "Valeur par défaut invalide pour '%-.192s'"
- ger "Fehlerhafter Vorgabewert (DEFAULT) für '%-.192s'"
- greek "ÅóöáëìÝíç ðñïêáèïñéóìÝíç ôéìÞ (default value) ãéá '%-.192s'"
- hun "Ervenytelen ertek: '%-.192s'"
- ita "Valore di default non valido per '%-.192s'"
- kor "'%-.192s'ÀÇ À¯È¿ÇÏÁö ¸øÇÑ µðÆúÆ® °ªÀ» »ç¿ëÇϼ̽À´Ï´Ù."
- nor "Ugyldig standardverdi for '%-.192s'"
- norwegian-ny "Ugyldig standardverdi for '%-.192s'"
- pol "Niew³a?ciwa warto?æ domy?lna dla '%-.192s'"
- por "Valor padrão (default) inválido para '%-.192s'"
- rum "Valoarea de default este invalida pentru '%-.192s'"
- rus "îÅËÏÒÒÅËÔÎÏÅ ÚÎÁÞÅÎÉÅ ÐÏ ÕÍÏÌÞÁÎÉÀ ÄÌÑ '%-.192s'"
- serbian "Loša default vrednost za '%-.192s'"
- slo "Chybná implicitná hodnota pre '%-.192s'"
- spa "Valor por defecto invalido para '%-.192s'"
- swe "Ogiltigt DEFAULT värde för '%-.192s'"
- ukr "îÅצÒÎÅ ÚÎÁÞÅÎÎÑ ÐÏ ÚÁÍÏ×ÞÕ×ÁÎÎÀ ÄÌÑ '%-.192s'"
-ER_MULTIPLE_PRI_KEY 42000 S1009
- cze "Definov-Báno více primárních klíèù"
- dan "Flere primærnøgler specificeret"
- nla "Meerdere primaire zoeksleutels gedefinieerd"
- eng "Multiple primary key defined"
- jps "•¡”‚Ì primary key ‚ª’è‹`‚³‚ê‚Ü‚µ‚½",
- est "Mitut primaarset võtit ei saa olla"
- fre "Plusieurs clefs primaires définies"
- ger "Mehrere Primärschlüssel (PRIMARY KEY) definiert"
- greek "Ðåñéóóüôåñá áðü Ýíá primary key ïñßóôçêáí"
- hun "Tobbszoros elsodleges kulcs definialas."
- ita "Definite piu` chiave primarie"
- jpn "Ê£¿ô¤Î primary key ¤¬ÄêµÁ¤µ¤ì¤Þ¤·¤¿"
- kor "Multiple primary key°¡ Á¤ÀǵǾî ÀÖ½¿"
- nor "Fleire primærnøkle spesifisert"
- norwegian-ny "Fleire primærnyklar spesifisert"
- pol "Zdefiniowano wiele kluczy podstawowych"
- por "Definida mais de uma chave primária"
- rum "Chei primare definite de mai multe ori"
- rus "õËÁÚÁÎÏ ÎÅÓËÏÌØËÏ ÐÅÒ×ÉÞÎÙÈ ËÌÀÞÅÊ"
- serbian "Definisani višestruki primarni kljuèevi"
- slo "Zadefinovaných viac primárnych kµúèov"
- spa "Multiples claves primarias definidas"
- swe "Flera PRIMARY KEY använda"
- ukr "ðÅÒ×ÉÎÎÏÇÏ ËÌÀÞÁ ×ÉÚÎÁÞÅÎÏ ÎÅÏÄÎÏÒÁÚÏ×Ï"
-ER_TOO_MANY_KEYS 42000 S1009
- cze "Zad-Báno pøíli¹ mnoho klíèù, je povoleno nejvíce %d klíèù"
- dan "For mange nøgler specificeret. Kun %d nøgler må bruges"
- nla "Teveel zoeksleutels gedefinieerd. Maximaal zijn %d zoeksleutels toegestaan"
- eng "Too many keys specified; max %d keys allowed"
- jps "key ‚ÌŽw’肪‘½‚·‚¬‚Ü‚·. key ‚ÍÅ‘å %d ‚Ü‚Å‚Å‚·",
- est "Liiga palju võtmeid. Maksimaalselt võib olla %d võtit"
- fre "Trop de clefs sont définies. Maximum de %d clefs alloué"
- ger "Zu viele Schlüssel definiert. Maximal %d Schlüssel erlaubt"
- greek "ÐÜñá ðïëëÜ key ïñßóèçêáí. Ôï ðïëý %d åðéôñÝðïíôáé"
- hun "Tul sok kulcs. Maximum %d kulcs engedelyezett."
- ita "Troppe chiavi. Sono ammesse max %d chiavi"
- jpn "key ¤Î»ØÄ꤬¿¤¹¤®¤Þ¤¹. key ¤ÏºÇÂç %d ¤Þ¤Ç¤Ç¤¹"
- kor "³Ê¹« ¸¹Àº Å°°¡ Á¤ÀǵǾî ÀÖÀ¾´Ï´Ù.. ÃÖ´ë %dÀÇ Å°°¡ °¡´ÉÇÔ"
- nor "For mange nøkler spesifisert. Maks %d nøkler tillatt"
- norwegian-ny "For mange nykler spesifisert. Maks %d nyklar tillatt"
- pol "Okre?lono zbyt wiele kluczy. Dostêpnych jest maksymalnie %d kluczy"
- por "Especificadas chaves demais. O máximo permitido são %d chaves"
- rum "Prea multe chei. Numarul de chei maxim este %d"
- rus "õËÁÚÁÎÏ ÓÌÉÛËÏÍ ÍÎÏÇÏ ËÌÀÞÅÊ. òÁÚÒÅÛÁÅÔÓÑ ÕËÁÚÙ×ÁÔØ ÎÅ ÂÏÌÅÅ %d ËÌÀÞÅÊ"
- serbian "Navedeno je previše kljuèeva. Maksimum %d kljuèeva je dozvoljeno"
- slo "Zadaných ríli¹ veµa kµúèov. Najviac %d kµúèov je povolených"
- spa "Demasiadas claves primarias declaradas. Un maximo de %d claves son permitidas"
- swe "För många nycklar använda. Man får ha högst %d nycklar"
- ukr "úÁÂÁÇÁÔÏ ËÌÀÞ¦× ÚÁÚÎÁÞÅÎÏ. äÏÚ×ÏÌÅÎÏ ÎŠ¦ÌØÛÅ %d ËÌÀÞ¦×"
-ER_TOO_MANY_KEY_PARTS 42000 S1009
- cze "Zad-Báno pøíli¹ mnoho èást klíèù, je povoleno nejvíce %d èástí"
- dan "For mange nøgledele specificeret. Kun %d dele må bruges"
- nla "Teveel zoeksleutel onderdelen gespecificeerd. Maximaal %d onderdelen toegestaan"
- eng "Too many key parts specified; max %d parts allowed"
- est "Võti koosneb liiga paljudest osadest. Maksimaalselt võib olla %d osa"
- fre "Trop de parties specifiées dans la clef. Maximum de %d parties"
- ger "Zu viele Teilschlüssel definiert. Maximal %d Teilschlüssel erlaubt"
- greek "ÐÜñá ðïëëÜ key parts ïñßóèçêáí. Ôï ðïëý %d åðéôñÝðïíôáé"
- hun "Tul sok kulcsdarabot definialt. Maximum %d resz engedelyezett"
- ita "Troppe parti di chiave specificate. Sono ammesse max %d parti"
- kor "³Ê¹« ¸¹Àº Å° ºÎºÐ(parts)µéÀÌ Á¤ÀǵǾî ÀÖÀ¾´Ï´Ù.. ÃÖ´ë %d ºÎºÐÀÌ °¡´ÉÇÔ"
- nor "For mange nøkkeldeler spesifisert. Maks %d deler tillatt"
- norwegian-ny "For mange nykkeldelar spesifisert. Maks %d delar tillatt"
- pol "Okre?lono zbyt wiele czê?ci klucza. Dostêpnych jest maksymalnie %d czê?ci"
- por "Especificadas partes de chave demais. O máximo permitido são %d partes"
- rum "Prea multe chei. Numarul de chei maxim este %d"
- rus "õËÁÚÁÎÏ ÓÌÉÛËÏÍ ÍÎÏÇÏ ÞÁÓÔÅÊ ÓÏÓÔÁ×ÎÏÇÏ ËÌÀÞÁ. òÁÚÒÅÛÁÅÔÓÑ ÕËÁÚÙ×ÁÔØ ÎÅ ÂÏÌÅÅ %d ÞÁÓÔÅÊ"
- serbian "Navedeno je previše delova kljuèa. Maksimum %d delova je dozvoljeno"
- slo "Zadaných ríli¹ veµa èastí kµúèov. Je povolených najviac %d èastí"
- spa "Demasiadas partes de clave declaradas. Un maximo de %d partes son permitidas"
- swe "För många nyckeldelar använda. Man får ha högst %d nyckeldelar"
- ukr "úÁÂÁÇÁÔÏ ÞÁÓÔÉÎ ËÌÀÞÁ ÚÁÚÎÁÞÅÎÏ. äÏÚ×ÏÌÅÎÏ ÎŠ¦ÌØÛÅ %d ÞÁÓÔÉÎ"
-ER_TOO_LONG_KEY 42000 S1009
- cze "Zadan-Bý klíè byl pøíli¹ dlouhý, nejvìt¹í délka klíèe je %d"
- dan "Specificeret nøgle var for lang. Maksimal nøglelængde er %d"
- nla "Gespecificeerde zoeksleutel was te lang. De maximale lengte is %d"
- eng "Specified key was too long; max key length is %d bytes"
- jps "key ‚ª’·‚·‚¬‚Ü‚·. key ‚Ì’·‚³‚ÍÅ‘å %d ‚Å‚·",
- est "Võti on liiga pikk. Maksimaalne võtmepikkus on %d"
- fre "La clé est trop longue. Longueur maximale: %d"
- ger "Schlüssel ist zu lang. Die maximale Schlüssellänge beträgt %d"
- greek "Ôï êëåéäß ðïõ ïñßóèçêå åßíáé ðïëý ìåãÜëï. Ôï ìÝãéóôï ìÞêïò åßíáé %d"
- hun "A megadott kulcs tul hosszu. Maximalis kulcshosszusag: %d"
- ita "La chiave specificata e` troppo lunga. La max lunghezza della chiave e` %d"
- jpn "key ¤¬Ä¹¤¹¤®¤Þ¤¹. key ¤ÎŤµ¤ÏºÇÂç %d ¤Ç¤¹"
- kor "Á¤ÀÇµÈ Å°°¡ ³Ê¹« ±é´Ï´Ù. ÃÖ´ë Å°ÀÇ ±æÀÌ´Â %dÀÔ´Ï´Ù."
- nor "Spesifisert nøkkel var for lang. Maks nøkkellengde er is %d"
- norwegian-ny "Spesifisert nykkel var for lang. Maks nykkellengde er %d"
- pol "Zdefinowany klucz jest zbyt d³ugi. Maksymaln? d³ugo?ci? klucza jest %d"
- por "Chave especificada longa demais. O comprimento de chave máximo permitido é %d"
- rum "Cheia specificata este prea lunga. Marimea maxima a unei chei este de %d"
- rus "õËÁÚÁÎ ÓÌÉÛËÏÍ ÄÌÉÎÎÙÊ ËÌÀÞ. íÁËÓÉÍÁÌØÎÁÑ ÄÌÉÎÁ ËÌÀÞÁ ÓÏÓÔÁ×ÌÑÅÔ %d ÂÁÊÔ"
- serbian "Navedeni kljuè je predug. Maksimalna dužina kljuèa je %d"
- slo "Zadaný kµúè je príli¹ dlhý, najväè¹ia då¾ka kµúèa je %d"
- spa "Declaracion de clave demasiado larga. La maxima longitud de clave es %d"
- swe "För lång nyckel. Högsta tillåtna nyckellängd är %d"
- ukr "úÁÚÎÁÞÅÎÉÊ ËÌÀÞ ÚÁÄÏ×ÇÉÊ. îÁʦÌØÛÁ ÄÏ×ÖÉÎÁ ËÌÀÞÁ %d ÂÁÊÔ¦×"
-ER_KEY_COLUMN_DOES_NOT_EXITS 42000 S1009
- cze "Kl-Bíèový sloupec '%-.192s' v tabulce neexistuje"
- dan "Nøglefeltet '%-.192s' eksisterer ikke i tabellen"
- nla "Zoeksleutel kolom '%-.192s' bestaat niet in tabel"
- eng "Key column '%-.192s' doesn't exist in table"
- jps "Key column '%-.192s' ‚ªƒe[ƒuƒ‹‚É‚ ‚è‚Ü‚¹‚ñ.",
- est "Võtme tulp '%-.192s' puudub tabelis"
- fre "La clé '%-.192s' n'existe pas dans la table"
- ger "In der Tabelle gibt es kein Schlüsselfeld '%-.192s'"
- greek "Ôï ðåäßï êëåéäß '%-.192s' äåí õðÜñ÷åé óôïí ðßíáêá"
- hun "A(z) '%-.192s'kulcsoszlop nem letezik a tablaban"
- ita "La colonna chiave '%-.192s' non esiste nella tabella"
- jpn "Key column '%-.192s' ¤¬¥Æ¡¼¥Ö¥ë¤Ë¤¢¤ê¤Þ¤»¤ó."
- kor "Key Ä®·³ '%-.192s'´Â Å×ÀÌºí¿¡ Á¸ÀçÇÏÁö ¾Ê½À´Ï´Ù."
- nor "Nøkkel felt '%-.192s' eksiterer ikke i tabellen"
- norwegian-ny "Nykkel kolonne '%-.192s' eksiterar ikkje i tabellen"
- pol "Kolumna '%-.192s' zdefiniowana w kluczu nie istnieje w tabeli"
- por "Coluna chave '%-.192s' não existe na tabela"
- rum "Coloana cheie '%-.192s' nu exista in tabela"
- rus "ëÌÀÞÅ×ÏÊ ÓÔÏÌÂÅà '%-.192s' × ÔÁÂÌÉÃÅ ÎÅ ÓÕÝÅÓÔ×ÕÅÔ"
- serbian "Kljuèna kolona '%-.192s' ne postoji u tabeli"
- slo "Kµúèový ståpec '%-.192s' v tabuµke neexistuje"
- spa "La columna clave '%-.192s' no existe en la tabla"
- swe "Nyckelkolumn '%-.192s' finns inte"
- ukr "ëÌÀÞÏ×ÉÊ ÓÔÏ×ÂÅÃØ '%-.192s' ÎÅ ¦ÓÎÕ¤ Õ ÔÁÂÌÉæ"
-ER_BLOB_USED_AS_KEY 42000 S1009
- cze "Blob sloupec '%-.192s' nem-Bù¾e být pou¾it jako klíè"
- dan "BLOB feltet '%-.192s' kan ikke bruges ved specifikation af indeks"
- nla "BLOB kolom '%-.192s' kan niet gebruikt worden bij zoeksleutel specificatie"
- eng "BLOB column '%-.192s' can't be used in key specification with the used table type"
- est "BLOB-tüüpi tulpa '%-.192s' ei saa kasutada võtmena"
- fre "Champ BLOB '%-.192s' ne peut être utilisé dans une clé"
- ger "BLOB-Feld '%-.192s' kann beim verwendeten Tabellentyp nicht als Schlüssel verwendet werden"
- greek "Ðåäßï ôýðïõ Blob '%-.192s' äåí ìðïñåß íá ÷ñçóéìïðïéçèåß óôïí ïñéóìü åíüò êëåéäéïý (key specification)"
- hun "Blob objektum '%-.192s' nem hasznalhato kulcskent"
- ita "La colonna BLOB '%-.192s' non puo` essere usata nella specifica della chiave"
- kor "BLOB Ä®·³ '%-.192s'´Â Å° Á¤ÀÇ¿¡¼­ »ç¿ëµÉ ¼ö ¾ø½À´Ï´Ù."
- nor "Blob felt '%-.192s' kan ikke brukes ved spesifikasjon av nøkler"
- norwegian-ny "Blob kolonne '%-.192s' kan ikkje brukast ved spesifikasjon av nyklar"
- pol "Kolumna typu Blob '%-.192s' nie mo¿e byæ u¿yta w specyfikacji klucza"
- por "Coluna BLOB '%-.192s' não pode ser utilizada na especificação de chave para o tipo de tabela usado"
- rum "Coloana de tip BLOB '%-.192s' nu poate fi folosita in specificarea cheii cu tipul de tabla folosit"
- rus "óÔÏÌÂÅà ÔÉÐÁ BLOB '%-.192s' ÎÅ ÍÏÖÅÔ ÂÙÔØ ÉÓÐÏÌØÚÏ×ÁÎ ËÁË ÚÎÁÞÅÎÉÅ ËÌÀÞÁ × ÔÁÂÌÉÃÅ ÔÁËÏÇÏ ÔÉÐÁ"
- serbian "BLOB kolona '%-.192s' ne može biti upotrebljena za navoðenje kljuèa sa tipom tabele koji se trenutno koristi"
- slo "Blob pole '%-.192s' nemô¾e by» pou¾ité ako kµúè"
- spa "La columna Blob '%-.192s' no puede ser usada en una declaracion de clave"
- swe "En BLOB '%-.192s' kan inte vara nyckel med den använda tabelltypen"
- ukr "BLOB ÓÔÏ×ÂÅÃØ '%-.192s' ÎÅ ÍÏÖÅ ÂÕÔÉ ×ÉËÏÒÉÓÔÁÎÉÊ Õ ×ÉÚÎÁÞÅÎΦ ËÌÀÞÁ × ÃØÏÍÕ ÔÉЦ ÔÁÂÌÉæ"
-ER_TOO_BIG_FIELDLENGTH 42000 S1009
- cze "P-Bøíli¹ velká délka sloupce '%-.192s' (nejvíce %lu). Pou¾ijte BLOB"
- dan "For stor feltlængde for kolonne '%-.192s' (maks = %lu). Brug BLOB i stedet"
- nla "Te grote kolomlengte voor '%-.192s' (max = %lu). Maak hiervoor gebruik van het type BLOB"
- eng "Column length too big for column '%-.192s' (max = %lu); use BLOB or TEXT instead"
- jps "column '%-.192s' ‚Í,Šm•Û‚·‚é column ‚Ì‘å‚«‚³‚ª‘½‚·‚¬‚Ü‚·. (Å‘å %lu ‚Ü‚Å). BLOB ‚ð‚©‚í‚è‚ÉŽg—p‚µ‚Ä‚­‚¾‚³‚¢.",
- est "Tulba '%-.192s' pikkus on liiga pikk (maksimaalne pikkus: %lu). Kasuta BLOB väljatüüpi"
- fre "Champ '%-.192s' trop long (max = %lu). Utilisez un BLOB"
- ger "Feldlänge für Feld '%-.192s' zu groß (maximal %lu). BLOB- oder TEXT-Spaltentyp verwenden!"
- greek "Ðïëý ìåãÜëï ìÞêïò ãéá ôï ðåäßï '%-.192s' (max = %lu). Ðáñáêáëþ ÷ñçóéìïðïéåßóôå ôïí ôýðï BLOB"
- hun "A(z) '%-.192s' oszlop tul hosszu. (maximum = %lu). Hasznaljon BLOB tipust inkabb."
- ita "La colonna '%-.192s' e` troppo grande (max=%lu). Utilizza un BLOB."
- jpn "column '%-.192s' ¤Ï,³ÎÊݤ¹¤ë column ¤ÎÂ礭¤µ¤¬Â¿¤¹¤®¤Þ¤¹. (ºÇÂç %lu ¤Þ¤Ç). BLOB ¤ò¤«¤ï¤ê¤Ë»ÈÍѤ·¤Æ¤¯¤À¤µ¤¤."
- kor "Ä®·³ '%-.192s'ÀÇ Ä®·³ ±æÀÌ°¡ ³Ê¹« ±é´Ï´Ù (ÃÖ´ë = %lu). ´ë½Å¿¡ BLOB¸¦ »ç¿ëÇϼ¼¿ä."
- nor "For stor nøkkellengde for kolonne '%-.192s' (maks = %lu). Bruk BLOB istedenfor"
- norwegian-ny "For stor nykkellengde for felt '%-.192s' (maks = %lu). Bruk BLOB istadenfor"
- pol "Zbyt du¿a d³ugo?æ kolumny '%-.192s' (maks. = %lu). W zamian u¿yj typu BLOB"
- por "Comprimento da coluna '%-.192s' grande demais (max = %lu); use BLOB em seu lugar"
- rum "Lungimea coloanei '%-.192s' este prea lunga (maximum = %lu). Foloseste BLOB mai bine"
- rus "óÌÉÛËÏÍ ÂÏÌØÛÁÑ ÄÌÉÎÁ ÓÔÏÌÂÃÁ '%-.192s' (ÍÁËÓÉÍÕÍ = %lu). éÓÐÏÌØÚÕÊÔÅ ÔÉÐ BLOB ÉÌÉ TEXT ×ÍÅÓÔÏ ÔÅËÕÝÅÇÏ"
- serbian "Previše podataka za kolonu '%-.192s' (maksimum je %lu). Upotrebite BLOB polje"
- slo "Príli¹ veµká då¾ka pre pole '%-.192s' (maximum = %lu). Pou¾ite BLOB"
- spa "Longitud de columna demasiado grande para la columna '%-.192s' (maximo = %lu).Usar BLOB en su lugar"
- swe "För stor kolumnlängd angiven för '%-.192s' (max= %lu). Använd en BLOB instället"
- ukr "úÁÄÏ×ÇÁ ÄÏ×ÖÉÎÁ ÓÔÏ×ÂÃÑ '%-.192s' (max = %lu). ÷ÉËÏÒÉÓÔÁÊÔÅ ÔÉÐ BLOB"
-ER_WRONG_AUTO_KEY 42000 S1009
- cze "M-Bù¾ete mít pouze jedno AUTO pole a to musí být definováno jako klíè"
- dan "Der kan kun specificeres eet AUTO_INCREMENT-felt, og det skal være indekseret"
- nla "Er kan slechts 1 autofield zijn en deze moet als zoeksleutel worden gedefinieerd."
- eng "Incorrect table definition; there can be only one auto column and it must be defined as a key"
- jps "ƒe[ƒuƒ‹‚Ì’è‹`‚ªˆá‚¢‚Ü‚·; there can be only one auto column and it must be defined as a key",
- est "Vigane tabelikirjeldus; Tabelis tohib olla üks auto_increment tüüpi tulp ning see peab olema defineeritud võtmena"
- fre "Un seul champ automatique est permis et il doit être indexé"
- ger "Falsche Tabellendefinition. Es darf nur eine AUTO_INCREMENT-Spalte geben, und diese muss als Schlüssel definiert werden"
- greek "Ìðïñåß íá õðÜñ÷åé ìüíï Ýíá auto field êáé ðñÝðåé íá Ý÷åé ïñéóèåß óáí key"
- hun "Csak egy auto mezo lehetseges, es azt kulcskent kell definialni."
- ita "Puo` esserci solo un campo AUTO e deve essere definito come chiave"
- jpn "¥Æ¡¼¥Ö¥ë¤ÎÄêµÁ¤¬°ã¤¤¤Þ¤¹; there can be only one auto column and it must be defined as a key"
- kor "ºÎÁ¤È®ÇÑ Å×À̺í Á¤ÀÇ; Å×À̺íÀº ÇϳªÀÇ auto Ä®·³ÀÌ Á¸ÀçÇÏ°í Å°·Î Á¤ÀǵǾîÁ®¾ß ÇÕ´Ï´Ù."
- nor "Bare ett auto felt kan være definert som nøkkel."
- norwegian-ny "Bare eitt auto felt kan være definert som nøkkel."
- pol "W tabeli mo¿e byæ tylko jedno pole auto i musi ono byæ zdefiniowane jako klucz"
- por "Definição incorreta de tabela. Somente é permitido um único campo auto-incrementado e ele tem que ser definido como chave"
- rum "Definitia tabelei este incorecta; Nu pot fi mai mult de o singura coloana de tip auto si aceasta trebuie definita ca cheie"
- rus "îÅËÏÒÒÅËÔÎÏÅ ÏÐÒÅÄÅÌÅÎÉÅ ÔÁÂÌÉÃÙ: ÍÏÖÅÔ ÓÕÝÅÓÔ×Ï×ÁÔØ ÔÏÌØËÏ ÏÄÉÎ Á×ÔÏÉÎËÒÅÍÅÎÔÎÙÊ ÓÔÏÌÂÅÃ, É ÏÎ ÄÏÌÖÅÎ ÂÙÔØ ÏÐÒÅÄÅÌÅÎ ËÁË ËÌÀÞ"
- serbian "Pogrešna definicija tabele; U tabeli može postojati samo jedna 'AUTO' kolona i ona mora biti istovremeno definisana kao kolona kljuèa"
- slo "Mô¾ete ma» iba jedno AUTO pole a to musí by» definované ako kµúè"
- spa "Puede ser solamente un campo automatico y este debe ser definido como una clave"
- swe "Det får finnas endast ett AUTO_INCREMENT-fält och detta måste vara en nyckel"
- ukr "îÅצÒÎÅ ×ÉÚÎÁÞÅÎÎÑ ÔÁÂÌÉæ; íÏÖÅ ÂÕÔÉ ÌÉÛÅ ÏÄÉÎ Á×ÔÏÍÁÔÉÞÎÉÊ ÓÔÏ×ÂÅÃØ, ÝÏ ÐÏ×ÉÎÅÎ ÂÕÔÉ ×ÉÚÎÁÞÅÎÉÊ ÑË ËÌÀÞ"
-ER_READY
- cze "%s: p-Bøipraven na spojení\nVersion: '%s' socket: '%s' port: %d""
- dan "%s: klar til tilslutninger\nVersion: '%s' socket: '%s' port: %d""
- nla "%s: klaar voor verbindingen\nVersion: '%s' socket: '%s' port: %d""
- eng "%s: ready for connections.\nVersion: '%s' socket: '%s' port: %d"
- jps "%s: €”õŠ®—¹\nVersion: '%s' socket: '%s' port: %d"",
- est "%s: ootab ühendusi\nVersion: '%s' socket: '%s' port: %d""
- fre "%s: Prêt pour des connexions\nVersion: '%s' socket: '%s' port: %d""
- ger "%s: Bereit für Verbindungen.\nVersion: '%s' Socket: '%s' Port: %d"
- greek "%s: óå áíáìïíÞ óõíäÝóåùí\nVersion: '%s' socket: '%s' port: %d""
- hun "%s: kapcsolatra kesz\nVersion: '%s' socket: '%s' port: %d""
- ita "%s: Pronto per le connessioni\nVersion: '%s' socket: '%s' port: %d""
- jpn "%s: ½àÈ÷´°Î»\nVersion: '%s' socket: '%s' port: %d""
- kor "%s: ¿¬°á ÁغñÁßÀÔ´Ï´Ù\nVersion: '%s' socket: '%s' port: %d""
- nor "%s: klar for tilkoblinger\nVersion: '%s' socket: '%s' port: %d""
- norwegian-ny "%s: klar for tilkoblingar\nVersion: '%s' socket: '%s' port: %d""
- pol "%s: gotowe do po³?czenia\nVersion: '%s' socket: '%s' port: %d""
- por "%s: Pronto para conexões\nVersion: '%s' socket: '%s' port: %d""
- rum "%s: sint gata pentru conectii\nVersion: '%s' socket: '%s' port: %d""
- rus "%s: çÏÔÏ× ÐÒÉÎÉÍÁÔØ ÓÏÅÄÉÎÅÎÉÑ.\n÷ÅÒÓÉÑ: '%s' ÓÏËÅÔ: '%s' ÐÏÒÔ: %d"
- serbian "%s: Spreman za konekcije\nVersion: '%s' socket: '%s' port: %d""
- slo "%s: pripravený na spojenie\nVersion: '%s' socket: '%s' port: %d""
- spa "%s: preparado para conexiones\nVersion: '%s' socket: '%s' port: %d""
- swe "%s: klar att ta emot klienter\nVersion: '%s' socket: '%s' port: %d""
- ukr "%s: çÏÔÏ×ÉÊ ÄÌÑ Ú'¤ÄÎÁÎØ!\nVersion: '%s' socket: '%s' port: %d""
-ER_NORMAL_SHUTDOWN
- cze "%s: norm-Bální ukonèení\n"
- dan "%s: Normal nedlukning\n"
- nla "%s: Normaal afgesloten \n"
- eng "%s: Normal shutdown\n"
- est "%s: MySQL lõpetas\n"
- fre "%s: Arrêt normal du serveur\n"
- ger "%s: Normal heruntergefahren\n"
- greek "%s: ÖõóéïëïãéêÞ äéáäéêáóßá shutdown\n"
- hun "%s: Normal leallitas\n"
- ita "%s: Shutdown normale\n"
- kor "%s: Á¤»óÀûÀÎ shutdown\n"
- nor "%s: Normal avslutning\n"
- norwegian-ny "%s: Normal nedkopling\n"
- pol "%s: Standardowe zakoñczenie dzia³ania\n"
- por "%s: 'Shutdown' normal\n"
- rum "%s: Terminare normala\n"
- rus "%s: ëÏÒÒÅËÔÎÁÑ ÏÓÔÁÎÏ×ËÁ\n"
- serbian "%s: Normalno gašenje\n"
- slo "%s: normálne ukonèenie\n"
- spa "%s: Apagado normal\n"
- swe "%s: Normal avslutning\n"
- ukr "%s: îÏÒÍÁÌØÎÅ ÚÁ×ÅÒÛÅÎÎÑ\n"
-ER_GOT_SIGNAL
- cze "%s: p-Bøijat signal %d, konèím\n"
- dan "%s: Fangede signal %d. Afslutter!!\n"
- nla "%s: Signaal %d. Systeem breekt af!\n"
- eng "%s: Got signal %d. Aborting!\n"
- jps "%s: Got signal %d. ’†’f!\n",
- est "%s: sain signaali %d. Lõpetan!\n"
- fre "%s: Reçu le signal %d. Abandonne!\n"
- ger "%s: Signal %d erhalten. Abbruch!\n"
- greek "%s: ÅëÞöèç ôï ìÞíõìá %d. Ç äéáäéêáóßá åãêáôáëåßðåôáé!\n"
- hun "%s: %d jelzes. Megszakitva!\n"
- ita "%s: Ricevuto segnale %d. Interruzione!\n"
- jpn "%s: Got signal %d. ̾̂!\n"
- kor "%s: %d ½ÅÈ£°¡ µé¾î¿ÔÀ½. ÁßÁö!\n"
- nor "%s: Oppdaget signal %d. Avslutter!\n"
- norwegian-ny "%s: Oppdaga signal %d. Avsluttar!\n"
- pol "%s: Otrzymano sygna³ %d. Koñczenie dzia³ania!\n"
- por "%s: Obteve sinal %d. Abortando!\n"
- rum "%s: Semnal %d obtinut. Aborting!\n"
- rus "%s: ðÏÌÕÞÅÎ ÓÉÇÎÁÌ %d. ðÒÅËÒÁÝÁÅÍ!\n"
- serbian "%s: Dobio signal %d. Prekidam!\n"
- slo "%s: prijatý signál %d, ukonèenie (Abort)!\n"
- spa "%s: Recibiendo signal %d. Abortando!\n"
- swe "%s: Fick signal %d. Avslutar!\n"
- ukr "%s: ïÔÒÉÍÁÎÏ ÓÉÇÎÁÌ %d. ðÅÒÅÒÉ×ÁÀÓØ!\n"
-ER_SHUTDOWN_COMPLETE
- cze "%s: ukon-Bèení práce hotovo\n"
- dan "%s: Server lukket\n"
- nla "%s: Afsluiten afgerond\n"
- eng "%s: Shutdown complete\n"
- jps "%s: Shutdown Š®—¹\n",
- est "%s: Lõpp\n"
- fre "%s: Arrêt du serveur terminé\n"
- ger "%s: Herunterfahren beendet\n"
- greek "%s: Ç äéáäéêáóßá Shutdown ïëïêëçñþèçêå\n"
- hun "%s: A leallitas kesz\n"
- ita "%s: Shutdown completato\n"
- jpn "%s: Shutdown ´°Î»\n"
- kor "%s: Shutdown ÀÌ ¿Ï·áµÊ!\n"
- nor "%s: Avslutning komplett\n"
- norwegian-ny "%s: Nedkopling komplett\n"
- pol "%s: Zakoñczenie dzia³ania wykonane\n"
- por "%s: 'Shutdown' completo\n"
- rum "%s: Terminare completa\n"
- rus "%s: ïÓÔÁÎÏ×ËÁ ÚÁ×ÅÒÛÅÎÁ\n"
- serbian "%s: Gašenje završeno\n"
- slo "%s: práca ukonèená\n"
- spa "%s: Apagado completado\n"
- swe "%s: Avslutning klar\n"
- ukr "%s: òÏÂÏÔÕ ÚÁ×ÅÒÛÅÎÏ\n"
-ER_FORCING_CLOSE 08S01
- cze "%s: n-Básilné uzavøení threadu %ld u¾ivatele '%-.48s'\n"
- dan "%s: Forceret nedlukning af tråd: %ld bruger: '%-.48s'\n"
- nla "%s: Afsluiten afgedwongen van thread %ld gebruiker: '%-.48s'\n"
- eng "%s: Forcing close of thread %ld user: '%-.48s'\n"
- jps "%s: ƒXƒŒƒbƒh %ld ‹­§I—¹ user: '%-.48s'\n",
- est "%s: Sulgen jõuga lõime %ld kasutaja: '%-.48s'\n"
- fre "%s: Arrêt forcé de la tâche (thread) %ld utilisateur: '%-.48s'\n"
- ger "%s: Thread %ld zwangsweise beendet. Benutzer: '%-.48s'\n"
- greek "%s: Ôï thread èá êëåßóåé %ld user: '%-.48s'\n"
- hun "%s: A(z) %ld thread kenyszeritett zarasa. Felhasznalo: '%-.48s'\n"
- ita "%s: Forzata la chiusura del thread %ld utente: '%-.48s'\n"
- jpn "%s: ¥¹¥ì¥Ã¥É %ld ¶¯À©½ªÎ» user: '%-.48s'\n"
- kor "%s: thread %ldÀÇ °­Á¦ Á¾·á user: '%-.48s'\n"
- nor "%s: Påtvinget avslutning av tråd %ld bruker: '%-.48s'\n"
- norwegian-ny "%s: Påtvinga avslutning av tråd %ld brukar: '%-.48s'\n"
- pol "%s: Wymuszenie zamkniêcia w?tku %ld u¿ytkownik: '%-.48s'\n"
- por "%s: Forçando finalização da 'thread' %ld - usuário '%-.48s'\n"
- rum "%s: Terminare fortata a thread-ului %ld utilizatorului: '%-.48s'\n"
- rus "%s: ðÒÉÎÕÄÉÔÅÌØÎÏ ÚÁËÒÙ×ÁÅÍ ÐÏÔÏË %ld ÐÏÌØÚÏ×ÁÔÅÌÑ: '%-.48s'\n"
- serbian "%s: Usiljeno gašenje thread-a %ld koji pripada korisniku: '%-.48s'\n"
- slo "%s: násilné ukonèenie vlákna %ld u¾ívateµa '%-.48s'\n"
- spa "%s: Forzando a cerrar el thread %ld usuario: '%-.48s'\n"
- swe "%s: Stänger av tråd %ld; användare: '%-.48s'\n"
- ukr "%s: ðÒÉÓËÏÒÀÀ ÚÁËÒÉÔÔÑ Ç¦ÌËÉ %ld ËÏÒÉÓÔÕ×ÁÞÁ: '%-.48s'\n"
-ER_IPSOCK_ERROR 08S01
- cze "Nemohu vytvo-Bøit IP socket"
- dan "Kan ikke oprette IP socket"
- nla "Kan IP-socket niet openen"
- eng "Can't create IP socket"
- jps "IP socket ‚ªì‚ê‚Ü‚¹‚ñ",
- est "Ei suuda luua IP socketit"
- fre "Ne peut créer la connexion IP (socket)"
- ger "Kann IP-Socket nicht erzeugen"
- greek "Äåí åßíáé äõíáôÞ ç äçìéïõñãßá IP socket"
- hun "Az IP socket nem hozhato letre"
- ita "Impossibile creare il socket IP"
- jpn "IP socket ¤¬ºî¤ì¤Þ¤»¤ó"
- kor "IP ¼ÒÄÏÀ» ¸¸µéÁö ¸øÇß½À´Ï´Ù."
- nor "Kan ikke opprette IP socket"
- norwegian-ny "Kan ikkje opprette IP socket"
- pol "Nie mo¿na stworzyæ socket'u IP"
- por "Não pode criar o soquete IP"
- rum "Nu pot crea IP socket"
- rus "îÅ×ÏÚÍÏÖÎÏ ÓÏÚÄÁÔØ IP-ÓÏËÅÔ"
- serbian "Ne mogu da kreiram IP socket"
- slo "Nemô¾em vytvori» IP socket"
- spa "No puedo crear IP socket"
- swe "Kan inte skapa IP-socket"
- ukr "îÅ ÍÏÖÕ ÓÔ×ÏÒÉÔÉ IP ÒÏÚ'¤Í"
-ER_NO_SUCH_INDEX 42S12 S1009
- cze "Tabulka '%-.192s' nem-Bá index odpovídající CREATE INDEX. Vytvoøte tabulku znovu"
- dan "Tabellen '%-.192s' har ikke den nøgle, som blev brugt i CREATE INDEX. Genopret tabellen"
- nla "Tabel '%-.192s' heeft geen INDEX zoals deze gemaakt worden met CREATE INDEX. Maak de tabel opnieuw"
- eng "Table '%-.192s' has no index like the one used in CREATE INDEX; recreate the table"
- jps "Table '%-.192s' ‚Í‚»‚̂悤‚È index ‚ðŽ‚Á‚Ä‚¢‚Ü‚¹‚ñ(CREATE INDEX ŽÀsŽž‚ÉŽw’肳‚ê‚Ä‚¢‚Ü‚¹‚ñ). ƒe[ƒuƒ‹‚ðì‚è’¼‚µ‚Ä‚­‚¾‚³‚¢",
- est "Tabelil '%-.192s' puuduvad võtmed. Loo tabel uuesti"
- fre "La table '%-.192s' n'a pas d'index comme celle utilisée dans CREATE INDEX. Recréez la table"
- ger "Tabelle '%-.192s' besitzt keinen wie den in CREATE INDEX verwendeten Index. Tabelle neu anlegen"
- greek "Ï ðßíáêáò '%-.192s' äåí Ý÷åé åõñåôÞñéï (index) óáí áõôü ðïõ ÷ñçóéìïðïéåßôå óôçí CREATE INDEX. Ðáñáêáëþ, îáíáäçìéïõñãÞóôå ôïí ðßíáêá"
- hun "A(z) '%-.192s' tablahoz nincs meg a CREATE INDEX altal hasznalt index. Alakitsa at a tablat"
- ita "La tabella '%-.192s' non ha nessun indice come quello specificatato dalla CREATE INDEX. Ricrea la tabella"
- jpn "Table '%-.192s' ¤Ï¤½¤Î¤è¤¦¤Ê index ¤ò»ý¤Ã¤Æ¤¤¤Þ¤»¤ó(CREATE INDEX ¼Â¹Ô»þ¤Ë»ØÄꤵ¤ì¤Æ¤¤¤Þ¤»¤ó). ¥Æ¡¼¥Ö¥ë¤òºî¤êľ¤·¤Æ¤¯¤À¤µ¤¤"
- kor "Å×À̺í '%-.192s'´Â À妽º¸¦ ¸¸µéÁö ¾Ê¾Ò½À´Ï´Ù. alter Å×À̺í¸í·ÉÀ» ÀÌ¿ëÇÏ¿© Å×À̺íÀ» ¼öÁ¤Çϼ¼¿ä..."
- nor "Tabellen '%-.192s' har ingen index som den som er brukt i CREATE INDEX. Gjenopprett tabellen"
- norwegian-ny "Tabellen '%-.192s' har ingen index som den som er brukt i CREATE INDEX. Oprett tabellen på nytt"
- pol "Tabela '%-.192s' nie ma indeksu takiego jak w CREATE INDEX. Stwórz tabelê"
- por "Tabela '%-.192s' não possui um índice como o usado em CREATE INDEX. Recrie a tabela"
- rum "Tabela '%-.192s' nu are un index ca acela folosit in CREATE INDEX. Re-creeaza tabela"
- rus "÷ ÔÁÂÌÉÃÅ '%-.192s' ÎÅÔ ÔÁËÏÇÏ ÉÎÄÅËÓÁ, ËÁË × CREATE INDEX. óÏÚÄÁÊÔÅ ÔÁÂÌÉÃÕ ÚÁÎÏ×Ï"
- serbian "Tabela '%-.192s' nema isti indeks kao onaj upotrebljen pri komandi 'CREATE INDEX'. Napravite tabelu ponovo"
- slo "Tabuµka '%-.192s' nemá index zodpovedajúci CREATE INDEX. Vytvorte tabulku znova"
- spa "La tabla '%-.192s' no tiene indice como el usado en CREATE INDEX. Crea de nuevo la tabla"
- swe "Tabellen '%-.192s' har inget index som motsvarar det angivna i CREATE INDEX. Skapa om tabellen"
- ukr "ôÁÂÌÉÃÑ '%-.192s' ÍÁ¤ ¦ÎÄÅËÓ, ÝÏ ÎÅ ÓЦ×ÐÁÄÁ¤ Ú ×ËÁÚÁÎÎÉÍ Õ CREATE INDEX. óÔ×ÏÒ¦ÔØ ÔÁÂÌÉÃÀ ÚÎÏ×Õ"
-ER_WRONG_FIELD_TERMINATORS 42000 S1009
- cze "Argument separ-Bátoru polo¾ek nebyl oèekáván. Pøeètìte si manuál"
- dan "Felt adskiller er ikke som forventet, se dokumentationen"
- nla "De argumenten om velden te scheiden zijn anders dan verwacht. Raadpleeg de handleiding"
- eng "Field separator argument is not what is expected; check the manual"
- est "Väljade eraldaja erineb oodatust. Tutvu kasutajajuhendiga"
- fre "Séparateur de champs inconnu. Vérifiez dans le manuel"
- ger "Feldbegrenzer-Argument ist nicht in der erwarteten Form. Bitte im Handbuch nachlesen"
- greek "Ï äéá÷ùñéóôÞò ðåäßùí äåí åßíáé áõôüò ðïõ áíáìåíüôáí. Ðáñáêáëþ áíáôñÝîôå óôï manual"
- hun "A mezoelvalaszto argumentumok nem egyeznek meg a varttal. Nezze meg a kezikonyvben!"
- ita "L'argomento 'Field separator' non e` quello atteso. Controlla il manuale"
- kor "ÇÊµå ±¸ºÐÀÚ ÀμöµéÀÌ ¿ÏÀüÇÏÁö ¾Ê½À´Ï´Ù. ¸Þ´º¾óÀ» ã¾Æ º¸¼¼¿ä."
- nor "Felt skiller argumentene er ikke som forventet, se dokumentasjonen"
- norwegian-ny "Felt skiljer argumenta er ikkje som venta, sjå dokumentasjonen"
- pol "Nie oczekiwano separatora. Sprawd¥ podrêcznik"
- por "Argumento separador de campos não é o esperado. Cheque o manual"
- rum "Argumentul pentru separatorul de cimpuri este diferit de ce ma asteptam. Verifica manualul"
- rus "áÒÇÕÍÅÎÔ ÒÁÚÄÅÌÉÔÅÌÑ ÐÏÌÅÊ - ÎÅ ÔÏÔ, ËÏÔÏÒÙÊ ÏÖÉÄÁÌÓÑ. ïÂÒÁÝÁÊÔÅÓØ Ë ÄÏËÕÍÅÎÔÁÃÉÉ"
- serbian "Argument separatora polja nije ono što se oèekivalo. Proverite uputstvo MySQL server-a"
- slo "Argument oddeµovaè polí nezodpovedá po¾iadavkám. Skontrolujte v manuáli"
- spa "Los separadores de argumentos del campo no son los especificados. Comprueba el manual"
- swe "Fältseparatorerna är vad som förväntades. Kontrollera mot manualen"
- ukr "èÉÂÎÉÊ ÒÏÚĦÌÀ×ÁÞ ÐÏ̦×. ðÏÞÉÔÁÊÔÅ ÄÏËÕÍÅÎÔÁæÀ"
-ER_BLOBS_AND_NO_TERMINATED 42000 S1009
- cze "Nen-Bí mo¾né pou¾ít pevný rowlength s BLOBem. Pou¾ijte 'fields terminated by'."
- dan "Man kan ikke bruge faste feltlængder med BLOB. Brug i stedet 'fields terminated by'."
- nla "Bij het gebruik van BLOBs is het niet mogelijk om vaste rijlengte te gebruiken. Maak s.v.p. gebruik van 'fields terminated by'."
- eng "You can't use fixed rowlength with BLOBs; please use 'fields terminated by'"
- est "BLOB-tüüpi väljade olemasolul ei saa kasutada fikseeritud väljapikkust. Vajalik 'fields terminated by' määrang."
- fre "Vous ne pouvez utiliser des lignes de longueur fixe avec des BLOBs. Utiliser 'fields terminated by'."
- ger "Eine feste Zeilenlänge kann für BLOB-Felder nicht verwendet werden. Bitte 'fields terminated by' verwenden"
- greek "Äåí ìðïñåßôå íá ÷ñçóéìïðïéÞóåôå fixed rowlength óå BLOBs. Ðáñáêáëþ ÷ñçóéìïðïéåßóôå 'fields terminated by'."
- hun "Fix hosszusagu BLOB-ok nem hasznalhatok. Hasznalja a 'mezoelvalaszto jelet' ."
- ita "Non possono essere usate righe a lunghezza fissa con i BLOB. Usa 'FIELDS TERMINATED BY'."
- jpn "You can't use fixed rowlength with BLOBs; please use 'fields terminated by'."
- kor "BLOB·Î´Â °íÁ¤±æÀÌÀÇ lowlength¸¦ »ç¿ëÇÒ ¼ö ¾ø½À´Ï´Ù. 'fields terminated by'¸¦ »ç¿ëÇϼ¼¿ä."
- nor "En kan ikke bruke faste feltlengder med BLOB. Vennlisgt bruk 'fields terminated by'."
- norwegian-ny "Ein kan ikkje bruke faste feltlengder med BLOB. Vennlisgt bruk 'fields terminated by'."
- pol "Nie mo¿na u¿yæ sta³ej d³ugo?ci wiersza z polami typu BLOB. U¿yj 'fields terminated by'."
- por "Você não pode usar comprimento de linha fixo com BLOBs. Por favor, use campos com comprimento limitado."
- rum "Nu poti folosi lungime de cimp fix pentru BLOB-uri. Foloseste 'fields terminated by'."
- rus "æÉËÓÉÒÏ×ÁÎÎÙÊ ÒÁÚÍÅÒ ÚÁÐÉÓÉ Ó ÐÏÌÑÍÉ ÔÉÐÁ BLOB ÉÓÐÏÌØÚÏ×ÁÔØ ÎÅÌØÚÑ, ÐÒÉÍÅÎÑÊÔÅ 'fields terminated by'"
- serbian "Ne možete koristiti fiksnu velièinu sloga kada imate BLOB polja. Molim koristite 'fields terminated by' opciju."
- slo "Nie je mo¾né pou¾i» fixnú då¾ku s BLOBom. Pou¾ite 'fields terminated by'."
- spa "No puedes usar longitudes de filas fijos con BLOBs. Por favor usa 'campos terminados por '."
- swe "Man kan inte använda fast radlängd med blobs. Använd 'fields terminated by'"
- ukr "îÅ ÍÏÖÎÁ ×ÉËÏÒÉÓÔÏ×Õ×ÁÔÉ ÓÔÁÌÕ ÄÏ×ÖÉÎÕ ÓÔÒÏËÉ Ú BLOB. úËÏÒÉÓÔÁÊÔÅÓÑ 'fields terminated by'"
-ER_TEXTFILE_NOT_READABLE
- cze "Soubor '%-.128s' mus-Bí být v adresáøi databáze nebo èitelný pro v¹echny"
- dan "Filen '%-.128s' skal være i database-folderen, eller kunne læses af alle"
- nla "Het bestand '%-.128s' dient in de database directory voor the komen of leesbaar voor iedereen te zijn."
- eng "The file '%-.128s' must be in the database directory or be readable by all"
- jps "ƒtƒ@ƒCƒ‹ '%-.128s' ‚Í databse ‚Ì directory ‚É‚ ‚é‚©‘S‚Ẵ†[ƒU[‚ª“Ç‚ß‚é‚悤‚É‹–‰Â‚³‚ê‚Ä‚¢‚È‚¯‚ê‚΂Ȃè‚Ü‚¹‚ñ.",
- est "Fail '%-.128s' peab asuma andmebaasi kataloogis või olema kõigile loetav"
- fre "Le fichier '%-.128s' doit être dans le répertoire de la base et lisible par tous"
- ger "Datei '%-.128s' muss im Datenbank-Verzeichnis vorhanden oder lesbar für alle sein"
- greek "Ôï áñ÷åßï '%-.128s' ðñÝðåé íá õðÜñ÷åé óôï database directory Þ íá ìðïñåß íá äéáâáóôåß áðü üëïõò"
- hun "A(z) '%-.128s'-nak az adatbazis konyvtarban kell lennie, vagy mindenki szamara olvashatonak"
- ita "Il file '%-.128s' deve essere nella directory del database e deve essere leggibile da tutti"
- jpn "¥Õ¥¡¥¤¥ë '%-.128s' ¤Ï databse ¤Î directory ¤Ë¤¢¤ë¤«Á´¤Æ¤Î¥æ¡¼¥¶¡¼¤¬Æɤá¤ë¤è¤¦¤Ëµö²Ä¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó."
- kor "'%-.128s' È­ÀÏ´Â µ¥ÀÌŸº£À̽º µð·ºÅ丮¿¡ Á¸ÀçÇϰųª ¸ðµÎ¿¡°Ô Àб⠰¡´ÉÇÏ¿©¾ß ÇÕ´Ï´Ù."
- nor "Filen '%-.128s' må være i database-katalogen for å være lesbar for alle"
- norwegian-ny "Filen '%-.128s' må være i database-katalogen for å være lesbar for alle"
- pol "Plik '%-.128s' musi znajdowaæ sie w katalogu bazy danych lub mieæ prawa czytania przez wszystkich"
- por "Arquivo '%-.128s' tem que estar no diretório do banco de dados ou ter leitura possível para todos"
- rum "Fisierul '%-.128s' trebuie sa fie in directorul bazei de data sau trebuie sa poata sa fie citit de catre toata lumea (verifica permisiile)"
- rus "æÁÊÌ '%-.128s' ÄÏÌÖÅÎ ÎÁÈÏÄÉÔØÓÑ × ÔÏÍ ÖÅ ËÁÔÁÌÏÇÅ, ÞÔÏ É ÂÁÚÁ ÄÁÎÎÙÈ, ÉÌÉ ÂÙÔØ ÏÂÝÅÄÏÓÔÕÐÎÙÍ ÄÌÑ ÞÔÅÎÉÑ"
- serbian "File '%-.128s' mora biti u direktorijumu gde su file-ovi baze i mora imati odgovarajuæa prava pristupa"
- slo "Súbor '%-.128s' musí by» v adresári databázy, alebo èitateµný pre v¹etkých"
- spa "El archivo '%-.128s' debe estar en el directorio de la base de datos o ser de lectura por todos"
- swe "Textfilen '%-.128s' måste finnas i databasbiblioteket eller vara läsbar för alla"
- ukr "æÁÊÌ '%-.128s' ÐÏ×ÉÎÅÎ ÂÕÔÉ Õ ÔÅæ ÂÁÚÉ ÄÁÎÎÉÈ ÁÂÏ ÍÁÔÉ ×ÓÔÁÎÏ×ÌÅÎÅ ÐÒÁ×Ï ÎÁ ÞÉÔÁÎÎÑ ÄÌÑ ÕÓ¦È"
-ER_FILE_EXISTS_ERROR
- cze "Soubor '%-.200s' ji-B¾ existuje"
- dan "Filen '%-.200s' eksisterer allerede"
- nla "Het bestand '%-.200s' bestaat reeds"
- eng "File '%-.200s' already exists"
- jps "File '%-.200s' ‚ÍŠù‚É‘¶Ý‚µ‚Ü‚·",
- est "Fail '%-.200s' juba eksisteerib"
- fre "Le fichier '%-.200s' existe déjà"
- ger "Datei '%-.200s' bereits vorhanden"
- greek "Ôï áñ÷åßï '%-.200s' õðÜñ÷åé Þäç"
- hun "A '%-.200s' file mar letezik."
- ita "Il file '%-.200s' esiste gia`"
- jpn "File '%-.200s' ¤Ï´û¤Ë¸ºß¤·¤Þ¤¹"
- kor "'%-.200s' È­ÀÏÀº ÀÌ¹Ì Á¸ÀçÇÕ´Ï´Ù."
- nor "Filen '%-.200s' eksisterte allerede"
- norwegian-ny "Filen '%-.200s' eksisterte allereide"
- pol "Plik '%-.200s' ju¿ istnieje"
- por "Arquivo '%-.200s' já existe"
- rum "Fisierul '%-.200s' exista deja"
- rus "æÁÊÌ '%-.200s' ÕÖÅ ÓÕÝÅÓÔ×ÕÅÔ"
- serbian "File '%-.200s' veæ postoji"
- slo "Súbor '%-.200s' u¾ existuje"
- spa "El archivo '%-.200s' ya existe"
- swe "Filen '%-.200s' existerar redan"
- ukr "æÁÊÌ '%-.200s' ×ÖÅ ¦ÓÎÕ¤"
-ER_LOAD_INFO
- cze "Z-Báznamù: %ld Vymazáno: %ld Pøeskoèeno: %ld Varování: %ld"
- dan "Poster: %ld Fjernet: %ld Sprunget over: %ld Advarsler: %ld"
- nla "Records: %ld Verwijderd: %ld Overgeslagen: %ld Waarschuwingen: %ld"
- eng "Records: %ld Deleted: %ld Skipped: %ld Warnings: %ld"
- jps "ƒŒƒR[ƒh”: %ld íœ: %ld Skipped: %ld Warnings: %ld",
- est "Kirjeid: %ld Kustutatud: %ld Vahele jäetud: %ld Hoiatusi: %ld"
- fre "Enregistrements: %ld Effacés: %ld Non traités: %ld Avertissements: %ld"
- ger "Datensätze: %ld Gelöscht: %ld Ausgelassen: %ld Warnungen: %ld"
- greek "ÅããñáöÝò: %ld ÄéáãñáöÝò: %ld ÐáñåêÜìöèçóáí: %ld ÐñïåéäïðïéÞóåéò: %ld"
- hun "Rekordok: %ld Torolve: %ld Skipped: %ld Warnings: %ld"
- ita "Records: %ld Cancellati: %ld Saltati: %ld Avvertimenti: %ld"
- jpn "¥ì¥³¡¼¥É¿ô: %ld ºï½ü: %ld Skipped: %ld Warnings: %ld"
- kor "·¹ÄÚµå: %ld°³ »èÁ¦: %ld°³ ½ºÅµ: %ld°³ °æ°í: %ld°³"
- nor "Poster: %ld Fjernet: %ld Hoppet over: %ld Advarsler: %ld"
- norwegian-ny "Poster: %ld Fjerna: %ld Hoppa over: %ld Åtvaringar: %ld"
- pol "Recordów: %ld Usuniêtych: %ld Pominiêtych: %ld Ostrze¿eñ: %ld"
- por "Registros: %ld - Deletados: %ld - Ignorados: %ld - Avisos: %ld"
- rum "Recorduri: %ld Sterse: %ld Sarite (skipped): %ld Atentionari (warnings): %ld"
- rus "úÁÐÉÓÅÊ: %ld õÄÁÌÅÎÏ: %ld ðÒÏÐÕÝÅÎÏ: %ld ðÒÅÄÕÐÒÅÖÄÅÎÉÊ: %ld"
- serbian "Slogova: %ld Izbrisano: %ld Preskoèeno: %ld Upozorenja: %ld"
- slo "Záznamov: %ld Zmazaných: %ld Preskoèených: %ld Varovania: %ld"
- spa "Registros: %ld Borrados: %ld Saltados: %ld Peligros: %ld"
- swe "Rader: %ld Bortagna: %ld Dubletter: %ld Varningar: %ld"
- ukr "úÁÐÉÓ¦×: %ld ÷ÉÄÁÌÅÎÏ: %ld ðÒÏÐÕÝÅÎÏ: %ld úÁÓÔÅÒÅÖÅÎØ: %ld"
-ER_ALTER_INFO
- cze "Z-Báznamù: %ld Zdvojených: %ld"
- dan "Poster: %ld Ens: %ld"
- nla "Records: %ld Dubbel: %ld"
- eng "Records: %ld Duplicates: %ld"
- jps "ƒŒƒR[ƒh”: %ld d•¡: %ld",
- est "Kirjeid: %ld Kattuvaid: %ld"
- fre "Enregistrements: %ld Doublons: %ld"
- ger "Datensätze: %ld Duplikate: %ld"
- greek "ÅããñáöÝò: %ld ÅðáíáëÞøåéò: %ld"
- hun "Rekordok: %ld Duplikalva: %ld"
- ita "Records: %ld Duplicati: %ld"
- jpn "¥ì¥³¡¼¥É¿ô: %ld ½ÅÊ£: %ld"
- kor "·¹ÄÚµå: %ld°³ Áߺ¹: %ld°³"
- nor "Poster: %ld Like: %ld"
- norwegian-ny "Poster: %ld Like: %ld"
- pol "Rekordów: %ld Duplikatów: %ld"
- por "Registros: %ld - Duplicados: %ld"
- rum "Recorduri: %ld Duplicate: %ld"
- rus "úÁÐÉÓÅÊ: %ld äÕÂÌÉËÁÔÏ×: %ld"
- serbian "Slogova: %ld Duplikata: %ld"
- slo "Záznamov: %ld Opakovaných: %ld"
- spa "Registros: %ld Duplicados: %ld"
- swe "Rader: %ld Dubletter: %ld"
- ukr "úÁÐÉÓ¦×: %ld äÕÂ̦ËÁÔ¦×: %ld"
-ER_WRONG_SUB_KEY
- cze "Chybn-Bá podèást klíèe -- není to øetìzec nebo je del¹í ne¾ délka èásti klíèe"
- dan "Forkert indeksdel. Den anvendte nøgledel er ikke en streng eller længden er større end nøglelængden"
- nla "Foutief sub-gedeelte van de zoeksleutel. De gebruikte zoeksleutel is geen onderdeel van een string of of de gebruikte lengte is langer dan de zoeksleutel"
- eng "Incorrect prefix key; the used key part isn't a string, the used length is longer than the key part, or the storage engine doesn't support unique prefix keys"
- est "Vigane võtme osa. Kasutatud võtmeosa ei ole string tüüpi, määratud pikkus on pikem kui võtmeosa või tabelihandler ei toeta seda tüüpi võtmeid"
- fre "Mauvaise sous-clef. Ce n'est pas un 'string' ou la longueur dépasse celle définie dans la clef"
- ger "Falscher Unterteilschlüssel. Der verwendete Schlüsselteil ist entweder kein String, die verwendete Länge ist länger als der Teilschlüssel oder die Speicher-Engine unterstützt keine Unterteilschlüssel"
- greek "ÅóöáëìÝíï sub part key. Ôï ÷ñçóéìïðïéïýìåíï key part äåí åßíáé string Þ ôï ìÞêïò ôïõ åßíáé ìåãáëýôåñï"
- hun "Rossz alkulcs. A hasznalt kulcsresz nem karaktersorozat vagy hosszabb, mint a kulcsresz"
- ita "Sotto-parte della chiave errata. La parte di chiave utilizzata non e` una stringa o la lunghezza e` maggiore della parte di chiave."
- jpn "Incorrect prefix key; the used key part isn't a string or the used length is longer than the key part"
- kor "ºÎÁ¤È®ÇÑ ¼­¹ö ÆÄÆ® Å°. »ç¿ëµÈ Å° ÆÄÆ®°¡ ½ºÆ®¸µÀÌ ¾Æ´Ï°Å³ª Å° ÆÄÆ®ÀÇ ±æÀÌ°¡ ³Ê¹« ±é´Ï´Ù."
- nor "Feil delnøkkel. Den brukte delnøkkelen er ikke en streng eller den oppgitte lengde er lengre enn nøkkel lengden"
- norwegian-ny "Feil delnykkel. Den brukte delnykkelen er ikkje ein streng eller den oppgitte lengda er lengre enn nykkellengden"
- pol "B³êdna podczê?æ klucza. U¿yta czê?æ klucza nie jest ³añcuchem lub u¿yta d³ugo?æ jest wiêksza ni¿ czê?æ klucza"
- por "Sub parte da chave incorreta. A parte da chave usada não é uma 'string' ou o comprimento usado é maior que parte da chave ou o manipulador de tabelas não suporta sub chaves únicas"
- rum "Componentul cheii este incorrect. Componentul folosit al cheii nu este un sir sau lungimea folosita este mai lunga decit lungimea cheii"
- rus "îÅËÏÒÒÅËÔÎÁÑ ÞÁÓÔØ ËÌÀÞÁ. éÓÐÏÌØÚÕÅÍÁÑ ÞÁÓÔØ ËÌÀÞÁ ÎÅ Ñ×ÌÑÅÔÓÑ ÓÔÒÏËÏÊ, ÕËÁÚÁÎÎÁÑ ÄÌÉÎÁ ÂÏÌØÛÅ, ÞÅÍ ÄÌÉÎÁ ÞÁÓÔÉ ËÌÀÞÁ, ÉÌÉ ÏÂÒÁÂÏÔÞÉË ÔÁÂÌÉÃÙ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÕÎÉËÁÌØÎÙÅ ÞÁÓÔÉ ËÌÀÞÁ"
- serbian "Pogrešan pod-kljuè dela kljuèa. Upotrebljeni deo kljuèa nije string, upotrebljena dužina je veæa od dela kljuèa ili handler tabela ne podržava jedinstvene pod-kljuèeve"
- slo "Incorrect prefix key; the used key part isn't a string or the used length is longer than the key part"
- spa "Parte de la clave es erronea. Una parte de la clave no es una cadena o la longitud usada es tan grande como la parte de la clave"
- swe "Felaktig delnyckel. Nyckeldelen är inte en sträng eller den angivna längden är längre än kolumnlängden"
- ukr "îÅצÒÎÁ ÞÁÓÔÉÎÁ ËÌÀÞÁ. ÷ÉËÏÒÉÓÔÁÎÁ ÞÁÓÔÉÎÁ ËÌÀÞÁ ÎÅ ¤ ÓÔÒÏËÏÀ, ÚÁÄÏ×ÇÁ ÁÂÏ ×ËÁÚ¦×ÎÉË ÔÁÂÌÉæ ΊЦÄÔÒÉÍÕ¤ ÕΦËÁÌØÎÉÈ ÞÁÓÔÉÎ ËÌÀÞÅÊ"
-ER_CANT_REMOVE_ALL_FIELDS 42000
- cze "Nen-Bí mo¾né vymazat v¹echny polo¾ky s ALTER TABLE. Pou¾ijte DROP TABLE"
- dan "Man kan ikke slette alle felter med ALTER TABLE. Brug DROP TABLE i stedet."
- nla "Het is niet mogelijk alle velden te verwijderen met ALTER TABLE. Gebruik a.u.b. DROP TABLE hiervoor!"
- eng "You can't delete all columns with ALTER TABLE; use DROP TABLE instead"
- jps "ALTER TABLE ‚Å‘S‚Ä‚Ì column ‚Í휂ł«‚Ü‚¹‚ñ. DROP TABLE ‚ðŽg—p‚µ‚Ä‚­‚¾‚³‚¢",
- est "ALTER TABLE kasutades ei saa kustutada kõiki tulpasid. Kustuta tabel DROP TABLE abil"
- fre "Vous ne pouvez effacer tous les champs avec ALTER TABLE. Utilisez DROP TABLE"
- ger "Mit ALTER TABLE können nicht alle Felder auf einmal gelöscht werden. Dafür DROP TABLE verwenden"
- greek "Äåí åßíáé äõíáôÞ ç äéáãñáöÞ üëùí ôùí ðåäßùí ìå ALTER TABLE. Ðáñáêáëþ ÷ñçóéìïðïéåßóôå DROP TABLE"
- hun "Az osszes mezo nem torolheto az ALTER TABLE-lel. Hasznalja a DROP TABLE-t helyette"
- ita "Non si possono cancellare tutti i campi con una ALTER TABLE. Utilizzare DROP TABLE"
- jpn "ALTER TABLE ¤ÇÁ´¤Æ¤Î column ¤Ïºï½ü¤Ç¤­¤Þ¤»¤ó. DROP TABLE ¤ò»ÈÍѤ·¤Æ¤¯¤À¤µ¤¤"
- kor "ALTER TABLE ¸í·ÉÀ¸·Î´Â ¸ðµç Ä®·³À» Áö¿ï ¼ö ¾ø½À´Ï´Ù. DROP TABLE ¸í·ÉÀ» ÀÌ¿ëÇϼ¼¿ä."
- nor "En kan ikke slette alle felt med ALTER TABLE. Bruk DROP TABLE isteden."
- norwegian-ny "Ein kan ikkje slette alle felt med ALTER TABLE. Bruk DROP TABLE istadenfor."
- pol "Nie mo¿na usun?æ wszystkich pól wykorzystuj?c ALTER TABLE. W zamian u¿yj DROP TABLE"
- por "Você não pode deletar todas as colunas com ALTER TABLE; use DROP TABLE em seu lugar"
- rum "Nu poti sterge toate coloanele cu ALTER TABLE. Foloseste DROP TABLE in schimb"
- rus "îÅÌØÚÑ ÕÄÁÌÉÔØ ×ÓÅ ÓÔÏÌÂÃÙ Ó ÐÏÍÏÝØÀ ALTER TABLE. éÓÐÏÌØÚÕÊÔÅ DROP TABLE"
- serbian "Ne možete da izbrišete sve kolone pomoæu komande 'ALTER TABLE'. Upotrebite komandu 'DROP TABLE' ako želite to da uradite"
- slo "One nemô¾em zmaza» all fields with ALTER TABLE; use DROP TABLE instead"
- spa "No puede borrar todos los campos con ALTER TABLE. Usa DROP TABLE para hacerlo"
- swe "Man kan inte radera alla fält med ALTER TABLE. Använd DROP TABLE istället"
- ukr "îÅ ÍÏÖÌÉ×Ï ×ÉÄÁÌÉÔÉ ×Ó¦ ÓÔÏ×Âæ ÚÁ ÄÏÐÏÍÏÇÏÀ ALTER TABLE. äÌÑ ÃØÏÇÏ ÓËÏÒÉÓÔÁÊÔÅÓÑ DROP TABLE"
-ER_CANT_DROP_FIELD_OR_KEY 42000
- cze "Nemohu zru-B¹it '%-.192s' (provést DROP). Zkontrolujte, zda neexistují záznamy/klíèe"
- dan "Kan ikke udføre DROP '%-.192s'. Undersøg om feltet/nøglen eksisterer."
- nla "Kan '%-.192s' niet weggooien. Controleer of het veld of de zoeksleutel daadwerkelijk bestaat."
- eng "Can't DROP '%-.192s'; check that column/key exists"
- jps "'%-.192s' ‚ð”jŠü‚Å‚«‚Ü‚¹‚ñ‚Å‚µ‚½; check that column/key exists",
- est "Ei suuda kustutada '%-.192s'. Kontrolli kas tulp/võti eksisteerib"
- fre "Ne peut effacer (DROP) '%-.192s'. Vérifiez s'il existe"
- ger "Kann '%-.192s' nicht löschen. Existiert die Spalte oder der Schlüssel?"
- greek "Áäýíáôç ç äéáãñáöÞ (DROP) '%-.192s'. Ðáñáêáëþ åëÝãîôå áí ôï ðåäßï/êëåéäß õðÜñ÷åé"
- hun "A DROP '%-.192s' nem lehetseges. Ellenorizze, hogy a mezo/kulcs letezik-e"
- ita "Impossibile cancellare '%-.192s'. Controllare che il campo chiave esista"
- jpn "'%-.192s' ¤òÇË´þ¤Ç¤­¤Þ¤»¤ó¤Ç¤·¤¿; check that column/key exists"
- kor "'%-.192s'¸¦ DROPÇÒ ¼ö ¾ø½À´Ï´Ù. Ä®·³À̳ª Å°°¡ Á¸ÀçÇÏ´ÂÁö äũÇϼ¼¿ä."
- nor "Kan ikke DROP '%-.192s'. Undersøk om felt/nøkkel eksisterer."
- norwegian-ny "Kan ikkje DROP '%-.192s'. Undersøk om felt/nøkkel eksisterar."
- pol "Nie mo¿na wykonaæ operacji DROP '%-.192s'. Sprawd¥, czy to pole/klucz istnieje"
- por "Não se pode fazer DROP '%-.192s'. Confira se esta coluna/chave existe"
- rum "Nu pot sa DROP '%-.192s'. Verifica daca coloana/cheia exista"
- rus "îÅ×ÏÚÍÏÖÎÏ ÕÄÁÌÉÔØ (DROP) '%-.192s'. õÂÅÄÉÔÅÓØ ÞÔÏ ÓÔÏÌÂÅÃ/ËÌÀÞ ÄÅÊÓÔ×ÉÔÅÌØÎÏ ÓÕÝÅÓÔ×ÕÅÔ"
- serbian "Ne mogu da izvršim komandu drop 'DROP' na '%-.192s'. Proverite da li ta kolona (odnosno kljuè) postoji"
- slo "Nemô¾em zru¹i» (DROP) '%-.192s'. Skontrolujte, èi neexistujú záznamy/kµúèe"
- spa "No puedo ELIMINAR '%-.192s'. compuebe que el campo/clave existe"
- swe "Kan inte ta bort '%-.192s'. Kontrollera att fältet/nyckel finns"
- ukr "îÅ ÍÏÖÕ DROP '%-.192s'. ðÅÒÅצÒÔÅ, ÞÉ ÃÅÊ ÓÔÏ×ÂÅÃØ/ËÌÀÞ ¦ÓÎÕ¤"
-ER_INSERT_INFO
- cze "Z-Báznamù: %ld Zdvojených: %ld Varování: %ld"
- dan "Poster: %ld Ens: %ld Advarsler: %ld"
- nla "Records: %ld Dubbel: %ld Waarschuwing: %ld"
- eng "Records: %ld Duplicates: %ld Warnings: %ld"
- jps "ƒŒƒR[ƒh”: %ld d•¡”: %ld Warnings: %ld",
- est "Kirjeid: %ld Kattuvaid: %ld Hoiatusi: %ld"
- fre "Enregistrements: %ld Doublons: %ld Avertissements: %ld"
- ger "Datensätze: %ld Duplikate: %ld Warnungen: %ld"
- greek "ÅããñáöÝò: %ld ÅðáíáëÞøåéò: %ld ÐñïåéäïðïéÞóåéò: %ld"
- hun "Rekordok: %ld Duplikalva: %ld Warnings: %ld"
- ita "Records: %ld Duplicati: %ld Avvertimenti: %ld"
- jpn "¥ì¥³¡¼¥É¿ô: %ld ½ÅÊ£¿ô: %ld Warnings: %ld"
- kor "·¹ÄÚµå: %ld°³ Áߺ¹: %ld°³ °æ°í: %ld°³"
- nor "Poster: %ld Like: %ld Advarsler: %ld"
- norwegian-ny "Postar: %ld Like: %ld Åtvaringar: %ld"
- pol "Rekordów: %ld Duplikatów: %ld Ostrze¿eñ: %ld"
- por "Registros: %ld - Duplicados: %ld - Avisos: %ld"
- rum "Recorduri: %ld Duplicate: %ld Atentionari (warnings): %ld"
- rus "úÁÐÉÓÅÊ: %ld äÕÂÌÉËÁÔÏ×: %ld ðÒÅÄÕÐÒÅÖÄÅÎÉÊ: %ld"
- serbian "Slogova: %ld Duplikata: %ld Upozorenja: %ld"
- slo "Záznamov: %ld Opakovaných: %ld Varovania: %ld"
- spa "Registros: %ld Duplicados: %ld Peligros: %ld"
- swe "Rader: %ld Dubletter: %ld Varningar: %ld"
- ukr "úÁÐÉÓ¦×: %ld äÕÂ̦ËÁÔ¦×: %ld úÁÓÔÅÒÅÖÅÎØ: %ld"
-ER_UPDATE_TABLE_USED
- eng "You can't specify target table '%-.192s' for update in FROM clause"
- ger "Die Verwendung der zu aktualisierenden Zieltabelle '%-.192s' ist in der FROM-Klausel nicht zulässig."
- rus "îÅ ÄÏÐÕÓËÁÅÔÓÑ ÕËÁÚÁÎÉÅ ÔÁÂÌÉÃÙ '%-.192s' × ÓÐÉÓËÅ ÔÁÂÌÉà FROM ÄÌÑ ×ÎÅÓÅÎÉÑ × ÎÅÅ ÉÚÍÅÎÅÎÉÊ"
- swe "INSERT-table '%-.192s' får inte finnas i FROM tabell-listan"
- ukr "ôÁÂÌÉÃÑ '%-.192s' ÝÏ ÚͦÎÀ¤ÔØÓÑ ÎÅ ÄÏÚ×ÏÌÅÎÁ Õ ÐÅÒÅ̦ËÕ ÔÁÂÌÉÃØ FROM"
-ER_NO_SUCH_THREAD
- cze "Nezn-Bámá identifikace threadu: %lu"
- dan "Ukendt tråd id: %lu"
- nla "Onbekend thread id: %lu"
- eng "Unknown thread id: %lu"
- jps "thread id: %lu ‚Í‚ ‚è‚Ü‚¹‚ñ",
- est "Tundmatu lõim: %lu"
- fre "Numéro de tâche inconnu: %lu"
- ger "Unbekannte Thread-ID: %lu"
- greek "Áãíùóôï thread id: %lu"
- hun "Ervenytelen szal (thread) id: %lu"
- ita "Thread id: %lu sconosciuto"
- jpn "thread id: %lu ¤Ï¤¢¤ê¤Þ¤»¤ó"
- kor "¾Ë¼ö ¾ø´Â ¾²·¹µå id: %lu"
- nor "Ukjent tråd id: %lu"
- norwegian-ny "Ukjent tråd id: %lu"
- pol "Nieznany identyfikator w?tku: %lu"
- por "'Id' de 'thread' %lu desconhecido"
- rum "Id-ul: %lu thread-ului este necunoscut"
- rus "îÅÉÚ×ÅÓÔÎÙÊ ÎÏÍÅÒ ÐÏÔÏËÁ: %lu"
- serbian "Nepoznat thread identifikator: %lu"
- slo "Neznáma identifikácia vlákna: %lu"
- spa "Identificador del thread: %lu desconocido"
- swe "Finns ingen tråd med id %lu"
- ukr "îÅצÄÏÍÉÊ ¦ÄÅÎÔÉƦËÁÔÏÒ Ç¦ÌËÉ: %lu"
-ER_KILL_DENIED_ERROR
- cze "Nejste vlastn-Bíkem threadu %lu"
- dan "Du er ikke ejer af tråden %lu"
- nla "U bent geen bezitter van thread %lu"
- eng "You are not owner of thread %lu"
- jps "thread %lu ‚̃I[ƒi[‚Å‚Í‚ ‚è‚Ü‚¹‚ñ",
- est "Ei ole lõime %lu omanik"
- fre "Vous n'êtes pas propriétaire de la tâche no: %lu"
- ger "Sie sind nicht Eigentümer von Thread %lu"
- greek "Äåí åßóèå owner ôïõ thread %lu"
- hun "A %lu thread-nek mas a tulajdonosa"
- ita "Utente non proprietario del thread %lu"
- jpn "thread %lu ¤Î¥ª¡¼¥Ê¡¼¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó"
- kor "¾²·¹µå(Thread) %luÀÇ ¼ÒÀ¯ÀÚ°¡ ¾Æ´Õ´Ï´Ù."
- nor "Du er ikke eier av tråden %lu"
- norwegian-ny "Du er ikkje eigar av tråd %lu"
- pol "Nie jeste? w³a?cicielem w?tku %lu"
- por "Você não é proprietário da 'thread' %lu"
- rum "Nu sinteti proprietarul threadului %lu"
- rus "÷Ù ÎÅ Ñ×ÌÑÅÔÅÓØ ×ÌÁÄÅÌØÃÅÍ ÐÏÔÏËÁ %lu"
- serbian "Vi niste vlasnik thread-a %lu"
- slo "Nie ste vlastníkom vlákna %lu"
- spa "Tu no eres el propietario del thread%lu"
- swe "Du är inte ägare till tråd %lu"
- ukr "÷É ÎÅ ×ÏÌÏÄÁÒ Ç¦ÌËÉ %lu"
-ER_NO_TABLES_USED
- cze "Nejsou pou-B¾ity ¾ádné tabulky"
- dan "Ingen tabeller i brug"
- nla "Geen tabellen gebruikt."
- eng "No tables used"
- est "Ühtegi tabelit pole kasutusel"
- fre "Aucune table utilisée"
- ger "Keine Tabellen verwendet"
- greek "Äåí ÷ñçóéìïðïéÞèçêáí ðßíáêåò"
- hun "Nincs hasznalt tabla"
- ita "Nessuna tabella usata"
- kor "¾î¶² Å×ÀÌºíµµ »ç¿ëµÇÁö ¾Ê¾Ò½À´Ï´Ù."
- nor "Ingen tabeller i bruk"
- norwegian-ny "Ingen tabellar i bruk"
- pol "Nie ma ¿adej u¿ytej tabeli"
- por "Nenhuma tabela usada"
- rum "Nici o tabela folosita"
- rus "îÉËÁËÉÅ ÔÁÂÌÉÃÙ ÎÅ ÉÓÐÏÌØÚÏ×ÁÎÙ"
- serbian "Nema upotrebljenih tabela"
- slo "Nie je pou¾itá ¾iadna tabuµka"
- spa "No ha tablas usadas"
- swe "Inga tabeller angivna"
- ukr "îÅ ×ÉËÏÒÉÓÔÁÎÏ ÔÁÂÌÉÃØ"
-ER_TOO_BIG_SET
- cze "P-Bøíli¹ mnoho øetìzcù pro sloupec %-.192s a SET"
- dan "For mange tekststrenge til specifikationen af SET i kolonne %-.192s"
- nla "Teveel strings voor kolom %-.192s en SET"
- eng "Too many strings for column %-.192s and SET"
- est "Liiga palju string tulbale %-.192s tüübile SET"
- fre "Trop de chaînes dans la colonne %-.192s avec SET"
- ger "Zu viele Strings für Feld %-.192s und SET angegeben"
- greek "ÐÜñá ðïëëÜ strings ãéá ôï ðåäßï %-.192s êáé SET"
- hun "Tul sok karakter: %-.192s es SET"
- ita "Troppe stringhe per la colonna %-.192s e la SET"
- kor "Ä®·³ %-.192s¿Í SET¿¡¼­ ½ºÆ®¸µÀÌ ³Ê¹« ¸¹½À´Ï´Ù."
- nor "For mange tekststrenger kolonne %-.192s og SET"
- norwegian-ny "For mange tekststrengar felt %-.192s og SET"
- pol "Zbyt wiele ³añcuchów dla kolumny %-.192s i polecenia SET"
- por "'Strings' demais para coluna '%-.192s' e SET"
- rum "Prea multe siruri pentru coloana %-.192s si SET"
- rus "óÌÉÛËÏÍ ÍÎÏÇÏ ÚÎÁÞÅÎÉÊ ÄÌÑ ÓÔÏÌÂÃÁ %-.192s × SET"
- serbian "Previše string-ova za kolonu '%-.192s' i komandu 'SET'"
- slo "Príli¹ mnoho re»azcov pre pole %-.192s a SET"
- spa "Muchas strings para columna %-.192s y SET"
- swe "För många alternativ till kolumn %-.192s för SET"
- ukr "úÁÂÁÇÁÔÏ ÓÔÒÏË ÄÌÑ ÓÔÏ×ÂÃÑ %-.192s ÔÁ SET"
-ER_NO_UNIQUE_LOGFILE
- cze "Nemohu vytvo-Bøit jednoznaèné jméno logovacího souboru %-.200s.(1-999)\n"
- dan "Kan ikke lave unikt log-filnavn %-.200s.(1-999)\n"
- nla "Het is niet mogelijk een unieke naam te maken voor de logfile %-.200s.(1-999)\n"
- eng "Can't generate a unique log-filename %-.200s.(1-999)\n"
- est "Ei suuda luua unikaalset logifaili nime %-.200s.(1-999)\n"
- fre "Ne peut générer un unique nom de journal %-.200s.(1-999)\n"
- ger "Kann keinen eindeutigen Dateinamen für die Logdatei %-.200s(1-999) erzeugen\n"
- greek "Áäýíáôç ç äçìéïõñãßá unique log-filename %-.200s.(1-999)\n"
- hun "Egyedi log-filenev nem generalhato: %-.200s.(1-999)\n"
- ita "Impossibile generare un nome del file log unico %-.200s.(1-999)\n"
- kor "Unique ·Î±×È­ÀÏ '%-.200s'¸¦ ¸¸µé¼ö ¾ø½À´Ï´Ù.(1-999)\n"
- nor "Kan ikke lage unikt loggfilnavn %-.200s.(1-999)\n"
- norwegian-ny "Kan ikkje lage unikt loggfilnavn %-.200s.(1-999)\n"
- pol "Nie mo¿na stworzyæ unikalnej nazwy pliku z logiem %-.200s.(1-999)\n"
- por "Não pode gerar um nome de arquivo de 'log' único '%-.200s'.(1-999)\n"
- rum "Nu pot sa generez un nume de log unic %-.200s.(1-999)\n"
- rus "îÅ×ÏÚÍÏÖÎÏ ÓÏÚÄÁÔØ ÕÎÉËÁÌØÎÏÅ ÉÍÑ ÆÁÊÌÁ ÖÕÒÎÁÌÁ %-.200s.(1-999)\n"
- serbian "Ne mogu da generišem jedinstveno ime log-file-a: '%-.200s.(1-999)'\n"
- slo "Nemô¾em vytvori» unikátne meno log-súboru %-.200s.(1-999)\n"
- spa "No puede crear un unico archivo log %-.200s.(1-999)\n"
- swe "Kan inte generera ett unikt filnamn %-.200s.(1-999)\n"
- ukr "îÅ ÍÏÖÕ ÚÇÅÎÅÒÕ×ÁÔÉ ÕΦËÁÌØÎÅ ¦Í'Ñ log-ÆÁÊÌÕ %-.200s.(1-999)\n"
-ER_TABLE_NOT_LOCKED_FOR_WRITE
- cze "Tabulka '%-.192s' byla zam-Bèena s READ a nemù¾e být zmìnìna"
- dan "Tabellen '%-.192s' var låst med READ lås og kan ikke opdateres"
- nla "Tabel '%-.192s' was gelocked met een lock om te lezen. Derhalve kunnen geen wijzigingen worden opgeslagen."
- eng "Table '%-.192s' was locked with a READ lock and can't be updated"
- jps "Table '%-.192s' ‚Í READ lock ‚É‚È‚Á‚Ä‚¢‚ÄAXV‚Í‚Å‚«‚Ü‚¹‚ñ",
- est "Tabel '%-.192s' on lukustatud READ lukuga ning ei ole muudetav"
- fre "Table '%-.192s' verrouillée lecture (READ): modification impossible"
- ger "Tabelle '%-.192s' ist mit Lesesperre versehen und kann nicht aktualisiert werden"
- greek "Ï ðßíáêáò '%-.192s' Ý÷åé êëåéäùèåß ìå READ lock êáé äåí åðéôñÝðïíôáé áëëáãÝò"
- hun "A(z) '%-.192s' tabla zarolva lett (READ lock) es nem lehet frissiteni"
- ita "La tabella '%-.192s' e` soggetta a lock in lettura e non puo` essere aggiornata"
- jpn "Table '%-.192s' ¤Ï READ lock ¤Ë¤Ê¤Ã¤Æ¤¤¤Æ¡¢¹¹¿·¤Ï¤Ç¤­¤Þ¤»¤ó"
- kor "Å×À̺í '%-.192s'´Â READ ¶ôÀÌ Àá°ÜÀ־ °»½ÅÇÒ ¼ö ¾ø½À´Ï´Ù."
- nor "Tabellen '%-.192s' var låst med READ lås og kan ikke oppdateres"
- norwegian-ny "Tabellen '%-.192s' var låst med READ lås og kan ikkje oppdaterast"
- pol "Tabela '%-.192s' zosta³a zablokowana przez READ i nie mo¿e zostaæ zaktualizowana"
- por "Tabela '%-.192s' foi travada com trava de leitura e não pode ser atualizada"
- rum "Tabela '%-.192s' a fost locked cu un READ lock si nu poate fi actualizata"
- rus "ôÁÂÌÉÃÁ '%-.192s' ÚÁÂÌÏËÉÒÏ×ÁÎÁ ÕÒÏ×ÎÅÍ READ lock É ÎÅ ÍÏÖÅÔ ÂÙÔØ ÉÚÍÅÎÅÎÁ"
- serbian "Tabela '%-.192s' je zakljuèana READ lock-om; iz nje se može samo èitati ali u nju se ne može pisati"
- slo "Tabuµka '%-.192s' bola zamknutá s READ a nemô¾e by» zmenená"
- spa "Tabla '%-.192s' fue trabada con un READ lock y no puede ser actualizada"
- swe "Tabell '%-.192s' kan inte uppdateras emedan den är låst för läsning"
- ukr "ôÁÂÌÉÃÀ '%-.192s' ÚÁÂÌÏËÏ×ÁÎÏ Ô¦ÌØËÉ ÄÌÑ ÞÉÔÁÎÎÑ, ÔÏÍÕ §§ ÎÅ ÍÏÖÎÁ ÏÎÏ×ÉÔÉ"
-ER_TABLE_NOT_LOCKED
- cze "Tabulka '%-.192s' nebyla zam-Bèena s LOCK TABLES"
- dan "Tabellen '%-.192s' var ikke låst med LOCK TABLES"
- nla "Tabel '%-.192s' was niet gelocked met LOCK TABLES"
- eng "Table '%-.192s' was not locked with LOCK TABLES"
- jps "Table '%-.192s' ‚Í LOCK TABLES ‚É‚æ‚Á‚ăƒbƒN‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ",
- est "Tabel '%-.192s' ei ole lukustatud käsuga LOCK TABLES"
- fre "Table '%-.192s' non verrouillée: utilisez LOCK TABLES"
- ger "Tabelle '%-.192s' wurde nicht mit LOCK TABLES gesperrt"
- greek "Ï ðßíáêáò '%-.192s' äåí Ý÷åé êëåéäùèåß ìå LOCK TABLES"
- hun "A(z) '%-.192s' tabla nincs zarolva a LOCK TABLES-szel"
- ita "Non e` stato impostato il lock per la tabella '%-.192s' con LOCK TABLES"
- jpn "Table '%-.192s' ¤Ï LOCK TABLES ¤Ë¤è¤Ã¤Æ¥í¥Ã¥¯¤µ¤ì¤Æ¤¤¤Þ¤»¤ó"
- kor "Å×À̺í '%-.192s'´Â LOCK TABLES ¸í·ÉÀ¸·Î Àá±âÁö ¾Ê¾Ò½À´Ï´Ù."
- nor "Tabellen '%-.192s' var ikke låst med LOCK TABLES"
- norwegian-ny "Tabellen '%-.192s' var ikkje låst med LOCK TABLES"
- pol "Tabela '%-.192s' nie zosta³a zablokowana poleceniem LOCK TABLES"
- por "Tabela '%-.192s' não foi travada com LOCK TABLES"
- rum "Tabela '%-.192s' nu a fost locked cu LOCK TABLES"
- rus "ôÁÂÌÉÃÁ '%-.192s' ÎÅ ÂÙÌÁ ÚÁÂÌÏËÉÒÏ×ÁÎÁ Ó ÐÏÍÏÝØÀ LOCK TABLES"
- serbian "Tabela '%-.192s' nije bila zakljuèana komandom 'LOCK TABLES'"
- slo "Tabuµka '%-.192s' nebola zamknutá s LOCK TABLES"
- spa "Tabla '%-.192s' no fue trabada con LOCK TABLES"
- swe "Tabell '%-.192s' är inte låst med LOCK TABLES"
- ukr "ôÁÂÌÉÃÀ '%-.192s' ÎÅ ÂÕÌÏ ÂÌÏËÏ×ÁÎÏ Ú LOCK TABLES"
-ER_BLOB_CANT_HAVE_DEFAULT 42000
- cze "Blob polo-B¾ka '%-.192s' nemù¾e mít defaultní hodnotu"
- dan "BLOB feltet '%-.192s' kan ikke have en standard værdi"
- nla "Blob veld '%-.192s' can geen standaardwaarde bevatten"
- eng "BLOB/TEXT column '%-.192s' can't have a default value"
- est "BLOB-tüüpi tulp '%-.192s' ei saa omada vaikeväärtust"
- fre "BLOB '%-.192s' ne peut avoir de valeur par défaut"
- ger "BLOB/TEXT-Feld '%-.192s' darf keinen Vorgabewert (DEFAULT) haben"
- greek "Ôá Blob ðåäßá '%-.192s' äåí ìðïñïýí íá Ý÷ïõí ðñïêáèïñéóìÝíåò ôéìÝò (default value)"
- hun "A(z) '%-.192s' blob objektumnak nem lehet alapertelmezett erteke"
- ita "Il campo BLOB '%-.192s' non puo` avere un valore di default"
- jpn "BLOB column '%-.192s' can't have a default value"
- kor "BLOB Ä®·³ '%-.192s' ´Â µðÆúÆ® °ªÀ» °¡Áú ¼ö ¾ø½À´Ï´Ù."
- nor "Blob feltet '%-.192s' kan ikke ha en standard verdi"
- norwegian-ny "Blob feltet '%-.192s' kan ikkje ha ein standard verdi"
- pol "Pole typu blob '%-.192s' nie mo¿e mieæ domy?lnej warto?ci"
- por "Coluna BLOB '%-.192s' não pode ter um valor padrão (default)"
- rum "Coloana BLOB '%-.192s' nu poate avea o valoare default"
- rus "îÅ×ÏÚÍÏÖÎÏ ÕËÁÚÙ×ÁÔØ ÚÎÁÞÅÎÉÅ ÐÏ ÕÍÏÌÞÁÎÉÀ ÄÌÑ ÓÔÏÌÂÃÁ BLOB '%-.192s'"
- serbian "BLOB kolona '%-.192s' ne može imati default vrednost"
- slo "Pole BLOB '%-.192s' nemô¾e ma» implicitnú hodnotu"
- spa "Campo Blob '%-.192s' no puede tener valores patron"
- swe "BLOB fält '%-.192s' kan inte ha ett DEFAULT-värde"
- ukr "óÔÏ×ÂÅÃØ BLOB '%-.192s' ÎÅ ÍÏÖÅ ÍÁÔÉ ÚÎÁÞÅÎÎÑ ÐÏ ÚÁÍÏ×ÞÕ×ÁÎÎÀ"
-ER_WRONG_DB_NAME 42000
- cze "Nep-Bøípustné jméno databáze '%-.100s'"
- dan "Ugyldigt database navn '%-.100s'"
- nla "Databasenaam '%-.100s' is niet getoegestaan"
- eng "Incorrect database name '%-.100s'"
- jps "Žw’肵‚½ database –¼ '%-.100s' ‚ªŠÔˆá‚Á‚Ä‚¢‚Ü‚·",
- est "Vigane andmebaasi nimi '%-.100s'"
- fre "Nom de base de donnée illégal: '%-.100s'"
- ger "Unerlaubter Datenbankname '%-.100s'"
- greek "ËÜèïò üíïìá âÜóçò äåäïìÝíùí '%-.100s'"
- hun "Hibas adatbazisnev: '%-.100s'"
- ita "Nome database errato '%-.100s'"
- jpn "»ØÄꤷ¤¿ database ̾ '%-.100s' ¤¬´Ö°ã¤Ã¤Æ¤¤¤Þ¤¹"
- kor "'%-.100s' µ¥ÀÌŸº£À̽ºÀÇ À̸§ÀÌ ºÎÁ¤È®ÇÕ´Ï´Ù."
- nor "Ugyldig database navn '%-.100s'"
- norwegian-ny "Ugyldig database namn '%-.100s'"
- pol "Niedozwolona nazwa bazy danych '%-.100s'"
- por "Nome de banco de dados '%-.100s' incorreto"
- rum "Numele bazei de date este incorect '%-.100s'"
- rus "îÅËÏÒÒÅËÔÎÏÅ ÉÍÑ ÂÁÚÙ ÄÁÎÎÙÈ '%-.100s'"
- serbian "Pogrešno ime baze '%-.100s'"
- slo "Neprípustné meno databázy '%-.100s'"
- spa "Nombre de base de datos ilegal '%-.100s'"
- swe "Felaktigt databasnamn '%-.100s'"
- ukr "îÅצÒÎÅ ¦Í'Ñ ÂÁÚÉ ÄÁÎÎÉÈ '%-.100s'"
-ER_WRONG_TABLE_NAME 42000
- cze "Nep-Bøípustné jméno tabulky '%-.100s'"
- dan "Ugyldigt tabel navn '%-.100s'"
- nla "Niet toegestane tabelnaam '%-.100s'"
- eng "Incorrect table name '%-.100s'"
- jps "Žw’肵‚½ table –¼ '%-.100s' ‚Í‚Ü‚¿‚ª‚Á‚Ä‚¢‚Ü‚·",
- est "Vigane tabeli nimi '%-.100s'"
- fre "Nom de table illégal: '%-.100s'"
- ger "Unerlaubter Tabellenname '%-.100s'"
- greek "ËÜèïò üíïìá ðßíáêá '%-.100s'"
- hun "Hibas tablanev: '%-.100s'"
- ita "Nome tabella errato '%-.100s'"
- jpn "»ØÄꤷ¤¿ table ̾ '%-.100s' ¤Ï¤Þ¤Á¤¬¤Ã¤Æ¤¤¤Þ¤¹"
- kor "'%-.100s' Å×À̺í À̸§ÀÌ ºÎÁ¤È®ÇÕ´Ï´Ù."
- nor "Ugyldig tabell navn '%-.100s'"
- norwegian-ny "Ugyldig tabell namn '%-.100s'"
- pol "Niedozwolona nazwa tabeli '%-.100s'..."
- por "Nome de tabela '%-.100s' incorreto"
- rum "Numele tabelei este incorect '%-.100s'"
- rus "îÅËÏÒÒÅËÔÎÏÅ ÉÍÑ ÔÁÂÌÉÃÙ '%-.100s'"
- serbian "Pogrešno ime tabele '%-.100s'"
- slo "Neprípustné meno tabuµky '%-.100s'"
- spa "Nombre de tabla ilegal '%-.100s'"
- swe "Felaktigt tabellnamn '%-.100s'"
- ukr "îÅצÒÎÅ ¦Í'Ñ ÔÁÂÌÉæ '%-.100s'"
-ER_TOO_BIG_SELECT 42000
- cze "Zadan-Bý SELECT by procházel pøíli¹ mnoho záznamù a trval velmi dlouho. Zkontrolujte tvar WHERE a je-li SELECT v poøádku, pou¾ijte SET SQL_BIG_SELECTS=1"
- dan "SELECT ville undersøge for mange poster og ville sandsynligvis tage meget lang tid. Undersøg WHERE delen og brug SET SQL_BIG_SELECTS=1 hvis udtrykket er korrekt"
- nla "Het SELECT-statement zou te veel records analyseren en dus veel tijd in beslagnemen. Kijk het WHERE-gedeelte van de query na en kies SET SQL_BIG_SELECTS=1 als het stament in orde is."
- eng "The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET MAX_JOIN_SIZE=# if the SELECT is okay"
- est "SELECT lause peab läbi vaatama suure hulga kirjeid ja võtaks tõenäoliselt liiga kaua aega. Tasub kontrollida WHERE klauslit ja vajadusel kasutada käsku SET SQL_BIG_SELECTS=1"
- fre "SELECT va devoir examiner beaucoup d'enregistrements ce qui va prendre du temps. Vérifiez la clause WHERE et utilisez SET SQL_BIG_SELECTS=1 si SELECT se passe bien"
- ger "Die Ausführung des SELECT würde zu viele Datensätze untersuchen und wahrscheinlich sehr lange dauern. Bitte WHERE-Klausel überprüfen und gegebenenfalls SET SQL_BIG_SELECTS=1 oder SET MAX_JOIN_SIZE=# verwenden"
- greek "Ôï SELECT èá åîåôÜóåé ìåãÜëï áñéèìü åããñáöþí êáé ðéèáíþò èá êáèõóôåñÞóåé. Ðáñáêáëþ åîåôÜóôå ôéò ðáñáìÝôñïõò ôïõ WHERE êáé ÷ñçóéìïðïéåßóôå SET SQL_BIG_SELECTS=1 áí ôï SELECT åßíáé óùóôü"
- hun "A SELECT tul sok rekordot fog megvizsgalni es nagyon sokaig fog tartani. Ellenorizze a WHERE-t es hasznalja a SET SQL_BIG_SELECTS=1 beallitast, ha a SELECT okay"
- ita "La SELECT dovrebbe esaminare troppi record e usare troppo tempo. Controllare la WHERE e usa SET SQL_BIG_SELECTS=1 se e` tutto a posto."
- kor "SELECT ¸í·É¿¡¼­ ³Ê¹« ¸¹Àº ·¹Äڵ带 ã±â ¶§¹®¿¡ ¸¹Àº ½Ã°£ÀÌ ¼Ò¿äµË´Ï´Ù. µû¶ó¼­ WHERE ¹®À» Á¡°ËÇϰųª, ¸¸¾à SELECT°¡ okµÇ¸é SET SQL_BIG_SELECTS=1 ¿É¼ÇÀ» »ç¿ëÇϼ¼¿ä."
- nor "SELECT ville undersøke for mange poster og ville sannsynligvis ta veldig lang tid. Undersøk WHERE klausulen og bruk SET SQL_BIG_SELECTS=1 om SELECTen er korrekt"
- norwegian-ny "SELECT ville undersøkje for mange postar og ville sannsynligvis ta veldig lang tid. Undersøk WHERE klausulen og bruk SET SQL_BIG_SELECTS=1 om SELECTen er korrekt"
- pol "Operacja SELECT bêdzie dotyczy³a zbyt wielu rekordów i prawdopodobnie zajmie bardzo du¿o czasu. Sprawd¥ warunek WHERE i u¿yj SQL_OPTION BIG_SELECTS=1 je?li operacja SELECT jest poprawna"
- por "O SELECT examinaria registros demais e provavelmente levaria muito tempo. Cheque sua cláusula WHERE e use SET SQL_BIG_SELECTS=1, se o SELECT estiver correto"
- rum "SELECT-ul ar examina prea multe cimpuri si probabil ar lua prea mult timp; verifica clauza WHERE si foloseste SET SQL_BIG_SELECTS=1 daca SELECT-ul e okay"
- rus "äÌÑ ÔÁËÏÊ ×ÙÂÏÒËÉ SELECT ÄÏÌÖÅÎ ÂÕÄÅÔ ÐÒÏÓÍÏÔÒÅÔØ ÓÌÉÛËÏÍ ÍÎÏÇÏ ÚÁÐÉÓÅÊ É, ×ÉÄÉÍÏ, ÜÔÏ ÚÁÊÍÅÔ ÏÞÅÎØ ÍÎÏÇÏ ×ÒÅÍÅÎÉ. ðÒÏ×ÅÒØÔÅ ×ÁÛÅ ÕËÁÚÁÎÉÅ WHERE, É, ÅÓÌÉ × ÎÅÍ ×ÓÅ × ÐÏÒÑÄËÅ, ÕËÁÖÉÔÅ SET SQL_BIG_SELECTS=1"
- serbian "Komanda 'SELECT' æe ispitati previše slogova i potrošiti previše vremena. Proverite vaš 'WHERE' filter i upotrebite 'SET OPTION SQL_BIG_SELECTS=1' ako želite baš ovakvu komandu"
- slo "Zadaná po¾iadavka SELECT by prechádzala príli¹ mnoho záznamov a trvala by príli¹ dlho. Skontrolujte tvar WHERE a ak je v poriadku, pou¾ite SET SQL_BIG_SELECTS=1"
- spa "El SELECT puede examinar muchos registros y probablemente con mucho tiempo. Verifique tu WHERE y usa SET SQL_BIG_SELECTS=1 si el SELECT esta correcto"
- swe "Den angivna frågan skulle läsa mer än MAX_JOIN_SIZE rader. Kontrollera din WHERE och använd SET SQL_BIG_SELECTS=1 eller SET MAX_JOIN_SIZE=# ifall du vill hantera stora joins"
- ukr "úÁÐÉÔÕ SELECT ÐÏÔÒ¦ÂÎÏ ÏÂÒÏÂÉÔÉ ÂÁÇÁÔÏ ÚÁÐÉÓ¦×, ÝÏ, ÐÅ×ÎÅ, ÚÁÊÍÅ ÄÕÖÅ ÂÁÇÁÔÏ ÞÁÓÕ. ðÅÒÅצÒÔÅ ×ÁÛÅ WHERE ÔÁ ×ÉËÏÒÉÓÔÏ×ÕÊÔÅ SET SQL_BIG_SELECTS=1, ÑËÝÏ ÃÅÊ ÚÁÐÉÔ SELECT ¤ צÒÎÉÍ"
-ER_UNKNOWN_ERROR
- cze "Nezn-Bámá chyba"
- dan "Ukendt fejl"
- nla "Onbekende Fout"
- eng "Unknown error"
- est "Tundmatu viga"
- fre "Erreur inconnue"
- ger "Unbekannter Fehler"
- greek "ÐñïÝêõøå Üãíùóôï ëÜèïò"
- hun "Ismeretlen hiba"
- ita "Errore sconosciuto"
- kor "¾Ë¼ö ¾ø´Â ¿¡·¯ÀÔ´Ï´Ù."
- nor "Ukjent feil"
- norwegian-ny "Ukjend feil"
- por "Erro desconhecido"
- rum "Eroare unknown"
- rus "îÅÉÚ×ÅÓÔÎÁÑ ÏÛÉÂËÁ"
- serbian "Nepoznata greška"
- slo "Neznámá chyba"
- spa "Error desconocido"
- swe "Oidentifierat fel"
- ukr "îÅצÄÏÍÁ ÐÏÍÉÌËÁ"
-ER_UNKNOWN_PROCEDURE 42000
- cze "Nezn-Bámá procedura %-.192s"
- dan "Ukendt procedure %-.192s"
- nla "Onbekende procedure %-.192s"
- eng "Unknown procedure '%-.192s'"
- est "Tundmatu protseduur '%-.192s'"
- fre "Procédure %-.192s inconnue"
- ger "Unbekannte Prozedur '%-.192s'"
- greek "Áãíùóôç äéáäéêáóßá '%-.192s'"
- hun "Ismeretlen eljaras: '%-.192s'"
- ita "Procedura '%-.192s' sconosciuta"
- kor "¾Ë¼ö ¾ø´Â ¼öÇ๮ : '%-.192s'"
- nor "Ukjent prosedyre %-.192s"
- norwegian-ny "Ukjend prosedyre %-.192s"
- pol "Unkown procedure %-.192s"
- por "'Procedure' '%-.192s' desconhecida"
- rum "Procedura unknown '%-.192s'"
- rus "îÅÉÚ×ÅÓÔÎÁÑ ÐÒÏÃÅÄÕÒÁ '%-.192s'"
- serbian "Nepoznata procedura '%-.192s'"
- slo "Neznámá procedúra '%-.192s'"
- spa "Procedimiento desconocido %-.192s"
- swe "Okänd procedur: %-.192s"
- ukr "îÅצÄÏÍÁ ÐÒÏÃÅÄÕÒÁ '%-.192s'"
-ER_WRONG_PARAMCOUNT_TO_PROCEDURE 42000
- cze "Chybn-Bý poèet parametrù procedury %-.192s"
- dan "Forkert antal parametre til proceduren %-.192s"
- nla "Foutief aantal parameters doorgegeven aan procedure %-.192s"
- eng "Incorrect parameter count to procedure '%-.192s'"
- est "Vale parameetrite hulk protseduurile '%-.192s'"
- fre "Mauvais nombre de paramètres pour la procedure %-.192s"
- ger "Falsche Parameterzahl für Prozedur '%-.192s'"
- greek "ËÜèïò áñéèìüò ðáñáìÝôñùí óôç äéáäéêáóßá '%-.192s'"
- hun "Rossz parameter a(z) '%-.192s'eljaras szamitasanal"
- ita "Numero di parametri errato per la procedura '%-.192s'"
- kor "'%-.192s' ¼öÇ๮¿¡ ´ëÇÑ ºÎÁ¤È®ÇÑ ÆĶó¸ÞÅÍ"
- nor "Feil parameter antall til prosedyren %-.192s"
- norwegian-ny "Feil parameter tal til prosedyra %-.192s"
- pol "Incorrect parameter count to procedure %-.192s"
- por "Número de parâmetros incorreto para a 'procedure' '%-.192s'"
- rum "Procedura '%-.192s' are un numar incorect de parametri"
- rus "îÅËÏÒÒÅËÔÎÏÅ ËÏÌÉÞÅÓÔ×Ï ÐÁÒÁÍÅÔÒÏ× ÄÌÑ ÐÒÏÃÅÄÕÒÙ '%-.192s'"
- serbian "Pogrešan broj parametara za proceduru '%-.192s'"
- slo "Chybný poèet parametrov procedúry '%-.192s'"
- spa "Equivocado parametro count para procedimiento %-.192s"
- swe "Felaktigt antal parametrar till procedur %-.192s"
- ukr "èÉÂÎÁ ˦ÌØ˦ÓÔØ ÐÁÒÁÍÅÔÒ¦× ÐÒÏÃÅÄÕÒÉ '%-.192s'"
-ER_WRONG_PARAMETERS_TO_PROCEDURE
- cze "Chybn-Bé parametry procedury %-.192s"
- dan "Forkert(e) parametre til proceduren %-.192s"
- nla "Foutieve parameters voor procedure %-.192s"
- eng "Incorrect parameters to procedure '%-.192s'"
- est "Vigased parameetrid protseduurile '%-.192s'"
- fre "Paramètre erroné pour la procedure %-.192s"
- ger "Falsche Parameter für Prozedur '%-.192s'"
- greek "ËÜèïò ðáñÜìåôñïé óôçí äéáäéêáóßá '%-.192s'"
- hun "Rossz parameter a(z) '%-.192s' eljarasban"
- ita "Parametri errati per la procedura '%-.192s'"
- kor "'%-.192s' ¼öÇ๮¿¡ ´ëÇÑ ºÎÁ¤È®ÇÑ ÆĶó¸ÞÅÍ"
- nor "Feil parametre til prosedyren %-.192s"
- norwegian-ny "Feil parameter til prosedyra %-.192s"
- pol "Incorrect parameters to procedure %-.192s"
- por "Parâmetros incorretos para a 'procedure' '%-.192s'"
- rum "Procedura '%-.192s' are parametrii incorecti"
- rus "îÅËÏÒÒÅËÔÎÙÅ ÐÁÒÁÍÅÔÒÙ ÄÌÑ ÐÒÏÃÅÄÕÒÙ '%-.192s'"
- serbian "Pogrešni parametri prosleðeni proceduri '%-.192s'"
- slo "Chybné parametre procedúry '%-.192s'"
- spa "Equivocados parametros para procedimiento %-.192s"
- swe "Felaktiga parametrar till procedur %-.192s"
- ukr "èÉÂÎÉÊ ÐÁÒÁÍÅÔÅÒ ÐÒÏÃÅÄÕÒÉ '%-.192s'"
-ER_UNKNOWN_TABLE 42S02
- cze "Nezn-Bámá tabulka '%-.192s' v %-.32s"
- dan "Ukendt tabel '%-.192s' i %-.32s"
- nla "Onbekende tabel '%-.192s' in %-.32s"
- eng "Unknown table '%-.192s' in %-.32s"
- est "Tundmatu tabel '%-.192s' %-.32s-s"
- fre "Table inconnue '%-.192s' dans %-.32s"
- ger "Unbekannte Tabelle '%-.192s' in '%-.32s'"
- greek "Áãíùóôïò ðßíáêáò '%-.192s' óå %-.32s"
- hun "Ismeretlen tabla: '%-.192s' %-.32s-ban"
- ita "Tabella '%-.192s' sconosciuta in %-.32s"
- jpn "Unknown table '%-.192s' in %-.32s"
- kor "¾Ë¼ö ¾ø´Â Å×À̺í '%-.192s' (µ¥ÀÌŸº£À̽º %-.32s)"
- nor "Ukjent tabell '%-.192s' i %-.32s"
- norwegian-ny "Ukjend tabell '%-.192s' i %-.32s"
- pol "Unknown table '%-.192s' in %-.32s"
- por "Tabela '%-.192s' desconhecida em '%-.32s'"
- rum "Tabla '%-.192s' invalida in %-.32s"
- rus "îÅÉÚ×ÅÓÔÎÁÑ ÔÁÂÌÉÃÁ '%-.192s' × %-.32s"
- serbian "Nepoznata tabela '%-.192s' u '%-.32s'"
- slo "Neznáma tabuµka '%-.192s' v %-.32s"
- spa "Tabla desconocida '%-.192s' in %-.32s"
- swe "Okänd tabell '%-.192s' i '%-.32s'"
- ukr "îÅצÄÏÍÁ ÔÁÂÌÉÃÑ '%-.192s' Õ %-.32s"
-ER_FIELD_SPECIFIED_TWICE 42000
- cze "Polo-B¾ka '%-.192s' je zadána dvakrát"
- dan "Feltet '%-.192s' er anvendt to gange"
- nla "Veld '%-.192s' is dubbel gespecificeerd"
- eng "Column '%-.192s' specified twice"
- est "Tulp '%-.192s' on määratletud topelt"
- fre "Champ '%-.192s' spécifié deux fois"
- ger "Feld '%-.192s' wurde zweimal angegeben"
- greek "Ôï ðåäßï '%-.192s' Ý÷åé ïñéóèåß äýï öïñÝò"
- hun "A(z) '%-.192s' mezot ketszer definialta"
- ita "Campo '%-.192s' specificato 2 volte"
- kor "Ä®·³ '%-.192s'´Â µÎ¹ø Á¤ÀǵǾî ÀÖÀ¾´Ï´Ù."
- nor "Feltet '%-.192s' er spesifisert to ganger"
- norwegian-ny "Feltet '%-.192s' er spesifisert to gangar"
- pol "Field '%-.192s' specified twice"
- por "Coluna '%-.192s' especificada duas vezes"
- rum "Coloana '%-.192s' specificata de doua ori"
- rus "óÔÏÌÂÅà '%-.192s' ÕËÁÚÁÎ Ä×ÁÖÄÙ"
- serbian "Kolona '%-.192s' je navedena dva puta"
- slo "Pole '%-.192s' je zadané dvakrát"
- spa "Campo '%-.192s' especificado dos veces"
- swe "Fält '%-.192s' är redan använt"
- ukr "óÔÏ×ÂÅÃØ '%-.192s' ÚÁÚÎÁÞÅÎÏ Äצަ"
-ER_INVALID_GROUP_FUNC_USE
- cze "Nespr-Bávné pou¾ití funkce group"
- dan "Forkert brug af grupperings-funktion"
- nla "Ongeldig gebruik van GROUP-functie"
- eng "Invalid use of group function"
- est "Vigane grupeerimisfunktsiooni kasutus"
- fre "Utilisation invalide de la clause GROUP"
- ger "Falsche Verwendung einer Gruppierungsfunktion"
- greek "ÅóöáëìÝíç ÷ñÞóç ôçò group function"
- hun "A group funkcio ervenytelen hasznalata"
- ita "Uso non valido di una funzione di raggruppamento"
- kor "À߸øµÈ ±×·ì ÇÔ¼ö¸¦ »ç¿ëÇÏ¿´½À´Ï´Ù."
- por "Uso inválido de função de agrupamento (GROUP)"
- rum "Folosire incorecta a functiei group"
- rus "îÅÐÒÁ×ÉÌØÎÏÅ ÉÓÐÏÌØÚÏ×ÁÎÉÅ ÇÒÕÐÐÏ×ÙÈ ÆÕÎËÃÉÊ"
- serbian "Pogrešna upotreba 'GROUP' funkcije"
- slo "Nesprávne pou¾itie funkcie GROUP"
- spa "Invalido uso de función en grupo"
- swe "Felaktig användning av SQL grupp function"
- ukr "èÉÂÎÅ ×ÉËÏÒÉÓÔÁÎÎÑ ÆÕÎËæ§ ÇÒÕÐÕ×ÁÎÎÑ"
-ER_UNSUPPORTED_EXTENSION 42000
- cze "Tabulka '%-.192s' pou-B¾ívá roz¹íøení, které v této verzi MySQL není"
- dan "Tabellen '%-.192s' bruger et filtypenavn som ikke findes i denne MySQL version"
- nla "Tabel '%-.192s' gebruikt een extensie, die niet in deze MySQL-versie voorkomt."
- eng "Table '%-.192s' uses an extension that doesn't exist in this MySQL version"
- est "Tabel '%-.192s' kasutab laiendust, mis ei eksisteeri antud MySQL versioonis"
- fre "Table '%-.192s' : utilise une extension invalide pour cette version de MySQL"
- ger "Tabelle '%-.192s' verwendet eine Erweiterung, die in dieser MySQL-Version nicht verfügbar ist"
- greek "Ï ðßíáêò '%-.192s' ÷ñçóéìïðïéåß êÜðïéï extension ðïõ äåí õðÜñ÷åé óôçí Ýêäïóç áõôÞ ôçò MySQL"
- hun "A(z) '%-.192s' tabla olyan bovitest hasznal, amely nem letezik ebben a MySQL versioban."
- ita "La tabella '%-.192s' usa un'estensione che non esiste in questa versione di MySQL"
- kor "Å×À̺í '%-.192s'´Â È®Àå¸í·ÉÀ» ÀÌ¿ëÇÏÁö¸¸ ÇöÀçÀÇ MySQL ¹öÁ¯¿¡¼­´Â Á¸ÀçÇÏÁö ¾Ê½À´Ï´Ù."
- nor "Table '%-.192s' uses a extension that doesn't exist in this MySQL version"
- norwegian-ny "Table '%-.192s' uses a extension that doesn't exist in this MySQL version"
- pol "Table '%-.192s' uses a extension that doesn't exist in this MySQL version"
- por "Tabela '%-.192s' usa uma extensão que não existe nesta versão do MySQL"
- rum "Tabela '%-.192s' foloseste o extensire inexistenta in versiunea curenta de MySQL"
- rus "÷ ÔÁÂÌÉÃÅ '%-.192s' ÉÓÐÏÌØÚÕÀÔÓÑ ×ÏÚÍÏÖÎÏÓÔÉ, ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÍÙÅ × ÜÔÏÊ ×ÅÒÓÉÉ MySQL"
- serbian "Tabela '%-.192s' koristi ekstenziju koje ne postoji u ovoj verziji MySQL-a"
- slo "Tabuµka '%-.192s' pou¾íva roz¹írenie, ktoré v tejto verzii MySQL nie je"
- spa "Tabla '%-.192s' usa una extensión que no existe en esta MySQL versión"
- swe "Tabell '%-.192s' har en extension som inte finns i denna version av MySQL"
- ukr "ôÁÂÌÉÃÑ '%-.192s' ×ÉËÏÒÉÓÔÏ×Õ¤ ÒÏÚÛÉÒÅÎÎÑ, ÝÏ ÎÅ ¦ÓÎÕ¤ Õ Ã¦Ê ×ÅÒÓ¦§ MySQL"
-ER_TABLE_MUST_HAVE_COLUMNS 42000
- cze "Tabulka mus-Bí mít alespoò jeden sloupec"
- dan "En tabel skal have mindst een kolonne"
- nla "Een tabel moet minstens 1 kolom bevatten"
- eng "A table must have at least 1 column"
- jps "ƒe[ƒuƒ‹‚ÍÅ’á 1 ŒÂ‚Ì column ‚ª•K—v‚Å‚·",
- est "Tabelis peab olema vähemalt üks tulp"
- fre "Une table doit comporter au moins une colonne"
- ger "Eine Tabelle muss mindestens eine Spalte besitzen"
- greek "Åíáò ðßíáêáò ðñÝðåé íá Ý÷åé ôïõëÜ÷éóôïí Ýíá ðåäßï"
- hun "A tablanak legalabb egy oszlopot tartalmazni kell"
- ita "Una tabella deve avere almeno 1 colonna"
- jpn "¥Æ¡¼¥Ö¥ë¤ÏºÇÄã 1 ¸Ä¤Î column ¤¬É¬ÍפǤ¹"
- kor "ÇϳªÀÇ Å×ÀÌºí¿¡¼­´Â Àû¾îµµ ÇϳªÀÇ Ä®·³ÀÌ Á¸ÀçÇÏ¿©¾ß ÇÕ´Ï´Ù."
- por "Uma tabela tem que ter pelo menos uma (1) coluna"
- rum "O tabela trebuie sa aiba cel putin o coloana"
- rus "÷ ÔÁÂÌÉÃÅ ÄÏÌÖÅÎ ÂÙÔØ ËÁË ÍÉÎÉÍÕÍ ÏÄÉÎ ÓÔÏÌÂÅÃ"
- serbian "Tabela mora imati najmanje jednu kolonu"
- slo "Tabuµka musí ma» aspoò 1 pole"
- spa "Una tabla debe tener al menos 1 columna"
- swe "Tabeller måste ha minst 1 kolumn"
- ukr "ôÁÂÌÉÃÑ ÐÏ×ÉÎÎÁ ÍÁÔÉ ÈÏÞÁ ÏÄÉÎ ÓÔÏ×ÂÅÃØ"
-ER_RECORD_FILE_FULL
- cze "Tabulka '%-.192s' je pln-Bá"
- dan "Tabellen '%-.192s' er fuld"
- nla "De tabel '%-.192s' is vol"
- eng "The table '%-.192s' is full"
- jps "table '%-.192s' ‚Í‚¢‚Á‚Ï‚¢‚Å‚·",
- est "Tabel '%-.192s' on täis"
- fre "La table '%-.192s' est pleine"
- ger "Tabelle '%-.192s' ist voll"
- greek "Ï ðßíáêáò '%-.192s' åßíáé ãåìÜôïò"
- hun "A '%-.192s' tabla megtelt"
- ita "La tabella '%-.192s' e` piena"
- jpn "table '%-.192s' ¤Ï¤¤¤Ã¤Ñ¤¤¤Ç¤¹"
- kor "Å×À̺í '%-.192s'°¡ full³µ½À´Ï´Ù. "
- por "Tabela '%-.192s' está cheia"
- rum "Tabela '%-.192s' e plina"
- rus "ôÁÂÌÉÃÁ '%-.192s' ÐÅÒÅÐÏÌÎÅÎÁ"
- serbian "Tabela '%-.192s' je popunjena do kraja"
- slo "Tabuµka '%-.192s' je plná"
- spa "La tabla '%-.192s' está llena"
- swe "Tabellen '%-.192s' är full"
- ukr "ôÁÂÌÉÃÑ '%-.192s' ÚÁÐÏ×ÎÅÎÁ"
-ER_UNKNOWN_CHARACTER_SET 42000
- cze "Nezn-Bámá znaková sada: '%-.64s'"
- dan "Ukendt tegnsæt: '%-.64s'"
- nla "Onbekende character set: '%-.64s'"
- eng "Unknown character set: '%-.64s'"
- jps "character set '%-.64s' ‚̓Tƒ|[ƒg‚µ‚Ä‚¢‚Ü‚¹‚ñ",
- est "Vigane kooditabel '%-.64s'"
- fre "Jeu de caractères inconnu: '%-.64s'"
- ger "Unbekannter Zeichensatz: '%-.64s'"
- greek "Áãíùóôï character set: '%-.64s'"
- hun "Ervenytelen karakterkeszlet: '%-.64s'"
- ita "Set di caratteri '%-.64s' sconosciuto"
- jpn "character set '%-.64s' ¤Ï¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤Þ¤»¤ó"
- kor "¾Ë¼ö¾ø´Â ¾ð¾î Set: '%-.64s'"
- por "Conjunto de caracteres '%-.64s' desconhecido"
- rum "Set de caractere invalid: '%-.64s'"
- rus "îÅÉÚ×ÅÓÔÎÁÑ ËÏÄÉÒÏ×ËÁ '%-.64s'"
- serbian "Nepoznati karakter-set: '%-.64s'"
- slo "Neznáma znaková sada: '%-.64s'"
- spa "Juego de caracteres desconocido: '%-.64s'"
- swe "Okänd teckenuppsättning: '%-.64s'"
- ukr "îÅצÄÏÍÁ ËÏÄÏ×Á ÔÁÂÌÉÃÑ: '%-.64s'"
-ER_TOO_MANY_TABLES
- cze "P-Bøíli¹ mnoho tabulek, MySQL jich mù¾e mít v joinu jen %d"
- dan "For mange tabeller. MySQL kan kun bruge %d tabeller i et join"
- nla "Teveel tabellen. MySQL kan slechts %d tabellen in een join bevatten"
- eng "Too many tables; MySQL can only use %d tables in a join"
- jps "ƒe[ƒuƒ‹‚ª‘½‚·‚¬‚Ü‚·; MySQL can only use %d tables in a join",
- est "Liiga palju tabeleid. MySQL suudab JOINiga ühendada kuni %d tabelit"
- fre "Trop de tables. MySQL ne peut utiliser que %d tables dans un JOIN"
- ger "Zu viele Tabellen. MySQL kann in einem Join maximal %d Tabellen verwenden"
- greek "Ðïëý ìåãÜëïò áñéèìüò ðéíÜêùí. Ç MySQL ìðïñåß íá ÷ñçóéìïðïéÞóåé %d ðßíáêåò óå äéáäéêáóßá join"
- hun "Tul sok tabla. A MySQL csak %d tablat tud kezelni osszefuzeskor"
- ita "Troppe tabelle. MySQL puo` usare solo %d tabelle in una join"
- jpn "¥Æ¡¼¥Ö¥ë¤¬Â¿¤¹¤®¤Þ¤¹; MySQL can only use %d tables in a join"
- kor "³Ê¹« ¸¹Àº Å×À̺íÀÌ JoinµÇ¾ú½À´Ï´Ù. MySQL¿¡¼­´Â JOIN½Ã %d°³ÀÇ Å×ÀÌºí¸¸ »ç¿ëÇÒ ¼ö ÀÖ½À´Ï´Ù."
- por "Tabelas demais. O MySQL pode usar somente %d tabelas em uma junção (JOIN)"
- rum "Prea multe tabele. MySQL nu poate folosi mai mult de %d tabele intr-un join"
- rus "óÌÉÛËÏÍ ÍÎÏÇÏ ÔÁÂÌÉÃ. MySQL ÍÏÖÅÔ ÉÓÐÏÌØÚÏ×ÁÔØ ÔÏÌØËÏ %d ÔÁÂÌÉÃ × ÓÏÅÄÉÎÅÎÉÉ"
- serbian "Previše tabela. MySQL može upotrebiti maksimum %d tabela pri 'JOIN' operaciji"
- slo "Príli¹ mnoho tabuliek. MySQL mô¾e pou¾i» len %d v JOIN-e"
- spa "Muchas tablas. MySQL solamente puede usar %d tablas en un join"
- swe "För många tabeller. MySQL can ha högst %d tabeller i en och samma join"
- ukr "úÁÂÁÇÁÔÏ ÔÁÂÌÉÃØ. MySQL ÍÏÖÅ ×ÉËÏÒÉÓÔÏ×Õ×ÁÔÉ ÌÉÛÅ %d ÔÁÂÌÉÃØ Õ ÏÂ'¤ÄÎÁÎΦ"
-ER_TOO_MANY_FIELDS
- cze "P-Bøíli¹ mnoho polo¾ek"
- dan "For mange felter"
- nla "Te veel velden"
- eng "Too many columns"
- jps "column ‚ª‘½‚·‚¬‚Ü‚·",
- est "Liiga palju tulpasid"
- fre "Trop de champs"
- ger "Zu viele Felder"
- greek "Ðïëý ìåãÜëïò áñéèìüò ðåäßùí"
- hun "Tul sok mezo"
- ita "Troppi campi"
- jpn "column ¤¬Â¿¤¹¤®¤Þ¤¹"
- kor "Ä®·³ÀÌ ³Ê¹« ¸¹½À´Ï´Ù."
- por "Colunas demais"
- rum "Prea multe coloane"
- rus "óÌÉÛËÏÍ ÍÎÏÇÏ ÓÔÏÌÂÃÏ×"
- serbian "Previše kolona"
- slo "Príli¹ mnoho polí"
- spa "Muchos campos"
- swe "För många fält"
- ukr "úÁÂÁÇÁÔÏ ÓÔÏ×Âæ×"
-ER_TOO_BIG_ROWSIZE 42000
- cze "-BØádek je pøíli¹ velký. Maximální velikost øádku, nepoèítaje polo¾ky blob, je %ld. Musíte zmìnit nìkteré polo¾ky na blob"
- dan "For store poster. Max post størrelse, uden BLOB's, er %ld. Du må lave nogle felter til BLOB's"
- nla "Rij-grootte is groter dan toegestaan. Maximale rij grootte, blobs niet meegeteld, is %ld. U dient sommige velden in blobs te veranderen."
- eng "Row size too large. The maximum row size for the used table type, not counting BLOBs, is %ld. You have to change some columns to TEXT or BLOBs"
- jps "row size ‚ª‘å‚«‚·‚¬‚Ü‚·. BLOB ‚ðŠÜ‚Ü‚È‚¢ê‡‚Ì row size ‚ÌÅ‘å‚Í %ld ‚Å‚·. ‚¢‚­‚‚©‚Ì field ‚ð BLOB ‚É•Ï‚¦‚Ä‚­‚¾‚³‚¢.",
- est "Liiga pikk kirje. Kirje maksimumpikkus arvestamata BLOB-tüüpi välju on %ld. Muuda mõned väljad BLOB-tüüpi väljadeks"
- fre "Ligne trop grande. Le taille maximale d'une ligne, sauf les BLOBs, est %ld. Changez le type de quelques colonnes en BLOB"
- ger "Zeilenlänge zu groß. Die maximale Zeilenlänge für den verwendeten Tabellentyp (ohne BLOB-Felder) beträgt %ld. Einige Felder müssen in BLOB oder TEXT umgewandelt werden"
- greek "Ðïëý ìåãÜëï ìÝãåèïò åããñáöÞò. Ôï ìÝãéóôï ìÝãåèïò åããñáöÞò, ÷ùñßò íá õðïëïãßæïíôáé ôá blobs, åßíáé %ld. ÐñÝðåé íá ïñßóåôå êÜðïéá ðåäßá óáí blobs"
- hun "Tul nagy sormeret. A maximalis sormeret (nem szamolva a blob objektumokat) %ld. Nehany mezot meg kell valtoztatnia"
- ita "Riga troppo grande. La massima grandezza di una riga, non contando i BLOB, e` %ld. Devi cambiare alcuni campi in BLOB"
- jpn "row size ¤¬Â礭¤¹¤®¤Þ¤¹. BLOB ¤ò´Þ¤Þ¤Ê¤¤¾ì¹ç¤Î row size ¤ÎºÇÂç¤Ï %ld ¤Ç¤¹. ¤¤¤¯¤Ä¤«¤Î field ¤ò BLOB ¤ËÊѤ¨¤Æ¤¯¤À¤µ¤¤."
- kor "³Ê¹« Å« row »çÀÌÁîÀÔ´Ï´Ù. BLOB¸¦ °è»êÇÏÁö ¾Ê°í ÃÖ´ë row »çÀÌÁî´Â %ldÀÔ´Ï´Ù. ¾ó¸¶°£ÀÇ ÇʵåµéÀ» BLOB·Î ¹Ù²Ù¼Å¾ß °Ú±º¿ä.."
- por "Tamanho de linha grande demais. O máximo tamanho de linha, não contando BLOBs, é %ld. Você tem que mudar alguns campos para BLOBs"
- rum "Marimea liniei (row) prea mare. Marimea maxima a liniei, excluzind BLOB-urile este de %ld. Trebuie sa schimbati unele cimpuri in BLOB-uri"
- rus "óÌÉÛËÏÍ ÂÏÌØÛÏÊ ÒÁÚÍÅÒ ÚÁÐÉÓÉ. íÁËÓÉÍÁÌØÎÙÊ ÒÁÚÍÅÒ ÓÔÒÏËÉ, ÉÓËÌÀÞÁÑ ÐÏÌÑ BLOB, - %ld. ÷ÏÚÍÏÖÎÏ, ×ÁÍ ÓÌÅÄÕÅÔ ÉÚÍÅÎÉÔØ ÔÉÐ ÎÅËÏÔÏÒÙÈ ÐÏÌÅÊ ÎÁ BLOB"
- serbian "Prevelik slog. Maksimalna velièina sloga, ne raèunajuæi BLOB polja, je %ld. Trebali bi da promenite tip nekih polja u BLOB"
- slo "Riadok je príli¹ veµký. Maximálna veµkos» riadku, okrem 'BLOB', je %ld. Musíte zmeni» niektoré polo¾ky na BLOB"
- spa "Tamaño de línea muy grande. Máximo tamaño de línea, no contando blob, es %ld. Tu tienes que cambiar algunos campos para blob"
- swe "För stor total radlängd. Den högst tillåtna radlängden, förutom BLOBs, är %ld. Ändra några av dina fält till BLOB"
- ukr "úÁÄÏ×ÇÁ ÓÔÒÏËÁ. îÁʦÌØÛÏÀ ÄÏ×ÖÉÎÏÀ ÓÔÒÏËÉ, ÎÅ ÒÁÈÕÀÞÉ BLOB, ¤ %ld. ÷ÁÍ ÐÏÔÒ¦ÂÎÏ ÐÒÉ×ÅÓÔÉ ÄÅÑ˦ ÓÔÏ×Âæ ÄÏ ÔÉÐÕ BLOB"
-ER_STACK_OVERRUN
- cze "P-Bøeteèení zásobníku threadu: pou¾ito %ld z %ld. Pou¾ijte 'mysqld -O thread_stack=#' k zadání vìt¹ího zásobníku"
- dan "Thread stack brugt: Brugt: %ld af en %ld stak. Brug 'mysqld -O thread_stack=#' for at allokere en større stak om nødvendigt"
- nla "Thread stapel overrun: Gebruikte: %ld van een %ld stack. Gebruik 'mysqld -O thread_stack=#' om een grotere stapel te definieren (indien noodzakelijk)."
- eng "Thread stack overrun: Used: %ld of a %ld stack. Use 'mysqld -O thread_stack=#' to specify a bigger stack if needed"
- jps "Thread stack overrun: Used: %ld of a %ld stack. ƒXƒ^ƒbƒN—̈æ‚𑽂­‚Ƃ肽‚¢ê‡A'mysqld -O thread_stack=#' ‚ÆŽw’肵‚Ä‚­‚¾‚³‚¢",
- fre "Débordement de la pile des tâches (Thread stack). Utilisées: %ld pour une pile de %ld. Essayez 'mysqld -O thread_stack=#' pour indiquer une plus grande valeur"
- ger "Thread-Stack-Überlauf. Benutzt: %ld von %ld Stack. 'mysqld -O thread_stack=#' verwenden, um bei Bedarf einen größeren Stack anzulegen"
- greek "Stack overrun óôï thread: Used: %ld of a %ld stack. Ðáñáêáëþ ÷ñçóéìïðïéåßóôå 'mysqld -O thread_stack=#' ãéá íá ïñßóåôå Ýíá ìåãáëýôåñï stack áí ÷ñåéÜæåôáé"
- hun "Thread verem tullepes: Used: %ld of a %ld stack. Hasznalja a 'mysqld -O thread_stack=#' nagyobb verem definialasahoz"
- ita "Thread stack overrun: Usati: %ld di uno stack di %ld. Usa 'mysqld -O thread_stack=#' per specificare uno stack piu` grande."
- jpn "Thread stack overrun: Used: %ld of a %ld stack. ¥¹¥¿¥Ã¥¯Îΰè¤ò¿¤¯¤È¤ê¤¿¤¤¾ì¹ç¡¢'mysqld -O thread_stack=#' ¤È»ØÄꤷ¤Æ¤¯¤À¤µ¤¤"
- kor "¾²·¹µå ½ºÅÃÀÌ ³ÑÃƽÀ´Ï´Ù. »ç¿ë: %ld°³ ½ºÅÃ: %ld°³. ¸¸¾à ÇÊ¿ä½Ã ´õÅ« ½ºÅÃÀ» ¿øÇÒ¶§¿¡´Â 'mysqld -O thread_stack=#' ¸¦ Á¤ÀÇÇϼ¼¿ä"
- por "Estouro da pilha do 'thread'. Usados %ld de uma pilha de %ld. Use 'mysqld -O thread_stack=#' para especificar uma pilha maior, se necessário"
- rum "Stack-ul thread-ului a fost depasit (prea mic): Folositi: %ld intr-un stack de %ld. Folositi 'mysqld -O thread_stack=#' ca sa specifici un stack mai mare"
- rus "óÔÅË ÐÏÔÏËÏ× ÐÅÒÅÐÏÌÎÅÎ: ÉÓÐÏÌØÚÏ×ÁÎÏ: %ld ÉÚ %ld ÓÔÅËÁ. ðÒÉÍÅÎÑÊÔÅ 'mysqld -O thread_stack=#' ÄÌÑ ÕËÁÚÁÎÉÑ ÂÏÌØÛÅÇÏ ÒÁÚÍÅÒÁ ÓÔÅËÁ, ÅÓÌÉ ÎÅÏÂÈÏÄÉÍÏ"
- serbian "Prepisivanje thread stack-a: Upotrebljeno: %ld od %ld stack memorije. Upotrebite 'mysqld -O thread_stack=#' da navedete veæi stack ako je potrebno"
- slo "Preteèenie zásobníku vlákna: pou¾ité: %ld z %ld. Pou¾ite 'mysqld -O thread_stack=#' k zadaniu väè¹ieho zásobníka"
- spa "Sobrecarga de la pila de thread: Usada: %ld de una %ld pila. Use 'mysqld -O thread_stack=#' para especificar una mayor pila si necesario"
- swe "Trådstacken tog slut: Har använt %ld av %ld bytes. Använd 'mysqld -O thread_stack=#' ifall du behöver en större stack"
- ukr "óÔÅË Ç¦ÌÏË ÐÅÒÅÐÏ×ÎÅÎÏ: ÷ÉËÏÒÉÓÔÁÎÏ: %ld Ú %ld. ÷ÉËÏÒÉÓÔÏ×ÕÊÔÅ 'mysqld -O thread_stack=#' ÁÂÉ ÚÁÚÎÁÞÉÔÉ Â¦ÌØÛÉÊ ÓÔÅË, ÑËÝÏ ÎÅÏÂȦÄÎÏ"
-ER_WRONG_OUTER_JOIN 42000
- cze "V OUTER JOIN byl nalezen k-Bøí¾ový odkaz. Provìøte ON podmínky"
- dan "Krydsreferencer fundet i OUTER JOIN; check dine ON conditions"
- nla "Gekruiste afhankelijkheid gevonden in OUTER JOIN. Controleer uw ON-conditions"
- eng "Cross dependency found in OUTER JOIN; examine your ON conditions"
- est "Ristsõltuvus OUTER JOIN klauslis. Kontrolli oma ON tingimusi"
- fre "Dépendance croisée dans une clause OUTER JOIN. Vérifiez la condition ON"
- ger "OUTER JOIN enthält fehlerhafte Abhängigkeiten. In ON verwendete Bedingungen überprüfen"
- greek "Cross dependency âñÝèçêå óå OUTER JOIN. Ðáñáêáëþ åîåôÜóôå ôéò óõíèÞêåò ðïõ èÝóáôå óôï ON"
- hun "Keresztfuggoseg van az OUTER JOIN-ban. Ellenorizze az ON felteteleket"
- ita "Trovata una dipendenza incrociata nella OUTER JOIN. Controlla le condizioni ON"
- por "Dependência cruzada encontrada em junção externa (OUTER JOIN); examine as condições utilizadas nas cláusulas 'ON'"
- rum "Dependinta incrucisata (cross dependency) gasita in OUTER JOIN. Examinati conditiile ON"
- rus "÷ OUTER JOIN ÏÂÎÁÒÕÖÅÎÁ ÐÅÒÅËÒÅÓÔÎÁÑ ÚÁ×ÉÓÉÍÏÓÔØ. ÷ÎÉÍÁÔÅÌØÎÏ ÐÒÏÁÎÁÌÉÚÉÒÕÊÔÅ Ó×ÏÉ ÕÓÌÏ×ÉÑ ON"
- serbian "Unakrsna zavisnost pronaðena u komandi 'OUTER JOIN'. Istražite vaše 'ON' uslove"
- slo "V OUTER JOIN bol nájdený krí¾ový odkaz. Skontrolujte podmienky ON"
- spa "Dependencia cruzada encontrada en OUTER JOIN. Examine su condición ON"
- swe "Felaktigt referens i OUTER JOIN. Kontrollera ON-uttrycket"
- ukr "ðÅÒÅÈÒÅÓÎÁ ÚÁÌÅÖΦÓÔØ Õ OUTER JOIN. ðÅÒÅצÒÔÅ ÕÍÏ×Õ ON"
-ER_NULL_COLUMN_IN_INDEX 42000
- eng "Table handler doesn't support NULL in given index. Please change column '%-.192s' to be NOT NULL or use another handler"
- swe "Tabell hanteraren kan inte indexera NULL kolumner för den givna index typen. Ändra '%-.192s' till NOT NULL eller använd en annan hanterare"
-ER_CANT_FIND_UDF
- cze "Nemohu na-Bèíst funkci '%-.192s'"
- dan "Kan ikke læse funktionen '%-.192s'"
- nla "Kan functie '%-.192s' niet laden"
- eng "Can't load function '%-.192s'"
- jps "function '%-.192s' ‚ð ƒ[ƒh‚Å‚«‚Ü‚¹‚ñ",
- est "Ei suuda avada funktsiooni '%-.192s'"
- fre "Imposible de charger la fonction '%-.192s'"
- ger "Kann Funktion '%-.192s' nicht laden"
- greek "Äåí åßíáé äõíáôÞ ç äéáäéêáóßá load ãéá ôç óõíÜñôçóç '%-.192s'"
- hun "A(z) '%-.192s' fuggveny nem toltheto be"
- ita "Impossibile caricare la funzione '%-.192s'"
- jpn "function '%-.192s' ¤ò ¥í¡¼¥É¤Ç¤­¤Þ¤»¤ó"
- kor "'%-.192s' ÇÔ¼ö¸¦ ·ÎµåÇÏÁö ¸øÇß½À´Ï´Ù."
- por "Não pode carregar a função '%-.192s'"
- rum "Nu pot incarca functia '%-.192s'"
- rus "îÅ×ÏÚÍÏÖÎÏ ÚÁÇÒÕÚÉÔØ ÆÕÎËÃÉÀ '%-.192s'"
- serbian "Ne mogu da uèitam funkciju '%-.192s'"
- slo "Nemô¾em naèíta» funkciu '%-.192s'"
- spa "No puedo cargar función '%-.192s'"
- swe "Kan inte ladda funktionen '%-.192s'"
- ukr "îÅ ÍÏÖÕ ÚÁ×ÁÎÔÁÖÉÔÉ ÆÕÎËæÀ '%-.192s'"
-ER_CANT_INITIALIZE_UDF
- cze "Nemohu inicializovat funkci '%-.192s'; %-.80s"
- dan "Kan ikke starte funktionen '%-.192s'; %-.80s"
- nla "Kan functie '%-.192s' niet initialiseren; %-.80s"
- eng "Can't initialize function '%-.192s'; %-.80s"
- jps "function '%-.192s' ‚ð‰Šú‰»‚Å‚«‚Ü‚¹‚ñ; %-.80s",
- est "Ei suuda algväärtustada funktsiooni '%-.192s'; %-.80s"
- fre "Impossible d'initialiser la fonction '%-.192s'; %-.80s"
- ger "Kann Funktion '%-.192s' nicht initialisieren: %-.80s"
- greek "Äåí åßíáé äõíáôÞ ç Ýíáñîç ôçò óõíÜñôçóçò '%-.192s'; %-.80s"
- hun "A(z) '%-.192s' fuggveny nem inicializalhato; %-.80s"
- ita "Impossibile inizializzare la funzione '%-.192s'; %-.80s"
- jpn "function '%-.192s' ¤ò½é´ü²½¤Ç¤­¤Þ¤»¤ó; %-.80s"
- kor "'%-.192s' ÇÔ¼ö¸¦ ÃʱâÈ­ ÇÏÁö ¸øÇß½À´Ï´Ù.; %-.80s"
- por "Não pode inicializar a função '%-.192s' - '%-.80s'"
- rum "Nu pot initializa functia '%-.192s'; %-.80s"
- rus "îÅ×ÏÚÍÏÖÎÏ ÉÎÉÃÉÁÌÉÚÉÒÏ×ÁÔØ ÆÕÎËÃÉÀ '%-.192s'; %-.80s"
- serbian "Ne mogu da inicijalizujem funkciju '%-.192s'; %-.80s"
- slo "Nemô¾em inicializova» funkciu '%-.192s'; %-.80s"
- spa "No puedo inicializar función '%-.192s'; %-.80s"
- swe "Kan inte initialisera funktionen '%-.192s'; '%-.80s'"
- ukr "îÅ ÍÏÖÕ ¦Î¦Ã¦Á̦ÚÕ×ÁÔÉ ÆÕÎËæÀ '%-.192s'; %-.80s"
-ER_UDF_NO_PATHS
- cze "Pro sd-Bílenou knihovnu nejsou povoleny cesty"
- dan "Angivelse af sti ikke tilladt for delt bibliotek"
- nla "Geen pad toegestaan voor shared library"
- eng "No paths allowed for shared library"
- jps "shared library ‚ւ̃pƒX‚ª’Ê‚Á‚Ä‚¢‚Ü‚¹‚ñ",
- est "Teegi nimes ei tohi olla kataloogi"
- fre "Chemin interdit pour les bibliothèques partagées"
- ger "Keine Pfade gestattet für Shared Library"
- greek "Äåí âñÝèçêáí paths ãéá ôçí shared library"
- hun "Nincs ut a megosztott konyvtarakhoz (shared library)"
- ita "Non sono ammessi path per le librerie condivisa"
- jpn "shared library ¤Ø¤Î¥Ñ¥¹¤¬Ä̤äƤ¤¤Þ¤»¤ó"
- kor "°øÀ¯ ¶óÀ̹ö·¯¸®¸¦ À§ÇÑ Æнº°¡ Á¤ÀǵǾî ÀÖÁö ¾Ê½À´Ï´Ù."
- por "Não há caminhos (paths) permitidos para biblioteca compartilhada"
- rum "Nici un paths nu e permis pentru o librarie shared"
- rus "îÅÄÏÐÕÓÔÉÍÏ ÕËÁÚÙ×ÁÔØ ÐÕÔÉ ÄÌÑ ÄÉÎÁÍÉÞÅÓËÉÈ ÂÉÂÌÉÏÔÅË"
- serbian "Ne postoje dozvoljene putanje do share-ovane biblioteke"
- slo "Neprípustné ¾iadne cesty k zdieµanej kni¾nici"
- spa "No pasos permitidos para librarias conjugadas"
- swe "Man får inte ange sökväg för dynamiska bibliotek"
- ukr "îÅ ÄÏÚ×ÏÌÅÎÏ ×ÉËÏÒÉÓÔÏ×Õ×ÁÔÉ ÐÕÔ¦ ÄÌÑ ÒÏÚĦÌÀ×ÁÎÉÈ Â¦Â̦ÏÔÅË"
-ER_UDF_EXISTS
- cze "Funkce '%-.192s' ji-B¾ existuje"
- dan "Funktionen '%-.192s' findes allerede"
- nla "Functie '%-.192s' bestaat reeds"
- eng "Function '%-.192s' already exists"
- jps "Function '%-.192s' ‚ÍŠù‚É’è‹`‚³‚ê‚Ä‚¢‚Ü‚·",
- est "Funktsioon '%-.192s' juba eksisteerib"
- fre "La fonction '%-.192s' existe déjà"
- ger "Funktion '%-.192s' existiert schon"
- greek "Ç óõíÜñôçóç '%-.192s' õðÜñ÷åé Þäç"
- hun "A '%-.192s' fuggveny mar letezik"
- ita "La funzione '%-.192s' esiste gia`"
- jpn "Function '%-.192s' ¤Ï´û¤ËÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤¹"
- kor "'%-.192s' ÇÔ¼ö´Â ÀÌ¹Ì Á¸ÀçÇÕ´Ï´Ù."
- por "Função '%-.192s' já existe"
- rum "Functia '%-.192s' exista deja"
- rus "æÕÎËÃÉÑ '%-.192s' ÕÖÅ ÓÕÝÅÓÔ×ÕÅÔ"
- serbian "Funkcija '%-.192s' veæ postoji"
- slo "Funkcia '%-.192s' u¾ existuje"
- spa "Función '%-.192s' ya existe"
- swe "Funktionen '%-.192s' finns redan"
- ukr "æÕÎËÃ¦Ñ '%-.192s' ×ÖÅ ¦ÓÎÕ¤"
-ER_CANT_OPEN_LIBRARY
- cze "Nemohu otev-Bøít sdílenou knihovnu '%-.192s' (errno: %d %-.128s)"
- dan "Kan ikke åbne delt bibliotek '%-.192s' (errno: %d %-.128s)"
- nla "Kan shared library '%-.192s' niet openen (Errcode: %d %-.128s)"
- eng "Can't open shared library '%-.192s' (errno: %d %-.128s)"
- jps "shared library '%-.192s' ‚ðŠJ‚­Ž–‚ª‚Å‚«‚Ü‚¹‚ñ (errno: %d %-.128s)",
- est "Ei suuda avada jagatud teeki '%-.192s' (veakood: %d %-.128s)"
- fre "Impossible d'ouvrir la bibliothèque partagée '%-.192s' (errno: %d %-.128s)"
- ger "Kann Shared Library '%-.192s' nicht öffnen (Fehler: %d %-.128s)"
- greek "Äåí åßíáé äõíáôÞ ç áíÜãíùóç ôçò shared library '%-.192s' (êùäéêüò ëÜèïõò: %d %-.128s)"
- hun "A(z) '%-.192s' megosztott konyvtar nem hasznalhato (hibakod: %d %-.128s)"
- ita "Impossibile aprire la libreria condivisa '%-.192s' (errno: %d %-.128s)"
- jpn "shared library '%-.192s' ¤ò³«¤¯»ö¤¬¤Ç¤­¤Þ¤»¤ó (errno: %d %-.128s)"
- kor "'%-.192s' °øÀ¯ ¶óÀ̹ö·¯¸®¸¦ ¿­¼ö ¾ø½À´Ï´Ù.(¿¡·¯¹øÈ£: %d %-.128s)"
- nor "Can't open shared library '%-.192s' (errno: %d %-.128s)"
- norwegian-ny "Can't open shared library '%-.192s' (errno: %d %-.128s)"
- pol "Can't open shared library '%-.192s' (errno: %d %-.128s)"
- por "Não pode abrir biblioteca compartilhada '%-.192s' (erro no. %d '%-.128s')"
- rum "Nu pot deschide libraria shared '%-.192s' (Eroare: %d %-.128s)"
- rus "îÅ×ÏÚÍÏÖÎÏ ÏÔËÒÙÔØ ÄÉÎÁÍÉÞÅÓËÕÀ ÂÉÂÌÉÏÔÅËÕ '%-.192s' (ÏÛÉÂËÁ: %d %-.128s)"
- serbian "Ne mogu da otvorim share-ovanu biblioteku '%-.192s' (errno: %d %-.128s)"
- slo "Nemô¾em otvori» zdieµanú kni¾nicu '%-.192s' (chybový kód: %d %-.128s)"
- spa "No puedo abrir libraria conjugada '%-.192s' (errno: %d %-.128s)"
- swe "Kan inte öppna det dynamiska biblioteket '%-.192s' (Felkod: %d %-.128s)"
- ukr "îÅ ÍÏÖÕ ×¦ÄËÒÉÔÉ ÒÏÚĦÌÀ×ÁÎÕ Â¦Â̦ÏÔÅËÕ '%-.192s' (ÐÏÍÉÌËÁ: %d %-.128s)"
-ER_CANT_FIND_DL_ENTRY
- cze "Nemohu naj-Bít funkci '%-.128s' v knihovnì"
- dan "Kan ikke finde funktionen '%-.128s' i bibliotek"
- nla "Kan functie '%-.128s' niet in library vinden"
- eng "Can't find symbol '%-.128s' in library"
- jps "function '%-.128s' ‚ðƒ‰ƒCƒuƒ‰ƒŠ[’†‚ÉŒ©•t‚¯‚鎖‚ª‚Å‚«‚Ü‚¹‚ñ",
- est "Ei leia funktsiooni '%-.128s' antud teegis"
- fre "Impossible de trouver la fonction '%-.128s' dans la bibliothèque"
- ger "Kann Funktion '%-.128s' in der Library nicht finden"
- greek "Äåí åßíáé äõíáôÞ ç áíåýñåóç ôçò óõíÜñôçóçò '%-.128s' óôçí âéâëéïèÞêç"
- hun "A(z) '%-.128s' fuggveny nem talalhato a konyvtarban"
- ita "Impossibile trovare la funzione '%-.128s' nella libreria"
- jpn "function '%-.128s' ¤ò¥é¥¤¥Ö¥é¥ê¡¼Ãæ¤Ë¸«ÉÕ¤±¤ë»ö¤¬¤Ç¤­¤Þ¤»¤ó"
- kor "¶óÀ̹ö·¯¸®¿¡¼­ '%-.128s' ÇÔ¼ö¸¦ ãÀ» ¼ö ¾ø½À´Ï´Ù."
- por "Não pode encontrar a função '%-.128s' na biblioteca"
- rum "Nu pot gasi functia '%-.128s' in libraria"
- rus "îÅ×ÏÚÍÏÖÎÏ ÏÔÙÓËÁÔØ ÓÉÍ×ÏÌ '%-.128s' × ÂÉÂÌÉÏÔÅËÅ"
- serbian "Ne mogu da pronadjem funkciju '%-.128s' u biblioteci"
- slo "Nemô¾em nájs» funkciu '%-.128s' v kni¾nici"
- spa "No puedo encontrar función '%-.128s' en libraria"
- swe "Hittar inte funktionen '%-.128s' in det dynamiska biblioteket"
- ukr "îÅ ÍÏÖÕ ÚÎÁÊÔÉ ÆÕÎËæÀ '%-.128s' Õ Â¦Â̦ÏÔÅæ"
-ER_FUNCTION_NOT_DEFINED
- cze "Funkce '%-.192s' nen-Bí definována"
- dan "Funktionen '%-.192s' er ikke defineret"
- nla "Functie '%-.192s' is niet gedefinieerd"
- eng "Function '%-.192s' is not defined"
- jps "Function '%-.192s' ‚Í’è‹`‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ",
- est "Funktsioon '%-.192s' ei ole defineeritud"
- fre "La fonction '%-.192s' n'est pas définie"
- ger "Funktion '%-.192s' ist nicht definiert"
- greek "Ç óõíÜñôçóç '%-.192s' äåí Ý÷åé ïñéóèåß"
- hun "A '%-.192s' fuggveny nem definialt"
- ita "La funzione '%-.192s' non e` definita"
- jpn "Function '%-.192s' ¤ÏÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó"
- kor "'%-.192s' ÇÔ¼ö°¡ Á¤ÀǵǾî ÀÖÁö ¾Ê½À´Ï´Ù."
- por "Função '%-.192s' não está definida"
- rum "Functia '%-.192s' nu e definita"
- rus "æÕÎËÃÉÑ '%-.192s' ÎÅ ÏÐÒÅÄÅÌÅÎÁ"
- serbian "Funkcija '%-.192s' nije definisana"
- slo "Funkcia '%-.192s' nie je definovaná"
- spa "Función '%-.192s' no está definida"
- swe "Funktionen '%-.192s' är inte definierad"
- ukr "æÕÎËæÀ '%-.192s' ÎÅ ×ÉÚÎÁÞÅÎÏ"
-ER_HOST_IS_BLOCKED
- cze "Stroj '%-.64s' je zablokov-Bán kvùli mnoha chybám pøi pøipojování. Odblokujete pou¾itím 'mysqladmin flush-hosts'"
- dan "Værten '%-.64s' er blokeret på grund af mange fejlforespørgsler. Lås op med 'mysqladmin flush-hosts'"
- nla "Host '%-.64s' is geblokkeeerd vanwege te veel verbindings fouten. Deblokkeer met 'mysqladmin flush-hosts'"
- eng "Host '%-.64s' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'"
- jps "Host '%-.64s' ‚Í many connection error ‚Ì‚½‚ßA‹‘”Û‚³‚ê‚Ü‚µ‚½. 'mysqladmin flush-hosts' ‚ʼn𜂵‚Ä‚­‚¾‚³‚¢",
- est "Masin '%-.64s' on blokeeritud hulgaliste ühendusvigade tõttu. Blokeeringu saab tühistada 'mysqladmin flush-hosts' käsuga"
- fre "L'hôte '%-.64s' est bloqué à cause d'un trop grand nombre d'erreur de connexion. Débloquer le par 'mysqladmin flush-hosts'"
- ger "Host '%-.64s' blockiert wegen zu vieler Verbindungsfehler. Aufheben der Blockierung mit 'mysqladmin flush-hosts'"
- greek "Ï õðïëïãéóôÞò '%-.64s' Ý÷åé áðïêëåéóèåß ëüãù ðïëëáðëþí ëáèþí óýíäåóçò. ÐñïóðáèÞóôå íá äéïñþóåôå ìå 'mysqladmin flush-hosts'"
- hun "A '%-.64s' host blokkolodott, tul sok kapcsolodasi hiba miatt. Hasznalja a 'mysqladmin flush-hosts' parancsot"
- ita "Sistema '%-.64s' bloccato a causa di troppi errori di connessione. Per sbloccarlo: 'mysqladmin flush-hosts'"
- jpn "Host '%-.64s' ¤Ï many connection error ¤Î¤¿¤á¡¢µñÈݤµ¤ì¤Þ¤·¤¿. 'mysqladmin flush-hosts' ¤Ç²ò½ü¤·¤Æ¤¯¤À¤µ¤¤"
- kor "³Ê¹« ¸¹Àº ¿¬°á¿À·ù·Î ÀÎÇÏ¿© È£½ºÆ® '%-.64s'´Â ºí¶ôµÇ¾ú½À´Ï´Ù. 'mysqladmin flush-hosts'¸¦ ÀÌ¿ëÇÏ¿© ºí¶ôÀ» ÇØÁ¦Çϼ¼¿ä"
- por "'Host' '%-.64s' está bloqueado devido a muitos erros de conexão. Desbloqueie com 'mysqladmin flush-hosts'"
- rum "Host-ul '%-.64s' e blocat din cauza multelor erori de conectie. Poti deploca folosind 'mysqladmin flush-hosts'"
- rus "èÏÓÔ '%-.64s' ÚÁÂÌÏËÉÒÏ×ÁÎ ÉÚ-ÚÁ ÓÌÉÛËÏÍ ÂÏÌØÛÏÇÏ ËÏÌÉÞÅÓÔ×Á ÏÛÉÂÏË ÓÏÅÄÉÎÅÎÉÑ. òÁÚÂÌÏËÉÒÏ×ÁÔØ ÅÇÏ ÍÏÖÎÏ Ó ÐÏÍÏÝØÀ 'mysqladmin flush-hosts'"
- serbian "Host '%-.64s' je blokiran zbog previše grešaka u konekciji. Možete ga odblokirati pomoæu komande 'mysqladmin flush-hosts'"
- spa "Servidor '%-.64s' está bloqueado por muchos errores de conexión. Desbloquear con 'mysqladmin flush-hosts'"
- swe "Denna dator, '%-.64s', är blockerad pga många felaktig paket. Gör 'mysqladmin flush-hosts' för att ta bort alla blockeringarna"
- ukr "èÏÓÔ '%-.64s' ÚÁÂÌÏËÏ×ÁÎÏ Ú ÐÒÉÞÉÎÉ ×ÅÌÉËϧ ˦ÌØËÏÓÔ¦ ÐÏÍÉÌÏË Ú'¤ÄÎÁÎÎÑ. äÌÑ ÒÏÚÂÌÏËÕ×ÁÎÎÑ ×ÉËÏÒÉÓÔÏ×ÕÊÔÅ 'mysqladmin flush-hosts'"
-ER_HOST_NOT_PRIVILEGED
- cze "Stroj '%-.64s' nem-Bá povoleno se k tomuto MySQL serveru pøipojit"
- dan "Værten '%-.64s' kan ikke tilkoble denne MySQL-server"
- nla "Het is host '%-.64s' is niet toegestaan verbinding te maken met deze MySQL server"
- eng "Host '%-.64s' is not allowed to connect to this MySQL server"
- jps "Host '%-.64s' ‚Í MySQL server ‚ÉÚ‘±‚ð‹–‰Â‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ",
- est "Masinal '%-.64s' puudub ligipääs sellele MySQL serverile"
- fre "Le hôte '%-.64s' n'est pas authorisé à se connecter à ce serveur MySQL"
- ger "Host '%-.64s' hat keine Berechtigung, sich mit diesem MySQL-Server zu verbinden"
- greek "Ï õðïëïãéóôÞò '%-.64s' äåí Ý÷åé äéêáßùìá óýíäåóçò ìå ôïí MySQL server"
- hun "A '%-.64s' host szamara nem engedelyezett a kapcsolodas ehhez a MySQL szerverhez"
- ita "Al sistema '%-.64s' non e` consentita la connessione a questo server MySQL"
- jpn "Host '%-.64s' ¤Ï MySQL server ¤ËÀܳ¤òµö²Ä¤µ¤ì¤Æ¤¤¤Þ¤»¤ó"
- kor "'%-.64s' È£½ºÆ®´Â ÀÌ MySQL¼­¹ö¿¡ Á¢¼ÓÇÒ Çã°¡¸¦ ¹ÞÁö ¸øÇß½À´Ï´Ù."
- por "'Host' '%-.64s' não tem permissão para se conectar com este servidor MySQL"
- rum "Host-ul '%-.64s' nu este permis a se conecta la aceste server MySQL"
- rus "èÏÓÔÕ '%-.64s' ÎÅ ÒÁÚÒÅÛÁÅÔÓÑ ÐÏÄËÌÀÞÁÔØÓÑ Ë ÜÔÏÍÕ ÓÅÒ×ÅÒÕ MySQL"
- serbian "Host-u '%-.64s' nije dozvoljeno da se konektuje na ovaj MySQL server"
- spa "Servidor '%-.64s' no está permitido para conectar con este servidor MySQL"
- swe "Denna dator, '%-.64s', har inte privileger att använda denna MySQL server"
- ukr "èÏÓÔÕ '%-.64s' ÎÅ ÄÏ×ÏÌÅÎÏ Ú×'ÑÚÕ×ÁÔÉÓØ Ú ÃÉÍ ÓÅÒ×ÅÒÏÍ MySQL"
-ER_PASSWORD_ANONYMOUS_USER 42000
- cze "Pou-B¾íváte MySQL jako anonymní u¾ivatel a anonymní u¾ivatelé nemají povoleno mìnit hesla"
- dan "Du bruger MySQL som anonym bruger. Anonyme brugere må ikke ændre adgangskoder"
- nla "U gebruikt MySQL als anonieme gebruiker en deze mogen geen wachtwoorden wijzigen"
- eng "You are using MySQL as an anonymous user and anonymous users are not allowed to change passwords"
- jps "MySQL ‚ð anonymous users ‚ÅŽg—p‚µ‚Ä‚¢‚éó‘Ô‚Å‚ÍAƒpƒXƒ[ƒh‚Ì•ÏX‚Í‚Å‚«‚Ü‚¹‚ñ",
- est "Te kasutate MySQL-i anonüümse kasutajana, kelledel pole parooli muutmise õigust"
- fre "Vous utilisez un utilisateur anonyme et les utilisateurs anonymes ne sont pas autorisés à changer les mots de passe"
- ger "Sie benutzen MySQL als anonymer Benutzer und dürfen daher keine Passwörter ändern"
- greek "×ñçóéìïðïéåßôå ôçí MySQL óáí anonymous user êáé Ýôóé äåí ìðïñåßôå íá áëëÜîåôå ôá passwords Üëëùí ÷ñçóôþí"
- hun "Nevtelen (anonymous) felhasznalokent nem negedelyezett a jelszovaltoztatas"
- ita "Impossibile cambiare la password usando MySQL come utente anonimo"
- jpn "MySQL ¤ò anonymous users ¤Ç»ÈÍѤ·¤Æ¤¤¤ë¾õÂ֤Ǥϡ¢¥Ñ¥¹¥ï¡¼¥É¤ÎÊѹ¹¤Ï¤Ç¤­¤Þ¤»¤ó"
- kor "´ç½ÅÀº MySQL¼­¹ö¿¡ À͸íÀÇ »ç¿ëÀÚ·Î Á¢¼ÓÀ» Çϼ̽À´Ï´Ù.À͸íÀÇ »ç¿ëÀÚ´Â ¾ÏÈ£¸¦ º¯°æÇÒ ¼ö ¾ø½À´Ï´Ù."
- por "Você está usando o MySQL como usuário anônimo e usuários anônimos não têm permissão para mudar senhas"
- rum "Dumneavoastra folositi MySQL ca un utilizator anonim si utilizatorii anonimi nu au voie sa schime parolele"
- rus "÷Ù ÉÓÐÏÌØÚÕÅÔÅ MySQL ÏÔ ÉÍÅÎÉ ÁÎÏÎÉÍÎÏÇÏ ÐÏÌØÚÏ×ÁÔÅÌÑ, Á ÁÎÏÎÉÍÎÙÍ ÐÏÌØÚÏ×ÁÔÅÌÑÍ ÎÅ ÒÁÚÒÅÛÁÅÔÓÑ ÍÅÎÑÔØ ÐÁÒÏÌÉ"
- serbian "Vi koristite MySQL kao anonimni korisnik a anonimnim korisnicima nije dozvoljeno da menjaju lozinke"
- spa "Tu estás usando MySQL como un usuario anonimo y usuarios anonimos no tienen permiso para cambiar las claves"
- swe "Du använder MySQL som en anonym användare och som sådan får du inte ändra ditt lösenord"
- ukr "÷É ×ÉËÏÒÉÓÔÏ×Õ¤ÔÅ MySQL ÑË ÁÎÏΦÍÎÉÊ ËÏÒÉÓÔÕ×ÁÞ, ÔÏÍÕ ×ÁÍ ÎÅ ÄÏÚ×ÏÌÅÎÏ ÚͦÎÀ×ÁÔÉ ÐÁÒÏ̦"
-ER_PASSWORD_NOT_ALLOWED 42000
- cze "Na zm-Bìnu hesel ostatním musíte mít právo provést update tabulek v databázi mysql"
- dan "Du skal have tilladelse til at opdatere tabeller i MySQL databasen for at ændre andres adgangskoder"
- nla "U moet tabel update priveleges hebben in de mysql database om wachtwoorden voor anderen te mogen wijzigen"
- eng "You must have privileges to update tables in the mysql database to be able to change passwords for others"
- jps "‘¼‚̃†[ƒU[‚̃pƒXƒ[ƒh‚ð•ÏX‚·‚邽‚ß‚É‚Í, mysql ƒf[ƒ^ƒx[ƒX‚ɑ΂µ‚Ä update ‚Ì‹–‰Â‚ª‚È‚¯‚ê‚΂Ȃè‚Ü‚¹‚ñ.",
- est "Teiste paroolide muutmiseks on nõutav tabelite muutmisõigus 'mysql' andmebaasis"
- fre "Vous devez avoir le privilège update sur les tables de la base de donnée mysql pour pouvoir changer les mots de passe des autres"
- ger "Sie benötigen die Berechtigung zum Aktualisieren von Tabellen in der Datenbank 'mysql', um die Passwörter anderer Benutzer ändern zu können"
- greek "ÐñÝðåé íá Ý÷åôå äéêáßùìá äéüñèùóçò ðéíÜêùí (update) óôç âÜóç äåäïìÝíùí mysql ãéá íá ìðïñåßôå íá áëëÜîåôå ôá passwords Üëëùí ÷ñçóôþí"
- hun "Onnek tabla-update joggal kell rendelkeznie a mysql adatbazisban masok jelszavanak megvaltoztatasahoz"
- ita "E` necessario il privilegio di update sulle tabelle del database mysql per cambiare le password per gli altri utenti"
- jpn "¾¤Î¥æ¡¼¥¶¡¼¤Î¥Ñ¥¹¥ï¡¼¥É¤òÊѹ¹¤¹¤ë¤¿¤á¤Ë¤Ï, mysql ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ËÂФ·¤Æ update ¤Îµö²Ä¤¬¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó."
- kor "´ç½ÅÀº ´Ù¸¥»ç¿ëÀÚµéÀÇ ¾ÏÈ£¸¦ º¯°æÇÒ ¼ö ÀÖµµ·Ï µ¥ÀÌŸº£À̽º º¯°æ±ÇÇÑÀ» °¡Á®¾ß ÇÕ´Ï´Ù."
- por "Você deve ter privilégios para atualizar tabelas no banco de dados mysql para ser capaz de mudar a senha de outros"
- rum "Trebuie sa aveti privilegii sa actualizati tabelele in bazele de date mysql ca sa puteti sa schimati parolele altora"
- rus "äÌÑ ÔÏÇÏ ÞÔÏÂÙ ÉÚÍÅÎÑÔØ ÐÁÒÏÌÉ ÄÒÕÇÉÈ ÐÏÌØÚÏ×ÁÔÅÌÅÊ, Õ ×ÁÓ ÄÏÌÖÎÙ ÂÙÔØ ÐÒÉ×ÉÌÅÇÉÉ ÎÁ ÉÚÍÅÎÅÎÉÅ ÔÁÂÌÉÃ × ÂÁÚÅ ÄÁÎÎÙÈ mysql"
- serbian "Morate imati privilegije da možete da update-ujete odreðene tabele ako želite da menjate lozinke za druge korisnike"
- spa "Tu debes de tener permiso para actualizar tablas en la base de datos mysql para cambiar las claves para otros"
- swe "För att ändra lösenord för andra måste du ha rättigheter att uppdatera mysql-databasen"
- ukr "÷É ÐÏ×ÉΦ ÍÁÔÉ ÐÒÁ×Ï ÎÁ ÏÎÏ×ÌÅÎÎÑ ÔÁÂÌÉÃØ Õ ÂÁÚ¦ ÄÁÎÎÉÈ mysql, ÁÂÉ ÍÁÔÉ ÍÏÖÌÉצÓÔØ ÚͦÎÀ×ÁÔÉ ÐÁÒÏÌØ ¦ÎÛÉÍ"
-ER_PASSWORD_NO_MATCH 42000
- cze "V tabulce user nen-Bí ¾ádný odpovídající øádek"
- dan "Kan ikke finde nogen tilsvarende poster i bruger tabellen"
- nla "Kan geen enkele passende rij vinden in de gebruikers tabel"
- eng "Can't find any matching row in the user table"
- est "Ei leia vastavat kirjet kasutajate tabelis"
- fre "Impossible de trouver un enregistrement correspondant dans la table user"
- ger "Kann keinen passenden Datensatz in Tabelle 'user' finden"
- greek "Äåí åßíáé äõíáôÞ ç áíåýñåóç ôçò áíôßóôïé÷çò åããñáöÞò óôïí ðßíáêá ôùí ÷ñçóôþí"
- hun "Nincs megegyezo sor a user tablaban"
- ita "Impossibile trovare la riga corrispondente nella tabella user"
- kor "»ç¿ëÀÚ Å×ÀÌºí¿¡¼­ ÀÏÄ¡ÇÏ´Â °ÍÀ» ãÀ» ¼ö ¾øÀ¾´Ï´Ù."
- por "Não pode encontrar nenhuma linha que combine na tabela usuário (user table)"
- rum "Nu pot gasi nici o linie corespunzatoare in tabela utilizatorului"
- rus "îÅ×ÏÚÍÏÖÎÏ ÏÔÙÓËÁÔØ ÐÏÄÈÏÄÑÝÕÀ ÚÁÐÉÓØ × ÔÁÂÌÉÃÅ ÐÏÌØÚÏ×ÁÔÅÌÅÊ"
- serbian "Ne mogu da pronaðem odgovarajuæi slog u 'user' tabeli"
- spa "No puedo encontrar una línea correponsdiente en la tabla user"
- swe "Hittade inte användaren i 'user'-tabellen"
- ukr "îÅ ÍÏÖÕ ÚÎÁÊÔÉ ×¦ÄÐÏצÄÎÉÈ ÚÁÐÉÓ¦× Õ ÔÁÂÌÉæ ËÏÒÉÓÔÕ×ÁÞÁ"
-ER_UPDATE_INFO
- cze "Nalezen-Bých øádkù: %ld Zmìnìno: %ld Varování: %ld"
- dan "Poster fundet: %ld Ændret: %ld Advarsler: %ld"
- nla "Passende rijen: %ld Gewijzigd: %ld Waarschuwingen: %ld"
- eng "Rows matched: %ld Changed: %ld Warnings: %ld"
- jps "ˆê’v”(Rows matched): %ld •ÏX: %ld Warnings: %ld",
- est "Sobinud kirjeid: %ld Muudetud: %ld Hoiatusi: %ld"
- fre "Enregistrements correspondants: %ld Modifiés: %ld Warnings: %ld"
- ger "Datensätze gefunden: %ld Geändert: %ld Warnungen: %ld"
- hun "Megegyezo sorok szama: %ld Valtozott: %ld Warnings: %ld"
- ita "Rows riconosciute: %ld Cambiate: %ld Warnings: %ld"
- jpn "°ìÃ׿ô(Rows matched): %ld Êѹ¹: %ld Warnings: %ld"
- kor "ÀÏÄ¡ÇÏ´Â Rows : %ld°³ º¯°æµÊ: %ld°³ °æ°í: %ld°³"
- por "Linhas que combinaram: %ld - Alteradas: %ld - Avisos: %ld"
- rum "Linii identificate (matched): %ld Schimbate: %ld Atentionari (warnings): %ld"
- rus "óÏ×ÐÁÌÏ ÚÁÐÉÓÅÊ: %ld éÚÍÅÎÅÎÏ: %ld ðÒÅÄÕÐÒÅÖÄÅÎÉÊ: %ld"
- serbian "Odgovarajuæih slogova: %ld Promenjeno: %ld Upozorenja: %ld"
- spa "Líneas correspondientes: %ld Cambiadas: %ld Avisos: %ld"
- swe "Rader: %ld Uppdaterade: %ld Varningar: %ld"
- ukr "úÁÐÉÓ¦× ×¦ÄÐÏצÄÁ¤: %ld úͦÎÅÎÏ: %ld úÁÓÔÅÒÅÖÅÎØ: %ld"
-ER_CANT_CREATE_THREAD
- cze "Nemohu vytvo-Bøit nový thread (errno %d). Pokud je je¹tì nìjaká volná pamì», podívejte se do manuálu na èást o chybách specifických pro jednotlivé operaèní systémy"
- dan "Kan ikke danne en ny tråd (fejl nr. %d). Hvis computeren ikke er løbet tør for hukommelse, kan du se i brugervejledningen for en mulig operativ-system - afhængig fejl"
- nla "Kan geen nieuwe thread aanmaken (Errcode: %d). Indien er geen tekort aan geheugen is kunt u de handleiding consulteren over een mogelijke OS afhankelijke fout"
- eng "Can't create a new thread (errno %d); if you are not out of available memory, you can consult the manual for a possible OS-dependent bug"
- jps "V‹K‚ɃXƒŒƒbƒh‚ªì‚ê‚Ü‚¹‚ñ‚Å‚µ‚½ (errno %d). ‚à‚µÅ‘åŽg—p‹–‰Âƒƒ‚ƒŠ[”‚ð‰z‚¦‚Ä‚¢‚È‚¢‚̂ɃGƒ‰[‚ª”­¶‚µ‚Ä‚¢‚é‚È‚ç, ƒ}ƒjƒ…ƒAƒ‹‚Ì’†‚©‚ç 'possible OS-dependent bug' ‚Æ‚¢‚¤•¶Žš‚ð’T‚µ‚Ä‚­‚Ý‚Ä‚¾‚³‚¢.",
- est "Ei suuda luua uut lõime (veakood %d). Kui mälu ei ole otsas, on tõenäoliselt tegemist operatsioonisüsteemispetsiifilise veaga"
- fre "Impossible de créer une nouvelle tâche (errno %d). S'il reste de la mémoire libre, consultez le manual pour trouver un éventuel bug dépendant de l'OS"
- ger "Kann keinen neuen Thread erzeugen (Fehler: %d). Sollte noch Speicher verfügbar sein, bitte im Handbuch wegen möglicher Fehler im Betriebssystem nachschlagen"
- hun "Uj thread letrehozasa nem lehetseges (Hibakod: %d). Amenyiben van meg szabad memoria, olvassa el a kezikonyv operacios rendszerfuggo hibalehetosegekrol szolo reszet"
- ita "Impossibile creare un nuovo thread (errno %d). Se non ci sono problemi di memoria disponibile puoi consultare il manuale per controllare possibili problemi dipendenti dal SO"
- jpn "¿·µ¬¤Ë¥¹¥ì¥Ã¥É¤¬ºî¤ì¤Þ¤»¤ó¤Ç¤·¤¿ (errno %d). ¤â¤·ºÇÂç»ÈÍѵö²Ä¥á¥â¥ê¡¼¿ô¤ò±Û¤¨¤Æ¤¤¤Ê¤¤¤Î¤Ë¥¨¥é¡¼¤¬È¯À¸¤·¤Æ¤¤¤ë¤Ê¤é, ¥Þ¥Ë¥å¥¢¥ë¤ÎÃ椫¤é 'possible OS-dependent bug' ¤È¤¤¤¦Ê¸»ú¤òõ¤·¤Æ¤¯¤ß¤Æ¤À¤µ¤¤."
- kor "»õ·Î¿î ¾²·¹µå¸¦ ¸¸µé ¼ö ¾ø½À´Ï´Ù.(¿¡·¯¹øÈ£ %d). ¸¸¾à ¿©À¯¸Þ¸ð¸®°¡ ÀÖ´Ù¸é OS-dependent¹ö±× ÀÇ ¸Þ´º¾ó ºÎºÐÀ» ã¾Æº¸½Ã¿À."
- nor "Can't create a new thread (errno %d); if you are not out of available memory you can consult the manual for any possible OS dependent bug"
- norwegian-ny "Can't create a new thread (errno %d); if you are not out of available memory you can consult the manual for any possible OS dependent bug"
- pol "Can't create a new thread (errno %d); if you are not out of available memory you can consult the manual for any possible OS dependent bug"
- por "Não pode criar uma nova 'thread' (erro no. %d). Se você não estiver sem memória disponível, você pode consultar o manual sobre um possível 'bug' dependente do sistema operacional"
- rum "Nu pot crea un thread nou (Eroare %d). Daca mai aveti memorie disponibila in sistem, puteti consulta manualul - ar putea exista un potential bug in legatura cu sistemul de operare"
- rus "îÅ×ÏÚÍÏÖÎÏ ÓÏÚÄÁÔØ ÎÏ×ÙÊ ÐÏÔÏË (ÏÛÉÂËÁ %d). åÓÌÉ ÜÔÏ ÎÅ ÓÉÔÕÁÃÉÑ, Ó×ÑÚÁÎÎÁÑ Ó ÎÅÈ×ÁÔËÏÊ ÐÁÍÑÔÉ, ÔÏ ×ÁÍ ÓÌÅÄÕÅÔ ÉÚÕÞÉÔØ ÄÏËÕÍÅÎÔÁÃÉÀ ÎÁ ÐÒÅÄÍÅÔ ÏÐÉÓÁÎÉÑ ×ÏÚÍÏÖÎÏÊ ÏÛÉÂËÉ ÒÁÂÏÔÙ × ËÏÎËÒÅÔÎÏÊ ïó"
- serbian "Ne mogu da kreiram novi thread (errno %d). Ako imate još slobodne memorije, trebali biste da pogledate u priruèniku da li je ovo specifièna greška vašeg operativnog sistema"
- spa "No puedo crear un nuevo thread (errno %d). Si tu está con falta de memoria disponible, tu puedes consultar el Manual para posibles problemas con SO"
- swe "Kan inte skapa en ny tråd (errno %d)"
- ukr "îÅ ÍÏÖÕ ÓÔ×ÏÒÉÔÉ ÎÏ×Õ Ç¦ÌËÕ (ÐÏÍÉÌËÁ %d). ñËÝÏ ×É ÎÅ ×ÉËÏÒÉÓÔÁÌÉ ÕÓÀ ÐÁÍ'ÑÔØ, ÔÏ ÐÒÏÞÉÔÁÊÔÅ ÄÏËÕÍÅÎÔÁæÀ ÄÏ ×ÁÛϧ ïó - ÍÏÖÌÉ×Ï ÃÅ ÐÏÍÉÌËÁ ïó"
-ER_WRONG_VALUE_COUNT_ON_ROW 21S01
- cze "Po-Bèet sloupcù neodpovídá poètu hodnot na øádku %lu"
- dan "Kolonne antallet stemmer ikke overens med antallet af værdier i post %lu"
- nla "Kolom aantal komt niet overeen met waarde aantal in rij %lu"
- eng "Column count doesn't match value count at row %lu"
- est "Tulpade hulk erineb väärtuste hulgast real %lu"
- ger "Anzahl der Felder stimmt nicht mit der Anzahl der Werte in Zeile %lu überein"
- hun "Az oszlopban talalhato ertek nem egyezik meg a %lu sorban szamitott ertekkel"
- ita "Il numero delle colonne non corrisponde al conteggio alla riga %lu"
- kor "Row %lu¿¡¼­ Ä®·³ Ä«¿îÆ®¿Í value Ä«¿îÅÍ¿Í ÀÏÄ¡ÇÏÁö ¾Ê½À´Ï´Ù."
- por "Contagem de colunas não confere com a contagem de valores na linha %lu"
- rum "Numarul de coloane nu corespunde cu numarul de valori la linia %lu"
- rus "ëÏÌÉÞÅÓÔ×Ï ÓÔÏÌÂÃÏ× ÎÅ ÓÏ×ÐÁÄÁÅÔ Ó ËÏÌÉÞÅÓÔ×ÏÍ ÚÎÁÞÅÎÉÊ × ÚÁÐÉÓÉ %lu"
- serbian "Broj kolona ne odgovara broju vrednosti u slogu %lu"
- spa "El número de columnas no corresponde al número en la línea %lu"
- swe "Antalet kolumner motsvarar inte antalet värden på rad: %lu"
- ukr "ë¦ÌØ˦ÓÔØ ÓÔÏ×ÂÃ¦× ÎÅ ÓЦ×ÐÁÄÁ¤ Ú Ë¦ÌØ˦ÓÔÀ ÚÎÁÞÅÎØ Õ ÓÔÒÏæ %lu"
-ER_CANT_REOPEN_TABLE
- cze "Nemohu znovuotev-Bøít tabulku: '%-.192s"
- dan "Kan ikke genåbne tabel '%-.192s"
- nla "Kan tabel niet opnieuw openen: '%-.192s"
- eng "Can't reopen table: '%-.192s'"
- est "Ei suuda taasavada tabelit '%-.192s'"
- fre "Impossible de réouvrir la table: '%-.192s"
- ger "Kann Tabelle'%-.192s' nicht erneut öffnen"
- hun "Nem lehet ujra-megnyitni a tablat: '%-.192s"
- ita "Impossibile riaprire la tabella: '%-.192s'"
- kor "Å×À̺íÀ» ´Ù½Ã ¿­¼ö ¾ø±º¿ä: '%-.192s"
- nor "Can't reopen table: '%-.192s"
- norwegian-ny "Can't reopen table: '%-.192s"
- pol "Can't reopen table: '%-.192s"
- por "Não pode reabrir a tabela '%-.192s"
- rum "Nu pot redeschide tabela: '%-.192s'"
- rus "îÅ×ÏÚÍÏÖÎÏ ÚÁÎÏ×Ï ÏÔËÒÙÔØ ÔÁÂÌÉÃÕ '%-.192s'"
- serbian "Ne mogu da ponovo otvorim tabelu '%-.192s'"
- slo "Can't reopen table: '%-.192s"
- spa "No puedo reabrir tabla: '%-.192s"
- swe "Kunde inte stänga och öppna tabell '%-.192s"
- ukr "îÅ ÍÏÖÕ ÐÅÒÅצÄËÒÉÔÉ ÔÁÂÌÉÃÀ: '%-.192s'"
-ER_INVALID_USE_OF_NULL 22004
- cze "Neplatn-Bé u¾ití hodnoty NULL"
- dan "Forkert brug af nulværdi (NULL)"
- nla "Foutief gebruik van de NULL waarde"
- eng "Invalid use of NULL value"
- jps "NULL ’l‚ÌŽg—p•û–@‚ª•s“KØ‚Å‚·",
- est "NULL väärtuse väärkasutus"
- fre "Utilisation incorrecte de la valeur NULL"
- ger "Unerlaubte Verwendung eines NULL-Werts"
- hun "A NULL ervenytelen hasznalata"
- ita "Uso scorretto del valore NULL"
- jpn "NULL ÃͤλÈÍÑÊýË¡¤¬ÉÔŬÀڤǤ¹"
- kor "NULL °ªÀ» À߸ø »ç¿ëÇϼ̱º¿ä..."
- por "Uso inválido do valor NULL"
- rum "Folosirea unei value NULL e invalida"
- rus "îÅÐÒÁ×ÉÌØÎÏÅ ÉÓÐÏÌØÚÏ×ÁÎÉÅ ×ÅÌÉÞÉÎÙ NULL"
- serbian "Pogrešna upotreba vrednosti NULL"
- spa "Invalido uso de valor NULL"
- swe "Felaktig använding av NULL"
- ukr "èÉÂÎÅ ×ÉËÏÒÉÓÔÁÎÎÑ ÚÎÁÞÅÎÎÑ NULL"
-ER_REGEXP_ERROR 42000
- cze "Regul-Bární výraz vrátil chybu '%-.64s'"
- dan "Fik fejl '%-.64s' fra regexp"
- nla "Fout '%-.64s' ontvangen van regexp"
- eng "Got error '%-.64s' from regexp"
- est "regexp tagastas vea '%-.64s'"
- fre "Erreur '%-.64s' provenant de regexp"
- ger "regexp lieferte Fehler '%-.64s'"
- hun "'%-.64s' hiba a regularis kifejezes hasznalata soran (regexp)"
- ita "Errore '%-.64s' da regexp"
- kor "regexp¿¡¼­ '%-.64s'°¡ ³µ½À´Ï´Ù."
- por "Obteve erro '%-.64s' em regexp"
- rum "Eroarea '%-.64s' obtinuta din expresia regulara (regexp)"
- rus "ðÏÌÕÞÅÎÁ ÏÛÉÂËÁ '%-.64s' ÏÔ ÒÅÇÕÌÑÒÎÏÇÏ ×ÙÒÁÖÅÎÉÑ"
- serbian "Funkcija regexp je vratila grešku '%-.64s'"
- spa "Obtenido error '%-.64s' de regexp"
- swe "Fick fel '%-.64s' från REGEXP"
- ukr "ïÔÒÉÍÁÎÏ ÐÏÍÉÌËÕ '%-.64s' ×¦Ä ÒÅÇÕÌÑÒÎÏÇÏ ×ÉÒÁÚÕ"
-ER_MIX_OF_GROUP_FUNC_AND_FIELDS 42000
- cze "Pokud nen-Bí ¾ádná GROUP BY klauzule, není dovoleno souèasné pou¾ití GROUP polo¾ek (MIN(),MAX(),COUNT()...) s ne GROUP polo¾kami"
- dan "Sammenblanding af GROUP kolonner (MIN(),MAX(),COUNT()...) uden GROUP kolonner er ikke tilladt, hvis der ikke er noget GROUP BY prædikat"
- nla "Het mixen van GROUP kolommen (MIN(),MAX(),COUNT()...) met no-GROUP kolommen is foutief indien er geen GROUP BY clausule is"
- eng "Mixing of GROUP columns (MIN(),MAX(),COUNT(),...) with no GROUP columns is illegal if there is no GROUP BY clause"
- est "GROUP tulpade (MIN(),MAX(),COUNT()...) kooskasutamine tavaliste tulpadega ilma GROUP BY klauslita ei ole lubatud"
- fre "Mélanger les colonnes GROUP (MIN(),MAX(),COUNT()...) avec des colonnes normales est interdit s'il n'y a pas de clause GROUP BY"
- ger "Das Vermischen von GROUP-Feldern (MIN(),MAX(),COUNT()...) mit Nicht-GROUP-Feldern ist nicht zulässig, wenn keine GROUP-BY-Klausel vorhanden ist"
- hun "A GROUP mezok (MIN(),MAX(),COUNT()...) kevert hasznalata nem lehetseges GROUP BY hivatkozas nelkul"
- ita "Il mescolare funzioni di aggregazione (MIN(),MAX(),COUNT()...) e non e` illegale se non c'e` una clausula GROUP BY"
- kor "Mixing of GROUP Ä®·³s (MIN(),MAX(),COUNT(),...) with no GROUP Ä®·³s is illegal if there is no GROUP BY clause"
- por "Mistura de colunas agrupadas (com MIN(), MAX(), COUNT(), ...) com colunas não agrupadas é ilegal, se não existir uma cláusula de agrupamento (cláusula GROUP BY)"
- rum "Amestecarea de coloane GROUP (MIN(),MAX(),COUNT()...) fara coloane GROUP este ilegala daca nu exista o clauza GROUP BY"
- rus "ïÄÎÏ×ÒÅÍÅÎÎÏÅ ÉÓÐÏÌØÚÏ×ÁÎÉÅ ÓÇÒÕÐÐÉÒÏ×ÁÎÎÙÈ (GROUP) ÓÔÏÌÂÃÏ× (MIN(),MAX(),COUNT(),...) Ó ÎÅÓÇÒÕÐÐÉÒÏ×ÁÎÎÙÍÉ ÓÔÏÌÂÃÁÍÉ Ñ×ÌÑÅÔÓÑ ÎÅËÏÒÒÅËÔÎÙÍ, ÅÓÌÉ × ×ÙÒÁÖÅÎÉÉ ÅÓÔØ GROUP BY"
- serbian "Upotreba agregatnih funkcija (MIN(),MAX(),COUNT()...) bez 'GROUP' kolona je pogrešna ako ne postoji 'GROUP BY' iskaz"
- spa "Mezcla de columnas GROUP (MIN(),MAX(),COUNT()...) con no GROUP columnas es ilegal si no hat la clausula GROUP BY"
- swe "Man får ha både GROUP-kolumner (MIN(),MAX(),COUNT()...) och fält i en fråga om man inte har en GROUP BY-del"
- ukr "úͦÛÕ×ÁÎÎÑ GROUP ÓÔÏ×ÂÃ¦× (MIN(),MAX(),COUNT()...) Ú ÎÅ GROUP ÓÔÏ×ÂÃÑÍÉ ¤ ÚÁÂÏÒÏÎÅÎÉÍ, ÑËÝÏ ÎÅ ÍÁ¤ GROUP BY"
-ER_NONEXISTING_GRANT 42000
- cze "Neexistuje odpov-Bídající grant pro u¾ivatele '%-.48s' na stroji '%-.64s'"
- dan "Denne tilladelse findes ikke for brugeren '%-.48s' på vært '%-.64s'"
- nla "Deze toegang (GRANT) is niet toegekend voor gebruiker '%-.48s' op host '%-.64s'"
- eng "There is no such grant defined for user '%-.48s' on host '%-.64s'"
- jps "ƒ†[ƒU[ '%-.48s' (ƒzƒXƒg '%-.64s' ‚̃†[ƒU[) ‚Í‹–‰Â‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ",
- est "Sellist õigust ei ole defineeritud kasutajale '%-.48s' masinast '%-.64s'"
- fre "Un tel droit n'est pas défini pour l'utilisateur '%-.48s' sur l'hôte '%-.64s'"
- ger "Für Benutzer '%-.48s' auf Host '%-.64s' gibt es keine solche Berechtigung"
- hun "A '%-.48s' felhasznalonak nincs ilyen joga a '%-.64s' host-on"
- ita "GRANT non definita per l'utente '%-.48s' dalla macchina '%-.64s'"
- jpn "¥æ¡¼¥¶¡¼ '%-.48s' (¥Û¥¹¥È '%-.64s' ¤Î¥æ¡¼¥¶¡¼) ¤Ïµö²Ä¤µ¤ì¤Æ¤¤¤Þ¤»¤ó"
- kor "»ç¿ëÀÚ '%-.48s' (È£½ºÆ® '%-.64s')¸¦ À§ÇÏ¿© Á¤ÀÇµÈ ±×·± ½ÂÀÎÀº ¾ø½À´Ï´Ù."
- por "Não existe tal permissão (grant) definida para o usuário '%-.48s' no 'host' '%-.64s'"
- rum "Nu exista un astfel de grant definit pentru utilzatorul '%-.48s' de pe host-ul '%-.64s'"
- rus "ôÁËÉÅ ÐÒÁ×Á ÎÅ ÏÐÒÅÄÅÌÅÎÙ ÄÌÑ ÐÏÌØÚÏ×ÁÔÅÌÑ '%-.48s' ÎÁ ÈÏÓÔÅ '%-.64s'"
- serbian "Ne postoji odobrenje za pristup korisniku '%-.48s' na host-u '%-.64s'"
- spa "No existe permiso definido para usuario '%-.48s' en el servidor '%-.64s'"
- swe "Det finns inget privilegium definierat för användare '%-.48s' på '%-.64s'"
- ukr "ðÏ×ÎÏ×ÁÖÅÎØ ÎÅ ×ÉÚÎÁÞÅÎÏ ÄÌÑ ËÏÒÉÓÔÕ×ÁÞÁ '%-.48s' Ú ÈÏÓÔÕ '%-.64s'"
-ER_TABLEACCESS_DENIED_ERROR 42000
- cze "%-.16s p-Bøíkaz nepøístupný pro u¾ivatele: '%-.48s'@'%-.64s' pro tabulku '%-.192s'"
- dan "%-.16s-kommandoen er ikke tilladt for brugeren '%-.48s'@'%-.64s' for tabellen '%-.192s'"
- nla "%-.16s commando geweigerd voor gebruiker: '%-.48s'@'%-.64s' voor tabel '%-.192s'"
- eng "%-.16s command denied to user '%-.48s'@'%-.64s' for table '%-.192s'"
- jps "ƒRƒ}ƒ“ƒh %-.16s ‚Í ƒ†[ƒU[ '%-.48s'@'%-.64s' ,ƒe[ƒuƒ‹ '%-.192s' ‚ɑ΂µ‚Ä‹–‰Â‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ",
- est "%-.16s käsk ei ole lubatud kasutajale '%-.48s'@'%-.64s' tabelis '%-.192s'"
- fre "La commande '%-.16s' est interdite à l'utilisateur: '%-.48s'@'@%-.64s' sur la table '%-.192s'"
- ger "%-.16s Befehl nicht erlaubt für Benutzer '%-.48s'@'%-.64s' auf Tabelle '%-.192s'"
- hun "%-.16s parancs a '%-.48s'@'%-.64s' felhasznalo szamara nem engedelyezett a '%-.192s' tablaban"
- ita "Comando %-.16s negato per l'utente: '%-.48s'@'%-.64s' sulla tabella '%-.192s'"
- jpn "¥³¥Þ¥ó¥É %-.16s ¤Ï ¥æ¡¼¥¶¡¼ '%-.48s'@'%-.64s' ,¥Æ¡¼¥Ö¥ë '%-.192s' ¤ËÂФ·¤Æµö²Ä¤µ¤ì¤Æ¤¤¤Þ¤»¤ó"
- kor "'%-.16s' ¸í·ÉÀº ´ÙÀ½ »ç¿ëÀÚ¿¡°Ô °ÅºÎµÇ¾ú½À´Ï´Ù. : '%-.48s'@'%-.64s' for Å×À̺í '%-.192s'"
- por "Comando '%-.16s' negado para o usuário '%-.48s'@'%-.64s' na tabela '%-.192s'"
- rum "Comanda %-.16s interzisa utilizatorului: '%-.48s'@'%-.64s' pentru tabela '%-.192s'"
- rus "ëÏÍÁÎÄÁ %-.16s ÚÁÐÒÅÝÅÎÁ ÐÏÌØÚÏ×ÁÔÅÌÀ '%-.48s'@'%-.64s' ÄÌÑ ÔÁÂÌÉÃÙ '%-.192s'"
- serbian "%-.16s komanda zabranjena za korisnika '%-.48s'@'%-.64s' za tabelu '%-.192s'"
- spa "%-.16s comando negado para usuario: '%-.48s'@'%-.64s' para tabla '%-.192s'"
- swe "%-.16s ej tillåtet för '%-.48s'@'%-.64s' för tabell '%-.192s'"
- ukr "%-.16s ËÏÍÁÎÄÁ ÚÁÂÏÒÏÎÅÎÁ ËÏÒÉÓÔÕ×ÁÞÕ: '%-.48s'@'%-.64s' Õ ÔÁÂÌÉæ '%-.192s'"
-ER_COLUMNACCESS_DENIED_ERROR 42000
- cze "%-.16s p-Bøíkaz nepøístupný pro u¾ivatele: '%-.48s'@'%-.64s' pro sloupec '%-.192s' v tabulce '%-.192s'"
- dan "%-.16s-kommandoen er ikke tilladt for brugeren '%-.48s'@'%-.64s' for kolonne '%-.192s' in tabellen '%-.192s'"
- nla "%-.16s commando geweigerd voor gebruiker: '%-.48s'@'%-.64s' voor kolom '%-.192s' in tabel '%-.192s'"
- eng "%-.16s command denied to user '%-.48s'@'%-.64s' for column '%-.192s' in table '%-.192s'"
- jps "ƒRƒ}ƒ“ƒh %-.16s ‚Í ƒ†[ƒU[ '%-.48s'@'%-.64s'\n ƒJƒ‰ƒ€ '%-.192s' ƒe[ƒuƒ‹ '%-.192s' ‚ɑ΂µ‚Ä‹–‰Â‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ",
- est "%-.16s käsk ei ole lubatud kasutajale '%-.48s'@'%-.64s' tulbale '%-.192s' tabelis '%-.192s'"
- fre "La commande '%-.16s' est interdite à l'utilisateur: '%-.48s'@'@%-.64s' sur la colonne '%-.192s' de la table '%-.192s'"
- ger "%-.16s Befehl nicht erlaubt für Benutzer '%-.48s'@'%-.64s' und Feld '%-.192s' in Tabelle '%-.192s'"
- hun "%-.16s parancs a '%-.48s'@'%-.64s' felhasznalo szamara nem engedelyezett a '%-.192s' mezo eseten a '%-.192s' tablaban"
- ita "Comando %-.16s negato per l'utente: '%-.48s'@'%-.64s' sulla colonna '%-.192s' della tabella '%-.192s'"
- jpn "¥³¥Þ¥ó¥É %-.16s ¤Ï ¥æ¡¼¥¶¡¼ '%-.48s'@'%-.64s'\n ¥«¥é¥à '%-.192s' ¥Æ¡¼¥Ö¥ë '%-.192s' ¤ËÂФ·¤Æµö²Ä¤µ¤ì¤Æ¤¤¤Þ¤»¤ó"
- kor "'%-.16s' ¸í·ÉÀº ´ÙÀ½ »ç¿ëÀÚ¿¡°Ô °ÅºÎµÇ¾ú½À´Ï´Ù. : '%-.48s'@'%-.64s' for Ä®·³ '%-.192s' in Å×À̺í '%-.192s'"
- por "Comando '%-.16s' negado para o usuário '%-.48s'@'%-.64s' na coluna '%-.192s', na tabela '%-.192s'"
- rum "Comanda %-.16s interzisa utilizatorului: '%-.48s'@'%-.64s' pentru coloana '%-.192s' in tabela '%-.192s'"
- rus "ëÏÍÁÎÄÁ %-.16s ÚÁÐÒÅÝÅÎÁ ÐÏÌØÚÏ×ÁÔÅÌÀ '%-.48s'@'%-.64s' ÄÌÑ ÓÔÏÌÂÃÁ '%-.192s' × ÔÁÂÌÉÃÅ '%-.192s'"
- serbian "%-.16s komanda zabranjena za korisnika '%-.48s'@'%-.64s' za kolonu '%-.192s' iz tabele '%-.192s'"
- spa "%-.16s comando negado para usuario: '%-.48s'@'%-.64s' para columna '%-.192s' en la tabla '%-.192s'"
- swe "%-.16s ej tillåtet för '%-.48s'@'%-.64s' för kolumn '%-.192s' i tabell '%-.192s'"
- ukr "%-.16s ËÏÍÁÎÄÁ ÚÁÂÏÒÏÎÅÎÁ ËÏÒÉÓÔÕ×ÁÞÕ: '%-.48s'@'%-.64s' ÄÌÑ ÓÔÏ×ÂÃÑ '%-.192s' Õ ÔÁÂÌÉæ '%-.192s'"
-ER_ILLEGAL_GRANT_FOR_TABLE 42000
- cze "Neplatn-Bý pøíkaz GRANT/REVOKE. Prosím, pøeètìte si v manuálu, jaká privilegia je mo¾né pou¾ít."
- dan "Forkert GRANT/REVOKE kommando. Se i brugervejledningen hvilke privilegier der kan specificeres."
- nla "Foutief GRANT/REVOKE commando. Raadpleeg de handleiding welke priveleges gebruikt kunnen worden."
- eng "Illegal GRANT/REVOKE command; please consult the manual to see which privileges can be used"
- est "Vigane GRANT/REVOKE käsk. Tutvu kasutajajuhendiga"
- fre "Commande GRANT/REVOKE incorrecte. Consultez le manuel."
- ger "Unzulässiger GRANT- oder REVOKE-Befehl. Verfügbare Berechtigungen sind im Handbuch aufgeführt"
- greek "Illegal GRANT/REVOKE command; please consult the manual to see which privileges can be used."
- hun "Ervenytelen GRANT/REVOKE parancs. Kerem, nezze meg a kezikonyvben, milyen jogok lehetsegesek"
- ita "Comando GRANT/REVOKE illegale. Prego consultare il manuale per sapere quali privilegi possono essere usati."
- jpn "Illegal GRANT/REVOKE command; please consult the manual to see which privleges can be used."
- kor "À߸øµÈ GRANT/REVOKE ¸í·É. ¾î¶² ±Ç¸®¿Í ½ÂÀÎÀÌ »ç¿ëµÇ¾î Áú ¼ö ÀÖ´ÂÁö ¸Þ´º¾óÀ» º¸½Ã¿À."
- nor "Illegal GRANT/REVOKE command; please consult the manual to see which privleges can be used."
- norwegian-ny "Illegal GRANT/REVOKE command; please consult the manual to see which privleges can be used."
- pol "Illegal GRANT/REVOKE command; please consult the manual to see which privleges can be used."
- por "Comando GRANT/REVOKE ilegal. Por favor consulte no manual quais privilégios podem ser usados."
- rum "Comanda GRANT/REVOKE ilegala. Consultati manualul in privinta privilegiilor ce pot fi folosite."
- rus "îÅ×ÅÒÎÁÑ ËÏÍÁÎÄÁ GRANT ÉÌÉ REVOKE. ïÂÒÁÔÉÔÅÓØ Ë ÄÏËÕÍÅÎÔÁÃÉÉ, ÞÔÏÂÙ ×ÙÑÓÎÉÔØ, ËÁËÉÅ ÐÒÉ×ÉÌÅÇÉÉ ÍÏÖÎÏ ÉÓÐÏÌØÚÏ×ÁÔØ"
- serbian "Pogrešna 'GRANT' odnosno 'REVOKE' komanda. Molim Vas pogledajte u priruèniku koje vrednosti mogu biti upotrebljene."
- slo "Illegal GRANT/REVOKE command; please consult the manual to see which privleges can be used."
- spa "Ilegal comando GRANT/REVOKE. Por favor consulte el manual para cuales permisos pueden ser usados."
- swe "Felaktigt GRANT-privilegium använt"
- ukr "èÉÂÎÁ GRANT/REVOKE ËÏÍÁÎÄÁ; ÐÒÏÞÉÔÁÊÔÅ ÄÏËÕÍÅÎÔÁæÀ ÓÔÏÓÏ×ÎÏ ÔÏÇÏ, Ñ˦ ÐÒÁ×Á ÍÏÖÎÁ ×ÉËÏÒÉÓÔÏ×Õ×ÁÔÉ"
-ER_GRANT_WRONG_HOST_OR_USER 42000
- cze "Argument p-Bøíkazu GRANT u¾ivatel nebo stroj je pøíli¹ dlouhý"
- dan "Værts- eller brugernavn for langt til GRANT"
- nla "De host of gebruiker parameter voor GRANT is te lang"
- eng "The host or user argument to GRANT is too long"
- est "Masina või kasutaja nimi GRANT lauses on liiga pikk"
- fre "L'hôte ou l'utilisateur donné en argument à GRANT est trop long"
- ger "Das Host- oder User-Argument für GRANT ist zu lang"
- hun "A host vagy felhasznalo argumentuma tul hosszu a GRANT parancsban"
- ita "L'argomento host o utente per la GRANT e` troppo lungo"
- kor "½ÂÀÎ(GRANT)À» À§ÇÏ¿© »ç¿ëÇÑ »ç¿ëÀÚ³ª È£½ºÆ®ÀÇ °ªµéÀÌ ³Ê¹« ±é´Ï´Ù."
- por "Argumento de 'host' ou de usuário para o GRANT é longo demais"
- rum "Argumentul host-ului sau utilizatorului pentru GRANT e prea lung"
- rus "óÌÉÛËÏÍ ÄÌÉÎÎÏÅ ÉÍÑ ÐÏÌØÚÏ×ÁÔÅÌÑ/ÈÏÓÔÁ ÄÌÑ GRANT"
- serbian "Argument 'host' ili 'korisnik' prosleðen komandi 'GRANT' je predugaèak"
- spa "El argumento para servidor o usuario para GRANT es demasiado grande"
- swe "Felaktigt maskinnamn eller användarnamn använt med GRANT"
- ukr "áÒÇÕÍÅÎÔ host ÁÂÏ user ÄÌÑ GRANT ÚÁÄÏ×ÇÉÊ"
-ER_NO_SUCH_TABLE 42S02
- cze "Tabulka '%-.192s.%-.192s' neexistuje"
- dan "Tabellen '%-.192s.%-.192s' eksisterer ikke"
- nla "Tabel '%-.192s.%-.192s' bestaat niet"
- eng "Table '%-.192s.%-.192s' doesn't exist"
- est "Tabelit '%-.192s.%-.192s' ei eksisteeri"
- fre "La table '%-.192s.%-.192s' n'existe pas"
- ger "Tabelle '%-.192s.%-.192s' existiert nicht"
- hun "A '%-.192s.%-.192s' tabla nem letezik"
- ita "La tabella '%-.192s.%-.192s' non esiste"
- jpn "Table '%-.192s.%-.192s' doesn't exist"
- kor "Å×À̺í '%-.192s.%-.192s' ´Â Á¸ÀçÇÏÁö ¾Ê½À´Ï´Ù."
- nor "Table '%-.192s.%-.192s' doesn't exist"
- norwegian-ny "Table '%-.192s.%-.192s' doesn't exist"
- pol "Table '%-.192s.%-.192s' doesn't exist"
- por "Tabela '%-.192s.%-.192s' não existe"
- rum "Tabela '%-.192s.%-.192s' nu exista"
- rus "ôÁÂÌÉÃÁ '%-.192s.%-.192s' ÎÅ ÓÕÝÅÓÔ×ÕÅÔ"
- serbian "Tabela '%-.192s.%-.192s' ne postoji"
- slo "Table '%-.192s.%-.192s' doesn't exist"
- spa "Tabla '%-.192s.%-.192s' no existe"
- swe "Det finns ingen tabell som heter '%-.192s.%-.192s'"
- ukr "ôÁÂÌÉÃÑ '%-.192s.%-.192s' ÎÅ ¦ÓÎÕ¤"
-ER_NONEXISTING_TABLE_GRANT 42000
- cze "Neexistuje odpov-Bídající grant pro u¾ivatele '%-.48s' na stroji '%-.64s' pro tabulku '%-.192s'"
- dan "Denne tilladelse eksisterer ikke for brugeren '%-.48s' på vært '%-.64s' for tabellen '%-.192s'"
- nla "Deze toegang (GRANT) is niet toegekend voor gebruiker '%-.48s' op host '%-.64s' op tabel '%-.192s'"
- eng "There is no such grant defined for user '%-.48s' on host '%-.64s' on table '%-.192s'"
- est "Sellist õigust ei ole defineeritud kasutajale '%-.48s' masinast '%-.64s' tabelile '%-.192s'"
- fre "Un tel droit n'est pas défini pour l'utilisateur '%-.48s' sur l'hôte '%-.64s' sur la table '%-.192s'"
- ger "Eine solche Berechtigung ist für User '%-.48s' auf Host '%-.64s' an Tabelle '%-.192s' nicht definiert"
- hun "A '%-.48s' felhasznalo szamara a '%-.64s' host '%-.192s' tablajaban ez a parancs nem engedelyezett"
- ita "GRANT non definita per l'utente '%-.48s' dalla macchina '%-.64s' sulla tabella '%-.192s'"
- kor "»ç¿ëÀÚ '%-.48s'(È£½ºÆ® '%-.64s')´Â Å×À̺í '%-.192s'¸¦ »ç¿ëÇϱâ À§ÇÏ¿© Á¤ÀÇµÈ ½ÂÀÎÀº ¾ø½À´Ï´Ù. "
- por "Não existe tal permissão (grant) definido para o usuário '%-.48s' no 'host' '%-.64s', na tabela '%-.192s'"
- rum "Nu exista un astfel de privilegiu (grant) definit pentru utilizatorul '%-.48s' de pe host-ul '%-.64s' pentru tabela '%-.192s'"
- rus "ôÁËÉÅ ÐÒÁ×Á ÎÅ ÏÐÒÅÄÅÌÅÎÙ ÄÌÑ ÐÏÌØÚÏ×ÁÔÅÌÑ '%-.48s' ÎÁ ËÏÍÐØÀÔÅÒÅ '%-.64s' ÄÌÑ ÔÁÂÌÉÃÙ '%-.192s'"
- serbian "Ne postoji odobrenje za pristup korisniku '%-.48s' na host-u '%-.64s' tabeli '%-.192s'"
- spa "No existe tal permiso definido para usuario '%-.48s' en el servidor '%-.64s' en la tabla '%-.192s'"
- swe "Det finns inget privilegium definierat för användare '%-.48s' på '%-.64s' för tabell '%-.192s'"
- ukr "ðÏ×ÎÏ×ÁÖÅÎØ ÎÅ ×ÉÚÎÁÞÅÎÏ ÄÌÑ ËÏÒÉÓÔÕ×ÁÞÁ '%-.48s' Ú ÈÏÓÔÕ '%-.64s' ÄÌÑ ÔÁÂÌÉæ '%-.192s'"
-ER_NOT_ALLOWED_COMMAND 42000
- cze "Pou-B¾itý pøíkaz není v této verzi MySQL povolen"
- dan "Den brugte kommando er ikke tilladt med denne udgave af MySQL"
- nla "Het used commando is niet toegestaan in deze MySQL versie"
- eng "The used command is not allowed with this MySQL version"
- est "Antud käsk ei ole lubatud käesolevas MySQL versioonis"
- fre "Cette commande n'existe pas dans cette version de MySQL"
- ger "Der verwendete Befehl ist in dieser MySQL-Version nicht zulässig"
- hun "A hasznalt parancs nem engedelyezett ebben a MySQL verzioban"
- ita "Il comando utilizzato non e` supportato in questa versione di MySQL"
- kor "»ç¿ëµÈ ¸í·ÉÀº ÇöÀçÀÇ MySQL ¹öÁ¯¿¡¼­´Â ÀÌ¿ëµÇÁö ¾Ê½À´Ï´Ù."
- por "Comando usado não é permitido para esta versão do MySQL"
- rum "Comanda folosita nu este permisa pentru aceasta versiune de MySQL"
- rus "üÔÁ ËÏÍÁÎÄÁ ÎÅ ÄÏÐÕÓËÁÅÔÓÑ × ÄÁÎÎÏÊ ×ÅÒÓÉÉ MySQL"
- serbian "Upotrebljena komanda nije dozvoljena sa ovom verzijom MySQL servera"
- spa "El comando usado no es permitido con esta versión de MySQL"
- swe "Du kan inte använda detta kommando med denna MySQL version"
- ukr "÷ÉËÏÒÉÓÔÏ×Õ×ÁÎÁ ËÏÍÁÎÄÁ ÎÅ ÄÏÚ×ÏÌÅÎÁ Õ Ã¦Ê ×ÅÒÓ¦§ MySQL"
-ER_SYNTAX_ERROR 42000
- cze "Va-B¹e syntaxe je nìjaká divná"
- dan "Der er en fejl i SQL syntaksen"
- nla "Er is iets fout in de gebruikte syntax"
- eng "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use"
- est "Viga SQL süntaksis"
- fre "Erreur de syntaxe"
- ger "Fehler in der SQL-Syntax. Bitte die korrekte Syntax im Handbuch nachschlagen"
- greek "You have an error in your SQL syntax"
- hun "Szintaktikai hiba"
- ita "Errore di sintassi nella query SQL"
- jpn "Something is wrong in your syntax"
- kor "SQL ±¸¹®¿¡ ¿À·ù°¡ ÀÖ½À´Ï´Ù."
- nor "Something is wrong in your syntax"
- norwegian-ny "Something is wrong in your syntax"
- pol "Something is wrong in your syntax"
- por "Você tem um erro de sintaxe no seu SQL"
- rum "Aveti o eroare in sintaxa RSQL"
- rus "õ ×ÁÓ ÏÛÉÂËÁ × ÚÁÐÒÏÓÅ. éÚÕÞÉÔÅ ÄÏËÕÍÅÎÔÁÃÉÀ ÐÏ ÉÓÐÏÌØÚÕÅÍÏÊ ×ÅÒÓÉÉ MySQL ÎÁ ÐÒÅÄÍÅÔ ËÏÒÒÅËÔÎÏÇÏ ÓÉÎÔÁËÓÉÓÁ"
- serbian "Imate grešku u vašoj SQL sintaksi"
- slo "Something is wrong in your syntax"
- spa "Algo está equivocado en su sintax"
- swe "Du har något fel i din syntax"
- ukr "õ ×ÁÓ ÐÏÍÉÌËÁ Õ ÓÉÎÔÁËÓÉÓ¦ SQL"
-ER_DELAYED_CANT_CHANGE_LOCK
- cze "Zpo-B¾dìný insert threadu nebyl schopen získat po¾adovaný zámek pro tabulku %-.192s"
- dan "Forsinket indsættelse tråden (delayed insert thread) kunne ikke opnå lås på tabellen %-.192s"
- nla "'Delayed insert' thread kon de aangevraagde 'lock' niet krijgen voor tabel %-.192s"
- eng "Delayed insert thread couldn't get requested lock for table %-.192s"
- est "INSERT DELAYED lõim ei suutnud saada soovitud lukku tabelile %-.192s"
- fre "La tâche 'delayed insert' n'a pas pu obtenir le verrou démandé sur la table %-.192s"
- ger "Verzögerter (DELAYED) Einfüge-Thread konnte die angeforderte Sperre für Tabelle '%-.192s' nicht erhalten"
- hun "A kesleltetett beillesztes (delayed insert) thread nem kapott zatolast a %-.192s tablahoz"
- ita "Il thread di inserimento ritardato non riesce ad ottenere il lock per la tabella %-.192s"
- kor "Áö¿¬µÈ insert ¾²·¹µå°¡ Å×À̺í %-.192sÀÇ ¿ä±¸µÈ ¶ôÅ·À» ó¸®ÇÒ ¼ö ¾ø¾ú½À´Ï´Ù."
- por "'Thread' de inserção retardada (atrasada) pois não conseguiu obter a trava solicitada para tabela '%-.192s'"
- rum "Thread-ul pentru inserarea aminata nu a putut obtine lacatul (lock) pentru tabela %-.192s"
- rus "ðÏÔÏË, ÏÂÓÌÕÖÉ×ÁÀÝÉÊ ÏÔÌÏÖÅÎÎÕÀ ×ÓÔÁ×ËÕ (delayed insert), ÎÅ ÓÍÏÇ ÐÏÌÕÞÉÔØ ÚÁÐÒÁÛÉ×ÁÅÍÕÀ ÂÌÏËÉÒÏ×ËÕ ÎÁ ÔÁÂÌÉÃÕ %-.192s"
- serbian "Prolongirani 'INSERT' thread nije mogao da dobije traženo zakljuèavanje tabele '%-.192s'"
- spa "Thread de inserción retarda no pudiendo bloquear para la tabla %-.192s"
- swe "DELAYED INSERT-tråden kunde inte låsa tabell '%-.192s'"
- ukr "ç¦ÌËÁ ÄÌÑ INSERT DELAYED ÎÅ ÍÏÖÅ ÏÔÒÉÍÁÔÉ ÂÌÏËÕ×ÁÎÎÑ ÄÌÑ ÔÁÂÌÉæ %-.192s"
-ER_TOO_MANY_DELAYED_THREADS
- cze "P-Bøíli¹ mnoho zpo¾dìných threadù"
- dan "For mange slettede tråde (threads) i brug"
- nla "Te veel 'delayed' threads in gebruik"
- eng "Too many delayed threads in use"
- est "Liiga palju DELAYED lõimesid kasutusel"
- fre "Trop de tâche 'delayed' en cours"
- ger "Zu viele verzögerte (DELAYED) Threads in Verwendung"
- hun "Tul sok kesletetett thread (delayed)"
- ita "Troppi threads ritardati in uso"
- kor "³Ê¹« ¸¹Àº Áö¿¬ ¾²·¹µå¸¦ »ç¿ëÇÏ°í ÀÖ½À´Ï´Ù."
- por "Excesso de 'threads' retardadas (atrasadas) em uso"
- rum "Prea multe threaduri aminate care sint in uz"
- rus "óÌÉÛËÏÍ ÍÎÏÇÏ ÐÏÔÏËÏ×, ÏÂÓÌÕÖÉ×ÁÀÝÉÈ ÏÔÌÏÖÅÎÎÕÀ ×ÓÔÁ×ËÕ (delayed insert)"
- serbian "Previše prolongiranih thread-ova je u upotrebi"
- spa "Muchos threads retardados en uso"
- swe "Det finns redan 'max_delayed_threads' trådar i använding"
- ukr "úÁÂÁÇÁÔÏ ÚÁÔÒÉÍÁÎÉÈ Ç¦ÌÏË ×ÉËÏÒÉÓÔÏ×Õ¤ÔØÓÑ"
-ER_ABORTING_CONNECTION 08S01
- cze "Zru-B¹eno spojení %ld do databáze: '%-.192s' u¾ivatel: '%-.48s' (%-.64s)"
- dan "Afbrudt forbindelse %ld til database: '%-.192s' bruger: '%-.48s' (%-.64s)"
- nla "Afgebroken verbinding %ld naar db: '%-.192s' gebruiker: '%-.48s' (%-.64s)"
- eng "Aborted connection %ld to db: '%-.192s' user: '%-.48s' (%-.64s)"
- est "Ühendus katkestatud %ld andmebaasile: '%-.192s' kasutajale: '%-.48s' (%-.64s)"
- fre "Connection %ld avortée vers la bd: '%-.192s' utilisateur: '%-.48s' (%-.64s)"
- ger "Abbruch der Verbindung %ld zur Datenbank '%-.192s'. Benutzer: '%-.48s' (%-.64s)"
- hun "Megszakitott kapcsolat %ld db: '%-.192s' adatbazishoz, felhasznalo: '%-.48s' (%-.64s)"
- ita "Interrotta la connessione %ld al db: '%-.192s' utente: '%-.48s' (%-.64s)"
- jpn "Aborted connection %ld to db: '%-.192s' user: '%-.48s' (%-.64s)"
- kor "µ¥ÀÌŸº£À̽º Á¢¼ÓÀ» À§ÇÑ ¿¬°á %ld°¡ Áß´ÜµÊ : '%-.192s' »ç¿ëÀÚ: '%-.48s' (%-.64s)"
- nor "Aborted connection %ld to db: '%-.192s' user: '%-.48s' (%-.64s)"
- norwegian-ny "Aborted connection %ld to db: '%-.192s' user: '%-.48s' (%-.64s)"
- pol "Aborted connection %ld to db: '%-.192s' user: '%-.48s' (%-.64s)"
- por "Conexão %ld abortou para o banco de dados '%-.192s' - usuário '%-.48s' (%-.64s)"
- rum "Conectie terminata %ld la baza de date: '%-.192s' utilizator: '%-.48s' (%-.64s)"
- rus "ðÒÅÒ×ÁÎÏ ÓÏÅÄÉÎÅÎÉÅ %ld Ë ÂÁÚÅ ÄÁÎÎÙÈ '%-.192s' ÐÏÌØÚÏ×ÁÔÅÌÑ '%-.48s' (%-.64s)"
- serbian "Prekinuta konekcija broj %ld ka bazi: '%-.192s' korisnik je bio: '%-.48s' (%-.64s)"
- slo "Aborted connection %ld to db: '%-.192s' user: '%-.48s' (%-.64s)"
- spa "Conexión abortada %ld para db: '%-.192s' usuario: '%-.48s' (%-.64s)"
- swe "Avbröt länken för tråd %ld till db '%-.192s', användare '%-.48s' (%-.64s)"
- ukr "ðÅÒÅÒ×ÁÎÏ Ú'¤ÄÎÁÎÎÑ %ld ÄÏ ÂÁÚÉ ÄÁÎÎÉÈ: '%-.192s' ËÏÒÉÓÔÕ×ÁÞÁ: '%-.48s' (%-.64s)"
-ER_NET_PACKET_TOO_LARGE 08S01
- cze "Zji-B¹tìn pøíchozí packet del¹í ne¾ 'max_allowed_packet'"
- dan "Modtog en datapakke som var større end 'max_allowed_packet'"
- nla "Groter pakket ontvangen dan 'max_allowed_packet'"
- eng "Got a packet bigger than 'max_allowed_packet' bytes"
- est "Saabus suurem pakett kui lubatud 'max_allowed_packet' muutujaga"
- fre "Paquet plus grand que 'max_allowed_packet' reçu"
- ger "Empfangenes Paket ist größer als 'max_allowed_packet' Bytes"
- hun "A kapott csomag nagyobb, mint a maximalisan engedelyezett: 'max_allowed_packet'"
- ita "Ricevuto un pacchetto piu` grande di 'max_allowed_packet'"
- kor "'max_allowed_packet'º¸´Ù ´õÅ« ÆÐŶÀ» ¹Þ¾Ò½À´Ï´Ù."
- por "Obteve um pacote maior do que a taxa máxima de pacotes definida (max_allowed_packet)"
- rum "Un packet mai mare decit 'max_allowed_packet' a fost primit"
- rus "ðÏÌÕÞÅÎÎÙÊ ÐÁËÅÔ ÂÏÌØÛÅ, ÞÅÍ 'max_allowed_packet'"
- serbian "Primio sam mrežni paket veæi od definisane vrednosti 'max_allowed_packet'"
- spa "Obtenido un paquete mayor que 'max_allowed_packet'"
- swe "Kommunkationspaketet är större än 'max_allowed_packet'"
- ukr "ïÔÒÉÍÁÎÏ ÐÁËÅÔ Â¦ÌØÛÉÊ Î¦Ö max_allowed_packet"
-ER_NET_READ_ERROR_FROM_PIPE 08S01
- cze "Zji-B¹tìna chyba pøi ètení z roury spojení"
- dan "Fik læsefejl fra forbindelse (connection pipe)"
- nla "Kreeg leesfout van de verbindings pipe"
- eng "Got a read error from the connection pipe"
- est "Viga ühendustoru lugemisel"
- fre "Erreur de lecture reçue du pipe de connexion"
- ger "Lese-Fehler bei einer Verbindungs-Pipe"
- hun "Olvasasi hiba a kapcsolat soran"
- ita "Rilevato un errore di lettura dalla pipe di connessione"
- kor "¿¬°á ÆÄÀÌÇÁ·ÎºÎÅÍ ¿¡·¯°¡ ¹ß»ýÇÏ¿´½À´Ï´Ù."
- por "Obteve um erro de leitura no 'pipe' da conexão"
- rum "Eroare la citire din cauza lui 'connection pipe'"
- rus "ðÏÌÕÞÅÎÁ ÏÛÉÂËÁ ÞÔÅÎÉÑ ÏÔ ÐÏÔÏËÁ ÓÏÅÄÉÎÅÎÉÑ (connection pipe)"
- serbian "Greška pri èitanju podataka sa pipe-a"
- spa "Obtenido un error de lectura de la conexión pipe"
- swe "Fick läsfel från klienten vid läsning från 'PIPE'"
- ukr "ïÔÒÉÍÁÎÏ ÐÏÍÉÌËÕ ÞÉÔÁÎÎÑ Ú ËÏÍÕΦËÁæÊÎÏÇÏ ËÁÎÁÌÕ"
-ER_NET_FCNTL_ERROR 08S01
- cze "Zji-B¹tìna chyba fcntl()"
- dan "Fik fejlmeddelelse fra fcntl()"
- nla "Kreeg fout van fcntl()"
- eng "Got an error from fcntl()"
- est "fcntl() tagastas vea"
- fre "Erreur reçue de fcntl() "
- ger "fcntl() lieferte einen Fehler"
- hun "Hiba a fcntl() fuggvenyben"
- ita "Rilevato un errore da fcntl()"
- kor "fcntl() ÇÔ¼ö·ÎºÎÅÍ ¿¡·¯°¡ ¹ß»ýÇÏ¿´½À´Ï´Ù."
- por "Obteve um erro em fcntl()"
- rum "Eroare obtinuta de la fcntl()"
- rus "ðÏÌÕÞÅÎÁ ÏÛÉÂËÁ ÏÔ fcntl()"
- serbian "Greška pri izvršavanju funkcije fcntl()"
- spa "Obtenido un error de fcntl()"
- swe "Fick fatalt fel från 'fcntl()'"
- ukr "ïÔÒÉÍÁÎÏ ÐÏÍÉÌËËÕ ×¦Ä fcntl()"
-ER_NET_PACKETS_OUT_OF_ORDER 08S01
- cze "P-Bøíchozí packety v chybném poøadí"
- dan "Modtog ikke datapakker i korrekt rækkefølge"
- nla "Pakketten in verkeerde volgorde ontvangen"
- eng "Got packets out of order"
- est "Paketid saabusid vales järjekorras"
- fre "Paquets reçus dans le désordre"
- ger "Pakete nicht in der richtigen Reihenfolge empfangen"
- hun "Helytelen sorrendben erkezett adatcsomagok"
- ita "Ricevuti pacchetti non in ordine"
- kor "¼ø¼­°¡ ¸ÂÁö¾Ê´Â ÆÐŶÀ» ¹Þ¾Ò½À´Ï´Ù."
- por "Obteve pacotes fora de ordem"
- rum "Packets care nu sint ordonati au fost gasiti"
- rus "ðÁËÅÔÙ ÐÏÌÕÞÅÎÙ × ÎÅ×ÅÒÎÏÍ ÐÏÒÑÄËÅ"
- serbian "Primio sam mrežne pakete van reda"
- spa "Obtenido paquetes desordenados"
- swe "Kommunikationspaketen kom i fel ordning"
- ukr "ïÔÒÉÍÁÎÏ ÐÁËÅÔÉ Õ ÎÅÎÁÌÅÖÎÏÍÕ ÐÏÒÑÄËÕ"
-ER_NET_UNCOMPRESS_ERROR 08S01
- cze "Nemohu rozkomprimovat komunika-Bèní packet"
- dan "Kunne ikke dekomprimere kommunikations-pakke (communication packet)"
- nla "Communicatiepakket kon niet worden gedecomprimeerd"
- eng "Couldn't uncompress communication packet"
- est "Viga andmepaketi lahtipakkimisel"
- fre "Impossible de décompresser le paquet reçu"
- ger "Kommunikationspaket lässt sich nicht entpacken"
- hun "A kommunikacios adatcsomagok nem tomorithetok ki"
- ita "Impossibile scompattare i pacchetti di comunicazione"
- kor "Åë½Å ÆÐŶÀÇ ¾ÐÃàÇØÁ¦¸¦ ÇÒ ¼ö ¾ø¾ú½À´Ï´Ù."
- por "Não conseguiu descomprimir pacote de comunicação"
- rum "Nu s-a putut decompresa pachetul de comunicatie (communication packet)"
- rus "îÅ×ÏÚÍÏÖÎÏ ÒÁÓÐÁËÏ×ÁÔØ ÐÁËÅÔ, ÐÏÌÕÞÅÎÎÙÊ ÞÅÒÅÚ ËÏÍÍÕÎÉËÁÃÉÏÎÎÙÊ ÐÒÏÔÏËÏÌ"
- serbian "Ne mogu da dekompresujem mrežne pakete"
- spa "No puedo descomprimir paquetes de comunicación"
- swe "Kunde inte packa up kommunikationspaketet"
- ukr "îÅ ÍÏÖÕ ÄÅËÏÍÐÒÅÓÕ×ÁÔÉ ËÏÍÕΦËÁæÊÎÉÊ ÐÁËÅÔ"
-ER_NET_READ_ERROR 08S01
- cze "Zji-B¹tìna chyba pøi ètení komunikaèního packetu"
- dan "Fik fejlmeddelelse ved læsning af kommunikations-pakker (communication packets)"
- nla "Fout bij het lezen van communicatiepakketten"
- eng "Got an error reading communication packets"
- est "Viga andmepaketi lugemisel"
- fre "Erreur de lecture des paquets reçus"
- ger "Fehler beim Lesen eines Kommunikationspakets"
- hun "HIba a kommunikacios adatcsomagok olvasasa soran"
- ita "Rilevato un errore ricevendo i pacchetti di comunicazione"
- kor "Åë½Å ÆÐŶÀ» Àд Áß ¿À·ù°¡ ¹ß»ýÇÏ¿´½À´Ï´Ù."
- por "Obteve um erro na leitura de pacotes de comunicação"
- rum "Eroare obtinuta citind pachetele de comunicatie (communication packets)"
- rus "ðÏÌÕÞÅÎÁ ÏÛÉÂËÁ × ÐÒÏÃÅÓÓÅ ÐÏÌÕÞÅÎÉÑ ÐÁËÅÔÁ ÞÅÒÅÚ ËÏÍÍÕÎÉËÁÃÉÏÎÎÙÊ ÐÒÏÔÏËÏÌ "
- serbian "Greška pri primanju mrežnih paketa"
- spa "Obtenido un error leyendo paquetes de comunicación"
- swe "Fick ett fel vid läsning från klienten"
- ukr "ïÔÒÉÍÁÎÏ ÐÏÍÉÌËÕ ÞÉÔÁÎÎÑ ËÏÍÕΦËÁæÊÎÉÈ ÐÁËÅÔ¦×"
-ER_NET_READ_INTERRUPTED 08S01
- cze "Zji-B¹tìn timeout pøi ètení komunikaèního packetu"
- dan "Timeout-fejl ved læsning af kommunukations-pakker (communication packets)"
- nla "Timeout bij het lezen van communicatiepakketten"
- eng "Got timeout reading communication packets"
- est "Kontrollaja ületamine andmepakettide lugemisel"
- fre "Timeout en lecture des paquets reçus"
- ger "Zeitüberschreitung beim Lesen eines Kommunikationspakets"
- hun "Idotullepes a kommunikacios adatcsomagok olvasasa soran"
- ita "Rilevato un timeout ricevendo i pacchetti di comunicazione"
- kor "Åë½Å ÆÐŶÀ» Àд Áß timeoutÀÌ ¹ß»ýÇÏ¿´½À´Ï´Ù."
- por "Obteve expiração de tempo (timeout) na leitura de pacotes de comunicação"
- rum "Timeout obtinut citind pachetele de comunicatie (communication packets)"
- rus "ðÏÌÕÞÅÎ ÔÁÊÍÁÕÔ ÏÖÉÄÁÎÉÑ ÐÁËÅÔÁ ÞÅÒÅÚ ËÏÍÍÕÎÉËÁÃÉÏÎÎÙÊ ÐÒÏÔÏËÏÌ "
- serbian "Vremenski limit za èitanje mrežnih paketa je istekao"
- spa "Obtenido timeout leyendo paquetes de comunicación"
- swe "Fick 'timeout' vid läsning från klienten"
- ukr "ïÔÒÉÍÁÎÏ ÚÁÔÒÉÍËÕ ÞÉÔÁÎÎÑ ËÏÍÕΦËÁæÊÎÉÈ ÐÁËÅÔ¦×"
-ER_NET_ERROR_ON_WRITE 08S01
- cze "Zji-B¹tìna chyba pøi zápisu komunikaèního packetu"
- dan "Fik fejlmeddelelse ved skrivning af kommunukations-pakker (communication packets)"
- nla "Fout bij het schrijven van communicatiepakketten"
- eng "Got an error writing communication packets"
- est "Viga andmepaketi kirjutamisel"
- fre "Erreur d'écriture des paquets envoyés"
- ger "Fehler beim Schreiben eines Kommunikationspakets"
- hun "Hiba a kommunikacios csomagok irasa soran"
- ita "Rilevato un errore inviando i pacchetti di comunicazione"
- kor "Åë½Å ÆÐŶÀ» ±â·ÏÇÏ´Â Áß ¿À·ù°¡ ¹ß»ýÇÏ¿´½À´Ï´Ù."
- por "Obteve um erro na escrita de pacotes de comunicação"
- rum "Eroare in scrierea pachetelor de comunicatie (communication packets)"
- rus "ðÏÌÕÞÅÎÁ ÏÛÉÂËÁ ÐÒÉ ÐÅÒÅÄÁÞÅ ÐÁËÅÔÁ ÞÅÒÅÚ ËÏÍÍÕÎÉËÁÃÉÏÎÎÙÊ ÐÒÏÔÏËÏÌ "
- serbian "Greška pri slanju mrežnih paketa"
- spa "Obtenido un error de escribiendo paquetes de comunicación"
- swe "Fick ett fel vid skrivning till klienten"
- ukr "ïÔÒÉÍÁÎÏ ÐÏÍÉÌËÕ ÚÁÐÉÓÕ ËÏÍÕΦËÁæÊÎÉÈ ÐÁËÅÔ¦×"
-ER_NET_WRITE_INTERRUPTED 08S01
- cze "Zji-B¹tìn timeout pøi zápisu komunikaèního packetu"
- dan "Timeout-fejl ved skrivning af kommunukations-pakker (communication packets)"
- nla "Timeout bij het schrijven van communicatiepakketten"
- eng "Got timeout writing communication packets"
- est "Kontrollaja ületamine andmepakettide kirjutamisel"
- fre "Timeout d'écriture des paquets envoyés"
- ger "Zeitüberschreitung beim Schreiben eines Kommunikationspakets"
- hun "Idotullepes a kommunikacios csomagok irasa soran"
- ita "Rilevato un timeout inviando i pacchetti di comunicazione"
- kor "Åë½Å ÆÐÆÂÀ» ±â·ÏÇÏ´Â Áß timeoutÀÌ ¹ß»ýÇÏ¿´½À´Ï´Ù."
- por "Obteve expiração de tempo ('timeout') na escrita de pacotes de comunicação"
- rum "Timeout obtinut scriind pachetele de comunicatie (communication packets)"
- rus "ðÏÌÕÞÅÎ ÔÁÊÍÁÕÔ × ÐÒÏÃÅÓÓÅ ÐÅÒÅÄÁÞÉ ÐÁËÅÔÁ ÞÅÒÅÚ ËÏÍÍÕÎÉËÁÃÉÏÎÎÙÊ ÐÒÏÔÏËÏÌ "
- serbian "Vremenski limit za slanje mrežnih paketa je istekao"
- spa "Obtenido timeout escribiendo paquetes de comunicación"
- swe "Fick 'timeout' vid skrivning till klienten"
- ukr "ïÔÒÉÍÁÎÏ ÚÁÔÒÉÍËÕ ÚÁÐÉÓÕ ËÏÍÕΦËÁæÊÎÉÈ ÐÁËÅÔ¦×"
-ER_TOO_LONG_STRING 42000
- cze "V-Býsledný øetìzec je del¹í ne¾ 'max_allowed_packet'"
- dan "Strengen med resultater er større end 'max_allowed_packet'"
- nla "Resultaat string is langer dan 'max_allowed_packet'"
- eng "Result string is longer than 'max_allowed_packet' bytes"
- est "Tulemus on pikem kui lubatud 'max_allowed_packet' muutujaga"
- fre "La chaîne résultat est plus grande que 'max_allowed_packet'"
- ger "Ergebnis-String ist länger als 'max_allowed_packet' Bytes"
- hun "Ez eredmeny sztring nagyobb, mint a lehetseges maximum: 'max_allowed_packet'"
- ita "La stringa di risposta e` piu` lunga di 'max_allowed_packet'"
- por "'String' resultante é mais longa do que 'max_allowed_packet'"
- rum "Sirul rezultat este mai lung decit 'max_allowed_packet'"
- rus "òÅÚÕÌØÔÉÒÕÀÝÁÑ ÓÔÒÏËÁ ÂÏÌØÛÅ, ÞÅÍ 'max_allowed_packet'"
- serbian "Rezultujuèi string je duži nego što to dozvoljava parametar servera 'max_allowed_packet'"
- spa "La string resultante es mayor que max_allowed_packet"
- swe "Resultatsträngen är längre än max_allowed_packet"
- ukr "óÔÒÏËÁ ÒÅÚÕÌØÔÁÔÕ ÄÏ×ÛÁ Î¦Ö max_allowed_packet"
-ER_TABLE_CANT_HANDLE_BLOB 42000
- cze "Typ pou-B¾ité tabulky nepodporuje BLOB/TEXT sloupce"
- dan "Denne tabeltype understøtter ikke brug af BLOB og TEXT kolonner"
- nla "Het gebruikte tabel type ondersteunt geen BLOB/TEXT kolommen"
- eng "The used table type doesn't support BLOB/TEXT columns"
- est "Valitud tabelitüüp ei toeta BLOB/TEXT tüüpi välju"
- fre "Ce type de table ne supporte pas les colonnes BLOB/TEXT"
- ger "Der verwendete Tabellentyp unterstützt keine BLOB- und TEXT-Felder"
- hun "A hasznalt tabla tipus nem tamogatja a BLOB/TEXT mezoket"
- ita "Il tipo di tabella usata non supporta colonne di tipo BLOB/TEXT"
- por "Tipo de tabela usado não permite colunas BLOB/TEXT"
- rum "Tipul de tabela folosit nu suporta coloane de tip BLOB/TEXT"
- rus "éÓÐÏÌØÚÕÅÍÁÑ ÔÁÂÌÉÃÁ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÔÉÐÙ BLOB/TEXT"
- serbian "Iskorišteni tip tabele ne podržava kolone tipa 'BLOB' odnosno 'TEXT'"
- spa "El tipo de tabla usada no permite soporte para columnas BLOB/TEXT"
- swe "Den använda tabelltypen kan inte hantera BLOB/TEXT-kolumner"
- ukr "÷ÉËÏÒÉÓÔÁÎÉÊ ÔÉÐ ÔÁÂÌÉæ ΊЦÄÔÒÉÍÕ¤ BLOB/TEXT ÓÔÏ×Âæ"
-ER_TABLE_CANT_HANDLE_AUTO_INCREMENT 42000
- cze "Typ pou-B¾ité tabulky nepodporuje AUTO_INCREMENT sloupce"
- dan "Denne tabeltype understøtter ikke brug af AUTO_INCREMENT kolonner"
- nla "Het gebruikte tabel type ondersteunt geen AUTO_INCREMENT kolommen"
- eng "The used table type doesn't support AUTO_INCREMENT columns"
- est "Valitud tabelitüüp ei toeta AUTO_INCREMENT tüüpi välju"
- fre "Ce type de table ne supporte pas les colonnes AUTO_INCREMENT"
- ger "Der verwendete Tabellentyp unterstützt keine AUTO_INCREMENT-Felder"
- hun "A hasznalt tabla tipus nem tamogatja az AUTO_INCREMENT tipusu mezoket"
- ita "Il tipo di tabella usata non supporta colonne di tipo AUTO_INCREMENT"
- por "Tipo de tabela usado não permite colunas AUTO_INCREMENT"
- rum "Tipul de tabela folosit nu suporta coloane de tip AUTO_INCREMENT"
- rus "éÓÐÏÌØÚÕÅÍÁÑ ÔÁÂÌÉÃÁ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ Á×ÔÏÉÎËÒÅÍÅÎÔÎÙÅ ÓÔÏÌÂÃÙ"
- serbian "Iskorišteni tip tabele ne podržava kolone tipa 'AUTO_INCREMENT'"
- spa "El tipo de tabla usada no permite soporte para columnas AUTO_INCREMENT"
- swe "Den använda tabelltypen kan inte hantera AUTO_INCREMENT-kolumner"
- ukr "÷ÉËÏÒÉÓÔÁÎÉÊ ÔÉÐ ÔÁÂÌÉæ ΊЦÄÔÒÉÍÕ¤ AUTO_INCREMENT ÓÔÏ×Âæ"
-ER_DELAYED_INSERT_TABLE_LOCKED
- cze "INSERT DELAYED nen-Bí mo¾no s tabulkou '%-.192s' pou¾ít, proto¾e je zamèená pomocí LOCK TABLES"
- dan "INSERT DELAYED kan ikke bruges med tabellen '%-.192s', fordi tabellen er låst med LOCK TABLES"
- nla "INSERT DELAYED kan niet worden gebruikt bij table '%-.192s', vanwege een 'lock met LOCK TABLES"
- eng "INSERT DELAYED can't be used with table '%-.192s' because it is locked with LOCK TABLES"
- est "INSERT DELAYED ei saa kasutada tabeli '%-.192s' peal, kuna see on lukustatud LOCK TABLES käsuga"
- fre "INSERT DELAYED ne peut être utilisé avec la table '%-.192s', car elle est verrouée avec LOCK TABLES"
- ger "INSERT DELAYED kann für Tabelle '%-.192s' nicht verwendet werden, da sie mit LOCK TABLES gesperrt ist"
- greek "INSERT DELAYED can't be used with table '%-.192s', because it is locked with LOCK TABLES"
- hun "Az INSERT DELAYED nem hasznalhato a '%-.192s' tablahoz, mert a tabla zarolt (LOCK TABLES)"
- ita "L'inserimento ritardato (INSERT DELAYED) non puo` essere usato con la tabella '%-.192s', perche` soggetta a lock da 'LOCK TABLES'"
- jpn "INSERT DELAYED can't be used with table '%-.192s', because it is locked with LOCK TABLES"
- kor "INSERT DELAYED can't be used with table '%-.192s', because it is locked with LOCK TABLES"
- nor "INSERT DELAYED can't be used with table '%-.192s', because it is locked with LOCK TABLES"
- norwegian-ny "INSERT DELAYED can't be used with table '%-.192s', because it is locked with LOCK TABLES"
- pol "INSERT DELAYED can't be used with table '%-.192s', because it is locked with LOCK TABLES"
- por "INSERT DELAYED não pode ser usado com a tabela '%-.192s', porque ela está travada com LOCK TABLES"
- rum "INSERT DELAYED nu poate fi folosit cu tabela '%-.192s', deoarece este locked folosing LOCK TABLES"
- rus "îÅÌØÚÑ ÉÓÐÏÌØÚÏ×ÁÔØ INSERT DELAYED ÄÌÑ ÔÁÂÌÉÃÙ '%-.192s', ÐÏÔÏÍÕ ÞÔÏ ÏÎÁ ÚÁÂÌÏËÉÒÏ×ÁÎÁ Ó ÐÏÍÏÝØÀ LOCK TABLES"
- serbian "Komanda 'INSERT DELAYED' ne može biti iskorištena u tabeli '%-.192s', zbog toga što je zakljuèana komandom 'LOCK TABLES'"
- slo "INSERT DELAYED can't be used with table '%-.192s', because it is locked with LOCK TABLES"
- spa "INSERT DELAYED no puede ser usado con tablas '%-.192s', porque esta bloqueada con LOCK TABLES"
- swe "INSERT DELAYED kan inte användas med tabell '%-.192s', emedan den är låst med LOCK TABLES"
- ukr "INSERT DELAYED ÎÅ ÍÏÖÅ ÂÕÔÉ ×ÉËÏÒÉÓÔÁÎÏ Ú ÔÁÂÌÉÃÅÀ '%-.192s', ÔÏÍÕ ÝÏ §§ ÚÁÂÌÏËÏ×ÁÎÏ Ú LOCK TABLES"
-ER_WRONG_COLUMN_NAME 42000
- cze "Nespr-Bávné jméno sloupce '%-.100s'"
- dan "Forkert kolonnenavn '%-.100s'"
- nla "Incorrecte kolom naam '%-.100s'"
- eng "Incorrect column name '%-.100s'"
- est "Vigane tulba nimi '%-.100s'"
- fre "Nom de colonne '%-.100s' incorrect"
- ger "Falscher Spaltenname '%-.100s'"
- hun "Ervenytelen mezonev: '%-.100s'"
- ita "Nome colonna '%-.100s' non corretto"
- por "Nome de coluna '%-.100s' incorreto"
- rum "Nume increct de coloana '%-.100s'"
- rus "îÅ×ÅÒÎÏÅ ÉÍÑ ÓÔÏÌÂÃÁ '%-.100s'"
- serbian "Pogrešno ime kolone '%-.100s'"
- spa "Incorrecto nombre de columna '%-.100s'"
- swe "Felaktigt kolumnnamn '%-.100s'"
- ukr "îÅצÒÎÅ ¦Í'Ñ ÓÔÏ×ÂÃÑ '%-.100s'"
-ER_WRONG_KEY_COLUMN 42000
- cze "Handler pou-B¾ité tabulky neumí indexovat sloupce '%-.192s'"
- dan "Den brugte tabeltype kan ikke indeksere kolonnen '%-.192s'"
- nla "De gebruikte tabel 'handler' kan kolom '%-.192s' niet indexeren"
- eng "The used storage engine can't index column '%-.192s'"
- est "Tabelihandler ei oska indekseerida tulpa '%-.192s'"
- fre "Le handler de la table ne peut indexé la colonne '%-.192s'"
- ger "Die verwendete Speicher-Engine kann die Spalte '%-.192s' nicht indizieren"
- greek "The used table handler can't index column '%-.192s'"
- hun "A hasznalt tablakezelo nem tudja a '%-.192s' mezot indexelni"
- ita "Il gestore delle tabelle non puo` indicizzare la colonna '%-.192s'"
- jpn "The used table handler can't index column '%-.192s'"
- kor "The used table handler can't index column '%-.192s'"
- nor "The used table handler can't index column '%-.192s'"
- norwegian-ny "The used table handler can't index column '%-.192s'"
- pol "The used table handler can't index column '%-.192s'"
- por "O manipulador de tabela usado não pode indexar a coluna '%-.192s'"
- rum "Handler-ul tabelei folosite nu poate indexa coloana '%-.192s'"
- rus "éÓÐÏÌØÚÏ×ÁÎÎÙÊ ÏÂÒÁÂÏÔÞÉË ÔÁÂÌÉÃÙ ÎÅ ÍÏÖÅÔ ÐÒÏÉÎÄÅËÓÉÒÏ×ÁÔØ ÓÔÏÌÂÅà '%-.192s'"
- serbian "Handler tabele ne može da indeksira kolonu '%-.192s'"
- slo "The used table handler can't index column '%-.192s'"
- spa "El manipulador de tabla usado no puede indexar columna '%-.192s'"
- swe "Den använda tabelltypen kan inte indexera kolumn '%-.192s'"
- ukr "÷ÉËÏÒÉÓÔÁÎÉÊ ×ËÁÚ¦×ÎÉË ÔÁÂÌÉæ ÎÅ ÍÏÖÅ ¦ÎÄÅËÓÕ×ÁÔÉ ÓÔÏ×ÂÅÃØ '%-.192s'"
-ER_WRONG_MRG_TABLE
- cze "V-B¹echny tabulky v MERGE tabulce nejsou definovány stejnì"
- dan "Tabellerne i MERGE er ikke defineret ens"
- nla "Niet alle tabellen in de MERGE tabel hebben identieke gedefinities"
- eng "Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist"
- est "Kõik tabelid MERGE tabeli määratluses ei ole identsed"
- fre "Toutes les tables de la table de type MERGE n'ont pas la même définition"
- ger "Nicht alle Tabellen in der MERGE-Tabelle sind gleich definiert"
- hun "A MERGE tablaban talalhato tablak definicioja nem azonos"
- ita "Non tutte le tabelle nella tabella di MERGE sono definite in maniera identica"
- jpn "All tables in the MERGE table are not defined identically"
- kor "All tables in the MERGE table are not defined identically"
- nor "All tables in the MERGE table are not defined identically"
- norwegian-ny "All tables in the MERGE table are not defined identically"
- pol "All tables in the MERGE table are not defined identically"
- por "Todas as tabelas contidas na tabela fundida (MERGE) não estão definidas identicamente"
- rum "Toate tabelele din tabela MERGE nu sint definite identic"
- rus "îÅ ×ÓÅ ÔÁÂÌÉÃÙ × MERGE ÏÐÒÅÄÅÌÅÎÙ ÏÄÉÎÁËÏ×Ï"
- serbian "Tabele iskorištene u 'MERGE' tabeli nisu definisane na isti naèin"
- slo "All tables in the MERGE table are not defined identically"
- spa "Todas las tablas en la MERGE tabla no estan definidas identicamente"
- swe "Tabellerna i MERGE-tabellen är inte identiskt definierade"
- ukr "ôÁÂÌÉæ Õ MERGE TABLE ÍÁÀÔØ Ò¦ÚÎÕ ÓÔÒÕËÔÕÒÕ"
-ER_DUP_UNIQUE 23000
- cze "Kv-Bùli unique constraintu nemozu zapsat do tabulky '%-.192s'"
- dan "Kan ikke skrive til tabellen '%-.192s' fordi det vil bryde CONSTRAINT regler"
- nla "Kan niet opslaan naar table '%-.192s' vanwege 'unique' beperking"
- eng "Can't write, because of unique constraint, to table '%-.192s'"
- est "Ei suuda kirjutada tabelisse '%-.192s', kuna see rikub ühesuse kitsendust"
- fre "Écriture impossible à cause d'un index UNIQUE sur la table '%-.192s'"
- ger "Schreiben in Tabelle '%-.192s' nicht möglich wegen einer Eindeutigkeitsbeschränkung (unique constraint)"
- hun "A '%-.192s' nem irhato, az egyedi mezok miatt"
- ita "Impossibile scrivere nella tabella '%-.192s' per limitazione di unicita`"
- por "Não pode gravar, devido à restrição UNIQUE, na tabela '%-.192s'"
- rum "Nu pot scrie pe hard-drive, din cauza constraintului unic (unique constraint) pentru tabela '%-.192s'"
- rus "îÅ×ÏÚÍÏÖÎÏ ÚÁÐÉÓÁÔØ × ÔÁÂÌÉÃÕ '%-.192s' ÉÚ-ÚÁ ÏÇÒÁÎÉÞÅÎÉÊ ÕÎÉËÁÌØÎÏÇÏ ËÌÀÞÁ"
- serbian "Zbog provere jedinstvenosti ne mogu da upišem podatke u tabelu '%-.192s'"
- spa "No puedo escribir, debido al único constraint, para tabla '%-.192s'"
- swe "Kan inte skriva till tabell '%-.192s'; UNIQUE-test"
- ukr "îÅ ÍÏÖÕ ÚÁÐÉÓÁÔÉ ÄÏ ÔÁÂÌÉæ '%-.192s', Ú ÐÒÉÞÉÎÉ ×ÉÍÏÇ ÕΦËÁÌØÎÏÓÔ¦"
-ER_BLOB_KEY_WITHOUT_LENGTH 42000
- cze "BLOB sloupec '%-.192s' je pou-B¾it ve specifikaci klíèe bez délky"
- dan "BLOB kolonnen '%-.192s' brugt i nøglespecifikation uden nøglelængde"
- nla "BLOB kolom '%-.192s' gebruikt in zoeksleutel specificatie zonder zoeksleutel lengte"
- eng "BLOB/TEXT column '%-.192s' used in key specification without a key length"
- est "BLOB-tüüpi tulp '%-.192s' on kasutusel võtmes ilma pikkust määratlemata"
- fre "La colonne '%-.192s' de type BLOB est utilisée dans une définition d'index sans longueur d'index"
- ger "BLOB- oder TEXT-Spalte '%-.192s' wird in der Schlüsseldefinition ohne Schlüssellängenangabe verwendet"
- greek "BLOB column '%-.192s' used in key specification without a key length"
- hun "BLOB mezo '%-.192s' hasznalt a mezo specifikacioban, a mezohossz megadasa nelkul"
- ita "La colonna '%-.192s' di tipo BLOB e` usata in una chiave senza specificarne la lunghezza"
- jpn "BLOB column '%-.192s' used in key specification without a key length"
- kor "BLOB column '%-.192s' used in key specification without a key length"
- nor "BLOB column '%-.192s' used in key specification without a key length"
- norwegian-ny "BLOB column '%-.192s' used in key specification without a key length"
- pol "BLOB column '%-.192s' used in key specification without a key length"
- por "Coluna BLOB '%-.192s' usada na especificação de chave sem o comprimento da chave"
- rum "Coloana BLOB '%-.192s' este folosita in specificarea unei chei fara ca o lungime de cheie sa fie folosita"
- rus "óÔÏÌÂÅÃ ÔÉÐÁ BLOB '%-.192s' ÂÙÌ ÕËÁÚÁÎ × ÏÐÒÅÄÅÌÅÎÉÉ ËÌÀÞÁ ÂÅÚ ÕËÁÚÁÎÉÑ ÄÌÉÎÙ ËÌÀÞÁ"
- serbian "BLOB kolona '%-.192s' je upotrebljena u specifikaciji kljuèa bez navoðenja dužine kljuèa"
- slo "BLOB column '%-.192s' used in key specification without a key length"
- spa "Columna BLOB column '%-.192s' usada en especificación de clave sin tamaño de la clave"
- swe "Du har inte angett någon nyckellängd för BLOB '%-.192s'"
- ukr "óÔÏ×ÂÅÃØ BLOB '%-.192s' ×ÉËÏÒÉÓÔÁÎÏ Õ ×ÉÚÎÁÞÅÎΦ ËÌÀÞÁ ÂÅÚ ×ËÁÚÁÎÎÑ ÄÏ×ÖÉÎÉ ËÌÀÞÁ"
-ER_PRIMARY_CANT_HAVE_NULL 42000
- cze "V-B¹echny èásti primárního klíèe musejí být NOT NULL; pokud potøebujete NULL, pou¾ijte UNIQUE"
- dan "Alle dele af en PRIMARY KEY skal være NOT NULL; Hvis du skal bruge NULL i nøglen, brug UNIQUE istedet"
- nla "Alle delen van een PRIMARY KEY moeten NOT NULL zijn; Indien u NULL in een zoeksleutel nodig heeft kunt u UNIQUE gebruiken"
- eng "All parts of a PRIMARY KEY must be NOT NULL; if you need NULL in a key, use UNIQUE instead"
- est "Kõik PRIMARY KEY peavad olema määratletud NOT NULL piiranguga; vajadusel kasuta UNIQUE tüüpi võtit"
- fre "Toutes les parties d'un index PRIMARY KEY doivent être NOT NULL; Si vous avez besoin d'un NULL dans l'index, utilisez un index UNIQUE"
- ger "Alle Teile eines PRIMARY KEY müssen als NOT NULL definiert sein. Wenn NULL in einem Schlüssel benötigt wird, muss ein UNIQUE-Schlüssel verwendet werden"
- hun "Az elsodleges kulcs teljes egeszeben csak NOT NULL tipusu lehet; Ha NULL mezot szeretne a kulcskent, hasznalja inkabb a UNIQUE-ot"
- ita "Tutte le parti di una chiave primaria devono essere dichiarate NOT NULL; se necessitano valori NULL nelle chiavi utilizzare UNIQUE"
- por "Todas as partes de uma chave primária devem ser não-nulas. Se você precisou usar um valor nulo (NULL) em uma chave, use a cláusula UNIQUE em seu lugar"
- rum "Toate partile unei chei primare (PRIMARY KEY) trebuie sa fie NOT NULL; Daca aveti nevoie de NULL in vreo cheie, folositi UNIQUE in schimb"
- rus "÷ÓÅ ÞÁÓÔÉ ÐÅÒ×ÉÞÎÏÇÏ ËÌÀÞÁ (PRIMARY KEY) ÄÏÌÖÎÙ ÂÙÔØ ÏÐÒÅÄÅÌÅÎÙ ËÁË NOT NULL; åÓÌÉ ×ÁÍ ÎÕÖÎÁ ÐÏÄÄÅÒÖËÁ ×ÅÌÉÞÉÎ NULL × ËÌÀÞÅ, ×ÏÓÐÏÌØÚÕÊÔÅÓØ ÉÎÄÅËÓÏÍ UNIQUE"
- serbian "Svi delovi primarnog kljuèa moraju biti razlièiti od NULL; Ako Vam ipak treba NULL vrednost u kljuèu, upotrebite 'UNIQUE'"
- spa "Todas las partes de un PRIMARY KEY deben ser NOT NULL; Si necesitas NULL en una clave, use UNIQUE"
- swe "Alla delar av en PRIMARY KEY måste vara NOT NULL; Om du vill ha en nyckel med NULL, använd UNIQUE istället"
- ukr "õÓ¦ ÞÁÓÔÉÎÉ PRIMARY KEY ÐÏ×ÉÎΦ ÂÕÔÉ NOT NULL; ñËÝÏ ×É ÐÏÔÒÅÂÕ¤ÔÅ NULL Õ ËÌÀÞ¦, ÓËÏÒÉÓÔÁÊÔÅÓÑ UNIQUE"
-ER_TOO_MANY_ROWS 42000
- cze "V-Býsledek obsahuje více ne¾ jeden øádek"
- dan "Resultatet bestod af mere end een række"
- nla "Resultaat bevatte meer dan een rij"
- eng "Result consisted of more than one row"
- est "Tulemis oli rohkem kui üks kirje"
- fre "Le résultat contient plus d'un enregistrement"
- ger "Ergebnis besteht aus mehr als einer Zeile"
- hun "Az eredmeny tobb, mint egy sort tartalmaz"
- ita "Il risultato consiste di piu` di una riga"
- por "O resultado consistiu em mais do que uma linha"
- rum "Resultatul constista din mai multe linii"
- rus "÷ ÒÅÚÕÌØÔÁÔÅ ×ÏÚ×ÒÁÝÅÎÁ ÂÏÌÅÅ ÞÅÍ ÏÄÎÁ ÓÔÒÏËÁ"
- serbian "Rezultat je saèinjen od više slogova"
- spa "Resultado compuesto de mas que una línea"
- swe "Resultet bestod av mera än en rad"
- ukr "òÅÚÕÌØÔÁÔ ÚÎÁÈÏÄÉÔØÓÑ Õ Â¦ÌØÛÅ Î¦Ö ÏÄÎ¦Ê ÓÔÒÏæ"
-ER_REQUIRES_PRIMARY_KEY 42000
- cze "Tento typ tabulky vy-B¾aduje primární klíè"
- dan "Denne tabeltype kræver en primærnøgle"
- nla "Dit tabel type heeft een primaire zoeksleutel nodig"
- eng "This table type requires a primary key"
- est "Antud tabelitüüp nõuab primaarset võtit"
- fre "Ce type de table nécessite une clé primaire (PRIMARY KEY)"
- ger "Dieser Tabellentyp benötigt einen Primärschlüssel (PRIMARY KEY)"
- hun "Az adott tablatipushoz elsodleges kulcs hasznalata kotelezo"
- ita "Questo tipo di tabella richiede una chiave primaria"
- por "Este tipo de tabela requer uma chave primária"
- rum "Aceast tip de tabela are nevoie de o cheie primara"
- rus "üÔÏÔ ÔÉÐ ÔÁÂÌÉÃÙ ÔÒÅÂÕÅÔ ÏÐÒÅÄÅÌÅÎÉÑ ÐÅÒ×ÉÞÎÏÇÏ ËÌÀÞÁ"
- serbian "Ovaj tip tabele zahteva da imate definisan primarni kljuè"
- spa "Este tipo de tabla necesita de una primary key"
- swe "Denna tabelltyp kräver en PRIMARY KEY"
- ukr "ãÅÊ ÔÉÐ ÔÁÂÌÉæ ÐÏÔÒÅÂÕ¤ ÐÅÒ×ÉÎÎÏÇÏ ËÌÀÞÁ"
-ER_NO_RAID_COMPILED
- cze "Tato verze MySQL nen-Bí zkompilována s podporou RAID"
- dan "Denne udgave af MySQL er ikke oversat med understøttelse af RAID"
- nla "Deze versie van MySQL is niet gecompileerd met RAID ondersteuning"
- eng "This version of MySQL is not compiled with RAID support"
- est "Antud MySQL versioon on kompileeritud ilma RAID toeta"
- fre "Cette version de MySQL n'est pas compilée avec le support RAID"
- ger "Diese MySQL-Version ist nicht mit RAID-Unterstützung kompiliert"
- hun "Ezen leforditott MySQL verzio nem tartalmaz RAID support-ot"
- ita "Questa versione di MYSQL non e` compilata con il supporto RAID"
- por "Esta versão do MySQL não foi compilada com suporte a RAID"
- rum "Aceasta versiune de MySQL, nu a fost compilata cu suport pentru RAID"
- rus "üÔÁ ×ÅÒÓÉÑ MySQL ÓËÏÍÐÉÌÉÒÏ×ÁÎÁ ÂÅÚ ÐÏÄÄÅÒÖËÉ RAID"
- serbian "Ova verzija MySQL servera nije kompajlirana sa podrškom za RAID ureðaje"
- spa "Esta versión de MySQL no es compilada con soporte RAID"
- swe "Denna version av MySQL är inte kompilerad med RAID"
- ukr "ãÑ ×ÅÒÓ¦Ñ MySQL ÎÅ ÚËÏÍЦÌØÏ×ÁÎÁ Ú Ð¦ÄÔÒÉÍËÏÀ RAID"
-ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE
- cze "Update tabulky bez WHERE s kl-Bíèem není v módu bezpeèných update dovoleno"
- dan "Du bruger sikker opdaterings modus ('safe update mode') og du forsøgte at opdatere en tabel uden en WHERE klausul, der gør brug af et KEY felt"
- nla "U gebruikt 'safe update mode' en u probeerde een tabel te updaten zonder een WHERE met een KEY kolom"
- eng "You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column"
- est "Katse muuta tabelit turvalises rezhiimis ilma WHERE klauslita"
- fre "Vous êtes en mode 'safe update' et vous essayez de faire un UPDATE sans clause WHERE utilisant un index"
- ger "MySQL läuft im sicheren Aktualisierungsmodus (safe update mode). Sie haben versucht, eine Tabelle zu aktualisieren, ohne in der WHERE-Klausel ein KEY-Feld anzugeben"
- hun "On a biztonsagos update modot hasznalja, es WHERE that uses a KEY column"
- ita "In modalita` 'safe update' si e` cercato di aggiornare una tabella senza clausola WHERE su una chiave"
- por "Você está usando modo de atualização seguro e tentou atualizar uma tabela sem uma cláusula WHERE que use uma coluna chave"
- rus "÷Ù ÒÁÂÏÔÁÅÔÅ × ÒÅÖÉÍÅ ÂÅÚÏÐÁÓÎÙÈ ÏÂÎÏ×ÌÅÎÉÊ (safe update mode) É ÐÏÐÒÏÂÏ×ÁÌÉ ÉÚÍÅÎÉÔØ ÔÁÂÌÉÃÕ ÂÅÚ ÉÓÐÏÌØÚÏ×ÁÎÉÑ ËÌÀÞÅ×ÏÇÏ ÓÔÏÌÂÃÁ × ÞÁÓÔÉ WHERE"
- serbian "Vi koristite safe update mod servera, a probali ste da promenite podatke bez 'WHERE' komande koja koristi kolonu kljuèa"
- spa "Tu estás usando modo de actualización segura y tentado actualizar una tabla sin un WHERE que usa una KEY columna"
- swe "Du använder 'säker uppdateringsmod' och försökte uppdatera en tabell utan en WHERE-sats som använder sig av en nyckel"
- ukr "÷É Õ ÒÅÖÉͦ ÂÅÚÐÅÞÎÏÇÏ ÏÎÏ×ÌÅÎÎÑ ÔÁ ÎÁÍÁÇÁ¤ÔÅÓØ ÏÎÏ×ÉÔÉ ÔÁÂÌÉÃÀ ÂÅÚ ÏÐÅÒÁÔÏÒÁ WHERE, ÝÏ ×ÉËÏÒÉÓÔÏ×Õ¤ KEY ÓÔÏ×ÂÅÃØ"
-ER_KEY_DOES_NOT_EXITS 42000 S1009
- cze "Kl-Bíè '%-.192s' v tabulce '%-.192s' neexistuje"
- dan "Nøglen '%-.192s' eksisterer ikke i tabellen '%-.192s'"
- nla "Zoeksleutel '%-.192s' bestaat niet in tabel '%-.192s'"
- eng "Key '%-.192s' doesn't exist in table '%-.192s'"
- est "Võti '%-.192s' ei eksisteeri tabelis '%-.192s'"
- fre "L'index '%-.192s' n'existe pas sur la table '%-.192s'"
- ger "Schlüssel '%-.192s' existiert in der Tabelle '%-.192s' nicht"
- hun "A '%-.192s' kulcs nem letezik a '%-.192s' tablaban"
- ita "La chiave '%-.192s' non esiste nella tabella '%-.192s'"
- por "Chave '%-.192s' não existe na tabela '%-.192s'"
- rus "ëÌÀÞ '%-.192s' ÎÅ ÓÕÝÅÓÔ×ÕÅÔ × ÔÁÂÌÉÃÅ '%-.192s'"
- serbian "Kljuè '%-.192s' ne postoji u tabeli '%-.192s'"
- spa "Clave '%-.192s' no existe en la tabla '%-.192s'"
- swe "Nyckel '%-.192s' finns inte in tabell '%-.192s'"
- ukr "ëÌÀÞ '%-.192s' ÎÅ ¦ÓÎÕ¤ × ÔÁÂÌÉæ '%-.192s'"
-ER_CHECK_NO_SUCH_TABLE 42000
- cze "Nemohu otev-Bøít tabulku"
- dan "Kan ikke åbne tabellen"
- nla "Kan tabel niet openen"
- eng "Can't open table"
- est "Ei suuda avada tabelit"
- fre "Impossible d'ouvrir la table"
- ger "Kann Tabelle nicht öffnen"
- hun "Nem tudom megnyitni a tablat"
- ita "Impossibile aprire la tabella"
- por "Não pode abrir a tabela"
- rus "îÅ×ÏÚÍÏÖÎÏ ÏÔËÒÙÔØ ÔÁÂÌÉÃÕ"
- serbian "Ne mogu da otvorim tabelu"
- spa "No puedo abrir tabla"
- swe "Kan inte öppna tabellen"
- ukr "îÅ ÍÏÖÕ ×¦ÄËÒÉÔÉ ÔÁÂÌÉÃÀ"
-ER_CHECK_NOT_IMPLEMENTED 42000
- cze "Handler tabulky nepodporuje %s"
- dan "Denne tabeltype understøtter ikke %s"
- nla "De 'handler' voor de tabel ondersteund geen %s"
- eng "The storage engine for the table doesn't support %s"
- est "Antud tabelitüüp ei toeta %s käske"
- fre "Ce type de table ne supporte pas les %s"
- ger "Die Speicher-Engine für diese Tabelle unterstützt kein %s"
- greek "The handler for the table doesn't support %s"
- hun "A tabla kezeloje (handler) nem tamogatja az %s"
- ita "Il gestore per la tabella non supporta il %s"
- jpn "The handler for the table doesn't support %s"
- kor "The handler for the table doesn't support %s"
- nor "The handler for the table doesn't support %s"
- norwegian-ny "The handler for the table doesn't support %s"
- pol "The handler for the table doesn't support %s"
- por "O manipulador de tabela não suporta %s"
- rum "The handler for the table doesn't support %s"
- rus "ïÂÒÁÂÏÔÞÉË ÔÁÂÌÉÃÙ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÜÔÏÇÏ: %s"
- serbian "Handler za ovu tabelu ne dozvoljava %s komande"
- slo "The handler for the table doesn't support %s"
- spa "El manipulador de la tabla no permite soporte para %s"
- swe "Tabellhanteraren för denna tabell kan inte göra %s"
- ukr "÷ËÁÚ¦×ÎÉË ÔÁÂÌÉæ ΊЦÄÔÒÉÍÕÅ %s"
-ER_CANT_DO_THIS_DURING_AN_TRANSACTION 25000
- cze "Proveden-Bí tohoto pøíkazu není v transakci dovoleno"
- dan "Du må ikke bruge denne kommando i en transaktion"
- nla "Het is u niet toegestaan dit commando uit te voeren binnen een transactie"
- eng "You are not allowed to execute this command in a transaction"
- est "Seda käsku ei saa kasutada transaktsiooni sees"
- fre "Vous n'êtes pas autorisé à exécute cette commande dans une transaction"
- ger "Sie dürfen diesen Befehl nicht in einer Transaktion ausführen"
- hun "Az On szamara nem engedelyezett a parancs vegrehajtasa a tranzakcioban"
- ita "Non puoi eseguire questo comando in una transazione"
- por "Não lhe é permitido executar este comando em uma transação"
- rus "÷ÁÍ ÎÅ ÒÁÚÒÅÛÅÎÏ ×ÙÐÏÌÎÑÔØ ÜÔÕ ËÏÍÁÎÄÕ × ÔÒÁÎÚÁËÃÉÉ"
- serbian "Nije Vam dozvoljeno da izvršite ovu komandu u transakciji"
- spa "No tienes el permiso para ejecutar este comando en una transición"
- swe "Du får inte utföra detta kommando i en transaktion"
- ukr "÷ÁÍ ÎÅ ÄÏÚ×ÏÌÅÎÏ ×ÉËÏÎÕ×ÁÔÉ ÃÀ ËÏÍÁÎÄÕ × ÔÒÁÎÚÁËæ§"
-ER_ERROR_DURING_COMMIT
- cze "Chyba %d p-Bøi COMMIT"
- dan "Modtog fejl %d mens kommandoen COMMIT blev udført"
- nla "Kreeg fout %d tijdens COMMIT"
- eng "Got error %d during COMMIT"
- est "Viga %d käsu COMMIT täitmisel"
- fre "Erreur %d lors du COMMIT"
- ger "Fehler %d beim COMMIT"
- hun "%d hiba a COMMIT vegrehajtasa soran"
- ita "Rilevato l'errore %d durante il COMMIT"
- por "Obteve erro %d durante COMMIT"
- rus "ðÏÌÕÞÅÎÁ ÏÛÉÂËÁ %d × ÐÒÏÃÅÓÓÅ COMMIT"
- serbian "Greška %d za vreme izvršavanja komande 'COMMIT'"
- spa "Obtenido error %d durante COMMIT"
- swe "Fick fel %d vid COMMIT"
- ukr "ïÔÒÉÍÁÎÏ ÐÏÍÉÌËÕ %d Ð¦Ä ÞÁÓ COMMIT"
-ER_ERROR_DURING_ROLLBACK
- cze "Chyba %d p-Bøi ROLLBACK"
- dan "Modtog fejl %d mens kommandoen ROLLBACK blev udført"
- nla "Kreeg fout %d tijdens ROLLBACK"
- eng "Got error %d during ROLLBACK"
- est "Viga %d käsu ROLLBACK täitmisel"
- fre "Erreur %d lors du ROLLBACK"
- ger "Fehler %d beim ROLLBACK"
- hun "%d hiba a ROLLBACK vegrehajtasa soran"
- ita "Rilevato l'errore %d durante il ROLLBACK"
- por "Obteve erro %d durante ROLLBACK"
- rus "ðÏÌÕÞÅÎÁ ÏÛÉÂËÁ %d × ÐÒÏÃÅÓÓÅ ROLLBACK"
- serbian "Greška %d za vreme izvršavanja komande 'ROLLBACK'"
- spa "Obtenido error %d durante ROLLBACK"
- swe "Fick fel %d vid ROLLBACK"
- ukr "ïÔÒÉÍÁÎÏ ÐÏÍÉÌËÕ %d Ð¦Ä ÞÁÓ ROLLBACK"
-ER_ERROR_DURING_FLUSH_LOGS
- cze "Chyba %d p-Bøi FLUSH_LOGS"
- dan "Modtog fejl %d mens kommandoen FLUSH_LOGS blev udført"
- nla "Kreeg fout %d tijdens FLUSH_LOGS"
- eng "Got error %d during FLUSH_LOGS"
- est "Viga %d käsu FLUSH_LOGS täitmisel"
- fre "Erreur %d lors du FLUSH_LOGS"
- ger "Fehler %d bei FLUSH_LOGS"
- hun "%d hiba a FLUSH_LOGS vegrehajtasa soran"
- ita "Rilevato l'errore %d durante il FLUSH_LOGS"
- por "Obteve erro %d durante FLUSH_LOGS"
- rus "ðÏÌÕÞÅÎÁ ÏÛÉÂËÁ %d × ÐÒÏÃÅÓÓÅ FLUSH_LOGS"
- serbian "Greška %d za vreme izvršavanja komande 'FLUSH_LOGS'"
- spa "Obtenido error %d durante FLUSH_LOGS"
- swe "Fick fel %d vid FLUSH_LOGS"
- ukr "ïÔÒÉÍÁÎÏ ÐÏÍÉÌËÕ %d Ð¦Ä ÞÁÓ FLUSH_LOGS"
-ER_ERROR_DURING_CHECKPOINT
- cze "Chyba %d p-Bøi CHECKPOINT"
- dan "Modtog fejl %d mens kommandoen CHECKPOINT blev udført"
- nla "Kreeg fout %d tijdens CHECKPOINT"
- eng "Got error %d during CHECKPOINT"
- est "Viga %d käsu CHECKPOINT täitmisel"
- fre "Erreur %d lors du CHECKPOINT"
- ger "Fehler %d bei CHECKPOINT"
- hun "%d hiba a CHECKPOINT vegrehajtasa soran"
- ita "Rilevato l'errore %d durante il CHECKPOINT"
- por "Obteve erro %d durante CHECKPOINT"
- rus "ðÏÌÕÞÅÎÁ ÏÛÉÂËÁ %d × ÐÒÏÃÅÓÓÅ CHECKPOINT"
- serbian "Greška %d za vreme izvršavanja komande 'CHECKPOINT'"
- spa "Obtenido error %d durante CHECKPOINT"
- swe "Fick fel %d vid CHECKPOINT"
- ukr "ïÔÒÉÍÁÎÏ ÐÏÍÉÌËÕ %d Ð¦Ä ÞÁÓ CHECKPOINT"
-ER_NEW_ABORTING_CONNECTION 08S01
- cze "Spojen-Bí %ld do databáze: '%-.192s' u¾ivatel: '%-.48s' stroj: '%-.64s' (%-.64s) bylo pøeru¹eno"
- dan "Afbrød forbindelsen %ld til databasen '%-.192s' bruger: '%-.48s' vært: '%-.64s' (%-.64s)"
- nla "Afgebroken verbinding %ld naar db: '%-.192s' gebruiker: '%-.48s' host: '%-.64s' (%-.64s)"
- eng "Aborted connection %ld to db: '%-.192s' user: '%-.48s' host: '%-.64s' (%-.64s)"
- est "Ühendus katkestatud %ld andmebaas: '%-.192s' kasutaja: '%-.48s' masin: '%-.64s' (%-.64s)"
- fre "Connection %ld avortée vers la bd: '%-.192s' utilisateur: '%-.48s' hôte: '%-.64s' (%-.64s)"
- ger "Abbruch der Verbindung %ld zur Datenbank '%-.192s'. Benutzer: '%-.48s', Host: '%-.64s' (%-.64s)"
- ita "Interrotta la connessione %ld al db: ''%-.192s' utente: '%-.48s' host: '%-.64s' (%-.64s)"
- por "Conexão %ld abortada para banco de dados '%-.192s' - usuário '%-.48s' - 'host' '%-.64s' ('%-.64s')"
- rus "ðÒÅÒ×ÁÎÏ ÓÏÅÄÉÎÅÎÉÅ %ld Ë ÂÁÚÅ ÄÁÎÎÙÈ '%-.192s' ÐÏÌØÚÏ×ÁÔÅÌÑ '%-.48s' Ó ÈÏÓÔÁ '%-.64s' (%-.64s)"
- serbian "Prekinuta konekcija broj %ld ka bazi: '%-.192s' korisnik je bio: '%-.48s' a host: '%-.64s' (%-.64s)"
- spa "Abortada conexión %ld para db: '%-.192s' usuario: '%-.48s' servidor: '%-.64s' (%-.64s)"
- swe "Avbröt länken för tråd %ld till db '%-.192s', användare '%-.48s', host '%-.64s' (%-.64s)"
- ukr "ðÅÒÅÒ×ÁÎÏ Ú'¤ÄÎÁÎÎÑ %ld ÄÏ ÂÁÚÉ ÄÁÎÎÉÈ: '%-.192s' ËÏÒÉÓÔÕ×ÁÞ: '%-.48s' ÈÏÓÔ: '%-.64s' (%-.64s)"
-ER_DUMP_NOT_IMPLEMENTED
- cze "Handler tabulky nepodporuje bin-Bární dump"
- dan "Denne tabeltype unserstøtter ikke binært tabeldump"
- nla "De 'handler' voor de tabel ondersteund geen binaire tabel dump"
- eng "The storage engine for the table does not support binary table dump"
- fre "Ce type de table ne supporte pas les copies binaires"
- ger "Die Speicher-Engine für die Tabelle unterstützt keinen binären Tabellen-Dump"
- ita "Il gestore per la tabella non supporta il dump binario"
- jpn "The handler for the table does not support binary table dump"
- por "O manipulador de tabela não suporta 'dump' binário de tabela"
- rum "The handler for the table does not support binary table dump"
- rus "ïÂÒÁÂÏÔÞÉË ÜÔÏÊ ÔÁÂÌÉÃÙ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ Ä×ÏÉÞÎÏÇÏ ÓÏÈÒÁÎÅÎÉÑ ÏÂÒÁÚÁ ÔÁÂÌÉÃÙ (dump)"
- serbian "Handler tabele ne podržava binarni dump tabele"
- spa "El manipulador de tabla no soporta dump para tabla binaria"
- swe "Tabellhanteraren klarar inte en binär kopiering av tabellen"
- ukr "ãÅÊ ÔÉÐ ÔÁÂÌÉæ ΊЦÄÔÒÉÍÕ¤ ¦ÎÁÒÎÕ ÐÅÒÅÄÁÞÕ ÔÁÂÌÉæ"
-ER_FLUSH_MASTER_BINLOG_CLOSED
- eng "Binlog closed, cannot RESET MASTER"
- ger "Binlog geschlossen. Kann RESET MASTER nicht ausführen"
- por "Binlog fechado. Não pode fazer RESET MASTER"
- rus "ä×ÏÉÞÎÙÊ ÖÕÒÎÁÌ ÏÂÎÏ×ÌÅÎÉÑ ÚÁËÒÙÔ, ÎÅ×ÏÚÍÏÖÎÏ ×ÙÐÏÌÎÉÔØ RESET MASTER"
- serbian "Binarni log file zatvoren, ne mogu da izvršim komandu 'RESET MASTER'"
- ukr "òÅÐ̦ËÁæÊÎÉÊ ÌÏÇ ÚÁËÒÉÔÏ, ÎÅ ÍÏÖÕ ×ÉËÏÎÁÔÉ RESET MASTER"
-ER_INDEX_REBUILD
- cze "P-Bøebudování indexu dumpnuté tabulky '%-.192s' nebylo úspì¹né"
- dan "Kunne ikke genopbygge indekset for den dumpede tabel '%-.192s'"
- nla "Gefaald tijdens heropbouw index van gedumpte tabel '%-.192s'"
- eng "Failed rebuilding the index of dumped table '%-.192s'"
- fre "La reconstruction de l'index de la table copiée '%-.192s' a échoué"
- ger "Neuerstellung des Index der Dump-Tabelle '%-.192s' fehlgeschlagen"
- greek "Failed rebuilding the index of dumped table '%-.192s'"
- hun "Failed rebuilding the index of dumped table '%-.192s'"
- ita "Fallita la ricostruzione dell'indice della tabella copiata '%-.192s'"
- por "Falhou na reconstrução do índice da tabela 'dumped' '%-.192s'"
- rus "ïÛÉÂËÁ ÐÅÒÅÓÔÒÏÊËÉ ÉÎÄÅËÓÁ ÓÏÈÒÁÎÅÎÎÏÊ ÔÁÂÌÉÃÙ '%-.192s'"
- serbian "Izgradnja indeksa dump-ovane tabele '%-.192s' nije uspela"
- spa "Falla reconstruyendo el indice de la tabla dumped '%-.192s'"
- ukr "îÅ×ÄÁÌŠצÄÎÏ×ÌÅÎÎÑ ¦ÎÄÅËÓÁ ÐÅÒÅÄÁÎϧ ÔÁÂÌÉæ '%-.192s'"
-ER_MASTER
- cze "Chyba masteru: '%-.64s'"
- dan "Fejl fra master: '%-.64s'"
- nla "Fout van master: '%-.64s'"
- eng "Error from master: '%-.64s'"
- fre "Erreur reçue du maître: '%-.64s'"
- ger "Fehler vom Master: '%-.64s'"
- ita "Errore dal master: '%-.64s"
- por "Erro no 'master' '%-.64s'"
- rus "ïÛÉÂËÁ ÏÔ ÇÏÌÏ×ÎÏÇÏ ÓÅÒ×ÅÒÁ: '%-.64s'"
- serbian "Greška iz glavnog servera '%-.64s' u klasteru"
- spa "Error del master: '%-.64s'"
- swe "Fick en master: '%-.64s'"
- ukr "ðÏÍÉÌËÁ ×¦Ä ÇÏÌÏ×ÎÏÇÏ: '%-.64s'"
-ER_MASTER_NET_READ 08S01
- cze "S-Bí»ová chyba pøi ètení z masteru"
- dan "Netværksfejl ved læsning fra master"
- nla "Net fout tijdens lezen van master"
- eng "Net error reading from master"
- fre "Erreur de lecture réseau reçue du maître"
- ger "Netzfehler beim Lesen vom Master"
- ita "Errore di rete durante la ricezione dal master"
- por "Erro de rede lendo do 'master'"
- rus "÷ÏÚÎÉËÌÁ ÏÛÉÂËÁ ÞÔÅÎÉÑ × ÐÒÏÃÅÓÓÅ ËÏÍÍÕÎÉËÁÃÉÉ Ó ÇÏÌÏ×ÎÙÍ ÓÅÒ×ÅÒÏÍ"
- serbian "Greška u primanju mrežnih paketa sa glavnog servera u klasteru"
- spa "Error de red leyendo del master"
- swe "Fick nätverksfel vid läsning från master"
- ukr "íÅÒÅÖÅ×Á ÐÏÍÉÌËÁ ÞÉÔÁÎÎÑ ×¦Ä ÇÏÌÏ×ÎÏÇÏ"
-ER_MASTER_NET_WRITE 08S01
- cze "S-Bí»ová chyba pøi zápisu na master"
- dan "Netværksfejl ved skrivning til master"
- nla "Net fout tijdens schrijven naar master"
- eng "Net error writing to master"
- fre "Erreur d'écriture réseau reçue du maître"
- ger "Netzfehler beim Schreiben zum Master"
- ita "Errore di rete durante l'invio al master"
- por "Erro de rede gravando no 'master'"
- rus "÷ÏÚÎÉËÌÁ ÏÛÉÂËÁ ÚÁÐÉÓÉ × ÐÒÏÃÅÓÓÅ ËÏÍÍÕÎÉËÁÃÉÉ Ó ÇÏÌÏ×ÎÙÍ ÓÅÒ×ÅÒÏÍ"
- serbian "Greška u slanju mrežnih paketa na glavni server u klasteru"
- spa "Error de red escribiendo para el master"
- swe "Fick nätverksfel vid skrivning till master"
- ukr "íÅÒÅÖÅ×Á ÐÏÍÉÌËÁ ÚÁÐÉÓÕ ÄÏ ÇÏÌÏ×ÎÏÇÏ"
-ER_FT_MATCHING_KEY_NOT_FOUND
- cze "-B®ádný sloupec nemá vytvoøen fulltextový index"
- dan "Kan ikke finde en FULLTEXT nøgle som svarer til kolonne listen"
- nla "Kan geen FULLTEXT index vinden passend bij de kolom lijst"
- eng "Can't find FULLTEXT index matching the column list"
- est "Ei suutnud leida FULLTEXT indeksit, mis kattuks kasutatud tulpadega"
- fre "Impossible de trouver un index FULLTEXT correspondant à cette liste de colonnes"
- ger "Kann keinen FULLTEXT-Index finden, der der Feldliste entspricht"
- ita "Impossibile trovare un indice FULLTEXT che corrisponda all'elenco delle colonne"
- por "Não pode encontrar um índice para o texto todo que combine com a lista de colunas"
- rus "îÅ×ÏÚÍÏÖÎÏ ÏÔÙÓËÁÔØ ÐÏÌÎÏÔÅËÓÔÏ×ÙÊ (FULLTEXT) ÉÎÄÅËÓ, ÓÏÏÔ×ÅÔÓÔ×ÕÀÝÉÊ ÓÐÉÓËÕ ÓÔÏÌÂÃÏ×"
- serbian "Ne mogu da pronaðem 'FULLTEXT' indeks koli odgovara listi kolona"
- spa "No puedo encontrar índice FULLTEXT correspondiendo a la lista de columnas"
- swe "Hittar inte ett FULLTEXT-index i kolumnlistan"
- ukr "îÅ ÍÏÖÕ ÚÎÁÊÔÉ FULLTEXT ¦ÎÄÅËÓ, ÝÏ ×¦ÄÐÏצÄÁ¤ ÐÅÒÅ̦ËÕ ÓÔÏ×Âæ×"
-ER_LOCK_OR_ACTIVE_TRANSACTION
- cze "Nemohu prov-Bést zadaný pøíkaz, proto¾e existují aktivní zamèené tabulky nebo aktivní transakce"
- dan "Kan ikke udføre den givne kommando fordi der findes aktive, låste tabeller eller fordi der udføres en transaktion"
- nla "Kan het gegeven commando niet uitvoeren, want u heeft actieve gelockte tabellen of een actieve transactie"
- eng "Can't execute the given command because you have active locked tables or an active transaction"
- est "Ei suuda täita antud käsku kuna on aktiivseid lukke või käimasolev transaktsioon"
- fre "Impossible d'exécuter la commande car vous avez des tables verrouillées ou une transaction active"
- ger "Kann den angegebenen Befehl wegen einer aktiven Tabellensperre oder einer aktiven Transaktion nicht ausführen"
- ita "Impossibile eseguire il comando richiesto: tabelle sotto lock o transazione in atto"
- por "Não pode executar o comando dado porque você tem tabelas ativas travadas ou uma transação ativa"
- rus "îÅ×ÏÚÍÏÖÎÏ ×ÙÐÏÌÎÉÔØ ÕËÁÚÁÎÎÕÀ ËÏÍÁÎÄÕ, ÐÏÓËÏÌØËÕ Õ ×ÁÓ ÐÒÉÓÕÔÓÔ×ÕÀÔ ÁËÔÉ×ÎÏ ÚÁÂÌÏËÉÒÏ×ÁÎÎÙÅ ÔÁÂÌÉÃÁ ÉÌÉ ÏÔËÒÙÔÁÑ ÔÒÁÎÚÁËÃÉÑ"
- serbian "Ne mogu da izvršim datu komandu zbog toga što su tabele zakljuèane ili je transakcija u toku"
- spa "No puedo ejecutar el comando dado porque tienes tablas bloqueadas o una transición activa"
- swe "Kan inte utföra kommandot emedan du har en låst tabell eller an aktiv transaktion"
- ukr "îÅ ÍÏÖÕ ×ÉËÏÎÁÔÉ ÐÏÄÁÎÕ ËÏÍÁÎÄÕ ÔÏÍÕ, ÝÏ ÔÁÂÌÉÃÑ ÚÁÂÌÏËÏ×ÁÎÁ ÁÂÏ ×ÉËÏÎÕ¤ÔØÓÑ ÔÒÁÎÚÁËæÑ"
-ER_UNKNOWN_SYSTEM_VARIABLE
- cze "Nezn-Bámá systémová promìnná '%-.64s'"
- dan "Ukendt systemvariabel '%-.64s'"
- nla "Onbekende systeem variabele '%-.64s'"
- eng "Unknown system variable '%-.64s'"
- est "Tundmatu süsteemne muutuja '%-.64s'"
- fre "Variable système '%-.64s' inconnue"
- ger "Unbekannte Systemvariable '%-.64s'"
- ita "Variabile di sistema '%-.64s' sconosciuta"
- por "Variável de sistema '%-.64s' desconhecida"
- rus "îÅÉÚ×ÅÓÔÎÁÑ ÓÉÓÔÅÍÎÁÑ ÐÅÒÅÍÅÎÎÁÑ '%-.64s'"
- serbian "Nepoznata sistemska promenljiva '%-.64s'"
- spa "Desconocida variable de sistema '%-.64s'"
- swe "Okänd systemvariabel: '%-.64s'"
- ukr "îÅצÄÏÍÁ ÓÉÓÔÅÍÎÁ ÚͦÎÎÁ '%-.64s'"
-ER_CRASHED_ON_USAGE
- cze "Tabulka '%-.192s' je ozna-Bèena jako poru¹ená a mìla by být opravena"
- dan "Tabellen '%-.192s' er markeret med fejl og bør repareres"
- nla "Tabel '%-.192s' staat als gecrashed gemarkeerd en dient te worden gerepareerd"
- eng "Table '%-.192s' is marked as crashed and should be repaired"
- est "Tabel '%-.192s' on märgitud vigaseks ja tuleb parandada"
- fre "La table '%-.192s' est marquée 'crashed' et devrait être réparée"
- ger "Tabelle '%-.192s' ist als defekt markiert und sollte repariert werden"
- ita "La tabella '%-.192s' e` segnalata come corrotta e deve essere riparata"
- por "Tabela '%-.192s' está marcada como danificada e deve ser reparada"
- rus "ôÁÂÌÉÃÁ '%-.192s' ÐÏÍÅÞÅÎÁ ËÁË ÉÓÐÏÒÞÅÎÎÁÑ É ÄÏÌÖÎÁ ÐÒÏÊÔÉ ÐÒÏ×ÅÒËÕ É ÒÅÍÏÎÔ"
- serbian "Tabela '%-.192s' je markirana kao ošteæena i trebala bi biti popravljena"
- spa "Tabla '%-.192s' está marcada como crashed y debe ser reparada"
- swe "Tabell '%-.192s' är trasig och bör repareras med REPAIR TABLE"
- ukr "ôÁÂÌÉÃÀ '%-.192s' ÍÁÒËÏ×ÁÎÏ ÑË Ú¦ÐÓÏ×ÁÎÕ ÔÁ §§ ÐÏÔÒ¦ÂÎÏ ×¦ÄÎÏ×ÉÔÉ"
-ER_CRASHED_ON_REPAIR
- cze "Tabulka '%-.192s' je ozna-Bèena jako poru¹ená a poslední (automatická?) oprava se nezdaøila"
- dan "Tabellen '%-.192s' er markeret med fejl og sidste (automatiske?) REPAIR fejlede"
- nla "Tabel '%-.192s' staat als gecrashed gemarkeerd en de laatste (automatische?) reparatie poging mislukte"
- eng "Table '%-.192s' is marked as crashed and last (automatic?) repair failed"
- est "Tabel '%-.192s' on märgitud vigaseks ja viimane (automaatne?) parandus ebaõnnestus"
- fre "La table '%-.192s' est marquée 'crashed' et le dernier 'repair' a échoué"
- ger "Tabelle '%-.192s' ist als defekt markiert und der letzte (automatische?) Reparaturversuch schlug fehl"
- ita "La tabella '%-.192s' e` segnalata come corrotta e l'ultima ricostruzione (automatica?) e` fallita"
- por "Tabela '%-.192s' está marcada como danificada e a última reparação (automática?) falhou"
- rus "ôÁÂÌÉÃÁ '%-.192s' ÐÏÍÅÞÅÎÁ ËÁË ÉÓÐÏÒÞÅÎÎÁÑ É ÐÏÓÌÅÄÎÉÊ (Á×ÔÏÍÁÔÉÞÅÓËÉÊ?) ÒÅÍÏÎÔ ÎÅ ÂÙÌ ÕÓÐÅÛÎÙÍ"
- serbian "Tabela '%-.192s' je markirana kao ošteæena, a zadnja (automatska?) popravka je bila neuspela"
- spa "Tabla '%-.192s' está marcada como crashed y la última reparación (automactica?) falló"
- swe "Tabell '%-.192s' är trasig och senast (automatiska?) reparation misslyckades"
- ukr "ôÁÂÌÉÃÀ '%-.192s' ÍÁÒËÏ×ÁÎÏ ÑË Ú¦ÐÓÏ×ÁÎÕ ÔÁ ÏÓÔÁÎΤ (Á×ÔÏÍÁÔÉÞÎÅ?) צÄÎÏ×ÌÅÎÎÑ ÎÅ ×ÄÁÌÏÓÑ"
-ER_WARNING_NOT_COMPLETE_ROLLBACK
- dan "Advarsel: Visse data i tabeller der ikke understøtter transaktioner kunne ikke tilbagestilles"
- nla "Waarschuwing: Roll back mislukt voor sommige buiten transacties gewijzigde tabellen"
- eng "Some non-transactional changed tables couldn't be rolled back"
- est "Hoiatus: mõnesid transaktsioone mittetoetavaid tabeleid ei suudetud tagasi kerida"
- fre "Attention: certaines tables ne supportant pas les transactions ont été changées et elles ne pourront pas être restituées"
- ger "Änderungen an einigen nicht transaktionalen Tabellen konnten nicht zurückgerollt werden"
- ita "Attenzione: Alcune delle modifiche alle tabelle non transazionali non possono essere ripristinate (roll back impossibile)"
- por "Aviso: Algumas tabelas não-transacionais alteradas não puderam ser reconstituídas (rolled back)"
- rus "÷ÎÉÍÁÎÉÅ: ÐÏ ÎÅËÏÔÏÒÙÍ ÉÚÍÅÎÅÎÎÙÍ ÎÅÔÒÁÎÚÁËÃÉÏÎÎÙÍ ÔÁÂÌÉÃÁÍ ÎÅ×ÏÚÍÏÖÎÏ ÂÕÄÅÔ ÐÒÏÉÚ×ÅÓÔÉ ÏÔËÁÔ ÔÒÁÎÚÁËÃÉÉ"
- serbian "Upozorenje: Neke izmenjene tabele ne podržavaju komandu 'ROLLBACK'"
- spa "Aviso: Algunas tablas no transancionales no pueden tener rolled back"
- swe "Warning: Några icke transaktionella tabeller kunde inte återställas vid ROLLBACK"
- ukr "úÁÓÔÅÒÅÖÅÎÎÑ: äÅÑ˦ ÎÅÔÒÁÎÚÁËæÊΦ ÚͦÎÉ ÔÁÂÌÉÃØ ÎÅ ÍÏÖÎÁ ÂÕÄÅ ÐÏ×ÅÒÎÕÔÉ"
-ER_TRANS_CACHE_FULL
- dan "Fler-udtryks transaktion krævede mere plads en 'max_binlog_cache_size' bytes. Forhøj værdien af denne variabel og prøv igen"
- nla "Multi-statement transactie vereist meer dan 'max_binlog_cache_size' bytes opslag. Verhoog deze mysqld variabele en probeer opnieuw"
- eng "Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage; increase this mysqld variable and try again"
- est "Mitme lausendiga transaktsioon nõudis rohkem ruumi kui lubatud 'max_binlog_cache_size' muutujaga. Suurenda muutuja väärtust ja proovi uuesti"
- fre "Cette transaction à commandes multiples nécessite plus de 'max_binlog_cache_size' octets de stockage, augmentez cette variable de mysqld et réessayez"
- ger "Transaktionen, die aus mehreren Befehlen bestehen, benötigten mehr als 'max_binlog_cache_size' Bytes an Speicher. Btte vergrössern Sie diese Server-Variable versuchen Sie es noch einmal"
- ita "La transazione a comandi multipli (multi-statement) ha richiesto piu` di 'max_binlog_cache_size' bytes di disco: aumentare questa variabile di mysqld e riprovare"
- por "Transações multi-declaradas (multi-statement transactions) requeriram mais do que o valor limite (max_binlog_cache_size) de bytes para armazenagem. Aumente o valor desta variável do mysqld e tente novamente"
- rus "ôÒÁÎÚÁËÃÉÉ, ×ËÌÀÞÁÀÝÅÊ ÂÏÌØÛÏÅ ËÏÌÉÞÅÓÔ×Ï ËÏÍÁÎÄ, ÐÏÔÒÅÂÏ×ÁÌÏÓØ ÂÏÌÅÅ ÞÅÍ 'max_binlog_cache_size' ÂÁÊÔ. õ×ÅÌÉÞØÔÅ ÜÔÕ ÐÅÒÅÍÅÎÎÕÀ ÓÅÒ×ÅÒÁ mysqld É ÐÏÐÒÏÂÕÊÔÅ ÅÝÅ ÒÁÚ"
- spa "Multipla transición necesita mas que 'max_binlog_cache_size' bytes de almacenamiento. Aumente esta variable mysqld y tente de nuevo"
- swe "Transaktionen krävde mera än 'max_binlog_cache_size' minne. Öka denna mysqld-variabel och försök på nytt"
- ukr "ôÒÁÎÚÁËÃ¦Ñ Ú ÂÁÇÁÔØÍÁ ×ÉÒÁÚÁÍÉ ×ÉÍÁÇÁ¤ ¦ÌØÛÅ Î¦Ö 'max_binlog_cache_size' ÂÁÊÔ¦× ÄÌÑ ÚÂÅÒ¦ÇÁÎÎÑ. ú¦ÌØÛÔÅ ÃÀ ÚͦÎÎÕ mysqld ÔÁ ÓÐÒÏÂÕÊÔÅ ÚÎÏ×Õ"
-ER_SLAVE_MUST_STOP
- dan "Denne handling kunne ikke udføres med kørende slave, brug først kommandoen STOP SLAVE"
- nla "Deze operatie kan niet worden uitgevoerd met een actieve slave, doe eerst STOP SLAVE"
- eng "This operation cannot be performed with a running slave; run STOP SLAVE first"
- fre "Cette opération ne peut être réalisée avec un esclave actif, faites STOP SLAVE d'abord"
- ger "Diese Operation kann bei einem aktiven Slave nicht durchgeführt werden. Bitte zuerst STOP SLAVE ausführen"
- ita "Questa operazione non puo' essere eseguita con un database 'slave' che gira, lanciare prima STOP SLAVE"
- por "Esta operação não pode ser realizada com um 'slave' em execução. Execute STOP SLAVE primeiro"
- rus "üÔÕ ÏÐÅÒÁÃÉÀ ÎÅ×ÏÚÍÏÖÎÏ ×ÙÐÏÌÎÉÔØ ÐÒÉ ÒÁÂÏÔÁÀÝÅÍ ÐÏÔÏËÅ ÐÏÄÞÉÎÅÎÎÏÇÏ ÓÅÒ×ÅÒÁ. óÎÁÞÁÌÁ ×ÙÐÏÌÎÉÔÅ STOP SLAVE"
- serbian "Ova operacija ne može biti izvršena dok je aktivan podreðeni server. Zadajte prvo komandu 'STOP SLAVE' da zaustavite podreðeni server."
- spa "Esta operación no puede ser hecha con el esclavo funcionando, primero use STOP SLAVE"
- swe "Denna operation kan inte göras under replikering; Gör STOP SLAVE först"
- ukr "ïÐÅÒÁÃ¦Ñ ÎÅ ÍÏÖÅ ÂÕÔÉ ×ÉËÏÎÁÎÁ Ú ÚÁÐÕÝÅÎÉÍ Ð¦ÄÌÅÇÌÉÍ, ÓÐÏÞÁÔËÕ ×ÉËÏÎÁÊÔÅ STOP SLAVE"
-ER_SLAVE_NOT_RUNNING
- dan "Denne handling kræver en kørende slave. Konfigurer en slave og brug kommandoen START SLAVE"
- nla "Deze operatie vereist een actieve slave, configureer slave en doe dan START SLAVE"
- eng "This operation requires a running slave; configure slave and do START SLAVE"
- fre "Cette opération nécessite un esclave actif, configurez les esclaves et faites START SLAVE"
- ger "Diese Operation benötigt einen aktiven Slave. Bitte Slave konfigurieren und mittels START SLAVE aktivieren"
- ita "Questa operaione richiede un database 'slave', configurarlo ed eseguire START SLAVE"
- por "Esta operação requer um 'slave' em execução. Configure o 'slave' e execute START SLAVE"
- rus "äÌÑ ÜÔÏÊ ÏÐÅÒÁÃÉÉ ÔÒÅÂÕÅÔÓÑ ÒÁÂÏÔÁÀÝÉÊ ÐÏÄÞÉÎÅÎÎÙÊ ÓÅÒ×ÅÒ. óÎÁÞÁÌÁ ×ÙÐÏÌÎÉÔÅ START SLAVE"
- serbian "Ova operacija zahteva da je aktivan podreðeni server. Konfigurišite prvo podreðeni server i onda izvršite komandu 'START SLAVE'"
- spa "Esta operación necesita el esclavo funcionando, configure esclavo y haga el START SLAVE"
- swe "Denna operation kan endast göras under replikering; Konfigurera slaven och gör START SLAVE"
- ukr "ïÐÅÒÁÃ¦Ñ ×ÉÍÁÇÁ¤ ÚÁÐÕÝÅÎÏÇÏ Ð¦ÄÌÅÇÌÏÇÏ, ÚËÏÎƦÇÕÒÕÊÔŠЦÄÌÅÇÌÏÇÏ ÔÁ ×ÉËÏÎÁÊÔÅ START SLAVE"
-ER_BAD_SLAVE
- dan "Denne server er ikke konfigureret som slave. Ret in config-filen eller brug kommandoen CHANGE MASTER TO"
- nla "De server is niet geconfigureerd als slave, fix in configuratie bestand of met CHANGE MASTER TO"
- eng "The server is not configured as slave; fix in config file or with CHANGE MASTER TO"
- fre "Le server n'est pas configuré comme un esclave, changez le fichier de configuration ou utilisez CHANGE MASTER TO"
- ger "Der Server ist nicht als Slave konfiguriert. Bitte in der Konfigurationsdatei oder mittels CHANGE MASTER TO beheben"
- ita "Il server non e' configurato come 'slave', correggere il file di configurazione cambiando CHANGE MASTER TO"
- por "O servidor não está configurado como 'slave'. Acerte o arquivo de configuração ou use CHANGE MASTER TO"
- rus "üÔÏÔ ÓÅÒ×ÅÒ ÎÅ ÎÁÓÔÒÏÅÎ ËÁË ÐÏÄÞÉÎÅÎÎÙÊ. ÷ÎÅÓÉÔÅ ÉÓÐÒÁ×ÌÅÎÉÑ × ËÏÎÆÉÇÕÒÁÃÉÏÎÎÏÍ ÆÁÊÌÅ ÉÌÉ Ó ÐÏÍÏÝØÀ CHANGE MASTER TO"
- serbian "Server nije konfigurisan kao podreðeni server, ispravite konfiguracioni file ili na njemu izvršite komandu 'CHANGE MASTER TO'"
- spa "El servidor no está configurado como esclavo, edite el archivo config file o con CHANGE MASTER TO"
- swe "Servern är inte konfigurerade som en replikationsslav. Ändra konfigurationsfilen eller gör CHANGE MASTER TO"
- ukr "óÅÒ×ÅÒ ÎÅ ÚËÏÎƦÇÕÒÏ×ÁÎÏ ÑË Ð¦ÄÌÅÇÌÉÊ, ×ÉÐÒÁ×ÔÅ ÃÅ Õ ÆÁÊ̦ ËÏÎƦÇÕÒÁæ§ ÁÂÏ Ú CHANGE MASTER TO"
-ER_MASTER_INFO
- eng "Could not initialize master info structure; more error messages can be found in the MySQL error log"
- fre "Impossible d'initialiser les structures d'information de maître, vous trouverez des messages d'erreur supplémentaires dans le journal des erreurs de MySQL"
- ger "Konnte Master-Info-Struktur nicht initialisieren. Weitere Fehlermeldungen können im MySQL-Error-Log eingesehen werden"
- serbian "Nisam mogao da inicijalizujem informacionu strukturu glavnog servera, proverite da li imam privilegije potrebne za pristup file-u 'master.info'"
- swe "Kunde inte initialisera replikationsstrukturerna. See MySQL fel fil för mera information"
-ER_SLAVE_THREAD
- dan "Kunne ikke danne en slave-tråd; check systemressourcerne"
- nla "Kon slave thread niet aanmaken, controleer systeem resources"
- eng "Could not create slave thread; check system resources"
- fre "Impossible de créer une tâche esclave, vérifiez les ressources système"
- ger "Konnte Slave-Thread nicht starten. Bitte System-Ressourcen überprüfen"
- ita "Impossibile creare il thread 'slave', controllare le risorse di sistema"
- por "Não conseguiu criar 'thread' de 'slave'. Verifique os recursos do sistema"
- rus "îÅ×ÏÚÍÏÖÎÏ ÓÏÚÄÁÔØ ÐÏÔÏË ÐÏÄÞÉÎÅÎÎÏÇÏ ÓÅÒ×ÅÒÁ. ðÒÏ×ÅÒØÔÅ ÓÉÓÔÅÍÎÙÅ ÒÅÓÕÒÓÙ"
- serbian "Nisam mogao da startujem thread za podreðeni server, proverite sistemske resurse"
- spa "No puedo crear el thread esclavo, verifique recursos del sistema"
- swe "Kunde inte starta en tråd för replikering"
- ukr "îÅ ÍÏÖÕ ÓÔ×ÏÒÉÔÉ Ð¦ÄÌÅÇÌÕ Ç¦ÌËÕ, ÐÅÒÅצÒÔÅ ÓÉÓÔÅÍΦ ÒÅÓÕÒÓÉ"
-ER_TOO_MANY_USER_CONNECTIONS 42000
- dan "Brugeren %-.64s har allerede mere end 'max_user_connections' aktive forbindelser"
- nla "Gebruiker %-.64s heeft reeds meer dan 'max_user_connections' actieve verbindingen"
- eng "User %-.64s already has more than 'max_user_connections' active connections"
- est "Kasutajal %-.64s on juba rohkem ühendusi kui lubatud 'max_user_connections' muutujaga"
- fre "L'utilisateur %-.64s possède déjà plus de 'max_user_connections' connexions actives"
- ger "Benutzer '%-.64s' hat mehr als 'max_user_connections' aktive Verbindungen"
- ita "L'utente %-.64s ha gia' piu' di 'max_user_connections' connessioni attive"
- por "Usuário '%-.64s' já possui mais que o valor máximo de conexões (max_user_connections) ativas"
- rus "õ ÐÏÌØÚÏ×ÁÔÅÌÑ %-.64s ÕÖÅ ÂÏÌØÛÅ ÞÅÍ 'max_user_connections' ÁËÔÉ×ÎÙÈ ÓÏÅÄÉÎÅÎÉÊ"
- serbian "Korisnik %-.64s veæ ima više aktivnih konekcija nego što je to odreðeno 'max_user_connections' promenljivom"
- spa "Usario %-.64s ya tiene mas que 'max_user_connections' conexiones activas"
- swe "Användare '%-.64s' har redan 'max_user_connections' aktiva inloggningar"
- ukr "ëÏÒÉÓÔÕ×ÁÞ %-.64s ×ÖÅ ÍÁ¤ ¦ÌØÛÅ Î¦Ö 'max_user_connections' ÁËÔÉ×ÎÉÈ Ú'¤ÄÎÁÎØ"
-ER_SET_CONSTANTS_ONLY
- dan "Du må kun bruge konstantudtryk med SET"
- nla "U mag alleen constante expressies gebruiken bij SET"
- eng "You may only use constant expressions with SET"
- est "Ainult konstantsed suurused on lubatud SET klauslis"
- fre "Seules les expressions constantes sont autorisées avec SET"
- ger "Bei SET dürfen nur konstante Ausdrücke verwendet werden"
- ita "Si possono usare solo espressioni costanti con SET"
- por "Você pode usar apenas expressões constantes com SET"
- rus "÷Ù ÍÏÖÅÔÅ ÉÓÐÏÌØÚÏ×ÁÔØ × SET ÔÏÌØËÏ ËÏÎÓÔÁÎÔÎÙÅ ×ÙÒÁÖÅÎÉÑ"
- serbian "Možete upotrebiti samo konstantan iskaz sa komandom 'SET'"
- spa "Tu solo debes usar expresiones constantes con SET"
- swe "Man kan endast använda konstantuttryck med SET"
- ukr "íÏÖÎÁ ×ÉËÏÒÉÓÔÏ×Õ×ÁÔÉ ÌÉÛÅ ×ÉÒÁÚÉ Ú¦ ÓÔÁÌÉÍÉ Õ SET"
-ER_LOCK_WAIT_TIMEOUT
- dan "Lock wait timeout overskredet"
- nla "Lock wacht tijd overschreden"
- eng "Lock wait timeout exceeded; try restarting transaction"
- est "Kontrollaeg ületatud luku järel ootamisel; Proovi transaktsiooni otsast alata"
- fre "Timeout sur l'obtention du verrou"
- ger "Beim Warten auf eine Sperre wurde die zulässige Wartezeit überschritten. Bitte versuchen Sie, die Transaktion neu zu starten"
- ita "E' scaduto il timeout per l'attesa del lock"
- por "Tempo de espera (timeout) de travamento excedido. Tente reiniciar a transação."
- rus "ôÁÊÍÁÕÔ ÏÖÉÄÁÎÉÑ ÂÌÏËÉÒÏ×ËÉ ÉÓÔÅË; ÐÏÐÒÏÂÕÊÔÅ ÐÅÒÅÚÁÐÕÓÔÉÔØ ÔÒÁÎÚÁËÃÉÀ"
- serbian "Vremenski limit za zakljuèavanje tabele je istekao; Probajte da ponovo startujete transakciju"
- spa "Tiempo de bloqueo de espera excedido"
- swe "Fick inte ett lås i tid ; Försök att starta om transaktionen"
- ukr "úÁÔÒÉÍËÕ ÏÞ¦ËÕ×ÁÎÎÑ ÂÌÏËÕ×ÁÎÎÑ ×ÉÞÅÒÐÁÎÏ"
-ER_LOCK_TABLE_FULL
- dan "Det totale antal låse overstiger størrelsen på låse-tabellen"
- nla "Het totale aantal locks overschrijdt de lock tabel grootte"
- eng "The total number of locks exceeds the lock table size"
- est "Lukkude koguarv ületab lukutabeli suuruse"
- fre "Le nombre total de verrou dépasse la taille de la table des verrous"
- ger "Die Gesamtzahl der Sperren überschreitet die Größe der Sperrtabelle"
- ita "Il numero totale di lock e' maggiore della grandezza della tabella di lock"
- por "O número total de travamentos excede o tamanho da tabela de travamentos"
- rus "ïÂÝÅÅ ËÏÌÉÞÅÓÔ×Ï ÂÌÏËÉÒÏ×ÏË ÐÒÅ×ÙÓÉÌÏ ÒÁÚÍÅÒÙ ÔÁÂÌÉÃÙ ÂÌÏËÉÒÏ×ÏË"
- serbian "Broj totalnih zakljuèavanja tabele premašuje velièinu tabele zakljuèavanja"
- spa "El número total de bloqueos excede el tamaño de bloqueo de la tabla"
- swe "Antal lås överskrider antalet reserverade lås"
- ukr "úÁÇÁÌØÎÁ ˦ÌØ˦ÓÔØ ÂÌÏËÕ×ÁÎØ ÐÅÒÅ×ÉÝÉÌÁ ÒÏÚÍ¦Ò ÂÌÏËÕ×ÁÎØ ÄÌÑ ÔÁÂÌÉæ"
-ER_READ_ONLY_TRANSACTION 25000
- dan "Update lås kan ikke opnås under en READ UNCOMMITTED transaktion"
- nla "Update locks kunnen niet worden verkregen tijdens een READ UNCOMMITTED transactie"
- eng "Update locks cannot be acquired during a READ UNCOMMITTED transaction"
- est "Uuenduslukke ei saa kasutada READ UNCOMMITTED transaktsiooni käigus"
- fre "Un verrou en update ne peut être acquit pendant une transaction READ UNCOMMITTED"
- ger "Während einer READ-UNCOMMITTED-Transaktion können keine UPDATE-Sperren angefordert werden"
- ita "I lock di aggiornamento non possono essere acquisiti durante una transazione 'READ UNCOMMITTED'"
- por "Travamentos de atualização não podem ser obtidos durante uma transação de tipo READ UNCOMMITTED"
- rus "âÌÏËÉÒÏ×ËÉ ÏÂÎÏ×ÌÅÎÉÊ ÎÅÌØÚÑ ÐÏÌÕÞÉÔØ × ÐÒÏÃÅÓÓÅ ÞÔÅÎÉÑ ÎÅ ÐÒÉÎÑÔÏÊ (× ÒÅÖÉÍÅ READ UNCOMMITTED) ÔÒÁÎÚÁËÃÉÉ"
- serbian "Zakljuèavanja izmena ne mogu biti realizovana sve dok traje 'READ UNCOMMITTED' transakcija"
- spa "Bloqueos de actualización no pueden ser adqueridos durante una transición READ UNCOMMITTED"
- swe "Updateringslås kan inte göras när man använder READ UNCOMMITTED"
- ukr "ïÎÏ×ÉÔÉ ÂÌÏËÕ×ÁÎÎÑ ÎÅ ÍÏÖÌÉ×Ï ÎÁ ÐÒÏÔÑÚ¦ ÔÒÁÎÚÁËæ§ READ UNCOMMITTED"
-ER_DROP_DB_WITH_READ_LOCK
- dan "DROP DATABASE er ikke tilladt mens en tråd holder på globalt read lock"
- nla "DROP DATABASE niet toegestaan terwijl thread een globale 'read lock' bezit"
- eng "DROP DATABASE not allowed while thread is holding global read lock"
- est "DROP DATABASE ei ole lubatud kui lõim omab globaalset READ lukku"
- fre "DROP DATABASE n'est pas autorisée pendant qu'une tâche possède un verrou global en lecture"
- ger "DROP DATABASE ist nicht erlaubt, solange der Thread eine globale Lesesperre hält"
- ita "DROP DATABASE non e' permesso mentre il thread ha un lock globale di lettura"
- por "DROP DATABASE não permitido enquanto uma 'thread' está mantendo um travamento global de leitura"
- rus "îÅ ÄÏÐÕÓËÁÅÔÓÑ DROP DATABASE, ÐÏËÁ ÐÏÔÏË ÄÅÒÖÉÔ ÇÌÏÂÁÌØÎÕÀ ÂÌÏËÉÒÏ×ËÕ ÞÔÅÎÉÑ"
- serbian "Komanda 'DROP DATABASE' nije dozvoljena dok thread globalno zakljuèava èitanje podataka"
- spa "DROP DATABASE no permitido mientras un thread está ejerciendo un bloqueo de lectura global"
- swe "DROP DATABASE är inte tillåtet när man har ett globalt läslås"
- ukr "DROP DATABASE ÎÅ ÄÏÚ×ÏÌÅÎÏ ÄÏËÉ Ç¦ÌËÁ ÐÅÒÅÂÕ×Á¤ Ð¦Ä ÚÁÇÁÌØÎÉÍ ÂÌÏËÕ×ÁÎÎÑÍ ÞÉÔÁÎÎÑ"
-ER_CREATE_DB_WITH_READ_LOCK
- dan "CREATE DATABASE er ikke tilladt mens en tråd holder på globalt read lock"
- nla "CREATE DATABASE niet toegestaan terwijl thread een globale 'read lock' bezit"
- eng "CREATE DATABASE not allowed while thread is holding global read lock"
- est "CREATE DATABASE ei ole lubatud kui lõim omab globaalset READ lukku"
- fre "CREATE DATABASE n'est pas autorisée pendant qu'une tâche possède un verrou global en lecture"
- ger "CREATE DATABASE ist nicht erlaubt, solange der Thread eine globale Lesesperre hält"
- ita "CREATE DATABASE non e' permesso mentre il thread ha un lock globale di lettura"
- por "CREATE DATABASE não permitido enquanto uma 'thread' está mantendo um travamento global de leitura"
- rus "îÅ ÄÏÐÕÓËÁÅÔÓÑ CREATE DATABASE, ÐÏËÁ ÐÏÔÏË ÄÅÒÖÉÔ ÇÌÏÂÁÌØÎÕÀ ÂÌÏËÉÒÏ×ËÕ ÞÔÅÎÉÑ"
- serbian "Komanda 'CREATE DATABASE' nije dozvoljena dok thread globalno zakljuèava èitanje podataka"
- spa "CREATE DATABASE no permitido mientras un thread está ejerciendo un bloqueo de lectura global"
- swe "CREATE DATABASE är inte tillåtet när man har ett globalt läslås"
- ukr "CREATE DATABASE ÎÅ ÄÏÚ×ÏÌÅÎÏ ÄÏËÉ Ç¦ÌËÁ ÐÅÒÅÂÕ×Á¤ Ð¦Ä ÚÁÇÁÌØÎÉÍ ÂÌÏËÕ×ÁÎÎÑÍ ÞÉÔÁÎÎÑ"
-ER_WRONG_ARGUMENTS
- nla "Foutieve parameters voor %s"
- eng "Incorrect arguments to %s"
- est "Vigased parameetrid %s-le"
- fre "Mauvais arguments à %s"
- ger "Falsche Argumente für %s"
- ita "Argomenti errati a %s"
- por "Argumentos errados para %s"
- rus "îÅ×ÅÒÎÙÅ ÐÁÒÁÍÅÔÒÙ ÄÌÑ %s"
- serbian "Pogrešni argumenti prosleðeni na %s"
- spa "Argumentos errados para %s"
- swe "Felaktiga argument till %s"
- ukr "èÉÂÎÉÊ ÁÒÇÕÍÅÎÔ ÄÌÑ %s"
-ER_NO_PERMISSION_TO_CREATE_USER 42000
- nla "'%-.48s'@'%-.64s' mag geen nieuwe gebruikers creeren"
- eng "'%-.48s'@'%-.64s' is not allowed to create new users"
- est "Kasutajal '%-.48s'@'%-.64s' ei ole lubatud luua uusi kasutajaid"
- fre "'%-.48s'@'%-.64s' n'est pas autorisé à créer de nouveaux utilisateurs"
- ger "'%-.48s'@'%-.64s' ist nicht berechtigt, neue Benutzer hinzuzufügen"
- ita "A '%-.48s'@'%-.64s' non e' permesso creare nuovi utenti"
- por "Não é permitido a '%-.48s'@'%-.64s' criar novos usuários"
- rus "'%-.48s'@'%-.64s' ÎÅ ÒÁÚÒÅÛÁÅÔÓÑ ÓÏÚÄÁ×ÁÔØ ÎÏ×ÙÈ ÐÏÌØÚÏ×ÁÔÅÌÅÊ"
- serbian "Korisniku '%-.48s'@'%-.64s' nije dozvoljeno da kreira nove korisnike"
- spa "'%-.48s`@`%-.64s` no es permitido para crear nuevos usuarios"
- swe "'%-.48s'@'%-.64s' har inte rättighet att skapa nya användare"
- ukr "ëÏÒÉÓÔÕ×ÁÞÕ '%-.48s'@'%-.64s' ÎÅ ÄÏÚ×ÏÌÅÎÏ ÓÔ×ÏÒÀ×ÁÔÉ ÎÏ×ÉÈ ËÏÒÉÓÔÕ×ÁÞ¦×"
-ER_UNION_TABLES_IN_DIFFERENT_DIR
- nla "Incorrecte tabel definitie; alle MERGE tabellen moeten tot dezelfde database behoren"
- eng "Incorrect table definition; all MERGE tables must be in the same database"
- est "Vigane tabelimääratlus; kõik MERGE tabeli liikmed peavad asuma samas andmebaasis"
- fre "Définition de table incorrecte; toutes les tables MERGE doivent être dans la même base de donnée"
- ger "Falsche Tabellendefinition. Alle MERGE-Tabellen müssen sich in derselben Datenbank befinden"
- ita "Definizione della tabella errata; tutte le tabelle di tipo MERGE devono essere nello stesso database"
- por "Definição incorreta da tabela. Todas as tabelas contidas na junção devem estar no mesmo banco de dados."
- rus "îÅ×ÅÒÎÏÅ ÏÐÒÅÄÅÌÅÎÉÅ ÔÁÂÌÉÃÙ; ÷ÓÅ ÔÁÂÌÉÃÙ × MERGE ÄÏÌÖÎÙ ÐÒÉÎÁÄÌÅÖÁÔØ ÏÄÎÏÊ É ÔÏÊ ÖÅ ÂÁÚÅ ÄÁÎÎÙÈ"
- serbian "Pogrešna definicija tabele; sve 'MERGE' tabele moraju biti u istoj bazi podataka"
- spa "Incorrecta definición de la tabla; Todas las tablas MERGE deben estar en el mismo banco de datos"
- swe "Felaktig tabelldefinition; alla tabeller i en MERGE-tabell måste vara i samma databas"
-ER_LOCK_DEADLOCK 40001
- nla "Deadlock gevonden tijdens lock-aanvraag poging; Probeer herstart van de transactie"
- eng "Deadlock found when trying to get lock; try restarting transaction"
- est "Lukustamisel tekkis tupik (deadlock); alusta transaktsiooni otsast"
- fre "Deadlock découvert en essayant d'obtenir les verrous : essayez de redémarrer la transaction"
- ger "Beim Versuch, eine Sperre anzufordern, ist ein Deadlock aufgetreten. Versuchen Sie, die Transaktion neu zu starten"
- ita "Trovato deadlock durante il lock; Provare a far ripartire la transazione"
- por "Encontrado um travamento fatal (deadlock) quando tentava obter uma trava. Tente reiniciar a transação."
- rus "÷ÏÚÎÉËÌÁ ÔÕÐÉËÏ×ÁÑ ÓÉÔÕÁÃÉÑ × ÐÒÏÃÅÓÓÅ ÐÏÌÕÞÅÎÉÑ ÂÌÏËÉÒÏ×ËÉ; ðÏÐÒÏÂÕÊÔÅ ÐÅÒÅÚÁÐÕÓÔÉÔØ ÔÒÁÎÚÁËÃÉÀ"
- serbian "Unakrsno zakljuèavanje pronaðeno kada sam pokušao da dobijem pravo na zakljuèavanje; Probajte da restartujete transakciju"
- spa "Encontrado deadlock cuando tentando obtener el bloqueo; Tente recomenzar la transición"
- swe "Fick 'DEADLOCK' vid låsförsök av block/rad. Försök att starta om transaktionen"
-ER_TABLE_CANT_HANDLE_FT
- nla "Het gebruikte tabel type ondersteund geen FULLTEXT indexen"
- eng "The used table type doesn't support FULLTEXT indexes"
- est "Antud tabelitüüp ei toeta FULLTEXT indekseid"
- fre "Le type de table utilisé ne supporte pas les index FULLTEXT"
- ger "Der verwendete Tabellentyp unterstützt keine FULLTEXT-Indizes"
- ita "La tabella usata non supporta gli indici FULLTEXT"
- por "O tipo de tabela utilizado não suporta índices de texto completo (fulltext indexes)"
- rus "éÓÐÏÌØÚÕÅÍÙÊ ÔÉÐ ÔÁÂÌÉà ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÐÏÌÎÏÔÅËÓÔÏ×ÙÈ ÉÎÄÅËÓÏ×"
- serbian "Upotrebljeni tip tabele ne podržava 'FULLTEXT' indekse"
- spa "El tipo de tabla usada no soporta índices FULLTEXT"
- swe "Tabelltypen har inte hantering av FULLTEXT-index"
- ukr "÷ÉËÏÒÉÓÔÁÎÉÊ ÔÉÐ ÔÁÂÌÉæ ΊЦÄÔÒÉÍÕ¤ FULLTEXT ¦ÎÄÅËÓ¦×"
-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)"
- 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_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"
- fre "Impossible d'ajouter un enregistrement fils : une constrainte externe l'empèche"
- ger "Hinzufügen oder Aktualisieren eines Kind-Datensatzes schlug aufgrund einer Fremdschlüssel-Beschränkung fehl"
- greek "Cannot add a child row: a foreign key constraint fails"
- hun "Cannot add a child row: a foreign key constraint fails"
- ita "Impossibile aggiungere la riga: un vincolo d'integrita' referenziale non e' soddisfatto"
- norwegian-ny "Cannot add a child row: a foreign key constraint fails"
- por "Não pode acrescentar uma linha filha: uma restrição de chave estrangeira falhou"
- rus "îÅ×ÏÚÍÏÖÎÏ ÄÏÂÁ×ÉÔØ ÉÌÉ ÏÂÎÏ×ÉÔØ ÄÏÞÅÒÎÀÀ ÓÔÒÏËÕ: ÐÒÏ×ÅÒËÁ ÏÇÒÁÎÉÞÅÎÉÊ ×ÎÅÛÎÅÇÏ ËÌÀÞÁ ÎÅ ×ÙÐÏÌÎÑÅÔÓÑ"
- spa "No puede adicionar una línea hijo: falla de clave extranjera constraint"
- swe "FOREIGN KEY-konflikt: Kan inte skriva barn"
-ER_ROW_IS_REFERENCED 23000
- eng "Cannot delete or update a parent row: a foreign key constraint fails"
- fre "Impossible de supprimer un enregistrement père : une constrainte externe l'empèche"
- ger "Löschen oder Aktualisieren eines Eltern-Datensatzes schlug aufgrund einer Fremdschlüssel-Beschränkung fehl"
- greek "Cannot delete a parent row: a foreign key constraint fails"
- hun "Cannot delete a parent row: a foreign key constraint fails"
- ita "Impossibile cancellare la riga: un vincolo d'integrita' referenziale non e' soddisfatto"
- por "Não pode apagar uma linha pai: uma restrição de chave estrangeira falhou"
- rus "îÅ×ÏÚÍÏÖÎÏ ÕÄÁÌÉÔØ ÉÌÉ ÏÂÎÏ×ÉÔØ ÒÏÄÉÔÅÌØÓËÕÀ ÓÔÒÏËÕ: ÐÒÏ×ÅÒËÁ ÏÇÒÁÎÉÞÅÎÉÊ ×ÎÅÛÎÅÇÏ ËÌÀÞÁ ÎÅ ×ÙÐÏÌÎÑÅÔÓÑ"
- serbian "Ne mogu da izbrišem roditeljski slog: provera spoljnog kljuèa je neuspela"
- spa "No puede deletar una línea padre: falla de clave extranjera constraint"
- swe "FOREIGN KEY-konflikt: Kan inte radera fader"
-ER_CONNECT_TO_MASTER 08S01
- nla "Fout bij opbouwen verbinding naar master: %-.128s"
- eng "Error connecting to master: %-.128s"
- ger "Fehler bei der Verbindung zum Master: %-.128s"
- ita "Errore durante la connessione al master: %-.128s"
- por "Erro conectando com o master: %-.128s"
- rus "ïÛÉÂËÁ ÓÏÅÄÉÎÅÎÉÑ Ó ÇÏÌÏ×ÎÙÍ ÓÅÒ×ÅÒÏÍ: %-.128s"
- spa "Error de coneccion a master: %-.128s"
- swe "Fick fel vid anslutning till master: %-.128s"
-ER_QUERY_ON_MASTER
- nla "Fout bij uitvoeren query op master: %-.128s"
- eng "Error running query on master: %-.128s"
- ger "Beim Ausführen einer Abfrage auf dem Master trat ein Fehler auf: %-.128s"
- ita "Errore eseguendo una query sul master: %-.128s"
- por "Erro rodando consulta no master: %-.128s"
- rus "ïÛÉÂËÁ ×ÙÐÏÌÎÅÎÉÑ ÚÁÐÒÏÓÁ ÎÁ ÇÏÌÏ×ÎÏÍ ÓÅÒ×ÅÒÅ: %-.128s"
- spa "Error executando el query en master: %-.128s"
- swe "Fick fel vid utförande av command på mastern: %-.128s"
-ER_ERROR_WHEN_EXECUTING_COMMAND
- nla "Fout tijdens uitvoeren van commando %s: %-.128s"
- eng "Error when executing command %s: %-.128s"
- est "Viga käsu %s täitmisel: %-.128s"
- ger "Fehler beim Ausführen des Befehls %s: %-.128s"
- ita "Errore durante l'esecuzione del comando %s: %-.128s"
- por "Erro quando executando comando %s: %-.128s"
- rus "ïÛÉÂËÁ ÐÒÉ ×ÙÐÏÌÎÅÎÉÉ ËÏÍÁÎÄÙ %s: %-.128s"
- serbian "Greška pri izvršavanju komande %s: %-.128s"
- spa "Error de %s: %-.128s"
- swe "Fick fel vid utförande av %s: %-.128s"
-ER_WRONG_USAGE
- nla "Foutief gebruik van %s en %s"
- eng "Incorrect usage of %s and %s"
- est "Vigane %s ja %s kasutus"
- ger "Falsche Verwendung von %s und %s"
- ita "Uso errato di %s e %s"
- por "Uso errado de %s e %s"
- rus "îÅ×ÅÒÎÏÅ ÉÓÐÏÌØÚÏ×ÁÎÉÅ %s É %s"
- serbian "Pogrešna upotreba %s i %s"
- spa "Equivocado uso de %s y %s"
- swe "Felaktig använding av %s and %s"
- ukr "Wrong usage of %s and %s"
-ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT 21000
- nla "De gebruikte SELECT commando's hebben een verschillend aantal kolommen"
- eng "The used SELECT statements have a different number of columns"
- est "Tulpade arv kasutatud SELECT lausetes ei kattu"
- ger "Die verwendeten SELECT-Befehle liefern unterschiedliche Anzahlen von Feldern zurück"
- ita "La SELECT utilizzata ha un numero di colonne differente"
- por "Os comandos SELECT usados têm diferente número de colunas"
- rus "éÓÐÏÌØÚÏ×ÁÎÎÙÅ ÏÐÅÒÁÔÏÒÙ ×ÙÂÏÒËÉ (SELECT) ÄÁÀÔ ÒÁÚÎÏÅ ËÏÌÉÞÅÓÔ×Ï ÓÔÏÌÂÃÏ×"
- serbian "Upotrebljene 'SELECT' komande adresiraju razlièit broj kolona"
- spa "El comando SELECT usado tiene diferente número de columnas"
- swe "SELECT-kommandona har olika antal kolumner"
-ER_CANT_UPDATE_WITH_READLOCK
- nla "Kan de query niet uitvoeren vanwege een conflicterende read lock"
- eng "Can't execute the query because you have a conflicting read lock"
- est "Ei suuda täita päringut konfliktse luku tõttu"
- ger "Augrund eines READ-LOCK-Konflikts kann die Abfrage nicht ausgeführt werden"
- ita "Impossibile eseguire la query perche' c'e' un conflitto con in lock di lettura"
- por "Não posso executar a consulta porque você tem um conflito de travamento de leitura"
- rus "îÅ×ÏÚÍÏÖÎÏ ÉÓÐÏÌÎÉÔØ ÚÁÐÒÏÓ, ÐÏÓËÏÌØËÕ Õ ×ÁÓ ÕÓÔÁÎÏ×ÌÅÎÙ ËÏÎÆÌÉËÔÕÀÝÉÅ ÂÌÏËÉÒÏ×ËÉ ÞÔÅÎÉÑ"
- serbian "Ne mogu da izvršim upit zbog toga što imate zakljuèavanja èitanja podataka u konfliktu"
- spa "No puedo ejecutar el query porque usted tiene conflicto de traba de lectura"
- swe "Kan inte utföra kommandot emedan du har ett READ-lås"
-ER_MIXING_NOT_ALLOWED
- nla "Het combineren van transactionele en niet-transactionele tabellen is uitgeschakeld."
- eng "Mixing of transactional and non-transactional tables is disabled"
- est "Transaktsioone toetavate ning mittetoetavate tabelite kooskasutamine ei ole lubatud"
- ger "Die gleichzeitige Verwendung von Tabellen mit und ohne Transaktionsunterstützung ist deaktiviert"
- ita "E' disabilitata la possibilita' di mischiare tabelle transazionali e non-transazionali"
- por "Mistura de tabelas transacional e não-transacional está desabilitada"
- rus "éÓÐÏÌØÚÏ×ÁÎÉÅ ÔÒÁÎÚÁËÃÉÏÎÎÙÈ ÔÁÂÌÉà ÎÁÒÑÄÕ Ó ÎÅÔÒÁÎÚÁËÃÉÏÎÎÙÍÉ ÚÁÐÒÅÝÅÎÏ"
- serbian "Mešanje tabela koje podržavaju transakcije i onih koje ne podržavaju transakcije je iskljuèeno"
- spa "Mezla de transancional y no-transancional tablas está deshabilitada"
- swe "Blandning av transaktionella och icke-transaktionella tabeller är inaktiverat"
-ER_DUP_ARGUMENT
- nla "Optie '%s' tweemaal gebruikt in opdracht"
- eng "Option '%s' used twice in statement"
- est "Määrangut '%s' on lauses kasutatud topelt"
- ger "Option '%s' wird im Befehl zweimal verwendet"
- ita "L'opzione '%s' e' stata usata due volte nel comando"
- por "Opção '%s' usada duas vezes no comando"
- rus "ïÐÃÉÑ '%s' Ä×ÁÖÄÙ ÉÓÐÏÌØÚÏ×ÁÎÁ × ×ÙÒÁÖÅÎÉÉ"
- spa "Opción '%s' usada dos veces en el comando"
- swe "Option '%s' användes två gånger"
-ER_USER_LIMIT_REACHED 42000
- nla "Gebruiker '%-.64s' heeft het maximale gebruik van de '%s' faciliteit overschreden (huidige waarde: %ld)"
- eng "User '%-.64s' has exceeded the '%s' resource (current value: %ld)"
- ger "Benutzer '%-.64s' hat die Ressourcenbeschränkung '%s' überschritten (aktueller Wert: %ld)"
- ita "L'utente '%-.64s' ha ecceduto la risorsa '%s' (valore corrente: %ld)"
- por "Usuário '%-.64s' tem excedido o '%s' recurso (atual valor: %ld)"
- rus "ðÏÌØÚÏ×ÁÔÅÌØ '%-.64s' ÐÒÅ×ÙÓÉÌ ÉÓÐÏÌØÚÏ×ÁÎÉÅ ÒÅÓÕÒÓÁ '%s' (ÔÅËÕÝÅÅ ÚÎÁÞÅÎÉÅ: %ld)"
- spa "Usuario '%-.64s' ha excedido el recurso '%s' (actual valor: %ld)"
- swe "Användare '%-.64s' har överskridit '%s' (nuvarande värde: %ld)"
-ER_SPECIFIC_ACCESS_DENIED_ERROR 42000
- nla "Toegang geweigerd. U moet het %-.128s privilege hebben voor deze operatie"
- eng "Access denied; you need the %-.128s privilege for this operation"
- ger "Kein Zugriff. Hierfür wird die Berechtigung %-.128s benötigt"
- ita "Accesso non consentito. Serve il privilegio %-.128s per questa operazione"
- por "Acesso negado. Você precisa o privilégio %-.128s para essa operação"
- rus "÷ ÄÏÓÔÕÐÅ ÏÔËÁÚÁÎÏ. ÷ÁÍ ÎÕÖÎÙ ÐÒÉ×ÉÌÅÇÉÉ %-.128s ÄÌÑ ÜÔÏÊ ÏÐÅÒÁÃÉÉ"
- spa "Acceso negado. Usted necesita el privilegio %-.128s para esta operación"
- swe "Du har inte privlegiet '%-.128s' som behövs för denna operation"
- ukr "Access denied. You need the %-.128s privilege for this operation"
-ER_LOCAL_VARIABLE
- nla "Variabele '%-.64s' is SESSION en kan niet worden gebruikt met SET GLOBAL"
- eng "Variable '%-.64s' is a SESSION variable and can't be used with SET GLOBAL"
- ger "Variable '%-.64s' ist eine lokale Variable und kann nicht mit SET GLOBAL verändert werden"
- ita "La variabile '%-.64s' e' una variabile locale ( SESSION ) e non puo' essere cambiata usando SET GLOBAL"
- por "Variável '%-.64s' é uma SESSION variável e não pode ser usada com SET GLOBAL"
- rus "ðÅÒÅÍÅÎÎÁÑ '%-.64s' Ñ×ÌÑÅÔÓÑ ÐÏÔÏËÏ×ÏÊ (SESSION) ÐÅÒÅÍÅÎÎÏÊ É ÎÅ ÍÏÖÅÔ ÂÙÔØ ÉÚÍÅÎÅÎÁ Ó ÐÏÍÏÝØÀ SET GLOBAL"
- spa "Variable '%-.64s' es una SESSION variable y no puede ser usada con SET GLOBAL"
- swe "Variabel '%-.64s' är en SESSION variabel och kan inte ändrad med SET GLOBAL"
-ER_GLOBAL_VARIABLE
- nla "Variabele '%-.64s' is GLOBAL en dient te worden gewijzigd met SET GLOBAL"
- eng "Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL"
- ger "Variable '%-.64s' ist eine globale Variable und muss mit SET GLOBAL verändert werden"
- ita "La variabile '%-.64s' e' una variabile globale ( GLOBAL ) e deve essere cambiata usando SET GLOBAL"
- por "Variável '%-.64s' é uma GLOBAL variável e deve ser configurada com SET GLOBAL"
- rus "ðÅÒÅÍÅÎÎÁÑ '%-.64s' Ñ×ÌÑÅÔÓÑ ÇÌÏÂÁÌØÎÏÊ (GLOBAL) ÐÅÒÅÍÅÎÎÏÊ, É ÅÅ ÓÌÅÄÕÅÔ ÉÚÍÅÎÑÔØ Ó ÐÏÍÏÝØÀ SET GLOBAL"
- spa "Variable '%-.64s' es una GLOBAL variable y no puede ser configurada con SET GLOBAL"
- swe "Variabel '%-.64s' är en GLOBAL variabel och bör sättas med SET GLOBAL"
-ER_NO_DEFAULT 42000
- nla "Variabele '%-.64s' heeft geen standaard waarde"
- eng "Variable '%-.64s' doesn't have a default value"
- ger "Variable '%-.64s' hat keinen Vorgabewert"
- ita "La variabile '%-.64s' non ha un valore di default"
- por "Variável '%-.64s' não tem um valor padrão"
- rus "ðÅÒÅÍÅÎÎÁÑ '%-.64s' ÎÅ ÉÍÅÅÔ ÚÎÁÞÅÎÉÑ ÐÏ ÕÍÏÌÞÁÎÉÀ"
- spa "Variable '%-.64s' no tiene un valor patrón"
- swe "Variabel '%-.64s' har inte ett DEFAULT-värde"
-ER_WRONG_VALUE_FOR_VAR 42000
- nla "Variabele '%-.64s' kan niet worden gewijzigd naar de waarde '%-.200s'"
- eng "Variable '%-.64s' can't be set to the value of '%-.200s'"
- ger "Variable '%-.64s' kann nicht auf '%-.200s' gesetzt werden"
- ita "Alla variabile '%-.64s' non puo' essere assegato il valore '%-.200s'"
- por "Variável '%-.64s' não pode ser configurada para o valor de '%-.200s'"
- rus "ðÅÒÅÍÅÎÎÁÑ '%-.64s' ÎÅ ÍÏÖÅÔ ÂÙÔØ ÕÓÔÁÎÏ×ÌÅÎÁ × ÚÎÁÞÅÎÉÅ '%-.200s'"
- spa "Variable '%-.64s' no puede ser configurada para el valor de '%-.200s'"
- swe "Variabel '%-.64s' kan inte sättas till '%-.200s'"
-ER_WRONG_TYPE_FOR_VAR 42000
- nla "Foutief argumenttype voor variabele '%-.64s'"
- eng "Incorrect argument type to variable '%-.64s'"
- ger "Falscher Argumenttyp für Variable '%-.64s'"
- ita "Tipo di valore errato per la variabile '%-.64s'"
- por "Tipo errado de argumento para variável '%-.64s'"
- rus "îÅ×ÅÒÎÙÊ ÔÉÐ ÁÒÇÕÍÅÎÔÁ ÄÌÑ ÐÅÒÅÍÅÎÎÏÊ '%-.64s'"
- spa "Tipo de argumento equivocado para variable '%-.64s'"
- swe "Fel typ av argument till variabel '%-.64s'"
-ER_VAR_CANT_BE_READ
- nla "Variabele '%-.64s' kan alleen worden gewijzigd, niet gelezen"
- eng "Variable '%-.64s' can only be set, not read"
- ger "Variable '%-.64s' kann nur verändert, nicht gelesen werden"
- ita "Alla variabile '%-.64s' e' di sola scrittura quindi puo' essere solo assegnato un valore, non letto"
- por "Variável '%-.64s' somente pode ser configurada, não lida"
- rus "ðÅÒÅÍÅÎÎÁÑ '%-.64s' ÍÏÖÅÔ ÂÙÔØ ÔÏÌØËÏ ÕÓÔÁÎÏ×ÌÅÎÁ, ÎÏ ÎÅ ÓÞÉÔÁÎÁ"
- spa "Variable '%-.64s' solamente puede ser configurada, no leída"
- swe "Variabeln '%-.64s' kan endast sättas, inte läsas"
-ER_CANT_USE_OPTION_HERE 42000
- nla "Foutieve toepassing/plaatsing van '%s'"
- eng "Incorrect usage/placement of '%s'"
- ger "Falsche Verwendung oder Platzierung von '%s'"
- ita "Uso/posizione di '%s' sbagliato"
- por "Errado uso/colocação de '%s'"
- rus "îÅ×ÅÒÎÏÅ ÉÓÐÏÌØÚÏ×ÁÎÉÅ ÉÌÉ × ÎÅ×ÅÒÎÏÍ ÍÅÓÔÅ ÕËÁÚÁÎ '%s'"
- spa "Equivocado uso/colocación de '%s'"
- swe "Fel använding/placering av '%s'"
-ER_NOT_SUPPORTED_YET 42000
- nla "Deze versie van MySQL ondersteunt nog geen '%s'"
- eng "This version of MySQL doesn't yet support '%s'"
- ger "Diese MySQL-Version unterstützt '%s' nicht"
- ita "Questa versione di MySQL non supporta ancora '%s'"
- por "Esta versão de MySQL não suporta ainda '%s'"
- rus "üÔÁ ×ÅÒÓÉÑ MySQL ÐÏËÁ ÅÝÅ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ '%s'"
- spa "Esta versión de MySQL no soporta todavia '%s'"
- swe "Denna version av MySQL kan ännu inte utföra '%s'"
-ER_MASTER_FATAL_ERROR_READING_BINLOG
- nla "Kreeg fatale fout %d: '%-.128s' van master tijdens lezen van data uit binaire log"
- eng "Got fatal error %d from master when reading data from binary log: '%-.128s'"
- ger "Schwerer Fehler %d: '%-.128s vom Master beim Lesen des binären Logs"
- ita "Errore fatale %d: '%-.128s' dal master leggendo i dati dal log binario"
- por "Obteve fatal erro %d: '%-.128s' do master quando lendo dados do binary log"
- rus "ðÏÌÕÞÅÎÁ ÎÅÉÓÐÒÁ×ÉÍÁÑ ÏÛÉÂËÁ %d: '%-.128s' ÏÔ ÇÏÌÏ×ÎÏÇÏ ÓÅÒ×ÅÒÁ × ÐÒÏÃÅÓÓÅ ×ÙÂÏÒËÉ ÄÁÎÎÙÈ ÉÚ Ä×ÏÉÞÎÏÇÏ ÖÕÒÎÁÌÁ"
- spa "Recibió fatal error %d: '%-.128s' del master cuando leyendo datos del binary log"
- swe "Fick fatalt fel %d: '%-.128s' från master vid läsning av binärloggen"
-ER_SLAVE_IGNORED_TABLE
- eng "Slave SQL thread ignored the query because of replicate-*-table rules"
- ger "Slave-SQL-Thread hat die Abfrage aufgrund von replicate-*-table-Regeln ignoriert"
- nla "Slave SQL thread negeerde de query vanwege replicate-*-table opties"
- por "Slave SQL thread ignorado a consulta devido às normas de replicação-*-tabela"
- spa "Slave SQL thread ignorado el query debido a las reglas de replicación-*-tabla"
- swe "Slav SQL tråden ignorerade frågan pga en replicate-*-table regel"
-ER_INCORRECT_GLOBAL_LOCAL_VAR
- eng "Variable '%-.192s' is a %s variable"
- serbian "Promenljiva '%-.192s' je %s promenljiva"
- ger "Variable '%-.192s' ist eine %s-Variable"
- nla "Variabele '%-.192s' is geen %s variabele"
- spa "Variable '%-.192s' es una %s variable"
- swe "Variabel '%-.192s' är av typ %s"
-ER_WRONG_FK_DEF 42000
- eng "Incorrect foreign key definition for '%-.192s': %s"
- ger "Falsche Fremdschlüssel-Definition für '%-.192s': %s"
- nla "Incorrecte foreign key definitie voor '%-.192s': %s"
- por "Definição errada da chave estrangeira para '%-.192s': %s"
- spa "Equivocada definición de llave extranjera para '%-.192s': %s"
- swe "Felaktig FOREIGN KEY-definition för '%-.192s': %s"
-ER_KEY_REF_DO_NOT_MATCH_TABLE_REF
- eng "Key reference and table reference don't match"
- ger "Schlüssel- und Tabellenverweis passen nicht zusammen"
- nla "Sleutel- en tabelreferentie komen niet overeen"
- por "Referência da chave e referência da tabela não coincidem"
- spa "Referencia de llave y referencia de tabla no coinciden"
- swe "Nyckelreferensen och tabellreferensen stämmer inte överens"
-ER_OPERAND_COLUMNS 21000
- eng "Operand should contain %d column(s)"
- ger "Operand sollte %d Spalte(n) enthalten"
- nla "Operand behoort %d kolommen te bevatten"
- rus "ïÐÅÒÁÎÄ ÄÏÌÖÅÎ ÓÏÄÅÒÖÁÔØ %d ËÏÌÏÎÏË"
- spa "Operando debe tener %d columna(s)"
- ukr "ïÐÅÒÁÎÄ ÍÁ¤ ÓËÌÁÄÁÔÉÓÑ Ú %d ÓÔÏ×Âæ×"
-ER_SUBQUERY_NO_1_ROW 21000
- eng "Subquery returns more than 1 row"
- ger "Unterabfrage lieferte mehr als einen Datensatz zurück"
- nla "Subquery retourneert meer dan 1 rij"
- por "Subconsulta retorna mais que 1 registro"
- rus "ðÏÄÚÁÐÒÏÓ ×ÏÚ×ÒÁÝÁÅÔ ÂÏÌÅÅ ÏÄÎÏÊ ÚÁÐÉÓÉ"
- spa "Subconsulta retorna mas que 1 línea"
- swe "Subquery returnerade mer än 1 rad"
- ukr "ð¦ÄÚÁÐÉÔ ÐÏ×ÅÒÔÁ¤ ¦ÌØÛ ÎiÖ 1 ÚÁÐÉÓ"
-ER_UNKNOWN_STMT_HANDLER
- dan "Unknown prepared statement handler (%.*s) given to %s"
- eng "Unknown prepared statement handler (%.*s) given to %s"
- ger "Unbekannter Prepared-Statement-Handler (%.*s) für %s angegeben"
- nla "Onebekende prepared statement handler (%.*s) voor %s aangegeven"
- por "Desconhecido manipulador de declaração preparado (%.*s) determinado para %s"
- spa "Desconocido preparado comando handler (%.*s) dado para %s"
- swe "Okänd PREPARED STATEMENT id (%.*s) var given till %s"
- ukr "Unknown prepared statement handler (%.*s) given to %s"
-ER_CORRUPT_HELP_DB
- eng "Help database is corrupt or does not exist"
- ger "Die Hilfe-Datenbank ist beschädigt oder existiert nicht"
- nla "Help database is beschadigd of bestaat niet"
- por "Banco de dado de ajuda corrupto ou não existente"
- spa "Base de datos Help está corrupto o no existe"
- swe "Hjälpdatabasen finns inte eller är skadad"
-ER_CYCLIC_REFERENCE
- eng "Cyclic reference on subqueries"
- ger "Zyklischer Verweis in Unterabfragen"
- nla "Cyclische verwijzing in subqueries"
- por "Referência cíclica em subconsultas"
- rus "ãÉËÌÉÞÅÓËÁÑ ÓÓÙÌËÁ ÎÁ ÐÏÄÚÁÐÒÏÓ"
- spa "Cíclica referencia en subconsultas"
- swe "Cyklisk referens i subqueries"
- ukr "ãÉË̦ÞÎÅ ÐÏÓÉÌÁÎÎÑ ÎÁ ЦÄÚÁÐÉÔ"
-ER_AUTO_CONVERT
- eng "Converting column '%s' from %s to %s"
- ger "Feld '%s' wird von %s nach %s umgewandelt"
- nla "Veld '%s' wordt van %s naar %s geconverteerd"
- por "Convertendo coluna '%s' de %s para %s"
- rus "ðÒÅÏÂÒÁÚÏ×ÁÎÉÅ ÐÏÌÑ '%s' ÉÚ %s × %s"
- spa "Convirtiendo columna '%s' de %s para %s"
- swe "Konvertar kolumn '%s' från %s till %s"
- ukr "ðÅÒÅÔ×ÏÒÅÎÎÑ ÓÔÏ×ÂÃÁ '%s' Ú %s Õ %s"
-ER_ILLEGAL_REFERENCE 42S22
- eng "Reference '%-.64s' not supported (%s)"
- ger "Verweis '%-.64s' wird nicht unterstützt (%s)"
- nla "Verwijzing '%-.64s' niet ondersteund (%s)"
- por "Referência '%-.64s' não suportada (%s)"
- rus "óÓÙÌËÁ '%-.64s' ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔÓÑ (%s)"
- spa "Referencia '%-.64s' no soportada (%s)"
- swe "Referens '%-.64s' stöds inte (%s)"
- ukr "ðÏÓÉÌÁÎÎÑ '%-.64s' ÎÅ ÐiÄÔÒÉÍÕÅÔÓÑ (%s)"
-ER_DERIVED_MUST_HAVE_ALIAS 42000
- eng "Every derived table must have its own alias"
- ger "Für jede abgeleitete Tabelle muss ein eigener Alias angegeben werden"
- nla "Voor elke afgeleide tabel moet een unieke alias worden gebruikt"
- por "Cada tabela derivada deve ter seu próprio alias"
- spa "Cada tabla derivada debe tener su propio alias"
- swe "Varje 'derived table' måste ha sitt eget alias"
-ER_SELECT_REDUCED 01000
- eng "Select %u was reduced during optimization"
- ger "Select %u wurde während der Optimierung reduziert"
- nla "Select %u werd geredureerd tijdens optimtalisatie"
- por "Select %u foi reduzido durante otimização"
- rus "Select %u ÂÙÌ ÕÐÒÁÚÄÎÅÎ × ÐÒÏÃÅÓÓÅ ÏÐÔÉÍÉÚÁÃÉÉ"
- spa "Select %u fué reducido durante optimización"
- swe "Select %u reducerades vid optimiering"
- ukr "Select %u was ÓËÁÓÏ×ÁÎÏ ÐÒÉ ÏÐÔÉÍiÚÁÃii"
-ER_TABLENAME_NOT_ALLOWED_HERE 42000
- eng "Table '%-.192s' from one of the SELECTs cannot be used in %-.32s"
- ger "Tabelle '%-.192s', die in einem der SELECT-Befehle verwendet wurde, kann nicht in %-.32s verwendet werden"
- nla "Tabel '%-.192s' uit een van de SELECTS kan niet in %-.32s gebruikt worden"
- por "Tabela '%-.192s' de um dos SELECTs não pode ser usada em %-.32s"
- spa "Tabla '%-.192s' de uno de los SELECT no puede ser usada en %-.32s"
- swe "Tabell '%-.192s' från en SELECT kan inte användas i %-.32s"
-ER_NOT_SUPPORTED_AUTH_MODE 08004
- eng "Client does not support authentication protocol requested by server; consider upgrading MySQL client"
- ger "Client unterstützt das vom Server erwartete Authentifizierungsprotokoll nicht. Bitte aktualisieren Sie Ihren MySQL-Client"
- nla "Client ondersteunt het door de server verwachtte authenticatieprotocol niet. Overweeg een nieuwere MySQL client te gebruiken"
- por "Cliente não suporta o protocolo de autenticação exigido pelo servidor; considere a atualização do cliente MySQL"
- spa "Cliente no soporta protocolo de autenticación solicitado por el servidor; considere actualizar el cliente MySQL"
- swe "Klienten stöder inte autentiseringsprotokollet som begärts av servern; överväg uppgradering av klientprogrammet."
-ER_SPATIAL_CANT_HAVE_NULL 42000
- eng "All parts of a SPATIAL index must be NOT NULL"
- ger "Alle Teile eines SPATIAL-Index müssen als NOT NULL deklariert sein"
- nla "Alle delete van een SPATIAL index dienen als NOT NULL gedeclareerd te worden"
- por "Todas as partes de uma SPATIAL index devem ser NOT NULL"
- spa "Todas las partes de una SPATIAL index deben ser NOT NULL"
- swe "Alla delar av en SPATIAL index måste vara NOT NULL"
-ER_COLLATION_CHARSET_MISMATCH 42000
- eng "COLLATION '%s' is not valid for CHARACTER SET '%s'"
- ger "COLLATION '%s' ist für CHARACTER SET '%s' ungültig"
- nla "COLLATION '%s' is niet geldig voor CHARACTER SET '%s'"
- por "COLLATION '%s' não é válida para CHARACTER SET '%s'"
- spa "COLLATION '%s' no es válido para CHARACTER SET '%s'"
- swe "COLLATION '%s' är inte tillåtet för CHARACTER SET '%s'"
-ER_SLAVE_WAS_RUNNING
- eng "Slave is already running"
- ger "Slave läuft bereits"
- nla "Slave is reeds actief"
- por "O slave já está rodando"
- spa "Slave ya está funcionando"
- swe "Slaven har redan startat"
-ER_SLAVE_WAS_NOT_RUNNING
- eng "Slave already has been stopped"
- ger "Slave wurde bereits angehalten"
- nla "Slave is reeds gestopt"
- por "O slave já está parado"
- spa "Slave ya fué parado"
- swe "Slaven har redan stoppat"
-ER_TOO_BIG_FOR_UNCOMPRESS
- eng "Uncompressed data size too large; the maximum size is %d (probably, length of uncompressed data was corrupted)"
- ger "Unkomprimierte Daten sind zu groß. Die maximale Größe beträgt %d (wahrscheinlich wurde die Länge der unkomprimierten Daten beschädigt)"
- nla "Ongecomprimeerder data is te groot; de maximum lengte is %d (waarschijnlijk, de lengte van de gecomprimeerde data was beschadigd)"
- por "Tamanho muito grande dos dados des comprimidos. O máximo tamanho é %d. (provavelmente, o comprimento dos dados descomprimidos está corrupto)"
- spa "Tamaño demasiado grande para datos descomprimidos. El máximo tamaño es %d. (probablemente, extensión de datos descomprimidos fué corrompida)"
-ER_ZLIB_Z_MEM_ERROR
- eng "ZLIB: Not enough memory"
- ger "ZLIB: Nicht genug Speicher"
- nla "ZLIB: Onvoldoende geheugen"
- por "ZLIB: Não suficiente memória disponível"
- spa "Z_MEM_ERROR: No suficiente memoria para zlib"
-ER_ZLIB_Z_BUF_ERROR
- eng "ZLIB: Not enough room in the output buffer (probably, length of uncompressed data was corrupted)"
- ger "ZLIB: Im Ausgabepuffer ist nicht genug Platz vorhanden (wahrscheinlich wurde die Länge der unkomprimierten Daten beschädigt)"
- nla "ZLIB: Onvoldoende ruimte in uitgaande buffer (waarschijnlijk, de lengte van de ongecomprimeerde data was beschadigd)"
- por "ZLIB: Não suficiente espaço no buffer emissor (provavelmente, o comprimento dos dados descomprimidos está corrupto)"
- spa "Z_BUF_ERROR: No suficiente espacio en el búfer de salida para zlib (probablemente, extensión de datos descomprimidos fué corrompida)"
-ER_ZLIB_Z_DATA_ERROR
- eng "ZLIB: Input data corrupted"
- ger "ZLIB: Eingabedaten beschädigt"
- nla "ZLIB: Invoer data beschadigd"
- por "ZLIB: Dados de entrada está corrupto"
- spa "ZLIB: Dato de entrada fué corrompido para zlib"
-ER_CUT_VALUE_GROUP_CONCAT
- eng "%d line(s) were cut by GROUP_CONCAT()"
- ger "%d Zeile(n) durch GROUP_CONCAT() abgeschnitten"
- nla "%d regel(s) door GROUP_CONCAT() ingekort"
- por "%d linha(s) foram cortada(s) por GROUP_CONCAT()"
- spa "%d línea(s) fue(fueron) cortadas por group_concat()"
- swe "%d rad(er) kapades av group_concat()"
- ukr "%d line(s) was(were) cut by group_concat()"
-ER_WARN_TOO_FEW_RECORDS 01000
- eng "Row %lu doesn't contain data for all columns"
- ger "Zeile %lu enthält nicht für alle Felder Daten"
- nla "Rij %lu bevat niet de data voor alle kolommen"
- por "Conta de registro é menor que a conta de coluna na linha %lu"
- spa "Línea %lu no contiene datos para todas las columnas"
-ER_WARN_TOO_MANY_RECORDS 01000
- eng "Row %lu was truncated; it contained more data than there were input columns"
- ger "Zeile %lu gekürzt, die Zeile enthielt mehr Daten, als es Eingabefelder gibt"
- nla "Regel %lu ingekort, bevatte meer data dan invoer kolommen"
- por "Conta de registro é maior que a conta de coluna na linha %lu"
- spa "Línea %lu fué truncada; La misma contine mas datos que las que existen en las columnas de entrada"
-ER_WARN_NULL_TO_NOTNULL 22004
- eng "Column set to default value; NULL supplied to NOT NULL column '%s' at row %lu"
- ger "Feld auf Vorgabewert gesetzt, da NULL für NOT-NULL-Feld '%s' in Zeile %lu angegeben"
- por "Dado truncado, NULL fornecido para NOT NULL coluna '%s' na linha %lu"
- spa "Datos truncado, NULL suministrado para NOT NULL columna '%s' en la línea %lu"
-ER_WARN_DATA_OUT_OF_RANGE 22003
- eng "Out of range value for column '%s' at row %lu"
-WARN_DATA_TRUNCATED 01000
- eng "Data truncated for column '%s' at row %lu"
- ger "Daten abgeschnitten für Feld '%s' in Zeile %lu"
- por "Dado truncado para coluna '%s' na linha %lu"
- spa "Datos truncados para columna '%s' en la línea %lu"
-ER_WARN_USING_OTHER_HANDLER
- eng "Using storage engine %s for table '%s'"
- ger "Für Tabelle '%s' wird Speicher-Engine %s benutzt"
- por "Usando engine de armazenamento %s para tabela '%s'"
- spa "Usando motor de almacenamiento %s para tabla '%s'"
- swe "Använder handler %s för tabell '%s'"
-ER_CANT_AGGREGATE_2COLLATIONS
- eng "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'"
- ger "Unerlaubte Mischung von Sortierreihenfolgen (%s, %s) und (%s, %s) für Operation '%s'"
- por "Combinação ilegal de collations (%s,%s) e (%s,%s) para operação '%s'"
- spa "Ilegal mezcla de collations (%s,%s) y (%s,%s) para operación '%s'"
-ER_DROP_USER
- eng "Cannot drop one or more of the requested users"
- ger "Kann einen oder mehrere der angegebenen Benutzer nicht löschen"
-ER_REVOKE_GRANTS
- eng "Can't revoke all privileges for one or more of the requested users"
- ger "Kann nicht alle Berechtigungen widerrufen, die für einen oder mehrere Benutzer gewährt wurden"
- por "Não pode revocar todos os privilégios, grant para um ou mais dos usuários pedidos"
- spa "No puede revocar todos los privilegios, derecho para uno o mas de los usuarios solicitados"
-ER_CANT_AGGREGATE_3COLLATIONS
- eng "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'"
- ger "Unerlaubte Mischung von Sortierreihenfolgen (%s, %s), (%s, %s), (%s, %s) für Operation '%s'"
- por "Ilegal combinação de collations (%s,%s), (%s,%s), (%s,%s) para operação '%s'"
- spa "Ilegal mezcla de collations (%s,%s), (%s,%s), (%s,%s) para operación '%s'"
-ER_CANT_AGGREGATE_NCOLLATIONS
- eng "Illegal mix of collations for operation '%s'"
- ger "Unerlaubte Mischung von Sortierreihenfolgen für Operation '%s'"
- por "Ilegal combinação de collations para operação '%s'"
- spa "Ilegal mezcla de collations para operación '%s'"
-ER_VARIABLE_IS_NOT_STRUCT
- eng "Variable '%-.64s' is not a variable component (can't be used as XXXX.variable_name)"
- ger "Variable '%-.64s' ist keine Variablen-Komponente (kann nicht als XXXX.variablen_name verwendet werden)"
- por "Variável '%-.64s' não é uma variável componente (Não pode ser usada como XXXX.variável_nome)"
- spa "Variable '%-.64s' no es una variable componente (No puede ser usada como XXXX.variable_name)"
-ER_UNKNOWN_COLLATION
- eng "Unknown collation: '%-.64s'"
- ger "Unbekannte Sortierreihenfolge: '%-.64s'"
- por "Collation desconhecida: '%-.64s'"
- spa "Collation desconocida: '%-.64s'"
-ER_SLAVE_IGNORED_SSL_PARAMS
- eng "SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later if MySQL slave with SSL is started"
- ger "SSL-Parameter in CHANGE MASTER werden ignoriert, weil dieser MySQL-Slave ohne SSL-Unterstützung kompiliert wurde. Sie können aber später verwendet werden, wenn ein MySQL-Slave mit SSL gestartet wird"
- por "SSL parâmetros em CHANGE MASTER são ignorados porque este escravo MySQL foi compilado sem o SSL suporte. Os mesmos podem ser usados mais tarde quando o escravo MySQL com SSL seja iniciado."
- spa "Parametros SSL en CHANGE MASTER son ignorados porque este slave MySQL fue compilado sin soporte SSL; pueden ser usados despues cuando el slave MySQL con SSL sea inicializado"
-ER_SERVER_IS_IN_SECURE_AUTH_MODE
- eng "Server is running in --secure-auth mode, but '%s'@'%s' has a password in the old format; please change the password to the new format"
- ger "Server läuft im Modus --secure-auth, aber '%s'@'%s' hat ein Passwort im alten Format. Bitte Passwort ins neue Format ändern"
- por "Servidor está rodando em --secure-auth modo, porêm '%s'@'%s' tem senha no formato antigo; por favor troque a senha para o novo formato"
- rus "óÅÒ×ÅÒ ÚÁÐÕÝÅÎ × ÒÅÖÉÍÅ --secure-auth (ÂÅÚÏÐÁÓÎÏÊ Á×ÔÏÒÉÚÁÃÉÉ), ÎÏ ÄÌÑ ÐÏÌØÚÏ×ÁÔÅÌÑ '%s'@'%s' ÐÁÒÏÌØ ÓÏÈÒÁÎ£Î × ÓÔÁÒÏÍ ÆÏÒÍÁÔÅ; ÎÅÏÂÈÏÄÉÍÏ ÏÂÎÏ×ÉÔØ ÆÏÒÍÁÔ ÐÁÒÏÌÑ"
- spa "Servidor está rodando en modo --secure-auth, pero '%s'@'%s' tiene clave en el antiguo formato; por favor cambie la clave para el nuevo formato"
-ER_WARN_FIELD_RESOLVED
- eng "Field or reference '%-.192s%s%-.192s%s%-.192s' of SELECT #%d was resolved in SELECT #%d"
- ger "Feld oder Verweis '%-.192s%s%-.192s%s%-.192s' im SELECT-Befehl Nr. %d wurde im SELECT-Befehl Nr. %d aufgelöst"
- por "Campo ou referência '%-.192s%s%-.192s%s%-.192s' de SELECT #%d foi resolvido em SELECT #%d"
- rus "ðÏÌÅ ÉÌÉ ÓÓÙÌËÁ '%-.192s%s%-.192s%s%-.192s' ÉÚ SELECTÁ #%d ÂÙÌÁ ÎÁÊÄÅÎÁ × SELECTÅ #%d"
- spa "Campo o referencia '%-.192s%s%-.192s%s%-.192s' de SELECT #%d fue resolvido en SELECT #%d"
- ukr "óÔÏ×ÂÅÃØ ÁÂÏ ÐÏÓÉÌÁÎÎÑ '%-.192s%s%-.192s%s%-.192s' ¦Ú SELECTÕ #%d ÂÕÌÏ ÚÎÁÊÄÅÎÅ Õ SELECT¦ #%d"
-ER_BAD_SLAVE_UNTIL_COND
- eng "Incorrect parameter or combination of parameters for START SLAVE UNTIL"
- ger "Falscher Parameter oder falsche Kombination von Parametern für START SLAVE UNTIL"
- por "Parâmetro ou combinação de parâmetros errado para START SLAVE UNTIL"
- spa "Parametro equivocado o combinación de parametros para START SLAVE UNTIL"
-ER_MISSING_SKIP_SLAVE
- eng "It is recommended to use --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL; otherwise, you will get problems if you get an unexpected slave's mysqld restart"
- ger "Es wird empfohlen, mit --skip-slave-start zu starten, wenn mit START SLAVE UNTIL eine Schritt-für-Schritt-Replikation ausgeführt wird. Ansonsten gibt es Probleme, wenn ein Slave-Server unerwartet neu startet"
- por "É recomendado para rodar com --skip-slave-start quando fazendo replicação passo-por-passo com START SLAVE UNTIL, de outra forma você não está seguro em caso de inesperada reinicialição do mysqld escravo"
- spa "Es recomendado rodar con --skip-slave-start cuando haciendo replicación step-by-step con START SLAVE UNTIL, a menos que usted no esté seguro en caso de inesperada reinicialización del mysqld slave"
-ER_UNTIL_COND_IGNORED
- eng "SQL thread is not to be started so UNTIL options are ignored"
- ger "SQL-Thread soll nicht gestartet werden. Daher werden UNTIL-Optionen ignoriert"
- por "Thread SQL não pode ser inicializado tal que opções UNTIL são ignoradas"
- spa "SQL thread no es inicializado tal que opciones UNTIL son ignoradas"
-ER_WRONG_NAME_FOR_INDEX 42000
- eng "Incorrect index name '%-.100s'"
- ger "Falscher Indexname '%-.100s'"
- por "Incorreto nome de índice '%-.100s'"
- spa "Nombre de índice incorrecto '%-.100s'"
- swe "Felaktigt index namn '%-.100s'"
-ER_WRONG_NAME_FOR_CATALOG 42000
- eng "Incorrect catalog name '%-.100s'"
- ger "Falscher Katalogname '%-.100s'"
- por "Incorreto nome de catálogo '%-.100s'"
- spa "Nombre de catalog incorrecto '%-.100s'"
- swe "Felaktigt katalog namn '%-.100s'"
-ER_WARN_QC_RESIZE
- eng "Query cache failed to set size %lu; new query cache size is %lu"
- ger "Änderung der Query-Cache-Größe auf %lu fehlgeschlagen; neue Query-Cache-Größe ist %lu"
- por "Falha em Query cache para configurar tamanho %lu, novo tamanho de query cache é %lu"
- rus "ëÅÛ ÚÁÐÒÏÓÏ× ÎÅ ÍÏÖÅÔ ÕÓÔÁÎÏ×ÉÔØ ÒÁÚÍÅÒ %lu, ÎÏ×ÙÊ ÒÁÚÍÅÒ ËÅÛÁ ÚÐÒÏÓÏ× - %lu"
- spa "Query cache fallada para configurar tamaño %lu, nuevo tamaño de query cache es %lu"
- swe "Storleken av "Query cache" kunde inte sättas till %lu, ny storlek är %lu"
- ukr "ëÅÛ ÚÁÐÉÔ¦× ÎÅÓÐÒÏÍÏÖÅÎ ×ÓÔÁÎÏ×ÉÔÉ ÒÏÚÍ¦Ò %lu, ÎÏ×ÉÊ ÒÏÚÍ¦Ò ËÅÛÁ ÚÁÐÉÔ¦× - %lu"
-ER_BAD_FT_COLUMN
- eng "Column '%-.192s' cannot be part of FULLTEXT index"
- ger "Feld '%-.192s' kann nicht Teil eines FULLTEXT-Index sein"
- por "Coluna '%-.192s' não pode ser parte de índice FULLTEXT"
- spa "Columna '%-.192s' no puede ser parte de FULLTEXT index"
- swe "Kolumn '%-.192s' kan inte vara del av ett FULLTEXT index"
-ER_UNKNOWN_KEY_CACHE
- eng "Unknown key cache '%-.100s'"
- ger "Unbekannter Schlüssel-Cache '%-.100s'"
- por "Key cache desconhecida '%-.100s'"
- spa "Desconocida key cache '%-.100s'"
- swe "Okänd nyckel cache '%-.100s'"
-ER_WARN_HOSTNAME_WONT_WORK
- eng "MySQL is started in --skip-name-resolve mode; you must restart it without this switch for this grant to work"
- ger "MySQL wurde mit --skip-name-resolve gestartet. Diese Option darf nicht verwendet werden, damit diese Rechtevergabe möglich ist"
- por "MySQL foi inicializado em modo --skip-name-resolve. Você necesita reincializá-lo sem esta opção para este grant funcionar"
- spa "MySQL esta inicializado en modo --skip-name-resolve. Usted necesita reinicializarlo sin esta opción para este derecho funcionar"
-ER_UNKNOWN_STORAGE_ENGINE 42000
- eng "Unknown table engine '%s'"
- ger "Unbekannte Speicher-Engine '%s'"
- por "Motor de tabela desconhecido '%s'"
- spa "Desconocido motor de tabla '%s'"
-# When using this error code, use ER(ER_WARN_DEPRECATED_SYNTAX_WITH_VER)
-# for the message string. See, for example, code in mysql_priv.h.
-ER_WARN_DEPRECATED_SYNTAX
- eng "'%s' is deprecated and will be removed in a future release. Please use %s instead"
- ger "'%s' ist veraltet. Bitte benutzen Sie '%s'"
- por "'%s' é desatualizado. Use '%s' em seu lugar"
- spa "'%s' está desaprobado, use '%s' en su lugar"
-ER_NON_UPDATABLE_TABLE
- eng "The target table %-.100s of the %s is not updatable"
- ger "Die Zieltabelle %-.100s von %s ist nicht aktualisierbar"
- por "A tabela destino %-.100s do %s não é atualizável"
- rus "ôÁÂÌÉÃÁ %-.100s × %s ÎÅ ÍÏÖÅÔ ÉÚÍÅÎÑÔÓÑ"
- spa "La tabla destino %-.100s del %s no es actualizable"
- swe "Tabell %-.100s använd med '%s' är inte uppdateringsbar"
- ukr "ôÁÂÌÉÃÑ %-.100s Õ %s ÎÅ ÍÏÖÅ ÏÎÏ×ÌÀ×ÁÔÉÓØ"
-ER_FEATURE_DISABLED
- eng "The '%s' feature is disabled; you need MySQL built with '%s' to have it working"
- ger "Das Feature '%s' ist ausgeschaltet, Sie müssen MySQL mit '%s' übersetzen, damit es verfügbar ist"
- por "O recurso '%s' foi desativado; você necessita MySQL construído com '%s' para ter isto funcionando"
- spa "El recurso '%s' fue deshabilitado; usted necesita construir MySQL con '%s' para tener eso funcionando"
- swe "'%s' är inte aktiverad; För att aktivera detta måste du bygga om MySQL med '%s' definerad"
-ER_OPTION_PREVENTS_STATEMENT
- eng "The MySQL server is running with the %s option so it cannot execute this statement"
- ger "Der MySQL-Server läuft mit der Option %s und kann diese Anweisung deswegen nicht ausführen"
- por "O servidor MySQL está rodando com a opção %s razão pela qual não pode executar esse commando"
- spa "El servidor MySQL está rodando con la opción %s tal que no puede ejecutar este comando"
- swe "MySQL är startad med %s. Pga av detta kan du inte använda detta kommando"
-ER_DUPLICATED_VALUE_IN_TYPE
- eng "Column '%-.100s' has duplicated value '%-.64s' in %s"
- ger "Feld '%-.100s' hat doppelten Wert '%-.64s' in %s"
- por "Coluna '%-.100s' tem valor duplicado '%-.64s' em %s"
- spa "Columna '%-.100s' tiene valor doblado '%-.64s' en %s"
-ER_TRUNCATED_WRONG_VALUE 22007
- eng "Truncated incorrect %-.32s value: '%-.128s'"
- ger "Falscher %-.32s-Wert gekürzt: '%-.128s'"
- por "Truncado errado %-.32s valor: '%-.128s'"
- spa "Equivocado truncado %-.32s valor: '%-.128s'"
-ER_TOO_MUCH_AUTO_TIMESTAMP_COLS
- eng "Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause"
- ger "Fehlerhafte Tabellendefinition. Es kann nur eine einzige TIMESTAMP-Spalte mit CURRENT_TIMESTAMP als DEFAULT oder in einer ON-UPDATE-Klausel geben"
- por "Incorreta definição de tabela; Pode ter somente uma coluna TIMESTAMP com CURRENT_TIMESTAMP em DEFAULT ou ON UPDATE cláusula"
- spa "Incorrecta definición de tabla; Solamente debe haber una columna TIMESTAMP con CURRENT_TIMESTAMP en DEFAULT o ON UPDATE cláusula"
-ER_INVALID_ON_UPDATE
- eng "Invalid ON UPDATE clause for '%-.192s' column"
- ger "Ungültige ON-UPDATE-Klausel für Spalte '%-.192s'"
- por "Inválida cláusula ON UPDATE para campo '%-.192s'"
- spa "Inválido ON UPDATE cláusula para campo '%-.192s'"
-ER_UNSUPPORTED_PS
- eng "This command is not supported in the prepared statement protocol yet"
- ger "Dieser Befehl wird im Protokoll für vorbereitete Anweisungen noch nicht unterstützt"
-ER_GET_ERRMSG
- dan "Modtog fejl %d '%-.100s' fra %s"
- eng "Got error %d '%-.100s' from %s"
- ger "Fehler %d '%-.100s' von %s"
- nor "Mottok feil %d '%-.100s' fa %s"
- norwegian-ny "Mottok feil %d '%-.100s' fra %s"
-ER_GET_TEMPORARY_ERRMSG
- dan "Modtog temporary fejl %d '%-.100s' fra %s"
- eng "Got temporary error %d '%-.100s' from %s"
- ger "Temporärer Fehler %d '%-.100s' von %s"
- nor "Mottok temporary feil %d '%-.100s' fra %s"
- norwegian-ny "Mottok temporary feil %d '%-.100s' fra %s"
-ER_UNKNOWN_TIME_ZONE
- eng "Unknown or incorrect time zone: '%-.64s'"
- ger "Unbekannte oder falsche Zeitzone: '%-.64s'"
-ER_WARN_INVALID_TIMESTAMP
- eng "Invalid TIMESTAMP value in column '%s' at row %lu"
- ger "Ungültiger TIMESTAMP-Wert in Feld '%s', Zeile %lu"
-ER_INVALID_CHARACTER_STRING
- eng "Invalid %s character string: '%.64s'"
- ger "Ungültiger %s-Zeichen-String: '%.64s'"
-ER_WARN_ALLOWED_PACKET_OVERFLOWED
- eng "Result of %s() was larger than max_allowed_packet (%ld) - truncated"
- ger "Ergebnis von %s() war größer als max_allowed_packet (%ld) Bytes und wurde deshalb gekürzt"
-ER_CONFLICTING_DECLARATIONS
- eng "Conflicting declarations: '%s%s' and '%s%s'"
- ger "Widersprüchliche Deklarationen: '%s%s' und '%s%s'"
-ER_SP_NO_RECURSIVE_CREATE 2F003
- eng "Can't create a %s from within another stored routine"
- ger "Kann kein %s innerhalb einer anderen gespeicherten Routine erzeugen"
-ER_SP_ALREADY_EXISTS 42000
- eng "%s %s already exists"
- ger "%s %s existiert bereits"
-ER_SP_DOES_NOT_EXIST 42000
- eng "%s %s does not exist"
- ger "%s %s existiert nicht"
-ER_SP_DROP_FAILED
- eng "Failed to DROP %s %s"
- ger "DROP %s %s ist fehlgeschlagen"
-ER_SP_STORE_FAILED
- eng "Failed to CREATE %s %s"
- ger "CREATE %s %s ist fehlgeschlagen"
-ER_SP_LILABEL_MISMATCH 42000
- eng "%s with no matching label: %s"
- ger "%s ohne passende Marke: %s"
-ER_SP_LABEL_REDEFINE 42000
- eng "Redefining label %s"
- ger "Neudefinition der Marke %s"
-ER_SP_LABEL_MISMATCH 42000
- eng "End-label %s without match"
- ger "Ende-Marke %s ohne zugehörigen Anfang"
-ER_SP_UNINIT_VAR 01000
- eng "Referring to uninitialized variable %s"
- ger "Zugriff auf nichtinitialisierte Variable %s"
-ER_SP_BADSELECT 0A000
- eng "PROCEDURE %s can't return a result set in the given context"
- ger "PROCEDURE %s kann im gegebenen Kontext keine Ergebnismenge zurückgeben"
-ER_SP_BADRETURN 42000
- eng "RETURN is only allowed in a FUNCTION"
- ger "RETURN ist nur innerhalb einer FUNCTION erlaubt"
-ER_SP_BADSTATEMENT 0A000
- eng "%s is not allowed in stored procedures"
- ger "%s ist in gespeicherten Prozeduren nicht erlaubt"
-ER_UPDATE_LOG_DEPRECATED_IGNORED 42000
- eng "The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored. This option will be removed in MySQL 5.6."
- ger "Das Update-Log ist veraltet und wurde durch das Binär-Log ersetzt. SET SQL_LOG_UPDATE wird ignoriert. Diese Option wird in MySQL 5.6 entfernt."
-ER_UPDATE_LOG_DEPRECATED_TRANSLATED 42000
- eng "The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN. This option will be removed in MySQL 5.6."
- ger "Das Update-Log ist veraltet und wurde durch das Binär-Log ersetzt. SET SQL_LOG_UPDATE wurde in SET SQL_LOG_BIN übersetzt. Diese Option wird in MySQL 5.6 entfernt."
-ER_QUERY_INTERRUPTED 70100
- eng "Query execution was interrupted"
- ger "Ausführung der Abfrage wurde unterbrochen"
-ER_SP_WRONG_NO_OF_ARGS 42000
- eng "Incorrect number of arguments for %s %s; expected %u, got %u"
- ger "Falsche Anzahl von Argumenten für %s %s; erwarte %u, erhalte %u"
-ER_SP_COND_MISMATCH 42000
- eng "Undefined CONDITION: %s"
- ger "Undefinierte CONDITION: %s"
-ER_SP_NORETURN 42000
- eng "No RETURN found in FUNCTION %s"
- ger "Kein RETURN in FUNCTION %s gefunden"
-ER_SP_NORETURNEND 2F005
- eng "FUNCTION %s ended without RETURN"
- ger "FUNCTION %s endete ohne RETURN"
-ER_SP_BAD_CURSOR_QUERY 42000
- eng "Cursor statement must be a SELECT"
- ger "Cursor-Anweisung muss ein SELECT sein"
-ER_SP_BAD_CURSOR_SELECT 42000
- eng "Cursor SELECT must not have INTO"
- ger "Cursor-SELECT darf kein INTO haben"
-ER_SP_CURSOR_MISMATCH 42000
- eng "Undefined CURSOR: %s"
- ger "Undefinierter CURSOR: %s"
-ER_SP_CURSOR_ALREADY_OPEN 24000
- eng "Cursor is already open"
- ger "Cursor ist schon geöffnet"
-ER_SP_CURSOR_NOT_OPEN 24000
- eng "Cursor is not open"
- ger "Cursor ist nicht geöffnet"
-ER_SP_UNDECLARED_VAR 42000
- eng "Undeclared variable: %s"
- ger "Nicht deklarierte Variable: %s"
-ER_SP_WRONG_NO_OF_FETCH_ARGS
- eng "Incorrect number of FETCH variables"
- ger "Falsche Anzahl von FETCH-Variablen"
-ER_SP_FETCH_NO_DATA 02000
- eng "No data - zero rows fetched, selected, or processed"
- ger "Keine Daten - null Zeilen geholt (fetch), ausgewählt oder verarbeitet"
-ER_SP_DUP_PARAM 42000
- eng "Duplicate parameter: %s"
- ger "Doppelter Parameter: %s"
-ER_SP_DUP_VAR 42000
- eng "Duplicate variable: %s"
- ger "Doppelte Variable: %s"
-ER_SP_DUP_COND 42000
- eng "Duplicate condition: %s"
- ger "Doppelte Bedingung: %s"
-ER_SP_DUP_CURS 42000
- eng "Duplicate cursor: %s"
- ger "Doppelter Cursor: %s"
-ER_SP_CANT_ALTER
- eng "Failed to ALTER %s %s"
- ger "ALTER %s %s fehlgeschlagen"
-ER_SP_SUBSELECT_NYI 0A000
- eng "Subquery value not supported"
- ger "Subquery-Wert wird nicht unterstützt"
-ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG 0A000
- eng "%s is not allowed in stored function or trigger"
- ger "%s ist in gespeicherten Funktionen und in Triggern nicht erlaubt"
-ER_SP_VARCOND_AFTER_CURSHNDLR 42000
- eng "Variable or condition declaration after cursor or handler declaration"
- ger "Deklaration einer Variablen oder einer Bedingung nach der Deklaration eines Cursors oder eines Handlers"
-ER_SP_CURSOR_AFTER_HANDLER 42000
- eng "Cursor declaration after handler declaration"
- ger "Deklaration eines Cursors nach der Deklaration eines Handlers"
-ER_SP_CASE_NOT_FOUND 20000
- eng "Case not found for CASE statement"
- ger "Fall für CASE-Anweisung nicht gefunden"
-ER_FPARSER_TOO_BIG_FILE
- eng "Configuration file '%-.192s' is too big"
- ger "Konfigurationsdatei '%-.192s' ist zu groß"
- rus "óÌÉÛËÏÍ ÂÏÌØÛÏÊ ËÏÎÆÉÇÕÒÁÃÉÏÎÎÙÊ ÆÁÊÌ '%-.192s'"
- ukr "úÁÎÁÄÔÏ ×ÅÌÉËÉÊ ËÏÎƦÇÕÒÁæÊÎÉÊ ÆÁÊÌ '%-.192s'"
-ER_FPARSER_BAD_HEADER
- eng "Malformed file type header in file '%-.192s'"
- ger "Nicht wohlgeformter Dateityp-Header in Datei '%-.192s'"
- rus "îÅ×ÅÒÎÙÊ ÚÁÇÏÌÏ×ÏË ÔÉÐÁ ÆÁÊÌÁ '%-.192s'"
- ukr "îÅצÒÎÉÊ ÚÁÇÏÌÏ×ÏË ÔÉÐÕ Õ ÆÁÊ̦ '%-.192s'"
-ER_FPARSER_EOF_IN_COMMENT
- eng "Unexpected end of file while parsing comment '%-.200s'"
- ger "Unerwartetes Dateiende beim Parsen des Kommentars '%-.200s'"
- rus "îÅÏÖÉÄÁÎÎÙÊ ËÏÎÅà ÆÁÊÌÁ × ËÏÍÅÎÔÁÒÉÉ '%-.200s'"
- ukr "îÅÓÐÏĦ×ÁÎÎÉÊ Ë¦ÎÅÃØ ÆÁÊÌÕ Õ ËÏÍÅÎÔÁÒ¦ '%-.200s'"
-ER_FPARSER_ERROR_IN_PARAMETER
- eng "Error while parsing parameter '%-.192s' (line: '%-.192s')"
- ger "Fehler beim Parsen des Parameters '%-.192s' (Zeile: '%-.192s')"
- rus "ïÛÉÂËÁ ÐÒÉ ÒÁÓÐÏÚÎÁ×ÁÎÉÉ ÐÁÒÁÍÅÔÒÁ '%-.192s' (ÓÔÒÏËÁ: '%-.192s')"
- ukr "ðÏÍÉÌËÁ × ÒÏÓЦÚÎÁ×ÁÎΦ ÐÁÒÁÍÅÔÒÕ '%-.192s' (ÒÑÄÏË: '%-.192s')"
-ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER
- eng "Unexpected end of file while skipping unknown parameter '%-.192s'"
- ger "Unerwartetes Dateiende beim Überspringen des unbekannten Parameters '%-.192s'"
- rus "îÅÏÖÉÄÁÎÎÙÊ ËÏÎÅà ÆÁÊÌÁ ÐÒÉ ÐÒÏÐÕÓËÅ ÎÅÉÚ×ÅÓÔÎÏÇÏ ÐÁÒÁÍÅÔÒÁ '%-.192s'"
- ukr "îÅÓÐÏĦ×ÁÎÎÉÊ Ë¦ÎÅÃØ ÆÁÊÌÕ Õ ÓÐÒϦ ÐÒÏÍÉÎÕÔÉ ÎÅצÄÏÍÉÊ ÐÁÒÁÍÅÔÒ '%-.192s'"
-ER_VIEW_NO_EXPLAIN
- eng "EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
- ger "EXPLAIN/SHOW kann nicht verlangt werden. Rechte für zugrunde liegende Tabelle fehlen"
- rus "EXPLAIN/SHOW ÎÅ ÍÏÖÅÔ ÂÙÔØ ×ÙÐÏÌÎÅÎÎÏ; ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÎÁ ÔÁËÂÌÉÃÙ ÚÁÐÒÏÓÁ"
- ukr "EXPLAIN/SHOW ÎÅ ÍÏÖÅ ÂÕÔÉ ×¦ËÏÎÁÎÏ; ÎÅÍÁ¤ ÐÒÁ× ÎÁ ÔÉÂÌÉæ ÚÁÐÉÔÕ"
-ER_FRM_UNKNOWN_TYPE
- eng "File '%-.192s' has unknown type '%-.64s' in its header"
- ger "Datei '%-.192s' hat unbekannten Typ '%-.64s' im Header"
- rus "æÁÊÌ '%-.192s' ÓÏÄÅÒÖÉÔ ÎÅÉÚ×ÅÓÔÎÙÊ ÔÉÐ '%-.64s' × ÚÁÇÏÌÏ×ËÅ"
- ukr "æÁÊÌ '%-.192s' ÍÁ¤ ÎÅצÄÏÍÉÊ ÔÉÐ '%-.64s' Õ ÚÁÇÏÌÏ×ËÕ"
-ER_WRONG_OBJECT
- eng "'%-.192s.%-.192s' is not %s"
- ger "'%-.192s.%-.192s' ist nicht %s"
- rus "'%-.192s.%-.192s' - ÎÅ %s"
- ukr "'%-.192s.%-.192s' ÎÅ ¤ %s"
-ER_NONUPDATEABLE_COLUMN
- eng "Column '%-.192s' is not updatable"
- ger "Feld '%-.192s' ist nicht aktualisierbar"
- rus "óÔÏÌÂÅà '%-.192s' ÎÅ ÏÂÎÏ×ÌÑÅÍÙÊ"
- ukr "óÔÏ×ÂÅÃØ '%-.192s' ÎÅ ÍÏÖÅ ÂÕÔÉ ÚÍÉÎÅÎÉÊ"
-ER_VIEW_SELECT_DERIVED
- eng "View's SELECT contains a subquery in the FROM clause"
- ger "SELECT der View enthält eine Subquery in der FROM-Klausel"
- rus "View SELECT ÓÏÄÅÒÖÉÔ ÐÏÄÚÁÐÒÏÓ × ËÏÎÓÔÒÕËÃÉÉ FROM"
- ukr "View SELECT ÍÁ¤ ЦÄÚÁÐÉÔ Õ ËÏÎÓÔÒÕËæ§ FROM"
-ER_VIEW_SELECT_CLAUSE
- eng "View's SELECT contains a '%s' clause"
- ger "SELECT der View enthält eine '%s'-Klausel"
- rus "View SELECT ÓÏÄÅÒÖÉÔ ËÏÎÓÔÒÕËÃÉÀ '%s'"
- ukr "View SELECT ÍÁ¤ ËÏÎÓÔÒÕËæÀ '%s'"
-ER_VIEW_SELECT_VARIABLE
- eng "View's SELECT contains a variable or parameter"
- ger "SELECT der View enthält eine Variable oder einen Parameter"
- rus "View SELECT ÓÏÄÅÒÖÉÔ ÐÅÒÅÍÅÎÎÕÀ ÉÌÉ ÐÁÒÁÍÅÔÒ"
- ukr "View SELECT ÍÁ¤ ÚÍÉÎÎÕ ÁÂÏ ÐÁÒÁÍÅÔÅÒ"
-ER_VIEW_SELECT_TMPTABLE
- eng "View's SELECT refers to a temporary table '%-.192s'"
- ger "SELECT der View verweist auf eine temporäre Tabelle '%-.192s'"
- rus "View SELECT ÓÏÄÅÒÖÉÔ ÓÓÙÌËÕ ÎÁ ×ÒÅÍÅÎÎÕÀ ÔÁÂÌÉÃÕ '%-.192s'"
- ukr "View SELECT ×ÉËÏÒÉÓÔÏ×Õ¤ ÔÉÍÞÁÓÏ×Õ ÔÁÂÌÉÃÀ '%-.192s'"
-ER_VIEW_WRONG_LIST
- eng "View's SELECT and view's field list have different column counts"
- ger "SELECT- und Feldliste der Views haben unterschiedliche Anzahlen von Spalten"
- rus "View SELECT É ÓÐÉÓÏË ÐÏÌÅÊ view ÉÍÅÀÔ ÒÁÚÎÏÅ ËÏÌÉÞÅÓÔ×Ï ÓÔÏÌÂÃÏ×"
- ukr "View SELECT ¦ ÐÅÒÅÌ¦Ë ÓÔÏ×ÂÃ¦× view ÍÁÀÔØ Ò¦ÚÎÕ Ë¦ÌØ˦ÓÔØ ÓËÏ×Âæ×"
-ER_WARN_VIEW_MERGE
- eng "View merge algorithm can't be used here for now (assumed undefined algorithm)"
- ger "View-Merge-Algorithmus kann hier momentan nicht verwendet werden (undefinierter Algorithmus wird angenommen)"
- rus "áÌÇÏÒÉÔÍ ÓÌÉÑÎÉÑ view ÎÅ ÍÏÖÅÔ ÂÙÔØ ÉÓÐÏÌØÚÏ×ÁÎ ÓÅÊÞÁÓ (ÁÌÇÏÒÉÔÍ ÂÕÄÅÔ ÎÅÏÐÅÒÅÄÅÌÅÎÎÙÍ)"
- ukr "áÌÇÏÒÉÔÍ ÚÌÉ×ÁÎÎÑ view ÎÅ ÍÏÖÅ ÂÕÔÉ ×ÉËÏÒÉÓÔÁÎÉÊ ÚÁÒÁÚ (ÁÌÇÏÒÉÔÍ ÂÕÄÅ ÎÅ×ÉÚÎÁÞÅÎÉÊ)"
-ER_WARN_VIEW_WITHOUT_KEY
- eng "View being updated does not have complete key of underlying table in it"
- ger "Die aktualisierte View enthält nicht den vollständigen Schlüssel der zugrunde liegenden Tabelle"
- rus "ïÂÎÏ×ÌÑÅÍÙÊ view ÎÅ ÓÏÄÅÒÖÉÔ ËÌÀÞÁ ÉÓÐÏÌØÚÏ×ÁÎÎÙÈ(ÏÊ) × ÎÅÍ ÔÁÂÌÉÃ(Ù)"
- ukr "View, ÝÏ ÏÎÏ×ÌÀÅÔØÓÑ, ΊͦÓÔÉÔØ ÐÏ×ÎÏÇÏ ËÌÀÞÁ ÔÁÂÌÉæ(Ø), ÝÏ ×ÉËÏÒ¦ÓÔÁÎÁ × ÎØÀÏÍÕ"
-ER_VIEW_INVALID
- eng "View '%-.192s.%-.192s' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them"
-ER_SP_NO_DROP_SP
- eng "Can't drop or alter a %s from within another stored routine"
- ger "Kann eine %s nicht von innerhalb einer anderen gespeicherten Routine löschen oder ändern"
-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"
-ER_TRG_DOES_NOT_EXIST
- eng "Trigger does not exist"
- ger "Trigger existiert nicht"
-ER_TRG_ON_VIEW_OR_TEMP_TABLE
- eng "Trigger's '%-.192s' is view or temporary table"
- ger "'%-.192s' des Triggers ist View oder temporäre Tabelle"
-ER_TRG_CANT_CHANGE_ROW
- eng "Updating of %s row is not allowed in %strigger"
- ger "Aktualisieren einer %s-Zeile ist in einem %s-Trigger nicht erlaubt"
-ER_TRG_NO_SUCH_ROW_IN_TRG
- eng "There is no %s row in %s trigger"
- ger "Es gibt keine %s-Zeile im %s-Trigger"
-ER_NO_DEFAULT_FOR_FIELD
- eng "Field '%-.192s' doesn't have a default value"
- ger "Feld '%-.192s' hat keinen Vorgabewert"
-ER_DIVISION_BY_ZERO 22012
- eng "Division by 0"
- ger "Division durch 0"
-ER_TRUNCATED_WRONG_VALUE_FOR_FIELD 22007
- eng "Incorrect %-.32s value: '%-.128s' for column '%.192s' at row %lu"
- ger "Falscher %-.32s-Wert: '%-.128s' für Feld '%.192s' in Zeile %lu"
-ER_ILLEGAL_VALUE_FOR_TYPE 22007
- eng "Illegal %s '%-.192s' value found during parsing"
- ger "Nicht zulässiger %s-Wert '%-.192s' beim Parsen gefunden"
-ER_VIEW_NONUPD_CHECK
- eng "CHECK OPTION on non-updatable view '%-.192s.%-.192s'"
- ger "CHECK OPTION auf nicht-aktualisierbarem View '%-.192s.%-.192s'"
- rus "CHECK OPTION ÄÌÑ ÎÅÏÂÎÏ×ÌÑÅÍÏÇÏ VIEW '%-.192s.%-.192s'"
- ukr "CHECK OPTION ÄÌÑ VIEW '%-.192s.%-.192s' ÝÏ ÎÅ ÍÏÖÅ ÂÕÔÉ ÏÎÏ×ÌÅÎÎÉÍ"
-ER_VIEW_CHECK_FAILED
- eng "CHECK OPTION failed '%-.192s.%-.192s'"
- ger "CHECK OPTION fehlgeschlagen: '%-.192s.%-.192s'"
- rus "ÐÒÏ×ÅÒËÁ CHECK OPTION ÄÌÑ VIEW '%-.192s.%-.192s' ÐÒÏ×ÁÌÉÌÁÓØ"
- ukr "ðÅÒÅצÒËÁ CHECK OPTION ÄÌÑ VIEW '%-.192s.%-.192s' ÎÅ ÐÒÏÊÛÌÁ"
-ER_PROCACCESS_DENIED_ERROR 42000
- eng "%-.16s command denied to user '%-.48s'@'%-.64s' for routine '%-.192s'"
- ger "Befehl %-.16s nicht zulässig für Benutzer '%-.48s'@'%-.64s' in Routine '%-.192s'"
-ER_RELAY_LOG_FAIL
- eng "Failed purging old relay logs: %s"
- ger "Bereinigen alter Relais-Logs fehlgeschlagen: %s"
-ER_PASSWD_LENGTH
- eng "Password hash should be a %d-digit hexadecimal number"
- ger "Passwort-Hash sollte eine Hexdaezimalzahl mit %d Stellen sein"
-ER_UNKNOWN_TARGET_BINLOG
- eng "Target log not found in binlog index"
- ger "Ziel-Log im Binlog-Index nicht gefunden"
-ER_IO_ERR_LOG_INDEX_READ
- eng "I/O error reading log index file"
- ger "Fehler beim Lesen der Log-Index-Datei"
-ER_BINLOG_PURGE_PROHIBITED
- eng "Server configuration does not permit binlog purge"
- ger "Server-Konfiguration erlaubt keine Binlog-Bereinigung"
-ER_FSEEK_FAIL
- eng "Failed on fseek()"
- ger "fseek() fehlgeschlagen"
-ER_BINLOG_PURGE_FATAL_ERR
- eng "Fatal error during log purge"
- ger "Schwerwiegender Fehler bei der Log-Bereinigung"
-ER_LOG_IN_USE
- eng "A purgeable log is in use, will not purge"
- ger "Ein zu bereinigendes Log wird gerade benutzt, daher keine Bereinigung"
-ER_LOG_PURGE_UNKNOWN_ERR
- eng "Unknown error during log purge"
- ger "Unbekannter Fehler bei Log-Bereinigung"
-ER_RELAY_LOG_INIT
- eng "Failed initializing relay log position: %s"
- ger "Initialisierung der Relais-Log-Position fehlgeschlagen: %s"
-ER_NO_BINARY_LOGGING
- eng "You are not using binary logging"
- ger "Sie verwenden keine Binärlogs"
-ER_RESERVED_SYNTAX
- eng "The '%-.64s' syntax is reserved for purposes internal to the MySQL server"
- ger "Die Schreibweise '%-.64s' ist für interne Zwecke des MySQL-Servers reserviert"
-ER_WSAS_FAILED
- eng "WSAStartup Failed"
- ger "WSAStartup fehlgeschlagen"
-ER_DIFF_GROUPS_PROC
- eng "Can't handle procedures with different groups yet"
- ger "Kann Prozeduren mit unterschiedlichen Gruppen noch nicht verarbeiten"
-ER_NO_GROUP_FOR_PROC
- eng "Select must have a group with this procedure"
- ger "SELECT muss bei dieser Prozedur ein GROUP BY haben"
-ER_ORDER_WITH_PROC
- eng "Can't use ORDER clause with this procedure"
- ger "Kann bei dieser Prozedur keine ORDER-BY-Klausel verwenden"
-ER_LOGGING_PROHIBIT_CHANGING_OF
- eng "Binary logging and replication forbid changing the global server %s"
- ger "Binärlogs und Replikation verhindern Wechsel des globalen Servers %s"
-ER_NO_FILE_MAPPING
- eng "Can't map file: %-.200s, errno: %d"
- ger "Kann Datei nicht abbilden: %-.200s, Fehler: %d"
-ER_WRONG_MAGIC
- eng "Wrong magic in %-.64s"
- ger "Falsche magische Zahlen in %-.64s"
-ER_PS_MANY_PARAM
- eng "Prepared statement contains too many placeholders"
- ger "Vorbereitete Anweisung enthält zu viele Platzhalter"
-ER_KEY_PART_0
- eng "Key part '%-.192s' length cannot be 0"
- ger "Länge des Schlüsselteils '%-.192s' kann nicht 0 sein"
-ER_VIEW_CHECKSUM
- eng "View text checksum failed"
- ger "View-Text-Prüfsumme fehlgeschlagen"
- rus "ðÒÏ×ÅÒËÁ ËÏÎÔÒÏÌØÎÏÊ ÓÕÍÍÙ ÔÅËÓÔÁ VIEW ÐÒÏ×ÁÌÉÌÁÓØ"
- ukr "ðÅÒÅצÒËÁ ËÏÎÔÒÏÌØÎϧ ÓÕÍÉ ÔÅËÓÔÕ VIEW ÎÅ ÐÒÏÊÛÌÁ"
-ER_VIEW_MULTIUPDATE
- eng "Can not modify more than one base table through a join view '%-.192s.%-.192s'"
- ger "Kann nicht mehr als eine Basistabelle über Join-View '%-.192s.%-.192s' ändern"
- rus "îÅÌØÚÑ ÉÚÍÅÎÉÔØ ÂÏÌØÛÅ ÞÅÍ ÏÄÎÕ ÂÁÚÏ×ÕÀ ÔÁÂÌÉÃÕ ÉÓÐÏÌØÚÕÑ ÍÎÏÇÏÔÁÂÌÉÞÎÙÊ VIEW '%-.192s.%-.192s'"
- ukr "îÅÍÏÖÌÉ×Ï ÏÎÏ×ÉÔÉ Â¦ÌØÛ ÎÉÖ ÏÄÎÕ ÂÁÚÏ×Õ ÔÁÂÌÉÃÀ ×ÙËÏÒÉÓÔÏ×ÕÀÞÉ VIEW '%-.192s.%-.192s', ÝÏ Í¦ÓÔ¦ÔØ ÄÅ˦ÌØËÁ ÔÁÂÌÉÃØ"
-ER_VIEW_NO_INSERT_FIELD_LIST
- eng "Can not insert into join view '%-.192s.%-.192s' without fields list"
- ger "Kann nicht ohne Feldliste in Join-View '%-.192s.%-.192s' einfügen"
- rus "îÅÌØÚÑ ×ÓÔÁ×ÌÑÔØ ÚÁÐÉÓÉ × ÍÎÏÇÏÔÁÂÌÉÞÎÙÊ VIEW '%-.192s.%-.192s' ÂÅÚ ÓÐÉÓËÁ ÐÏÌÅÊ"
- ukr "îÅÍÏÖÌÉ×Ï ÕÓÔÁ×ÉÔÉ ÒÑÄËÉ Õ VIEW '%-.192s.%-.192s', ÝÏ Í¦ÓÔÉÔØ ÄÅ˦ÌØËÁ ÔÁÂÌÉÃØ, ÂÅÚ ÓÐÉÓËÕ ÓÔÏ×Âæ×"
-ER_VIEW_DELETE_MERGE_VIEW
- eng "Can not delete from join view '%-.192s.%-.192s'"
- ger "Kann nicht aus Join-View '%-.192s.%-.192s' löschen"
- rus "îÅÌØÚÑ ÕÄÁÌÑÔØ ÉÚ ÍÎÏÇÏÔÁÂÌÉÞÎÏÇÏ VIEW '%-.192s.%-.192s'"
- ukr "îÅÍÏÖÌÉ×Ï ×ÉÄÁÌÉÔÉ ÒÑÄËÉ Õ VIEW '%-.192s.%-.192s', ÝÏ Í¦ÓÔÉÔØ ÄÅ˦ÌØËÁ ÔÁÂÌÉÃØ"
-ER_CANNOT_USER
- eng "Operation %s failed for %.256s"
- ger "Operation %s schlug fehl für %.256s"
- norwegian-ny "Operation %s failed for '%.256s'"
-ER_XAER_NOTA XAE04
- eng "XAER_NOTA: Unknown XID"
- ger "XAER_NOTA: Unbekannte XID"
-ER_XAER_INVAL XAE05
- eng "XAER_INVAL: Invalid arguments (or unsupported command)"
- ger "XAER_INVAL: Ungültige Argumente (oder nicht unterstützter Befehl)"
-ER_XAER_RMFAIL XAE07
- eng "XAER_RMFAIL: The command cannot be executed when global transaction is in the %.64s state"
- ger "XAER_RMFAIL: DEr Befehl kann nicht ausgeführt werden, wenn die globale Transaktion im Zustand %.64s ist"
- rus "XAER_RMFAIL: ÜÔÕ ËÏÍÁÎÄÕ ÎÅÌØÚÑ ×ÙÐÏÌÎÑÔØ ËÏÇÄÁ ÇÌÏÂÁÌØÎÁÑ ÔÒÁÎÚÁËÃÉÑ ÎÁÈÏÄÉÔÓÑ × ÓÏÓÔÏÑÎÉÉ '%.64s'"
-ER_XAER_OUTSIDE XAE09
- eng "XAER_OUTSIDE: Some work is done outside global transaction"
- ger "XAER_OUTSIDE: Einige Arbeiten werden außerhalb der globalen Transaktion verrichtet"
-ER_XAER_RMERR XAE03
- eng "XAER_RMERR: Fatal error occurred in the transaction branch - check your data for consistency"
- ger "XAER_RMERR: Schwerwiegender Fehler im Transaktionszweig - prüfen Sie Ihre Daten auf Konsistenz"
-ER_XA_RBROLLBACK XA100
- eng "XA_RBROLLBACK: Transaction branch was rolled back"
- ger "XA_RBROLLBACK: Transaktionszweig wurde zurückgerollt"
-ER_NONEXISTING_PROC_GRANT 42000
- eng "There is no such grant defined for user '%-.48s' on host '%-.64s' on routine '%-.192s'"
- ger "Es gibt diese Berechtigung für Benutzer '%-.48s' auf Host '%-.64s' für Routine '%-.192s' nicht"
-ER_PROC_AUTO_GRANT_FAIL
- eng "Failed to grant EXECUTE and ALTER ROUTINE privileges"
- ger "Gewährung von EXECUTE- und ALTER-ROUTINE-Rechten fehlgeschlagen"
-ER_PROC_AUTO_REVOKE_FAIL
- eng "Failed to revoke all privileges to dropped routine"
- ger "Rücknahme aller Rechte für die gelöschte Routine fehlgeschlagen"
-ER_DATA_TOO_LONG 22001
- eng "Data too long for column '%s' at row %lu"
- ger "Daten zu lang für Feld '%s' in Zeile %lu"
-ER_SP_BAD_SQLSTATE 42000
- eng "Bad SQLSTATE: '%s'"
- ger "Ungültiger SQLSTATE: '%s'"
-ER_STARTUP
- eng "%s: ready for connections.\nVersion: '%s' socket: '%s' port: %d %s"
- ger "%s: bereit für Verbindungen.\nVersion: '%s' Socket: '%s' Port: %d %s"
-ER_LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR
- eng "Can't load value from file with fixed size rows to variable"
- ger "Kann Wert aus Datei mit Zeilen fester Größe nicht in Variable laden"
-ER_CANT_CREATE_USER_WITH_GRANT 42000
- eng "You are not allowed to create a user with GRANT"
- ger "Sie dürfen keinen Benutzer mit GRANT anlegen"
-ER_WRONG_VALUE_FOR_TYPE
- eng "Incorrect %-.32s value: '%-.128s' for function %-.32s"
- ger "Falscher %-.32s-Wert: '%-.128s' für Funktion %-.32s"
-ER_TABLE_DEF_CHANGED
- eng "Table definition has changed, please retry transaction"
- ger "Tabellendefinition wurde geändert, bitte starten Sie die Transaktion neu"
-ER_SP_DUP_HANDLER 42000
- eng "Duplicate handler declared in the same block"
- ger "Doppelter Handler im selben Block deklariert"
-ER_SP_NOT_VAR_ARG 42000
- eng "OUT or INOUT argument %d for routine %s is not a variable or NEW pseudo-variable in BEFORE trigger"
- ger "OUT- oder INOUT-Argument %d für Routine %s ist keine Variable"
-ER_SP_NO_RETSET 0A000
- eng "Not allowed to return a result set from a %s"
- ger "Rückgabe einer Ergebnismenge aus einer %s ist nicht erlaubt"
-ER_CANT_CREATE_GEOMETRY_OBJECT 22003
- eng "Cannot get geometry object from data you send to the GEOMETRY field"
- ger "Kann kein Geometrieobjekt aus den Daten machen, die Sie dem GEOMETRY-Feld übergeben haben"
-ER_FAILED_ROUTINE_BREAK_BINLOG
- eng "A routine failed and has neither NO SQL nor READS SQL DATA in its declaration and binary logging is enabled; if non-transactional tables were updated, the binary log will miss their changes"
- ger "Eine Routine, die weder NO SQL noch READS SQL DATA in der Deklaration hat, schlug fehl und Binärlogging ist aktiv. Wenn Nicht-Transaktions-Tabellen aktualisiert wurden, enthält das Binärlog ihre Änderungen nicht"
-ER_BINLOG_UNSAFE_ROUTINE
- eng "This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)"
- ger "Diese Routine hat weder DETERMINISTIC, NO SQL noch READS SQL DATA in der Deklaration und Binärlogging ist aktiv (*vielleicht* sollten Sie die weniger sichere Variable log_bin_trust_routine_creators verwenden)"
-ER_BINLOG_CREATE_ROUTINE_NEED_SUPER
- eng "You do not have the SUPER privilege and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)"
- ger "Sie haben keine SUPER-Berechtigung und Binärlogging ist aktiv (*vielleicht* sollten Sie die weniger sichere Variable log_bin_trust_routine_creators verwenden)"
-ER_EXEC_STMT_WITH_OPEN_CURSOR
- eng "You can't execute a prepared statement which has an open cursor associated with it. Reset the statement to re-execute it."
- ger "Sie können keine vorbereitete Anweisung ausführen, die mit einem geöffneten Cursor verknüpft ist. Setzen Sie die Anweisung zurück, um sie neu auszuführen"
-ER_STMT_HAS_NO_OPEN_CURSOR
- eng "The statement (%lu) has no open cursor."
- ger "Die Anweisung (%lu) hat keinen geöffneten Cursor"
-ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG
- eng "Explicit or implicit commit is not allowed in stored function or trigger."
- ger "Explizites oder implizites Commit ist in gespeicherten Funktionen und in Triggern nicht erlaubt"
-ER_NO_DEFAULT_FOR_VIEW_FIELD
- eng "Field of view '%-.192s.%-.192s' underlying table doesn't have a default value"
- ger "Ein Feld der dem View '%-.192s.%-.192s' zugrundeliegenden Tabelle hat keinen Vorgabewert"
-ER_SP_NO_RECURSION
- eng "Recursive stored functions and triggers are not allowed."
- ger "Rekursive gespeicherte Routinen und Triggers sind nicht erlaubt"
-ER_TOO_BIG_SCALE 42000 S1009
- eng "Too big scale %u specified for '%-.192s'. Maximum is %lu."
- ger "Zu großer Skalierungsfaktor %u für '%-.192s' angegeben. Maximum ist %lu"
-ER_TOO_BIG_PRECISION 42000 S1009
- eng "Too big precision %u specified for '%-.192s'. Maximum is %lu."
- ger "Zu große Genauigkeit %u für '%-.192s' angegeben. Maximum ist %lu"
-ER_M_BIGGER_THAN_D 42000 S1009
- eng "For float(M,D), double(M,D) or decimal(M,D), M must be >= D (column '%-.192s')."
- ger "Für FLOAT(M,D), DOUBLE(M,D) oder DECIMAL(M,D) muss M >= D sein (Feld '%-.192s')"
-ER_WRONG_LOCK_OF_SYSTEM_TABLE
- eng "You can't combine write-locking of system tables with other tables or lock types"
- ger "Sie können Schreibsperren auf der Systemtabelle nicht mit anderen Tabellen kombinieren"
-ER_CONNECT_TO_FOREIGN_DATA_SOURCE
- eng "Unable to connect to foreign data source: %.64s"
- ger "Kann nicht mit Fremddatenquelle verbinden: %.64s"
-ER_QUERY_ON_FOREIGN_DATA_SOURCE
- eng "There was a problem processing the query on the foreign data source. Data source error: %-.64s"
- ger "Bei der Verarbeitung der Abfrage ist in der Fremddatenquelle ein Problem aufgetreten. Datenquellenfehlermeldung: %-.64s"
-ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST
- eng "The foreign data source you are trying to reference does not exist. Data source error: %-.64s"
- ger "Die Fremddatenquelle, auf die Sie zugreifen wollen, existiert nicht. Datenquellenfehlermeldung: %-.64s"
-ER_FOREIGN_DATA_STRING_INVALID_CANT_CREATE
- eng "Can't create federated table. The data source connection string '%-.64s' is not in the correct format"
- ger "Kann föderierte Tabelle nicht erzeugen. Der Datenquellen-Verbindungsstring '%-.64s' hat kein korrektes Format"
-ER_FOREIGN_DATA_STRING_INVALID
- eng "The data source connection string '%-.64s' is not in the correct format"
- ger "Der Datenquellen-Verbindungsstring '%-.64s' hat kein korrektes Format"
-ER_CANT_CREATE_FEDERATED_TABLE
- eng "Can't create federated table. Foreign data src error: %-.64s"
- ger "Kann föderierte Tabelle nicht erzeugen. Fremddatenquellenfehlermeldung: %-.64s"
-ER_TRG_IN_WRONG_SCHEMA
- eng "Trigger in wrong schema"
- ger "Trigger im falschen Schema"
-ER_STACK_OVERRUN_NEED_MORE
- eng "Thread stack overrun: %ld bytes used of a %ld byte stack, and %ld bytes needed. Use 'mysqld -O thread_stack=#' to specify a bigger stack."
- ger "Thread-Stack-Überlauf: %ld Bytes eines %ld-Byte-Stacks in Verwendung, und %ld Bytes benötigt. Verwenden Sie 'mysqld -O thread_stack=#', um einen größeren Stack anzugeben"
-ER_TOO_LONG_BODY 42000 S1009
- eng "Routine body for '%-.100s' is too long"
- ger "Routinen-Body für '%-.100s' ist zu lang"
-ER_WARN_CANT_DROP_DEFAULT_KEYCACHE
- eng "Cannot drop default keycache"
- ger "Der vorgabemäßige Schlüssel-Cache kann nicht gelöscht werden"
-ER_TOO_BIG_DISPLAYWIDTH 42000 S1009
- eng "Display width out of range for '%-.192s' (max = %lu)"
- ger "Anzeigebreite außerhalb des zulässigen Bereichs für '%-.192s' (Maximum: %lu)"
-ER_XAER_DUPID XAE08
- eng "XAER_DUPID: The XID already exists"
- ger "XAER_DUPID: Die XID existiert bereits"
-ER_DATETIME_FUNCTION_OVERFLOW 22008
- eng "Datetime function: %-.32s field overflow"
- ger "Datetime-Funktion: %-.32s Feldüberlauf"
-ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG
- eng "Can't update table '%-.192s' in stored function/trigger because it is already used by statement which invoked this stored function/trigger."
- ger "Kann Tabelle '%-.192s' in gespeicherter Funktion oder Trigger nicht aktualisieren, weil sie bereits von der Anweisung verwendet wird, die diese gespeicherte Funktion oder den Trigger aufrief"
-ER_VIEW_PREVENT_UPDATE
- eng "The definition of table '%-.192s' prevents operation %.192s on table '%-.192s'."
- ger "Die Definition der Tabelle '%-.192s' verhindert die Operation %.192s auf Tabelle '%-.192s'"
-ER_PS_NO_RECURSION
- eng "The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner"
- ger "Die vorbereitete Anweisung enthält einen Aufruf einer gespeicherten Routine, die auf eben dieselbe Anweisung verweist. Es ist nicht erlaubt, eine vorbereitete Anweisung in solch rekursiver Weise auszuführen"
-ER_SP_CANT_SET_AUTOCOMMIT
- eng "Not allowed to set autocommit from a stored function or trigger"
- ger "Es ist nicht erlaubt, innerhalb einer gespeicherten Funktion oder eines Triggers AUTOCOMMIT zu setzen"
-ER_MALFORMED_DEFINER
- eng "Definer is not fully qualified"
- ger "Definierer des View ist nicht vollständig spezifiziert"
-ER_VIEW_FRM_NO_USER
- eng "View '%-.192s'.'%-.192s' has no definer information (old table format). Current user is used as definer. Please recreate the view!"
- ger "View '%-.192s'.'%-.192s' hat keine Definierer-Information (altes Tabellenformat). Der aktuelle Benutzer wird als Definierer verwendet. Bitte erstellen Sie den View neu"
-ER_VIEW_OTHER_USER
- eng "You need the SUPER privilege for creation view with '%-.192s'@'%-.192s' definer"
- ger "Sie brauchen die SUPER-Berechtigung, um einen View mit dem Definierer '%-.192s'@'%-.192s' zu erzeugen"
-ER_NO_SUCH_USER
- eng "The user specified as a definer ('%-.64s'@'%-.64s') does not exist"
-ER_FORBID_SCHEMA_CHANGE
- eng "Changing schema from '%-.192s' to '%-.192s' is not allowed."
- ger "Wechsel des Schemas von '%-.192s' auf '%-.192s' ist nicht erlaubt"
-ER_ROW_IS_REFERENCED_2 23000
- eng "Cannot delete or update a parent row: a foreign key constraint fails (%.192s)"
- ger "Kann Eltern-Zeile nicht löschen oder aktualisieren: eine Fremdschlüsselbedingung schlägt fehl (%.192s)"
-ER_NO_REFERENCED_ROW_2 23000
- eng "Cannot add or update a child row: a foreign key constraint fails (%.192s)"
- ger "Kann Kind-Zeile nicht hinzufügen oder aktualisieren: eine Fremdschlüsselbedingung schlägt fehl (%.192s)"
-ER_SP_BAD_VAR_SHADOW 42000
- eng "Variable '%-.64s' must be quoted with `...`, or renamed"
- ger "Variable '%-.64s' muss mit `...` geschützt oder aber umbenannt werden"
-ER_TRG_NO_DEFINER
- eng "No definer attribute for trigger '%-.192s'.'%-.192s'. The trigger will be activated under the authorization of the invoker, which may have insufficient privileges. Please recreate the trigger."
- ger "Kein Definierer-Attribut für Trigger '%-.192s'.'%-.192s'. Der Trigger wird mit der Autorisierung des Aufrufers aktiviert, der möglicherweise keine zureichenden Berechtigungen hat. Bitte legen Sie den Trigger neu an."
-ER_OLD_FILE_FORMAT
- eng "'%-.192s' has an old format, you should re-create the '%s' object(s)"
- ger "'%-.192s' hat altes Format, Sie sollten die '%s'-Objekt(e) neu erzeugen"
-ER_SP_RECURSION_LIMIT
- eng "Recursive limit %d (as set by the max_sp_recursion_depth variable) was exceeded for routine %.192s"
- ger "Rekursionsgrenze %d (durch Variable max_sp_recursion_depth gegeben) wurde für Routine %.192s überschritten"
-ER_SP_PROC_TABLE_CORRUPT
- eng "Failed to load routine %-.192s. The table mysql.proc is missing, corrupt, or contains bad data (internal code %d)"
- ger "Routine %-.192s konnte nicht geladen werden. Die Tabelle mysql.proc fehlt, ist beschädigt, oder enthält fehlerhaften Daten (interner Code: %d)"
-ER_SP_WRONG_NAME 42000
- eng "Incorrect routine name '%-.192s'"
- ger "Ungültiger Routinenname '%-.192s'"
-ER_TABLE_NEEDS_UPGRADE
- eng "Table upgrade required. Please do \"REPAIR TABLE `%-.32s`\" or dump/reload to fix it!"
- ger "Tabellenaktualisierung erforderlich. Bitte zum Reparieren \"REPAIR TABLE `%-.32s`\" eingeben!"
-ER_SP_NO_AGGREGATE 42000
- eng "AGGREGATE is not supported for stored functions"
- ger "AGGREGATE wird bei gespeicherten Funktionen nicht unterstützt"
-ER_MAX_PREPARED_STMT_COUNT_REACHED 42000
- eng "Can't create more than max_prepared_stmt_count statements (current value: %lu)"
- ger "Kann nicht mehr Anweisungen als max_prepared_stmt_count erzeugen (aktueller Wert: %lu)"
-ER_VIEW_RECURSIVE
- eng "`%-.192s`.`%-.192s` contains view recursion"
- ger "`%-.192s`.`%-.192s` 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"
-ER_TABLE_CANT_HANDLE_SPKEYS
- eng "The used table type doesn't support SPATIAL indexes"
- ger "Der verwendete Tabellentyp unterstützt keine SPATIAL-Indizes"
-ER_NO_TRIGGERS_ON_SYSTEM_SCHEMA
- eng "Triggers can not be created on system tables"
- ger "Trigger können nicht auf Systemtabellen erzeugt werden"
-ER_REMOVED_SPACES
- eng "Leading spaces are removed from name '%s'"
- ger "Führende Leerzeichen werden aus dem Namen '%s' entfernt"
-ER_AUTOINC_READ_FAILED
- eng "Failed to read auto-increment value from storage engine"
- ger "Lesen des Autoincrement-Werts von der Speicher-Engine fehlgeschlagen"
-ER_USERNAME
- eng "user name"
- ger "Benutzername"
-ER_HOSTNAME
- eng "host name"
- ger "Hostname"
-ER_WRONG_STRING_LENGTH
- eng "String '%-.70s' is too long for %s (should be no longer than %d)"
- ger "String '%-.70s' ist zu lang für %s (sollte nicht länger sein als %d)"
-ER_NON_INSERTABLE_TABLE
- eng "The target table %-.100s of the %s is not insertable-into"
- ger "Die Zieltabelle %-.100s von %s ist nicht einfügbar"
-ER_ADMIN_WRONG_MRG_TABLE
- eng "Table '%-.64s' is differently defined or of non-MyISAM type or doesn't exist"
-ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT
- eng "Too high level of nesting for select"
-ER_NAME_BECOMES_EMPTY
- eng "Name '%-.64s' has become ''"
-ER_AMBIGUOUS_FIELD_TERM
- eng "First character of the FIELDS TERMINATED string is ambiguous; please use non-optional and non-empty FIELDS ENCLOSED BY"
-ER_FOREIGN_SERVER_EXISTS
- eng "The foreign server, %s, you are trying to create already exists."
-ER_FOREIGN_SERVER_DOESNT_EXIST
- eng "The foreign server name you are trying to reference does not exist. Data source error: %-.64s"
- ger "Die externe Verbindung, auf die Sie zugreifen wollen, existiert nicht. Datenquellenfehlermeldung: %-.64s"
-ER_ILLEGAL_HA_CREATE_OPTION
- eng "Table storage engine '%-.64s' does not support the create option '%.64s'"
- ger "Speicher-Engine '%-.64s' der Tabelle unterstützt die Option '%.64s' nicht"
-ER_PARTITION_REQUIRES_VALUES_ERROR
- eng "Syntax error: %-.64s PARTITIONING requires definition of VALUES %-.64s for each partition"
- ger "Fehler in der SQL-Syntax: %-.64s-PARTITIONierung erfordert Definition von VALUES %-.64s für jede Partition"
- swe "Syntaxfel: %-.64s PARTITIONering kräver definition av VALUES %-.64s för varje partition"
-ER_PARTITION_WRONG_VALUES_ERROR
- eng "Only %-.64s PARTITIONING can use VALUES %-.64s in partition definition"
- ger "Nur %-.64s-PARTITIONierung kann VALUES %-.64s in der Partitionsdefinition verwenden"
- swe "Endast %-.64s partitionering kan använda VALUES %-.64s i definition av partitionen"
-ER_PARTITION_MAXVALUE_ERROR
- eng "MAXVALUE can only be used in last partition definition"
- ger "MAXVALUE kann nur für die Definition der letzten Partition verwendet werden"
- swe "MAXVALUE kan bara användas i definitionen av den sista partitionen"
-ER_PARTITION_SUBPARTITION_ERROR
- eng "Subpartitions can only be hash partitions and by key"
- ger "Unterpartitionen dürfen nur HASH- oder KEY-Partitionen sein"
- swe "Subpartitioner kan bara vara hash och key partitioner"
-ER_PARTITION_SUBPART_MIX_ERROR
- eng "Must define subpartitions on all partitions if on one partition"
- ger "Unterpartitionen können nur Hash- oder Key-Partitionen sein"
- swe "Subpartitioner måste definieras på alla partitioner om på en"
-ER_PARTITION_WRONG_NO_PART_ERROR
- eng "Wrong number of partitions defined, mismatch with previous setting"
- ger "Falsche Anzahl von Partitionen definiert, stimmt nicht mit vorherigen Einstellungen überein"
- swe "Antal partitioner definierade och antal partitioner är inte lika"
-ER_PARTITION_WRONG_NO_SUBPART_ERROR
- eng "Wrong number of subpartitions defined, mismatch with previous setting"
- ger "Falsche Anzahl von Unterpartitionen definiert, stimmt nicht mit vorherigen Einstellungen überein"
- swe "Antal subpartitioner definierade och antal subpartitioner är inte lika"
-ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR
- eng "Constant, random or timezone-dependent expressions in (sub)partitioning function are not allowed"
- ger "Konstante oder Random-Ausdrücke in (Unter-)Partitionsfunktionen sind nicht erlaubt"
- swe "Konstanta uttryck eller slumpmässiga uttryck är inte tillåtna (sub)partitioneringsfunktioner"
-ER_NO_CONST_EXPR_IN_RANGE_OR_LIST_ERROR
- eng "Expression in RANGE/LIST VALUES must be constant"
- ger "Ausdrücke in RANGE/LIST VALUES müssen konstant sein"
- swe "Uttryck i RANGE/LIST VALUES måste vara ett konstant uttryck"
-ER_FIELD_NOT_FOUND_PART_ERROR
- eng "Field in list of fields for partition function not found in table"
- ger "Felder in der Feldliste der Partitionierungsfunktion wurden in der Tabelle nicht gefunden"
- swe "Fält i listan av fält för partitionering med key inte funnen i tabellen"
-ER_LIST_OF_FIELDS_ONLY_IN_HASH_ERROR
- eng "List of fields is only allowed in KEY partitions"
- ger "Eine Feldliste ist nur in KEY-Partitionen erlaubt"
- swe "En lista av fält är endast tillåtet för KEY partitioner"
-ER_INCONSISTENT_PARTITION_INFO_ERROR
- eng "The partition info in the frm file is not consistent with what can be written into the frm file"
- ger "Die Partitionierungsinformationen in der frm-Datei stimmen nicht mit dem überein, was in die frm-Datei geschrieben werden kann"
- swe "Partitioneringsinformationen i frm-filen är inte konsistent med vad som kan skrivas i frm-filen"
-ER_PARTITION_FUNC_NOT_ALLOWED_ERROR
- eng "The %-.192s function returns the wrong type"
- ger "Die %-.192s-Funktion gibt einen falschen Typ zurück"
- swe "%-.192s-funktionen returnerar felaktig typ"
-ER_PARTITIONS_MUST_BE_DEFINED_ERROR
- eng "For %-.64s partitions each partition must be defined"
- ger "Für %-.64s-Partitionen muss jede Partition definiert sein"
- swe "För %-.64s partitionering så måste varje partition definieras"
-ER_RANGE_NOT_INCREASING_ERROR
- eng "VALUES LESS THAN value must be strictly increasing for each partition"
- ger "Werte in VALUES LESS THAN müssen für jede Partition strikt aufsteigend sein"
- swe "Värden i VALUES LESS THAN måste vara strikt växande för varje partition"
-ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR
- eng "VALUES value must be of same type as partition function"
- ger "VALUES-Werte müssen vom selben Typ wie die Partitionierungsfunktion sein"
- swe "Värden i VALUES måste vara av samma typ som partitioneringsfunktionen"
-ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR
- eng "Multiple definition of same constant in list partitioning"
- ger "Mehrfachdefinition derselben Konstante bei Listen-Partitionierung"
- swe "Multipel definition av samma konstant i list partitionering"
-ER_PARTITION_ENTRY_ERROR
- eng "Partitioning can not be used stand-alone in query"
- ger "Partitionierung kann in einer Abfrage nicht alleinstehend benutzt werden"
- swe "Partitioneringssyntax kan inte användas på egen hand i en SQL-fråga"
-ER_MIX_HANDLER_ERROR
- eng "The mix of handlers in the partitions is not allowed in this version of MySQL"
- ger "Das Vermischen von Handlern in Partitionen ist in dieser Version von MySQL nicht erlaubt"
- swe "Denna mix av lagringsmotorer är inte tillåten i denna version av MySQL"
-ER_PARTITION_NOT_DEFINED_ERROR
- eng "For the partitioned engine it is necessary to define all %-.64s"
- ger "Für die partitionierte Engine müssen alle %-.64s definiert sein"
- swe "För partitioneringsmotorn så är det nödvändigt att definiera alla %-.64s"
-ER_TOO_MANY_PARTITIONS_ERROR
- eng "Too many partitions (including subpartitions) were defined"
- ger "Es wurden zu vielen Partitionen (einschließlich Unterpartitionen) definiert"
- swe "För många partitioner (inkluderande subpartitioner) definierades"
-ER_SUBPARTITION_ERROR
- eng "It is only possible to mix RANGE/LIST partitioning with HASH/KEY partitioning for subpartitioning"
- ger "RANGE/LIST-Partitionierung kann bei Unterpartitionen nur zusammen mit HASH/KEY-Partitionierung verwendet werden"
- swe "Det är endast möjligt att blanda RANGE/LIST partitionering med HASH/KEY partitionering för subpartitionering"
-ER_CANT_CREATE_HANDLER_FILE
- eng "Failed to create specific handler file"
- ger "Erzeugen einer spezifischen Handler-Datei fehlgeschlagen"
- swe "Misslyckades med att skapa specifik fil i lagringsmotor"
-ER_BLOB_FIELD_IN_PART_FUNC_ERROR
- eng "A BLOB field is not allowed in partition function"
- ger "In der Partitionierungsfunktion sind BLOB-Spalten nicht erlaubt"
- swe "Ett BLOB-fält är inte tillåtet i partitioneringsfunktioner"
-ER_UNIQUE_KEY_NEED_ALL_FIELDS_IN_PF
- eng "A %-.192s must include all columns in the table's partitioning function"
-ER_NO_PARTS_ERROR
- eng "Number of %-.64s = 0 is not an allowed value"
- ger "Eine Anzahl von %-.64s = 0 ist kein erlaubter Wert"
- swe "Antal %-.64s = 0 är inte ett tillåten värde"
-ER_PARTITION_MGMT_ON_NONPARTITIONED
- eng "Partition management on a not partitioned table is not possible"
- ger "Partitionsverwaltung einer nicht partitionierten Tabelle ist nicht möglich"
- swe "Partitioneringskommando på en opartitionerad tabell är inte möjligt"
-ER_FOREIGN_KEY_ON_PARTITIONED
- eng "Foreign key clause is not yet supported in conjunction with partitioning"
- ger "Fremdschlüssel-Beschränkungen sind im Zusammenhang mit Partitionierung nicht zulässig"
- swe "Foreign key klausul är inte ännu implementerad i kombination med partitionering"
-ER_DROP_PARTITION_NON_EXISTENT
- eng "Error in list of partitions to %-.64s"
- ger "Fehler in der Partitionsliste bei %-.64s"
- swe "Fel i listan av partitioner att %-.64s"
-ER_DROP_LAST_PARTITION
- eng "Cannot remove all partitions, use DROP TABLE instead"
- ger "Es lassen sich nicht sämtliche Partitionen löschen, benutzen Sie statt dessen DROP TABLE"
- swe "Det är inte tillåtet att ta bort alla partitioner, använd DROP TABLE istället"
-ER_COALESCE_ONLY_ON_HASH_PARTITION
- eng "COALESCE PARTITION can only be used on HASH/KEY partitions"
- ger "COALESCE PARTITION kann nur auf HASH- oder KEY-Partitionen benutzt werden"
- swe "COALESCE PARTITION kan bara användas på HASH/KEY partitioner"
-ER_REORG_HASH_ONLY_ON_SAME_NO
- eng "REORGANIZE PARTITION can only be used to reorganize partitions not to change their numbers"
- ger "REORGANIZE PARTITION kann nur zur Reorganisation von Partitionen verwendet werden, nicht, um ihre Nummern zu ändern"
- swe "REORGANIZE PARTITION kan bara användas för att omorganisera partitioner, inte för att ändra deras antal"
-ER_REORG_NO_PARAM_ERROR
- eng "REORGANIZE PARTITION without parameters can only be used on auto-partitioned tables using HASH PARTITIONs"
- ger "REORGANIZE PARTITION ohne Parameter kann nur für auto-partitionierte Tabellen verwendet werden, die HASH-Partitionierung benutzen"
- swe "REORGANIZE PARTITION utan parametrar kan bara användas på auto-partitionerade tabeller som använder HASH partitionering"
-ER_ONLY_ON_RANGE_LIST_PARTITION
- eng "%-.64s PARTITION can only be used on RANGE/LIST partitions"
- ger "%-.64s PARTITION kann nur für RANGE- oder LIST-Partitionen verwendet werden"
- swe "%-.64s PARTITION kan bara användas på RANGE/LIST-partitioner"
-ER_ADD_PARTITION_SUBPART_ERROR
- eng "Trying to Add partition(s) with wrong number of subpartitions"
- ger "Es wurde versucht, eine oder mehrere Partitionen mit der falschen Anzahl von Unterpartitionen hinzuzufügen"
- swe "ADD PARTITION med fel antal subpartitioner"
-ER_ADD_PARTITION_NO_NEW_PARTITION
- eng "At least one partition must be added"
- ger "Es muss zumindest eine Partition hinzugefügt werden"
- swe "Åtminstone en partition måste läggas till vid ADD PARTITION"
-ER_COALESCE_PARTITION_NO_PARTITION
- eng "At least one partition must be coalesced"
- ger "Zumindest eine Partition muss mit COALESCE PARTITION zusammengefügt werden"
- swe "Åtminstone en partition måste slås ihop vid COALESCE PARTITION"
-ER_REORG_PARTITION_NOT_EXIST
- eng "More partitions to reorganize than there are partitions"
- ger "Es wurde versucht, mehr Partitionen als vorhanden zu reorganisieren"
- swe "Fler partitioner att reorganisera än det finns partitioner"
-ER_SAME_NAME_PARTITION
- eng "Duplicate partition name %-.192s"
- ger "Doppelter Partitionsname: %-.192s"
- swe "Duplicerat partitionsnamn %-.192s"
-ER_NO_BINLOG_ERROR
- eng "It is not allowed to shut off binlog on this command"
- ger "Es es nicht erlaubt, bei diesem Befehl binlog abzuschalten"
- swe "Det är inte tillåtet att stänga av binlog på detta kommando"
-ER_CONSECUTIVE_REORG_PARTITIONS
- eng "When reorganizing a set of partitions they must be in consecutive order"
- ger "Bei der Reorganisation eines Satzes von Partitionen müssen diese in geordneter Reihenfolge vorliegen"
- swe "När ett antal partitioner omorganiseras måste de vara i konsekutiv ordning"
-ER_REORG_OUTSIDE_RANGE
- eng "Reorganize of range partitions cannot change total ranges except for last partition where it can extend the range"
- ger "Die Reorganisation von RANGE-Partitionen kann Gesamtbereiche nicht verändern, mit Ausnahme der letzten Partition, die den Bereich erweitern kann"
- swe "Reorganisering av rangepartitioner kan inte ändra den totala intervallet utom för den sista partitionen där intervallet kan utökas"
-ER_PARTITION_FUNCTION_FAILURE
- eng "Partition function not supported in this version for this handler"
- ger "Partitionsfunktion in dieser Version dieses Handlers nicht unterstützt"
-ER_PART_STATE_ERROR
- eng "Partition state cannot be defined from CREATE/ALTER TABLE"
- ger "Partitionszustand kann nicht von CREATE oder ALTER TABLE aus definiert werden"
- swe "Partition state kan inte definieras från CREATE/ALTER TABLE"
-ER_LIMITED_PART_RANGE
- eng "The %-.64s handler only supports 32 bit integers in VALUES"
- ger "Der Handler %-.64s unterstützt in VALUES nur 32-Bit-Integers"
- swe "%-.64s stödjer endast 32 bitar i integers i VALUES"
-ER_PLUGIN_IS_NOT_LOADED
- eng "Plugin '%-.192s' is not loaded"
- ger "Plugin '%-.192s' ist nicht geladen"
-ER_WRONG_VALUE
- eng "Incorrect %-.32s value: '%-.128s'"
- ger "Falscher %-.32s-Wert: '%-.128s'"
-ER_NO_PARTITION_FOR_GIVEN_VALUE
- eng "Table has no partition for value %-.64s"
- ger "Tabelle hat für den Wert %-.64s keine Partition"
-ER_FILEGROUP_OPTION_ONLY_ONCE
- eng "It is not allowed to specify %s more than once"
- ger "%s darf nicht mehr als einmal angegegeben werden"
-ER_CREATE_FILEGROUP_FAILED
- eng "Failed to create %s"
- ger "Anlegen von %s fehlgeschlagen"
-ER_DROP_FILEGROUP_FAILED
- eng "Failed to drop %s"
- ger "Löschen (drop) von %s fehlgeschlagen"
-ER_TABLESPACE_AUTO_EXTEND_ERROR
- eng "The handler doesn't support autoextend of tablespaces"
- ger "Der Handler unterstützt keine automatische Erweiterung (Autoextend) von Tablespaces"
-ER_WRONG_SIZE_NUMBER
- eng "A size parameter was incorrectly specified, either number or on the form 10M"
- ger "Ein Größen-Parameter wurde unkorrekt angegeben, muss entweder Zahl sein oder im Format 10M"
-ER_SIZE_OVERFLOW_ERROR
- eng "The size number was correct but we don't allow the digit part to be more than 2 billion"
- ger "Die Zahl für die Größe war korrekt, aber der Zahlanteil darf nicht größer als 2 Milliarden sein"
-ER_ALTER_FILEGROUP_FAILED
- eng "Failed to alter: %s"
- ger "Änderung von %s fehlgeschlagen"
-ER_BINLOG_ROW_LOGGING_FAILED
- eng "Writing one row to the row-based binary log failed"
- ger "Schreiben einer Zeilen ins zeilenbasierte Binärlog fehlgeschlagen"
-ER_BINLOG_ROW_WRONG_TABLE_DEF
- eng "Table definition on master and slave does not match: %s"
- ger "Tabellendefinition auf Master und Slave stimmt nicht überein: %s"
-ER_BINLOG_ROW_RBR_TO_SBR
- eng "Slave running with --log-slave-updates must use row-based binary logging to be able to replicate row-based binary log events"
- ger "Slave, die mit --log-slave-updates laufen, müssen zeilenbasiertes Loggen verwenden, um zeilenbasierte Binärlog-Ereignisse loggen zu können"
-ER_EVENT_ALREADY_EXISTS
- eng "Event '%-.192s' already exists"
- ger "Event '%-.192s' existiert bereits"
-ER_EVENT_STORE_FAILED
- eng "Failed to store event %s. Error code %d from storage engine."
- ger "Speichern von Event %s fehlgeschlagen. Fehlercode der Speicher-Engine: %d"
-ER_EVENT_DOES_NOT_EXIST
- eng "Unknown event '%-.192s'"
- ger "Unbekanntes Event '%-.192s'"
-ER_EVENT_CANT_ALTER
- eng "Failed to alter event '%-.192s'"
- ger "Ändern des Events '%-.192s' fehlgeschlagen"
-ER_EVENT_DROP_FAILED
- eng "Failed to drop %s"
- ger "Löschen von %s fehlgeschlagen"
-ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG
- eng "INTERVAL is either not positive or too big"
- ger "INTERVAL ist entweder nicht positiv oder zu groß"
-ER_EVENT_ENDS_BEFORE_STARTS
- eng "ENDS is either invalid or before STARTS"
- ger "ENDS ist entweder ungültig oder liegt vor STARTS"
-ER_EVENT_EXEC_TIME_IN_THE_PAST
- eng "Event execution time is in the past. Event has been disabled"
-ER_EVENT_OPEN_TABLE_FAILED
- eng "Failed to open mysql.event"
- ger "Öffnen von mysql.event fehlgeschlagen"
-ER_EVENT_NEITHER_M_EXPR_NOR_M_AT
- eng "No datetime expression provided"
- ger "Kein DATETIME-Ausdruck angegeben"
-ER_COL_COUNT_DOESNT_MATCH_CORRUPTED
- eng "Column count of mysql.%s is wrong. Expected %d, found %d. The table is probably corrupted"
- ger "Spaltenanzahl von mysql.%s falsch. %d erwartet, aber %d gefunden. Tabelle ist wahrscheinlich beschädigt"
-ER_CANNOT_LOAD_FROM_TABLE
- eng "Cannot load from mysql.%s. The table is probably corrupted"
- ger "Kann mysql.%s nicht einlesen. Tabelle ist wahrscheinlich beschädigt"
-ER_EVENT_CANNOT_DELETE
- eng "Failed to delete the event from mysql.event"
- ger "Löschen des Events aus mysql.event fehlgeschlagen"
-ER_EVENT_COMPILE_ERROR
- eng "Error during compilation of event's body"
- ger "Fehler beim Kompilieren des Event-Bodys"
-ER_EVENT_SAME_NAME
- eng "Same old and new event name"
- ger "Alter und neuer Event-Name sind gleich"
-ER_EVENT_DATA_TOO_LONG
- eng "Data for column '%s' too long"
- ger "Daten der Spalte '%s' zu lang"
-ER_DROP_INDEX_FK
- eng "Cannot drop index '%-.192s': needed in a foreign key constraint"
- ger "Kann Index '%-.192s' nicht löschen: wird für einen Fremdschlüssel benötigt"
-# When using this error message, use the ER_WARN_DEPRECATED_SYNTAX error
-# code. See, for example, code in mysql_priv.h.
-ER_WARN_DEPRECATED_SYNTAX_WITH_VER
- eng "The syntax '%s' is deprecated and will be removed in MySQL %s. Please use %s instead"
- ger "Die Syntax '%s' ist veraltet und wird in MySQL %s entfernt. Bitte benutzen Sie statt dessen %s"
-ER_CANT_WRITE_LOCK_LOG_TABLE
- eng "You can't write-lock a log table. Only read access is possible"
- ger "Eine Log-Tabelle kann nicht schreibgesperrt werden. Es ist ohnehin nur Lesezugriff möglich"
-ER_CANT_LOCK_LOG_TABLE
- eng "You can't use locks with log tables."
- ger "Log-Tabellen können nicht mit normalen Lesesperren gesperrt werden. Verwenden Sie statt dessen READ LOCAL"
-ER_FOREIGN_DUPLICATE_KEY 23000 S1009
- eng "Upholding foreign key constraints for table '%.192s', entry '%-.192s', key %d would lead to a duplicate entry"
- ger "Aufrechterhalten der Fremdschlüssel-Constraints für Tabelle '%.192s', Eintrag '%-.192s', Schlüssel %d würde zu einem doppelten Eintrag führen"
-ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE
- eng "Column count of mysql.%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 mysql.%s falsch. %d erwartet, aber %d erhalten. Erzeugt mit MySQL %d, jetzt unter %d. Bitte benutzen Sie mysql_upgrade, um den Fehler zu beheben"
-ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR
- eng "Cannot switch out of the row-based binary log format when the session has open temporary tables"
- ger "Kann nicht aus dem zeilenbasierten Binärlog-Format herauswechseln, wenn die Sitzung offene temporäre Tabellen hat"
-ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT
- eng "Cannot change the binary logging format inside a stored function or trigger"
- ger "Das Binärlog-Format kann innerhalb einer gespeicherten Funktion oder eines Triggers nicht geändert werden"
-ER_NDB_CANT_SWITCH_BINLOG_FORMAT
- eng "The NDB cluster engine does not support changing the binlog format on the fly yet"
- ger "Die Speicher-Engine NDB Cluster unterstützt das Ändern des Binärlog-Formats zur Laufzeit noch nicht"
-ER_PARTITION_NO_TEMPORARY
- eng "Cannot create temporary table with partitions"
- ger "Anlegen temporärer Tabellen mit Partitionen nicht möglich"
-ER_PARTITION_CONST_DOMAIN_ERROR
- eng "Partition constant is out of partition function domain"
- ger "Partitionskonstante liegt außerhalb der Partitionsfunktionsdomäne"
- swe "Partitionskonstanten är utanför partitioneringsfunktionens domän"
-ER_PARTITION_FUNCTION_IS_NOT_ALLOWED
- eng "This partition function is not allowed"
- ger "Diese Partitionierungsfunktion ist nicht erlaubt"
- swe "Denna partitioneringsfunktion är inte tillåten"
-ER_DDL_LOG_ERROR
- eng "Error in DDL log"
- ger "Fehler im DDL-Log"
-ER_NULL_IN_VALUES_LESS_THAN
- eng "Not allowed to use NULL value in VALUES LESS THAN"
- ger "In VALUES LESS THAN dürfen keine NULL-Werte verwendet werden"
- swe "Det är inte tillåtet att använda NULL-värden i VALUES LESS THAN"
-ER_WRONG_PARTITION_NAME
- eng "Incorrect partition name"
- ger "Falscher Partitionsname"
- swe "Felaktigt partitionsnamn"
-ER_CANT_CHANGE_TX_ISOLATION 25001
- eng "Transaction isolation level can't be changed while a transaction is in progress"
- ger "Transaktionsisolationsebene kann während einer laufenden Transaktion nicht geändert werden"
-ER_DUP_ENTRY_AUTOINCREMENT_CASE
- eng "ALTER TABLE causes auto_increment resequencing, resulting in duplicate entry '%-.192s' for key '%-.192s'"
- ger "ALTER TABLE führt zur Neusequenzierung von auto_increment, wodurch der doppelte Eintrag '%-.192s' für Schlüssel '%-.192s' auftritt"
-ER_EVENT_MODIFY_QUEUE_ERROR
- eng "Internal scheduler error %d"
- ger "Interner Scheduler-Fehler %d"
-ER_EVENT_SET_VAR_ERROR
- eng "Error during starting/stopping of the scheduler. Error code %u"
- ger "Fehler während des Startens oder Anhalten des Schedulers. Fehlercode %u"
-ER_PARTITION_MERGE_ERROR
- eng "Engine cannot be used in partitioned tables"
- ger "Engine kann in partitionierten Tabellen nicht verwendet werden"
- swe "Engine inte användas i en partitionerad tabell"
-ER_CANT_ACTIVATE_LOG
- eng "Cannot activate '%-.64s' log"
- ger "Kann Logdatei '%-.64s' nicht aktivieren"
-ER_RBR_NOT_AVAILABLE
- eng "The server was not built with row-based replication"
-ER_BASE64_DECODE_ERROR
- eng "Decoding of base64 string failed"
- swe "Avkodning av base64 sträng misslyckades"
- ger "Der Server hat keine zeilenbasierte Replikation"
-ER_EVENT_RECURSION_FORBIDDEN
- eng "Recursion of EVENT DDL statements is forbidden when body is present"
- ger "Rekursivität von EVENT-DDL-Anweisungen ist unzulässig wenn ein Hauptteil (Body) existiert"
-ER_EVENTS_DB_ERROR
- eng "Cannot proceed because system tables used by Event Scheduler were found damaged at server start"
- ger "Kann nicht weitermachen, weil die Tabellen, die von Events verwendet werden, beim Serverstart als beschädigt markiert wurden"
-ER_ONLY_INTEGERS_ALLOWED
- eng "Only integers allowed as number here"
- ger "An dieser Stelle sind nur Ganzzahlen zulässig"
-ER_UNSUPORTED_LOG_ENGINE
- eng "This storage engine cannot be used for log tables"
- ger "Diese Speicher-Engine kann für Logtabellen nicht verwendet werden"
-ER_BAD_LOG_STATEMENT
- eng "You cannot '%s' a log table if logging is enabled"
- ger "Sie können eine Logtabelle nicht '%s', wenn Loggen angeschaltet ist"
-ER_CANT_RENAME_LOG_TABLE
- eng "Cannot rename '%s'. When logging enabled, rename to/from log table must rename two tables: the log table to an archive table and another table back to '%s'"
- ger "Kann '%s' nicht umbenennen. Wenn Loggen angeschaltet ist, müssen beim Umbenennen zu/von einer Logtabelle zwei Tabellen angegeben werden: die Logtabelle zu einer Archivtabelle und eine weitere Tabelle zurück zu '%s'"
-ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT 42000
- eng "Incorrect parameter count in the call to native function '%-.192s'"
- ger "Falsche Anzahl von Parametern beim Aufruf der nativen Funktion '%-.192s'"
-ER_WRONG_PARAMETERS_TO_NATIVE_FCT 42000
- eng "Incorrect parameters in the call to native function '%-.192s'"
- ger "Falscher Parameter beim Aufruf der nativen Funktion '%-.192s'"
-ER_WRONG_PARAMETERS_TO_STORED_FCT 42000
- eng "Incorrect parameters in the call to stored function '%-.192s'"
- ger "Falsche Parameter beim Aufruf der gespeicherten Funktion '%-.192s'"
-ER_NATIVE_FCT_NAME_COLLISION
- eng "This function '%-.192s' has the same name as a native function"
- ger "Die Funktion '%-.192s' hat denselben Namen wie eine native Funktion"
-# When using this error message, use the ER_DUP_ENTRY error code. See, for
-# example, code in handler.cc.
-ER_DUP_ENTRY_WITH_KEY_NAME 23000 S1009
- cze "Zvojen-Bý klíè '%-.64s' (èíslo klíèe '%-.192s')"
- dan "Ens værdier '%-.64s' for indeks '%-.192s'"
- nla "Dubbele ingang '%-.64s' voor zoeksleutel '%-.192s'"
- eng "Duplicate entry '%-.64s' for key '%-.192s'"
- jps "'%-.64s' ‚Í key '%-.192s' ‚É‚¨‚¢‚Äd•¡‚µ‚Ä‚¢‚Ü‚·",
- est "Kattuv väärtus '%-.64s' võtmele '%-.192s'"
- fre "Duplicata du champ '%-.64s' pour la clef '%-.192s'"
- ger "Doppelter Eintrag '%-.64s' für Schlüssel '%-.192s'"
- greek "ÄéðëÞ åããñáöÞ '%-.64s' ãéá ôï êëåéäß '%-.192s'"
- hun "Duplikalt bejegyzes '%-.64s' a '%-.192s' kulcs szerint."
- ita "Valore duplicato '%-.64s' per la chiave '%-.192s'"
- jpn "'%-.64s' ¤Ï key '%-.192s' ¤Ë¤ª¤¤¤Æ½ÅÊ£¤·¤Æ¤¤¤Þ¤¹"
- kor "Áߺ¹µÈ ÀÔ·Â °ª '%-.64s': key '%-.192s'"
- nor "Like verdier '%-.64s' for nøkkel '%-.192s'"
- norwegian-ny "Like verdiar '%-.64s' for nykkel '%-.192s'"
- pol "Powtórzone wyst?pienie '%-.64s' dla klucza '%-.192s'"
- por "Entrada '%-.64s' duplicada para a chave '%-.192s'"
- rum "Cimpul '%-.64s' e duplicat pentru cheia '%-.192s'"
- rus "äÕÂÌÉÒÕÀÝÁÑÓÑ ÚÁÐÉÓØ '%-.64s' ÐÏ ËÌÀÞÕ '%-.192s'"
- serbian "Dupliran unos '%-.64s' za kljuè '%-.192s'"
- slo "Opakovaný kµúè '%-.64s' (èíslo kµúèa '%-.192s')"
- spa "Entrada duplicada '%-.64s' para la clave '%-.192s'"
- swe "Dubbel nyckel '%-.64s' för nyckel '%-.192s'"
- ukr "äÕÂÌÀÀÞÉÊ ÚÁÐÉÓ '%-.64s' ÄÌÑ ËÌÀÞÁ '%-.192s'"
-ER_BINLOG_PURGE_EMFILE
- eng "Too many files opened, please execute the command again"
- ger "Zu viele offene Dateien, bitte führen Sie den Befehl noch einmal aus"
-ER_EVENT_CANNOT_CREATE_IN_THE_PAST
- eng "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was dropped immediately after creation."
-ER_EVENT_CANNOT_ALTER_IN_THE_PAST
- eng "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was dropped immediately after creation."
-ER_SLAVE_INCIDENT
- eng "The incident %s occured on the master. Message: %-.64s"
-ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT
- eng "Table has no partition for some existing values"
-ER_BINLOG_UNSAFE_STATEMENT
- eng "Statement may not be safe to log in statement format."
- swe "Detta är inte säkert att logga i statement-format."
-ER_SLAVE_FATAL_ERROR
- eng "Fatal error: %s"
-ER_SLAVE_RELAY_LOG_READ_FAILURE
- eng "Relay log read failure: %s"
-ER_SLAVE_RELAY_LOG_WRITE_FAILURE
- eng "Relay log write failure: %s"
-ER_SLAVE_CREATE_EVENT_FAILURE
- eng "Failed to create %s"
-ER_SLAVE_MASTER_COM_FAILURE
- eng "Master command %s failed: %s"
-ER_BINLOG_LOGGING_IMPOSSIBLE
- eng "Binary logging not possible. Message: %s"
-
-ER_VIEW_NO_CREATION_CTX
- eng "View `%-.64s`.`%-.64s` has no creation context"
-ER_VIEW_INVALID_CREATION_CTX
- eng "Creation context of view `%-.64s`.`%-.64s' is invalid"
-
-ER_SR_INVALID_CREATION_CTX
- eng "Creation context of stored routine `%-.64s`.`%-.64s` is invalid"
-
-ER_TRG_CORRUPTED_FILE
- eng "Corrupted TRG file for table `%-.64s`.`%-.64s`"
-ER_TRG_NO_CREATION_CTX
- eng "Triggers for table `%-.64s`.`%-.64s` have no creation context"
-ER_TRG_INVALID_CREATION_CTX
- eng "Trigger creation context of table `%-.64s`.`%-.64s` is invalid"
-
-ER_EVENT_INVALID_CREATION_CTX
- eng "Creation context of event `%-.64s`.`%-.64s` is invalid"
-
-ER_TRG_CANT_OPEN_TABLE
- eng "Cannot open table for trigger `%-.64s`.`%-.64s`"
-
-ER_CANT_CREATE_SROUTINE
- eng "Cannot create stored routine `%-.64s`. Check warnings"
-ER_SLAVE_AMBIGOUS_EXEC_MODE
- eng "Ambiguous slave modes combination. %s"
-
-ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT
- eng "The BINLOG statement of type `%s` was not preceded by a format description BINLOG statement."
-ER_SLAVE_CORRUPT_EVENT
- eng "Corrupted replication event was detected"
-
-ER_LOAD_DATA_INVALID_COLUMN
- eng "Invalid column reference (%-.64s) in LOAD DATA"
-
-ER_LOG_PURGE_NO_FILE
- eng "Being purged log %s was not found"
-
-ER_XA_RBTIMEOUT XA106
- eng "XA_RBTIMEOUT: Transaction branch was rolled back: took too long"
-
-ER_XA_RBDEADLOCK XA102
- eng "XA_RBDEADLOCK: Transaction branch was rolled back: deadlock was detected"
-
-ER_NEED_REPREPARE
- eng "Prepared statement needs to be re-prepared"
-
-ER_DELAYED_NOT_SUPPORTED
- eng "DELAYED option not supported for table '%-.192s'"
-
-WARN_NO_MASTER_INFO
- eng "The master info structure does not exist"
-
-WARN_OPTION_IGNORED
- eng "<%-.64s> option ignored"
-
-WARN_PLUGIN_DELETE_BUILTIN
- eng "Built-in plugins cannot be deleted"
-
-WARN_PLUGIN_BUSY
- eng "Plugin is busy and will be uninstalled on shutdown"
-
-ER_VARIABLE_IS_READONLY
- eng "%s variable '%s' is read-only. Use SET %s to assign the value"
-
-ER_WARN_ENGINE_TRANSACTION_ROLLBACK
- eng "Storage engine %s does not support rollback for this statement. Transaction rolled back and must be restarted"
-
-ER_SLAVE_HEARTBEAT_FAILURE
- eng "Unexpected master's heartbeat data: %s"
-ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE
- eng "The requested value for the heartbeat period %s %s"
-
-ER_NDB_REPLICATION_SCHEMA_ERROR
- eng "Bad schema for mysql.ndb_replication table. Message: %-.64s"
-ER_CONFLICT_FN_PARSE_ERROR
- eng "Error in parsing conflict function. Message: %-.64s"
-ER_EXCEPTIONS_WRITE_ERROR
- eng "Write to exceptions table failed. Message: %-.128s""
-
-ER_TOO_LONG_TABLE_COMMENT
- eng "Comment for table '%-.64s' is too long (max = %lu)"
- por "Comentário para a tabela '%-.64s' é longo demais (max = %lu)"
-
-ER_TOO_LONG_FIELD_COMMENT
- eng "Comment for field '%-.64s' is too long (max = %lu)"
- por "Comentário para o campo '%-.64s' é longo demais (max = %lu)"
-
-ER_FUNC_INEXISTENT_NAME_COLLISION 42000
- eng "FUNCTION %s does not exist. Check the 'Function Name Parsing and Resolution' section in the Reference Manual"
-
-# When updating these, please update EXPLAIN_FILENAME_MAX_EXTRA_LENGTH in
-# mysql_priv.h with the new maximal additional length for explain_filename.
-ER_DATABASE_NAME
- eng "Database"
- swe "Databas"
-ER_TABLE_NAME
- eng "Table"
- swe "Tabell"
-ER_PARTITION_NAME
- eng "Partition"
- swe "Partition"
-ER_SUBPARTITION_NAME
- eng "Subpartition"
- swe "Subpartition"
-ER_TEMPORARY_NAME
- eng "Temporary"
- swe "Temporär"
-ER_RENAMED_NAME
- eng "Renamed"
- swe "Namnändrad"
-ER_TOO_MANY_CONCURRENT_TRXS
- eng "Too many active concurrent transactions"
-
-WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED
- eng "Non-ASCII separator arguments are not fully supported"
-
-ER_DEBUG_SYNC_TIMEOUT
- eng "debug sync point wait timed out"
- ger "Debug Sync Point Wartezeit überschritten"
-ER_DEBUG_SYNC_HIT_LIMIT
- eng "debug sync point hit limit reached"
- ger "Debug Sync Point Hit Limit erreicht"
-
-#
-# MariaDB error messages section starts here
-#
-
-# The following is here to allow us to detect if there was missing
-# error messages in the errmsg.sys file
-
-ER_LAST_MYSQL_ERROR_MESSAGE
- eng ""
-
-# MariaDB error numbers starts from 1900
-start-error-number 1900
-
-ER_VCOL_BASED_ON_VCOL
- eng "A computed column cannot be based on a computed column"
-
-ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED
- eng "Function or expression is not allowed for column '%s'"
-
-ER_DATA_CONVERSION_ERROR_FOR_VIRTUAL_COLUMN
- eng "Generated value for computed column '%s' cannot be converted to type '%s'"
-
-ER_PRIMARY_KEY_BASED_ON_VIRTUAL_COLUMN
- eng "Primary key cannot be defined upon a computed column"
-
-ER_KEY_BASED_ON_GENERATED_VIRTUAL_COLUMN
- eng "Key/Index cannot be defined on a non-stored computed column"
-
-ER_WRONG_FK_OPTION_FOR_VIRTUAL_COLUMN
- eng "Cannot define foreign key with %s clause on a computed column"
-
-ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN
- eng "The value specified for computed column '%s' in table '%s' ignored"
-
-ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN
- eng "This is not yet supported for computed columns"
-
-ER_CONST_EXPR_IN_VCOL
- eng "Constant expression in computed column function is not allowed"
-
-ER_ROW_EXPR_FOR_VCOL
- eng "Expression for computed column cannot return a row"
-ER_UNSUPPORTED_ENGINE_FOR_VIRTUAL_COLUMNS
- eng "%s storage engine does not support computed columns"
-ER_UNKNOWN_OPTION
- eng "Unknown option '%-.64s'"
-ER_BAD_OPTION_VALUE
- eng "Incorrect value '%-.64s' for option '%-.64s'"
-ER_NETWORK_READ_EVENT_CHECKSUM_FAILURE
- eng "Replication event checksum verification failed while reading from network."
-ER_BINLOG_READ_EVENT_CHECKSUM_FAILURE
- eng "Replication event checksum verification failed while reading from a log file."
-ER_CANT_DO_ONLINE
- eng "Can't execute the given '%s' command as online"
-ER_DATA_OVERFLOW 22003
- eng "Got overflow when converting '%-.128s' to %-.32s. Value truncated."
-ER_DATA_TRUNCATED 22003
- eng "Truncated value '%-.128s' when converting to %-.32s"
-ER_BAD_DATA 22007
- eng "Encountered illegal value '%-.128s' when converting to %-.32s"
-ER_DYN_COL_WRONG_FORMAT
- eng "Encountered illegal format of dynamic column string"
-ER_DYN_COL_IMPLEMENTATION_LIMIT
- eng "Dynamic column implementation limit reached"
-ER_DYN_COL_DATA 22007
- eng "Illegal value used as argument of dynamic column function"
-ER_DYN_COL_WRONG_CHARSET
- eng "Dynamic column contains unknown character set"
-ER_ILLEGAL_SUBQUERY_OPTIMIZER_SWITCHES
- eng "At least one of the 'in_to_exists' or 'materialization' optimizer_switch flags must be 'on'."
-ER_QUERY_CACHE_IS_DISABLED
- eng "Query cache is disabled (resize or similar command in progress); repeat this command later"
-ER_QUERY_CACHE_IS_GLOBALY_DISABLED
- eng "Query cache is globally disabled and you can't enable it only for this session"
-ER_VIEW_ORDERBY_IGNORED
- eng "View '%-.192s'.'%-.192s' ORDER BY clause ignored because there is other ORDER BY clause already."
-ER_CONNECTION_KILLED 70100
- eng "Connection was killed"
-ER_INTERNAL_ERROR
- eng "Internal error: '%-.192s'"
-ER_SPATIAL_MUST_HAVE_GEOM_COL 42000
- eng "A SPATIAL index may only contain a geometrical type column"
-
diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc
index 1cf046da51f..b0c67fbfe32 100644
--- a/sql/signal_handler.cc
+++ b/sql/signal_handler.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2011, 2012, Oracle and/or its affiliates.
- Copyright (c) 2011, 2014, Monty Program Ab.
+ Copyright (c) 2011, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,7 +17,10 @@
#include "my_global.h"
#include <signal.h>
-#include "mysql_priv.h"
+//#include "sys_vars.h"
+#include <keycache.h>
+#include "mysqld.h"
+#include "sql_class.h"
#include "my_stacktrace.h"
#ifdef __WIN__
@@ -39,6 +42,8 @@ extern volatile sig_atomic_t calling_initgroups;
extern volatile sig_atomic_t ld_assume_kernel_is_set;
#endif
+extern const char *optimizer_switch_names[];
+
/**
* Handler for fatal signals on POSIX, exception handler on Windows.
*
@@ -104,6 +109,7 @@ extern "C" sig_handler handle_fatal_signal(int sig)
set_server_version();
my_safe_printf_stderr("Server version: %s\n", server_version);
+
my_safe_printf_stderr("key_buffer_size=%lu\n",
(ulong) dflt_key_cache->key_cache_mem_size);
@@ -114,42 +120,24 @@ extern "C" sig_handler handle_fatal_signal(int sig)
(ulong) max_used_connections);
my_safe_printf_stderr("max_threads=%u\n",
- (uint) thread_scheduler.max_threads);
+ (uint) thread_scheduler->max_threads +
+ (uint) extra_max_connections);
my_safe_printf_stderr("thread_count=%u\n", (uint) thread_count);
- my_safe_printf_stderr("connection_count=%u\n", (uint) connection_count);
-
my_safe_printf_stderr("It is possible that mysqld could use up to \n"
"key_buffer_size + "
"(read_buffer_size + sort_buffer_size)*max_threads = "
"%lu K bytes of memory\n",
- ((ulong) dflt_key_cache->key_cache_mem_size +
+ (ulong)(dflt_key_cache->key_cache_mem_size +
(global_system_variables.read_buff_size +
global_system_variables.sortbuff_size) *
- thread_scheduler.max_threads +
- max_connections * sizeof(THD)) / 1024);
+ (thread_scheduler->max_threads + extra_max_connections) +
+ (max_connections + extra_max_connections)* sizeof(THD)) / 1024);
my_safe_printf_stderr("%s",
"Hope that's ok; if not, decrease some variables in the equation.\n\n");
-#if defined(HAVE_LINUXTHREADS)
-#define UNSAFE_DEFAULT_LINUX_THREADS 200
- if (sizeof(char*) == 4 && thread_count > UNSAFE_DEFAULT_LINUX_THREADS)
- {
- my_safe_printf_stderr(
- "You seem to be running 32-bit Linux and have "
- "%d concurrent connections.\n"
- "If you have not changed STACK_SIZE in LinuxThreads "
- "and built the binary \n"
- "yourself, LinuxThreads is quite likely to steal "
- "a part of the global heap for\n"
- "the thread stack. Please read "
- "http://dev.mysql.com/doc/mysql/en/linux-installation.html\n\n"
- thread_count);
- }
-#endif /* HAVE_LINUXTHREADS */
-
#ifdef HAVE_STACKTRACE
thd= current_thd;
@@ -162,7 +150,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,
- my_thread_stack_size);
+ (ulong)my_thread_stack_size);
}
if (thd)
{
@@ -192,29 +180,30 @@ extern "C" sig_handler handle_fatal_signal(int sig)
case KILL_SERVER_HARD:
kreason= "KILL_SERVER";
break;
+ case ABORT_QUERY:
+ case ABORT_QUERY_HARD:
+ kreason= "ABORT_QUERY";
+ break;
}
my_safe_printf_stderr("%s", "\n"
"Trying to get some variables.\n"
"Some pointers may be invalid and cause the dump to abort.\n");
my_safe_printf_stderr("Query (%p): ", thd->query());
- my_safe_print_str(thd->query(), min(65535, thd->query_length()));
- my_safe_printf_stderr("Connection ID (thread ID): %lu\n",
+ my_safe_print_str(thd->query(), min(65536U, thd->query_length()));
+ my_safe_printf_stderr("\nConnection ID (thread ID): %lu\n",
(ulong) thd->thread_id);
- my_safe_printf_stderr("Status: %s\n", kreason);
-
+ my_safe_printf_stderr("Status: %s\n\n", kreason);
+ my_safe_printf_stderr("%s", "Optimizer switch: ");
ulonglong optsw= thd->variables.optimizer_switch;
- const char **optimizer_switch_names= optimizer_switch_typelib.type_names;
- my_safe_printf_stderr("Optimizer switch: ");
for (uint i= 0; optimizer_switch_names[i+1]; i++, optsw >>= 1)
{
if (i)
- my_safe_printf_stderr(",");
+ my_safe_printf_stderr("%s", ",");
my_safe_printf_stderr("%s=%s",
- optimizer_switch_names[i],
- optsw & 1 ? "on" : "off");
+ optimizer_switch_names[i], optsw & 1 ? "on" : "off");
}
- my_safe_printf_stderr("\n\n");
+ my_safe_printf_stderr("%s", "\n\n");
}
my_safe_printf_stderr("%s",
"The manual page at "
diff --git a/sql/slave.cc b/sql/slave.cc
index e32d5161d9d..f224de2e1ff 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -1,6 +1,5 @@
-/*
- Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 2008, 2014, Monty Program Ab.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,8 +12,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@@ -27,22 +25,33 @@
replication slave.
*/
-#include "mysql_priv.h"
-
-#include <mysql.h>
-#include <myisam.h>
+#include "sql_priv.h"
+#include "my_global.h"
#include "slave.h"
+#include "sql_parse.h" // execute_init_command
+#include "sql_table.h" // mysql_rm_table
#include "rpl_mi.h"
#include "rpl_rli.h"
#include "sql_repl.h"
#include "rpl_filter.h"
#include "repl_failsafe.h"
+#include "transaction.h"
#include <thr_alarm.h>
#include <my_dir.h>
#include <sql_common.h>
#include <errmsg.h>
#include <mysqld_error.h>
#include <mysys_err.h>
+#include "rpl_handler.h"
+#include <signal.h>
+#include <mysql.h>
+#include <myisam.h>
+
+#include "sql_base.h" // close_thread_tables
+#include "tztime.h" // struct Time_zone
+#include "log_event.h" // Rotate_log_event,
+ // Create_file_log_event,
+ // Format_description_log_event
#ifdef HAVE_REPLICATION
@@ -52,12 +61,14 @@
#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")
#define MAX_SLAVE_RETRY_PAUSE 5
+/*
+ a parameter of sql_slave_killed() to defer the killed status
+*/
+#define SLAVE_WAIT_GROUP_DONE 60
bool use_slave_mask = 0;
MY_BITMAP slave_error_mask;
char slave_skip_error_names[SHOW_VAR_FUNC_BUFF_SIZE];
-typedef bool (*CHECK_KILLED_FUNC)(THD*,void*);
-
char* slave_load_tmpdir = 0;
Master_info *active_mi= 0;
my_bool replicate_same_server_id;
@@ -71,7 +82,8 @@ ulonglong relay_log_space_limit = 0;
*/
int disconnect_slave_event_count = 0, abort_slave_event_count = 0;
-int events_till_abort = -1;
+
+static pthread_key(Master_info*, RPL_MASTER_INFO);
enum enum_slave_reconnect_actions
{
@@ -139,20 +151,45 @@ static int safe_reconnect(THD* thd, MYSQL* mysql, Master_info* mi,
bool suppress_warnings);
static int connect_to_master(THD* thd, MYSQL* mysql, Master_info* mi,
bool reconnect, bool suppress_warnings);
-static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed,
- void* thread_killed_arg);
-static int request_table_dump(MYSQL* mysql, const char* db, const char* table);
-static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
- const char* table_name, bool overwrite);
-static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi);
static Log_event* next_event(Relay_log_info* rli);
static int queue_event(Master_info* mi,const char* buf,ulong event_len);
static int terminate_slave_thread(THD *thd,
- pthread_mutex_t *term_lock,
- pthread_cond_t *term_cond,
+ mysql_mutex_t *term_lock,
+ mysql_cond_t *term_cond,
volatile uint *slave_running,
bool skip_lock);
static bool check_io_slave_killed(THD *thd, Master_info *mi, const char *info);
+/*
+ Function to set the slave's max_allowed_packet based on the value
+ of slave_max_allowed_packet.
+
+ @in_param thd Thread handler for slave
+ @in_param mysql MySQL connection handle
+*/
+
+static void set_slave_max_allowed_packet(THD *thd, MYSQL *mysql)
+{
+ DBUG_ENTER("set_slave_max_allowed_packet");
+ // thd and mysql must be valid
+ DBUG_ASSERT(thd && mysql);
+
+ thd->variables.max_allowed_packet= slave_max_allowed_packet;
+ thd->net.max_packet_size= slave_max_allowed_packet;
+ /*
+ Adding MAX_LOG_EVENT_HEADER_LEN to the max_packet_size on the I/O
+ thread and the mysql->option max_allowed_packet, since a
+ replication event can become this much larger than
+ the corresponding packet (query) sent from client to master.
+ */
+ thd->net.max_packet_size+= MAX_LOG_EVENT_HEADER;
+ /*
+ Skipping the setting of mysql->net.max_packet size to slave
+ max_allowed_packet since this is done during mysql_real_connect.
+ */
+ mysql->options.max_allowed_packet=
+ slave_max_allowed_packet+MAX_LOG_EVENT_HEADER;
+ DBUG_VOID_RETURN;
+}
/*
Find out which replications threads are running
@@ -198,8 +235,8 @@ void lock_slave_threads(Master_info* mi)
DBUG_ENTER("lock_slave_threads");
//TODO: see if we can do this without dual mutex
- pthread_mutex_lock(&mi->run_lock);
- pthread_mutex_lock(&mi->rli.run_lock);
+ mysql_mutex_lock(&mi->run_lock);
+ mysql_mutex_lock(&mi->rli.run_lock);
DBUG_VOID_RETURN;
}
@@ -213,29 +250,58 @@ void unlock_slave_threads(Master_info* mi)
DBUG_ENTER("unlock_slave_threads");
//TODO: see if we can do this without dual mutex
- pthread_mutex_unlock(&mi->rli.run_lock);
- pthread_mutex_unlock(&mi->run_lock);
+ mysql_mutex_unlock(&mi->rli.run_lock);
+ mysql_mutex_unlock(&mi->run_lock);
DBUG_VOID_RETURN;
}
+#ifdef HAVE_PSI_INTERFACE
+static PSI_thread_key key_thread_slave_io, key_thread_slave_sql;
+
+static PSI_thread_info all_slave_threads[]=
+{
+ { &key_thread_slave_io, "slave_io", PSI_FLAG_GLOBAL},
+ { &key_thread_slave_sql, "slave_sql", PSI_FLAG_GLOBAL}
+};
+
+static void init_slave_psi_keys(void)
+{
+ const char* category= "sql";
+ int count;
+
+ if (PSI_server == NULL)
+ return;
+
+ count= array_elements(all_slave_threads);
+ PSI_server->register_thread(category, all_slave_threads, count);
+}
+#endif /* HAVE_PSI_INTERFACE */
/* Initialize slave structures */
int init_slave()
{
DBUG_ENTER("init_slave");
+ int error= 0;
+
+#ifdef HAVE_PSI_INTERFACE
+ init_slave_psi_keys();
+#endif
/*
This is called when mysqld starts. Before client connections are
accepted. However bootstrap may conflict with us if it does START SLAVE.
So it's safer to take the lock.
*/
- pthread_mutex_lock(&LOCK_active_mi);
+ mysql_mutex_lock(&LOCK_active_mi);
/*
TODO: re-write this to interate through the list of files
for multi-master
*/
- active_mi= new Master_info;
+ active_mi= new Master_info(relay_log_recovery);
+
+ if (pthread_key_create(&RPL_MASTER_INFO, NULL))
+ goto err;
/*
If --slave-skip-errors=... was not used, the string value for the
@@ -254,22 +320,21 @@ int init_slave()
if (!active_mi)
{
sql_print_error("Failed to allocate memory for the master info structure");
+ error= 1;
goto err;
}
if (init_master_info(active_mi,master_info_file,relay_log_info_file,
- !master_host, (SLAVE_IO | SLAVE_SQL)))
+ 1, (SLAVE_IO | SLAVE_SQL)))
{
sql_print_error("Failed to initialize the master info structure");
+ error= 1;
goto err;
}
- if (server_id && !master_host && active_mi->host[0])
- master_host= active_mi->host;
-
/* If server id is not set, start_slave_thread() will say it */
- if (master_host && !opt_skip_slave_start)
+ if (active_mi->host[0] && !opt_skip_slave_start)
{
if (start_slave_threads(1 /* need mutex */,
0 /* no wait for start*/,
@@ -279,18 +344,66 @@ int init_slave()
SLAVE_IO | SLAVE_SQL))
{
sql_print_error("Failed to create slave threads");
+ error= 1;
goto err;
}
}
- pthread_mutex_unlock(&LOCK_active_mi);
- DBUG_RETURN(0);
err:
- pthread_mutex_unlock(&LOCK_active_mi);
- DBUG_RETURN(1);
+ mysql_mutex_unlock(&LOCK_active_mi);
+ DBUG_RETURN(error);
}
+/*
+ Updates the master info based on the information stored in the
+ relay info and ignores relay logs previously retrieved by the IO
+ thread, which thus starts fetching again based on to the
+ group_master_log_pos and group_master_log_name. Eventually, the old
+ relay logs will be purged by the normal purge mechanism.
+
+ In the feature, we should improve this routine in order to avoid throwing
+ away logs that are safely stored in the disk. Note also that this recovery
+ routine relies on the correctness of the relay-log.info and only tolerates
+ coordinate problems in master.info.
+
+ In this function, there is no need for a mutex as the caller
+ (i.e. init_slave) already has one acquired.
+
+ Specifically, the following structures are updated:
+
+ 1 - mi->master_log_pos <-- rli->group_master_log_pos
+ 2 - mi->master_log_name <-- rli->group_master_log_name
+ 3 - It moves the relay log to the new relay log file, by
+ rli->group_relay_log_pos <-- BIN_LOG_HEADER_SIZE;
+ rli->event_relay_log_pos <-- BIN_LOG_HEADER_SIZE;
+ rli->group_relay_log_name <-- rli->relay_log.get_log_fname();
+ rli->event_relay_log_name <-- rli->relay_log.get_log_fname();
+
+ If there is an error, it returns (1), otherwise returns (0).
+ */
+int init_recovery(Master_info* mi, const char** errmsg)
+{
+ DBUG_ENTER("init_recovery");
+
+ Relay_log_info *rli= &mi->rli;
+ if (rli->group_master_log_name[0])
+ {
+ mi->master_log_pos= max(BIN_LOG_HEADER_SIZE,
+ rli->group_master_log_pos);
+ strmake_buf(mi->master_log_name, rli->group_master_log_name);
+
+ sql_print_warning("Recovery from master pos %ld and file %s.",
+ (ulong) mi->master_log_pos, mi->master_log_name);
+
+ strmake_buf(rli->group_relay_log_name, rli->relay_log.get_log_fname());
+ strmake_buf(rli->event_relay_log_name, rli->relay_log.get_log_fname());
+
+ rli->group_relay_log_pos= rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE;
+ }
+ DBUG_RETURN(0);
+}
+
/**
Convert slave skip errors bitmap into a printable string.
*/
@@ -308,6 +421,9 @@ static void print_slave_skip_errors(void)
DBUG_ASSERT(sizeof(slave_skip_error_names) > MIN_ROOM);
DBUG_ASSERT(MAX_SLAVE_ERROR <= 999999); // 6 digits
+ /* Make @@slave_skip_errors show the nice human-readable value. */
+ opt_slave_skip_errors= slave_skip_error_names;
+
if (!use_slave_mask || bitmap_is_clear_all(&slave_error_mask))
{
/* purecov: begin tested */
@@ -409,7 +525,8 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock)
if (!mi->inited)
DBUG_RETURN(0); /* successfully do nothing */
int error,force_all = (thread_mask & SLAVE_FORCE_ALL);
- pthread_mutex_t *sql_lock = &mi->rli.run_lock, *io_lock = &mi->run_lock;
+ mysql_mutex_t *sql_lock = &mi->rli.run_lock, *io_lock = &mi->run_lock;
+ mysql_mutex_t *log_lock= mi->rli.relay_log.get_log_lock();
if (thread_mask & (SLAVE_SQL|SLAVE_FORCE_ALL))
{
@@ -421,6 +538,19 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock)
skip_lock)) &&
!force_all)
DBUG_RETURN(error);
+
+ mysql_mutex_lock(log_lock);
+
+ DBUG_PRINT("info",("Flushing relay-log info file."));
+ if (current_thd)
+ thd_proc_info(current_thd, "Flushing relay-log info file.");
+ if (flush_relay_log_info(&mi->rli))
+ DBUG_RETURN(ER_ERROR_DURING_FLUSH_LOGS);
+
+ if (my_sync(mi->rli.info_fd, MYF(MY_WME)))
+ DBUG_RETURN(ER_ERROR_DURING_FLUSH_LOGS);
+
+ mysql_mutex_unlock(log_lock);
}
if (thread_mask & (SLAVE_IO|SLAVE_FORCE_ALL))
{
@@ -432,8 +562,25 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock)
skip_lock)) &&
!force_all)
DBUG_RETURN(error);
+
+ mysql_mutex_lock(log_lock);
+
+ DBUG_PRINT("info",("Flushing relay log and master info file."));
+ if (current_thd)
+ thd_proc_info(current_thd, "Flushing relay log and master info files.");
+ if (flush_master_info(mi, TRUE, FALSE))
+ DBUG_RETURN(ER_ERROR_DURING_FLUSH_LOGS);
+
+ if (mi->rli.relay_log.is_open() &&
+ my_sync(mi->rli.relay_log.get_log_file()->file, MYF(MY_WME)))
+ DBUG_RETURN(ER_ERROR_DURING_FLUSH_LOGS);
+
+ if (my_sync(mi->fd, MYF(MY_WME)))
+ DBUG_RETURN(ER_ERROR_DURING_FLUSH_LOGS);
+
+ mysql_mutex_unlock(log_lock);
}
- DBUG_RETURN(0);
+ DBUG_RETURN(0);
}
@@ -473,19 +620,19 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock)
*/
static int
terminate_slave_thread(THD *thd,
- pthread_mutex_t *term_lock,
- pthread_cond_t *term_cond,
+ mysql_mutex_t *term_lock,
+ mysql_cond_t *term_cond,
volatile uint *slave_running,
bool skip_lock)
{
DBUG_ENTER("terminate_slave_thread");
if (!skip_lock)
{
- pthread_mutex_lock(term_lock);
+ mysql_mutex_lock(term_lock);
}
else
{
- safe_mutex_assert_owner(term_lock);
+ mysql_mutex_assert_owner(term_lock);
}
if (!*slave_running)
{
@@ -495,7 +642,7 @@ terminate_slave_thread(THD *thd,
if run_lock (term_lock) is acquired locally then either
slave_running status is fine
*/
- pthread_mutex_unlock(term_lock);
+ mysql_mutex_unlock(term_lock);
DBUG_RETURN(0);
}
else
@@ -516,18 +663,19 @@ terminate_slave_thread(THD *thd,
int error __attribute__((unused));
DBUG_PRINT("loop", ("killing slave thread"));
- pthread_mutex_lock(&thd->LOCK_thd_kill);
+ mysql_mutex_lock(&thd->LOCK_thd_data);
#ifndef DONT_USE_THR_ALARM
/*
Error codes from pthread_kill are:
EINVAL: invalid signal number (can't happen)
ESRCH: thread already killed (can happen, should be ignored)
*/
- IF_DBUG(int err= ) pthread_kill(thd->real_id, thr_client_alarm);
+ int err __attribute__((unused))= pthread_kill(thd->real_id, thr_client_alarm);
DBUG_ASSERT(err != EINVAL);
#endif
- thd->awake(KILL_CONNECTION);
- pthread_mutex_unlock(&thd->LOCK_thd_kill);
+ thd->awake(NOT_KILLED);
+
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
/*
There is a small chance that slave thread might miss the first
@@ -535,40 +683,44 @@ terminate_slave_thread(THD *thd,
*/
struct timespec abstime;
set_timespec(abstime,2);
- error= pthread_cond_timedwait(term_cond, term_lock, &abstime);
+ error= mysql_cond_timedwait(term_cond, term_lock, &abstime);
DBUG_ASSERT(error == ETIMEDOUT || error == 0);
}
DBUG_ASSERT(*slave_running == 0);
if (!skip_lock)
- pthread_mutex_unlock(term_lock);
+ mysql_mutex_unlock(term_lock);
DBUG_RETURN(0);
}
-int start_slave_thread(pthread_handler h_func, pthread_mutex_t *start_lock,
- pthread_mutex_t *cond_lock,
- pthread_cond_t *start_cond,
+int start_slave_thread(
+#ifdef HAVE_PSI_INTERFACE
+ PSI_thread_key thread_key,
+#endif
+ pthread_handler h_func, mysql_mutex_t *start_lock,
+ mysql_mutex_t *cond_lock,
+ mysql_cond_t *start_cond,
volatile uint *slave_running,
volatile ulong *slave_run_id,
- Master_info* mi,
- bool high_priority)
+ Master_info* mi)
{
pthread_t th;
ulong start_id;
+ int error;
DBUG_ENTER("start_slave_thread");
DBUG_ASSERT(mi->inited);
if (start_lock)
- pthread_mutex_lock(start_lock);
+ mysql_mutex_lock(start_lock);
if (!server_id)
{
if (start_cond)
- pthread_cond_broadcast(start_cond);
+ mysql_cond_broadcast(start_cond);
if (start_lock)
- pthread_mutex_unlock(start_lock);
+ mysql_mutex_unlock(start_lock);
sql_print_error("Server id not set, will not start slave");
DBUG_RETURN(ER_BAD_SLAVE);
}
@@ -576,19 +728,19 @@ int start_slave_thread(pthread_handler h_func, pthread_mutex_t *start_lock,
if (*slave_running)
{
if (start_cond)
- pthread_cond_broadcast(start_cond);
+ mysql_cond_broadcast(start_cond);
if (start_lock)
- pthread_mutex_unlock(start_lock);
+ mysql_mutex_unlock(start_lock);
DBUG_RETURN(ER_SLAVE_MUST_STOP);
}
start_id= *slave_run_id;
DBUG_PRINT("info",("Creating new slave thread"));
- if (high_priority)
- my_pthread_attr_setprio(&connection_attrib,CONNECT_PRIOR);
- if (pthread_create(&th, &connection_attrib, h_func, (void*)mi))
+ if ((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)
- pthread_mutex_unlock(start_lock);
+ mysql_mutex_unlock(start_lock);
DBUG_RETURN(ER_SLAVE_THREAD);
}
if (start_cond && cond_lock) // caller has cond_lock
@@ -597,21 +749,29 @@ int start_slave_thread(pthread_handler h_func, pthread_mutex_t *start_lock,
while (start_id == *slave_run_id)
{
DBUG_PRINT("sleep",("Waiting for slave thread to start"));
- const char* old_msg = thd->enter_cond(start_cond,cond_lock,
- "Waiting for slave thread to start");
- pthread_cond_wait(start_cond, cond_lock);
+ const char *old_msg= thd->enter_cond(start_cond, cond_lock,
+ "Waiting for slave thread to start");
+ /*
+ It is not sufficient to test this at loop bottom. We must test
+ it after registering the mutex in enter_cond(). If the kill
+ happens after testing of thd->killed and before the mutex is
+ registered, we could otherwise go waiting though thd->killed is
+ set.
+ */
+ if (!thd->killed)
+ mysql_cond_wait(start_cond, cond_lock);
thd->exit_cond(old_msg);
- pthread_mutex_lock(cond_lock); // re-acquire it as exit_cond() released
+ mysql_mutex_lock(cond_lock); // re-acquire it as exit_cond() released
if (thd->killed)
{
if (start_lock)
- pthread_mutex_unlock(start_lock);
+ mysql_mutex_unlock(start_lock);
DBUG_RETURN(thd->killed_errno());
}
}
}
if (start_lock)
- pthread_mutex_unlock(start_lock);
+ mysql_mutex_unlock(start_lock);
DBUG_RETURN(0);
}
@@ -629,8 +789,8 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start,
Master_info* mi, const char* master_info_fname,
const char* slave_info_fname, int thread_mask)
{
- pthread_mutex_t *lock_io=0,*lock_sql=0,*lock_cond_io=0,*lock_cond_sql=0;
- pthread_cond_t* cond_io=0,*cond_sql=0;
+ mysql_mutex_t *lock_io=0, *lock_sql=0, *lock_cond_io=0, *lock_cond_sql=0;
+ mysql_cond_t* cond_io=0, *cond_sql=0;
int error=0;
DBUG_ENTER("start_slave_threads");
@@ -648,16 +808,24 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start,
}
if (thread_mask & SLAVE_IO)
- error=start_slave_thread(handle_slave_io,lock_io,lock_cond_io,
- cond_io,
- &mi->slave_running, &mi->slave_run_id,
- mi, 1); //high priority, to read the most possible
+ error= start_slave_thread(
+#ifdef HAVE_PSI_INTERFACE
+ key_thread_slave_io,
+#endif
+ handle_slave_io, lock_io, lock_cond_io,
+ cond_io,
+ &mi->slave_running, &mi->slave_run_id,
+ mi);
if (!error && (thread_mask & SLAVE_SQL))
{
- error=start_slave_thread(handle_slave_sql,lock_sql,lock_cond_sql,
- cond_sql,
- &mi->rli.slave_running, &mi->rli.slave_run_id,
- mi, 0);
+ error= start_slave_thread(
+#ifdef HAVE_PSI_INTERFACE
+ key_thread_slave_sql,
+#endif
+ handle_slave_sql, lock_sql, lock_cond_sql,
+ cond_sql,
+ &mi->rli.slave_running, &mi->rli.slave_run_id,
+ mi);
if (error)
terminate_slave_threads(mi, thread_mask & SLAVE_IO, !need_slave_mutex);
}
@@ -665,17 +833,6 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start,
}
-#ifdef NOT_USED_YET
-static int end_slave_on_walk(Master_info* mi, uchar* /*unused*/)
-{
- DBUG_ENTER("end_slave_on_walk");
-
- end_master_info(mi);
- DBUG_RETURN(0);
-}
-#endif
-
-
/*
Release slave threads at time of executing shutdown.
@@ -694,7 +851,7 @@ void end_slave()
will make us wait until slave threads have started, and START SLAVE
returns, then we terminate them here.
*/
- pthread_mutex_lock(&LOCK_active_mi);
+ mysql_mutex_lock(&LOCK_active_mi);
if (active_mi)
{
/*
@@ -704,7 +861,7 @@ void end_slave()
*/
terminate_slave_threads(active_mi,SLAVE_FORCE_ALL);
}
- pthread_mutex_unlock(&LOCK_active_mi);
+ mysql_mutex_unlock(&LOCK_active_mi);
DBUG_VOID_RETURN;
}
@@ -719,14 +876,14 @@ void end_slave()
*/
void close_active_mi()
{
- pthread_mutex_lock(&LOCK_active_mi);
+ mysql_mutex_lock(&LOCK_active_mi);
if (active_mi)
{
end_master_info(active_mi);
delete active_mi;
active_mi= 0;
}
- pthread_mutex_unlock(&LOCK_active_mi);
+ mysql_mutex_unlock(&LOCK_active_mi);
}
static bool io_slave_killed(THD* thd, Master_info* mi)
@@ -738,9 +895,22 @@ static bool io_slave_killed(THD* thd, Master_info* mi)
DBUG_RETURN(mi->abort_slave || abort_loop || thd->killed);
}
+/**
+ The function analyzes a possible killed status and makes
+ a decision whether to accept it or not.
+ Normally upon accepting the sql thread goes to shutdown.
+ In the event of deffering decision @rli->last_event_start_time waiting
+ timer is set to force the killed status be accepted upon its expiration.
+ @param thd pointer to a THD instance
+ @param rli pointer to Relay_log_info instance
+
+ @return TRUE the killed status is recognized, FALSE a possible killed
+ status is deferred.
+*/
static bool sql_slave_killed(THD* thd, Relay_log_info* rli)
{
+ bool ret= FALSE;
DBUG_ENTER("sql_slave_killed");
DBUG_ASSERT(rli->sql_thd == thd);
@@ -755,36 +925,72 @@ static bool sql_slave_killed(THD* thd, Relay_log_info* rli)
as well.
Example: OPTION_KEEP_LOG is set if a temporary table is created or dropped.
*/
- if (rli->abort_slave && rli->is_in_group() &&
- (thd->transaction.all.modified_non_trans_table ||
- (thd->options & OPTION_KEEP_LOG)))
- DBUG_RETURN(0);
- /*
- If we are in an unsafe situation (stopping could corrupt replication),
- we give one minute to the slave SQL thread of grace before really
- terminating, in the hope that it will be able to read more events and
- the unsafe situation will soon be left. Note that this one minute starts
- from the last time anything happened in the slave SQL thread. So it's
- really one minute of idleness, we don't timeout if the slave SQL thread
- is actively working.
- */
- if (rli->last_event_start_time == 0)
- DBUG_RETURN(1);
- DBUG_PRINT("info", ("Slave SQL thread is in an unsafe situation, giving "
- "it some grace period"));
- if (difftime(time(0), rli->last_event_start_time) > 60)
+ if ((thd->transaction.all.modified_non_trans_table ||
+ (thd->variables.option_bits & OPTION_KEEP_LOG))
+ && rli->is_in_group())
{
- rli->report(ERROR_LEVEL, 0,
- "SQL thread had to stop in an unsafe situation, in "
- "the middle of applying updates to a "
- "non-transactional table without any primary key. "
- "There is a risk of duplicate updates when the slave "
- "SQL thread is restarted. Please check your tables' "
- "contents after restart.");
- DBUG_RETURN(1);
+ char msg_stopped[]=
+ "... Slave SQL Thread stopped with incomplete event group "
+ "having non-transactional changes. "
+ "If the group consists solely of row-based events, you can try "
+ "to restart the slave with --slave-exec-mode=IDEMPOTENT, which "
+ "ignores duplicate key, key not found, and similar errors (see "
+ "documentation for details).";
+
+ if (rli->abort_slave)
+ {
+ DBUG_PRINT("info", ("Request to stop slave SQL Thread received while "
+ "applying a group that has non-transactional "
+ "changes; waiting for completion of the group ... "));
+
+ /*
+ Slave sql thread shutdown in face of unfinished group modified
+ Non-trans table is handled via a timer. The slave may eventually
+ give out to complete the current group and in that case there
+ might be issues at consequent slave restart, see the error message.
+ WL#2975 offers a robust solution requiring to store the last exectuted
+ event's coordinates along with the group's coordianates
+ instead of waiting with @c last_event_start_time the timer.
+ */
+
+ if (rli->last_event_start_time == 0)
+ rli->last_event_start_time= my_time(0);
+ ret= difftime(my_time(0), rli->last_event_start_time) <=
+ SLAVE_WAIT_GROUP_DONE ? FALSE : TRUE;
+
+ DBUG_EXECUTE_IF("stop_slave_middle_group",
+ DBUG_EXECUTE_IF("incomplete_group_in_relay_log",
+ ret= TRUE;);); // time is over
+
+ if (ret == 0)
+ {
+ rli->report(WARNING_LEVEL, 0,
+ "Request to stop slave SQL Thread received while "
+ "applying a group that has non-transactional "
+ "changes; waiting for completion of the group ... ");
+ }
+ else
+ {
+ rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ ER(ER_SLAVE_FATAL_ERROR), msg_stopped);
+ }
+ }
+ else
+ {
+ ret= TRUE;
+ rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, ER(ER_SLAVE_FATAL_ERROR),
+ msg_stopped);
+ }
+ }
+ else
+ {
+ ret= TRUE;
}
}
- DBUG_RETURN(0);
+ if (ret)
+ rli->last_event_start_time= 0;
+
+ DBUG_RETURN(ret);
}
@@ -827,6 +1033,172 @@ const char *print_slave_db_safe(const char* db)
DBUG_RETURN((db ? db : ""));
}
+#endif /* HAVE_REPLICATION */
+
+int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
+ const char *default_val)
+{
+ uint length;
+ DBUG_ENTER("init_strvar_from_file");
+
+ if ((length=my_b_gets(f,var, max_size)))
+ {
+ char* last_p = var + length -1;
+ if (*last_p == '\n')
+ *last_p = 0; // if we stopped on newline, kill it
+ else
+ {
+ /*
+ If we truncated a line or stopped on last char, remove all chars
+ up to and including newline.
+ */
+ int c;
+ while (((c=my_b_get(f)) != '\n' && c != my_b_EOF)) ;
+ }
+ DBUG_RETURN(0);
+ }
+ else if (default_val)
+ {
+ strmake(var, default_val, max_size-1);
+ DBUG_RETURN(0);
+ }
+ DBUG_RETURN(1);
+}
+
+
+/*
+ when moving these functions to mysys, don't forget to
+ remove slave.cc from libmysqld/CMakeLists.txt
+*/
+int init_intvar_from_file(int* var, IO_CACHE* f, int default_val)
+{
+ char buf[32];
+ DBUG_ENTER("init_intvar_from_file");
+
+
+ if (my_b_gets(f, buf, sizeof(buf)))
+ {
+ *var = atoi(buf);
+ DBUG_RETURN(0);
+ }
+ else if (default_val)
+ {
+ *var = default_val;
+ DBUG_RETURN(0);
+ }
+ DBUG_RETURN(1);
+}
+
+int init_floatvar_from_file(float* var, IO_CACHE* f, float default_val)
+{
+ char buf[16];
+ DBUG_ENTER("init_floatvar_from_file");
+
+
+ if (my_b_gets(f, buf, sizeof(buf)))
+ {
+ if (sscanf(buf, "%f", var) != 1)
+ DBUG_RETURN(1);
+ else
+ DBUG_RETURN(0);
+ }
+ else if (default_val != 0.0)
+ {
+ *var = default_val;
+ DBUG_RETURN(0);
+ }
+ DBUG_RETURN(1);
+}
+
+
+/**
+ A master info read method
+
+ This function is called from @c init_master_info() along with
+ relatives to restore some of @c active_mi members.
+ Particularly, this function is responsible for restoring
+ IGNORE_SERVER_IDS list of servers whose events the slave is
+ going to ignore (to not log them in the relay log).
+ Items being read are supposed to be decimal output of values of a
+ type shorter or equal of @c long and separated by the single space.
+
+ @param arr @c DYNAMIC_ARRAY pointer to storage for servers id
+ @param f @c IO_CACHE pointer to the source file
+
+ @retval 0 All OK
+ @retval non-zero An error
+*/
+
+int init_dynarray_intvar_from_file(DYNAMIC_ARRAY* arr, IO_CACHE* f)
+{
+ int ret= 0;
+ char buf[16 * (sizeof(long)*4 + 1)]; // static buffer to use most of times
+ char *buf_act= buf; // actual buffer can be dynamic if static is short
+ char *token, *last;
+ uint num_items; // number of items of `arr'
+ size_t read_size;
+ DBUG_ENTER("init_dynarray_intvar_from_file");
+
+ if ((read_size= my_b_gets(f, buf_act, sizeof(buf))) == 0)
+ {
+ return 0; // no line in master.info
+ }
+ if (read_size + 1 == sizeof(buf) && buf[sizeof(buf) - 2] != '\n')
+ {
+ /*
+ short read happend; allocate sufficient memory and make the 2nd read
+ */
+ char buf_work[(sizeof(long)*3 + 1)*16];
+ memcpy(buf_work, buf, sizeof(buf_work));
+ num_items= atoi(strtok_r(buf_work, " ", &last));
+ size_t snd_size;
+ /*
+ max size lower bound approximate estimation bases on the formula:
+ (the items number + items themselves) *
+ (decimal size + space) - 1 + `\n' + '\0'
+ */
+ size_t max_size= (1 + num_items) * (sizeof(long)*3 + 1) + 1;
+ buf_act= (char*) my_malloc(max_size, MYF(MY_WME));
+ memcpy(buf_act, buf, read_size);
+ snd_size= my_b_gets(f, buf_act + read_size, max_size - read_size);
+ if (snd_size == 0 ||
+ ((snd_size + 1 == max_size - read_size) && buf_act[max_size - 2] != '\n'))
+ {
+ /*
+ failure to make the 2nd read or short read again
+ */
+ ret= 1;
+ goto err;
+ }
+ }
+ token= strtok_r(buf_act, " ", &last);
+ if (token == NULL)
+ {
+ ret= 1;
+ goto err;
+ }
+ num_items= atoi(token);
+ for (uint i=0; i < num_items; i++)
+ {
+ token= strtok_r(NULL, " ", &last);
+ if (token == NULL)
+ {
+ ret= 1;
+ goto err;
+ }
+ else
+ {
+ ulong val= atol(token);
+ insert_dynamic(arr, (uchar *) &val);
+ }
+ }
+err:
+ if (buf_act != buf)
+ my_free(buf_act);
+ DBUG_RETURN(ret);
+}
+
+#ifdef HAVE_REPLICATION
/*
Check if the error is caused by network.
@@ -845,6 +1217,7 @@ bool is_network_error(uint errorno)
errorno == ER_CON_COUNT_ERROR ||
errorno == ER_CONNECTION_KILLED ||
errorno == ER_NEW_ABORTING_CONNECTION ||
+ errorno == ER_NET_READ_INTERRUPTED ||
errorno == ER_SERVER_SHUTDOWN)
return TRUE;
@@ -993,15 +1366,17 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
unavailable (very old master not supporting UNIX_TIMESTAMP()?).
*/
+#ifdef ENABLED_DEBUG_SYNC
DBUG_EXECUTE_IF("dbug.before_get_UNIX_TIMESTAMP",
{
const char act[]=
"now "
"wait_for signal.get_unix_timestamp";
- DBUG_ASSERT(opt_debug_sync_timeout > 0);
+ DBUG_ASSERT(debug_sync_service);
DBUG_ASSERT(!debug_sync_set_action(current_thd,
STRING_WITH_LEN(act)));
};);
+#endif
master_res= NULL;
if (!mysql_real_query(mysql, STRING_WITH_LEN("SELECT UNIX_TIMESTAMP()")) &&
@@ -1011,6 +1386,8 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
mi->clock_diff_with_master=
(long) (time((time_t*) 0) - strtoul(master_row[0], 0, 10));
}
+ else if (check_io_slave_killed(mi->io_thd, mi, NULL))
+ goto slave_killed_err;
else if (is_network_error(mysql_errno(mysql)))
{
mi->report(WARNING_LEVEL, mysql_errno(mysql),
@@ -1041,15 +1418,17 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
Note: we could have put a @@SERVER_ID in the previous SELECT
UNIX_TIMESTAMP() instead, but this would not have worked on 3.23 masters.
*/
+#ifdef ENABLED_DEBUG_SYNC
DBUG_EXECUTE_IF("dbug.before_get_SERVER_ID",
{
const char act[]=
"now "
"wait_for signal.get_server_id";
- DBUG_ASSERT(opt_debug_sync_timeout > 0);
+ DBUG_ASSERT(debug_sync_service);
DBUG_ASSERT(!debug_sync_set_action(current_thd,
STRING_WITH_LEN(act)));
};);
+#endif
master_res= NULL;
master_row= NULL;
if (!mysql_real_query(mysql,
@@ -1057,7 +1436,7 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
(master_res= mysql_store_result(mysql)) &&
(master_row= mysql_fetch_row(master_res)))
{
- if ((::server_id == strtoul(master_row[1], 0, 10)) &&
+ if ((::server_id == (mi->master_id= strtoul(master_row[1], 0, 10))) &&
!mi->rli.replicate_same_server_id)
{
errmsg= "The slave I/O thread stops because master and slave have equal \
@@ -1071,7 +1450,9 @@ not always make sense; please check the manual before using it).";
}
else if (mysql_errno(mysql))
{
- if (is_network_error(mysql_errno(mysql)))
+ if (check_io_slave_killed(mi->io_thd, mi, NULL))
+ goto slave_killed_err;
+ else if (is_network_error(mysql_errno(mysql)))
{
mi->report(WARNING_LEVEL, mysql_errno(mysql),
"Get master SERVER_ID failed with error: %s", mysql_error(mysql));
@@ -1095,6 +1476,13 @@ maybe it is a *VERY OLD MASTER*.");
mysql_free_result(master_res);
master_res= NULL;
}
+ if (mi->master_id == 0 && mi->ignore_server_ids.elements > 0)
+ {
+ errmsg= "Slave configured with server id filtering could not detect the master server id.";
+ err_code= ER_SLAVE_FATAL_ERROR;
+ sprintf(err_buff, ER(err_code), errmsg);
+ goto err;
+ }
/*
Check that the master's global character_set_server and ours are the same.
@@ -1135,6 +1523,8 @@ be equal for the Statement-format replication to work";
goto err;
}
}
+ else if (check_io_slave_killed(mi->io_thd, mi, NULL))
+ goto slave_killed_err;
else if (is_network_error(mysql_errno(mysql)))
{
mi->report(WARNING_LEVEL, mysql_errno(mysql),
@@ -1196,6 +1586,8 @@ be equal for the Statement-format replication to work";
goto err;
}
}
+ else if (check_io_slave_killed(mi->io_thd, mi, NULL))
+ goto slave_killed_err;
else if (is_network_error(err_code= mysql_errno(mysql)))
{
mi->report(ERROR_LEVEL, err_code,
@@ -1227,6 +1619,50 @@ when it try to get the value of TIME_ZONE global variable from master.";
}
}
+ if (mi->heartbeat_period != 0.0)
+ {
+ char llbuf[22];
+ const char query_format[]= "SET @master_heartbeat_period= %s";
+ char query[sizeof(query_format) - 2 + sizeof(llbuf)];
+ /*
+ the period is an ulonglong of nano-secs.
+ */
+ llstr((ulonglong) (mi->heartbeat_period*1000000000UL), llbuf);
+ sprintf(query, query_format, llbuf);
+
+ DBUG_EXECUTE_IF("simulate_slave_heartbeat_network_error",
+ { static ulong dbug_count= 0;
+ if (++dbug_count < 3)
+ goto heartbeat_network_error;
+ });
+ if (mysql_real_query(mysql, query, strlen(query)))
+ {
+ if (check_io_slave_killed(mi->io_thd, mi, NULL))
+ goto slave_killed_err;
+
+ if (is_network_error(mysql_errno(mysql)))
+ {
+ IF_DBUG(heartbeat_network_error: , )
+ mi->report(WARNING_LEVEL, mysql_errno(mysql),
+ "SET @master_heartbeat_period to master failed with error: %s",
+ mysql_error(mysql));
+ mysql_free_result(mysql_store_result(mysql));
+ goto network_err;
+ }
+ else
+ {
+ /* Fatal error */
+ errmsg= "The slave I/O thread stops because a fatal error is encountered "
+ "when it tries to SET @master_heartbeat_period on master.";
+ err_code= ER_SLAVE_FATAL_ERROR;
+ sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql));
+ mysql_free_result(mysql_store_result(mysql));
+ goto err;
+ }
+ }
+ mysql_free_result(mysql_store_result(mysql));
+ }
+
/*
Querying if master is capable to checksum and notifying it about own
CRC-awareness. The master's side instant value of @@global.binlog_checksum
@@ -1255,10 +1691,14 @@ when it try to get the value of TIME_ZONE global variable from master.";
if (mysql_errno(mysql) == ER_UNKNOWN_SYSTEM_VARIABLE)
{
- // this is tolerable as OM -> NS is supported
- mi->report(WARNING_LEVEL, mysql_errno(mysql),
- "Notifying master by %s failed with "
- "error: %s", query, mysql_error(mysql));
+ /* Ignore this expected error if not a high error level */
+ if (global_system_variables.log_warnings > 1)
+ {
+ // this is tolerable as OM -> NS is supported
+ mi->report(WARNING_LEVEL, mysql_errno(mysql),
+ "Notifying master by %s failed with "
+ "error: %s", query, mysql_error(mysql));
+ }
}
else
{
@@ -1324,6 +1764,49 @@ when it try to get the value of TIME_ZONE global variable from master.";
#ifndef DBUG_OFF
past_checksum:
#endif
+
+ /*
+ Request the master to filter away events with the @@skip_replication flag
+ set, if we are running with
+ --replicate-events-marked-for-skip=FILTER_ON_MASTER.
+ */
+ if (opt_replicate_events_marked_for_skip == RPL_SKIP_FILTER_ON_MASTER)
+ {
+ if (mysql_real_query(mysql, STRING_WITH_LEN("SET skip_replication=1")))
+ {
+ err_code= mysql_errno(mysql);
+ if (is_network_error(err_code))
+ {
+ mi->report(ERROR_LEVEL, err_code,
+ "Setting master-side filtering of @@skip_replication failed "
+ "with error: %s", mysql_error(mysql));
+ goto network_err;
+ }
+ else if (err_code == ER_UNKNOWN_SYSTEM_VARIABLE)
+ {
+ /*
+ The master is older than the slave and does not support the
+ @@skip_replication feature.
+ This is not a problem, as such master will not generate events with
+ the @@skip_replication flag set in the first place. We will still
+ do slave-side filtering of such events though, to handle the (rare)
+ case of downgrading a master and receiving old events generated from
+ before the downgrade with the @@skip_replication flag set.
+ */
+ DBUG_PRINT("info", ("Old master does not support master-side filtering "
+ "of @@skip_replication events."));
+ }
+ else
+ {
+ /* Fatal error */
+ errmsg= "The slave I/O thread stops because a fatal error is "
+ "encountered when it tries to request filtering of events marked "
+ "with the @@skip_replication flag.";
+ sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql));
+ goto err;
+ }
+ }
+ }
err:
if (errmsg)
{
@@ -1347,198 +1830,6 @@ slave_killed_err:
DBUG_RETURN(2);
}
-/*
- Used by fetch_master_table (used by LOAD TABLE tblname FROM MASTER and LOAD
- DATA FROM MASTER). Drops the table (if 'overwrite' is true) and recreates it
- from the dump. Honours replication inclusion/exclusion rules.
- db must be non-zero (guarded by assertion).
-
- RETURN VALUES
- 0 success
- 1 error
-*/
-
-static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
- const char* table_name, bool overwrite)
-{
- ulong packet_len;
- char *query, *save_db;
- uint32 save_db_length;
- Vio* save_vio;
- HA_CHECK_OPT check_opt;
- TABLE_LIST tables;
- int error= 1;
- handler *file;
- ulonglong save_options;
- NET *net= &mysql->net;
- const char *found_semicolon= NULL;
- DBUG_ENTER("create_table_from_dump");
-
- packet_len= my_net_read(net); // read create table statement
- if (packet_len == packet_error)
- {
- my_message(ER_MASTER_NET_READ, ER(ER_MASTER_NET_READ), MYF(0));
- DBUG_RETURN(1);
- }
- if (net->read_pos[0] == 255) // error from master
- {
- char *err_msg;
- err_msg= (char*) net->read_pos + ((mysql->server_capabilities &
- CLIENT_PROTOCOL_41) ?
- 3+SQLSTATE_LENGTH+1 : 3);
- my_error(ER_MASTER, MYF(0), err_msg);
- DBUG_RETURN(1);
- }
- thd->command = COM_TABLE_DUMP;
- if (!(query = thd->strmake((char*) net->read_pos, packet_len)))
- {
- sql_print_error("create_table_from_dump: out of memory");
- my_message(ER_GET_ERRNO, "Out of memory", MYF(0));
- DBUG_RETURN(1);
- }
- thd->set_query(query, packet_len);
- thd->is_slave_error = 0;
-
- bzero((char*) &tables,sizeof(tables));
- tables.db = (char*)db;
- tables.alias= tables.table_name= (char*)table_name;
-
- /* Drop the table if 'overwrite' is true */
- if (overwrite)
- {
- if (mysql_rm_table(thd,&tables,1,0)) /* drop if exists */
- {
- sql_print_error("create_table_from_dump: failed to drop the table");
- goto err;
- }
- else
- {
- /* Clear the OK result of mysql_rm_table(). */
- thd->main_da.reset_diagnostics_area();
- }
- }
-
- /* Create the table. We do not want to log the "create table" statement */
- save_options = thd->options;
- thd->options &= ~ (OPTION_BIN_LOG);
- thd_proc_info(thd, "Creating table from master dump");
- // save old db in case we are creating in a different database
- save_db = thd->db;
- save_db_length= thd->db_length;
- thd->db = (char*)db;
- DBUG_ASSERT(thd->db != 0);
- thd->db_length= strlen(thd->db);
- /* run create table */
- mysql_parse(thd, thd->query(), packet_len, &found_semicolon);
- thd->db = save_db; // leave things the way the were before
- thd->db_length= save_db_length;
- thd->options = save_options;
-
- if (thd->is_slave_error)
- goto err; // mysql_parse took care of the error send
-
- thd_proc_info(thd, "Opening master dump table");
- thd->main_da.reset_diagnostics_area(); /* cleanup from CREATE_TABLE */
- /*
- Note: If this function starts to fail for MERGE tables,
- change the next two lines to these:
- tables.table= NULL; // was set by mysql_rm_table()
- if (!open_n_lock_single_table(thd, &tables, TL_WRITE))
- */
- tables.lock_type = TL_WRITE;
- if (!open_ltable(thd, &tables, TL_WRITE, 0))
- {
- sql_print_error("create_table_from_dump: could not open created table");
- goto err;
- }
-
- file = tables.table->file;
- thd_proc_info(thd, "Reading master dump table data");
- /* Copy the data file */
- if (file->net_read_dump(net))
- {
- my_message(ER_MASTER_NET_READ, ER(ER_MASTER_NET_READ), MYF(0));
- sql_print_error("create_table_from_dump: failed in\
- handler::net_read_dump()");
- goto err;
- }
-
- check_opt.init();
- check_opt.flags|= T_VERY_SILENT | T_CALC_CHECKSUM | T_QUICK;
- thd_proc_info(thd, "Rebuilding the index on master dump table");
- /*
- We do not want repair() to spam us with messages
- just send them to the error log, and report the failure in case of
- problems.
- */
- save_vio = thd->net.vio;
- thd->net.vio = 0;
- /* Rebuild the index file from the copied data file (with REPAIR) */
- error=file->ha_repair(thd,&check_opt) != 0;
- thd->net.vio = save_vio;
- if (error)
- my_error(ER_INDEX_REBUILD, MYF(0), tables.table->s->table_name.str);
-
-err:
- close_thread_tables(thd);
- DBUG_RETURN(error);
-}
-
-
-int fetch_master_table(THD *thd, const char *db_name, const char *table_name,
- Master_info *mi, MYSQL *mysql, bool overwrite)
-{
- int error= 1;
- const char *errmsg=0;
- bool called_connected= (mysql != NULL);
- DBUG_ENTER("fetch_master_table");
- DBUG_PRINT("enter", ("db_name: '%s' table_name: '%s'",
- db_name,table_name));
-
- if (!called_connected)
- {
- if (!(mysql = mysql_init(NULL)))
- {
- DBUG_RETURN(1);
- }
- if (connect_to_master(thd, mysql, mi))
- {
- my_error(ER_CONNECT_TO_MASTER, MYF(0), mysql_error(mysql));
- /*
- We need to clear the active VIO since, theoretically, somebody
- might issue an awake() on this thread. If we are then in the
- middle of closing and destroying the VIO inside the
- mysql_close(), we will have a problem.
- */
-#ifdef SIGNAL_WITH_VIO_CLOSE
- thd->clear_active_vio();
-#endif
- mysql_close(mysql);
- DBUG_RETURN(1);
- }
- if (thd->killed)
- goto err;
- }
-
- if (request_table_dump(mysql, db_name, table_name))
- {
- error= ER_UNKNOWN_ERROR;
- errmsg= "Failed on table dump request";
- goto err;
- }
- if (create_table_from_dump(thd, mysql, db_name,
- table_name, overwrite))
- goto err; // create_table_from_dump have sent the error already
- error = 0;
-
- err:
- if (!called_connected)
- mysql_close(mysql);
- if (errmsg && thd->vio_ok())
- my_message(error, errmsg, MYF(0));
- DBUG_RETURN(test(error)); // Return 1 on error
-}
-
static bool wait_for_relay_log_space(Relay_log_info* rli)
{
@@ -1548,7 +1839,7 @@ static bool wait_for_relay_log_space(Relay_log_info* rli)
THD* thd = mi->io_thd;
DBUG_ENTER("wait_for_relay_log_space");
- pthread_mutex_lock(&rli->log_space_lock);
+ mysql_mutex_lock(&rli->log_space_lock);
save_proc_info= thd->enter_cond(&rli->log_space_cond,
&rli->log_space_lock,
"\
@@ -1556,7 +1847,7 @@ Waiting for the slave SQL thread to free enough relay log space");
while (rli->log_space_limit < rli->log_space_total &&
!(slave_killed=io_slave_killed(thd,mi)) &&
!rli->ignore_log_space_limit)
- pthread_cond_wait(&rli->log_space_cond, &rli->log_space_lock);
+ mysql_cond_wait(&rli->log_space_cond, &rli->log_space_lock);
/*
Makes the IO thread read only one event at a time
@@ -1598,9 +1889,9 @@ Waiting for the slave SQL thread to free enough relay log space");
#endif
if (rli->sql_force_rotate_relay)
{
- pthread_mutex_lock(&active_mi->data_lock);
+ mysql_mutex_lock(&active_mi->data_lock);
rotate_relay_log(rli->mi);
- pthread_mutex_unlock(&active_mi->data_lock);
+ mysql_mutex_unlock(&active_mi->data_lock);
rli->sql_force_rotate_relay= false;
}
@@ -1628,11 +1919,11 @@ Waiting for the slave SQL thread to free enough relay log space");
static void write_ignored_events_info_to_relay_log(THD *thd, Master_info *mi)
{
Relay_log_info *rli= &mi->rli;
- pthread_mutex_t *log_lock= rli->relay_log.get_log_lock();
+ mysql_mutex_t *log_lock= rli->relay_log.get_log_lock();
DBUG_ENTER("write_ignored_events_info_to_relay_log");
DBUG_ASSERT(thd == mi->io_thd);
- pthread_mutex_lock(log_lock);
+ mysql_mutex_lock(log_lock);
if (rli->ign_master_log_name_end[0])
{
DBUG_PRINT("info",("writing a Rotate event to track down ignored events"));
@@ -1641,7 +1932,7 @@ static void write_ignored_events_info_to_relay_log(THD *thd, Master_info *mi)
Rotate_log_event::DUP_NAME);
rli->ign_master_log_name_end[0]= 0;
/* can unlock before writing as slave SQL thd will soon see our Rotate */
- pthread_mutex_unlock(log_lock);
+ mysql_mutex_unlock(log_lock);
if (likely((bool)ev))
{
ev->server_id= 0; // don't be ignored by slave SQL thread
@@ -1663,7 +1954,7 @@ static void write_ignored_events_info_to_relay_log(THD *thd, Master_info *mi)
" SHOW SLAVE STATUS may be inaccurate");
}
else
- pthread_mutex_unlock(log_lock);
+ mysql_mutex_unlock(log_lock);
DBUG_VOID_RETURN;
}
@@ -1672,28 +1963,54 @@ int register_slave_on_master(MYSQL* mysql, Master_info *mi,
bool *suppress_warnings)
{
uchar buf[1024], *pos= buf;
- uint report_host_len, report_user_len=0, report_password_len=0;
+ uint report_host_len=0, report_user_len=0, report_password_len=0;
DBUG_ENTER("register_slave_on_master");
*suppress_warnings= FALSE;
- if (!report_host)
+ if (report_host)
+ report_host_len= strlen(report_host);
+ if (report_host_len > HOSTNAME_LENGTH)
+ {
+ sql_print_warning("The length of report_host is %d. "
+ "It is larger than the max length(%d), so this "
+ "slave cannot be registered to the master.",
+ report_host_len, HOSTNAME_LENGTH);
DBUG_RETURN(0);
- report_host_len= strlen(report_host);
+ }
+
if (report_user)
report_user_len= strlen(report_user);
+ if (report_user_len > USERNAME_LENGTH)
+ {
+ sql_print_warning("The length of report_user is %d. "
+ "It is larger than the max length(%d), so this "
+ "slave cannot be registered to the master.",
+ report_user_len, USERNAME_LENGTH);
+ DBUG_RETURN(0);
+ }
+
if (report_password)
report_password_len= strlen(report_password);
- /* 30 is a good safety margin */
- if (report_host_len + report_user_len + report_password_len + 30 >
- sizeof(buf))
- DBUG_RETURN(0); // safety
+ if (report_password_len > MAX_PASSWORD_LENGTH)
+ {
+ sql_print_warning("The length of report_password is %d. "
+ "It is larger than the max length(%d), so this "
+ "slave cannot be registered to the master.",
+ report_password_len, MAX_PASSWORD_LENGTH);
+ DBUG_RETURN(0);
+ }
int4store(pos, server_id); pos+= 4;
pos= net_store_data(pos, (uchar*) report_host, report_host_len);
pos= net_store_data(pos, (uchar*) report_user, report_user_len);
pos= net_store_data(pos, (uchar*) report_password, report_password_len);
int2store(pos, (uint16) report_port); pos+= 2;
- int4store(pos, rpl_recovery_rank); pos+= 4;
+ /*
+ Fake rpl_recovery_rank, which was removed in BUG#13963,
+ so that this server can register itself on old servers,
+ see BUG#49259.
+ */
+ int4store(pos, /* rpl_recovery_rank */ 0); pos+= 4;
/* The master will fill in master_id */
int4store(pos, 0); pos+= 4;
@@ -1795,8 +2112,12 @@ bool show_master_info(THD* thd, Master_info* mi)
field_list.push_back(new Item_empty_string("Last_IO_Error", 20));
field_list.push_back(new Item_return_int("Last_SQL_Errno", 4, MYSQL_TYPE_LONG));
field_list.push_back(new Item_empty_string("Last_SQL_Error", 20));
+ field_list.push_back(new Item_empty_string("Replicate_Ignore_Server_Ids",
+ FN_REFLEN));
+ field_list.push_back(new Item_return_int("Master_Server_Id", sizeof(ulong),
+ MYSQL_TYPE_LONG));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
@@ -1810,14 +2131,14 @@ bool show_master_info(THD* thd, Master_info* mi)
slave_running can be accessed without run_lock but not other
non-volotile members like mi->io_thd, which is guarded by the mutex.
*/
- pthread_mutex_lock(&mi->run_lock);
+ mysql_mutex_lock(&mi->run_lock);
protocol->store(mi->io_thd ? mi->io_thd->proc_info : "", &my_charset_bin);
- pthread_mutex_unlock(&mi->run_lock);
+ mysql_mutex_unlock(&mi->run_lock);
- pthread_mutex_lock(&mi->data_lock);
- pthread_mutex_lock(&mi->rli.data_lock);
- pthread_mutex_lock(&mi->err_lock);
- pthread_mutex_lock(&mi->rli.err_lock);
+ mysql_mutex_lock(&mi->data_lock);
+ mysql_mutex_lock(&mi->rli.data_lock);
+ mysql_mutex_lock(&mi->err_lock);
+ mysql_mutex_lock(&mi->rli.err_lock);
protocol->store(mi->host, &my_charset_bin);
protocol->store(mi->user, &my_charset_bin);
protocol->store((uint32) mi->port);
@@ -1830,7 +2151,8 @@ bool show_master_info(THD* thd, Master_info* mi)
protocol->store((ulonglong) mi->rli.group_relay_log_pos);
protocol->store(mi->rli.group_master_log_name, &my_charset_bin);
protocol->store(mi->slave_running == MYSQL_SLAVE_RUN_CONNECT ?
- "Yes" : "No", &my_charset_bin);
+ "Yes" : (mi->slave_running == MYSQL_SLAVE_RUN_NOT_CONNECT ?
+ "Connecting" : "No"), &my_charset_bin);
protocol->store(mi->rli.slave_running ? "Yes":"No", &my_charset_bin);
protocol->store(rpl_filter->get_do_db());
protocol->store(rpl_filter->get_ignore_db());
@@ -1916,11 +2238,37 @@ bool show_master_info(THD* thd, Master_info* mi)
protocol->store(mi->rli.last_error().number);
// Last_SQL_Error
protocol->store(mi->rli.last_error().message, &my_charset_bin);
+ // Replicate_Ignore_Server_Ids
+ {
+ char buff[FN_REFLEN];
+ ulong i, cur_len;
+ for (i= 0, buff[0]= 0, cur_len= 0;
+ i < mi->ignore_server_ids.elements; i++)
+ {
+ ulong s_id, slen;
+ char sbuff[FN_REFLEN];
+ get_dynamic(&mi->ignore_server_ids, (uchar*) &s_id, i);
+ slen= sprintf(sbuff, (i==0? "%lu" : ", %lu"), s_id);
+ if (cur_len + slen + 4 > FN_REFLEN)
+ {
+ /*
+ break the loop whenever remained space could not fit
+ ellipses on the next cycle
+ */
+ sprintf(buff + cur_len, "...");
+ break;
+ }
+ cur_len += sprintf(buff + cur_len, "%s", sbuff);
+ }
+ protocol->store(buff, &my_charset_bin);
+ }
+ // Master_Server_id
+ protocol->store((uint32) mi->master_id);
- pthread_mutex_unlock(&mi->rli.err_lock);
- pthread_mutex_unlock(&mi->err_lock);
- pthread_mutex_unlock(&mi->rli.data_lock);
- pthread_mutex_unlock(&mi->data_lock);
+ mysql_mutex_unlock(&mi->rli.err_lock);
+ mysql_mutex_unlock(&mi->err_lock);
+ mysql_mutex_unlock(&mi->rli.data_lock);
+ mysql_mutex_unlock(&mi->data_lock);
if (my_net_write(&thd->net, (uchar*) thd->packet.ptr(), packet->length()))
DBUG_RETURN(TRUE);
@@ -1942,12 +2290,12 @@ void set_slave_thread_options(THD* thd)
when max_join_size is 4G, OPTION_BIG_SELECTS is automatically set, but
only for client threads.
*/
- ulonglong options= thd->options | OPTION_BIG_SELECTS;
+ ulonglong options= thd->variables.option_bits | OPTION_BIG_SELECTS;
if (opt_log_slave_updates)
options|= OPTION_BIN_LOG;
else
options&= ~OPTION_BIN_LOG;
- thd->options= options;
+ thd->variables.option_bits= options;
thd->variables.completion_type= 0;
DBUG_VOID_RETURN;
}
@@ -1988,83 +2336,81 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
SYSTEM_THREAD_SLAVE_SQL : SYSTEM_THREAD_SLAVE_IO;
thd->security_ctx->skip_grants();
my_net_init(&thd->net, 0);
-/*
- Adding MAX_LOG_EVENT_HEADER_LEN to the max_allowed_packet on all
- slave threads, since a replication event can become this much larger
- than the corresponding packet (query) sent from client to master.
-*/
- thd->variables.max_allowed_packet= slave_max_allowed_packet;
thd->slave_thread = 1;
thd->enable_slow_log= opt_log_slow_slave_statements;
thd->variables.log_slow_filter= global_system_variables.log_slow_filter;
set_slave_thread_options(thd);
thd->client_capabilities = CLIENT_LOCAL_FILES;
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
DBUG_EXECUTE_IF("simulate_io_slave_error_on_init",
simulate_error|= (1 << SLAVE_THD_IO););
DBUG_EXECUTE_IF("simulate_sql_slave_error_on_init",
simulate_error|= (1 << SLAVE_THD_SQL););
-#if !defined(DBUG_OFF)
- if (init_thr_lock() || thd->store_globals() || simulate_error & (1<< thd_type))
-#else
- if (init_thr_lock() || thd->store_globals())
-#endif
+ if (init_thr_lock() || thd->store_globals() ||
+ IF_DBUG(simulate_error & (1<< thd_type), 0))
{
thd->cleanup();
DBUG_RETURN(-1);
}
- lex_start(thd);
if (thd_type == SLAVE_THD_SQL)
thd_proc_info(thd, "Waiting for the next event in relay log");
else
thd_proc_info(thd, "Waiting for master update");
- thd->version=refresh_version;
thd->set_time();
+ /* Do not use user-supplied timeout value for system threads. */
+ thd->variables.lock_wait_timeout= LONG_TIMEOUT;
DBUG_RETURN(0);
}
+/*
+ Sleep for a given amount of time or until killed.
+
+ @param thd Thread context of the current thread.
+ @param seconds The number of seconds to sleep.
+ @param func Function object to check if the thread has been killed.
+ @param info The Rpl_info object associated with this sleep.
-static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed,
- void* thread_killed_arg)
+ @retval True if the thread has been killed, false otherwise.
+*/
+template <typename killed_func, typename rpl_info>
+static inline bool slave_sleep(THD *thd, time_t seconds,
+ killed_func func, rpl_info info)
{
- int nap_time;
- thr_alarm_t alarmed;
- DBUG_ENTER("safe_sleep");
- thr_alarm_init(&alarmed);
- time_t start_time= my_time(0);
- time_t end_time= start_time+sec;
+ bool ret;
+ struct timespec abstime;
+ const char *old_proc_info;
- while ((nap_time= (int) (end_time - start_time)) > 0)
- {
- ALARM alarm_buff;
- /*
- The only reason we are asking for alarm is so that
- we will be woken up in case of murder, so if we do not get killed,
- set the alarm so it goes off after we wake up naturally
- */
- thr_alarm(&alarmed, 2 * nap_time, &alarm_buff);
- sleep(nap_time);
- thr_end_alarm(&alarmed);
+ mysql_mutex_t *lock= &info->sleep_lock;
+ mysql_cond_t *cond= &info->sleep_cond;
- if ((*thread_killed)(thd,thread_killed_arg))
- DBUG_RETURN(1);
- start_time= my_time(0);
+ /* Absolute system time at which the sleep time expires. */
+ set_timespec(abstime, seconds);
+ mysql_mutex_lock(lock);
+ old_proc_info= thd->enter_cond(cond, lock, thd->proc_info);
+
+ while (! (ret= func(thd, info)))
+ {
+ int error= mysql_cond_timedwait(cond, lock, &abstime);
+ if (error == ETIMEDOUT || error == ETIME)
+ break;
}
- DBUG_RETURN(0);
+ /* Implicitly unlocks the mutex. */
+ thd->exit_cond(old_proc_info);
+ return ret;
}
-static int request_dump(MYSQL* mysql, Master_info* mi,
- bool *suppress_warnings)
+static int request_dump(THD *thd, MYSQL* mysql, Master_info* mi,
+ bool *suppress_warnings)
{
uchar buf[FN_REFLEN + 10];
int len;
- int binlog_flags = 0; // for now
+ ushort binlog_flags = 0; // for now
char* logname = mi->master_log_name;
DBUG_ENTER("request_dump");
@@ -2073,6 +2419,11 @@ static int request_dump(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)))
+ DBUG_RETURN(1);
+
// TODO if big log files: Change next to int8store()
int4store(buf, (ulong) mi->master_log_pos);
int2store(buf + 4, binlog_flags);
@@ -2091,37 +2442,7 @@ static int request_dump(MYSQL* mysql, Master_info* mi,
else
sql_print_error("Error on COM_BINLOG_DUMP: %d %s, will retry in %d secs",
mysql_errno(mysql), mysql_error(mysql),
- master_connect_retry);
- DBUG_RETURN(1);
- }
-
- DBUG_RETURN(0);
-}
-
-
-static int request_table_dump(MYSQL* mysql, const char* db, const char* table)
-{
- uchar buf[1024], *p = buf;
- DBUG_ENTER("request_table_dump");
-
- uint table_len = (uint) strlen(table);
- uint db_len = (uint) strlen(db);
- if (table_len + db_len > sizeof(buf) - 2)
- {
- sql_print_error("request_table_dump: Buffer overrun");
- DBUG_RETURN(1);
- }
-
- *p++ = db_len;
- memcpy(p, db, db_len);
- p += db_len;
- *p++ = table_len;
- memcpy(p, table, table_len);
-
- if (simple_command(mysql, COM_TABLE_DUMP, buf, p - buf + table_len, 1))
- {
- sql_print_error("request_table_dump: Error sending the table dump \
-command");
+ mi->connect_retry);
DBUG_RETURN(1);
}
@@ -2205,7 +2526,7 @@ static int has_temporary_error(THD *thd)
DBUG_ENTER("has_temporary_error");
DBUG_EXECUTE_IF("all_errors_are_temporary_errors",
- if (thd->main_da.is_error())
+ if (thd->stmt_da->is_error())
{
thd->clear_error();
my_error(ER_LOCK_DEADLOCK, MYF(0));
@@ -2224,20 +2545,21 @@ static int has_temporary_error(THD *thd)
currently, InnoDB deadlock detected by InnoDB or lock
wait timeout (innodb_lock_wait_timeout exceeded
*/
- if (thd->main_da.sql_errno() == ER_LOCK_DEADLOCK ||
- thd->main_da.sql_errno() == ER_LOCK_WAIT_TIMEOUT)
+ if (thd->stmt_da->sql_errno() == ER_LOCK_DEADLOCK ||
+ thd->stmt_da->sql_errno() == ER_LOCK_WAIT_TIMEOUT)
DBUG_RETURN(1);
#ifdef HAVE_NDB_BINLOG
/*
currently temporary error set in ndbcluster
*/
- List_iterator_fast<MYSQL_ERROR> it(thd->warn_list);
+ List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
MYSQL_ERROR *err;
while ((err= it++))
{
- DBUG_PRINT("info", ("has warning %d %s", err->code, err->msg));
- switch (err->code)
+ DBUG_PRINT("info", ("has condition %d %s", err->get_sql_errno(),
+ err->get_message_text()));
+ switch (err->get_sql_errno())
{
case ER_GET_TEMPORARY_ERRMSG:
DBUG_RETURN(1);
@@ -2286,8 +2608,8 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli)
ev->get_type_str(), ev->get_type_code(),
ev->server_id));
DBUG_PRINT("info", ("thd->options: %s%s; rli->last_event_start_time: %lu",
- FLAGSTR(thd->options, OPTION_NOT_AUTOCOMMIT),
- FLAGSTR(thd->options, OPTION_BEGIN),
+ FLAGSTR(thd->variables.option_bits, OPTION_NOT_AUTOCOMMIT),
+ FLAGSTR(thd->variables.option_bits, OPTION_BEGIN),
(ulong) rli->last_event_start_time));
/*
@@ -2323,12 +2645,15 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli)
ev->when= hrtime_to_my_time(hrtime);
ev->when_sec_part= hrtime_sec_part(hrtime);
}
+ thd->variables.option_bits=
+ (thd->variables.option_bits & ~OPTION_SKIP_REPLICATION) |
+ (ev->flags & LOG_EVENT_SKIP_REPLICATION_F ? OPTION_SKIP_REPLICATION : 0);
ev->thd = thd; // because up to this point, ev->thd == 0
int reason= ev->shall_skip(rli);
if (reason == Log_event::EVENT_SKIP_COUNT)
- --rli->slave_skip_counter;
- pthread_mutex_unlock(&rli->data_lock);
+ sql_slave_skip_counter= --rli->slave_skip_counter;
+ mysql_mutex_unlock(&rli->data_lock);
if (reason == Log_event::EVENT_SKIP_NOT)
exec_res= ev->apply_event(rli);
@@ -2347,7 +2672,7 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli)
"skipped because event skip counter was non-zero"
};
DBUG_PRINT("info", ("OPTION_BEGIN: %d; IN_STMT: %d",
- thd->options & OPTION_BEGIN ? 1 : 0,
+ test(thd->variables.option_bits & OPTION_BEGIN),
rli->get_flag(Relay_log_info::IN_STMT)));
DBUG_PRINT("skip_event", ("%s event was %s",
ev->get_type_str(), explain[reason]));
@@ -2434,7 +2759,7 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
event execution. But we will release it in places where we will
wait for something for example inside of next_event().
*/
- pthread_mutex_lock(&rli->data_lock);
+ mysql_mutex_lock(&rli->data_lock);
Log_event * ev = next_event(rli);
@@ -2442,7 +2767,7 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
if (sql_slave_killed(thd,rli))
{
- pthread_mutex_unlock(&rli->data_lock);
+ mysql_mutex_unlock(&rli->data_lock);
delete ev;
DBUG_RETURN(1);
}
@@ -2465,10 +2790,31 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
error in query execution to be printed.
*/
rli->abort_slave= 1;
- pthread_mutex_unlock(&rli->data_lock);
+ mysql_mutex_unlock(&rli->data_lock);
delete ev;
DBUG_RETURN(1);
}
+
+ { /**
+ The following failure injecion works in cooperation with tests
+ setting @@global.debug= 'd,incomplete_group_in_relay_log'.
+ Xid or Commit events are not executed to force the slave sql
+ read hanging if the realy log does not have any more events.
+ */
+ DBUG_EXECUTE_IF("incomplete_group_in_relay_log",
+ if ((ev->get_type_code() == XID_EVENT) ||
+ ((ev->get_type_code() == QUERY_EVENT) &&
+ strcmp("COMMIT", ((Query_log_event *) ev)->query) == 0))
+ {
+ DBUG_ASSERT(thd->transaction.all.modified_non_trans_table);
+ rli->abort_slave= 1;
+ mysql_mutex_unlock(&rli->data_lock);
+ delete ev;
+ rli->inc_event_relay_log_pos();
+ DBUG_RETURN(0);
+ };);
+ }
+
exec_res= apply_event_and_update_pos(ev, thd, rli);
switch (ev->get_type_code()) {
@@ -2550,12 +2896,12 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
exec_res= 0;
rli->cleanup_context(thd, 1);
/* chance for concurrent connection to get more locks */
- safe_sleep(thd, min(rli->trans_retries, MAX_SLAVE_RETRY_PAUSE),
- (CHECK_KILLED_FUNC)sql_slave_killed, (void*)rli);
- pthread_mutex_lock(&rli->data_lock); // because of SHOW STATUS
+ slave_sleep(thd, min(rli->trans_retries, MAX_SLAVE_RETRY_PAUSE),
+ sql_slave_killed, rli);
+ mysql_mutex_lock(&rli->data_lock); // because of SHOW STATUS
rli->trans_retries++;
rli->retried_trans++;
- pthread_mutex_unlock(&rli->data_lock);
+ mysql_mutex_unlock(&rli->data_lock);
DBUG_PRINT("info", ("Slave retries transaction "
"rli->trans_retries: %lu", rli->trans_retries));
}
@@ -2583,7 +2929,7 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
}
DBUG_RETURN(exec_res);
}
- pthread_mutex_unlock(&rli->data_lock);
+ mysql_mutex_unlock(&rli->data_lock);
rli->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_READ_FAILURE,
ER(ER_SLAVE_RELAY_LOG_READ_FAILURE), "\
Could not parse relay log event entry. The possible reasons are: the master's \
@@ -2609,7 +2955,6 @@ static bool check_io_slave_killed(THD *thd, Master_info *mi, const char *info)
return FALSE;
}
-
/**
@brief Try to reconnect slave IO thread.
@@ -2650,8 +2995,7 @@ static int try_to_reconnect(THD *thd, MYSQL *mysql, Master_info *mi,
{
if (*retry_count > master_retry_count)
return 1; // Don't retry forever
- safe_sleep(thd, mi->connect_retry, (CHECK_KILLED_FUNC) io_slave_killed,
- (void *) mi);
+ slave_sleep(thd, mi->connect_retry, io_slave_killed, mi);
}
if (check_io_slave_killed(thd, mi, messages[SLAVE_RECON_MSG_KILLED_WAITING]))
return 1;
@@ -2715,7 +3059,9 @@ pthread_handler_t handle_slave_io(void *arg)
mysql= NULL ;
retry_count= 0;
- pthread_mutex_lock(&mi->run_lock);
+ thd= new THD; // note that contructor of THD uses DBUG_ !
+
+ mysql_mutex_lock(&mi->run_lock);
/* Inform waiting threads that slave has started */
mi->slave_run_id++;
@@ -2723,9 +3069,7 @@ pthread_handler_t handle_slave_io(void *arg)
mi->events_till_disconnect = disconnect_slave_event_count;
#endif
- thd= new THD; // note that contructor of THD uses DBUG_ !
THD_CHECK_SENTRY(thd);
- DBUG_ASSERT(mi->io_thd == 0);
mi->io_thd = thd;
pthread_detach_this_thread();
@@ -2733,22 +3077,32 @@ pthread_handler_t handle_slave_io(void *arg)
mi->clear_error();
if (init_slave_thread(thd, SLAVE_THD_IO))
{
- pthread_cond_broadcast(&mi->start_cond);
+ mysql_cond_broadcast(&mi->start_cond);
sql_print_error("Failed during slave I/O thread initialization");
goto err_during_init;
}
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
threads.append(thd);
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
mi->slave_running = 1;
mi->abort_slave = 0;
- pthread_mutex_unlock(&mi->run_lock);
- pthread_cond_broadcast(&mi->start_cond);
+ mysql_mutex_unlock(&mi->run_lock);
+ mysql_cond_broadcast(&mi->start_cond);
DBUG_PRINT("master_info",("log_file_name: '%s' position: %s",
mi->master_log_name,
llstr(mi->master_log_pos,llbuff)));
+ /* This must be called before run any binlog_relay_io hooks */
+ my_pthread_setspecific_ptr(RPL_MASTER_INFO, mi);
+
+ if (RUN_HOOK(binlog_relay_io, thread_start, (thd, mi)))
+ {
+ mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ ER(ER_SLAVE_FATAL_ERROR), "Failed to run 'thread_start' hook");
+ goto err;
+ }
+
if (!(mi->mysql = mysql = mysql_init(NULL)))
{
mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
@@ -2765,13 +3119,6 @@ pthread_handler_t handle_slave_io(void *arg)
mi->user, mi->host, mi->port,
IO_RPL_LOG_NAME,
llstr(mi->master_log_pos,llbuff));
- /*
- Adding MAX_LOG_EVENT_HEADER_LEN to the max_packet_size on the I/O
- thread, since a replication event can become this much larger than
- the corresponding packet (query) sent from client to master.
- */
- thd->net.max_packet_size= slave_max_allowed_packet;
- mysql->net.max_packet_size= thd->net.max_packet_size+= MAX_LOG_EVENT_HEADER;
}
else
{
@@ -2781,15 +3128,17 @@ pthread_handler_t handle_slave_io(void *arg)
connected:
+#ifdef ENABLED_DEBUG_SYNC
DBUG_EXECUTE_IF("dbug.before_get_running_status_yes",
{
const char act[]=
"now "
"wait_for signal.io_thread_let_running";
- DBUG_ASSERT(opt_debug_sync_timeout > 0);
+ DBUG_ASSERT(debug_sync_service);
DBUG_ASSERT(!debug_sync_set_action(thd,
STRING_WITH_LEN(act)));
};);
+#endif
// TODO: the assignment below should be under mutex (5.0)
mi->slave_running= MYSQL_SLAVE_RUN_CONNECT;
@@ -2799,7 +3148,7 @@ connected:
if (ret == 1)
/* Fatal error */
goto err;
-
+
if (ret == 2)
{
if (check_io_slave_killed(mi->io_thd, mi, "Slave I/O thread killed"
@@ -2849,7 +3198,7 @@ connected:
while (!io_slave_killed(thd,mi))
{
thd_proc_info(thd, "Requesting binlog dump");
- if (request_dump(mysql, mi, &suppress_warnings))
+ if (request_dump(thd, mysql, mi, &suppress_warnings))
{
sql_print_error("Failed on request_dump()");
if (check_io_slave_killed(thd, mi, "Slave I/O thread killed while \
@@ -2869,6 +3218,7 @@ requesting master dump") ||
goto err;
goto connected;
});
+ const char *event_buf;
DBUG_ASSERT(mi->last_error().number == 0);
while (!io_slave_killed(thd,mi))
@@ -2929,14 +3279,37 @@ Stopping slave I/O thread due to out-of-memory error from master");
retry_count=0; // ok event, reset retry counter
thd_proc_info(thd, "Queueing master event to the relay log");
- if (queue_event(mi,(const char*)mysql->net.read_pos + 1,
- event_len))
+ event_buf= (const char*)mysql->net.read_pos + 1;
+ if (RUN_HOOK(binlog_relay_io, after_read_event,
+ (thd, mi,(const char*)mysql->net.read_pos + 1,
+ event_len, &event_buf, &event_len)))
+ {
+ mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ ER(ER_SLAVE_FATAL_ERROR),
+ "Failed to run 'after_read_event' hook");
+ goto err;
+ }
+
+ /* 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,
ER(ER_SLAVE_RELAY_LOG_WRITE_FAILURE),
"could not queue event from master");
goto err;
}
+
+ if (RUN_HOOK(binlog_relay_io, after_queue_event,
+ (thd, mi, event_buf, event_len, synced)))
+ {
+ mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ ER(ER_SLAVE_FATAL_ERROR),
+ "Failed to run 'after_queue_event' hook");
+ goto err;
+ }
+
if (flush_master_info(mi, TRUE, TRUE))
{
sql_print_error("Failed to flush master info file");
@@ -2982,7 +3355,8 @@ err:
// print the current replication position
sql_print_information("Slave I/O thread exiting, read up to log '%s', position %s",
IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff));
- thd->set_query(NULL, 0);
+ RUN_HOOK(binlog_relay_io, thread_stop, (thd, mi));
+ thd->reset_query();
thd->reset_db(NULL, 0);
if (mysql)
{
@@ -3002,7 +3376,8 @@ err:
}
write_ignored_events_info_to_relay_log(thd, mi);
thd_proc_info(thd, "Waiting for slave mutex on exit");
- pthread_mutex_lock(&mi->run_lock);
+ thd->add_status_to_global();
+ mysql_mutex_lock(&mi->run_lock);
err_during_init:
/* Forget the relay log's format */
@@ -3012,11 +3387,11 @@ err_during_init:
change_rpl_status(RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE);
DBUG_ASSERT(thd->net.buff != 0);
net_end(&thd->net); // destructor will not free it, because net.vio is 0
- close_thread_tables(thd);
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
+ thd->unlink();
+ mysql_mutex_unlock(&LOCK_thread_count);
THD_CHECK_SENTRY(thd);
delete thd;
- pthread_mutex_unlock(&LOCK_thread_count);
mi->abort_slave= 0;
mi->slave_running= 0;
mi->io_thd= 0;
@@ -3025,9 +3400,9 @@ err_during_init:
is important. Otherwise a killer_thread can execute between the calls and
delete the mi structure leading to a crash! (see BUG#25306 for details)
*/
- pthread_cond_broadcast(&mi->stop_cond); // tell the world we are done
+ mysql_cond_broadcast(&mi->stop_cond); // tell the world we are done
DBUG_EXECUTE_IF("simulate_slave_delay_at_terminate_bug38694", sleep(5););
- pthread_mutex_unlock(&mi->run_lock);
+ mysql_mutex_unlock(&mi->run_lock);
DBUG_LEAVE; // Must match DBUG_ENTER()
my_thread_end();
@@ -3067,16 +3442,17 @@ int check_temp_dir(char* tmp_file)
/*
Check permissions to create a file.
*/
- if ((fd= my_create(tmp_file, CREATE_MODE,
- O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
- MYF(MY_WME))) < 0)
+ if ((fd= mysql_file_create(key_file_misc,
+ tmp_file, CREATE_MODE,
+ O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
+ MYF(MY_WME))) < 0)
DBUG_RETURN(1);
/*
Clean up.
*/
- my_close(fd, MYF(0));
- my_delete(tmp_file, MYF(0));
+ mysql_file_close(fd, MYF(0));
+ mysql_file_delete(key_file_misc, tmp_file, MYF(0));
DBUG_RETURN(0);
}
@@ -3107,16 +3483,18 @@ pthread_handler_t handle_slave_sql(void *arg)
LINT_INIT(saved_master_log_pos);
LINT_INIT(saved_log_pos);
+
+ thd = new THD; // note that contructor of THD uses DBUG_ !
+ thd->thread_stack = (char*)&thd; // remember where our stack is
+
DBUG_ASSERT(rli->inited);
- pthread_mutex_lock(&rli->run_lock);
+ mysql_mutex_lock(&rli->run_lock);
DBUG_ASSERT(!rli->slave_running);
errmsg= 0;
#ifndef DBUG_OFF
rli->events_till_abort = abort_slave_event_count;
#endif
- thd = new THD; // note that contructor of THD uses DBUG_ !
- thd->thread_stack = (char*)&thd; // remember where our stack is
rli->sql_thd= thd;
/* Inform waiting threads that slave has started */
@@ -3130,7 +3508,7 @@ pthread_handler_t handle_slave_sql(void *arg)
TODO: this is currently broken - slave start and change master
will be stuck if we fail here
*/
- pthread_cond_broadcast(&rli->start_cond);
+ mysql_cond_broadcast(&rli->start_cond);
rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
"Failed during slave thread initialization");
goto err_during_init;
@@ -3150,9 +3528,9 @@ pthread_handler_t handle_slave_sql(void *arg)
applied. In all other cases it must be FALSE.
*/
thd->variables.binlog_annotate_row_events= 0;
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
threads.append(thd);
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
/*
We are going to set slave_running to 1. Assuming slave I/O thread is
alive and connected, this is going to make Seconds_Behind_Master be 0
@@ -3162,8 +3540,8 @@ pthread_handler_t handle_slave_sql(void *arg)
Seconds_Behind_Master grows. No big deal.
*/
rli->abort_slave = 0;
- pthread_mutex_unlock(&rli->run_lock);
- pthread_cond_broadcast(&rli->start_cond);
+ mysql_mutex_unlock(&rli->run_lock);
+ mysql_cond_broadcast(&rli->start_cond);
/*
Reset errors for a clean start (otherwise, if the master is idle, the SQL
@@ -3178,9 +3556,9 @@ pthread_handler_t handle_slave_sql(void *arg)
rli->clear_error();
//tell the I/O thread to take relay_log_space_limit into account from now on
- pthread_mutex_lock(&rli->log_space_lock);
+ mysql_mutex_lock(&rli->log_space_lock);
rli->ignore_log_space_limit= 0;
- pthread_mutex_unlock(&rli->log_space_lock);
+ mysql_mutex_unlock(&rli->log_space_lock);
rli->trans_retries= 0; // start from "no error"
DBUG_PRINT("info", ("rli->trans_retries: %lu", rli->trans_retries));
@@ -3232,19 +3610,19 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME,
if (check_temp_dir(rli->slave_patternload_file))
{
- rli->report(ERROR_LEVEL, thd->main_da.sql_errno(),
+ rli->report(ERROR_LEVEL, thd->stmt_da->sql_errno(),
"Unable to use slave's temporary directory %s - %s",
- slave_load_tmpdir, thd->main_da.message());
+ slave_load_tmpdir, thd->stmt_da->message());
goto err;
}
/* execute init_slave variable */
- if (sys_init_slave.value_length)
+ if (opt_init_slave.length)
{
- execute_init_command(thd, &sys_init_slave, &LOCK_sys_init_slave);
+ execute_init_command(thd, &opt_init_slave, &LOCK_sys_init_slave);
if (thd->is_slave_error)
{
- rli->report(ERROR_LEVEL, thd->main_da.sql_errno(),
+ rli->report(ERROR_LEVEL, thd->stmt_da->sql_errno(),
"Slave SQL thread aborted. Can't execute init_slave query");
goto err;
}
@@ -3254,11 +3632,11 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME,
First check until condition - probably there is nothing to execute. We
do not want to wait for next event in this case.
*/
- pthread_mutex_lock(&rli->data_lock);
+ mysql_mutex_lock(&rli->data_lock);
if (rli->slave_skip_counter)
{
- strmake(saved_log_name, rli->group_relay_log_name, FN_REFLEN - 1);
- strmake(saved_master_log_name, rli->group_master_log_name, FN_REFLEN - 1);
+ strmake_buf(saved_log_name, rli->group_relay_log_name);
+ strmake_buf(saved_master_log_name, rli->group_master_log_name);
saved_log_pos= rli->group_relay_log_pos;
saved_master_log_pos= rli->group_master_log_pos;
saved_skip= rli->slave_skip_counter;
@@ -3269,10 +3647,10 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME,
char buf[22];
sql_print_information("Slave SQL thread stopped because it reached its"
" UNTIL position %s", llstr(rli->until_pos(), buf));
- pthread_mutex_unlock(&rli->data_lock);
+ mysql_mutex_unlock(&rli->data_lock);
goto err;
}
- pthread_mutex_unlock(&rli->data_lock);
+ mysql_mutex_unlock(&rli->data_lock);
/* Read queries from the IO/THREAD until this thread is killed */
@@ -3311,20 +3689,20 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME,
if (thd->is_error())
{
- char const *const errmsg= thd->main_da.message();
+ char const *const errmsg= thd->stmt_da->message();
DBUG_PRINT("info",
- ("thd->main_da.sql_errno()=%d; rli->last_error.number=%d",
- thd->main_da.sql_errno(), last_errno));
+ ("thd->stmt_da->sql_errno()=%d; rli->last_error.number=%d",
+ thd->stmt_da->sql_errno(), last_errno));
if (last_errno == 0)
{
/*
This function is reporting an error which was not reported
while executing exec_relay_log_event().
*/
- rli->report(ERROR_LEVEL, thd->main_da.sql_errno(), "%s", errmsg);
+ rli->report(ERROR_LEVEL, thd->stmt_da->sql_errno(), "%s", errmsg);
}
- else if (last_errno != thd->main_da.sql_errno())
+ else if (last_errno != thd->stmt_da->sql_errno())
{
/*
* An error was reported while executing exec_relay_log_event()
@@ -3333,12 +3711,12 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME,
* what caused the problem.
*/
sql_print_error("Slave (additional info): %s Error_code: %d",
- errmsg, thd->main_da.sql_errno());
+ errmsg, thd->stmt_da->sql_errno());
}
}
/* Print any warnings issued */
- List_iterator_fast<MYSQL_ERROR> it(thd->warn_list);
+ List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
MYSQL_ERROR *err;
/*
Added controlled slave thread cancel for replication
@@ -3347,9 +3725,9 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME,
bool udf_error = false;
while ((err= it++))
{
- if (err->code == ER_CANT_OPEN_LIBRARY)
+ if (err->get_sql_errno() == ER_CANT_OPEN_LIBRARY)
udf_error = true;
- sql_print_warning("Slave: %s Error_code: %d",err->msg, err->code);
+ sql_print_warning("Slave: %s Error_code: %d", err->get_message_text(), err->get_sql_errno());
}
if (udf_error)
sql_print_error("Error loading user-defined library, slave SQL "
@@ -3388,13 +3766,14 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \
variables is supposed to set them to 0 before terminating)).
*/
thd->catalog= 0;
- thd->set_query(NULL, 0);
+ thd->reset_query();
thd->reset_db(NULL, 0);
+ thd->add_status_to_global();
thd_proc_info(thd, "Waiting for slave mutex on exit");
- pthread_mutex_lock(&rli->run_lock);
+ mysql_mutex_lock(&rli->run_lock);
err_during_init:
/* We need data_lock, at least to wake up any waiting master_pos_wait() */
- pthread_mutex_lock(&rli->data_lock);
+ mysql_mutex_lock(&rli->data_lock);
DBUG_ASSERT(rli->slave_running == 1); // tracking buffer overrun
/* When master_pos_wait() wakes up it will check this and terminate */
rli->slave_running= 0;
@@ -3402,9 +3781,9 @@ err_during_init:
delete rli->relay_log.description_event_for_exec;
rli->relay_log.description_event_for_exec= 0;
/* Wake up master_pos_wait() */
- pthread_mutex_unlock(&rli->data_lock);
+ mysql_mutex_unlock(&rli->data_lock);
DBUG_PRINT("info",("Signaling possibly waiting master_pos_wait() functions"));
- pthread_cond_broadcast(&rli->data_cond);
+ mysql_cond_broadcast(&rli->data_cond);
rli->ignore_log_space_limit= 0; /* don't need any lock */
/* we die so won't remember charset - re-update them on next thread start */
rli->cached_charset_invalidate();
@@ -3421,18 +3800,18 @@ err_during_init:
THD_CHECK_SENTRY(thd);
rli->sql_thd= 0;
set_thd_in_use_temporary_tables(rli); // (re)set sql_thd in use for saved temp tables
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
THD_CHECK_SENTRY(thd);
delete thd;
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
/*
Note: the order of the broadcast and unlock calls below (first broadcast, then unlock)
is important. Otherwise a killer_thread can execute between the calls and
delete the mi structure leading to a crash! (see BUG#25306 for details)
*/
- pthread_cond_broadcast(&rli->stop_cond);
+ mysql_cond_broadcast(&rli->stop_cond);
DBUG_EXECUTE_IF("simulate_slave_delay_at_terminate_bug38694", sleep(5););
- pthread_mutex_unlock(&rli->run_lock); // tell the world we are done
+ mysql_mutex_unlock(&rli->run_lock); // tell the world we are done
DBUG_LEAVE; // Must match DBUG_ENTER()
my_thread_end();
@@ -3578,7 +3957,7 @@ err:
static int process_io_rotate(Master_info *mi, Rotate_log_event *rev)
{
DBUG_ENTER("process_io_rotate");
- safe_mutex_assert_owner(&mi->data_lock);
+ mysql_mutex_assert_owner(&mi->data_lock);
if (unlikely(!rev->is_valid()))
DBUG_RETURN(1);
@@ -3675,11 +4054,11 @@ static int queue_binlog_ver_1_event(Master_info *mi, const char *buf,
sql_print_error("Read invalid event from master: '%s',\
master could be corrupt but a more likely cause of this is a bug",
errmsg);
- my_free((char*) tmp_buf, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(tmp_buf);
DBUG_RETURN(1);
}
- pthread_mutex_lock(&mi->data_lock);
+ mysql_mutex_lock(&mi->data_lock);
ev->log_pos= mi->master_log_pos; /* 3.23 events don't contain log_pos */
switch (ev->get_type_code()) {
case STOP_EVENT:
@@ -3690,7 +4069,7 @@ static int queue_binlog_ver_1_event(Master_info *mi, const char *buf,
if (unlikely(process_io_rotate(mi,(Rotate_log_event*)ev)))
{
delete ev;
- pthread_mutex_unlock(&mi->data_lock);
+ mysql_mutex_unlock(&mi->data_lock);
DBUG_RETURN(1);
}
inc_pos= 0;
@@ -3711,8 +4090,8 @@ static int queue_binlog_ver_1_event(Master_info *mi, const char *buf,
delete ev;
mi->master_log_pos += inc_pos;
DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos));
- pthread_mutex_unlock(&mi->data_lock);
- my_free((char*)tmp_buf, MYF(0));
+ mysql_mutex_unlock(&mi->data_lock);
+ my_free(tmp_buf);
DBUG_RETURN(error);
}
default:
@@ -3730,7 +4109,7 @@ static int queue_binlog_ver_1_event(Master_info *mi, const char *buf,
if (unlikely(rli->relay_log.append(ev)))
{
delete ev;
- pthread_mutex_unlock(&mi->data_lock);
+ mysql_mutex_unlock(&mi->data_lock);
DBUG_RETURN(1);
}
rli->relay_log.harvest_bytes_written(&rli->log_space_total);
@@ -3738,7 +4117,7 @@ static int queue_binlog_ver_1_event(Master_info *mi, const char *buf,
delete ev;
mi->master_log_pos+= inc_pos;
DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos));
- pthread_mutex_unlock(&mi->data_lock);
+ mysql_mutex_unlock(&mi->data_lock);
DBUG_RETURN(0);
}
@@ -3764,10 +4143,10 @@ static int queue_binlog_ver_3_event(Master_info *mi, const char *buf,
sql_print_error("Read invalid event from master: '%s',\
master could be corrupt but a more likely cause of this is a bug",
errmsg);
- my_free((char*) tmp_buf, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(tmp_buf);
DBUG_RETURN(1);
}
- pthread_mutex_lock(&mi->data_lock);
+ mysql_mutex_lock(&mi->data_lock);
switch (ev->get_type_code()) {
case STOP_EVENT:
goto err;
@@ -3775,7 +4154,7 @@ static int queue_binlog_ver_3_event(Master_info *mi, const char *buf,
if (unlikely(process_io_rotate(mi,(Rotate_log_event*)ev)))
{
delete ev;
- pthread_mutex_unlock(&mi->data_lock);
+ mysql_mutex_unlock(&mi->data_lock);
DBUG_RETURN(1);
}
inc_pos= 0;
@@ -3788,7 +4167,7 @@ static int queue_binlog_ver_3_event(Master_info *mi, const char *buf,
if (unlikely(rli->relay_log.append(ev)))
{
delete ev;
- pthread_mutex_unlock(&mi->data_lock);
+ mysql_mutex_unlock(&mi->data_lock);
DBUG_RETURN(1);
}
rli->relay_log.harvest_bytes_written(&rli->log_space_total);
@@ -3796,7 +4175,7 @@ static int queue_binlog_ver_3_event(Master_info *mi, const char *buf,
mi->master_log_pos+= inc_pos;
err:
DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos));
- pthread_mutex_unlock(&mi->data_lock);
+ mysql_mutex_unlock(&mi->data_lock);
DBUG_RETURN(0);
}
@@ -3843,9 +4222,12 @@ static int queue_old_event(Master_info *mi, const char *buf,
static int queue_event(Master_info* mi,const char* buf, ulong event_len)
{
int error= 0;
- ulong inc_pos;
+ String error_msg;
+ ulonglong inc_pos;
+ ulonglong event_pos;
Relay_log_info *rli= &mi->rli;
- pthread_mutex_t *log_lock= rli->relay_log.get_log_lock();
+ mysql_mutex_t *log_lock= rli->relay_log.get_log_lock();
+ ulong s_id;
bool unlock_data_lock= TRUE;
/*
FD_q must have been prepared for the first R_a event
@@ -3915,8 +4297,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
(uchar)buf[EVENT_TYPE_OFFSET] != FORMAT_DESCRIPTION_EVENT /* a way to escape */)
DBUG_RETURN(queue_old_event(mi,buf,event_len));
- LINT_INIT(inc_pos);
- pthread_mutex_lock(&mi->data_lock);
+ mysql_mutex_lock(&mi->data_lock);
switch ((uchar)buf[EVENT_TYPE_OFFSET]) {
case STOP_EVENT:
@@ -3941,7 +4322,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
if (unlikely(process_io_rotate(mi, &rev)))
{
- error= 1;
+ error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE;
goto err;
}
/*
@@ -4023,7 +4404,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
mi->rli.relay_log.description_event_for_queue,
1)))
{
- error= 2;
+ error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE;
goto err;
}
delete mi->rli.relay_log.description_event_for_queue;
@@ -4048,12 +4429,82 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
}
break;
+
+ case HEARTBEAT_LOG_EVENT:
+ {
+ /*
+ HB (heartbeat) cannot come before RL (Relay)
+ */
+ char llbuf[22];
+ Heartbeat_log_event hb(buf,
+ mi->rli.relay_log.relay_log_checksum_alg
+ != BINLOG_CHECKSUM_ALG_OFF ?
+ event_len - BINLOG_CHECKSUM_LEN : event_len,
+ mi->rli.relay_log.description_event_for_queue);
+ if (!hb.is_valid())
+ {
+ error= ER_SLAVE_HEARTBEAT_FAILURE;
+ error_msg.append(STRING_WITH_LEN("inconsistent heartbeat event content;"));
+ error_msg.append(STRING_WITH_LEN("the event's data: log_file_name "));
+ error_msg.append(hb.get_log_ident(), (uint) strlen(hb.get_log_ident()));
+ error_msg.append(STRING_WITH_LEN(" log_pos "));
+ llstr(hb.log_pos, llbuf);
+ error_msg.append(llbuf, strlen(llbuf));
+ goto err;
+ }
+ mi->received_heartbeats++;
+ /*
+ compare local and event's versions of log_file, log_pos.
+
+ Heartbeat is sent only after an event corresponding to the corrdinates
+ the heartbeat carries.
+ Slave can not have a difference in coordinates except in the only
+ special case when mi->master_log_name, master_log_pos have never
+ been updated by Rotate event i.e when slave does not have any history
+ with the master (and thereafter mi->master_log_pos is NULL).
+
+ TODO: handling `when' for SHOW SLAVE STATUS' snds behind
+ */
+ if ((memcmp(mi->master_log_name, hb.get_log_ident(), hb.get_ident_len())
+ && mi->master_log_name != NULL)
+ || mi->master_log_pos != hb.log_pos)
+ {
+ /* missed events of heartbeat from the past */
+ error= ER_SLAVE_HEARTBEAT_FAILURE;
+ error_msg.append(STRING_WITH_LEN("heartbeat is not compatible with local info;"));
+ error_msg.append(STRING_WITH_LEN("the event's data: log_file_name "));
+ error_msg.append(hb.get_log_ident(), (uint) strlen(hb.get_log_ident()));
+ error_msg.append(STRING_WITH_LEN(" log_pos "));
+ llstr(hb.log_pos, llbuf);
+ error_msg.append(llbuf, strlen(llbuf));
+ goto err;
+ }
+ goto skip_relay_logging;
+ }
+ break;
+
default:
inc_pos= event_len;
break;
}
/*
+ 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
+ reconnect, MASTER_POS_WAIT(), etc.)
+ */
+ if (inc_pos > 0 &&
+ event_len >= LOG_POS_OFFSET+4 &&
+ (event_pos= uint4korr(buf+LOG_POS_OFFSET)) > mi->master_log_pos + inc_pos)
+ {
+ inc_pos= event_pos - mi->master_log_pos;
+ DBUG_PRINT("info", ("Adjust master_log_pos %llu->%llu to account for "
+ "master-side filtering",
+ mi->master_log_pos + inc_pos, event_pos));
+ }
+
+ /*
If this event is originating from this server, don't queue it.
We don't check this for 3.23 events because it's simpler like this; 3.23
will be filtered anyway by the SQL slave thread which also tests the
@@ -4066,10 +4517,21 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
direct master (an unsupported, useless setup!).
*/
- pthread_mutex_lock(log_lock);
-
- if ((uint4korr(buf + SERVER_ID_OFFSET) == ::server_id) &&
- !mi->rli.replicate_same_server_id)
+ mysql_mutex_lock(log_lock);
+ s_id= uint4korr(buf + SERVER_ID_OFFSET);
+ if ((s_id == ::server_id && !mi->rli.replicate_same_server_id) ||
+ /*
+ the following conjunction deals with IGNORE_SERVER_IDS, if set
+ If the master is on the ignore list, execution of
+ format description log events and rotate events is necessary.
+ */
+ (mi->ignore_server_ids.elements > 0 &&
+ mi->shall_ignore_server_id(s_id) &&
+ /* everything is filtered out from non-master */
+ (s_id != mi->master_id ||
+ /* for the master meta information is necessary */
+ (buf[EVENT_TYPE_OFFSET] != FORMAT_DESCRIPTION_EVENT &&
+ buf[EVENT_TYPE_OFFSET] != ROTATE_EVENT))))
{
/*
Do not write it to the relay log.
@@ -4084,10 +4546,14 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
But events which were generated by this slave and which do not exist in
the master's binlog (i.e. Format_desc, Rotate & Stop) should not increment
mi->master_log_pos.
+ If the event is originated remotely and is being filtered out by
+ IGNORE_SERVER_IDS it increments mi->master_log_pos
+ as well as rli->group_relay_log_pos.
*/
- if ((uchar)buf[EVENT_TYPE_OFFSET]!=FORMAT_DESCRIPTION_EVENT &&
- (uchar)buf[EVENT_TYPE_OFFSET]!=ROTATE_EVENT &&
- (uchar)buf[EVENT_TYPE_OFFSET]!=STOP_EVENT)
+ if (!(s_id == ::server_id && !mi->rli.replicate_same_server_id) ||
+ (buf[EVENT_TYPE_OFFSET] != FORMAT_DESCRIPTION_EVENT &&
+ buf[EVENT_TYPE_OFFSET] != ROTATE_EVENT &&
+ buf[EVENT_TYPE_OFFSET] != STOP_EVENT))
{
mi->master_log_pos+= inc_pos;
memcpy(rli->ign_master_log_name_end, mi->master_log_name, FN_REFLEN);
@@ -4095,8 +4561,8 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
rli->ign_master_log_pos_end= mi->master_log_pos;
}
rli->relay_log.signal_update(); // the slave SQL thread needs to re-check
- DBUG_PRINT("info", ("master_log_pos: %lu, event originating from the same server, ignored",
- (ulong) mi->master_log_pos));
+ DBUG_PRINT("info", ("master_log_pos: %lu, event originating from %u server, ignored",
+ (ulong) mi->master_log_pos, uint4korr(buf + SERVER_ID_OFFSET)));
}
else
{
@@ -4108,18 +4574,26 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
rli->relay_log.harvest_bytes_written(&rli->log_space_total);
}
else
- error= 3;
+ {
+ error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE;
+ }
rli->ign_master_log_name_end[0]= 0; // last event is not ignored
if (save_buf != NULL)
buf= save_buf;
}
- pthread_mutex_unlock(log_lock);
-
+ mysql_mutex_unlock(log_lock);
+skip_relay_logging:
+
err:
if (unlock_data_lock)
- pthread_mutex_unlock(&mi->data_lock);
+ mysql_mutex_unlock(&mi->data_lock);
DBUG_PRINT("info", ("error: %d", error));
+ if (error)
+ mi->report(ERROR_LEVEL, error, ER(error),
+ (error == ER_SLAVE_RELAY_LOG_WRITE_FAILURE)?
+ "could not queue event from master" :
+ error_msg.ptr());
DBUG_RETURN(error);
}
@@ -4133,13 +4607,13 @@ void end_relay_log_info(Relay_log_info* rli)
if (rli->info_fd >= 0)
{
end_io_cache(&rli->info_file);
- (void) my_close(rli->info_fd, MYF(MY_WME));
+ mysql_file_close(rli->info_fd, MYF(MY_WME));
rli->info_fd = -1;
}
if (rli->cur_log_fd >= 0)
{
end_io_cache(&rli->cache_buf);
- (void)my_close(rli->cur_log_fd, MYF(MY_WME));
+ mysql_file_close(rli->cur_log_fd, MYF(MY_WME));
rli->cur_log_fd = -1;
}
rli->inited = 0;
@@ -4218,7 +4692,7 @@ static int connect_to_master(THD* thd, MYSQL* mysql, Master_info* mi,
ulong err_count=0;
char llbuff[22];
DBUG_ENTER("connect_to_master");
-
+ set_slave_max_allowed_packet(thd, mysql);
#ifndef DBUG_OFF
mi->events_till_disconnect = disconnect_slave_event_count;
#endif
@@ -4247,6 +4721,20 @@ static int connect_to_master(THD* thd, MYSQL* mysql, Master_info* mi,
/* This one is not strictly needed but we have it here for completeness */
mysql_options(mysql, MYSQL_SET_CHARSET_DIR, (char *) charsets_dir);
+ /* Set MYSQL_PLUGIN_DIR in case master asks for an external authentication plugin */
+ if (opt_plugin_dir_ptr && *opt_plugin_dir_ptr)
+ mysql_options(mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir_ptr);
+
+ /* we disallow empty users */
+ if (mi->user == NULL || mi->user[0] == 0)
+ {
+ mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ ER(ER_SLAVE_FATAL_ERROR),
+ "Invalid (empty) username when attempting to "
+ "connect to the master server. Connection attempt "
+ "terminated.");
+ DBUG_RETURN(1);
+ }
while (!(slave_was_killed = io_slave_killed(thd,mi)) &&
(reconnect ? mysql_reconnect(mysql) != 0 :
mysql_real_connect(mysql, mi->host, mi->user, mi->password, 0,
@@ -4278,8 +4766,7 @@ static int connect_to_master(THD* thd, MYSQL* mysql, Master_info* mi,
change_rpl_status(RPL_ACTIVE_SLAVE,RPL_LOST_SOLDIER);
break;
}
- safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed,
- (void*)mi);
+ slave_sleep(thd,mi->connect_retry,io_slave_killed, mi);
}
if (!slave_was_killed)
@@ -4326,6 +4813,73 @@ static int safe_reconnect(THD* thd, MYSQL* mysql, Master_info* mi,
}
+MYSQL *rpl_connect_master(MYSQL *mysql)
+{
+ THD *thd= current_thd;
+ Master_info *mi= my_pthread_getspecific_ptr(Master_info*, RPL_MASTER_INFO);
+ if (!mi)
+ {
+ sql_print_error("'rpl_connect_master' must be called in slave I/O thread context.");
+ return NULL;
+ }
+
+ bool allocated= false;
+
+ if (!mysql)
+ {
+ if(!(mysql= mysql_init(NULL)))
+ {
+ sql_print_error("rpl_connect_master: failed in mysql_init()");
+ return NULL;
+ }
+ allocated= true;
+ }
+
+ /*
+ XXX: copied from connect_to_master, this function should not
+ change the slave status, so we cannot use connect_to_master
+ directly
+
+ TODO: make this part a seperate function to eliminate duplication
+ */
+ mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *) &slave_net_timeout);
+ mysql_options(mysql, MYSQL_OPT_READ_TIMEOUT, (char *) &slave_net_timeout);
+
+#ifdef HAVE_OPENSSL
+ if (mi->ssl)
+ {
+ mysql_ssl_set(mysql,
+ mi->ssl_key[0]?mi->ssl_key:0,
+ mi->ssl_cert[0]?mi->ssl_cert:0,
+ mi->ssl_ca[0]?mi->ssl_ca:0,
+ mi->ssl_capath[0]?mi->ssl_capath:0,
+ mi->ssl_cipher[0]?mi->ssl_cipher:0);
+ mysql_options(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
+ &mi->ssl_verify_server_cert);
+ }
+#endif
+
+ mysql_options(mysql, MYSQL_SET_CHARSET_NAME, default_charset_info->csname);
+ /* This one is not strictly needed but we have it here for completeness */
+ mysql_options(mysql, MYSQL_SET_CHARSET_DIR, (char *) charsets_dir);
+
+ if (mi->user == NULL
+ || mi->user[0] == 0
+ || io_slave_killed(thd, mi)
+ || !mysql_real_connect(mysql, mi->host, mi->user, mi->password, 0,
+ mi->port, 0, 0))
+ {
+ if (!io_slave_killed(thd, mi))
+ sql_print_error("rpl_connect_master: error connecting to master: %s (server_error: %d)",
+ mysql_error(mysql), mysql_errno(mysql));
+
+ if (allocated)
+ mysql_close(mysql); // this will free the object
+ return NULL;
+ }
+ return mysql;
+}
+
/*
Store the file and position where the execute-slave thread are in the
relay log.
@@ -4335,8 +4889,9 @@ static int safe_reconnect(THD* thd, MYSQL* mysql, Master_info* mi,
rli Relay log information
NOTES
- - As this is only called by the slave thread, we don't need to
- have a lock on this.
+ - As this is only called by the slave thread or on STOP SLAVE, with the
+ log_lock grabbed and the slave thread stopped, we don't need to have
+ a lock here.
- If there is an active transaction, then we don't update the position
in the relay log. This is to ensure that we re-execute statements
if we die in the middle of an transaction that was rolled back.
@@ -4379,8 +4934,18 @@ bool flush_relay_log_info(Relay_log_info* rli)
error=1;
if (flush_io_cache(file))
error=1;
-
- /* Flushing the relay log is done by the slave I/O thread */
+ if (sync_relayloginfo_period &&
+ !error &&
+ ++(rli->sync_counter) >= sync_relayloginfo_period)
+ {
+ if (my_sync(rli->info_fd, MYF(MY_WME)))
+ error=1;
+ rli->sync_counter= 0;
+ }
+ /*
+ Flushing the relay log is done by the slave I/O thread
+ or by the user on STOP SLAVE.
+ */
DBUG_RETURN(error);
}
@@ -4424,7 +4989,7 @@ static Log_event* next_event(Relay_log_info* rli)
{
Log_event* ev;
IO_CACHE* cur_log = rli->cur_log;
- pthread_mutex_t *log_lock = rli->relay_log.get_log_lock();
+ mysql_mutex_t *log_lock = rli->relay_log.get_log_lock();
const char* errmsg=0;
THD* thd = rli->sql_thd;
DBUG_ENTER("next_event");
@@ -4441,9 +5006,9 @@ static Log_event* next_event(Relay_log_info* rli)
so we assume calling function acquired this mutex for us and we will
hold it for the most of the loop below However, we will release it
whenever it is worth the hassle, and in the cases when we go into a
- pthread_cond_wait() with the non-data_lock mutex
+ mysql_cond_wait() with the non-data_lock mutex
*/
- safe_mutex_assert_owner(&rli->data_lock);
+ mysql_mutex_assert_owner(&rli->data_lock);
while (!sql_slave_killed(thd,rli))
{
@@ -4462,7 +5027,7 @@ static Log_event* next_event(Relay_log_info* rli)
if ((hot_log = (cur_log != &rli->cache_buf)))
{
DBUG_ASSERT(rli->cur_log_fd == -1); // foreign descriptor
- pthread_mutex_lock(log_lock);
+ mysql_mutex_lock(log_lock);
/*
Reading xxx_file_id is safe because the log will only
@@ -4472,7 +5037,7 @@ static Log_event* next_event(Relay_log_info* rli)
{
// The master has switched to a new log file; Reopen the old log file
cur_log=reopen_relay_log(rli, &errmsg);
- pthread_mutex_unlock(log_lock);
+ mysql_mutex_unlock(log_lock);
if (!cur_log) // No more log files
goto err;
hot_log=0; // Using old binary log
@@ -4520,7 +5085,7 @@ static Log_event* next_event(Relay_log_info* rli)
*/
rli->future_event_relay_log_pos= my_b_tell(cur_log);
if (hot_log)
- pthread_mutex_unlock(log_lock);
+ mysql_mutex_unlock(log_lock);
DBUG_RETURN(ev);
}
DBUG_ASSERT(thd==rli->sql_thd);
@@ -4530,7 +5095,7 @@ static Log_event* next_event(Relay_log_info* rli)
{
errmsg = "slave SQL thread aborted because of I/O error";
if (hot_log)
- pthread_mutex_unlock(log_lock);
+ mysql_mutex_unlock(log_lock);
goto err;
}
if (!cur_log->error) /* EOF */
@@ -4577,7 +5142,7 @@ static Log_event* next_event(Relay_log_info* rli)
0, rli->ign_master_log_pos_end,
Rotate_log_event::DUP_NAME);
rli->ign_master_log_name_end[0]= 0;
- pthread_mutex_unlock(log_lock);
+ mysql_mutex_unlock(log_lock);
if (unlikely(!ev))
{
errmsg= "Slave SQL thread failed to create a Rotate event "
@@ -4592,7 +5157,7 @@ static Log_event* next_event(Relay_log_info* rli)
We can, and should release data_lock while we are waiting for
update. If we do not, show slave status will block
*/
- pthread_mutex_unlock(&rli->data_lock);
+ mysql_mutex_unlock(&rli->data_lock);
/*
Possible deadlock :
@@ -4621,7 +5186,7 @@ static Log_event* next_event(Relay_log_info* rli)
be stopped, and the SQL thread sets ignore_log_space_limit to 0 when
it stops.
*/
- pthread_mutex_lock(&rli->log_space_lock);
+ mysql_mutex_lock(&rli->log_space_lock);
/*
If we have reached the limit of the relay space and we
@@ -4653,12 +5218,12 @@ static Log_event* next_event(Relay_log_info* rli)
~Relay_log_info(), i.e. when rli is destroyed, and rli will
not be destroyed before we exit the present function.
*/
- pthread_mutex_unlock(&rli->log_space_lock);
- pthread_cond_broadcast(&rli->log_space_cond);
- // Note that wait_for_update unlocks lock_log !
- rli->relay_log.wait_for_update(rli->sql_thd, 1);
+ mysql_mutex_unlock(&rli->log_space_lock);
+ mysql_cond_broadcast(&rli->log_space_cond);
+ // Note that wait_for_update_relay_log unlocks lock_log !
+ rli->relay_log.wait_for_update_relay_log(rli->sql_thd);
// re-acquire data lock since we released it earlier
- pthread_mutex_lock(&rli->data_lock);
+ mysql_mutex_lock(&rli->data_lock);
rli->last_master_timestamp= save_timestamp;
continue;
}
@@ -4669,7 +5234,7 @@ static Log_event* next_event(Relay_log_info* rli)
*/
end_io_cache(cur_log);
DBUG_ASSERT(rli->cur_log_fd >= 0);
- my_close(rli->cur_log_fd, MYF(MY_WME));
+ mysql_file_close(rli->cur_log_fd, MYF(MY_WME));
rli->cur_log_fd = -1;
if (relay_log_purge)
@@ -4709,8 +5274,7 @@ static Log_event* next_event(Relay_log_info* rli)
goto err;
}
rli->event_relay_log_pos = BIN_LOG_HEADER_SIZE;
- strmake(rli->event_relay_log_name,rli->linfo.log_file_name,
- sizeof(rli->event_relay_log_name)-1);
+ strmake_buf(rli->event_relay_log_name,rli->linfo.log_file_name);
flush_relay_log_info(rli);
}
@@ -4726,7 +5290,7 @@ static Log_event* next_event(Relay_log_info* rli)
DBUG_PRINT("info",("hot_log: %d",hot_log));
if (!hot_log) /* if hot_log, we already have this mutex */
- pthread_mutex_lock(log_lock);
+ mysql_mutex_lock(log_lock);
if (rli->relay_log.is_active(rli->linfo.log_file_name))
{
#ifdef EXTRA_DEBUG
@@ -4801,13 +5365,16 @@ static Log_event* next_event(Relay_log_info* rli)
my_b_seek(cur_log, (my_off_t) 0);
if (check_binlog_magic(cur_log,&errmsg))
{
- if (!hot_log) pthread_mutex_unlock(log_lock);
+ if (!hot_log)
+ mysql_mutex_unlock(log_lock);
goto err;
}
- if (!hot_log) pthread_mutex_unlock(log_lock);
+ if (!hot_log)
+ mysql_mutex_unlock(log_lock);
continue;
}
- if (!hot_log) pthread_mutex_unlock(log_lock);
+ if (!hot_log)
+ mysql_mutex_unlock(log_lock);
/*
if we get here, the log was not hot, so we will have to open it
ourselves. We are sure that the log is still not hot now (a log can get
@@ -4830,7 +5397,7 @@ static Log_event* next_event(Relay_log_info* rli)
TODO: come up with something better to handle this error
*/
if (hot_log)
- pthread_mutex_unlock(log_lock);
+ mysql_mutex_unlock(log_lock);
sql_print_error("Slave SQL thread: I/O error reading \
event(errno: %d cur_log->error: %d)",
my_errno,cur_log->error);
@@ -4868,6 +5435,8 @@ int rotate_relay_log(Master_info* mi)
Relay_log_info* rli= &mi->rli;
int error= 0;
+ DBUG_EXECUTE_IF("crash_before_rotate_relaylog", DBUG_SUICIDE(););
+
/*
We need to test inited because otherwise, new_file() will attempt to lock
LOCK_log, which may not be inited (if we're not a slave).
@@ -4897,7 +5466,7 @@ int rotate_relay_log(Master_info* mi)
Note that it needs to be protected by mi->data_lock.
*/
- safe_mutex_assert_owner(&mi->data_lock);
+ mysql_mutex_assert_owner(&mi->data_lock);
rli->relay_log.harvest_bytes_written(&rli->log_space_total);
end:
DBUG_RETURN(error);
diff --git a/sql/slave.h b/sql/slave.h
index 30574f42d69..c220f881619 100644
--- a/sql/slave.h
+++ b/sql/slave.h
@@ -12,8 +12,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef SLAVE_H
#define SLAVE_H
@@ -24,6 +23,17 @@
@file
*/
+
+/**
+ Some of defines are need in parser even though replication is not
+ compiled in (embedded).
+*/
+
+/**
+ The maximum is defined as (ULONG_MAX/1000) with 4 bytes ulong
+*/
+#define SLAVE_MAX_HEARTBEAT_PERIOD 4294967
+
#ifdef HAVE_REPLICATION
#include "log.h"
@@ -35,11 +45,15 @@
#define MAX_SLAVE_ERROR 2000
-
// Forward declarations
class Relay_log_info;
class Master_info;
+int init_intvar_from_file(int* var, IO_CACHE* f, int default_val);
+int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
+ const char *default_val);
+int init_floatvar_from_file(float* var, IO_CACHE* f, float default_val);
+int init_dynarray_intvar_from_file(DYNAMIC_ARRAY* arr, IO_CACHE* f);
/*****************************************************************************
@@ -108,6 +122,7 @@ extern MYSQL_PLUGIN_IMPORT char *relay_log_info_file;
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 my_bool opt_replicate_annotate_row_events;
extern ulonglong relay_log_space_limit;
@@ -137,7 +152,17 @@ extern ulonglong relay_log_space_limit;
*/
#define SLAVE_FORCE_ALL 4
+/*
+ Values for the option --replicate-events-marked-for-skip.
+ Must match the names in replicate_events_marked_for_skip_names in sys_vars.cc
+*/
+#define RPL_SKIP_REPLICATE 0
+#define RPL_SKIP_FILTER_ON_SLAVE 1
+#define RPL_SKIP_FILTER_ON_MASTER 2
+
+
int init_slave();
+int init_recovery(Master_info* mi, const char** errmsg);
void init_slave_skip_errors(const char* arg);
bool flush_relay_log_info(Relay_log_info* rli);
int register_slave_on_master(MYSQL* mysql);
@@ -150,15 +175,19 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start,
cond_lock is usually same as start_lock. It is needed for the case when
start_lock is 0 which happens if start_slave_thread() is called already
inside the start_lock section, but at the same time we want a
- pthread_cond_wait() on start_cond,start_lock
+ mysql_cond_wait() on start_cond, start_lock
*/
-int start_slave_thread(pthread_handler h_func, pthread_mutex_t* start_lock,
- pthread_mutex_t *cond_lock,
- pthread_cond_t* start_cond,
- volatile uint *slave_running,
- volatile ulong *slave_run_id,
- Master_info* mi,
- bool high_priority);
+int start_slave_thread(
+#ifdef HAVE_PSI_INTERFACE
+ PSI_thread_key thread_key,
+#endif
+ pthread_handler h_func,
+ mysql_mutex_t *start_lock,
+ mysql_mutex_t *cond_lock,
+ mysql_cond_t *start_cond,
+ volatile uint *slave_running,
+ volatile ulong *slave_run_id,
+ Master_info *mi);
/* If fd is -1, dump to NET */
int mysql_table_dump(THD* thd, const char* db,
@@ -198,25 +227,23 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli);
pthread_handler_t handle_slave_io(void *arg);
pthread_handler_t handle_slave_sql(void *arg);
+bool net_request_file(NET* net, const char* fname);
+
extern bool volatile abort_loop;
-extern Master_info main_mi, *active_mi; /* active_mi for multi-master */
-extern LIST master_list;
+extern Master_info *active_mi; /* active_mi for multi-master */
extern my_bool replicate_same_server_id;
extern int disconnect_slave_event_count, abort_slave_event_count ;
/* the master variables are defaults read from my.cnf or command line */
-extern uint master_port, master_connect_retry, report_port;
-extern char * master_user, *master_password, *master_host;
+extern uint report_port;
extern char *master_info_file, *report_user;
extern char *report_host, *report_password;
-extern my_bool master_ssl;
-extern char *master_ssl_ca, *master_ssl_capath, *master_ssl_cert;
-extern char *master_ssl_cipher, *master_ssl_key;
-
extern I_List<THD> threads;
+#else
+#define close_active_mi() /* no-op */
#endif /* HAVE_REPLICATION */
/* masks for start/stop operations on io and sql slave threads */
diff --git a/sql/sp.cc b/sql/sp.cc
index efa5526fed9..13f027493b4 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -12,14 +12,23 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
-#include "sp_head.h"
+#include "sql_priv.h"
+#include "unireg.h"
#include "sp.h"
+#include "sql_base.h" // close_thread_tables
+#include "sql_parse.h" // parse_sql
+#include "key.h" // key_copy
+#include "sql_show.h" // append_definer, append_identifier
+#include "sql_db.h" // get_default_db_collation, mysql_opt_change_db,
+ // mysql_change_db, check_db_dir_existence,
+ // load_db_opt_by_name
+#include "sql_table.h" // write_bin_log
+#include "sql_acl.h" // SUPER_ACL
+#include "sp_head.h"
#include "sp_cache.h"
-#include "sql_trigger.h"
+#include "lock.h" // lock_object_name
#include <my_user.h>
@@ -33,46 +42,17 @@ create_string(THD *thd, String *buf,
const char *body, ulong bodylen,
st_sp_chistics *chistics,
const LEX_STRING *definer_user,
- const LEX_STRING *definer_host);
+ const LEX_STRING *definer_host,
+ ulonglong sql_mode);
+
static int
db_load_routine(THD *thd, stored_procedure_type type, sp_name *name,
sp_head **sphp,
- ulong sql_mode, const char *params, const char *returns,
+ ulonglong sql_mode, const char *params, const char *returns,
const char *body, st_sp_chistics &chistics,
const char *definer, longlong created, longlong modified,
Stored_program_creation_ctx *creation_ctx);
-/*
- *
- * DB storage of Stored PROCEDUREs and FUNCTIONs
- *
- */
-
-enum
-{
- MYSQL_PROC_FIELD_DB = 0,
- MYSQL_PROC_FIELD_NAME,
- MYSQL_PROC_MYSQL_TYPE,
- MYSQL_PROC_FIELD_SPECIFIC_NAME,
- MYSQL_PROC_FIELD_LANGUAGE,
- MYSQL_PROC_FIELD_ACCESS,
- MYSQL_PROC_FIELD_DETERMINISTIC,
- MYSQL_PROC_FIELD_SECURITY_TYPE,
- MYSQL_PROC_FIELD_PARAM_LIST,
- MYSQL_PROC_FIELD_RETURNS,
- MYSQL_PROC_FIELD_BODY,
- MYSQL_PROC_FIELD_DEFINER,
- MYSQL_PROC_FIELD_CREATED,
- MYSQL_PROC_FIELD_MODIFIED,
- MYSQL_PROC_FIELD_SQL_MODE,
- MYSQL_PROC_FIELD_COMMENT,
- MYSQL_PROC_FIELD_CHARACTER_SET_CLIENT,
- MYSQL_PROC_FIELD_COLLATION_CONNECTION,
- MYSQL_PROC_FIELD_DB_COLLATION,
- MYSQL_PROC_FIELD_BODY_UTF8,
- MYSQL_PROC_FIELD_COUNT
-};
-
static const
TABLE_FIELD_TYPE proc_table_fields[MYSQL_PROC_FIELD_COUNT] =
{
@@ -134,7 +114,7 @@ TABLE_FIELD_TYPE proc_table_fields[MYSQL_PROC_FIELD_COUNT] =
},
{
{ C_STRING_WITH_LEN("definer") },
- { C_STRING_WITH_LEN("char(77)") },
+ { C_STRING_WITH_LEN("char(") },
{ C_STRING_WITH_LEN("utf8") }
},
{
@@ -162,7 +142,7 @@ TABLE_FIELD_TYPE proc_table_fields[MYSQL_PROC_FIELD_COUNT] =
},
{
{ C_STRING_WITH_LEN("comment") },
- { C_STRING_WITH_LEN("char(64)") },
+ { C_STRING_WITH_LEN("text") },
{ C_STRING_WITH_LEN("utf8") }
},
{
@@ -425,12 +405,13 @@ static Proc_table_intact proc_table_intact;
\# Pointer to TABLE object of mysql.proc
*/
-TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
+TABLE *open_proc_table_for_read(THD *thd, Open_tables_backup *backup)
{
+ TABLE_LIST table;
+
DBUG_ENTER("open_proc_table_for_read");
- TABLE_LIST table;
- table.init_one_table("mysql", "proc", TL_READ);
+ table.init_one_table("mysql", 5, "proc", 4, "proc", TL_READ);
if (open_system_tables_for_read(thd, &table, backup))
DBUG_RETURN(NULL);
@@ -460,11 +441,12 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
static TABLE *open_proc_table_for_update(THD *thd)
{
+ TABLE_LIST table_list;
+ TABLE *table;
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
DBUG_ENTER("open_proc_table_for_update");
- TABLE *table;
- TABLE_LIST table_list;
- table_list.init_one_table("mysql", "proc", TL_WRITE);
+ table_list.init_one_table("mysql", 5, "proc", 4, "proc", TL_WRITE);
if (!(table= open_system_table_for_update(thd, &table_list)))
DBUG_RETURN(NULL);
@@ -473,6 +455,7 @@ static TABLE *open_proc_table_for_update(THD *thd)
DBUG_RETURN(table);
close_thread_tables(thd);
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
DBUG_RETURN(NULL);
}
@@ -562,8 +545,8 @@ db_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
char buff[65];
String str(buff, sizeof(buff), &my_charset_bin);
bool saved_time_zone_used= thd->time_zone_used;
- ulong sql_mode, saved_mode= thd->variables.sql_mode;
- Open_tables_state open_tables_state_backup;
+ ulonglong sql_mode, saved_mode= thd->variables.sql_mode;
+ Open_tables_backup open_tables_state_backup;
Stored_program_creation_ctx *creation_ctx;
DBUG_ENTER("db_find_routine");
@@ -697,16 +680,24 @@ db_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
struct Silence_deprecated_warning : public Internal_error_handler
{
public:
- virtual bool handle_error(uint sql_errno, const char *message,
- MYSQL_ERROR::enum_warning_level level,
- THD *thd);
+ virtual bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl);
};
bool
-Silence_deprecated_warning::handle_error(uint sql_errno, const char *message,
- MYSQL_ERROR::enum_warning_level level,
- THD *thd)
+Silence_deprecated_warning::handle_condition(
+ THD *,
+ uint sql_errno,
+ const char*,
+ MYSQL_ERROR::enum_warning_level level,
+ const char*,
+ MYSQL_ERROR ** cond_hdl)
{
+ *cond_hdl= NULL;
if (sql_errno == ER_WARN_DEPRECATED_SYNTAX &&
level == MYSQL_ERROR::WARN_LEVEL_WARN)
return TRUE;
@@ -715,6 +706,62 @@ Silence_deprecated_warning::handle_error(uint sql_errno, const char *message,
}
+/**
+ @brief The function parses input strings and returns SP stucture.
+
+ @param[in] thd Thread handler
+ @param[in] defstr CREATE... string
+ @param[in] sql_mode SQL mode
+ @param[in] creation_ctx Creation context of stored routines
+
+ @return Pointer on sp_head struct
+ @retval # Pointer on sp_head struct
+ @retval 0 error
+*/
+
+static sp_head *sp_compile(THD *thd, String *defstr, ulonglong sql_mode,
+ Stored_program_creation_ctx *creation_ctx)
+{
+ sp_head *sp;
+ ulonglong old_sql_mode= thd->variables.sql_mode;
+ ha_rows old_select_limit= thd->variables.select_limit;
+ sp_rcontext *old_spcont= thd->spcont;
+ Silence_deprecated_warning warning_handler;
+ Parser_state parser_state;
+
+ thd->variables.sql_mode= sql_mode;
+ thd->variables.select_limit= HA_POS_ERROR;
+
+ if (parser_state.init(thd, defstr->c_ptr_safe(), defstr->length()))
+ {
+ thd->variables.sql_mode= old_sql_mode;
+ thd->variables.select_limit= old_select_limit;
+ return NULL;
+ }
+
+ lex_start(thd);
+ thd->push_internal_handler(&warning_handler);
+ thd->spcont= 0;
+
+ if (parse_sql(thd, & parser_state, creation_ctx) || thd->lex == NULL)
+ {
+ sp= thd->lex->sphead;
+ delete sp;
+ sp= 0;
+ }
+ else
+ {
+ sp= thd->lex->sphead;
+ }
+
+ thd->pop_internal_handler();
+ thd->spcont= old_spcont;
+ thd->variables.sql_mode= old_sql_mode;
+ thd->variables.select_limit= old_select_limit;
+ return sp;
+}
+
+
class Bad_db_error_handler : public Internal_error_handler
{
public:
@@ -722,9 +769,12 @@ public:
:m_error_caught(false)
{}
- virtual bool handle_error(uint sql_errno, const char *message,
- MYSQL_ERROR::enum_warning_level level,
- THD *thd);
+ virtual bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* message,
+ MYSQL_ERROR ** cond_hdl);
bool error_caught() const { return m_error_caught; }
@@ -733,9 +783,12 @@ private:
};
bool
-Bad_db_error_handler::handle_error(uint sql_errno, const char *message,
- MYSQL_ERROR::enum_warning_level level,
- THD *thd)
+Bad_db_error_handler::handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* message,
+ MYSQL_ERROR ** cond_hdl)
{
if (sql_errno == ER_BAD_DB_ERROR)
{
@@ -749,7 +802,7 @@ Bad_db_error_handler::handle_error(uint sql_errno, const char *message,
static int
db_load_routine(THD *thd, stored_procedure_type type,
sp_name *name, sp_head **sphp,
- ulong sql_mode, const char *params, const char *returns,
+ ulonglong sql_mode, const char *params, const char *returns,
const char *body, st_sp_chistics &chistics,
const char *definer, longlong created, longlong modified,
Stored_program_creation_ctx *creation_ctx)
@@ -760,10 +813,6 @@ db_load_routine(THD *thd, stored_procedure_type type,
LEX_STRING saved_cur_db_name=
{ saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) };
bool cur_db_changed;
- ulong old_sql_mode= thd->variables.sql_mode;
- ha_rows old_select_limit= thd->variables.select_limit;
- sp_rcontext *old_spcont= thd->spcont;
- Silence_deprecated_warning warning_handler;
Bad_db_error_handler db_not_exists_handler;
char definer_user_name_holder[USERNAME_LENGTH + 1];
LEX_STRING definer_user_name= { definer_user_name_holder,
@@ -772,10 +821,7 @@ db_load_routine(THD *thd, stored_procedure_type type,
char definer_host_name_holder[HOSTNAME_LENGTH + 1];
LEX_STRING definer_host_name= { definer_host_name_holder, HOSTNAME_LENGTH };
- int ret;
-
- thd->variables.sql_mode= sql_mode;
- thd->variables.select_limit= HA_POS_ERROR;
+ int ret= 0;
thd->lex= &newlex;
newlex.current_select= NULL;
@@ -799,7 +845,8 @@ db_load_routine(THD *thd, stored_procedure_type type,
params, strlen(params),
returns, strlen(returns),
body, strlen(body),
- &chistics, &definer_user_name, &definer_host_name))
+ &chistics, &definer_user_name, &definer_host_name,
+ sql_mode))
{
ret= SP_INTERNAL_ERROR;
goto end;
@@ -827,22 +874,9 @@ db_load_routine(THD *thd, stored_procedure_type type,
goto end;
}
- thd->spcont= NULL;
{
- Parser_state parser_state;
- if (parser_state.init(thd, defstr.c_ptr_safe(), defstr.length()))
- {
- ret= SP_INTERNAL_ERROR;
- goto end;
- }
-
- lex_start(thd);
-
- thd->push_internal_handler(&warning_handler);
- ret= parse_sql(thd, & parser_state, creation_ctx) || newlex.sphead == NULL;
- thd->pop_internal_handler();
-
+ *sphp= sp_compile(thd, &defstr, sql_mode, creation_ctx);
/*
Force switching back to the saved current database (if changed),
because it may be NULL. In this case, mysql_change_db() would
@@ -851,19 +885,16 @@ db_load_routine(THD *thd, stored_procedure_type type,
if (cur_db_changed && mysql_change_db(thd, &saved_cur_db_name, TRUE))
{
- delete newlex.sphead;
ret= SP_INTERNAL_ERROR;
goto end;
}
- if (ret)
+ if (!*sphp)
{
- delete newlex.sphead;
ret= SP_PARSE_ERROR;
goto end;
}
- *sphp= newlex.sphead;
(*sphp)->set_definer(&definer_user_name, &definer_host_name);
(*sphp)->set_info(created, modified, &chistics, sql_mode);
(*sphp)->set_creation_ctx(creation_ctx);
@@ -880,10 +911,8 @@ db_load_routine(THD *thd, stored_procedure_type type,
}
end:
+ thd->lex->sphead= NULL;
lex_end(thd->lex);
- thd->spcont= old_spcont;
- thd->variables.sql_mode= old_sql_mode;
- thd->variables.select_limit= old_select_limit;
thd->lex= old_lex;
return ret;
}
@@ -906,6 +935,11 @@ sp_returns_type(THD *thd, String &result, sp_head *sp)
{
result.append(STRING_WITH_LEN(" CHARSET "));
result.append(field->charset()->csname);
+ if (!(field->charset()->state & MY_CS_PRIMARY))
+ {
+ result.append(STRING_WITH_LEN(" COLLATE "));
+ result.append(field->charset()->name);
+ }
}
delete field;
@@ -941,7 +975,9 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
int ret;
TABLE *table;
char definer[USER_HOST_BUFF_SIZE];
- ulong saved_mode= thd->variables.sql_mode;
+ ulonglong 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);
@@ -961,6 +997,10 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
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))
+ DBUG_RETURN(SP_OPEN_TABLE_FAILED);
+
/* Reset sql_mode during data dictionary operations. */
thd->variables.sql_mode= 0;
@@ -969,8 +1009,8 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
row-based replication. The flag will be reset at the end of the
statement.
*/
- save_binlog_row_based= thd->current_stmt_binlog_row_based;
- thd->clear_current_stmt_binlog_row_based();
+ if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
+ thd->clear_current_stmt_binlog_format_row();
saved_count_cuted_fields= thd->count_cuted_fields;
thd->count_cuted_fields= CHECK_FIELD_WARN;
@@ -1138,7 +1178,10 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
ret= SP_OK;
if (table->file->ha_write_row(table->record[0]))
ret= SP_WRITE_ROW_FAILED;
- else if (mysql_bin_log.is_open())
+ if (ret == SP_OK)
+ sp_cache_invalidate();
+
+ if (ret == SP_OK && mysql_bin_log.is_open())
{
thd->clear_error();
@@ -1154,7 +1197,8 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
retstr.ptr(), retstr.length(),
sp->m_body.str, sp->m_body.length,
sp->m_chistics, &(thd->lex->definer->user),
- &(thd->lex->definer->host)))
+ &(thd->lex->definer->host),
+ saved_mode))
{
ret= SP_INTERNAL_ERROR;
goto done;
@@ -1162,22 +1206,21 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
/* restore sql_mode when binloging */
thd->variables.sql_mode= saved_mode;
/* Such a statement can always go directly to binlog, no trans cache */
- if (thd->binlog_query(THD::MYSQL_QUERY_TYPE,
+ if (thd->binlog_query(THD::STMT_QUERY_TYPE,
log_query.ptr(), log_query.length(),
- FALSE, FALSE, 0))
+ FALSE, FALSE, FALSE, 0))
ret= SP_INTERNAL_ERROR;
thd->variables.sql_mode= 0;
}
-
}
done:
thd->count_cuted_fields= saved_count_cuted_fields;
thd->variables.sql_mode= saved_mode;
-
- close_thread_tables(thd);
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(ret);
}
@@ -1203,6 +1246,8 @@ sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name)
TABLE *table;
int ret;
bool save_binlog_row_based;
+ MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ?
+ MDL_key::FUNCTION : MDL_key::PROCEDURE;
DBUG_ENTER("sp_drop_routine");
DBUG_PRINT("enter", ("type: %d name: %.*s",
type, (int) name->m_name.length, name->m_name.str));
@@ -1210,16 +1255,21 @@ sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name)
DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
type == TYPE_ENUM_FUNCTION);
+ /* Grab an exclusive MDL lock. */
+ if (lock_object_name(thd, mdl_type, name->m_db.str, name->m_name.str))
+ DBUG_RETURN(SP_DELETE_ROW_FAILED);
+
+ if (!(table= open_proc_table_for_update(thd)))
+ DBUG_RETURN(SP_OPEN_TABLE_FAILED);
+
/*
This statement will be replicated as a statement, even when using
row-based replication. The flag will be reset at the end of the
statement.
*/
- save_binlog_row_based= thd->current_stmt_binlog_row_based;
- thd->clear_current_stmt_binlog_row_based();
+ if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
+ thd->clear_current_stmt_binlog_format_row();
- 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 (table->file->ha_delete_row(table->record[0]))
@@ -1231,11 +1281,25 @@ sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name)
if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
ret= SP_INTERNAL_ERROR;
sp_cache_invalidate();
- }
- close_thread_tables(thd);
+ /*
+ A lame workaround for lack of cache flush:
+ make sure the routine is at least gone from the
+ 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_flush_obsolete(spc, &sp);
+ }
+ }
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(ret);
}
@@ -1264,6 +1328,8 @@ sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name,
TABLE *table;
int ret;
bool save_binlog_row_based;
+ MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ?
+ MDL_key::FUNCTION : MDL_key::PROCEDURE;
DBUG_ENTER("sp_update_routine");
DBUG_PRINT("enter", ("type: %d name: %.*s",
(int) type,
@@ -1271,18 +1337,48 @@ sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name,
DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
type == TYPE_ENUM_FUNCTION);
+
+ /* Grab an exclusive MDL lock. */
+ if (lock_object_name(thd, mdl_type, name->m_db.str, name->m_name.str))
+ DBUG_RETURN(SP_OPEN_TABLE_FAILED);
+
+ if (!(table= open_proc_table_for_update(thd)))
+ DBUG_RETURN(SP_OPEN_TABLE_FAILED);
+
/*
This statement will be replicated as a statement, even when using
row-based replication. The flag will be reset at the end of the
statement.
*/
- save_binlog_row_based= thd->current_stmt_binlog_row_based;
- thd->clear_current_stmt_binlog_row_based();
+ if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
+ thd->clear_current_stmt_binlog_format_row();
- 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 (type == TYPE_ENUM_FUNCTION && ! trust_function_creators &&
+ mysql_bin_log.is_open() &&
+ (chistics->daccess == SP_CONTAINS_SQL ||
+ chistics->daccess == SP_MODIFIES_SQL_DATA))
+ {
+ char *ptr;
+ bool is_deterministic;
+ ptr= get_field(thd->mem_root,
+ table->field[MYSQL_PROC_FIELD_DETERMINISTIC]);
+ if (ptr == NULL)
+ {
+ ret= SP_INTERNAL_ERROR;
+ goto err;
+ }
+ is_deterministic= ptr[0] == 'N' ? FALSE : TRUE;
+ if (!is_deterministic)
+ {
+ my_message(ER_BINLOG_UNSAFE_ROUTINE,
+ ER(ER_BINLOG_UNSAFE_ROUTINE), MYF(0));
+ ret= SP_INTERNAL_ERROR;
+ goto err;
+ }
+ }
+
store_record(table,record[1]);
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time();
@@ -1309,15 +1405,123 @@ sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name,
ret= SP_INTERNAL_ERROR;
sp_cache_invalidate();
}
-
- close_thread_tables(thd);
+err:
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(ret);
}
/**
+ This internal handler is used to trap errors from opening mysql.proc.
+*/
+
+class Lock_db_routines_error_handler : public Internal_error_handler
+{
+public:
+ bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl)
+ {
+ if (sql_errno == ER_NO_SUCH_TABLE ||
+ sql_errno == ER_NO_SUCH_TABLE_IN_ENGINE ||
+ sql_errno == ER_CANNOT_LOAD_FROM_TABLE ||
+ sql_errno == ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE ||
+ sql_errno == ER_COL_COUNT_DOESNT_MATCH_CORRUPTED)
+ return true;
+ return false;
+ }
+};
+
+
+/**
+ Acquires exclusive metadata lock on all stored routines in the
+ given database.
+
+ @note Will also return false (=success) if mysql.proc can't be opened
+ or is outdated. This allows DROP DATABASE to continue in these
+ cases.
+ */
+
+bool lock_db_routines(THD *thd, char *db)
+{
+ TABLE *table;
+ uint key_len;
+ Open_tables_backup open_tables_state_backup;
+ MDL_request_list mdl_requests;
+ Lock_db_routines_error_handler err_handler;
+ uchar keybuf[MAX_KEY_LENGTH];
+ DBUG_ENTER("lock_db_routines");
+
+ /*
+ mysql.proc will be re-opened during deletion, so we can ignore
+ errors when opening the table here. The error handler is
+ used to avoid getting the same warning twice.
+ */
+ thd->push_internal_handler(&err_handler);
+ table= open_proc_table_for_read(thd, &open_tables_state_backup);
+ thd->pop_internal_handler();
+ if (!table)
+ {
+ /*
+ DROP DATABASE should not fail even if mysql.proc does not exist
+ or is outdated. We therefore only abort mysql_rm_db() if we
+ have errors not handled by the error handler.
+ */
+ DBUG_RETURN(thd->is_error() || thd->killed);
+ }
+
+ table->field[MYSQL_PROC_FIELD_DB]->store(db, strlen(db), system_charset_info);
+ key_len= table->key_info->key_part[0].store_length;
+ table->field[MYSQL_PROC_FIELD_DB]->get_key_image(keybuf, key_len, Field::itRAW);
+ int nxtres= table->file->ha_index_init(0, 1);
+ if (nxtres)
+ {
+ table->file->print_error(nxtres, MYF(0));
+ close_system_tables(thd, &open_tables_state_backup);
+ DBUG_RETURN(true);
+ }
+
+ if (! table->file->ha_index_read_map(table->record[0], keybuf, (key_part_map)1,
+ HA_READ_KEY_EXACT))
+ {
+ do
+ {
+ char *sp_name= get_field(thd->mem_root,
+ table->field[MYSQL_PROC_FIELD_NAME]);
+ 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);
+ mdl_requests.push_front(mdl_request);
+ } while (! (nxtres= table->file->ha_index_next_same(table->record[0], keybuf, key_len)));
+ }
+ table->file->ha_index_end();
+ if (nxtres != 0 && nxtres != HA_ERR_END_OF_FILE)
+ {
+ table->file->print_error(nxtres, MYF(0));
+ close_system_tables(thd, &open_tables_state_backup);
+ DBUG_RETURN(true);
+ }
+ close_system_tables(thd, &open_tables_state_backup);
+
+ /* We should already hold a global IX lock and a schema X lock. */
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, "", "",
+ MDL_INTENTION_EXCLUSIVE) &&
+ thd->mdl_context.is_lock_owner(MDL_key::SCHEMA, db, "",
+ MDL_EXCLUSIVE));
+ DBUG_RETURN(thd->mdl_context.acquire_locks(&mdl_requests,
+ thd->variables.lock_wait_timeout));
+}
+
+
+/**
Drop all routines in database 'db'
@note Close the thread tables, the calling code might want to
@@ -1330,6 +1534,7 @@ sp_drop_db_routines(THD *thd, char *db)
TABLE *table;
int ret;
uint key_len;
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
uchar keybuf[MAX_KEY_LENGTH];
DBUG_ENTER("sp_drop_db_routines");
DBUG_PRINT("enter", ("db: %s", db));
@@ -1342,10 +1547,12 @@ sp_drop_db_routines(THD *thd, char *db)
key_len= table->key_info->key_part[0].store_length;
table->field[MYSQL_PROC_FIELD_DB]->get_key_image(keybuf, key_len, Field::itRAW);
-
-
ret= SP_OK;
- table->file->ha_index_init(0, 1);
+ if (table->file->ha_index_init(0, 1))
+ {
+ ret= SP_KEY_NOT_FOUND;
+ goto err_idx_init;
+ }
if (!table->file->ha_index_read_map(table->record[0], keybuf, (key_part_map)1,
HA_READ_KEY_EXACT))
{
@@ -1371,7 +1578,13 @@ sp_drop_db_routines(THD *thd, char *db)
}
table->file->ha_index_end();
+err_idx_init:
close_thread_tables(thd);
+ /*
+ Make sure to only release the MDL lock on mysql.proc, not other
+ metadata locks DROP DATABASE might have acquired.
+ */
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
err:
DBUG_RETURN(ret);
@@ -1397,10 +1610,7 @@ err:
bool
sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name)
{
- bool err_status= TRUE;
sp_head *sp;
- sp_cache **cache = type == TYPE_ENUM_PROCEDURE ?
- &thd->sp_proc_cache : &thd->sp_func_cache;
DBUG_ENTER("sp_show_create_routine");
DBUG_PRINT("enter", ("name: %.*s",
@@ -1410,28 +1620,29 @@ sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name)
DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE ||
type == TYPE_ENUM_FUNCTION);
- if (type == TYPE_ENUM_PROCEDURE)
+ /*
+ @todo: Consider using prelocking for this code as well. Currently
+ SHOW CREATE PROCEDURE/FUNCTION is a dirty read of the data
+ dictionary, i.e. takes no metadata locks.
+ 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))
+ DBUG_RETURN(TRUE);
+
+ if (sp == NULL || sp->show_create_routine(thd, type))
{
/*
- SHOW CREATE PROCEDURE may require two instances of one sp_head
- object when SHOW CREATE PROCEDURE is called for the procedure that
- is being executed. Basically, there is no actual recursion, so we
- increase the recursion limit for this statement (kind of hack).
-
- SHOW CREATE FUNCTION does not require this because SHOW CREATE
- statements are prohibitted within stored functions.
- */
-
- thd->variables.max_sp_recursion_depth++;
+ 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);
+ DBUG_RETURN(TRUE);
}
- if ((sp= sp_find_routine(thd, type, name, cache, FALSE)))
- err_status= sp->show_create_routine(thd, type);
-
- if (type == TYPE_ENUM_PROCEDURE)
- thd->variables.max_sp_recursion_depth--;
-
- DBUG_RETURN(err_status);
+ DBUG_RETURN(FALSE);
}
@@ -1580,7 +1791,7 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any)
&thd->sp_proc_cache, FALSE) != NULL ||
sp_find_routine(thd, TYPE_ENUM_FUNCTION, name,
&thd->sp_func_cache, FALSE) != NULL;
- mysql_reset_errors(thd, TRUE);
+ thd->warning_info->clear_warning_info(thd->query_id);
if (sp_object_found)
{
if (any)
@@ -1597,104 +1808,12 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any)
}
-/**
- Check if a routine exists in the mysql.proc table, without actually
- parsing the definition. (Used for dropping).
-
- @param thd thread context
- @param name name of procedure
-
- @retval
- 0 Success
- @retval
- non-0 Error; SP_OPEN_TABLE_FAILED or SP_KEY_NOT_FOUND
-*/
-
-int
-sp_routine_exists_in_table(THD *thd, stored_procedure_type type, sp_name *name)
-{
- TABLE *table;
- int ret;
- Open_tables_state open_tables_state_backup;
-
- if (!(table= open_proc_table_for_read(thd, &open_tables_state_backup)))
- ret= SP_OPEN_TABLE_FAILED;
- else
- {
- if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK)
- ret= SP_KEY_NOT_FOUND;
- close_system_tables(thd, &open_tables_state_backup);
- }
- return ret;
-}
-
-
-/**
- Structure that represents element in the set of stored routines
- used by statement or routine.
-*/
-struct Sroutine_hash_entry;
-
-struct Sroutine_hash_entry
-{
- /**
- Set key consisting of one-byte routine type and quoted routine name.
- */
- LEX_STRING key;
- /**
- Next element in list linking all routines in set. See also comments
- for LEX::sroutine/sroutine_list and sp_head::m_sroutines.
- */
- Sroutine_hash_entry *next;
- /**
- Uppermost view which directly or indirectly uses this routine.
- 0 if routine is not used in view. Note that it also can be 0 if
- statement uses routine both via view and directly.
- */
- TABLE_LIST *belong_to_view;
-};
-
-
extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen,
my_bool first)
{
Sroutine_hash_entry *rn= (Sroutine_hash_entry *)ptr;
- *plen= rn->key.length;
- return (uchar *)rn->key.str;
-}
-
-
-/**
- Check if
- - current statement (the one in thd->lex) needs table prelocking
- - first routine in thd->lex->sroutines_list needs to execute its body in
- prelocked mode.
-
- @param thd Current thread, thd->lex is the statement to be
- checked.
- @param[out] need_prelocking TRUE - prelocked mode should be activated
- before executing the statement;
- FALSE - Don't activate prelocking
- @param[out] first_no_prelocking TRUE - Tables used by first routine in
- thd->lex->sroutines_list should be
- prelocked. FALSE - Otherwise.
-
- @note
- This function assumes that for any "CALL proc(...)" statement routines_list
- will have 'proc' as first element (it may have several, consider e.g.
- "proc(sp_func(...)))". This property is currently guaranted by the parser.
-*/
-
-void sp_get_prelocking_info(THD *thd, bool *need_prelocking,
- bool *first_no_prelocking)
-{
- Sroutine_hash_entry *routine= thd->lex->sroutines_list.first;
-
- DBUG_ASSERT(routine);
- bool first_is_procedure= (routine->key.str[0] == TYPE_ENUM_PROCEDURE);
-
- *first_no_prelocking= first_is_procedure;
- *need_prelocking= !first_is_procedure || test(routine->next);
+ *plen= rn->mdl_request.key.length();
+ return (uchar *)rn->mdl_request.key.ptr();
}
@@ -1704,11 +1823,11 @@ void sp_get_prelocking_info(THD *thd, bool *need_prelocking,
In case when statement uses stored routines but does not need
prelocking (i.e. it does not use any tables) we will access the
- elements of LEX::sroutines set on prepared statement re-execution.
- Because of this we have to allocate memory for both hash element
- and copy of its key in persistent arena.
+ elements of Query_tables_list::sroutines set on prepared statement
+ re-execution. Because of this we have to allocate memory for both
+ hash element and copy of its key in persistent arena.
- @param lex LEX representing statement
+ @param prelocking_ctx Prelocking context of the statement
@param arena Arena in which memory for new element will be
allocated
@param key Key for the hash representing set
@@ -1716,7 +1835,7 @@ void sp_get_prelocking_info(THD *thd, bool *need_prelocking,
(0 if routine is not used by view)
@note
- Will also add element to end of 'LEX::sroutines_list' list.
+ Will also add element to end of 'Query_tables_list::sroutines_list' list.
@todo
When we will got rid of these accesses on re-executions we will be
@@ -1731,28 +1850,25 @@ void sp_get_prelocking_info(THD *thd, bool *need_prelocking,
the set).
*/
-static bool add_used_routine(LEX *lex, Query_arena *arena,
- const LEX_STRING *key,
- TABLE_LIST *belong_to_view)
+bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
+ const MDL_key *key, TABLE_LIST *belong_to_view)
{
- hash_init_opt(&lex->sroutines, system_charset_info,
- Query_tables_list::START_SROUTINES_HASH_SIZE,
- 0, 0, sp_sroutine_key, 0, 0);
+ my_hash_init_opt(&prelocking_ctx->sroutines, system_charset_info,
+ Query_tables_list::START_SROUTINES_HASH_SIZE,
+ 0, 0, sp_sroutine_key, 0, 0);
- if (!hash_search(&lex->sroutines, (uchar *)key->str, key->length))
+ if (!my_hash_search(&prelocking_ctx->sroutines, key->ptr(), key->length()))
{
Sroutine_hash_entry *rn=
- (Sroutine_hash_entry *)arena->alloc(sizeof(Sroutine_hash_entry) +
- key->length + 1);
+ (Sroutine_hash_entry *)arena->alloc(sizeof(Sroutine_hash_entry));
if (!rn) // OOM. Error will be reported using fatal_error().
return FALSE;
- rn->key.length= key->length;
- rn->key.str= (char *)rn + sizeof(Sroutine_hash_entry);
- memcpy(rn->key.str, key->str, key->length + 1);
- if (my_hash_insert(&lex->sroutines, (uchar *)rn))
+ rn->mdl_request.init(key, MDL_SHARED, MDL_TRANSACTION);
+ if (my_hash_insert(&prelocking_ctx->sroutines, (uchar *)rn))
return FALSE;
- lex->sroutines_list.link_in_list(rn, &rn->next);
+ prelocking_ctx->sroutines_list.link_in_list(rn, &rn->next);
rn->belong_to_view= belong_to_view;
+ rn->m_sp_cache_version= 0;
return TRUE;
}
return FALSE;
@@ -1766,24 +1882,27 @@ static bool add_used_routine(LEX *lex, Query_arena *arena,
To be friendly towards prepared statements one should pass
persistent arena as second argument.
- @param lex LEX representing statement
- @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/...)
+ @param prelocking_ctx Prelocking context of the statement
+ @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 'LEX::sroutines_list' list (and will
- take into account that this is explicitly used routine).
+ 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(LEX *lex, Query_arena *arena,
+void sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
sp_name *rt, enum stored_procedure_type rt_type)
{
- rt->set_routine_type(rt_type);
- (void)add_used_routine(lex, arena, &rt->m_sroutines_key, 0);
- lex->sroutines_list_own_last= lex->sroutines_list.next;
- lex->sroutines_list_own_elements= lex->sroutines_list.elements;
+ 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);
+ prelocking_ctx->sroutines_list_own_last= prelocking_ctx->sroutines_list.next;
+ prelocking_ctx->sroutines_list_own_elements=
+ prelocking_ctx->sroutines_list.elements;
}
@@ -1791,13 +1910,13 @@ void sp_add_used_routine(LEX *lex, Query_arena *arena,
Remove routines which are only indirectly used by statement from
the set of routines used by this statement.
- @param lex LEX representing statement
+ @param prelocking_ctx Prelocking context of the statement
*/
-void sp_remove_not_own_routines(LEX *lex)
+void sp_remove_not_own_routines(Query_tables_list *prelocking_ctx)
{
Sroutine_hash_entry *not_own_rt, *next_rt;
- for (not_own_rt= *lex->sroutines_list_own_last;
+ for (not_own_rt= *prelocking_ctx->sroutines_list_own_last;
not_own_rt; not_own_rt= next_rt)
{
/*
@@ -1805,12 +1924,13 @@ void sp_remove_not_own_routines(LEX *lex)
but we want to be more future-proof.
*/
next_rt= not_own_rt->next;
- hash_delete(&lex->sroutines, (uchar *)not_own_rt);
+ my_hash_delete(&prelocking_ctx->sroutines, (uchar *)not_own_rt);
}
- *lex->sroutines_list_own_last= NULL;
- lex->sroutines_list.next= lex->sroutines_list_own_last;
- lex->sroutines_list.elements= lex->sroutines_list_own_elements;
+ *prelocking_ctx->sroutines_list_own_last= NULL;
+ prelocking_ctx->sroutines_list.next= prelocking_ctx->sroutines_list_own_last;
+ prelocking_ctx->sroutines_list.elements=
+ prelocking_ctx->sroutines_list_own_elements;
}
@@ -1838,8 +1958,9 @@ bool sp_update_sp_used_routines(HASH *dst, HASH *src)
{
for (uint i=0 ; i < src->records ; i++)
{
- Sroutine_hash_entry *rt= (Sroutine_hash_entry *)hash_element(src, i);
- if (!hash_search(dst, (uchar *)rt->key.str, rt->key.length))
+ Sroutine_hash_entry *rt= (Sroutine_hash_entry *)my_hash_element(src, i);
+ if (!my_hash_search(dst, (uchar *)rt->mdl_request.key.ptr(),
+ rt->mdl_request.key.length()))
{
if (my_hash_insert(dst, (uchar *)rt))
return TRUE;
@@ -1854,23 +1975,24 @@ bool sp_update_sp_used_routines(HASH *dst, HASH *src)
routines used by statement.
@param thd Thread context
- @param lex LEX representing statement
+ @param prelocking_ctx Prelocking context of the statement
@param src Hash representing set from which routines will
be added
@param belong_to_view Uppermost view which uses these routines, 0 if none
- @note
- It will also add elements to end of 'LEX::sroutines_list' list.
+ @note It will also add elements to end of
+ 'Query_tables_list::sroutines_list' list.
*/
-static void
-sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src,
- TABLE_LIST *belong_to_view)
+void
+sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
+ HASH *src, TABLE_LIST *belong_to_view)
{
for (uint i=0 ; i < src->records ; i++)
{
- Sroutine_hash_entry *rt= (Sroutine_hash_entry *)hash_element(src, i);
- (void)add_used_routine(lex, thd->stmt_arena, &rt->key, belong_to_view);
+ 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);
}
}
@@ -1880,242 +2002,138 @@ sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src,
routines used by statement.
@param thd Thread context
- @param lex LEX representing statement
+ @param prelocking_ctx Prelocking context of the statement
@param src List representing set from which routines will
be added
@param belong_to_view Uppermost view which uses these routines, 0 if none
- @note
- It will also add elements to end of 'LEX::sroutines_list' list.
+ @note It will also add elements to end of
+ 'Query_tables_list::sroutines_list' list.
*/
-static void sp_update_stmt_used_routines(THD *thd, LEX *lex,
- SQL_I_List<Sroutine_hash_entry> *src,
- TABLE_LIST *belong_to_view)
+void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
+ SQL_I_List<Sroutine_hash_entry> *src,
+ TABLE_LIST *belong_to_view)
{
for (Sroutine_hash_entry *rt= src->first; rt; rt= rt->next)
- (void)add_used_routine(lex, thd->stmt_arena, &rt->key, belong_to_view);
+ (void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena,
+ &rt->mdl_request.key, belong_to_view);
}
/**
- Cache sub-set of routines used by statement, add tables used by these
- routines to statement table list. Do the same for all routines used
- by these routines.
-
- @param thd thread context
- @param lex LEX representing statement
- @param start first routine from the list of routines to be cached
- (this list defines mentioned sub-set).
- @param first_no_prelock If true, don't add tables or cache routines used by
- the body of the first routine (i.e. *start)
- will be executed in non-prelocked mode.
- @param tabs_changed Set to TRUE some tables were added, FALSE otherwise
-
- @note
- If some function is missing this won't be reported here.
- Instead this fact will be discovered during query execution.
-
- @retval
- 0 success
- @retval
- non-0 failure
+ A helper wrapper around sp_cache_routine() to use from
+ prelocking until 'sp_name' is eradicated as a class.
*/
-static int
-sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex,
- Sroutine_hash_entry *start,
- bool first_no_prelock)
+int sp_cache_routine(THD *thd, Sroutine_hash_entry *rt,
+ bool lookup_only, sp_head **sp)
{
- int ret= 0;
- bool first= TRUE;
- DBUG_ENTER("sp_cache_routines_and_add_tables_aux");
-
- for (Sroutine_hash_entry *rt= start; rt; rt= rt->next)
- {
- sp_name name(thd, rt->key.str, rt->key.length);
- stored_procedure_type type= (stored_procedure_type) rt->key.str[0];
- sp_head *sp;
-
- if (type == TYPE_ENUM_TRIGGER)
- continue;
- if (!(sp= sp_cache_lookup((type == TYPE_ENUM_FUNCTION ?
- &thd->sp_func_cache : &thd->sp_proc_cache),
- &name)))
- {
- switch ((ret= db_find_routine(thd, type, &name, &sp)))
- {
- case SP_OK:
- {
- if (type == TYPE_ENUM_FUNCTION)
- sp_cache_insert(&thd->sp_func_cache, sp);
- else
- sp_cache_insert(&thd->sp_proc_cache, sp);
- }
- break;
- case SP_KEY_NOT_FOUND:
- ret= SP_OK;
- break;
- default:
- /* Query might have been killed, don't set error. */
- if (thd->killed)
- break;
-
- /*
- Any error when loading an existing routine is either some problem
- with the mysql.proc table, or a parse error because the contents
- has been tampered with (in which case we clear that error).
- */
- if (ret == SP_PARSE_ERROR)
- thd->clear_error();
- /*
- If we cleared the parse error, or when db_find_routine() flagged
- 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())
- {
- /*
- 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[SAFE_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);
- }
- break;
- }
- }
- if (sp)
- {
- if (!(first && first_no_prelock))
- {
- sp_update_stmt_used_routines(thd, lex, &sp->m_sroutines,
- rt->belong_to_view);
- (void)sp->add_used_tables_to_table_list(thd, &lex->query_tables_last,
- rt->belong_to_view);
- }
- sp->propagate_attributes(lex);
- }
- first= FALSE;
- }
- DBUG_RETURN(ret);
-}
-
-
-/**
- Cache all routines from the set of used by statement, add tables used
- by those routines to statement table list. Do the same for all routines
- used by those routines.
+ 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);
- @param thd thread context
- @param lex LEX representing statement
- @param first_no_prelock If true, don't add tables or cache routines used by
- the body of the first routine (i.e. *start)
-
- @retval
- 0 success
- @retval
- non-0 failure
-*/
+ /*
+ 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);
-int
-sp_cache_routines_and_add_tables(THD *thd, LEX *lex, bool first_no_prelock)
-{
- return sp_cache_routines_and_add_tables_aux(thd, lex,
- lex->sroutines_list.first, first_no_prelock);
+ return sp_cache_routine(thd, type, &name, lookup_only, sp);
}
/**
- Add all routines used by view to the set of routines used by
- statement.
-
- Add tables used by those routines to statement table list. Do the same
- for all routines used by these routines.
-
- @param thd Thread context
- @param lex LEX representing statement
- @param view Table list element representing view
-
- @retval
- 0 success
- @retval
- non-0 failure
+ Ensure that routine is present in cache by loading it from the mysql.proc
+ table if needed. If the routine is present but old, reload it.
+ Emit an appropriate error if there was a problem during
+ 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,
+ but old, don't try to reload.
+ @param[out] sp Pointer to sp_head object for routine, NULL if routine was
+ not found.
+
+ @retval 0 Either routine is found and was succesfully loaded into cache
+ or it does not exist.
+ @retval non-0 Error while loading routine from mysql,proc table.
*/
-int
-sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, TABLE_LIST *view)
+int sp_cache_routine(THD *thd, enum stored_procedure_type type, sp_name *name,
+ bool lookup_only, sp_head **sp)
{
- Sroutine_hash_entry **last_cached_routine_ptr= lex->sroutines_list.next;
- sp_update_stmt_used_routines(thd, lex, &view->view->sroutines_list,
- view->top_table());
- return sp_cache_routines_and_add_tables_aux(thd, lex,
- *last_cached_routine_ptr, FALSE);
-}
-
-
-/**
- Add triggers for table to the set of routines used by statement.
- Add tables used by them to statement table list. Do the same for
- all implicitly used routines.
+ int ret= 0;
+ sp_cache **spc= (type == TYPE_ENUM_FUNCTION ?
+ &thd->sp_func_cache : &thd->sp_proc_cache);
- @param thd thread context
- @param lex LEX respresenting statement
- @param table Table list element for table with trigger
+ DBUG_ENTER("sp_cache_routine");
- @retval
- 0 success
- @retval
- non-0 failure
-*/
+ DBUG_ASSERT(type == TYPE_ENUM_FUNCTION || type == TYPE_ENUM_PROCEDURE);
-int
-sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
- TABLE_LIST *table)
-{
- int ret= 0;
+ *sp= sp_cache_lookup(spc, name);
- Sroutine_hash_entry **last_cached_routine_ptr= lex->sroutines_list.next;
+ if (lookup_only)
+ DBUG_RETURN(SP_OK);
- if (static_cast<int>(table->lock_type) >=
- static_cast<int>(TL_WRITE_ALLOW_WRITE))
+ if (*sp)
{
- for (int i= 0; i < (int)TRG_EVENT_MAX; i++)
- {
- if (table->trg_event_map &
- static_cast<uint8>(1 << static_cast<int>(i)))
+ sp_cache_flush_obsolete(spc, sp);
+ if (*sp)
+ DBUG_RETURN(SP_OK);
+ }
+
+ switch ((ret= db_find_routine(thd, type, name, sp)))
+ {
+ case SP_OK:
+ sp_cache_insert(spc, *sp);
+ break;
+ case SP_KEY_NOT_FOUND:
+ ret= SP_OK;
+ break;
+ default:
+ /* Query might have been killed, don't set error. */
+ if (thd->killed)
+ break;
+ /*
+ Any error when loading an existing routine is either some problem
+ with the mysql.proc table, or a parse error because the contents
+ has been tampered with (in which case we clear that error).
+ */
+ if (ret == SP_PARSE_ERROR)
+ thd->clear_error();
+ /*
+ If we cleared the parse error, or when db_find_routine() flagged
+ 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())
{
- for (int j= 0; j < (int)TRG_ACTION_MAX; j++)
- {
- /* We can have only one trigger per action type currently */
- sp_head *trigger= table->table->triggers->bodies[i][j];
- if (trigger &&
- add_used_routine(lex, thd->stmt_arena, &trigger->m_sroutines_key,
- table->belong_to_view))
- {
- trigger->add_used_tables_to_table_list(thd, &lex->query_tables_last,
- table->belong_to_view);
- trigger->propagate_attributes(lex);
- sp_update_stmt_used_routines(thd, lex,
- &trigger->m_sroutines,
- table->belong_to_view);
- }
- }
+ /*
+ 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);
}
- }
+ break;
}
- ret= sp_cache_routines_and_add_tables_aux(thd, lex,
- *last_cached_routine_ptr,
- FALSE);
- return ret;
+ DBUG_RETURN(ret);
}
@@ -2135,14 +2153,17 @@ create_string(THD *thd, String *buf,
const char *body, ulong bodylen,
st_sp_chistics *chistics,
const LEX_STRING *definer_user,
- const LEX_STRING *definer_host)
+ const LEX_STRING *definer_host,
+ ulonglong sql_mode)
{
+ ulonglong old_sql_mode= thd->variables.sql_mode;
/* 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;
+ thd->variables.sql_mode= sql_mode;
buf->append(STRING_WITH_LEN("CREATE "));
append_definer(thd, buf, definer_user, definer_host);
if (type == TYPE_ENUM_FUNCTION)
@@ -2190,5 +2211,80 @@ create_string(THD *thd, String *buf,
buf->append('\n');
}
buf->append(body, bodylen);
+ thd->variables.sql_mode= old_sql_mode;
return TRUE;
}
+
+
+/**
+ @brief The function loads sp_head struct for information schema purposes
+ (used for I_S ROUTINES & PARAMETERS tables).
+
+ @param[in] thd thread handler
+ @param[in] proc_table mysql.proc table structurte
+ @param[in] db database name
+ @param[in] name sp name
+ @param[in] sql_mode SQL mode
+ @param[in] type Routine type
+ @param[in] returns 'returns' string
+ @param[in] params parameters definition string
+ @param[out] free_sp_head returns 1 if we need to free sp_head struct
+ otherwise returns 0
+
+ @return Pointer on sp_head struct
+ @retval # Pointer on sp_head struct
+ @retval 0 error
+*/
+
+sp_head *
+sp_load_for_information_schema(THD *thd, TABLE *proc_table, String *db,
+ String *name, ulong sql_mode, stored_procedure_type type,
+ const char *returns, const char *params,
+ bool *free_sp_head)
+{
+ 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;
+ 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);
+ *free_sp_head= 0;
+ if ((sp= sp_cache_lookup(spc, &sp_name_obj)))
+ {
+ return sp;
+ }
+
+ 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 (!create_string(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))
+ return 0;
+
+ thd->lex= &newlex;
+ newlex.current_select= NULL;
+ sp= sp_compile(thd, &defstr, sql_mode, creation_ctx);
+ *free_sp_head= 1;
+ thd->lex->sphead= NULL;
+ lex_end(thd->lex);
+ thd->lex= old_lex;
+ return sp;
+}
diff --git a/sql/sp.h b/sql/sp.h
index 8a644aabcf4..3353132346b 100644
--- a/sql/sp.h
+++ b/sql/sp.h
@@ -1,7 +1,5 @@
/* -*- C++ -*- */
-/*
- Copyright (c) 2002-2008 MySQL AB, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2002, 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
@@ -14,12 +12,42 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef _SP_H_
#define _SP_H_
+#include "sql_string.h" // LEX_STRING
+
+class Field;
+class Open_tables_backup;
+class Open_tables_state;
+class Query_arena;
+class Query_tables_list;
+class Sroutine_hash_entry;
+class THD;
+class sp_cache;
+class sp_head;
+class sp_name;
+struct st_sp_chistics;
+struct LEX;
+struct TABLE;
+struct TABLE_LIST;
+typedef struct st_hash HASH;
+template <typename T> class SQL_I_List;
+
+/*
+ Values for the type enum. This reflects the order of the enum declaration
+ in the CREATE TABLE command.
+*/
+enum stored_procedure_type
+{
+ TYPE_ENUM_FUNCTION=1,
+ TYPE_ENUM_PROCEDURE=2,
+ TYPE_ENUM_TRIGGER=3,
+ TYPE_ENUM_PROXY=4
+};
+
/* Tells what SP_DEFAULT_ACCESS should be mapped to */
#define SP_DEFAULT_ACCESS_MAPPING SP_CONTAINS_SQL
@@ -37,20 +65,63 @@
#define SP_BODY_TOO_LONG -10
#define SP_FLD_STORE_FAILED -11
+/* DB storage of Stored PROCEDUREs and FUNCTIONs */
+enum
+{
+ MYSQL_PROC_FIELD_DB = 0,
+ MYSQL_PROC_FIELD_NAME,
+ MYSQL_PROC_MYSQL_TYPE,
+ MYSQL_PROC_FIELD_SPECIFIC_NAME,
+ MYSQL_PROC_FIELD_LANGUAGE,
+ MYSQL_PROC_FIELD_ACCESS,
+ MYSQL_PROC_FIELD_DETERMINISTIC,
+ MYSQL_PROC_FIELD_SECURITY_TYPE,
+ MYSQL_PROC_FIELD_PARAM_LIST,
+ MYSQL_PROC_FIELD_RETURNS,
+ MYSQL_PROC_FIELD_BODY,
+ MYSQL_PROC_FIELD_DEFINER,
+ MYSQL_PROC_FIELD_CREATED,
+ MYSQL_PROC_FIELD_MODIFIED,
+ MYSQL_PROC_FIELD_SQL_MODE,
+ MYSQL_PROC_FIELD_COMMENT,
+ MYSQL_PROC_FIELD_CHARACTER_SET_CLIENT,
+ MYSQL_PROC_FIELD_COLLATION_CONNECTION,
+ MYSQL_PROC_FIELD_DB_COLLATION,
+ MYSQL_PROC_FIELD_BODY_UTF8,
+ MYSQL_PROC_FIELD_COUNT
+};
+
/* Drop all routines in database 'db' */
int
sp_drop_db_routines(THD *thd, char *db);
+/**
+ Acquires exclusive metadata lock on all stored routines in the
+ given database.
+
+ @param thd Thread handler
+ @param db Database name
+
+ @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);
-bool
-sp_exist_routines(THD *thd, TABLE_LIST *procs, bool any);
+int
+sp_cache_routine(THD *thd, Sroutine_hash_entry *rt,
+ bool lookup_only, sp_head **sp);
+
int
-sp_routine_exists_in_table(THD *thd, stored_procedure_type type,
- sp_name *name);
+sp_cache_routine(THD *thd, stored_procedure_type type, sp_name *name,
+ bool lookup_only, sp_head **sp);
+
+bool
+sp_exist_routines(THD *thd, TABLE_LIST *procs, bool any);
bool
sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name);
@@ -65,22 +136,58 @@ sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name,
int
sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name);
+
+/**
+ Structure that represents element in the set of stored routines
+ used by statement or routine.
+*/
+
+class Sroutine_hash_entry
+{
+public:
+ /**
+ Metadata lock request for routine.
+ MDL_key in this request is also used as a key for set.
+ */
+ MDL_request mdl_request;
+ /**
+ Next element in list linking all routines in set. See also comments
+ for LEX::sroutine/sroutine_list and sp_head::m_sroutines.
+ */
+ Sroutine_hash_entry *next;
+ /**
+ Uppermost view which directly or indirectly uses this routine.
+ 0 if routine is not used in view. Note that it also can be 0 if
+ statement uses routine both via view and directly.
+ */
+ TABLE_LIST *belong_to_view;
+ /**
+ This is for prepared statement validation purposes.
+ A statement looks up and pre-loads all its stored functions
+ at prepare. Later on, if a function is gone from the cache,
+ execute may fail.
+ Remember the version of sp_head at prepare to be able to
+ invalidate the prepared statement at execute if it
+ changes.
+ */
+ ulong m_sp_cache_version;
+};
+
+
/*
- Procedures for pre-caching of stored routines and building table list
- for prelocking.
+ Procedures for handling sets of stored routines used by statement or routine.
*/
-void sp_get_prelocking_info(THD *thd, bool *need_prelocking,
- bool *first_no_prelocking);
-void sp_add_used_routine(LEX *lex, Query_arena *arena,
+void sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
sp_name *rt, stored_procedure_type rt_type);
-void sp_remove_not_own_routines(LEX *lex);
+bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
+ const MDL_key *key, 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);
-int sp_cache_routines_and_add_tables(THD *thd, LEX *lex,
- bool first_no_prelock);
-int sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex,
- TABLE_LIST *view);
-int sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
- TABLE_LIST *table);
+void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
+ HASH *src, TABLE_LIST *belong_to_view);
+void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
+ SQL_I_List<Sroutine_hash_entry> *src,
+ TABLE_LIST *belong_to_view);
extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen,
my_bool first);
@@ -89,6 +196,22 @@ extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen,
Routines which allow open/lock and close mysql.proc table even when
we already have some tables open and locked.
*/
-TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup);
+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, ulong 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,
+ CHARSET_INFO **cs);
+
+bool load_collation(MEM_ROOT *mem_root,
+ Field *field,
+ CHARSET_INFO *dflt_cl,
+ CHARSET_INFO **cl);
#endif /* _SP_H_ */
diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc
index 7e98480cffa..14f49ecc077 100644
--- a/sql/sp_cache.cc
+++ b/sql/sp_cache.cc
@@ -1,6 +1,4 @@
-/*
- Copyright (c) 2003-2008 MySQL AB, 2009, 2010 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,17 +11,17 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation
#endif
#include "sp_cache.h"
#include "sp_head.h"
-static pthread_mutex_t Cversion_lock;
+static mysql_mutex_t Cversion_lock;
static ulong volatile Cversion= 0;
@@ -34,8 +32,6 @@ static ulong volatile Cversion= 0;
class sp_cache
{
public:
- ulong version;
-
sp_cache();
~sp_cache();
@@ -53,26 +49,26 @@ public:
inline sp_head *lookup(char *name, uint namelen)
{
- return (sp_head *)hash_search(&m_hashtable, (const uchar *)name, namelen);
+ return (sp_head *) my_hash_search(&m_hashtable, (const uchar *)name,
+ namelen);
}
-#ifdef NOT_USED
- inline bool remove(char *name, uint namelen)
+ inline void remove(sp_head *sp)
{
- sp_head *sp= lookup(name, namelen);
- if (sp)
- {
- hash_delete(&m_hashtable, (uchar *)sp);
- return TRUE;
- }
- return FALSE;
+ my_hash_delete(&m_hashtable, (uchar *)sp);
}
-#endif
- inline void remove_all()
+ /**
+ Remove all elements from a stored routine cache if the current
+ number of elements exceeds the argument value.
+
+ @param[in] upper_limit_for_elements Soft upper limit of elements that
+ can be stored in the cache.
+ */
+ void enforce_limit(ulong upper_limit_for_elements)
{
- cleanup();
- init();
+ if (m_hashtable.records > upper_limit_for_elements)
+ my_hash_reset(&m_hashtable);
}
private:
@@ -83,12 +79,36 @@ private:
HASH m_hashtable;
}; // class sp_cache
+#ifdef HAVE_PSI_INTERFACE
+static PSI_mutex_key key_Cversion_lock;
+
+static PSI_mutex_info all_sp_cache_mutexes[]=
+{
+ { &key_Cversion_lock, "Cversion_lock", PSI_FLAG_GLOBAL}
+};
+
+static void init_sp_cache_psi_keys(void)
+{
+ const char* category= "sql";
+ int count;
+
+ if (PSI_server == NULL)
+ return;
+
+ count= array_elements(all_sp_cache_mutexes);
+ PSI_server->register_mutex(category, all_sp_cache_mutexes, count);
+}
+#endif
/* Initialize the SP caching once at startup */
void sp_cache_init()
{
- pthread_mutex_init(&Cversion_lock, MY_MUTEX_INIT_FAST);
+#ifdef HAVE_PSI_INTERFACE
+ init_sp_cache_psi_keys();
+#endif
+
+ mysql_mutex_init(key_Cversion_lock, &Cversion_lock, MY_MUTEX_INIT_FAST);
}
@@ -117,7 +137,7 @@ void sp_cache_clear(sp_cache **cp)
void sp_cache_end()
{
- pthread_mutex_destroy(&Cversion_lock);
+ mysql_mutex_destroy(&Cversion_lock);
}
@@ -143,8 +163,9 @@ void sp_cache_insert(sp_cache **cp, sp_head *sp)
{
if (!(c= new sp_cache()))
return; // End of memory error
- c->version= Cversion; // No need to lock when reading long variable
}
+ /* 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));
c->insert(sp);
@@ -196,49 +217,52 @@ void sp_cache_invalidate()
}
-/*
- Remove out-of-date SPs from the cache.
-
- SYNOPSIS
- sp_cache_flush_obsolete()
- cp Cache to flush
+/**
+ Remove an out-of-date SP from the cache.
- NOTE
- This invalidates pointers to sp_head objects this thread uses.
- In practice that means 'dont call this function when inside SP'.
+ @param[in] cp Cache to flush
+ @param[in] sp SP to remove.
+
+ @note This invalidates pointers to sp_head objects this thread
+ uses. In practice that means 'dont call this function when
+ inside SP'.
*/
-void sp_cache_flush_obsolete(sp_cache **cp)
+void sp_cache_flush_obsolete(sp_cache **cp, sp_head **sp)
{
- sp_cache *c= *cp;
- if (c)
+ if ((*sp)->sp_cache_version() < Cversion && !(*sp)->is_invoked())
{
- ulong v;
- v= Cversion; // No need to lock when reading long variable
- if (c->version < v)
- {
- DBUG_PRINT("info",("sp_cache: deleting all functions"));
- /* We need to delete all elements. */
- c->remove_all();
- c->version= v;
- }
+ (*cp)->remove(*sp);
+ *sp= NULL;
}
}
/**
- Return the current version of the cache.
+ Return the current global version of the cache.
*/
-ulong sp_cache_version(sp_cache **cp)
+ulong sp_cache_version()
{
- sp_cache *c= *cp;
- if (c)
- return c->version;
- return 0;
+ return Cversion;
}
+/**
+ Enforce that the current number of elements in the cache don't exceed
+ the argument value by flushing the cache if necessary.
+
+ @param[in] c Cache to check
+ @param[in] upper_limit_for_elements Soft upper limit for number of sp_head
+ objects that can be stored in the cache.
+*/
+void
+sp_cache_enforce_limit(sp_cache *c, ulong upper_limit_for_elements)
+{
+ if (c)
+ c->enforce_limit(upper_limit_for_elements);
+}
+
/*************************************************************************
Internal functions
*************************************************************************/
@@ -271,21 +295,20 @@ sp_cache::sp_cache()
sp_cache::~sp_cache()
{
- hash_free(&m_hashtable);
+ my_hash_free(&m_hashtable);
}
void
sp_cache::init()
{
- hash_init(&m_hashtable, system_charset_info, 0, 0, 0,
- hash_get_key_for_sp_head, hash_free_sp_head, 0);
- version= 0;
+ my_hash_init(&m_hashtable, system_charset_info, 0, 0, 0,
+ hash_get_key_for_sp_head, hash_free_sp_head, 0);
}
void
sp_cache::cleanup()
{
- hash_free(&m_hashtable);
+ my_hash_free(&m_hashtable);
}
diff --git a/sql/sp_cache.h b/sql/sp_cache.h
index c22f463b259..b21d4c4bf25 100644
--- a/sql/sp_cache.h
+++ b/sql/sp_cache.h
@@ -1,5 +1,5 @@
/* -*- C++ -*- */
-/* Copyright (c) 2002-2005, 2008 MySQL AB
+/* Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,7 +12,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef _SP_CACHE_H_
#define _SP_CACHE_H_
@@ -21,6 +21,8 @@
#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.
@@ -30,6 +32,7 @@
class sp_head;
class sp_cache;
+class sp_name;
/*
Cache usage scenarios:
@@ -58,7 +61,8 @@ 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);
void sp_cache_invalidate();
-void sp_cache_flush_obsolete(sp_cache **cp);
-ulong sp_cache_version(sp_cache **cp);
+void sp_cache_flush_obsolete(sp_cache **cp, sp_head **sp);
+ulong sp_cache_version();
+void sp_cache_enforce_limit(sp_cache *cp, ulong upper_limit_for_elements);
#endif /* _SP_CACHE_H_ */
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index d1742da9b91..2cd627a2a32 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -1,5 +1,6 @@
/*
- Copyright (c) 2002, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2002, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2011, 2013, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,10 +13,23 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_priv.h"
+#include "unireg.h"
+#include "sql_prepare.h"
+#include "sql_cache.h" // query_cache_*
+#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" // append_query_string, Query_log_event
+#include "sql_derived.h" // mysql_handle_derived
-#include "mysql_priv.h"
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation
#endif
@@ -24,6 +38,10 @@
#include "sp_pcontext.h"
#include "sp_rcontext.h"
#include "sp_cache.h"
+#include "set_var.h"
+#include "sql_parse.h" // cleanup_items
+#include "sql_base.h" // close_thread_tables
+#include "transaction.h" // trans_commit_stmt
/*
Sufficient max length of printed destinations and frame offsets (all uints).
@@ -46,12 +64,8 @@ extern "C" uchar *sp_table_key(const uchar *ptr, size_t *plen, my_bool first);
static void reset_start_time_for_sp(THD *thd)
{
- /*
- Do nothing if the context is a trigger or function because time should be
- constant during the execution of those.
- */
if (!thd->in_sub_stmt)
- thd->set_time();
+ thd->set_start_time();
}
Item_result
@@ -129,10 +143,10 @@ sp_get_item_value(THD *thd, Item *item, String *str)
case STRING_RESULT:
{
String *result= item->val_str(str);
-
+
if (!result)
return NULL;
-
+
{
char buf_holder[STRING_BUFFER_USUAL_SIZE];
String buf(buf_holder, sizeof(buf_holder), result->charset());
@@ -184,7 +198,6 @@ sp_get_flags_for_command(LEX *lex)
}
/* fallthrough */
case SQLCOM_ANALYZE:
- case SQLCOM_BACKUP_TABLE:
case SQLCOM_OPTIMIZE:
case SQLCOM_PRELOAD_KEYS:
case SQLCOM_ASSIGN_TO_KEYCACHE:
@@ -194,9 +207,9 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_SHOW_AUTHORS:
case SQLCOM_SHOW_BINLOGS:
case SQLCOM_SHOW_BINLOG_EVENTS:
+ case SQLCOM_SHOW_RELAYLOG_EVENTS:
case SQLCOM_SHOW_CHARSETS:
case SQLCOM_SHOW_COLLATIONS:
- case SQLCOM_SHOW_COLUMN_TYPES:
case SQLCOM_SHOW_CONTRIBUTORS:
case SQLCOM_SHOW_CREATE:
case SQLCOM_SHOW_CREATE_DB:
@@ -215,7 +228,6 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_SHOW_EVENTS:
case SQLCOM_SHOW_KEYS:
case SQLCOM_SHOW_MASTER_STAT:
- case SQLCOM_SHOW_NEW_MASTER:
case SQLCOM_SHOW_OPEN_TABLES:
case SQLCOM_SHOW_PRIVILEGES:
case SQLCOM_SHOW_PROCESSLIST:
@@ -231,7 +243,6 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_SHOW_VARIABLES:
case SQLCOM_SHOW_WARNS:
case SQLCOM_REPAIR:
- case SQLCOM_RESTORE_TABLE:
flags= sp_head::MULTI_RESULTS;
break;
/*
@@ -286,7 +297,6 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_COMMIT:
case SQLCOM_ROLLBACK:
case SQLCOM_LOAD:
- case SQLCOM_LOAD_MASTER_DATA:
case SQLCOM_LOCK_TABLES:
case SQLCOM_CREATE_PROCEDURE:
case SQLCOM_CREATE_SPFUNCTION:
@@ -373,11 +383,11 @@ sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr)
Save original values and restore them after save.
*/
-
+
thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL;
thd->abort_on_warning=
- test(thd->variables.sql_mode &
- (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES));
+ thd->variables.sql_mode &
+ (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES);
thd->transaction.stmt.modified_non_trans_table= FALSE;
/* Save the value in the field. Convert the value if needed. */
@@ -403,31 +413,34 @@ error:
}
-/*
- *
- * sp_name
- *
- */
+/**
+ Create temporary sp_name object from MDL key.
+
+ @note The lifetime of this object is bound to the lifetime of the MDL_key.
+ This should be fine as sp_name objects created by this constructor
+ are mainly used for SP-cache lookups.
-sp_name::sp_name(THD *thd, char *key, uint key_len)
+ @param key MDL key containing database and routine name.
+ @param qname_buff Buffer to be used for storing quoted routine name
+ (should be at least 2*NAME_LEN+1+1 bytes).
+*/
+
+sp_name::sp_name(const MDL_key *key, char *qname_buff)
{
- m_sroutines_key.str= key;
- m_sroutines_key.length= key_len;
- m_qname.str= ++key;
- m_qname.length= key_len - 1;
- if ((m_name.str= strchr(m_qname.str, '.')))
+ 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)
{
- m_db.length= m_name.str - key;
- m_db.str= strmake_root(thd->mem_root, key, m_db.length);
- m_name.str++;
- m_name.length= m_qname.length - m_db.length - 1;
+ strxmov(qname_buff, m_db.str, ".", m_name.str, NullS);
+ m_qname.length= m_db.length + 1 + m_name.length;
}
else
{
- m_name.str= m_qname.str;
- m_name.length= m_qname.length;
- m_db.str= 0;
- m_db.length= 0;
+ strmov(qname_buff, m_name.str);
+ m_qname.length= m_name.length;
}
m_explicit_name= false;
}
@@ -440,12 +453,10 @@ void
sp_name::init_qname(THD *thd)
{
const uint dot= !!m_db.length;
- /* m_sroutines format: m_type + [database + dot] + name + nul */
- m_sroutines_key.length= 1 + m_db.length + dot + m_name.length;
- if (!(m_sroutines_key.str= (char*) thd->alloc(m_sroutines_key.length + 1)))
+ /* 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;
- m_qname.length= m_sroutines_key.length - 1;
- m_qname.str= m_sroutines_key.str + 1;
sprintf(m_qname.str, "%.*s%.*s%.*s",
(int) m_db.length, (m_db.length ? m_db.str : ""),
dot, ".",
@@ -471,7 +482,7 @@ check_routine_name(LEX_STRING *ident)
{
if (!ident || !ident->str || !ident->str[0] ||
ident->str[ident->length-1] == ' ')
- {
+ {
my_error(ER_SP_WRONG_NAME, MYF(0), ident->str);
return TRUE;
}
@@ -508,7 +519,7 @@ sp_head::operator new(size_t size) throw()
DBUG_RETURN(sp);
}
-void
+void
sp_head::operator delete(void *ptr, size_t size) throw()
{
DBUG_ENTER("sp_head::operator delete");
@@ -530,8 +541,12 @@ sp_head::operator delete(void *ptr, size_t size) throw()
sp_head::sp_head()
- :Query_arena(&main_mem_root, INITIALIZED_FOR_SP),
- m_flags(0), m_recursion_level(0), m_next_cached_sp(0),
+ :Query_arena(&main_mem_root, STMT_INITIALIZED_FOR_SP),
+ m_flags(0),
+ m_sp_cache_version(0),
+ unsafe_flags(0),
+ m_recursion_level(0),
+ m_next_cached_sp(0),
m_cont_level(0)
{
const LEX_STRING str_reset= { NULL, 0 };
@@ -553,8 +568,9 @@ sp_head::sp_head()
m_backpatch.empty();
m_cont_backpatch.empty();
m_lex.empty();
- hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0);
- hash_init(&m_sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key, 0, 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;
@@ -605,9 +621,6 @@ sp_head::init(LEX *lex)
m_defstr.str= NULL;
m_defstr.length= 0;
- m_sroutines_key.str= NULL;
- m_sroutines_key.length= 0;
-
m_return_field_def.charset= NULL;
DBUG_VOID_RETURN;
@@ -637,14 +650,10 @@ sp_head::init_sp_name(THD *thd, sp_name *spname)
if (spname->m_qname.length == 0)
spname->init_qname(thd);
- m_sroutines_key.length= spname->m_sroutines_key.length;
- m_sroutines_key.str= (char*) memdup_root(thd->mem_root,
- spname->m_sroutines_key.str,
- spname->m_sroutines_key.length + 1);
- m_sroutines_key.str[0]= static_cast<char>(m_type);
-
- m_qname.length= m_sroutines_key.length - 1;
- m_qname.str= m_sroutines_key.str + 1;
+ 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;
}
@@ -726,7 +735,7 @@ create_typelib(MEM_ROOT *mem_root, Create_field *field_def, List<String> *src)
String *tmp= it++;
if (String::needs_conversion(tmp->length(), tmp->charset(),
- cs, &dummy))
+ cs, &dummy))
{
uint cnv_errs;
conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
@@ -753,16 +762,6 @@ create_typelib(MEM_ROOT *mem_root, Create_field *field_def, List<String> *src)
}
-int
-sp_head::create(THD *thd)
-{
- DBUG_ENTER("sp_head::create");
- DBUG_PRINT("info", ("type: %d name: %s params: %s body: %s",
- m_type, m_name.str, m_params.str, m_body.str));
-
- DBUG_RETURN(sp_create_routine(thd, m_type, this));
-}
-
sp_head::~sp_head()
{
LEX *lex;
@@ -787,13 +786,14 @@ sp_head::~sp_head()
while ((lex= (LEX *)m_lex.pop()))
{
THD *thd= lex->thd;
+ thd->lex->sphead= NULL;
lex_end(thd->lex);
delete thd->lex;
thd->lex= lex;
}
- hash_free(&m_sptabs);
- hash_free(&m_sroutines);
+ my_hash_free(&m_sptabs);
+ my_hash_free(&m_sroutines);
delete m_next_cached_sp;
@@ -835,7 +835,7 @@ sp_head::create_result_field(uint field_max_length, const char *field_name,
field->stored_in_db= m_return_field_def.stored_in_db;
if (field)
field->init(table);
-
+
DBUG_RETURN(field);
}
@@ -864,7 +864,7 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b)
Statements that have is_update_query(stmt) == FALSE (e.g. SELECTs) are not
written into binary log. Instead we catch function calls the statement
makes and write it into binary log separately (see #3).
-
+
2. PROCEDURE calls
CALL statements are not written into binary log. Instead
@@ -877,8 +877,8 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b)
This substitution is done in subst_spvars().
3. FUNCTION calls
-
- In sp_head::execute_function(), we check
+
+ In sp_head::execute_function(), we check
* If this function invocation is done from a statement that is written
into the binary log.
* If there were any attempts to write events to the binary log during
@@ -886,28 +886,28 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b)
If the answers are No and Yes, we write the function call into the binary
log as "SELECT spfunc(<param1value>, <param2value>, ...)"
-
-
+
+
4. Miscellaneous issues.
-
- 4.1 User variables.
+
+ 4.1 User variables.
When we call mysql_bin_log.write() for an SP statement, thd->user_var_events
- must hold set<{var_name, value}> pairs for all user variables used during
+ must hold set<{var_name, value}> pairs for all user variables used during
the statement execution.
This set is produced by tracking user variable reads during statement
- execution.
+ execution.
For SPs, this has the following implications:
- 1) thd->user_var_events may contain events from several SP statements and
- needs to be valid after exection of these statements was finished. In
+ 1) thd->user_var_events may contain events from several SP statements and
+ needs to be valid after exection of these statements was finished. In
order to achieve that, we
* Allocate user_var_events array elements on appropriate mem_root (grep
for user_var_events_alloc).
* Use is_query_in_union() to determine if user_var_event is created.
-
+
2) We need to empty thd->user_var_events after we have wrote a function
- call. This is currently done by making
+ call. This is currently done by making
reset_dynamic(&thd->user_var_events);
calls in several different places. (TODO cosider moving this into
mysql_bin_log.write() function)
@@ -926,7 +926,7 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b)
Replace thd->query{_length} with a string that one can write to
the binlog.
- The binlog-suitable string is produced by replacing references to SP local
+ The binlog-suitable string is produced by replacing references to SP local
variables with NAME_CONST('sp_var_name', value) calls.
@param thd Current thread.
@@ -963,11 +963,11 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
}
if (!sp_vars_uses.elements())
DBUG_RETURN(FALSE);
-
+
/* Sort SP var refs by their occurences in the query */
sp_vars_uses.sort(cmp_splocal_locations);
- /*
+ /*
Construct a statement string where SP local var refs are replaced
with "NAME_CONST(name, value)"
*/
@@ -975,7 +975,7 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
cur= query_str->str;
prev_pos= res= 0;
thd->query_name_consts= 0;
-
+
for (Item_splocal **splocal= sp_vars_uses.front();
splocal < sp_vars_uses.back(); splocal++)
{
@@ -985,16 +985,27 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
String str_value_holder(str_buffer, sizeof(str_buffer),
&my_charset_latin1);
String *str_value;
-
+
/* append the text between sp ref occurences */
res|= qbuf.append(cur + prev_pos, (*splocal)->pos_in_query - prev_pos);
prev_pos= (*splocal)->pos_in_query + (*splocal)->len_in_query;
-
+
+ res|= (*splocal)->fix_fields(thd, (Item **) splocal);
+ if (res)
+ break;
+
+ if ((*splocal)->limit_clause_param)
+ {
+ res|= qbuf.append_ulonglong((*splocal)->val_uint());
+ if (res)
+ break;
+ continue;
+ }
+
/* append the spvar substitute */
res|= qbuf.append(STRING_WITH_LEN(" NAME_CONST('"));
res|= qbuf.append((*splocal)->m_name.str, (*splocal)->m_name.length);
res|= qbuf.append(STRING_WITH_LEN("',"));
- res|= (*splocal)->fix_fields(thd, (Item **) splocal);
if (res)
break;
@@ -1009,11 +1020,11 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
res|= qbuf.append(')');
if (res)
break;
-
+
thd->query_name_consts++;
}
- res|= qbuf.append(cur + prev_pos, query_str->length - prev_pos);
- if (res)
+ if (res ||
+ qbuf.append(cur + prev_pos, query_str->length - prev_pos))
DBUG_RETURN(TRUE);
/*
@@ -1046,16 +1057,14 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
}
-/*
+/**
Return appropriate error about recursion limit reaching
- SYNOPSIS
- sp_head::recursion_level_error()
- thd Thread handle
+ @param thd Thread handle
- NOTE
- For functions and triggers we return error about prohibited recursion.
- For stored procedures we return about reaching recursion limit.
+ @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)
@@ -1072,12 +1081,119 @@ void sp_head::recursion_level_error(THD *thd)
/**
+ Find an SQL handler for any condition (warning or error) after execution
+ of a stored routine instruction. Basically, this function looks for an
+ appropriate SQL handler in RT-contexts. If an SQL handler is found, it is
+ remembered in the RT-context for future activation (the context can be
+ inactive at the moment).
+
+ If there is no pending condition, the function just returns.
+
+ If there was an error during the execution, an SQL handler for it will be
+ searched within the current and outer scopes.
+
+ There might be several errors in the Warning Info (that's possible by using
+ SIGNAL/RESIGNAL in nested scopes) -- the function is looking for an SQL
+ handler for the latest (current) error only.
+
+ If there was a warning during the execution, an SQL handler for it will be
+ searched within the current scope only.
+
+ If several warnings were thrown during the execution and there are different
+ SQL handlers for them, it is not determined which SQL handler will be chosen.
+ Only one SQL handler will be executed.
+
+ If warnings and errors were thrown during the execution, the error takes
+ precedence. I.e. error handler will be executed. If there is no handler
+ for that error, condition will remain unhandled.
+
+ Once a warning or an error has been handled it is not removed from
+ Warning Info.
+
+ According to The Standard (quoting PeterG):
+
+ An SQL procedure statement works like this ...
+ SQL/Foundation 13.5 <SQL procedure statement>
+ (General Rules) (greatly summarized) says:
+ (1) Empty diagnostics area, thus clearing the condition.
+ (2) Execute statement.
+ During execution, if Exception Condition occurs,
+ set Condition Area = Exception Condition and stop
+ statement.
+ During execution, if No Data occurs,
+ set Condition Area = No Data Condition and continue
+ statement.
+ During execution, if Warning occurs,
+ and Condition Area is not already full due to
+ an earlier No Data condition, set Condition Area
+ = Warning and continue statement.
+ (3) Finish statement.
+ At end of execution, if Condition Area is not
+ already full due to an earlier No Data or Warning,
+ set Condition Area = Successful Completion.
+ In effect, this system means there is a precedence:
+ Exception trumps No Data, No Data trumps Warning,
+ Warning trumps Successful Completion.
+
+ NB: "Procedure statements" include any DDL or DML or
+ control statements. So CREATE and DELETE and WHILE
+ and CALL and RETURN are procedure statements. But
+ DECLARE and END are not procedure statements.
+
+ @param thd thread handle
+ @param ctx runtime context of the stored routine
+*/
+
+static void
+find_handler_after_execution(THD *thd, sp_rcontext *ctx)
+{
+ if (thd->is_error())
+ {
+ ctx->find_handler(thd,
+ thd->stmt_da->sql_errno(),
+ thd->stmt_da->get_sqlstate(),
+ MYSQL_ERROR::WARN_LEVEL_ERROR,
+ thd->stmt_da->message());
+ }
+ else if (thd->warning_info->statement_warn_count())
+ {
+ List_iterator<MYSQL_ERROR> it(thd->warning_info->warn_list());
+ MYSQL_ERROR *err;
+ while ((err= it++))
+ {
+ if ((err->get_level() != MYSQL_ERROR::WARN_LEVEL_WARN &&
+ err->get_level() != MYSQL_ERROR::WARN_LEVEL_NOTE) ||
+ err->handled())
+ continue;
+
+ if (ctx->find_handler(thd,
+ err->get_sql_errno(),
+ err->get_sqlstate(),
+ err->get_level(),
+ err->get_message_text()))
+ {
+ err->mark_handled();
+ break;
+ }
+ }
+ }
+}
+
+
+/**
Execute the routine. The main instruction jump loop is there.
Assume the parameters already set.
+
+ @param thd Thread context.
+ @param merge_da_on_success Flag specifying if Warning Info should be
+ propagated to the caller on Completion
+ Condition or not.
+
@todo
- - Will write this SP statement into binlog separately
+ - Will write this SP statement into binlog separately
(TODO: consider changing the condition to "not inside event union")
+ @return Error status.
@retval
FALSE on success
@retval
@@ -1085,33 +1201,60 @@ void sp_head::recursion_level_error(THD *thd)
*/
bool
-sp_head::execute(THD *thd)
+sp_head::execute(THD *thd, bool merge_da_on_success)
{
DBUG_ENTER("sp_head::execute");
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) };
bool cur_db_changed= FALSE;
- sp_rcontext *ctx;
+ sp_rcontext *ctx= thd->spcont;
bool err_status= FALSE;
uint ip= 0;
- ulong save_sql_mode;
+ ulonglong save_sql_mode;
bool save_abort_on_warning;
Query_arena *old_arena;
/* per-instruction arena */
MEM_ROOT execute_mem_root;
- Query_arena execute_arena(&execute_mem_root, INITIALIZED_FOR_SP),
+ Query_arena execute_arena(&execute_mem_root, STMT_INITIALIZED_FOR_SP),
backup_arena;
query_id_t old_query_id;
TABLE *old_derived_tables;
LEX *old_lex;
Item_change_list old_change_list;
String old_packet;
+ uint old_server_status;
+ const uint status_backup_mask= SERVER_STATUS_CURSOR_EXISTS |
+ SERVER_STATUS_LAST_ROW_SENT;
Reprepare_observer *save_reprepare_observer= thd->m_reprepare_observer;
-
Object_creation_ctx *saved_creation_ctx;
+ Warning_info *saved_warning_info;
+ Warning_info warning_info(thd->warning_info->warn_id(), false);
- /* Use some extra margin for possible SP recursion and functions */
+ /*
+ Just reporting a stack overrun error
+ (@sa check_stack_overrun()) requires stack memory for error
+ message buffer. Thus, we have to put the below check
+ relatively close to the beginning of the execution stack,
+ where available stack margin is still big. As long as the check
+ has to be fairly high up the call stack, the amount of memory
+ we "book" for has to stay fairly high as well, and hence
+ not very accurate. The number below has been calculated
+ by trial and error, and reflects the amount of memory necessary
+ to execute a single stored procedure instruction, be it either
+ an SQL statement, or, heaviest of all, a CALL, which involves
+ parsing and loading of another stored procedure into the cache
+ (@sa db_load_routine() and Bug#10100).
+ At the time of measuring, a recursive SP invocation required
+ 3232 bytes of stack on 32 bit Linux, 6016 bytes on 64 bit Mac
+ and 11152 on 64 bit Solaris sparc.
+ The same with db_load_routine() required circa 7k bytes and
+ 14k bytes accordingly. Hence, here we book the stack with some
+ reasonable margin.
+
+ Reverting back to 8 * STACK_MIN_SIZE until further fix.
+ 8 * STACK_MIN_SIZE is required on some exotic platforms.
+ */
if (check_stack_overrun(thd, 8 * STACK_MIN_SIZE, (uchar*)&old_packet))
DBUG_RETURN(TRUE);
@@ -1153,11 +1296,14 @@ sp_head::execute(THD *thd)
goto done;
}
- if ((ctx= thd->spcont))
- ctx->clear_handler();
thd->is_slave_error= 0;
old_arena= thd->stmt_arena;
+ /* Push a new warning information area. */
+ warning_info.append_warning_info(thd, thd->warning_info);
+ saved_warning_info= thd->warning_info;
+ thd->warning_info= &warning_info;
+
/*
Switch query context. This has to be done early as this is sometimes
allocated trough sql_alloc
@@ -1204,8 +1350,7 @@ sp_head::execute(THD *thd)
We should also save Item tree change list to avoid rollback something
too early in the calling query.
*/
- old_change_list= thd->change_list;
- thd->change_list.empty();
+ thd->change_list.move_elements_to(&old_change_list);
/*
Cursors will use thd->packet, so they may corrupt data which was prepared
for sending by upper level. OTOH cursors in the same routine can share this
@@ -1215,6 +1360,7 @@ sp_head::execute(THD *thd)
It is probably safe to use same thd->convert_buff everywhere.
*/
old_packet.swap(thd->packet);
+ old_server_status= thd->server_status & status_backup_mask;
/*
Switch to per-instruction arena here. We can do it since we cleanup
@@ -1228,17 +1374,16 @@ sp_head::execute(THD *thd)
*/
thd->spcont->callers_arena= &backup_arena;
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+#if defined(ENABLED_PROFILING)
/* Discard the initial part of executing routines. */
thd->profiling.discard_current_query();
#endif
do
{
sp_instr *i;
- uint hip; // Handler ip
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
- /*
+#if defined(ENABLED_PROFILING)
+ /*
Treat each "instr" of a routine as discrete unit that could be profiled.
Profiling only records information for segments of code that set the
source of the query, and almost all kinds of instructions in s-p do not.
@@ -1247,15 +1392,19 @@ sp_head::execute(THD *thd)
thd->profiling.start_new_query("continuing inside routine");
#endif
- i = get_instr(ip); // Returns NULL when we're done.
+ /* get_instr returns NULL when we're done. */
+ i = get_instr(ip);
if (i == NULL)
{
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+#if defined(ENABLED_PROFILING)
thd->profiling.discard_current_query();
#endif
break;
}
+ /* Reset number of warnings for this query. */
+ thd->warning_info->reset_for_next_command();
+
DBUG_PRINT("execute", ("Instruction %u", ip));
/*
@@ -1272,67 +1421,60 @@ sp_head::execute(THD *thd)
items made during other permanent subquery transformations).
*/
thd->stmt_arena= i;
-
- /*
- Will write this SP statement into binlog separately
- (TODO: consider changing the condition to "not inside event union")
+
+ /*
+ Will write this SP statement into binlog separately.
+ TODO: consider changing the condition to "not inside event union".
*/
- if (thd->prelocked_mode == NON_PRELOCKED)
+ MEM_ROOT *user_var_events_alloc_saved= thd->user_var_events_alloc;
+ if (thd->locked_tables_mode <= LTM_LOCK_TABLES)
thd->user_var_events_alloc= thd->mem_root;
err_status= i->execute(thd, &ip);
if (i->free_list)
cleanup_items(i->free_list);
-
- /*
+
+ /*
If we've set thd->user_var_events_alloc to mem_root of this SP
statement, clean all the events allocated in it.
*/
- if (thd->prelocked_mode == NON_PRELOCKED)
+ if (thd->locked_tables_mode <= LTM_LOCK_TABLES)
{
reset_dynamic(&thd->user_var_events);
- thd->user_var_events_alloc= NULL;//DEBUG
+ thd->user_var_events_alloc= user_var_events_alloc_saved;
}
/* we should cleanup free_list and memroot, used by instruction */
thd->cleanup_after_query();
- free_root(&execute_mem_root, MYF(0));
+ free_root(&execute_mem_root, MYF(0));
/*
- Check if an exception has occurred and a handler has been found
- Note: We have to check even if err_status == FALSE, since warnings (and
- some errors) don't return a non-zero value. We also have to check even
- if thd->killed != 0, since some errors return with this even when a
- handler has been found (e.g. "bad data").
+ Find and process SQL handlers unless it is a fatal error (fatal
+ errors are not catchable by SQL handlers) or the connection has been
+ killed during execution.
*/
- if (ctx)
+ if (!thd->is_fatal_error && !thd->killed_errno())
{
- uint hf;
-
- switch (ctx->found_handler(&hip, &hf)) {
- case SP_HANDLER_NONE:
- break;
- case SP_HANDLER_CONTINUE:
- thd->restore_active_arena(&execute_arena, &backup_arena);
- thd->set_n_backup_active_arena(&execute_arena, &backup_arena);
- ctx->push_hstack(i->get_cont_dest());
- // Fall through
- default:
- ip= hip;
- err_status= FALSE;
- ctx->clear_handler();
- ctx->enter_handler(hip);
- thd->clear_error();
- thd->is_fatal_error= 0;
- thd->killed= NOT_KILLED;
- thd->mysys_var->abort= 0;
- continue;
- }
+ /*
+ Find SQL handler in the appropriate RT-contexts:
+ - warnings can be handled by SQL handlers within
+ the current scope only;
+ - errors can be handled by any SQL handler from outer scope.
+ */
+ find_handler_after_execution(thd, ctx);
+
+ /* If found, activate handler for the current scope. */
+ if (ctx->activate_handler(thd, &ip, i, &execute_arena, &backup_arena))
+ err_status= FALSE;
}
- } while (!err_status && !thd->killed);
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ /* Reset sp_rcontext::end_partial_result_set flag. */
+ ctx->end_partial_result_set= FALSE;
+
+ } while (!err_status && !thd->killed && !thd->is_fatal_error);
+
+#if defined(ENABLED_PROFILING)
thd->profiling.finish_current_query();
thd->profiling.start_new_query("tail end of routine");
#endif
@@ -1348,13 +1490,12 @@ sp_head::execute(THD *thd)
thd->spcont->pop_all_cursors(); // To avoid memory leaks after an error
/* Restore all saved */
+ thd->server_status= (thd->server_status & ~status_backup_mask) | old_server_status;
old_packet.swap(thd->packet);
DBUG_ASSERT(thd->change_list.is_empty());
- thd->change_list= old_change_list;
- /* To avoid wiping out thd->change_list on old_change_list destruction */
- old_change_list.empty();
+ old_change_list.move_elements_to(&thd->change_list);
thd->lex= old_lex;
- thd->query_id= old_query_id;
+ thd->set_query_id(old_query_id);
DBUG_ASSERT(!thd->derived_tables);
thd->derived_tables= old_derived_tables;
thd->variables.sql_mode= save_sql_mode;
@@ -1362,11 +1503,22 @@ sp_head::execute(THD *thd)
thd->m_reprepare_observer= save_reprepare_observer;
thd->stmt_arena= old_arena;
- state= EXECUTED;
+ state= STMT_EXECUTED;
+
+ /*
+ Restore the caller's original warning information area:
+ - warnings generated during trigger execution should not be
+ propagated to the caller on success;
+ - if there was an exception during execution, warning info should be
+ propagated to the caller in any case.
+ */
+ if (err_status || merge_da_on_success)
+ saved_warning_info->merge_with_routine_info(thd, thd->warning_info);
+ thd->warning_info= saved_warning_info;
done:
DBUG_PRINT("info", ("err_status: %d killed: %d is_slave_error: %d report_error: %d",
- err_status, thd->killed, thd->is_slave_error,
+ err_status, thd->killed, thd->is_slave_error,
thd->is_error()));
if (thd->killed)
@@ -1375,7 +1527,7 @@ sp_head::execute(THD *thd)
If the DB has changed, the pointer has changed too, but the
original thd->db will then have been freed
*/
- if (cur_db_changed && thd->killed < KILL_CONNECTION)
+ if (cur_db_changed && thd->killed != KILL_CONNECTION)
{
/*
Force switching back to the saved current database, because it may be
@@ -1509,7 +1661,7 @@ sp_head::execute_trigger(THD *thd,
sp_rcontext *nctx = NULL;
bool err_status= FALSE;
MEM_ROOT call_mem_root;
- Query_arena call_arena(&call_mem_root, Query_arena::INITIALIZED_FOR_SP);
+ Query_arena call_arena(&call_mem_root, Query_arena::STMT_INITIALIZED_FOR_SP);
Query_arena backup_arena;
DBUG_ENTER("sp_head::execute_trigger");
@@ -1583,7 +1735,7 @@ sp_head::execute_trigger(THD *thd,
thd->spcont= nctx;
- err_status= execute(thd);
+ err_status= execute(thd, FALSE);
err_with_cleanup:
thd->restore_active_arena(&call_arena, &backup_arena);
@@ -1642,7 +1794,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
Field *return_value_fld)
{
ulonglong binlog_save_options;
- bool need_binlog_call;
+ bool need_binlog_call= FALSE;
uint arg_no;
sp_rcontext *octx = thd->spcont;
sp_rcontext *nctx = NULL;
@@ -1650,7 +1802,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
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::INITIALIZED_FOR_SP);
+ 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));
@@ -1722,7 +1874,8 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
each substatement be binlogged its way.
*/
need_binlog_call= mysql_bin_log.is_open() &&
- (thd->options & OPTION_BIN_LOG) && !thd->current_stmt_binlog_row_based;
+ (thd->variables.option_bits & OPTION_BIN_LOG) &&
+ !thd->is_current_stmt_binlog_format_row();
/*
Remember the original arguments for unrolled replication of functions
@@ -1781,12 +1934,12 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
as one select and not resetting THD::user_var_events before
each invocation.
*/
- VOID(pthread_mutex_lock(&LOCK_thread_count));
+ mysql_mutex_lock(&LOCK_thread_count);
q= global_query_id;
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ mysql_mutex_unlock(&LOCK_thread_count);
mysql_bin_log.start_union_events(thd, q + 1);
- binlog_save_options= thd->options;
- thd->options&= ~OPTION_BIN_LOG;
+ binlog_save_options= thd->variables.option_bits;
+ thd->variables.option_bits&= ~OPTION_BIN_LOG;
}
/*
@@ -1799,19 +1952,19 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
*/
thd->set_n_backup_active_arena(&call_arena, &backup_arena);
- err_status= execute(thd);
+ err_status= execute(thd, TRUE);
thd->restore_active_arena(&call_arena, &backup_arena);
if (need_binlog_call)
{
mysql_bin_log.stop_union_events(thd);
- thd->options= binlog_save_options;
+ thd->variables.option_bits= binlog_save_options;
if (thd->binlog_evt_union.unioned_events)
{
int errcode = query_error_code(thd, thd->killed == NOT_KILLED);
Query_log_event qinfo(thd, binlog_buf.ptr(), binlog_buf.length(),
- thd->binlog_evt_union.unioned_events_trans, FALSE, errcode);
+ thd->binlog_evt_union.unioned_events_trans, FALSE, FALSE, errcode);
if (mysql_bin_log.write(&qinfo) &&
thd->binlog_evt_union.unioned_events_trans)
{
@@ -1848,15 +2001,23 @@ err_with_cleanup:
free_root(&call_mem_root, MYF(0));
thd->spcont= octx;
+ /*
+ If not insided a procedure and a function printing warning
+ messsages.
+ */
+ if (need_binlog_call &&
+ thd->spcont == NULL && !thd->binlog_evt_union.do_union)
+ thd->issue_unsafe_warnings();
+
DBUG_RETURN(err_status);
}
/**
- Execute a procedure.
+ Execute a procedure.
The function does the following steps:
- - Set all parameters
+ - Set all parameters
- changes security context for SUID routines
- call sp_head::execute
- copy back values of INOUT and OUT parameters
@@ -1876,6 +2037,8 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
{
bool err_status= FALSE;
uint params = m_pcont->context_var_count();
+ /* Query start time may be reset in a multi-stmt SP; keep this for later. */
+ ulonglong utime_before_sp_exec= thd->utime_after_lock;
sp_rcontext *save_spcont, *octx;
sp_rcontext *nctx = NULL;
bool save_enable_slow_log;
@@ -1892,14 +2055,14 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
save_spcont= octx= thd->spcont;
if (! octx)
- { // Create a temporary old context
- if (!(octx= new sp_rcontext(m_pcont, NULL, octx)) ||
- octx->init(thd))
+ {
+ /* Create a temporary old context. */
+ if (!(octx= new sp_rcontext(m_pcont, NULL, octx)) || octx->init(thd))
{
delete octx; /* Delete octx if it was init() that failed. */
DBUG_RETURN(TRUE);
}
-
+
#ifndef DBUG_OFF
octx->sp= 0;
#endif
@@ -1975,18 +2138,39 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
}
}
- /*
- Okay, got values for all arguments. Close tables that might be used by
- arguments evaluation. If arguments evaluation required prelocking mode,
+ /*
+ Okay, got values for all arguments. Close tables that might be used by
+ arguments evaluation. If arguments evaluation required prelocking mode,
we'll leave it here.
*/
+ thd->lex->unit.cleanup();
+
if (!thd->in_sub_stmt)
{
- thd->lex->unit.cleanup();
- close_thread_tables(thd);
- thd->rollback_item_tree_changes();
+ thd->stmt_da->can_overwrite_status= TRUE;
+ thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
+ thd->stmt_da->can_overwrite_status= FALSE;
}
+ thd_proc_info(thd, "closing tables");
+ close_thread_tables(thd);
+ thd_proc_info(thd, 0);
+
+ if (! thd->in_sub_stmt)
+ {
+ if (thd->transaction_rollback_request)
+ {
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+ else if (! thd->in_multi_stmt_transaction_mode())
+ thd->mdl_context.release_transactional_locks();
+ else
+ thd->mdl_context.release_statement_locks();
+ }
+
+ thd->rollback_item_tree_changes();
+
DBUG_PRINT("info",(" %.*s: eval args done", (int) m_name.length,
m_name.str));
}
@@ -1996,12 +2180,12 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
DBUG_PRINT("info", ("Disabling slow log for the execution"));
thd->enable_slow_log= FALSE;
}
- if (!(m_flags & LOG_GENERAL_LOG) && !(thd->options & OPTION_LOG_OFF))
+ if (!(m_flags & LOG_GENERAL_LOG) && !(thd->variables.option_bits & OPTION_LOG_OFF))
{
DBUG_PRINT("info", ("Disabling general log for the execution"));
save_log_general= true;
/* disable this bit */
- thd->options |= OPTION_LOG_OFF;
+ thd->variables.option_bits |= OPTION_LOG_OFF;
}
thd->spcont= nctx;
@@ -2012,10 +2196,10 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
#endif
if (!err_status)
- err_status= execute(thd);
+ err_status= execute(thd, TRUE);
if (save_log_general)
- thd->options &= ~OPTION_LOG_OFF;
+ 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
@@ -2055,6 +2239,16 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
err_status= TRUE;
break;
}
+
+ Send_field *out_param_info= new (thd->mem_root) Send_field();
+ nctx->get_item(i)->make_field(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;
+
+ srp->set_out_param_info(out_param_info);
}
}
@@ -2068,6 +2262,19 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
delete nctx;
thd->spcont= save_spcont;
+ thd->utime_after_lock= utime_before_sp_exec;
+
+ /*
+ If not insided a procedure and a function printing warning
+ messsages.
+ */
+ bool need_binlog_call= mysql_bin_log.is_open() &&
+ (thd->variables.option_bits & OPTION_BIN_LOG) &&
+ !thd->is_current_stmt_binlog_format_row();
+ if (need_binlog_call && thd->spcont == NULL &&
+ !thd->binlog_evt_union.do_union)
+ thd->issue_unsafe_warnings();
+
DBUG_RETURN(err_status);
}
@@ -2117,6 +2324,9 @@ sp_head::reset_lex(THD *thd)
sublex->uint_geom_type= 0;
sublex->vcol_info= 0;
+ /* Reset part of parser state which needs this. */
+ thd->m_parser_state->m_yacc.reset_before_substatement();
+
DBUG_RETURN(FALSE);
}
@@ -2142,17 +2352,14 @@ sp_head::restore_lex(THD *thd)
oldlex= (LEX *)m_lex.pop();
if (! oldlex)
- DBUG_RETURN(FALSE); // Nothing to restore
+ DBUG_RETURN(FALSE); // Nothing to restore
oldlex->trg_table_fields.push_back(&sublex->trg_table_fields);
- /*
- If this substatement needs row-based, the entire routine does too (we
- cannot switch from statement-based to row-based only for this
- substatement).
- */
- if (sublex->is_stmt_unsafe())
- m_flags|= BINLOG_ROW_BASED_IF_MIXED;
+ /* 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()));
+ unsafe_flags|= sublex->get_stmt_unsafe_flags();
/*
Add routines which are used by statement to respective set for
@@ -2167,6 +2374,7 @@ sp_head::restore_lex(THD *thd)
merge_table_list(thd, sublex->query_tables, sublex);
if (! sublex->sp_lex_in_use)
{
+ sublex->sphead= NULL;
lex_end(sublex);
delete sublex;
}
@@ -2299,7 +2507,7 @@ sp_head::do_cont_backpatch()
void
sp_head::set_info(longlong created, longlong modified,
- st_sp_chistics *chistics, ulong sql_mode)
+ st_sp_chistics *chistics, ulonglong sql_mode)
{
m_created= created;
m_modified= modified;
@@ -2309,8 +2517,8 @@ sp_head::set_info(longlong created, longlong modified,
m_chistics->comment.str= 0;
else
m_chistics->comment.str= strmake_root(mem_root,
- m_chistics->comment.str,
- m_chistics->comment.length);
+ m_chistics->comment.str,
+ m_chistics->comment.length);
m_sql_mode= sql_mode;
}
@@ -2351,7 +2559,7 @@ sp_head::reset_thd_mem_root(THD *thd)
DBUG_PRINT("info", ("mem_root 0x%lx moved to thd mem root 0x%lx",
(ulong) &mem_root, (ulong) &thd->mem_root));
free_list= thd->free_list; // Keep the old list
- thd->free_list= NULL; // Start a new one
+ thd->free_list= NULL; // Start a new one
m_thd= thd;
DBUG_VOID_RETURN;
}
@@ -2377,11 +2585,11 @@ sp_head::restore_thd_mem_root(THD *thd)
Item *flist= free_list; // The old list
set_query_arena(thd); // Get new free_list and mem_root
- state= INITIALIZED_FOR_SP;
+ state= STMT_INITIALIZED_FOR_SP;
DBUG_PRINT("info", ("mem_root 0x%lx returned from thd mem root 0x%lx",
(ulong) &mem_root, (ulong) &thd->mem_root));
- thd->free_list= flist; // Restore the old one
+ thd->free_list= flist; // Restore the old one
thd->mem_root= m_thd_root;
m_thd= NULL;
DBUG_VOID_RETURN;
@@ -2391,10 +2599,10 @@ sp_head::restore_thd_mem_root(THD *thd)
/**
Check if a user has access right to a routine.
- @param thd Thread handler
- @param sp SP
- @param full_access Set to 1 if the user has SELECT right to the
- 'mysql.proc' able or is the owner of the routine
+ @param thd Thread handler
+ @param sp SP
+ @param full_access Set to 1 if the user has SELECT right to the
+ 'mysql.proc' able or is the owner of the routine
@retval
false ok
@retval
@@ -2407,7 +2615,8 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access)
bzero((char*) &tables,sizeof(tables));
tables.db= (char*) "mysql";
tables.table_name= tables.alias= (char*) "proc";
- *full_access= ((!check_table_access(thd, SELECT_ACL, &tables, 1, TRUE) &&
+ *full_access= ((!check_table_access(thd, SELECT_ACL, &tables, FALSE,
+ 1, TRUE) &&
(tables.grant.privilege & SELECT_ACL) != 0) ||
(!strcmp(sp->m_definer_user.str,
thd->security_ctx->priv_user) &&
@@ -2459,7 +2668,7 @@ sp_head::show_create_routine(THD *thd, int type)
if (check_show_routine_access(thd, this, &full_access))
DBUG_RETURN(TRUE);
- sys_var::make_set(thd, m_sql_mode, &sql_mode_typelib, &sql_mode);
+ sql_mode_string_representation(thd, m_sql_mode, &sql_mode);
/* Send header. */
@@ -2490,7 +2699,7 @@ sp_head::show_create_routine(THD *thd, int type)
fields.push_back(new Item_empty_string("Database Collation",
MY_CS_NAME_SIZE));
- if (protocol->send_fields(&fields,
+ if (protocol->send_result_set_metadata(&fields,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
{
DBUG_RETURN(TRUE);
@@ -2523,8 +2732,6 @@ sp_head::show_create_routine(THD *thd, int type)
}
-
-
/**
Add instruction to SP.
@@ -2582,12 +2789,13 @@ void sp_head::optimize()
else
{
if (src != dst)
- { // Move the instruction and update prev. jumps
- sp_instr *ibp;
- List_iterator_fast<sp_instr> li(bp);
+ {
+ /* Move the instruction and update prev. jumps */
+ sp_instr *ibp;
+ List_iterator_fast<sp_instr> li(bp);
- set_dynamic(&m_instr, (uchar*)&i, dst);
- while ((ibp= li++))
+ set_dynamic(&m_instr, (uchar*)&i, dst);
+ while ((ibp= li++))
{
sp_instr_opt_meta *im= static_cast<sp_instr_opt_meta *>(ibp);
im->set_destination(src, dst);
@@ -2675,14 +2883,14 @@ sp_head::show_routine_code(THD *thd)
field_list.push_back(new Item_uint("Pos", 9));
// 1024 is for not to confuse old clients
field_list.push_back(new Item_empty_string("Instruction",
- max(buffer.length(), 1024)));
- if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
+ max(buffer.length(), 1024)));
+ if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS |
Protocol::SEND_EOF))
DBUG_RETURN(1);
for (ip= 0; (i = get_instr(ip)) ; ip++)
{
- /*
+ /*
Consistency check. If these are different something went wrong
during optimization.
*/
@@ -2745,7 +2953,7 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
int res= 0;
DBUG_ENTER("reset_lex_and_exec_core");
- /*
+ /*
The flag is saved at the entry to the following substatement.
It's reset further in the common code part.
It's merged with the saved parent's value at the exit of this func.
@@ -2761,11 +2969,9 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
*/
thd->lex= m_lex;
- VOID(pthread_mutex_lock(&LOCK_thread_count));
- thd->query_id= next_query_id();
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ thd->set_query_id(next_query_id());
- if (thd->prelocked_mode == NON_PRELOCKED)
+ if (thd->locked_tables_mode <= LTM_LOCK_TABLES)
{
/*
This statement will enter/leave prelocked mode on its own.
@@ -2795,12 +3001,37 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
DBUG_PRINT("info",("exec_core returned: %d", res));
}
- m_lex->unit.cleanup();
+ /*
+ Call after unit->cleanup() to close open table
+ key read.
+ */
+ if (open_tables)
+ {
+ m_lex->unit.cleanup();
+ /* Here we also commit or rollback the current statement. */
+ if (! thd->in_sub_stmt)
+ {
+ thd->stmt_da->can_overwrite_status= TRUE;
+ thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
+ thd->stmt_da->can_overwrite_status= FALSE;
+ }
+ thd_proc_info(thd, "closing tables");
+ close_thread_tables(thd);
+ thd_proc_info(thd, 0);
- thd_proc_info(thd, "closing tables");
- /* Here we also commit or rollback the current statement. */
- close_thread_tables(thd);
- thd_proc_info(thd, 0);
+ if (! thd->in_sub_stmt)
+ {
+ if (thd->transaction_rollback_request)
+ {
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+ else if (! thd->in_multi_stmt_transaction_mode())
+ thd->mdl_context.release_transactional_locks();
+ else
+ thd->mdl_context.release_statement_locks();
+ }
+ }
if (m_lex->query_tables_own_last)
{
@@ -2824,10 +3055,11 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
open_tables stage.
*/
if (!res || !thd->is_error() ||
- (thd->main_da.sql_errno() != ER_CANT_REOPEN_TABLE &&
- thd->main_da.sql_errno() != ER_NO_SUCH_TABLE &&
- thd->main_da.sql_errno() != ER_UPDATE_TABLE_USED))
- thd->stmt_arena->state= Query_arena::EXECUTED;
+ (thd->stmt_da->sql_errno() != ER_CANT_REOPEN_TABLE &&
+ thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE &&
+ thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE_IN_ENGINE &&
+ thd->stmt_da->sql_errno() != ER_UPDATE_TABLE_USED))
+ thd->stmt_arena->state= Query_arena::STMT_EXECUTED;
/*
Merge here with the saved parent's values
@@ -2859,8 +3091,8 @@ int sp_instr::exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables)
Check whenever we have access to tables for this statement
and open and lock them before executing instructions core function.
*/
- if (check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)
- || open_and_lock_tables(thd, tables))
+ if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)
+ || open_and_lock_tables(thd, tables, TRUE, 0))
result= -1;
else
result= 0;
@@ -2890,15 +3122,12 @@ int sp_instr::exec_core(THD *thd, uint *nextp)
int
sp_instr_stmt::execute(THD *thd, uint *nextp)
{
- char *query;
- uint32 query_length;
int res;
DBUG_ENTER("sp_instr_stmt::execute");
DBUG_PRINT("info", ("command: %d", m_lex_keeper.sql_command()));
- query= thd->query();
- query_length= thd->query_length();
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ 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
@@ -2909,17 +3138,20 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
(the order of query cache and subst_spvars calls is irrelevant because
queries with SP vars can't be cached)
*/
- if (unlikely((thd->options & OPTION_LOG_OFF)==0))
- general_log_write(thd, COM_QUERY, thd->query(), thd->query_length());
+ general_log_write(thd, COM_QUERY, thd->query(), thd->query_length());
- if (query_cache_send_result_to_client(thd,
- thd->query(),
- thd->query_length()) <= 0)
+ if (query_cache_send_result_to_client(thd, thd->query(),
+ thd->query_length()) <= 0)
{
res= m_lex_keeper.reset_lex_and_exec_core(thd, nextp, FALSE, this);
- if (thd->main_da.is_eof())
- net_end_statement(thd);
+ if (thd->stmt_da->is_eof())
+ {
+ /* Finalize server status flags after executing a statement. */
+ thd->update_server_status();
+
+ thd->protocol->end_statement();
+ }
query_cache_end_of_result(thd);
@@ -2927,12 +3159,20 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
log_slow_statement(thd);
}
else
+ {
+ /* change statistics */
+ enum_sql_command save_sql_command= thd->lex->sql_command;
+ thd->lex->sql_command= SQLCOM_SELECT;
+ status_var_increment(thd->status_var.com_stat[SQLCOM_SELECT]);
+ thd->update_stats();
+ thd->lex->sql_command= save_sql_command;
*nextp= m_ip+1;
- thd->set_query(query, query_length);
+ }
+ thd->set_query(query_backup);
thd->query_name_consts= 0;
if (!thd->is_error())
- thd->main_da.reset_diagnostics_area();
+ thd->stmt_da->reset_diagnostics_area();
}
DBUG_RETURN(res || thd->is_error());
}
@@ -2973,7 +3213,14 @@ sp_instr_stmt::print(String *str)
int
sp_instr_stmt::exec_core(THD *thd, uint *nextp)
{
+ MYSQL_QUERY_EXEC_START(thd->query(),
+ thd->thread_id,
+ (char *) (thd->db ? thd->db : ""),
+ &thd->security_ctx->priv_user[0],
+ (char *)thd->security_ctx->host_or_ip,
+ 3);
int res= mysql_execute_command(thd);
+ MYSQL_QUERY_EXEC_DONE(res);
*nextp= m_ip+1;
return res;
}
@@ -2998,23 +3245,14 @@ sp_instr_set::exec_core(THD *thd, uint *nextp)
{
int res= thd->spcont->set_variable(thd, m_offset, &m_value);
- if (res && thd->spcont->found_handler_here())
+ if (res)
{
- /*
- Failed to evaluate the value, and a handler has been found. Reset the
- variable to NULL.
- */
+ /* 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. */
-
- sp_rcontext *spcont= thd->spcont;
-
- thd->spcont= NULL; /* Avoid handlers */
- my_error(ER_OUT_OF_RESOURCES, MYF(0));
- spcont->clear_handler();
- thd->spcont= spcont;
+ my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
}
}
@@ -3114,7 +3352,7 @@ uint
sp_instr_jump::opt_mark(sp_head *sp, List<sp_instr> *leads)
{
m_dest= opt_shortcut_jump(sp, this);
- if (m_dest != m_ip+1) /* Jumping to following instruction? */
+ if (m_dest != m_ip+1) /* Jumping to following instruction? */
marked= 1;
m_optdest= sp->get_instr(m_dest);
return m_dest;
@@ -3144,9 +3382,9 @@ void
sp_instr_jump::opt_move(uint dst, List<sp_instr> *bp)
{
if (m_dest > m_ip)
- bp->push_back(this); // Forward
+ bp->push_back(this); // Forward
else if (m_optdest)
- m_dest= m_optdest->m_ip; // Backward
+ m_dest= m_optdest->m_ip; // Backward
m_ip= dst;
}
@@ -3302,7 +3540,7 @@ sp_instr_hpush_jump::execute(THD *thd, uint *nextp)
sp_cond_type_t *p;
while ((p= li++))
- thd->spcont->push_handler(p, m_ip+1, m_type, m_frame);
+ thd->spcont->push_handler(p, m_ip+1, m_type);
*nextp= m_dest;
DBUG_RETURN(0);
@@ -3436,7 +3674,7 @@ uint
sp_instr_hreturn::opt_mark(sp_head *sp, List<sp_instr> *leads)
{
marked= 1;
-
+
if (m_dest)
{
/*
@@ -3444,7 +3682,7 @@ sp_instr_hreturn::opt_mark(sp_head *sp, List<sp_instr> *leads)
*/
return m_dest;
}
-
+
/*
This is a CONTINUE handler; next instruction step will come from
the handler stack and not from opt_mark.
@@ -3564,18 +3802,6 @@ sp_instr_copen::execute(THD *thd, uint *nextp)
if (thd->stmt_arena->free_list)
cleanup_items(thd->stmt_arena->free_list);
thd->stmt_arena= old_arena;
- /*
- Work around the fact that errors in selects are not returned properly
- (but instead converted into a warning), so if a condition handler
- caught, we have lost the result code.
- */
- if (!res)
- {
- uint dummy1, dummy2;
-
- if (thd->spcont->found_handler(&dummy1, &dummy2))
- res= -1;
- }
/* TODO: Assert here that we either have an error or a cursor */
}
DBUG_RETURN(res);
@@ -3751,28 +3977,20 @@ sp_instr_set_case_expr::exec_core(THD *thd, uint *nextp)
{
int res= thd->spcont->set_case_expr(thd, m_case_expr_id, &m_case_expr);
- if (res &&
- !thd->spcont->get_case_expr(m_case_expr_id) &&
- thd->spcont->found_handler_here())
+ if (res && !thd->spcont->get_case_expr(m_case_expr_id))
{
/*
Failed to evaluate the value, the case expression is still not
- initialized, and a handler has been found. Set to NULL so we can continue.
+ initialized. Set to NULL so we can continue.
*/
Item *null_item= new Item_null();
-
+
if (!null_item ||
thd->spcont->set_case_expr(thd, m_case_expr_id, &null_item))
{
/* If this also failed, we have to abort. */
-
- sp_rcontext *spcont= thd->spcont;
-
- thd->spcont= NULL; /* Avoid handlers */
- my_error(ER_OUT_OF_RESOURCES, MYF(0));
- spcont->clear_handler();
- thd->spcont= spcont;
+ my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
}
}
else
@@ -3884,7 +4102,7 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
for (uint i= 0 ; i < m_sptabs.records ; i++)
{
- tab= (SP_TABLE *)hash_element(&m_sptabs, i);
+ tab= (SP_TABLE*) my_hash_element(&m_sptabs, i);
tab->query_lock_count= 0;
}
@@ -3925,10 +4143,10 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
(and therefore should not be prelocked). Otherwise we will erroneously
treat table with same name but with different alias as non-temporary.
*/
- if ((tab= (SP_TABLE *)hash_search(&m_sptabs, (uchar *)tname.ptr(),
- tname.length())) ||
- ((tab= (SP_TABLE *)hash_search(&m_sptabs, (uchar *)tname.ptr(),
- temp_table_key_length)) &&
+ if ((tab= (SP_TABLE*) my_hash_search(&m_sptabs, (uchar *)tname.ptr(),
+ tname.length())) ||
+ ((tab= (SP_TABLE*) my_hash_search(&m_sptabs, (uchar *)tname.ptr(),
+ temp_table_key_length)) &&
tab->temp))
{
if (tab->lock_type < table->lock_type)
@@ -3940,13 +4158,13 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
}
else
{
- if (!(tab= (SP_TABLE *)thd->calloc(sizeof(SP_TABLE))))
- return FALSE;
- if (lex_for_tmp_check->sql_command == SQLCOM_CREATE_TABLE &&
- lex_for_tmp_check->query_tables == table &&
- lex_for_tmp_check->create_info.options & HA_LEX_CREATE_TMP_TABLE)
+ if (!(tab= (SP_TABLE *)thd->calloc(sizeof(SP_TABLE))))
+ return FALSE;
+ if (lex_for_tmp_check->sql_command == SQLCOM_CREATE_TABLE &&
+ lex_for_tmp_check->query_tables == table &&
+ lex_for_tmp_check->create_info.options & HA_LEX_CREATE_TMP_TABLE)
{
- tab->temp= TRUE;
+ tab->temp= TRUE;
tab->qname.length= temp_table_key_length;
}
else
@@ -3959,7 +4177,7 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
tab->lock_type= table->lock_type;
tab->lock_count= tab->query_lock_count= 1;
tab->trg_event_map= table->trg_event_map;
- if (my_hash_insert(&m_sptabs, (uchar *)tab))
+ if (my_hash_insert(&m_sptabs, (uchar *)tab))
return FALSE;
}
}
@@ -4013,7 +4231,7 @@ sp_head::add_used_tables_to_table_list(THD *thd,
{
char *tab_buff, *key_buff;
TABLE_LIST *table;
- SP_TABLE *stab= (SP_TABLE *)hash_element(&m_sptabs, i);
+ SP_TABLE *stab= (SP_TABLE*) my_hash_element(&m_sptabs, i);
if (stab->temp)
continue;
@@ -4037,6 +4255,15 @@ sp_head::add_used_tables_to_table_list(THD *thd,
table->prelocking_placeholder= 1;
table->belong_to_view= belong_to_view;
table->trg_event_map= stab->trg_event_map;
+ /*
+ Since we don't allow DDL on base tables in prelocked mode it
+ is safe to infer the type of metadata lock from the type of
+ table lock.
+ */
+ table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name,
+ table->lock_type >= TL_WRITE_ALLOW_WRITE ?
+ MDL_SHARED_WRITE : MDL_SHARED_READ,
+ MDL_TRANSACTION);
/* Everyting else should be zeroed */
@@ -4057,22 +4284,20 @@ sp_head::add_used_tables_to_table_list(THD *thd,
/**
- Simple function for adding an explicetly named (systems) table to
+ Simple function for adding an explicitly named (systems) table to
the global table list, e.g. "mysql", "proc".
*/
TABLE_LIST *
sp_add_to_query_tables(THD *thd, LEX *lex,
const char *db, const char *name,
- thr_lock_type locktype)
+ thr_lock_type locktype,
+ enum_mdl_type mdl_type)
{
TABLE_LIST *table;
if (!(table= (TABLE_LIST *)thd->calloc(sizeof(TABLE_LIST))))
- {
- thd->fatal_error();
return NULL;
- }
table->db_length= strlen(db);
table->db= thd->strmake(db, table->db_length);
table->table_name_length= strlen(name);
@@ -4081,7 +4306,9 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
table->lock_type= locktype;
table->select_lex= lex->current_select;
table->cacheable_table= 1;
-
+ table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name,
+ mdl_type, MDL_TRANSACTION);
+
lex->add_to_query_tables(table);
return table;
}
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 3680935b9f2..409db33ef02 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -1,6 +1,6 @@
/* -*- C++ -*- */
/*
- Copyright (c) 2002, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2002, 2011, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,8 +13,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef _SP_HEAD_H_
#define _SP_HEAD_H_
@@ -23,6 +22,15 @@
#pragma interface /* gcc class implementation */
#endif
+/*
+ It is necessary to include set_var.h instead of item.h because there
+ 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.h"
#include <stddef.h>
/**
@@ -30,16 +38,6 @@
@ingroup Runtime_Environment
@{
*/
-/*
- Values for the type enum. This reflects the order of the enum declaration
- in the CREATE TABLE command.
-*/
-enum stored_procedure_type
-{
- TYPE_ENUM_FUNCTION=1,
- TYPE_ENUM_PROCEDURE=2,
- TYPE_ENUM_TRIGGER=3
-};
Item_result
sp_map_result_type(enum enum_field_types type);
@@ -116,36 +114,21 @@ public:
LEX_STRING m_db;
LEX_STRING m_name;
LEX_STRING m_qname;
- /**
- Key representing routine in the set of stored routines used by statement.
- Consists of 1-byte routine type and m_qname (which usually refences to
- same buffer). Note that one must complete initialization of the key by
- calling set_routine_type().
- */
- LEX_STRING m_sroutines_key;
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)
{
- m_qname.str= m_sroutines_key.str= 0;
- m_qname.length= m_sroutines_key.length= 0;
+ m_qname.str= 0;
+ m_qname.length= 0;
}
- /**
- Creates temporary sp_name object from key, used mainly
- for SP-cache lookups.
- */
- sp_name(THD *thd, char *key, uint key_len);
+ /** Create temporary sp_name object from MDL key. */
+ 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
- void set_routine_type(stored_procedure_type type)
- {
- m_sroutines_key.str[0]= (char) type;
- }
-
~sp_name()
{}
};
@@ -172,9 +155,8 @@ public:
HAS_COMMIT_OR_ROLLBACK= 128,
LOG_SLOW_STATEMENTS= 256, // Used by events
LOG_GENERAL_LOG= 512, // Used by events
- BINLOG_ROW_BASED_IF_MIXED= 1024,
- HAS_SQLCOM_RESET= 2048,
- HAS_SQLCOM_FLUSH= 4096
+ HAS_SQLCOM_RESET= 1024,
+ HAS_SQLCOM_FLUSH= 2048
};
stored_procedure_type m_type;
@@ -184,15 +166,9 @@ public:
const char *m_tmp_query; ///< Temporary pointer to sub query string
st_sp_chistics *m_chistics;
- ulong m_sql_mode; ///< For SHOW CREATE and execution
+ ulonglong m_sql_mode; ///< For SHOW CREATE and execution
LEX_STRING m_qname; ///< db.name
bool m_explicit_name; ///< Prepend the db name? */
- /**
- Key representing routine in the set of stored routines used by statement.
- [routine_type]db.name
- @sa sp_name::m_sroutines_key
- */
- LEX_STRING m_sroutines_key;
LEX_STRING m_db;
LEX_STRING m_name;
LEX_STRING m_params;
@@ -202,8 +178,40 @@ public:
LEX_STRING m_definer_user;
LEX_STRING m_definer_host;
+ /**
+ Is this routine being executed?
+ */
+ 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; }
+
+ /** Set the value of the SP cache version. */
+ void set_sp_cache_version(ulong version_arg)
+ {
+ m_sp_cache_version= version_arg;
+ }
private:
+ /**
+ Version of the stored routine cache at the moment when the
+ routine was added to it. Is used only for functions and
+ procedures, not used for triggers or events. When sp_head is
+ created, its version is 0. When it's added to the cache, the
+ version is assigned the global value 'Cversion'.
+ If later on Cversion is incremented, we know that the routine
+ is obsolete and should not be used --
+ sp_cache_flush_obsolete() will purge it.
+ */
+ ulong m_sp_cache_version;
Stored_program_creation_ctx *m_creation_ctx;
+ /**
+ Boolean combination of (1<<flag), where flag is a member of
+ LEX::enum_binlog_stmt_unsafe.
+ */
+ uint32 unsafe_flags;
public:
inline Stored_program_creation_ctx *get_creation_ctx()
@@ -290,9 +298,6 @@ public:
void
set_stmt_end(THD *thd);
- int
- create(THD *thd);
-
virtual ~sp_head();
bool
@@ -383,7 +388,7 @@ public:
Create_field *field_def);
void set_info(longlong created, longlong modified,
- st_sp_chistics *chistics, ulong sql_mode);
+ st_sp_chistics *chistics, ulonglong sql_mode);
void set_definer(const char *definer, uint definerlen);
void set_definer(const LEX_STRING *user_name, const LEX_STRING *host_name);
@@ -456,21 +461,27 @@ public:
/*
This method is intended for attributes of a routine which need
- to propagate upwards to the LEX of the caller (when a property of a
- sp_head needs to "taint" the caller).
+ to propagate upwards to the Query_tables_list of the caller (when
+ a property of a sp_head needs to "taint" the calling statement).
*/
- void propagate_attributes(LEX *lex)
+ void propagate_attributes(Query_tables_list *prelocking_ctx)
{
+ DBUG_ENTER("sp_head::propagate_attributes");
/*
If this routine needs row-based binary logging, the entire top statement
too (we cannot switch from statement-based to row-based only for this
routine, as in statement-based the top-statement may be binlogged and
the substatements not).
*/
- if (m_flags & BINLOG_ROW_BASED_IF_MIXED)
- lex->set_stmt_unsafe();
+ 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));
+ prelocking_ctx->set_stmt_unsafe_flags(unsafe_flags);
+ DBUG_VOID_RETURN;
}
+ sp_pcontext *get_parse_context() { return m_pcont; }
private:
@@ -510,7 +521,7 @@ private:
HASH m_sptabs;
bool
- execute(THD *thd);
+ execute(THD *thd, bool merge_da_on_success);
/**
Perform a forward flow analysis in the generated code.
@@ -543,7 +554,7 @@ public:
/// Should give each a name or type code for debugging purposes?
sp_instr(uint ip, sp_pcontext *ctx)
- :Query_arena(0, INITIALIZED_FOR_SP), marked(0), m_ip(ip), m_ctx(ctx)
+ :Query_arena(0, STMT_INITIALIZED_FOR_SP), marked(0), m_ip(ip), m_ctx(ctx)
{}
virtual ~sp_instr()
@@ -666,6 +677,8 @@ public:
{
if (m_lex_resp)
{
+ /* Prevent endless recursion. */
+ m_lex->sphead= NULL;
lex_end(m_lex);
delete m_lex;
}
@@ -1336,7 +1349,9 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
TABLE_LIST *
sp_add_to_query_tables(THD *thd, LEX *lex,
const char *db, const char *name,
- thr_lock_type locktype);
+ thr_lock_type locktype,
+ enum_mdl_type mdl_type);
+
Item *
sp_prepare_func_item(THD* thd, Item **it_addr);
diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc
index c89d5d0f69c..4c5087eaf27 100644
--- a/sql/sp_pcontext.cc
+++ b/sql/sp_pcontext.cc
@@ -1,6 +1,4 @@
-/*
- Copyright (c) 2002-2008 MySQL AB, 2008 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2002, 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
@@ -13,18 +11,14 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation
#endif
-#if defined(WIN32) || defined(__WIN__)
-#undef SAFEMALLOC /* Problems with threads */
-#endif
-
#include "sp_pcontext.h"
#include "sp_head.h"
@@ -54,7 +48,8 @@ sp_cond_check(LEX_STRING *sqlstate)
(c < 'A' || 'Z' < c))
return FALSE;
}
- if (strcmp(sqlstate->str, "00000") == 0)
+ /* SQLSTATE class '00' : completion condition */
+ if (strncmp(sqlstate->str, "00", 2) == 0)
return FALSE;
return TRUE;
}
@@ -65,21 +60,21 @@ sp_pcontext::sp_pcontext()
m_context_handlers(0), m_parent(NULL), m_pboundary(0),
m_label_scope(LABEL_DEFAULT_SCOPE)
{
- VOID(my_init_dynamic_array(&m_vars, sizeof(sp_variable_t *),
+ (void) my_init_dynamic_array(&m_vars, sizeof(sp_variable_t *),
PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC));
- VOID(my_init_dynamic_array(&m_case_expr_id_lst, sizeof(int),
+ PCONTEXT_ARRAY_INCREMENT_ALLOC);
+ (void) my_init_dynamic_array(&m_case_expr_id_lst, sizeof(int),
PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC));
- VOID(my_init_dynamic_array(&m_conds, sizeof(sp_cond_type_t *),
+ PCONTEXT_ARRAY_INCREMENT_ALLOC);
+ (void) my_init_dynamic_array(&m_conds, sizeof(sp_cond_type_t *),
PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC));
- VOID(my_init_dynamic_array(&m_cursors, sizeof(LEX_STRING),
+ PCONTEXT_ARRAY_INCREMENT_ALLOC);
+ (void) my_init_dynamic_array(&m_cursors, sizeof(LEX_STRING),
PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC));
- VOID(my_init_dynamic_array(&m_handlers, sizeof(sp_cond_type_t *),
+ PCONTEXT_ARRAY_INCREMENT_ALLOC);
+ (void) my_init_dynamic_array(&m_handlers, sizeof(sp_cond_type_t *),
PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC));
+ PCONTEXT_ARRAY_INCREMENT_ALLOC);
m_label.empty();
m_children.empty();
@@ -93,21 +88,21 @@ sp_pcontext::sp_pcontext(sp_pcontext *prev, label_scope_type label_scope)
m_context_handlers(0), m_parent(prev), m_pboundary(0),
m_label_scope(label_scope)
{
- VOID(my_init_dynamic_array(&m_vars, sizeof(sp_variable_t *),
+ (void) my_init_dynamic_array(&m_vars, sizeof(sp_variable_t *),
PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC));
- VOID(my_init_dynamic_array(&m_case_expr_id_lst, sizeof(int),
+ PCONTEXT_ARRAY_INCREMENT_ALLOC);
+ (void) my_init_dynamic_array(&m_case_expr_id_lst, sizeof(int),
PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC));
- VOID(my_init_dynamic_array(&m_conds, sizeof(sp_cond_type_t *),
+ PCONTEXT_ARRAY_INCREMENT_ALLOC);
+ (void) my_init_dynamic_array(&m_conds, sizeof(sp_cond_type_t *),
PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC));
- VOID(my_init_dynamic_array(&m_cursors, sizeof(LEX_STRING),
+ PCONTEXT_ARRAY_INCREMENT_ALLOC);
+ (void) my_init_dynamic_array(&m_cursors, sizeof(LEX_STRING),
PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC));
- VOID(my_init_dynamic_array(&m_handlers, sizeof(sp_cond_type_t *),
+ PCONTEXT_ARRAY_INCREMENT_ALLOC);
+ (void) my_init_dynamic_array(&m_handlers, sizeof(sp_cond_type_t *),
PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC));
+ PCONTEXT_ARRAY_INCREMENT_ALLOC);
m_label.empty();
m_children.empty();
diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h
index 28f211d787b..f1d0d250c47 100644
--- a/sql/sp_pcontext.h
+++ b/sql/sp_pcontext.h
@@ -1,7 +1,5 @@
/* -*- C++ -*- */
-/*
- Copyright (c) 2002-2007 MySQL AB, 2008, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2002, 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
@@ -14,8 +12,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef _SP_PCONTEXT_H_
#define _SP_PCONTEXT_H_
@@ -24,6 +21,12 @@
#pragma interface /* gcc class implementation */
#endif
+#include "sql_string.h" // LEX_STRING
+#include "mysql_com.h" // enum_field_types
+#include "field.h" // Create_field
+
+class sp_pcontext;
+
typedef enum
{
sp_param_in,
@@ -329,13 +332,6 @@ public:
int
push_cond(LEX_STRING *name, sp_cond_type_t *val);
- inline void
- pop_cond(uint num)
- {
- while (num--)
- pop_dynamic(&m_conds);
- }
-
sp_cond_type_t *
find_cond(LEX_STRING *name, my_bool scoped=0);
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index 2558055eda3..30acfebabb2 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -1,6 +1,4 @@
-/*
- Copyright (c) 2003-2008 MySQL AB, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2002, 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
@@ -13,29 +11,26 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation
#endif
-#if defined(WIN32) || defined(__WIN__)
-#undef SAFEMALLOC /* Problems with threads */
-#endif
-
#include "mysql.h"
#include "sp_head.h"
#include "sql_cursor.h"
#include "sp_rcontext.h"
#include "sp_pcontext.h"
-
+#include "sql_select.h" // create_virtual_tmp_table
sp_rcontext::sp_rcontext(sp_pcontext *root_parsing_ctx,
Field *return_value_fld,
sp_rcontext *prev_runtime_ctx)
- :m_root_parsing_ctx(root_parsing_ctx),
+ :end_partial_result_set(FALSE),
+ m_root_parsing_ctx(root_parsing_ctx),
m_var_table(0),
m_var_items(0),
m_return_value_fld(return_value_fld),
@@ -71,21 +66,24 @@ sp_rcontext::~sp_rcontext()
bool sp_rcontext::init(THD *thd)
{
+ uint handler_count= m_root_parsing_ctx->max_handler_index();
+
in_sub_stmt= thd->in_sub_stmt;
if (init_var_table(thd) || init_var_items())
return TRUE;
+ if (!(m_raised_conditions= new (thd->mem_root) Sql_condition_info[handler_count]))
+ return TRUE;
+
return
!(m_handler=
- (sp_handler_t*)thd->alloc(m_root_parsing_ctx->max_handler_index() *
- sizeof(sp_handler_t))) ||
+ (sp_handler_t*)thd->alloc(handler_count * sizeof(sp_handler_t))) ||
!(m_hstack=
- (uint*)thd->alloc(m_root_parsing_ctx->max_handler_index() *
- sizeof(uint))) ||
+ (uint*)thd->alloc(handler_count * sizeof(uint))) ||
!(m_in_handler=
- (uint*)thd->alloc(m_root_parsing_ctx->max_handler_index() *
- sizeof(uint))) ||
+ (sp_active_handler_t*)thd->alloc(handler_count *
+ sizeof(sp_active_handler_t))) ||
!(m_cstack=
(sp_cursor**)thd->alloc(m_root_parsing_ctx->max_cursor_index() *
sizeof(sp_cursor*))) ||
@@ -169,42 +167,50 @@ sp_rcontext::set_return_value(THD *thd, Item **return_value_item)
#define IS_NOT_FOUND_CONDITION(S) ((S)[0] == '0' && (S)[1] == '2')
#define IS_EXCEPTION_CONDITION(S) ((S)[0] != '0' || (S)[1] > '2')
-/*
- Find a handler for the given errno.
- This is called from all error message functions (e.g. push_warning,
- net_send_error, et al) when a sp_rcontext is in effect. If a handler
- is found, no error is sent, and the the SP execution loop will instead
- invoke the found handler.
- This might be called several times before we get back to the execution
- loop, so m_hfound can be >= 0 if a handler has already been found.
- (In which case we don't search again - the first found handler will
- be used.)
- Handlers are pushed on the stack m_handler, with the latest/innermost
+/**
+ Find an SQL handler for the given error.
+
+ SQL handlers are pushed on the stack m_handler, with the latest/innermost
one on the top; we then search for matching handlers from the top and
down.
+
We search through all the handlers, looking for the most specific one
(sql_errno more specific than sqlstate more specific than the rest).
Note that mysql error code handlers is a MySQL extension, not part of
the standard.
- SYNOPSIS
- sql_errno The error code
- level Warning level
+ SQL handlers for warnings are searched in the current scope only.
- RETURN
- 1 if a handler was found, m_hfound is set to its index (>= 0)
- 0 if not found, m_hfound is -1
+ SQL handlers for errors are searched in the current and in outer scopes.
+ That's why finding and activation of handler must be separated: an errror
+ handler might be located in the outer scope, which is not active at the
+ moment. Before such handler can be activated, execution flow should
+ unwind to that scope.
+
+ Found SQL handler is remembered in m_hfound for future activation.
+ If no handler is found, m_hfound is -1.
+
+ @param thd Thread handle
+ @param sql_errno The error code
+ @param sqlstate The error SQL state
+ @param level The error level
+ @param msg The error message
+
+ @retval TRUE if an SQL handler was found
+ @retval FALSE otherwise
*/
bool
-sp_rcontext::find_handler(THD *thd, uint sql_errno,
- MYSQL_ERROR::enum_warning_level level)
+sp_rcontext::find_handler(THD *thd,
+ uint sql_errno,
+ const char *sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char *msg)
{
- if (m_hfound >= 0)
- return 1; // Already got one
+ int i= m_hcount;
- const char *sqlstate= mysql_errno_to_sqlstate(sql_errno);
- int i= m_hcount, found= -1;
+ /* Reset previously found handler. */
+ m_hfound= -1;
/*
If this is a fatal sub-statement error, and this runtime
@@ -223,7 +229,7 @@ sp_rcontext::find_handler(THD *thd, uint sql_errno,
/* Check active handlers, to avoid invoking one recursively */
while (j--)
- if (m_in_handler[j] == m_handler[i].handler)
+ if (m_in_handler[j].ip == m_handler[i].handler)
break;
if (j >= 0)
continue; // Already executing this handler
@@ -232,86 +238,56 @@ sp_rcontext::find_handler(THD *thd, uint sql_errno,
{
case sp_cond_type_t::number:
if (sql_errno == cond->mysqlerr &&
- (found < 0 || m_handler[found].cond->type > sp_cond_type_t::number))
- found= i; // Always the most specific
+ (m_hfound < 0 || m_handler[m_hfound].cond->type > sp_cond_type_t::number))
+ m_hfound= i; // Always the most specific
break;
case sp_cond_type_t::state:
if (strcmp(sqlstate, cond->sqlstate) == 0 &&
- (found < 0 || m_handler[found].cond->type > sp_cond_type_t::state))
- found= i;
+ (m_hfound < 0 || m_handler[m_hfound].cond->type > sp_cond_type_t::state))
+ m_hfound= i;
break;
case sp_cond_type_t::warning:
if ((IS_WARNING_CONDITION(sqlstate) ||
level == MYSQL_ERROR::WARN_LEVEL_WARN) &&
- found < 0)
- found= i;
+ m_hfound < 0)
+ m_hfound= i;
break;
case sp_cond_type_t::notfound:
- if (IS_NOT_FOUND_CONDITION(sqlstate) && found < 0)
- found= i;
+ if (IS_NOT_FOUND_CONDITION(sqlstate) && m_hfound < 0)
+ m_hfound= i;
break;
case sp_cond_type_t::exception:
if (IS_EXCEPTION_CONDITION(sqlstate) &&
level == MYSQL_ERROR::WARN_LEVEL_ERROR &&
- found < 0)
- found= i;
+ m_hfound < 0)
+ m_hfound= i;
break;
}
}
- if (found < 0)
- {
- /*
- Only "exception conditions" are propagated to handlers in calling
- contexts. If no handler is found locally for a "completion condition"
- (warning or "not found") we will simply resume execution.
- */
- if (m_prev_runtime_ctx && IS_EXCEPTION_CONDITION(sqlstate) &&
- level == MYSQL_ERROR::WARN_LEVEL_ERROR)
- return m_prev_runtime_ctx->find_handler(thd, sql_errno, level);
- return FALSE;
- }
- m_hfound= found;
- return TRUE;
-}
-/*
- Handle the error for a given errno.
- The severity of the error is adjusted depending of the current sql_mode.
- If an handler is present for the error (see find_handler()),
- this function will return true.
- If a handler is found and if the severity of the error indicate
- that the current instruction executed should abort,
- the flag thd->net.report_error is also set.
- This will cause the execution of the current instruction in a
- sp_instr* to fail, and give control to the handler code itself
- in the sp_head::execute() loop.
+ if (m_hfound >= 0)
+ {
+ DBUG_ASSERT((uint) m_hfound < m_root_parsing_ctx->max_handler_index());
- SYNOPSIS
- sql_errno The error code
- level Warning level
- thd The current thread
-
- RETURN
- TRUE if a handler was found.
- FALSE if no handler was found.
-*/
-bool
-sp_rcontext::handle_error(uint sql_errno,
- MYSQL_ERROR::enum_warning_level level,
- THD *thd)
-{
- MYSQL_ERROR::enum_warning_level elevated_level= level;
+ m_raised_conditions[m_hfound].clear();
+ m_raised_conditions[m_hfound].set(sql_errno, sqlstate, level, msg);
+ return TRUE;
+ }
- /* Depending on the sql_mode of execution,
- warnings may be considered errors */
- if ((level == MYSQL_ERROR::WARN_LEVEL_WARN) &&
- thd->really_abort_on_warning())
+ /*
+ Only "exception conditions" are propagated to handlers in calling
+ contexts. If no handler is found locally for a "completion condition"
+ (warning or "not found") we will simply resume execution.
+ */
+ if (m_prev_runtime_ctx && IS_EXCEPTION_CONDITION(sqlstate) &&
+ level == MYSQL_ERROR::WARN_LEVEL_ERROR)
{
- elevated_level= MYSQL_ERROR::WARN_LEVEL_ERROR;
+ return m_prev_runtime_ctx->find_handler(thd, sql_errno, sqlstate,
+ level, msg);
}
- return find_handler(thd, sql_errno, elevated_level);
+ return FALSE;
}
void
@@ -338,7 +314,7 @@ sp_rcontext::pop_cursors(uint count)
}
void
-sp_rcontext::push_handler(struct sp_cond_type *cond, uint h, int type, uint f)
+sp_rcontext::push_handler(struct sp_cond_type *cond, uint h, int type)
{
DBUG_ENTER("sp_rcontext::push_handler");
DBUG_ASSERT(m_hcount < m_root_parsing_ctx->max_handler_index());
@@ -346,7 +322,6 @@ sp_rcontext::push_handler(struct sp_cond_type *cond, uint h, int type, uint f)
m_handler[m_hcount].cond= cond;
m_handler[m_hcount].handler= h;
m_handler[m_hcount].type= type;
- m_handler[m_hcount].foffset= f;
m_hcount+= 1;
DBUG_PRINT("info", ("m_hcount: %d", m_hcount));
@@ -358,7 +333,9 @@ sp_rcontext::pop_handlers(uint count)
{
DBUG_ENTER("sp_rcontext::pop_handlers");
DBUG_ASSERT(m_hcount >= count);
+
m_hcount-= count;
+
DBUG_PRINT("info", ("m_hcount: %d", m_hcount));
DBUG_VOID_RETURN;
}
@@ -368,7 +345,9 @@ sp_rcontext::push_hstack(uint h)
{
DBUG_ENTER("sp_rcontext::push_hstack");
DBUG_ASSERT(m_hsp < m_root_parsing_ctx->max_handler_index());
+
m_hstack[m_hsp++]= h;
+
DBUG_PRINT("info", ("m_hsp: %d", m_hsp));
DBUG_VOID_RETURN;
}
@@ -379,19 +358,74 @@ sp_rcontext::pop_hstack()
uint handler;
DBUG_ENTER("sp_rcontext::pop_hstack");
DBUG_ASSERT(m_hsp);
+
handler= m_hstack[--m_hsp];
+
DBUG_PRINT("info", ("m_hsp: %d", m_hsp));
DBUG_RETURN(handler);
}
-void
-sp_rcontext::enter_handler(int hid)
+/**
+ Prepare found handler to be executed.
+
+ @retval TRUE if an SQL handler is activated (was found) and IP of the
+ first handler instruction.
+ @retval FALSE if there is no active handler
+*/
+
+bool
+sp_rcontext::activate_handler(THD *thd,
+ uint *ip,
+ sp_instr *instr,
+ Query_arena *execute_arena,
+ Query_arena *backup_arena)
{
- DBUG_ENTER("sp_rcontext::enter_handler");
- DBUG_ASSERT(m_ihsp < m_root_parsing_ctx->max_handler_index());
- m_in_handler[m_ihsp++]= hid;
- DBUG_PRINT("info", ("m_ihsp: %d", m_ihsp));
- DBUG_VOID_RETURN;
+ if (m_hfound < 0)
+ return FALSE;
+
+ switch (m_handler[m_hfound].type) {
+ case SP_HANDLER_NONE:
+ break;
+
+ case SP_HANDLER_CONTINUE:
+ thd->restore_active_arena(execute_arena, backup_arena);
+ thd->set_n_backup_active_arena(execute_arena, backup_arena);
+ push_hstack(instr->get_cont_dest());
+
+ /* Fall through */
+
+ default:
+ /* End aborted result set. */
+
+ if (end_partial_result_set)
+ thd->protocol->end_partial_result_set(thd);
+
+ /* Enter handler. */
+
+ DBUG_ASSERT(m_ihsp < m_root_parsing_ctx->max_handler_index());
+ DBUG_ASSERT(m_hfound >= 0);
+
+ m_in_handler[m_ihsp].ip= m_handler[m_hfound].handler;
+ m_in_handler[m_ihsp].index= m_hfound;
+ m_ihsp++;
+
+ DBUG_PRINT("info", ("Entering handler..."));
+ DBUG_PRINT("info", ("m_ihsp: %d", m_ihsp));
+
+ /* Reset error state. */
+
+ thd->clear_error();
+ thd->reset_killed(); // Some errors set thd->killed
+ // (e.g. "bad data").
+
+ /* Return IP of the activated SQL handler. */
+ *ip= m_handler[m_hfound].handler;
+
+ /* Reset found handler. */
+ m_hfound= -1;
+ }
+
+ return TRUE;
}
void
@@ -399,11 +433,30 @@ sp_rcontext::exit_handler()
{
DBUG_ENTER("sp_rcontext::exit_handler");
DBUG_ASSERT(m_ihsp);
+
+ uint hindex= m_in_handler[m_ihsp-1].index;
+ m_raised_conditions[hindex].clear();
m_ihsp-= 1;
+
DBUG_PRINT("info", ("m_ihsp: %d", m_ihsp));
DBUG_VOID_RETURN;
}
+Sql_condition_info* sp_rcontext::raised_condition() const
+{
+ if (m_ihsp > 0)
+ {
+ uint hindex= m_in_handler[m_ihsp - 1].index;
+ Sql_condition_info *raised= & m_raised_conditions[hindex];
+ return raised;
+ }
+
+ if (m_prev_runtime_ctx)
+ return m_prev_runtime_ctx->raised_condition();
+
+ return NULL;
+}
+
int
sp_rcontext::set_variable(THD *thd, uint var_idx, Item **value)
@@ -479,8 +532,7 @@ sp_cursor::open(THD *thd)
MYF(0));
return -1;
}
- if (mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR, &result,
- &server_side_cursor))
+ if (mysql_open_cursor(thd, &result, &server_side_cursor))
return -1;
return 0;
}
@@ -522,6 +574,11 @@ sp_cursor::fetch(THD *thd, List<struct sp_variable> *vars)
return -1;
}
+ DBUG_EXECUTE_IF("bug23032_emit_warning",
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_UNKNOWN_ERROR,
+ ER(ER_UNKNOWN_ERROR)););
+
result.set_spvar_list(vars);
/* Attempt to fetch one row */
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
index de966c07192..5008a73d96c 100644
--- a/sql/sp_rcontext.h
+++ b/sql/sp_rcontext.h
@@ -1,5 +1,5 @@
/* -*- C++ -*- */
-/* Copyright (c) 2002-2008 MySQL AB
+/* Copyright (c) 2002, 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
@@ -12,7 +12,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef _SP_RCONTEXT_H_
#define _SP_RCONTEXT_H_
@@ -21,11 +21,19 @@
#pragma interface /* gcc class implementation */
#endif
+#include "sql_class.h" // select_result_interceptor
+
struct sp_cond_type;
class sp_cursor;
struct sp_variable;
class sp_lex_keeper;
class sp_instr_cpush;
+class Query_arena;
+class sp_head;
+class sp_pcontext;
+class Item_cache;
+typedef class st_select_lex_unit SELECT_LEX_UNIT;
+class Server_side_cursor;
#define SP_HANDLER_NONE 0
#define SP_HANDLER_EXIT 1
@@ -34,12 +42,61 @@ class sp_instr_cpush;
typedef struct
{
+ /** Condition caught by this HANDLER. */
struct sp_cond_type *cond;
- uint handler; // Location of handler
+ /** Location (instruction pointer) of the handler code. */
+ uint handler;
+ /** Handler type (EXIT, CONTINUE). */
int type;
- uint foffset; // Frame offset for the handlers declare level
} sp_handler_t;
+typedef struct
+{
+ /** Instruction pointer of the active handler. */
+ uint ip;
+ /** Handler index of the active handler. */
+ uint index;
+} sp_active_handler_t;
+
+
+class Sql_condition_info : public Sql_alloc
+{
+public:
+ /** SQL error code. */
+ uint m_sql_errno;
+
+ /** Error level. */
+ MYSQL_ERROR::enum_warning_level m_level;
+
+ /** SQLSTATE. */
+ char m_sql_state[SQLSTATE_LENGTH + 1];
+
+ /** Text message. */
+ char m_message[MYSQL_ERRMSG_SIZE];
+
+ void set(uint sql_errno, const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg)
+ {
+ m_sql_errno= sql_errno;
+ m_level= level;
+
+ memcpy(m_sql_state, sqlstate, SQLSTATE_LENGTH);
+ m_sql_state[SQLSTATE_LENGTH]= '\0';
+
+ strncpy(m_message, msg, MYSQL_ERRMSG_SIZE);
+ }
+
+ void clear()
+ {
+ m_sql_errno= 0;
+ m_level= MYSQL_ERROR::WARN_LEVEL_ERROR;
+
+ m_sql_state[0]= '\0';
+ m_message[0]= '\0';
+ }
+};
+
/*
This class is a runtime context of a Stored Routine. It is used in an
@@ -75,6 +132,13 @@ class sp_rcontext : public Sql_alloc
*/
Query_arena *callers_arena;
+ /*
+ End a open result set before start executing a continue/exit
+ handler if one is found as otherwise the client will hang
+ due to a violation of the client/server protocol.
+ */
+ bool end_partial_result_set;
+
#ifndef DBUG_OFF
/*
The routine for which this runtime context is created. Used for checking
@@ -107,52 +171,39 @@ class sp_rcontext : public Sql_alloc
return m_return_value_set;
}
- void push_handler(struct sp_cond_type *cond, uint h, int type, uint f);
+ /*
+ SQL handlers support.
+ */
- void pop_handlers(uint count);
+ void push_handler(struct sp_cond_type *cond, uint h, int type);
- // Returns 1 if a handler was found, 0 otherwise.
- bool
- find_handler(THD *thd, uint sql_errno,MYSQL_ERROR::enum_warning_level level);
+ void pop_handlers(uint count);
- // If there is an error handler for this error, handle it and return TRUE.
bool
- handle_error(uint sql_errno,
+ find_handler(THD *thd,
+ uint sql_errno,
+ const char *sqlstate,
MYSQL_ERROR::enum_warning_level level,
- THD *thd);
-
- // Returns handler type and sets *ip to location if one was found
- inline int
- found_handler(uint *ip, uint *fp)
- {
- if (m_hfound < 0)
- return SP_HANDLER_NONE;
- *ip= m_handler[m_hfound].handler;
- *fp= m_handler[m_hfound].foffset;
- return m_handler[m_hfound].type;
- }
+ const char *msg);
- // Returns true if we found a handler in this context
- inline bool
- found_handler_here()
- {
- return (m_hfound >= 0);
- }
+ Sql_condition_info *raised_condition() const;
- // Clears the handler find state
- inline void
- clear_handler()
- {
- m_hfound= -1;
- }
+ void
+ push_hstack(uint h);
- void push_hstack(uint h);
+ uint
+ pop_hstack();
- uint pop_hstack();
+ bool
+ activate_handler(THD *thd,
+ uint *ip,
+ sp_instr *instr,
+ Query_arena *execute_arena,
+ Query_arena *backup_arena);
- void enter_handler(int hid);
- void exit_handler();
+ void
+ exit_handler();
void
push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i);
@@ -160,7 +211,7 @@ class sp_rcontext : public Sql_alloc
void
pop_cursors(uint count);
- void
+ inline void
pop_all_cursors()
{
pop_cursors(m_ccount);
@@ -208,16 +259,25 @@ private:
during execution.
*/
bool m_return_value_set;
+
/**
TRUE if the context is created for a sub-statement.
*/
bool in_sub_stmt;
sp_handler_t *m_handler; // Visible handlers
+
+ /**
+ SQL conditions caught by each handler.
+ This is an array indexed by handler index.
+ */
+ Sql_condition_info *m_raised_conditions;
+
uint m_hcount; // Stack pointer for m_handler
uint *m_hstack; // Return stack for continue handlers
uint m_hsp; // Stack pointer for m_hstack
- uint *m_in_handler; // Active handler, for recursion check
+ /** Active handler stack. */
+ sp_active_handler_t *m_in_handler;
uint m_ihsp; // Stack pointer for m_in_handler
int m_hfound; // Set by find_handler; -1 if not found
diff --git a/sql/spatial.cc b/sql/spatial.cc
index 2c838379505..c5dc0c2140c 100644
--- a/sql/spatial.cc
+++ b/sql/spatial.cc
@@ -1,5 +1,6 @@
/*
- Copyright (c) 2002, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2002, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2011, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,10 +13,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-1301 USA
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "my_global.h" // REQUIRED for HAVE_* below
+#include "spatial.h"
+#include "gstream.h" // Gis_read_stream
+#include "sql_string.h" // String
#ifdef HAVE_SPATIAL
@@ -40,7 +44,7 @@
17
*/
-#define MAX_DIGITS_IN_DOUBLE 30
+#define MAX_DIGITS_IN_DOUBLE MY_GCVT_MAX_FIELD_WIDTH
/***************************** Gis_class_info *******************************/
@@ -477,7 +481,7 @@ bool Gis_point::init_from_wkt(Gis_read_stream *trs, String *wkb)
{
double x, y;
if (trs->get_next_number(&x) || trs->get_next_number(&y) ||
- wkb->reserve(POINT_DATA_SIZE))
+ wkb->reserve(POINT_DATA_SIZE, 512))
return 1;
wkb->q_append(x);
wkb->q_append(y);
@@ -782,8 +786,7 @@ int Gis_line_string::store_shapes(Gcalc_shape_transporter *trn) const
{
uint32 n_points;
double x, y;
- double prev_x= 0.0;
- double prev_y= 0.0;
+ double UNINIT_VAR(prev_x), UNINIT_VAR(prev_y);
int first_point= 1;
const char *data= m_data;
@@ -791,7 +794,7 @@ int Gis_line_string::store_shapes(Gcalc_shape_transporter *trn) const
return 1;
n_points= uint4korr(data);
data+= 4;
- if (n_points < 1 || no_data(data, POINT_DATA_SIZE * n_points))
+ if (n_points < 1 || not_enough_points(data, n_points))
return 1;
trn->start_line();
@@ -812,7 +815,6 @@ int Gis_line_string::store_shapes(Gcalc_shape_transporter *trn) const
return trn->complete_line();
}
-
const Geometry::Class_info *Gis_line_string::get_class_info() const
{
return &linestring_class;
@@ -933,7 +935,9 @@ uint Gis_polygon::init_from_wkb(const char *wkb, uint len, wkbByteOrder bo,
if (len < 4)
return 0;
- n_linear_rings= wkb_get_uint(wkb, bo);
+ if (!(n_linear_rings= wkb_get_uint(wkb, bo)))
+ return 0;
+
if (res->reserve(4, 512))
return 0;
wkb+= 4;
@@ -1227,7 +1231,7 @@ int Gis_polygon::store_shapes(Gcalc_shape_transporter *trn) const
return 1;
n_points= uint4korr(data);
data+= 4;
- if (!n_points || no_data(data, POINT_DATA_SIZE * n_points))
+ if (!n_points || not_enough_points(data, n_points))
return 1;
trn->start_ring();
@@ -2061,9 +2065,8 @@ int Gis_multi_polygon::area(double *ar, const char **end_of_data) const
int Gis_multi_polygon::centroid(String *result) const
{
uint32 n_polygons;
- bool first_loop= 1;
Gis_polygon p;
- double UNINIT_VAR(res_area), UNINIT_VAR(res_cx), UNINIT_VAR(res_cy);
+ double res_area= 0.0, res_cx= 0.0, res_cy= 0.0;
double cur_area, cur_cx, cur_cy;
const char *data= m_data;
@@ -2080,20 +2083,13 @@ int Gis_multi_polygon::centroid(String *result) const
p.centroid_xy(&cur_cx, &cur_cy))
return 1;
- if (!first_loop)
- {
- double sum_area= res_area + cur_area;
- res_cx= (res_area * res_cx + cur_area * cur_cx) / sum_area;
- res_cy= (res_area * res_cy + cur_area * cur_cy) / sum_area;
- }
- else
- {
- first_loop= 0;
- res_area= cur_area;
- res_cx= cur_cx;
- res_cy= cur_cy;
- }
+ res_area+= cur_area;
+ res_cx+= cur_area * cur_cx;
+ res_cy+= cur_area * cur_cy;
}
+
+ res_cx/= res_area;
+ res_cy/= res_area;
return create_point(result, res_cx, res_cy);
}
diff --git a/sql/spatial.h b/sql/spatial.h
index 4989ae2e583..ee2d6d5ec8d 100644
--- a/sql/spatial.h
+++ b/sql/spatial.h
@@ -1,5 +1,6 @@
/*
Copyright (c) 2002, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +13,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef _spatial_h
#define _spatial_h
@@ -23,6 +23,8 @@
#ifdef HAVE_SPATIAL
+class Gis_read_stream;
+
#include "gcalc_tools.h"
const uint SRID_SIZE= 4;
@@ -586,5 +588,5 @@ public:
struct Geometry_buffer : public
my_aligned_storage<sizeof(Gis_point), MY_ALIGNOF(Gis_point)> {};
-#endif /*HAVE_SPATAIAL*/
+#endif /*HAVE_SPATIAL*/
#endif
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 3154e7f9b7e..c4ef699c049 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 2009, 2014, Monty Program Ab.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -25,14 +25,36 @@
in the relevant fields. Empty strings comes last.
*/
-#include "mysql_priv.h"
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_priv.h"
+#include "sql_acl.h" // MYSQL_DB_FIELD_COUNT, ACL_ACCESS
+#include "sql_base.h" // close_mysql_tables
+#include "key.h" // key_copy, key_cmp_if_same, key_restore
+#include "sql_show.h" // append_identifier
+#include "sql_table.h" // build_table_filename
#include "hash_filo.h"
+#include "sql_parse.h" // check_access
+#include "sql_view.h" // VIEW_ANY_ACL
+#include "records.h" // READ_RECORD, read_record_info,
+ // init_read_record, end_read_record
+#include "rpl_filter.h" // rpl_filter
#include <m_ctype.h>
#include <stdarg.h>
#include "sp_head.h"
#include "sp.h"
+#include "transaction.h"
+#include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT
+#include "records.h" // init_read_record, end_read_record
#include <sql_common.h>
#include <mysql/plugin_auth.h>
+#include "sql_connect.h"
+#include "hostname.h"
+#include "sql_db.h"
+#include "sql_array.h"
+
+#include "sql_plugin_compat.h"
+
+bool mysql_user_table_is_in_short_password_format= false;
static const
TABLE_FIELD_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = {
@@ -48,7 +70,7 @@ TABLE_FIELD_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = {
},
{
{ C_STRING_WITH_LEN("User") },
- { C_STRING_WITH_LEN("char(16)") },
+ { C_STRING_WITH_LEN("char(") },
{NULL, 0}
},
{
@@ -162,17 +184,17 @@ static LEX_STRING old_password_plugin_name= {
/// @todo make it configurable
LEX_STRING *default_auth_plugin_name= &native_password_plugin_name;
-static plugin_ref native_password_plugin;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
static plugin_ref old_password_plugin;
#endif
+static plugin_ref native_password_plugin;
/* Classes */
struct acl_host_and_ip
{
char *hostname;
- long ip,ip_mask; // Used with masked ip:s
+ long ip, ip_mask; // Used with masked ip:s
};
class ACL_ACCESS {
@@ -197,7 +219,7 @@ public:
uint hostname_length;
USER_RESOURCES user_resource;
char *user;
- uint8 salt[SCRAMBLE_LENGTH+1]; // scrambled password in binary form
+ 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;
@@ -206,7 +228,7 @@ public:
ACL_USER *copy(MEM_ROOT *root)
{
- ACL_USER *dst= (ACL_USER *)alloc_root(root, sizeof(ACL_USER));
+ ACL_USER *dst= (ACL_USER *) alloc_root(root, sizeof(ACL_USER));
if (!dst)
return 0;
*dst= *this;
@@ -219,7 +241,7 @@ public:
dst->plugin= plugin;
else
dst->plugin.str= strmake_root(root, plugin.str, plugin.length);
- dst->auth_string.str = safe_strdup_root(root, auth_string.str);
+ dst->auth_string.str= safe_strdup_root(root, auth_string.str);
dst->host.hostname= safe_strdup_root(root, host.hostname);
return dst;
}
@@ -232,7 +254,253 @@ public:
char *user,*db;
};
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
+static void update_hostname(acl_host_and_ip *host, const char *hostname);
+static ulong get_sort(uint count,...);
+static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
+ const char *ip);
+static bool show_proxy_grants (THD *thd, LEX_USER *user,
+ char *buff, size_t buffsize);
+
+class ACL_PROXY_USER :public ACL_ACCESS
+{
+ acl_host_and_ip host;
+ const char *user;
+ acl_host_and_ip proxied_host;
+ const char *proxied_user;
+ bool with_grant;
+
+ typedef enum {
+ MYSQL_PROXIES_PRIV_HOST,
+ MYSQL_PROXIES_PRIV_USER,
+ MYSQL_PROXIES_PRIV_PROXIED_HOST,
+ MYSQL_PROXIES_PRIV_PROXIED_USER,
+ MYSQL_PROXIES_PRIV_WITH_GRANT,
+ MYSQL_PROXIES_PRIV_GRANTOR,
+ MYSQL_PROXIES_PRIV_TIMESTAMP } old_acl_proxy_users;
+public:
+ ACL_PROXY_USER () {};
+
+ void init(const char *host_arg, const char *user_arg,
+ const char *proxied_host_arg, const char *proxied_user_arg,
+ bool with_grant_arg)
+ {
+ user= (user_arg && *user_arg) ? user_arg : NULL;
+ update_hostname (&host,
+ (host_arg && *host_arg) ? host_arg : NULL);
+ proxied_user= (proxied_user_arg && *proxied_user_arg) ?
+ proxied_user_arg : NULL;
+ update_hostname (&proxied_host,
+ (proxied_host_arg && *proxied_host_arg) ?
+ proxied_host_arg : NULL);
+ with_grant= with_grant_arg;
+ sort= get_sort(4, host.hostname, user,
+ proxied_host.hostname, proxied_user);
+ }
+
+ void init(MEM_ROOT *mem, const char *host_arg, const char *user_arg,
+ const char *proxied_host_arg, const char *proxied_user_arg,
+ bool with_grant_arg)
+ {
+ init ((host_arg && *host_arg) ? strdup_root (mem, host_arg) : NULL,
+ (user_arg && *user_arg) ? strdup_root (mem, user_arg) : NULL,
+ (proxied_host_arg && *proxied_host_arg) ?
+ strdup_root (mem, proxied_host_arg) : NULL,
+ (proxied_user_arg && *proxied_user_arg) ?
+ strdup_root (mem, proxied_user_arg) : NULL,
+ with_grant_arg);
+ }
+
+ void init(TABLE *table, MEM_ROOT *mem)
+ {
+ init (get_field(mem, table->field[MYSQL_PROXIES_PRIV_HOST]),
+ get_field(mem, table->field[MYSQL_PROXIES_PRIV_USER]),
+ get_field(mem, table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]),
+ get_field(mem, table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]),
+ table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->val_int() != 0);
+ }
+
+ bool get_with_grant() { return with_grant; }
+ const char *get_user() { return user; }
+ const char *get_host() { return host.hostname; }
+ const char *get_proxied_user() { return proxied_user; }
+ const char *get_proxied_host() { return proxied_host.hostname; }
+ void set_user(MEM_ROOT *mem, const char *user_arg)
+ {
+ user= user_arg && *user_arg ? strdup_root(mem, user_arg) : NULL;
+ }
+ void set_host(MEM_ROOT *mem, const char *host_arg)
+ {
+ update_hostname(&host,
+ (host_arg && *host_arg) ?
+ strdup_root(mem, host_arg) : NULL);
+ }
+
+ bool check_validity(bool check_no_resolve)
+ {
+ if (check_no_resolve &&
+ (hostname_requires_resolving(host.hostname) ||
+ hostname_requires_resolving(proxied_host.hostname)))
+ {
+ sql_print_warning("'proxies_priv' entry '%s@%s %s@%s' "
+ "ignored in --skip-name-resolve mode.",
+ proxied_user ? proxied_user : "",
+ proxied_host.hostname ? proxied_host.hostname : "",
+ user ? user : "",
+ host.hostname ? host.hostname : "");
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ bool matches(const char *host_arg, const char *user_arg, const char *ip_arg,
+ const char *proxied_user_arg)
+ {
+ DBUG_ENTER("ACL_PROXY_USER::matches");
+ DBUG_PRINT("info", ("compare_hostname(%s,%s,%s) &&"
+ "compare_hostname(%s,%s,%s) &&"
+ "wild_compare (%s,%s) &&"
+ "wild_compare (%s,%s)",
+ host.hostname ? host.hostname : "<NULL>",
+ host_arg ? host_arg : "<NULL>",
+ ip_arg ? ip_arg : "<NULL>",
+ proxied_host.hostname ? proxied_host.hostname : "<NULL>",
+ host_arg ? host_arg : "<NULL>",
+ ip_arg ? ip_arg : "<NULL>",
+ user_arg ? user_arg : "<NULL>",
+ user ? user : "<NULL>",
+ proxied_user_arg ? proxied_user_arg : "<NULL>",
+ proxied_user ? proxied_user : "<NULL>"));
+ DBUG_RETURN(compare_hostname(&host, host_arg, ip_arg) &&
+ compare_hostname(&proxied_host, host_arg, ip_arg) &&
+ (!user ||
+ (user_arg && !wild_compare(user_arg, user, TRUE))) &&
+ (!proxied_user ||
+ (proxied_user && !wild_compare(proxied_user_arg,
+ proxied_user, TRUE))));
+ }
+
+
+ inline static bool auth_element_equals(const char *a, const char *b)
+ {
+ return (a == b || (a != NULL && b != NULL && !strcmp(a,b)));
+ }
+
+
+ bool pk_equals(ACL_PROXY_USER *grant)
+ {
+ DBUG_ENTER("pk_equals");
+ DBUG_PRINT("info", ("strcmp(%s,%s) &&"
+ "strcmp(%s,%s) &&"
+ "wild_compare (%s,%s) &&"
+ "wild_compare (%s,%s)",
+ user ? user : "<NULL>",
+ grant->user ? grant->user : "<NULL>",
+ proxied_user ? proxied_user : "<NULL>",
+ grant->proxied_user ? grant->proxied_user : "<NULL>",
+ host.hostname ? host.hostname : "<NULL>",
+ grant->host.hostname ? grant->host.hostname : "<NULL>",
+ proxied_host.hostname ? proxied_host.hostname : "<NULL>",
+ grant->proxied_host.hostname ?
+ grant->proxied_host.hostname : "<NULL>"));
+
+ DBUG_RETURN(auth_element_equals(user, grant->user) &&
+ auth_element_equals(proxied_user, grant->proxied_user) &&
+ auth_element_equals(host.hostname, grant->host.hostname) &&
+ auth_element_equals(proxied_host.hostname,
+ grant->proxied_host.hostname));
+ }
+
+
+ bool granted_on(const char *host_arg, const char *user_arg)
+ {
+ return (((!user && (!user_arg || !user_arg[0])) ||
+ (user && user_arg && !strcmp(user, user_arg))) &&
+ ((!host.hostname && (!host_arg || !host_arg[0])) ||
+ (host.hostname && host_arg && !strcmp(host.hostname, host_arg))));
+ }
+
+
+ void print_grant(String *str)
+ {
+ str->append(STRING_WITH_LEN("GRANT PROXY ON '"));
+ if (proxied_user)
+ str->append(proxied_user, strlen(proxied_user));
+ str->append(STRING_WITH_LEN("'@'"));
+ if (proxied_host.hostname)
+ str->append(proxied_host.hostname, strlen(proxied_host.hostname));
+ str->append(STRING_WITH_LEN("' TO '"));
+ if (user)
+ str->append(user, strlen(user));
+ str->append(STRING_WITH_LEN("'@'"));
+ if (host.hostname)
+ str->append(host.hostname, strlen(host.hostname));
+ str->append(STRING_WITH_LEN("'"));
+ if (with_grant)
+ str->append(STRING_WITH_LEN(" WITH GRANT OPTION"));
+ }
+
+ void set_data(ACL_PROXY_USER *grant)
+ {
+ with_grant= grant->with_grant;
+ }
+
+ static int store_pk(TABLE *table,
+ const LEX_STRING *host,
+ const LEX_STRING *user,
+ const LEX_STRING *proxied_host,
+ const LEX_STRING *proxied_user)
+ {
+ DBUG_ENTER("ACL_PROXY_USER::store_pk");
+ DBUG_PRINT("info", ("host=%s, user=%s, proxied_host=%s, proxied_user=%s",
+ host->str ? host->str : "<NULL>",
+ user->str ? user->str : "<NULL>",
+ proxied_host->str ? proxied_host->str : "<NULL>",
+ proxied_user->str ? proxied_user->str : "<NULL>"));
+ if (table->field[MYSQL_PROXIES_PRIV_HOST]->store(host->str,
+ host->length,
+ system_charset_info))
+ DBUG_RETURN(TRUE);
+ if (table->field[MYSQL_PROXIES_PRIV_USER]->store(user->str,
+ user->length,
+ system_charset_info))
+ DBUG_RETURN(TRUE);
+ if (table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]->store(proxied_host->str,
+ proxied_host->length,
+ system_charset_info))
+ DBUG_RETURN(TRUE);
+ if (table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]->store(proxied_user->str,
+ proxied_user->length,
+ system_charset_info))
+ DBUG_RETURN(TRUE);
+
+ DBUG_RETURN(FALSE);
+ }
+
+ 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,
+ bool with_grant,
+ const char *grantor)
+ {
+ DBUG_ENTER("ACL_PROXY_USER::store_pk");
+ if (store_pk(table, host, user, proxied_host, proxied_user))
+ DBUG_RETURN(TRUE);
+ DBUG_PRINT("info", ("with_grant=%s", with_grant ? "TRUE" : "FALSE"));
+ if (table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0,
+ TRUE))
+ DBUG_RETURN(TRUE);
+ if (table->field[MYSQL_PROXIES_PRIV_GRANTOR]->store(grantor,
+ strlen(grantor),
+ system_charset_info))
+ DBUG_RETURN(TRUE);
+
+ DBUG_RETURN(FALSE);
+ }
+};
#define FIRST_NON_YN_FIELD 26
@@ -252,28 +520,29 @@ static uchar* acl_entry_get_key(acl_entry *entry, size_t *length,
return (uchar*) entry->key;
}
-#define IP_ADDR_STRLEN (3+1+3+1+3+1+3)
-#define ACL_KEY_LENGTH (IP_ADDR_STRLEN+1+NAME_LEN+1+USERNAME_LENGTH+1)
+#define IP_ADDR_STRLEN (3 + 1 + 3 + 1 + 3 + 1 + 3)
+#define ACL_KEY_LENGTH (IP_ADDR_STRLEN + 1 + NAME_LEN + \
+ 1 + USERNAME_LENGTH + 1)
#if defined(HAVE_OPENSSL)
/*
Without SSL the handshake consists of one packet. This packet
- has both client capabilites and scrambled password.
+ has both client capabilities and scrambled password.
With SSL the handshake might consist of two packets. If the first
packet (client capabilities) has CLIENT_SSL flag set, we have to
switch to SSL and read the second packet. The scrambled password
- is in the second packet and client_capabilites field will be ignored.
+ is in the second packet and client_capabilities field will be ignored.
Maybe it is better to accept flags other than CLIENT_SSL from the
second packet?
*/
#define SSL_HANDSHAKE_SIZE 2
-#define NORMAL_HANDSHAKE_SIZE 6
#define MIN_HANDSHAKE_SIZE 2
#else
#define MIN_HANDSHAKE_SIZE 6
#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
+#define NORMAL_HANDSHAKE_SIZE 6
-static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs;
+static DYNAMIC_ARRAY acl_hosts, acl_users, acl_dbs, acl_proxy_users;
static MEM_ROOT mem, memex;
static bool initialized=0;
static bool allow_all_hosts=1;
@@ -288,11 +557,9 @@ static void init_check_host(void);
static void rebuild_check_host(void);
static ACL_USER *find_acl_user(const char *host, const char *user,
my_bool exact);
-static bool update_user_table(THD *, TABLE *, const char *, const char *,
- const char *, uint);
-static void update_hostname(acl_host_and_ip *host, const char *hostname);
-static bool compare_hostname(const acl_host_and_ip *host,const char *hostname,
- const char *ip);
+static bool update_user_table(THD *thd, TABLE *table, const char *host,
+ const char *user, const char *new_password,
+ uint new_password_len);
static my_bool acl_load(THD *thd, TABLE_LIST *tables);
static my_bool grant_load(THD *thd, TABLE_LIST *tables);
static inline void get_grantor(THD *thd, char* grantor);
@@ -305,8 +572,10 @@ enum enum_acl_lists
DB_ACL,
COLUMN_PRIVILEGES_HASH,
PROC_PRIVILEGES_HASH,
- FUNC_PRIVILEGES_HASH
+ FUNC_PRIVILEGES_HASH,
+ PROXY_USERS_ACL
};
+
/*
Convert scrambled password to binary form, according to scramble type,
Binary form is stored in user.salt.
@@ -358,6 +627,7 @@ static char *fix_plugin_ptr(char *name)
*/
static bool fix_user_plugin_ptr(ACL_USER *user)
{
+ user->salt_len= 0;
if (my_strcasecmp(system_charset_info, user->plugin.str,
native_password_plugin_name.str) == 0)
user->plugin= native_password_plugin_name;
@@ -373,25 +643,6 @@ static bool fix_user_plugin_ptr(ACL_USER *user)
}
/*
- This after_update function is used when user.password is less than
- SCRAMBLE_LENGTH bytes.
-*/
-
-static void restrict_update_of_old_passwords_var(THD *thd,
- enum_var_type var_type)
-{
- if (var_type == OPT_GLOBAL)
- {
- pthread_mutex_lock(&LOCK_global_system_variables);
- global_system_variables.old_passwords= 1;
- pthread_mutex_unlock(&LOCK_global_system_variables);
- }
- else
- thd->variables.old_passwords= 1;
-}
-
-
-/*
Initialize structures responsible for user/db-level privilege checking and
load privilege information for them from tables in the 'mysql' database.
@@ -416,8 +667,8 @@ my_bool acl_init(bool dont_read_acl_tables)
DBUG_ENTER("acl_init");
acl_cache= new hash_filo(ACL_CACHE_SIZE, 0, 0,
- (hash_get_key) acl_entry_get_key,
- (hash_free_key) free,
+ (my_hash_get_key) acl_entry_get_key,
+ (my_hash_free_key) free,
&my_charset_utf8_bin);
/*
@@ -444,7 +695,6 @@ my_bool acl_init(bool dont_read_acl_tables)
DBUG_RETURN(1); /* purecov: inspected */
thd->thread_stack= (char*) &thd;
thd->store_globals();
- lex_start(thd);
/*
It is safe to call acl_reload() since acl_* arrays and hashes which
will be freed there are global static objects and thus are initialized
@@ -457,6 +707,30 @@ my_bool acl_init(bool dont_read_acl_tables)
DBUG_RETURN(return_val);
}
+/**
+ Choose from either native or old password plugins when assigning a password
+*/
+
+static bool
+set_user_plugin (ACL_USER *user, int password_len)
+{
+ switch (password_len)
+ {
+ case 0: /* no password */
+ case SCRAMBLED_PASSWORD_CHAR_LENGTH:
+ user->plugin= native_password_plugin_name;
+ return FALSE;
+ case SCRAMBLED_PASSWORD_CHAR_LENGTH_323:
+ user->plugin= old_password_plugin_name;
+ return FALSE;
+ default:
+ sql_print_warning("Found invalid password for user: '%s@%s'; "
+ "Ignoring user", user->user ? user->user : "",
+ user->host.hostname ? user->host.hostname : "");
+ return TRUE;
+ }
+}
+
/*
Initialize structures responsible for user/db-level privilege checking
@@ -481,7 +755,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
char tmp_name[SAFE_NAME_LEN+1];
int password_length;
- ulong old_sql_mode= thd->variables.sql_mode;
+ ulonglong old_sql_mode= thd->variables.sql_mode;
DBUG_ENTER("acl_load");
thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
@@ -496,7 +770,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
goto end;
table->use_all_columns();
- VOID(my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50));
+ (void) my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50);
while (!(read_record_info.read_record(&read_record_info)))
{
ACL_HOST host;
@@ -541,7 +815,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL;
}
#endif
- VOID(push_dynamic(&acl_hosts,(uchar*) &host));
+ (void) push_dynamic(&acl_hosts,(uchar*) &host);
}
my_qsort((uchar*) dynamic_element(&acl_hosts,0,ACL_HOST*),acl_hosts.elements,
sizeof(ACL_HOST),(qsort_cmp) acl_compare);
@@ -553,7 +827,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
goto end;
table->use_all_columns();
- VOID(my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100));
+ (void) my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100);
+ username_char_length= min(table->field[1]->char_length(), USERNAME_CHAR_LENGTH);
password_length= table->field[2]->field_length /
table->field[2]->charset()->mbmaxlen;
if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
@@ -566,23 +841,23 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
DBUG_PRINT("info",("user table fields: %d, password length: %d",
table->s->fields, password_length));
- pthread_mutex_lock(&LOCK_global_system_variables);
+ mysql_mutex_lock(&LOCK_global_system_variables);
if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH)
{
if (opt_secure_auth)
{
- pthread_mutex_unlock(&LOCK_global_system_variables);
+ mysql_mutex_unlock(&LOCK_global_system_variables);
sql_print_error("Fatal error: mysql.user table is in old format, "
"but server started with --secure-auth option.");
goto end;
}
- sys_old_passwords.after_update= restrict_update_of_old_passwords_var;
+ mysql_user_table_is_in_short_password_format= true;
if (global_system_variables.old_passwords)
- pthread_mutex_unlock(&LOCK_global_system_variables);
+ mysql_mutex_unlock(&LOCK_global_system_variables);
else
{
global_system_variables.old_passwords= 1;
- pthread_mutex_unlock(&LOCK_global_system_variables);
+ mysql_mutex_unlock(&LOCK_global_system_variables);
sql_print_warning("mysql.user table is not updated to new password format; "
"Disabling new password usage until "
"mysql_fix_privilege_tables is run");
@@ -591,17 +866,14 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
}
else
{
- sys_old_passwords.after_update= 0;
- pthread_mutex_unlock(&LOCK_global_system_variables);
+ mysql_user_table_is_in_short_password_format= false;
+ mysql_mutex_unlock(&LOCK_global_system_variables);
}
allow_all_hosts=0;
while (!(read_record_info.read_record(&read_record_info)))
{
ACL_USER user;
- char *password;
- uint password_len;
-
bzero(&user, sizeof(user));
update_hostname(&user.host, get_field(&mem, table->field[0]));
user.user= get_field(&mem, table->field[1]);
@@ -614,33 +886,14 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
continue;
}
- password= get_field(&mem, table->field[2]);
- password_len= password ? strlen(password) : 0;
+ char *password= get_field(&mem, table->field[2]);
+ uint password_len= password ? strlen(password) : 0;
user.auth_string.str= password ? password : const_cast<char*>("");
user.auth_string.length= password_len;
set_user_salt(&user, password, password_len);
- switch (password_len) {
- case 0: /* no password */
- case SCRAMBLED_PASSWORD_CHAR_LENGTH:
- user.plugin= native_password_plugin_name;
- break;
- case SCRAMBLED_PASSWORD_CHAR_LENGTH_323:
- user.plugin= old_password_plugin_name;
- break;
- case 45: /* 4.1: to be removed */
- sql_print_warning("Found 4.1.0 style password for user '%s@%s'. "
- "Ignoring user. "
- "You should change password for this user.",
- user.user ? user.user : "",
- user.host.hostname ? user.host.hostname : "");
- continue;
- default:
- sql_print_warning("Found invalid password for user: '%s@%s'; "
- "Ignoring user", user.user ? user.user : "",
- user.host.hostname ? user.host.hostname : "");
+ if (set_user_plugin(&user, password_len))
continue;
- }
{
uint next_field;
@@ -761,7 +1014,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
user.access|= SUPER_ACL | EXECUTE_ACL;
#endif
}
- VOID(push_dynamic(&acl_users,(uchar*) &user));
+ (void) push_dynamic(&acl_users,(uchar*) &user);
if (!user.host.hostname ||
(user.host.hostname[0] == wild_many && !user.host.hostname[1]))
allow_all_hosts=1; // Anyone can connect
@@ -777,7 +1030,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
goto end;
table->use_all_columns();
- VOID(my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100));
+ (void) my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100);
while (!(read_record_info.read_record(&read_record_info)))
{
ACL_DB db;
@@ -832,12 +1085,44 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
}
#endif
- VOID(push_dynamic(&acl_dbs,(uchar*) &db));
+ (void) push_dynamic(&acl_dbs,(uchar*) &db);
}
my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
sizeof(ACL_DB),(qsort_cmp) acl_compare);
end_read_record(&read_record_info);
freeze_size(&acl_dbs);
+
+ (void) my_init_dynamic_array(&acl_proxy_users, sizeof(ACL_PROXY_USER),
+ 50, 100);
+ if (tables[3].table)
+ {
+ init_read_record(&read_record_info, thd, table= tables[3].table, NULL, 1,
+ 0, FALSE);
+ table->use_all_columns();
+ while (!(read_record_info.read_record(&read_record_info)))
+ {
+ ACL_PROXY_USER proxy;
+ proxy.init(table, &mem);
+ if (proxy.check_validity(check_no_resolve))
+ continue;
+ if (push_dynamic(&acl_proxy_users, (uchar*) &proxy))
+ {
+ end_read_record(&read_record_info);
+ goto end;
+ }
+ }
+ my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER*),
+ acl_proxy_users.elements,
+ sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
+ end_read_record(&read_record_info);
+ }
+ else
+ {
+ sql_print_error("Missing system table mysql.proxies_priv; "
+ "please run mysql_upgrade to create it");
+ }
+ freeze_size(&acl_proxy_users);
+
init_check_host();
initialized=1;
@@ -856,7 +1141,8 @@ void acl_free(bool end)
delete_dynamic(&acl_users);
delete_dynamic(&acl_dbs);
delete_dynamic(&acl_wild_hosts);
- hash_free(&acl_check_hosts);
+ delete_dynamic(&acl_proxy_users);
+ my_hash_free(&acl_check_hosts);
plugin_unlock(0, native_password_plugin);
plugin_unlock(0, old_password_plugin);
if (!end)
@@ -890,65 +1176,65 @@ void acl_free(bool end)
my_bool acl_reload(THD *thd)
{
- TABLE_LIST tables[3];
- DYNAMIC_ARRAY old_acl_hosts,old_acl_users,old_acl_dbs;
+ TABLE_LIST tables[4];
+ DYNAMIC_ARRAY old_acl_hosts, old_acl_users, old_acl_dbs, old_acl_proxy_users;
MEM_ROOT old_mem;
bool old_initialized;
my_bool return_val= TRUE;
DBUG_ENTER("acl_reload");
- if (thd->locked_tables)
- { // Can't have locked tables here
- thd->lock=thd->locked_tables;
- thd->locked_tables=0;
- close_thread_tables(thd);
- }
-
/*
To avoid deadlocks we should obtain table locks before
obtaining acl_cache->lock mutex.
*/
- bzero((char*) tables, sizeof(tables));
- tables[0].alias= tables[0].table_name= (char*) "host";
- tables[1].alias= tables[1].table_name= (char*) "user";
- tables[2].alias= tables[2].table_name= (char*) "db";
- tables[0].db=tables[1].db=tables[2].db=(char*) "mysql";
- tables[0].next_local= tables[0].next_global= tables+1;
- tables[1].next_local= tables[1].next_global= tables+2;
- tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ;
- tables[0].skip_temporary= tables[1].skip_temporary=
- tables[2].skip_temporary= TRUE;
-
- if (simple_open_n_lock_tables(thd, tables))
+ tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("host"), "host", TL_READ);
+ tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("user"), "user", TL_READ);
+ tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("db"), "db", TL_READ);
+ tables[3].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("proxies_priv"),
+ "proxies_priv", TL_READ);
+ tables[0].next_local= tables[0].next_global= tables + 1;
+ tables[1].next_local= tables[1].next_global= tables + 2;
+ tables[2].next_local= tables[2].next_global= tables + 3;
+ tables[0].open_type= tables[1].open_type= tables[2].open_type=
+ tables[3].open_type= OT_BASE_ONLY;
+ tables[3].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
+
+ if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{
/*
Execution might have been interrupted; only print the error message
if an error condition has been raised.
*/
- if (thd->main_da.is_error())
+ if (thd->stmt_da->is_error())
sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
- thd->main_da.message());
+ thd->stmt_da->message());
goto end;
}
if ((old_initialized=initialized))
- VOID(pthread_mutex_lock(&acl_cache->lock));
+ mysql_mutex_lock(&acl_cache->lock);
- old_acl_hosts=acl_hosts;
- old_acl_users=acl_users;
- old_acl_dbs=acl_dbs;
- old_mem=mem;
+ old_acl_hosts= acl_hosts;
+ old_acl_users= acl_users;
+ old_acl_proxy_users= acl_proxy_users;
+ old_acl_dbs= acl_dbs;
+ old_mem= mem;
delete_dynamic(&acl_wild_hosts);
- hash_free(&acl_check_hosts);
+ my_hash_free(&acl_check_hosts);
if ((return_val= acl_load(thd, tables)))
{ // Error. Revert to old list
DBUG_PRINT("error",("Reverting to old privileges"));
acl_free(); /* purecov: inspected */
- acl_hosts=old_acl_hosts;
- acl_users=old_acl_users;
- acl_dbs=old_acl_dbs;
- mem=old_mem;
+ acl_hosts= old_acl_hosts;
+ acl_users= old_acl_users;
+ acl_proxy_users= old_acl_proxy_users;
+ acl_dbs= old_acl_dbs;
+ mem= old_mem;
init_check_host();
}
else
@@ -956,12 +1242,13 @@ my_bool acl_reload(THD *thd)
free_root(&old_mem,MYF(0));
delete_dynamic(&old_acl_hosts);
delete_dynamic(&old_acl_users);
+ delete_dynamic(&old_acl_proxy_users);
delete_dynamic(&old_acl_dbs);
}
if (old_initialized)
- VOID(pthread_mutex_unlock(&acl_cache->lock));
+ mysql_mutex_unlock(&acl_cache->lock);
end:
- close_thread_tables(thd);
+ close_mysql_tables(thd);
DBUG_RETURN(return_val);
}
@@ -1101,7 +1388,7 @@ bool acl_getroot(Security_context *sctx, char *user, char *host,
DBUG_RETURN(FALSE);
}
- VOID(pthread_mutex_lock(&acl_cache->lock));
+ mysql_mutex_lock(&acl_cache->lock);
sctx->master_access= 0;
sctx->db_access= 0;
@@ -1149,16 +1436,16 @@ bool acl_getroot(Security_context *sctx, char *user, char *host,
sctx->master_access= acl_user->access;
if (acl_user->user)
- strmake(sctx->priv_user, user, USERNAME_LENGTH);
+ strmake_buf(sctx->priv_user, user);
else
*sctx->priv_user= 0;
if (acl_user->host.hostname)
- strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME - 1);
+ strmake_buf(sctx->priv_host, acl_user->host.hostname);
else
*sctx->priv_host= 0;
}
- VOID(pthread_mutex_unlock(&acl_cache->lock));
+ mysql_mutex_unlock(&acl_cache->lock);
DBUG_RETURN(res);
}
@@ -1181,7 +1468,7 @@ static void acl_update_user(const char *user, const char *host,
const LEX_STRING *plugin,
const LEX_STRING *auth)
{
- safe_mutex_assert_owner(&acl_cache->lock);
+ mysql_mutex_assert_owner(&acl_cache->lock);
for (uint i=0 ; i < acl_users.elements ; i++)
{
@@ -1203,11 +1490,12 @@ static void acl_update_user(const char *user, const char *host,
acl_user->plugin.str= strmake_root(&mem, plugin->str, plugin->length);
}
else
- if (password)
+ if (password[0])
{
acl_user->auth_string.str= strmake_root(&mem, password, password_len);
acl_user->auth_string.length= password_len;
set_user_salt(acl_user, password, password_len);
+ set_user_plugin(acl_user, password_len);
}
acl_user->access=privileges;
if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
@@ -1249,7 +1537,7 @@ static void acl_insert_user(const char *user, const char *host,
{
ACL_USER acl_user;
- safe_mutex_assert_owner(&acl_cache->lock);
+ mysql_mutex_assert_owner(&acl_cache->lock);
acl_user.user=*user ? strdup_root(&mem,user) : 0;
update_hostname(&acl_user.host, *host ? strdup_root(&mem, host): 0);
@@ -1264,11 +1552,10 @@ static void acl_insert_user(const char *user, const char *host,
}
else
{
- acl_user.plugin= password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323 ?
- old_password_plugin_name : native_password_plugin_name;
acl_user.auth_string.str= strmake_root(&mem, password, password_len);
acl_user.auth_string.length= password_len;
set_user_salt(&acl_user, password, password_len);
+ set_user_plugin(&acl_user, password_len);
}
acl_user.access=privileges;
@@ -1281,7 +1568,7 @@ static void acl_insert_user(const char *user, const char *host,
acl_user.x509_issuer= x509_issuer ? strdup_root(&mem,x509_issuer) : 0;
acl_user.x509_subject=x509_subject ? strdup_root(&mem,x509_subject) : 0;
- VOID(push_dynamic(&acl_users,(uchar*) &acl_user));
+ (void) push_dynamic(&acl_users,(uchar*) &acl_user);
if (!acl_user.host.hostname ||
(acl_user.host.hostname[0] == wild_many && !acl_user.host.hostname[1]))
allow_all_hosts=1; // Anyone can connect /* purecov: tested */
@@ -1296,7 +1583,7 @@ static void acl_insert_user(const char *user, const char *host,
static void acl_update_db(const char *user, const char *host, const char *db,
ulong privileges)
{
- safe_mutex_assert_owner(&acl_cache->lock);
+ mysql_mutex_assert_owner(&acl_cache->lock);
for (uint i=0 ; i < acl_dbs.elements ; i++)
{
@@ -1342,13 +1629,13 @@ static void acl_insert_db(const char *user, const char *host, const char *db,
ulong privileges)
{
ACL_DB acl_db;
- safe_mutex_assert_owner(&acl_cache->lock);
+ mysql_mutex_assert_owner(&acl_cache->lock);
acl_db.user=strdup_root(&mem,user);
update_hostname(&acl_db.host, *host ? strdup_root(&mem,host) : 0);
acl_db.db=strdup_root(&mem,db);
acl_db.access=privileges;
acl_db.sort=get_sort(3,acl_db.host.hostname,acl_db.db,acl_db.user);
- VOID(push_dynamic(&acl_dbs,(uchar*) &acl_db));
+ (void) push_dynamic(&acl_dbs,(uchar*) &acl_db);
my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
sizeof(ACL_DB),(qsort_cmp) acl_compare);
}
@@ -1385,12 +1672,12 @@ ulong acl_get(const char *host, const char *ip,
}
key_length= (size_t) (end-key);
- VOID(pthread_mutex_lock(&acl_cache->lock));
+ mysql_mutex_lock(&acl_cache->lock);
if (!db_is_pattern && (entry=(acl_entry*) acl_cache->search((uchar*) key,
key_length)))
{
db_access=entry->access;
- VOID(pthread_mutex_unlock(&acl_cache->lock));
+ mysql_mutex_unlock(&acl_cache->lock);
DBUG_PRINT("exit", ("access: 0x%lx", db_access));
DBUG_RETURN(db_access);
}
@@ -1444,7 +1731,7 @@ exit:
memcpy((uchar*) entry->key,key,key_length);
acl_cache->add(entry);
}
- VOID(pthread_mutex_unlock(&acl_cache->lock));
+ mysql_mutex_unlock(&acl_cache->lock);
DBUG_PRINT("exit", ("access: 0x%lx", db_access & host_access));
DBUG_RETURN(db_access & host_access);
}
@@ -1460,10 +1747,11 @@ exit:
static void init_check_host(void)
{
DBUG_ENTER("init_check_host");
- VOID(my_init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip),
- acl_users.elements,1));
- VOID(hash_init(&acl_check_hosts,system_charset_info,acl_users.elements,0,0,
- (hash_get_key) check_get_key,0,0));
+ (void) my_init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip),
+ acl_users.elements,1);
+ (void) my_hash_init(&acl_check_hosts,system_charset_info,
+ acl_users.elements, 0, 0,
+ (my_hash_get_key) check_get_key, 0, 0);
if (!allow_all_hosts)
{
for (uint i=0 ; i < acl_users.elements ; i++)
@@ -1485,8 +1773,9 @@ static void init_check_host(void)
if (j == acl_wild_hosts.elements) // If new
(void) push_dynamic(&acl_wild_hosts,(uchar*) &acl_user->host);
}
- else if (!hash_search(&acl_check_hosts,(uchar*) acl_user->host.hostname,
- strlen(acl_user->host.hostname)))
+ else if (!my_hash_search(&acl_check_hosts,(uchar*)
+ acl_user->host.hostname,
+ strlen(acl_user->host.hostname)))
{
if (my_hash_insert(&acl_check_hosts,(uchar*) acl_user))
{ // End of memory
@@ -1513,7 +1802,7 @@ static void init_check_host(void)
void rebuild_check_host(void)
{
delete_dynamic(&acl_wild_hosts);
- hash_free(&acl_check_hosts);
+ my_hash_free(&acl_check_hosts);
init_check_host();
}
@@ -1524,12 +1813,12 @@ bool acl_check_host(const char *host, const char *ip)
{
if (allow_all_hosts)
return 0;
- VOID(pthread_mutex_lock(&acl_cache->lock));
+ mysql_mutex_lock(&acl_cache->lock);
- if ((host && hash_search(&acl_check_hosts,(uchar*) host,strlen(host))) ||
- (ip && hash_search(&acl_check_hosts,(uchar*) ip, strlen(ip))))
+ if ((host && my_hash_search(&acl_check_hosts,(uchar*) host,strlen(host))) ||
+ (ip && my_hash_search(&acl_check_hosts,(uchar*) ip, strlen(ip))))
{
- VOID(pthread_mutex_unlock(&acl_cache->lock));
+ mysql_mutex_unlock(&acl_cache->lock);
return 0; // Found host
}
for (uint i=0 ; i < acl_wild_hosts.elements ; i++)
@@ -1537,11 +1826,11 @@ bool acl_check_host(const char *host, const char *ip)
acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,i,acl_host_and_ip*);
if (compare_hostname(acl, host, ip))
{
- VOID(pthread_mutex_unlock(&acl_cache->lock));
+ mysql_mutex_unlock(&acl_cache->lock);
return 0; // Host ok
}
}
- VOID(pthread_mutex_unlock(&acl_cache->lock));
+ mysql_mutex_unlock(&acl_cache->lock);
return 1; // Host is not allowed
}
@@ -1577,7 +1866,7 @@ int check_change_password(THD *thd, const char *host, const char *user,
my_strcasecmp(system_charset_info, host,
thd->security_ctx->priv_host)))
{
- if (check_access(thd, UPDATE_ACL, "mysql",0,1,0,0))
+ if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 0))
return(1);
}
if (!thd->slave_thread && !thd->security_ctx->user[0])
@@ -1631,9 +1920,7 @@ bool change_password(THD *thd, const char *host, const char *user,
if (check_change_password(thd, host, user, new_password, new_password_len))
DBUG_RETURN(1);
- bzero((char*) &tables, sizeof(tables));
- tables.alias= tables.table_name= (char*) "user";
- tables.db= (char*) "mysql";
+ tables.init_one_table("mysql", 5, "user", 4, "user", TL_WRITE);
#ifdef HAVE_REPLICATION
/*
@@ -1652,8 +1939,7 @@ bool change_password(THD *thd, const char *host, const char *user,
DBUG_RETURN(0);
}
#endif
-
- if (!(table= open_ltable(thd, &tables, TL_WRITE, 0)))
+ if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
DBUG_RETURN(1);
/*
@@ -1661,59 +1947,61 @@ bool change_password(THD *thd, const char *host, const char *user,
row-based replication. The flag will be reset at the end of the
statement.
*/
- if ((save_binlog_row_based= thd->current_stmt_binlog_row_based))
- thd->clear_current_stmt_binlog_row_based();
+ if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
+ thd->clear_current_stmt_binlog_format_row();
- VOID(pthread_mutex_lock(&acl_cache->lock));
+ mysql_mutex_lock(&acl_cache->lock);
ACL_USER *acl_user;
if (!(acl_user= find_acl_user(host, user, TRUE)))
{
- VOID(pthread_mutex_unlock(&acl_cache->lock));
+ mysql_mutex_unlock(&acl_cache->lock);
my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
goto end;
}
+
/* update loaded acl entry: */
if (acl_user->plugin.str == native_password_plugin_name.str ||
acl_user->plugin.str == old_password_plugin_name.str)
{
acl_user->auth_string.str= strmake_root(&mem, new_password, new_password_len);
acl_user->auth_string.length= new_password_len;
- acl_user->plugin= new_password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323 ?
- old_password_plugin_name : native_password_plugin_name;
set_user_salt(acl_user, new_password, new_password_len);
+ set_user_plugin(acl_user, new_password_len);
}
+ else
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_SET_PASSWORD_AUTH_PLUGIN, ER(ER_SET_PASSWORD_AUTH_PLUGIN));
if (update_user_table(thd, table,
acl_user->host.hostname ? acl_user->host.hostname : "",
acl_user->user ? acl_user->user : "",
new_password, new_password_len))
{
- VOID(pthread_mutex_unlock(&acl_cache->lock)); /* purecov: deadcode */
+ mysql_mutex_unlock(&acl_cache->lock); /* purecov: deadcode */
goto end;
}
acl_cache->clear(1); // Clear locked hostname cache
- VOID(pthread_mutex_unlock(&acl_cache->lock));
+ mysql_mutex_unlock(&acl_cache->lock);
result= 0;
if (mysql_bin_log.is_open())
{
query_length=
- my_sprintf(buff,
- (buff,"SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'",
- acl_user->user ? acl_user->user : "",
- acl_user->host.hostname ? acl_user->host.hostname : "",
- new_password));
+ sprintf(buff,"SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'",
+ acl_user->user ? acl_user->user : "",
+ acl_user->host.hostname ? acl_user->host.hostname : "",
+ new_password);
thd->clear_error();
- result= thd->binlog_query(THD::MYSQL_QUERY_TYPE, buff, query_length,
- FALSE, FALSE, 0);
+ result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length,
+ FALSE, FALSE, FALSE, 0);
}
end:
- close_thread_tables(thd);
+ close_mysql_tables(thd);
/* Restore the state of binlog format */
- DBUG_ASSERT(!thd->current_stmt_binlog_row_based);
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
if (save_binlog_row_based)
- thd->set_current_stmt_binlog_row_based();
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result);
}
@@ -1740,9 +2028,9 @@ bool is_acl_user(const char *host, const char *user)
if (!initialized)
return TRUE;
- VOID(pthread_mutex_lock(&acl_cache->lock));
+ mysql_mutex_lock(&acl_cache->lock);
res= find_acl_user(host, user, TRUE) != NULL;
- VOID(pthread_mutex_unlock(&acl_cache->lock));
+ mysql_mutex_unlock(&acl_cache->lock);
return res;
}
@@ -1757,7 +2045,7 @@ find_acl_user(const char *host, const char *user, my_bool exact)
DBUG_ENTER("find_acl_user");
DBUG_PRINT("enter",("host: '%s' user: '%s'",host,user));
- safe_mutex_assert_owner(&acl_cache->lock);
+ mysql_mutex_assert_owner(&acl_cache->lock);
for (uint i=0 ; i < acl_users.elements ; i++)
{
@@ -1840,24 +2128,83 @@ static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
(ip && !wild_compare(ip, host->hostname, 0)));
}
+/**
+ Check if the given host name needs to be resolved or not.
+ Host name has to be resolved if it actually contains *name*.
+
+ For example:
+ 192.168.1.1 --> FALSE
+ 192.168.1.0/255.255.255.0 --> FALSE
+ % --> FALSE
+ 192.168.1.% --> FALSE
+ AB% --> FALSE
+
+ AAAAFFFF --> TRUE (Hostname)
+ AAAA:FFFF:1234:5678 --> FALSE
+ ::1 --> FALSE
+
+ This function does not check if the given string is a valid host name or
+ not. It assumes that the argument is a valid host name.
+
+ @param hostname the string to check.
+
+ @return a flag telling if the argument needs to be resolved or not.
+ @retval TRUE the argument is a host name and needs to be resolved.
+ @retval FALSE the argument is either an IP address, or a patter and
+ should not be resolved.
+*/
+
bool hostname_requires_resolving(const char *hostname)
{
- char cur;
if (!hostname)
return FALSE;
- size_t namelen= strlen(hostname);
- size_t lhlen= strlen(my_localhost);
- if ((namelen == lhlen) &&
- !my_strnncoll(system_charset_info, (const uchar *)hostname, namelen,
- (const uchar *)my_localhost, strlen(my_localhost)))
+
+ /* Check if hostname is the localhost. */
+
+ size_t hostname_len= strlen(hostname);
+ size_t localhost_len= strlen(my_localhost);
+
+ if (hostname == my_localhost ||
+ (hostname_len == localhost_len &&
+ !my_strnncoll(system_charset_info,
+ (const uchar *) hostname, hostname_len,
+ (const uchar *) my_localhost, strlen(my_localhost))))
+ {
return FALSE;
- for (; (cur=*hostname); hostname++)
+ }
+
+ /*
+ If the string contains any of {':', '%', '_', '/'}, it is definitely
+ not a host name:
+ - ':' means that the string is an IPv6 address;
+ - '%' or '_' means that the string is a pattern;
+ - '/' means that the string is an IPv4 network address;
+ */
+
+ for (const char *p= hostname; *p; ++p)
{
- if ((cur != '%') && (cur != '_') && (cur != '.') && (cur != '/') &&
- ((cur < '0') || (cur > '9')))
- return TRUE;
+ switch (*p) {
+ case ':':
+ case '%':
+ case '_':
+ case '/':
+ return FALSE;
+ }
}
- return FALSE;
+
+ /*
+ Now we have to tell a host name (ab.cd, 12.ab) from an IPv4 address
+ (12.34.56.78). The assumption is that if the string contains only
+ digits and dots, it is an IPv4 address. Otherwise -- a host name.
+ */
+
+ for (const char *p= hostname; *p; ++p)
+ {
+ if (*p != '.' && !my_isdigit(&my_charset_latin1, *p))
+ return TRUE; /* a "letter" has been found. */
+ }
+
+ return FALSE; /* all characters are either dots or digits. */
}
@@ -1926,16 +2273,15 @@ static bool test_if_create_new_users(THD *thd)
{
TABLE_LIST tl;
ulong db_access;
- bzero((char*) &tl,sizeof(tl));
- tl.db= (char*) "mysql";
- tl.table_name= (char*) "user";
+ tl.init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("user"), "user", TL_WRITE);
create_new_users= 1;
db_access=acl_get(sctx->host, sctx->ip,
sctx->priv_user, tl.db, 0);
if (!(db_access & INSERT_ACL))
{
- if (check_grant(thd, INSERT_ACL, &tl, 0, UINT_MAX, 1))
+ if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE))
create_new_users=0;
}
}
@@ -1947,20 +2293,18 @@ static bool test_if_create_new_users(THD *thd)
Handle GRANT commands
****************************************************************************/
-static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
+static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo,
ulong rights, bool revoke_grant,
bool can_create_user, bool no_auto_create)
{
int error = -1;
bool old_row_exists=0;
- const char *password= "";
- uint password_len= 0;
char what= (revoke_grant) ? 'N' : 'Y';
uchar user_key[MAX_KEY_LENGTH];
LEX *lex= thd->lex;
DBUG_ENTER("replace_user_table");
- safe_mutex_assert_owner(&acl_cache->lock);
+ mysql_mutex_assert_owner(&acl_cache->lock);
if (combo.password.str && combo.password.str[0])
{
@@ -1970,9 +2314,9 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
DBUG_RETURN(-1);
}
- password_len= combo.password.length;
- password=combo.password.str;
}
+ else
+ combo.password= empty_lex_str;
table->use_all_columns();
table->field[0]->store(combo.host.str,combo.host.length,
@@ -2005,7 +2349,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
see also test_if_create_new_users()
*/
- else if (!password_len && no_auto_create)
+ else if (!combo.password.length && !combo.plugin.length && no_auto_create)
{
my_error(ER_PASSWORD_NO_MATCH, MYF(0));
goto end;
@@ -2030,21 +2374,11 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
system_charset_info);
table->field[1]->store(combo.user.str,combo.user.length,
system_charset_info);
- table->field[2]->store(password, password_len,
- system_charset_info);
}
else
{
old_row_exists = 1;
store_record(table,record[1]); // Save copy for update
- if (combo.password.str) // If password given
- table->field[2]->store(password, password_len, system_charset_info);
- else if (!rights && !revoke_grant &&
- lex->ssl_type == SSL_TYPE_NOT_SPECIFIED &&
- !lex->mqh.specified_limits)
- {
- DBUG_RETURN(0);
- }
}
/* Update table columns with new privileges */
@@ -2062,6 +2396,8 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
}
rights= get_access(table, 3, &next_field);
DBUG_PRINT("info",("table fields: %d",table->s->fields));
+ if (combo.password.str[0])
+ table->field[2]->store(combo.password.str, combo.password.length, system_charset_info);
if (table->s->fields >= 31) /* From 4.0.0 we have more fields */
{
/* We write down SSL related ACL stuff */
@@ -2119,13 +2455,26 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
table->field[next_field+3]->store((longlong) mqh.user_conn, FALSE);
mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour;
- next_field+=4;
- if (table->s->fields >= 41 && combo.plugin.str[0])
+ next_field+= 4;
+ if (table->s->fields >= 41)
{
- table->field[next_field]->store(combo.plugin.str, combo.plugin.length,
- system_charset_info);
- table->field[next_field+1]->store(combo.auth.str, combo.auth.length,
- system_charset_info);
+ table->field[next_field]->set_notnull();
+ table->field[next_field + 1]->set_notnull();
+ if (combo.plugin.str[0])
+ {
+ DBUG_ASSERT(combo.password.str[0] == 0);
+ table->field[2]->reset();
+ table->field[next_field]->store(combo.plugin.str, combo.plugin.length,
+ system_charset_info);
+ table->field[next_field + 1]->store(combo.auth.str, combo.auth.length,
+ system_charset_info);
+ }
+ if (combo.password.str[0])
+ {
+ DBUG_ASSERT(combo.plugin.str[0] == 0);
+ table->field[next_field]->reset();
+ table->field[next_field + 1]->reset();
+ }
}
}
@@ -2166,7 +2515,7 @@ end:
acl_cache->clear(1); // Clear privilege cache
if (old_row_exists)
acl_update_user(combo.user.str, combo.host.str,
- combo.password.str, password_len,
+ combo.password.str, combo.password.length,
lex->ssl_type,
lex->ssl_cipher,
lex->x509_issuer,
@@ -2176,7 +2525,8 @@ end:
&combo.plugin,
&combo.auth);
else
- acl_insert_user(combo.user.str, combo.host.str, password, password_len,
+ acl_insert_user(combo.user.str, combo.host.str,
+ combo.password.str, combo.password.length,
lex->ssl_type,
lex->ssl_cipher,
lex->x509_issuer,
@@ -2299,6 +2649,170 @@ abort:
}
+static void
+acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke)
+{
+ mysql_mutex_assert_owner(&acl_cache->lock);
+
+ DBUG_ENTER("acl_update_proxy_user");
+ for (uint i= 0; i < acl_proxy_users.elements; i++)
+ {
+ ACL_PROXY_USER *acl_user=
+ dynamic_element(&acl_proxy_users, i, ACL_PROXY_USER *);
+
+ if (acl_user->pk_equals(new_value))
+ {
+ if (is_revoke)
+ {
+ DBUG_PRINT("info", ("delting ACL_PROXY_USER"));
+ delete_dynamic_element(&acl_proxy_users, i);
+ }
+ else
+ {
+ DBUG_PRINT("info", ("updating ACL_PROXY_USER"));
+ acl_user->set_data(new_value);
+ }
+ break;
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+static void
+acl_insert_proxy_user(ACL_PROXY_USER *new_value)
+{
+ DBUG_ENTER("acl_insert_proxy_user");
+ mysql_mutex_assert_owner(&acl_cache->lock);
+ (void) push_dynamic(&acl_proxy_users, (uchar *) new_value);
+ my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER *),
+ acl_proxy_users.elements,
+ sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
+ DBUG_VOID_RETURN;
+}
+
+
+static int
+replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
+ const LEX_USER *proxied_user, bool with_grant_arg,
+ bool revoke_grant)
+{
+ bool old_row_exists= 0;
+ int error;
+ uchar user_key[MAX_KEY_LENGTH];
+ ACL_PROXY_USER new_grant;
+ char grantor[USER_HOST_BUFF_SIZE];
+
+ DBUG_ENTER("replace_proxies_priv_table");
+
+ if (!initialized)
+ {
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
+ DBUG_RETURN(-1);
+ }
+
+ /* Check if there is such a user in user table in memory? */
+ if (!find_acl_user(user->host.str,user->user.str, FALSE))
+ {
+ my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
+ DBUG_RETURN(-1);
+ }
+
+ table->use_all_columns();
+ ACL_PROXY_USER::store_pk (table, &user->host, &user->user,
+ &proxied_user->host, &proxied_user->user);
+
+ key_copy(user_key, table->record[0], table->key_info,
+ table->key_info->key_length);
+
+ get_grantor(thd, grantor);
+
+ if ((error= table->file->ha_index_init(0, 1)))
+ {
+ table->file->print_error(error, MYF(0));
+ DBUG_PRINT("info", ("ha_index_init error"));
+ DBUG_RETURN(-1);
+ }
+
+ if (table->file->ha_index_read_map(table->record[0], user_key,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))
+ {
+ DBUG_PRINT ("info", ("Row not found"));
+ if (revoke_grant)
+ { // no row, no revoke
+ my_error(ER_NONEXISTING_GRANT, MYF(0), user->user.str, user->host.str);
+ goto abort;
+ }
+ old_row_exists= 0;
+ restore_record(table, s->default_values);
+ ACL_PROXY_USER::store_data_record(table, &user->host, &user->user,
+ &proxied_user->host,
+ &proxied_user->user,
+ with_grant_arg,
+ grantor);
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Row found"));
+ old_row_exists= 1;
+ store_record(table, record[1]);
+ }
+
+ if (old_row_exists)
+ {
+ /* update old existing row */
+ if (!revoke_grant)
+ {
+ if ((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])))
+ goto table_error; /* purecov: inspected */
+ }
+ }
+ else if ((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))
+ goto table_error; /* purecov: inspected */
+ }
+
+ acl_cache->clear(1); // Clear privilege cache
+ if (old_row_exists)
+ {
+ new_grant.init(user->host.str, user->user.str,
+ proxied_user->host.str, proxied_user->user.str,
+ with_grant_arg);
+ acl_update_proxy_user(&new_grant, revoke_grant);
+ }
+ else
+ {
+ new_grant.init(&mem, user->host.str, user->user.str,
+ proxied_user->host.str, proxied_user->user.str,
+ with_grant_arg);
+ acl_insert_proxy_user(&new_grant);
+ }
+
+ table->file->ha_index_end();
+ DBUG_RETURN(0);
+
+ /* This could only happen if the grant tables got corrupted */
+table_error:
+ DBUG_PRINT("info", ("table error"));
+ table->file->print_error(error, MYF(0)); /* purecov: inspected */
+
+abort:
+ DBUG_PRINT("info", ("aborting replace_proxies_priv_table"));
+ table->file->ha_index_end();
+ DBUG_RETURN(-1);
+}
+
+
class GRANT_COLUMN :public Sql_alloc
{
public:
@@ -2389,8 +2903,8 @@ GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
const char *t, ulong p, ulong c)
:GRANT_NAME(h,d,u,t,p, FALSE), cols(c)
{
- (void) hash_init2(&hash_columns,4,system_charset_info,
- 0,0,0, (hash_get_key) get_key_column,0,0);
+ (void) my_hash_init2(&hash_columns,4,system_charset_info,
+ 0,0,0, (my_hash_get_key) get_key_column,0,0);
}
@@ -2433,15 +2947,15 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
if (!db || !tname)
{
/* Wrong table row; Ignore it */
- hash_clear(&hash_columns); /* allow for destruction */
+ my_hash_clear(&hash_columns); /* allow for destruction */
cols= 0;
return;
}
cols= (ulong) form->field[7]->val_int();
cols = fix_rights_for_column(cols);
- (void) hash_init2(&hash_columns,4,system_charset_info,
- 0,0,0, (hash_get_key) get_key_column,0,0);
+ (void) my_hash_init2(&hash_columns,4,system_charset_info,
+ 0,0,0, (my_hash_get_key) get_key_column,0,0);
if (cols)
{
uint key_prefix_len;
@@ -2461,7 +2975,12 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
key_copy(key, col_privs->record[0], col_privs->key_info, key_prefix_len);
col_privs->field[4]->store("",0, &my_charset_latin1);
- col_privs->file->ha_index_init(0, 1);
+ if (col_privs->file->ha_index_init(0, 1))
+ {
+ cols= 0;
+ return;
+ }
+
if (col_privs->file->ha_index_read_map(col_privs->record[0], (uchar*) key,
(key_part_map)15,
HA_READ_KEY_EXACT))
@@ -2499,7 +3018,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
GRANT_TABLE::~GRANT_TABLE()
{
- hash_free(&hash_columns);
+ my_hash_free(&hash_columns);
}
@@ -2513,7 +3032,7 @@ static uchar* get_grant_table(GRANT_NAME *buff, size_t *length,
void free_grant_table(GRANT_TABLE *grant_table)
{
- hash_free(&grant_table->hash_columns);
+ my_hash_free(&grant_table->hash_columns);
}
@@ -2542,11 +3061,11 @@ static GRANT_NAME *name_hash_search(HASH *name_hash,
len = (uint) (end - helping);
if (name_tolower)
my_casedn_str(files_charset_info, tname_ptr);
- for (grant_name= (GRANT_NAME*) hash_first(name_hash, (uchar*) helping,
- len, &state);
+ for (grant_name= (GRANT_NAME*) my_hash_first(name_hash, (uchar*) helping,
+ len, &state);
grant_name ;
- grant_name= (GRANT_NAME*) hash_next(name_hash,(uchar*) helping,
- len, &state))
+ grant_name= (GRANT_NAME*) my_hash_next(name_hash,(uchar*) helping,
+ len, &state))
{
if (exact)
{
@@ -2590,7 +3109,8 @@ table_hash_search(const char *host, const char *ip, const char *db,
inline GRANT_COLUMN *
column_hash_search(GRANT_TABLE *t, const char *cname, uint length)
{
- return (GRANT_COLUMN*) hash_search(&t->hash_columns, (uchar*) cname,length);
+ return (GRANT_COLUMN*) my_hash_search(&t->hash_columns,
+ (uchar*) cname, length);
}
@@ -2600,7 +3120,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
const char *db, const char *table_name,
ulong rights, bool revoke_grant)
{
- int error=0,result=0;
+ int result=0;
uchar key[MAX_KEY_LENGTH];
uint key_prefix_length;
KEY_PART_INFO *key_part= table->key_info->key_part;
@@ -2627,7 +3147,13 @@ static int replace_column_table(GRANT_TABLE *g_t,
List_iterator <LEX_COLUMN> iter(columns);
class LEX_COLUMN *column;
- table->file->ha_index_init(0, 1);
+ int error= table->file->ha_index_init(0, 1);
+ if (error)
+ {
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(-1);
+ }
+
while ((column= iter++))
{
ulong privileges= column->rights;
@@ -2774,7 +3300,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
goto end; /* purecov: deadcode */
}
if (grant_column)
- hash_delete(&g_t->hash_columns,(uchar*) grant_column);
+ my_hash_delete(&g_t->hash_columns,(uchar*) grant_column);
}
}
} while (!table->file->ha_index_next(table->record[0]) &&
@@ -2912,7 +3438,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
}
else
{
- hash_delete(&column_priv_hash,(uchar*) grant_table);
+ my_hash_delete(&column_priv_hash,(uchar*) grant_table);
}
DBUG_RETURN(0);
@@ -3031,7 +3557,8 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
}
else
{
- hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(uchar*) grant_name);
+ my_hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(uchar*)
+ grant_name);
}
DBUG_RETURN(0);
@@ -3093,8 +3620,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
class LEX_COLUMN *column;
List_iterator <LEX_COLUMN> column_iter(columns);
- if (open_and_lock_tables(thd, table_list) ||
- mysql_handle_derived(thd->lex, DT_PREPARE))
+ if (open_normal_and_derived_tables(thd, table_list, 0, DT_PREPARE))
DBUG_RETURN(TRUE);
while ((column = column_iter++))
@@ -3116,7 +3642,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
DBUG_RETURN(TRUE);
column_priv|= column->rights;
}
- close_thread_tables(thd);
+ close_mysql_tables(thd);
}
else
{
@@ -3148,27 +3674,26 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
/* open the mysql.tables_priv and mysql.columns_priv tables */
- bzero((char*) &tables,sizeof(tables));
- tables[0].alias=tables[0].table_name= (char*) "user";
- tables[1].alias=tables[1].table_name= (char*) "tables_priv";
- tables[2].alias=tables[2].table_name= (char*) "columns_priv";
+ tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("user"), "user", TL_WRITE);
+ tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("tables_priv"),
+ "tables_priv", TL_WRITE);
+ tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("columns_priv"),
+ "columns_priv", TL_WRITE);
tables[0].next_local= tables[0].next_global= tables+1;
/* Don't open column table if we don't need it ! */
- tables[1].next_local=
- tables[1].next_global= ((column_priv ||
- (revoke_grant &&
- ((rights & COL_ACLS) || columns.elements)))
- ? tables+2 : 0);
- tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE;
- tables[0].db=tables[1].db=tables[2].db=(char*) "mysql";
+ if (column_priv || (revoke_grant && ((rights & COL_ACLS) || columns.elements)))
+ tables[1].next_local= tables[1].next_global= tables+2;
/*
This statement will be replicated as a statement, even when using
row-based replication. The flag will be reset at the end of the
statement.
*/
- save_binlog_row_based= thd->current_stmt_binlog_row_based;
- thd->clear_current_stmt_binlog_row_based();
+ if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
+ thd->clear_current_stmt_binlog_format_row();
#ifdef HAVE_REPLICATION
/*
@@ -3185,7 +3710,9 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
{
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(FALSE);
}
}
@@ -3197,19 +3724,27 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
*/
Query_tables_list backup;
thd->lex->reset_n_backup_query_tables_list(&backup);
- if (simple_open_n_lock_tables(thd,tables))
+ /*
+ Restore Query_tables_list::sql_command value, which was reset
+ above, as the code writing query to the binary log assumes that
+ this value corresponds to the statement being executed.
+ */
+ thd->lex->sql_command= backup.sql_command;
+ if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{ // Should never happen
- close_thread_tables(thd); /* purecov: deadcode */
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ thd->lex->restore_backup_query_tables_list(&backup);
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(TRUE); /* purecov: deadcode */
}
if (!revoke_grant)
create_new_users= test_if_create_new_users(thd);
bool result= FALSE;
- rw_wrlock(&LOCK_grant);
- pthread_mutex_lock(&acl_cache->lock);
+ mysql_rwlock_wrlock(&LOCK_grant);
+ mysql_mutex_lock(&acl_cache->lock);
MEM_ROOT *old_root= thd->mem_root;
thd->mem_root= &memex;
grant_version++;
@@ -3281,8 +3816,8 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
column_priv= 0;
for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++)
{
- grant_column= (GRANT_COLUMN*) hash_element(&grant_table->hash_columns,
- idx);
+ grant_column= (GRANT_COLUMN*)
+ my_hash_element(&grant_table->hash_columns, idx);
grant_column->rights&= ~rights; // Fix other columns
column_priv|= grant_column->rights;
}
@@ -3314,14 +3849,14 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
}
}
thd->mem_root= old_root;
- pthread_mutex_unlock(&acl_cache->lock);
+ mysql_mutex_unlock(&acl_cache->lock);
if (!result) /* success */
{
result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
}
- rw_unlock(&LOCK_grant);
+ mysql_rwlock_unlock(&LOCK_grant);
if (!result) /* success */
my_ok(thd);
@@ -3329,7 +3864,9 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
/* Tables are automatically closed */
thd->lex->restore_backup_query_tables_list(&backup);
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result);
}
@@ -3382,20 +3919,19 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
/* open the mysql.user and mysql.procs_priv tables */
- bzero((char*) &tables,sizeof(tables));
- tables[0].alias=tables[0].table_name= (char*) "user";
- tables[1].alias=tables[1].table_name= (char*) "procs_priv";
+ tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("user"), "user", TL_WRITE);
+ tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("procs_priv"), "procs_priv", TL_WRITE);
tables[0].next_local= tables[0].next_global= tables+1;
- tables[0].lock_type=tables[1].lock_type=TL_WRITE;
- tables[0].db=tables[1].db=(char*) "mysql";
/*
This statement will be replicated as a statement, even when using
row-based replication. The flag will be reset at the end of the
statement.
*/
- save_binlog_row_based= thd->current_stmt_binlog_row_based;
- thd->clear_current_stmt_binlog_row_based();
+ if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
+ thd->clear_current_stmt_binlog_format_row();
#ifdef HAVE_REPLICATION
/*
@@ -3412,24 +3948,27 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
{
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(FALSE);
}
}
#endif
- if (simple_open_n_lock_tables(thd,tables))
+ if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{ // Should never happen
- close_thread_tables(thd);
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(TRUE);
}
if (!revoke_grant)
create_new_users= test_if_create_new_users(thd);
- rw_wrlock(&LOCK_grant);
- pthread_mutex_lock(&acl_cache->lock);
+ mysql_rwlock_wrlock(&LOCK_grant);
+ mysql_mutex_lock(&acl_cache->lock);
MEM_ROOT *old_root= thd->mem_root;
thd->mem_root= &memex;
@@ -3490,7 +4029,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
}
}
thd->mem_root= old_root;
- pthread_mutex_unlock(&acl_cache->lock);
+ mysql_mutex_unlock(&acl_cache->lock);
if (write_to_binlog)
{
@@ -3498,9 +4037,11 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
result= TRUE;
}
- rw_unlock(&LOCK_grant);
+ mysql_rwlock_unlock(&LOCK_grant);
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
/* Tables are automatically closed */
DBUG_RETURN(result);
@@ -3508,10 +4049,10 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
- ulong rights, bool revoke_grant)
+ ulong rights, bool revoke_grant, bool is_proxy)
{
List_iterator <LEX_USER> str_list (list);
- LEX_USER *Str, *tmp_Str;
+ LEX_USER *Str, *tmp_Str, *proxied_user= NULL;
char tmp_db[SAFE_NAME_LEN+1];
bool create_new_users=0;
TABLE_LIST tables[2];
@@ -3536,21 +4077,35 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
db=tmp_db;
}
- /* open the mysql.user and mysql.db tables */
- bzero((char*) &tables,sizeof(tables));
- tables[0].alias=tables[0].table_name=(char*) "user";
- tables[1].alias=tables[1].table_name=(char*) "db";
+ if (is_proxy)
+ {
+ DBUG_ASSERT(!db);
+ proxied_user= str_list++;
+ }
+
+ /* open the mysql.user and mysql.db or mysql.proxies_priv tables */
+ tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("user"), "user", TL_WRITE);
+ if (is_proxy)
+
+ tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("proxies_priv"),
+ "proxies_priv",
+ TL_WRITE);
+ else
+ tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("db"),
+ "db",
+ TL_WRITE);
tables[0].next_local= tables[0].next_global= tables+1;
- tables[0].lock_type=tables[1].lock_type=TL_WRITE;
- tables[0].db=tables[1].db=(char*) "mysql";
/*
This statement will be replicated as a statement, even when using
row-based replication. The flag will be reset at the end of the
statement.
*/
- save_binlog_row_based= thd->current_stmt_binlog_row_based;
- thd->clear_current_stmt_binlog_row_based();
+ if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
+ thd->clear_current_stmt_binlog_format_row();
#ifdef HAVE_REPLICATION
/*
@@ -3567,17 +4122,20 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
{
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(FALSE);
}
}
#endif
- if (simple_open_n_lock_tables(thd,tables))
+ if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{ // This should never happen
- close_thread_tables(thd); /* purecov: deadcode */
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(TRUE); /* purecov: deadcode */
}
@@ -3585,8 +4143,8 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
create_new_users= test_if_create_new_users(thd);
/* go through users in user_list */
- rw_wrlock(&LOCK_grant);
- VOID(pthread_mutex_lock(&acl_cache->lock));
+ mysql_rwlock_wrlock(&LOCK_grant);
+ mysql_mutex_lock(&acl_cache->lock);
grant_version++;
int result=0;
@@ -3624,21 +4182,29 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
result= -1;
}
}
+ else if (is_proxy)
+ {
+ if (replace_proxies_priv_table (thd, tables[1].table, Str, proxied_user,
+ rights & GRANT_ACL ? TRUE : FALSE,
+ revoke_grant))
+ result= -1;
+ }
}
- VOID(pthread_mutex_unlock(&acl_cache->lock));
+ mysql_mutex_unlock(&acl_cache->lock);
if (!result)
{
result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
}
- rw_unlock(&LOCK_grant);
- close_thread_tables(thd);
+ mysql_rwlock_unlock(&LOCK_grant);
if (!result)
my_ok(thd);
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result);
}
@@ -3649,9 +4215,9 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
void grant_free(void)
{
DBUG_ENTER("grant_free");
- hash_free(&column_priv_hash);
- hash_free(&proc_priv_hash);
- hash_free(&func_priv_hash);
+ my_hash_free(&column_priv_hash);
+ my_hash_free(&proc_priv_hash);
+ my_hash_free(&func_priv_hash);
free_root(&memex,MYF(0));
DBUG_VOID_RETURN;
}
@@ -3676,7 +4242,6 @@ my_bool grant_init()
DBUG_RETURN(1); /* purecov: deadcode */
thd->thread_stack= (char*) &thd;
thd->store_globals();
- lex_start(thd);
return_val= grant_reload(thd);
delete thd;
/* Remember that we don't have a THD */
@@ -3708,13 +4273,16 @@ static my_bool grant_load_procs_priv(TABLE *p_table)
MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
THR_MALLOC);
DBUG_ENTER("grant_load_procs_priv");
- (void) hash_init(&proc_priv_hash, &my_charset_utf8_bin,
- 0,0,0, (hash_get_key) get_grant_table,
- 0,0);
- (void) hash_init(&func_priv_hash, &my_charset_utf8_bin,
- 0,0,0, (hash_get_key) get_grant_table,
- 0,0);
- p_table->file->ha_index_init(0, 1);
+ (void) my_hash_init(&proc_priv_hash, &my_charset_utf8_bin,
+ 0,0,0, (my_hash_get_key) get_grant_table,
+ 0,0);
+ (void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin,
+ 0,0,0, (my_hash_get_key) get_grant_table,
+ 0,0);
+
+ if (p_table->file->ha_index_init(0, 1))
+ DBUG_RETURN(TRUE);
+
p_table->use_all_columns();
if (!p_table->file->ha_index_first(p_table->record[0]))
@@ -3804,18 +4372,21 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables)
bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
THR_MALLOC);
- ulong old_sql_mode= thd->variables.sql_mode;
+ ulonglong old_sql_mode= thd->variables.sql_mode;
DBUG_ENTER("grant_load");
thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
- (void) hash_init(&column_priv_hash, &my_charset_utf8_bin,
- 0,0,0, (hash_get_key) get_grant_table,
- (hash_free_key) free_grant_table,0);
+ (void) my_hash_init(&column_priv_hash, &my_charset_utf8_bin,
+ 0,0,0, (my_hash_get_key) get_grant_table,
+ (my_hash_free_key) free_grant_table,0);
t_table = tables[0].table;
c_table = tables[1].table;
- t_table->file->ha_index_init(0, 1);
+
+ if (t_table->file->ha_index_init(0, 1))
+ goto end_index_init;
+
t_table->use_all_columns();
c_table->use_all_columns();
@@ -3860,9 +4431,10 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables)
return_val=0; // Return ok
end_unlock:
- thd->variables.sql_mode= old_sql_mode;
t_table->file->ha_index_end();
my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
+end_index_init:
+ thd->variables.sql_mode= old_sql_mode;
DBUG_RETURN(return_val);
}
@@ -3887,19 +4459,15 @@ static my_bool grant_reload_procs_priv(THD *thd)
my_bool return_val= FALSE;
DBUG_ENTER("grant_reload_procs_priv");
- bzero((char*) &table, sizeof(table));
- table.alias= table.table_name= (char*) "procs_priv";
- table.db= (char *) "mysql";
- table.lock_type= TL_READ;
- table.skip_temporary= 1;
+ table.init_one_table("mysql", 5, "procs_priv",
+ strlen("procs_priv"), "procs_priv",
+ TL_READ);
+ table.open_type= OT_BASE_ONLY;
- if (simple_open_n_lock_tables(thd, &table))
- {
- close_thread_tables(thd);
+ if (open_and_lock_tables(thd, &table, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
DBUG_RETURN(TRUE);
- }
- rw_wrlock(&LOCK_grant);
+ mysql_rwlock_wrlock(&LOCK_grant);
/* Save a copy of the current hash if we need to undo the grant load */
old_proc_priv_hash= proc_priv_hash;
old_func_priv_hash= func_priv_hash;
@@ -3914,12 +4482,12 @@ static my_bool grant_reload_procs_priv(THD *thd)
}
else
{
- hash_free(&old_proc_priv_hash);
- hash_free(&old_func_priv_hash);
+ my_hash_free(&old_proc_priv_hash);
+ my_hash_free(&old_func_priv_hash);
}
- rw_unlock(&LOCK_grant);
+ mysql_rwlock_unlock(&LOCK_grant);
- close_thread_tables(thd);
+ close_mysql_tables(thd);
DBUG_RETURN(return_val);
}
@@ -3951,21 +4519,23 @@ my_bool grant_reload(THD *thd)
if (!initialized)
DBUG_RETURN(0);
- bzero((char*) tables, sizeof(tables));
- tables[0].alias= tables[0].table_name= (char*) "tables_priv";
- tables[1].alias= tables[1].table_name= (char*) "columns_priv";
- tables[0].db= tables[1].db= (char *) "mysql";
+ tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("tables_priv"),
+ "tables_priv", TL_READ);
+ tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("columns_priv"),
+ "columns_priv", TL_READ);
tables[0].next_local= tables[0].next_global= tables+1;
- tables[0].lock_type= tables[1].lock_type= TL_READ;
- tables[0].skip_temporary= tables[1].skip_temporary= TRUE;
+ tables[0].open_type= tables[1].open_type= OT_BASE_ONLY;
+
/*
To avoid deadlocks we should obtain table locks before
obtaining LOCK_grant rwlock.
*/
- if (simple_open_n_lock_tables(thd, tables))
+ if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
goto end;
- rw_wrlock(&LOCK_grant);
+ mysql_rwlock_wrlock(&LOCK_grant);
old_column_priv_hash= column_priv_hash;
/*
@@ -3984,11 +4554,11 @@ my_bool grant_reload(THD *thd)
}
else
{
- hash_free(&old_column_priv_hash);
+ my_hash_free(&old_column_priv_hash);
free_root(&old_mem,MYF(0));
}
- rw_unlock(&LOCK_grant);
- close_thread_tables(thd);
+ mysql_rwlock_unlock(&LOCK_grant);
+ close_mysql_tables(thd);
/*
It is OK failing to load procs_priv table because we may be
@@ -3997,45 +4567,59 @@ my_bool grant_reload(THD *thd)
if (grant_reload_procs_priv(thd))
return_val= 1;
- rw_wrlock(&LOCK_grant);
+ mysql_rwlock_wrlock(&LOCK_grant);
grant_version++;
- rw_unlock(&LOCK_grant);
+ mysql_rwlock_unlock(&LOCK_grant);
end:
DBUG_RETURN(return_val);
}
-/****************************************************************************
- Check table level grants
- SYNOPSIS
- bool check_grant()
- thd Thread handler
- want_access Bits of privileges user needs to have
- tables List of tables to check. The user should have 'want_access'
- to all tables in list.
- show_table <> 0 if we are in show table. In this case it's enough to have
- any privilege for the table
- number Check at most this number of tables.
- no_errors If 0 then we write an error. The error is sent directly to
- the client
+/**
+ @brief Check table level grants
- RETURN
- 0 ok
- 1 Error: User did not have the requested privileges
+ @param thd Thread handler
+ @param want_access Bits of privileges user needs to have.
+ @param tables List of tables to check. The user should have
+ 'want_access' to all tables in list.
+ @param any_combination_will_do TRUE if it's enough to have any privilege for
+ any combination of the table columns.
+ @param number Check at most this number of tables.
+ @param no_errors TRUE if no error should be sent directly to the client.
+
+ If table->grant.want_privilege != 0 then the requested privileges where
+ in the set of COL_ACLS but access was not granted on the table level. As
+ a consequence an extra check of column privileges is required.
- NOTE
- This functions assumes that either number of tables to be inspected
+ Specifically if this function returns FALSE the user has some kind of
+ privilege on a combination of columns in each table.
+
+ This function is usually preceeded by check_access which establish the
+ User-, Db- and Host access rights.
+
+ @see check_access
+ @see check_table_access
+
+ @note This functions assumes that either number of tables to be inspected
by it is limited explicitly (i.e. is is not UINT_MAX) or table list
used and thd->lex->query_tables_own_last value correspond to each
other (the latter should be either 0 or point to next_global member
of one of elements of this table list).
-****************************************************************************/
+
+ @return Access status
+ @retval FALSE Access granted; But column privileges might need to be
+ checked.
+ @retval TRUE The user did not have the requested privileges on any of the
+ tables.
+
+*/
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
- uint show_table, uint number, bool no_errors)
+ bool any_combination_will_do, uint number, bool no_errors)
{
- TABLE_LIST *table, *first_not_own_table= thd->lex->first_not_own_table();
+ TABLE_LIST *tl;
+ 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;
@@ -4052,76 +4636,111 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
the given table list refers to the list for prelocking (contains tables
of other queries). For simple queries first_not_own_table is 0.
*/
- for (i= 0, table= tables;
- i < number && table != first_not_own_table;
- table= table->next_global, i++)
+ for (i= 0, tl= tables;
+ i < number && tl != first_not_own_table;
+ tl= tl->next_global, i++)
{
- /* Remove SHOW_VIEW_ACL, because it will be checked during making view */
- table->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
+ /*
+ Save a copy of the privileges without the SHOW_VIEW_ACL attribute.
+ It will be checked during making view.
+ */
+ tl->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
}
- rw_rdlock(&LOCK_grant);
- for (table= tables;
- table && number-- && table != first_not_own_table;
- table= table->next_global)
+ mysql_rwlock_rdlock(&LOCK_grant);
+ for (tl= tables;
+ tl && number-- && tl != first_not_own_table;
+ tl= tl->next_global)
{
- GRANT_TABLE *grant_table;
- sctx = test(table->security_ctx) ?
- table->security_ctx : thd->security_ctx;
+ sctx = test(tl->security_ctx) ? tl->security_ctx : thd->security_ctx;
+
+ const ACL_internal_table_access *access=
+ get_cached_table_access(&tl->grant.m_internal,
+ tl->get_db_name(),
+ tl->get_table_name());
+
+ if (access)
+ {
+ switch(access->check(orig_want_access, &tl->grant.privilege))
+ {
+ case ACL_INTERNAL_ACCESS_GRANTED:
+ /*
+ Currently,
+ - the information_schema does not subclass ACL_internal_table_access,
+ there are no per table privilege checks for I_S,
+ - the performance schema does use per tables checks, but at most
+ returns 'CHECK_GRANT', and never 'ACCESS_GRANTED'.
+ so this branch is not used.
+ */
+ DBUG_ASSERT(0);
+ case ACL_INTERNAL_ACCESS_DENIED:
+ goto err;
+ case ACL_INTERNAL_ACCESS_CHECK_GRANT:
+ break;
+ }
+ }
want_access= orig_want_access;
want_access&= ~sctx->master_access;
if (!want_access)
continue; // ok
- if (!(~table->grant.privilege & want_access) ||
- table->is_anonymous_derived_table() || table->schema_table)
+ if (!(~tl->grant.privilege & want_access) ||
+ tl->is_anonymous_derived_table() || tl->schema_table)
{
/*
- It is subquery in the FROM clause. VIEW set table->derived after
+ It is subquery in the FROM clause. VIEW set tl->derived after
table opening, but this function always called before table opening.
*/
- if (!table->referencing_view)
+ if (!tl->referencing_view)
{
/*
If it's a temporary table created for a subquery in the FROM
clause, or an INFORMATION_SCHEMA table, drop the request for
a privilege.
*/
- table->grant.want_privilege= 0;
+ tl->grant.want_privilege= 0;
}
continue;
}
- if (!(grant_table= table_hash_search(sctx->host, sctx->ip,
- table->get_db_name(), sctx->priv_user,
- table->get_table_name(), FALSE)))
+ GRANT_TABLE *grant_table= table_hash_search(sctx->host, sctx->ip,
+ tl->get_db_name(),
+ sctx->priv_user,
+ tl->get_table_name(),
+ FALSE);
+
+ if (!grant_table)
{
- want_access &= ~table->grant.privilege;
+ want_access &= ~tl->grant.privilege;
goto err; // No grants
}
- if (show_table)
- continue; // We have some priv on this
- table->grant.grant_table=grant_table; // Remember for column test
- table->grant.version=grant_version;
- table->grant.privilege|= grant_table->privs;
- table->grant.want_privilege= ((want_access & COL_ACLS)
- & ~table->grant.privilege);
+ /*
+ For SHOW COLUMNS, SHOW INDEX it is enough to have some
+ privileges on any column combination on the table.
+ */
+ if (any_combination_will_do)
+ continue;
- if (!(~table->grant.privilege & want_access))
+ tl->grant.grant_table= grant_table; // Remember for column test
+ tl->grant.version= grant_version;
+ tl->grant.privilege|= grant_table->privs;
+ tl->grant.want_privilege= ((want_access & COL_ACLS) & ~tl->grant.privilege);
+
+ if (!(~tl->grant.privilege & want_access))
continue;
- if (want_access & ~(grant_table->cols | table->grant.privilege))
+ if (want_access & ~(grant_table->cols | tl->grant.privilege))
{
- want_access &= ~(grant_table->cols | table->grant.privilege);
+ want_access &= ~(grant_table->cols | tl->grant.privilege);
goto err; // impossible
}
}
- rw_unlock(&LOCK_grant);
- DBUG_RETURN(0);
+ mysql_rwlock_unlock(&LOCK_grant);
+ DBUG_RETURN(FALSE);
err:
- rw_unlock(&LOCK_grant);
+ mysql_rwlock_unlock(&LOCK_grant);
if (!no_errors) // Not a silent skip of table
{
char command[128];
@@ -4132,9 +4751,9 @@ err:
command,
sctx->priv_user,
sctx->host_or_ip,
- table ? table->get_table_name() : "unknown");
+ tl ? tl->get_table_name() : "unknown");
}
- DBUG_RETURN(1);
+ DBUG_RETURN(TRUE);
}
@@ -4169,7 +4788,7 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant,
if (!want_access)
DBUG_RETURN(0); // Already checked
- rw_rdlock(&LOCK_grant);
+ mysql_rwlock_rdlock(&LOCK_grant);
/* reload table if someone has modified any grants */
@@ -4187,12 +4806,12 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant,
grant_column=column_hash_search(grant_table, name, length);
if (grant_column && !(~grant_column->rights & want_access))
{
- rw_unlock(&LOCK_grant);
+ mysql_rwlock_unlock(&LOCK_grant);
DBUG_RETURN(0);
}
err:
- rw_unlock(&LOCK_grant);
+ mysql_rwlock_unlock(&LOCK_grant);
char command[128];
get_privilege_desc(command, sizeof(command), want_access);
my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
@@ -4307,7 +4926,7 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg,
*/
bool using_column_privileges= FALSE;
- rw_rdlock(&LOCK_grant);
+ mysql_rwlock_rdlock(&LOCK_grant);
for (; !fields->end_of_fields(); fields->next())
{
@@ -4348,11 +4967,11 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg,
goto err;
}
}
- rw_unlock(&LOCK_grant);
+ mysql_rwlock_unlock(&LOCK_grant);
return 0;
err:
- rw_unlock(&LOCK_grant);
+ mysql_rwlock_unlock(&LOCK_grant);
char command[128];
get_privilege_desc(command, sizeof(command), want_access);
@@ -4381,7 +5000,7 @@ static bool check_grant_db_routine(THD *thd, const char *db, HASH *hash)
for (uint idx= 0; idx < hash->records; ++idx)
{
- GRANT_NAME *item= (GRANT_NAME*) hash_element(hash, idx);
+ GRANT_NAME *item= (GRANT_NAME*) my_hash_element(hash, idx);
if (strcmp(item->user, sctx->priv_user) == 0 &&
strcmp(item->db, db) == 0 &&
@@ -4416,12 +5035,13 @@ bool check_grant_db(THD *thd,const char *db)
len= (uint) (end - helping) + 1;
- rw_rdlock(&LOCK_grant);
+ mysql_rwlock_rdlock(&LOCK_grant);
for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
{
- GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
- idx);
+ GRANT_TABLE *grant_table= (GRANT_TABLE*)
+ my_hash_element(&column_priv_hash,
+ idx);
if (len < grant_table->key_length &&
!memcmp(grant_table->hash_key,helping,len) &&
compare_hostname(&grant_table->host, sctx->host, sctx->ip))
@@ -4435,7 +5055,7 @@ bool check_grant_db(THD *thd,const char *db)
error= check_grant_db_routine(thd, db, &proc_priv_hash) &&
check_grant_db_routine(thd, db, &func_priv_hash);
- rw_unlock(&LOCK_grant);
+ mysql_rwlock_unlock(&LOCK_grant);
return error;
}
@@ -4471,7 +5091,7 @@ bool check_grant_routine(THD *thd, ulong want_access,
if (!want_access)
DBUG_RETURN(0); // ok
- rw_rdlock(&LOCK_grant);
+ mysql_rwlock_rdlock(&LOCK_grant);
for (table= procs; table; table= table->next_global)
{
GRANT_NAME *grant_proc;
@@ -4485,10 +5105,10 @@ bool check_grant_routine(THD *thd, ulong want_access,
goto err;
}
}
- rw_unlock(&LOCK_grant);
+ mysql_rwlock_unlock(&LOCK_grant);
DBUG_RETURN(0);
err:
- rw_unlock(&LOCK_grant);
+ mysql_rwlock_unlock(&LOCK_grant);
if (!no_errors)
{
char buff[1024];
@@ -4529,13 +5149,13 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name,
bool no_routine_acl= 1;
GRANT_NAME *grant_proc;
Security_context *sctx= thd->security_ctx;
- rw_rdlock(&LOCK_grant);
+ mysql_rwlock_rdlock(&LOCK_grant);
if ((grant_proc= routine_hash_search(sctx->priv_host,
sctx->ip, db,
sctx->priv_user,
name, is_proc, 0)))
no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
- rw_unlock(&LOCK_grant);
+ mysql_rwlock_unlock(&LOCK_grant);
return no_routine_acl;
}
@@ -4547,14 +5167,14 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name,
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;
GRANT_TABLE *grant_table;
- rw_rdlock(&LOCK_grant);
+ mysql_rwlock_rdlock(&LOCK_grant);
#ifdef EMBEDDED_LIBRARY
grant_table= NULL;
#else
- Security_context *sctx= thd->security_ctx;
- const char *db = table->db ? table->db : thd->db;
grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
table->table_name, 0);
#endif
@@ -4563,7 +5183,7 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table)
if (grant_table)
table->grant.privilege|= grant_table->privs;
privilege= table->grant.privilege;
- rw_unlock(&LOCK_grant);
+ mysql_rwlock_unlock(&LOCK_grant);
return privilege;
}
@@ -4594,7 +5214,7 @@ ulong get_column_grant(THD *thd, GRANT_INFO *grant,
GRANT_COLUMN *grant_column;
ulong priv;
- rw_rdlock(&LOCK_grant);
+ mysql_rwlock_rdlock(&LOCK_grant);
/* reload table if someone has modified any grants */
if (grant->version != grant_version)
{
@@ -4617,7 +5237,7 @@ ulong get_column_grant(THD *thd, GRANT_INFO *grant,
else
priv= (grant->privilege | grant_table->privs | grant_column->rights);
}
- rw_unlock(&LOCK_grant);
+ mysql_rwlock_unlock(&LOCK_grant);
return priv;
}
@@ -4645,13 +5265,13 @@ 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 USER", "EVENT", "TRIGGER", "CREATE TABLESPACE"
};
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
+ 14, 13, 11, 5, 7, 17
};
@@ -4685,14 +5305,14 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
DBUG_RETURN(TRUE);
}
- rw_rdlock(&LOCK_grant);
- VOID(pthread_mutex_lock(&acl_cache->lock));
+ mysql_rwlock_rdlock(&LOCK_grant);
+ mysql_mutex_lock(&acl_cache->lock);
acl_user= find_acl_user(lex_user->host.str, lex_user->user.str, TRUE);
if (!acl_user)
{
- VOID(pthread_mutex_unlock(&acl_cache->lock));
- rw_unlock(&LOCK_grant);
+ mysql_mutex_unlock(&acl_cache->lock);
+ mysql_rwlock_unlock(&LOCK_grant);
my_error(ER_NONEXISTING_GRANT, MYF(0),
lex_user->user.str, lex_user->host.str);
@@ -4706,11 +5326,11 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
strxmov(buff,"Grants for ",lex_user->user.str,"@",
lex_user->host.str,NullS);
field_list.push_back(field);
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
{
- VOID(pthread_mutex_unlock(&acl_cache->lock));
- rw_unlock(&LOCK_grant);
+ mysql_mutex_unlock(&acl_cache->lock);
+ mysql_rwlock_unlock(&LOCK_grant);
DBUG_RETURN(TRUE);
}
@@ -4905,8 +5525,8 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
for (index=0 ; index < column_priv_hash.records ; index++)
{
const char *user, *host;
- GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
- index);
+ GRANT_TABLE *grant_table= (GRANT_TABLE*)
+ my_hash_element(&column_priv_hash, index);
if (!(user=grant_table->user))
user= "";
@@ -4959,7 +5579,7 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
col_index++)
{
GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
- hash_element(&grant_table->hash_columns,col_index);
+ my_hash_element(&grant_table->hash_columns,col_index);
if (grant_column->rights & j)
{
if (!found_col)
@@ -5030,9 +5650,15 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
goto end;
}
+ if (show_proxy_grants(thd, lex_user, buff, sizeof(buff)))
+ {
+ error= -1;
+ goto end;
+ }
+
end:
- VOID(pthread_mutex_unlock(&acl_cache->lock));
- rw_unlock(&LOCK_grant);
+ mysql_mutex_unlock(&acl_cache->lock);
+ mysql_rwlock_unlock(&LOCK_grant);
my_eof(thd);
DBUG_RETURN(error);
@@ -5049,7 +5675,7 @@ static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash,
for (index=0 ; index < hash->records ; index++)
{
const char *user, *host;
- GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, index);
+ GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, index);
if (!(user=grant_proc->user))
user= "";
@@ -5156,14 +5782,14 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc)
{
ACL_USER *acl_user;
- pthread_mutex_lock(&acl_cache->lock);
+ mysql_mutex_lock(&acl_cache->lock);
if (initialized && (acl_user= find_acl_user(host,user, FALSE)))
uc->user_resources= acl_user->user_resource;
else
bzero((char*) &uc->user_resources, sizeof(uc->user_resources));
- pthread_mutex_unlock(&acl_cache->lock);
+ mysql_mutex_unlock(&acl_cache->lock);
}
/*
@@ -5187,7 +5813,7 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc)
< 0 Error.
*/
-#define GRANT_TABLES 5
+#define GRANT_TABLES 6
int open_grant_tables(THD *thd, TABLE_LIST *tables)
{
DBUG_ENTER("open_grant_tables");
@@ -5198,21 +5824,29 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables)
DBUG_RETURN(-1);
}
- bzero((char*) tables, GRANT_TABLES*sizeof(*tables));
- tables->alias= tables->table_name= (char*) "user";
- (tables+1)->alias= (tables+1)->table_name= (char*) "db";
- (tables+2)->alias= (tables+2)->table_name= (char*) "tables_priv";
- (tables+3)->alias= (tables+3)->table_name= (char*) "columns_priv";
- (tables+4)->alias= (tables+4)->table_name= (char*) "procs_priv";
- tables->next_local= tables->next_global= tables+1;
- (tables+1)->next_local= (tables+1)->next_global= tables+2;
- (tables+2)->next_local= (tables+2)->next_global= tables+3;
- (tables+3)->next_local= (tables+3)->next_global= tables+4;
- tables->lock_type= (tables+1)->lock_type=
- (tables+2)->lock_type= (tables+3)->lock_type=
- (tables+4)->lock_type= TL_WRITE;
- tables->db= (tables+1)->db= (tables+2)->db=
- (tables+3)->db= (tables+4)->db= (char*) "mysql";
+ tables->init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("user"), "user", TL_WRITE);
+ (tables+1)->init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("db"), "db", TL_WRITE);
+ (tables+2)->init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("tables_priv"),
+ "tables_priv", TL_WRITE);
+ (tables+3)->init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("columns_priv"),
+ "columns_priv", TL_WRITE);
+ (tables+4)->init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("procs_priv"),
+ "procs_priv", TL_WRITE);
+ (tables+5)->init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("proxies_priv"),
+ "proxies_priv", TL_WRITE);
+ tables[5].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
+
+ tables->next_local= tables->next_global= tables + 1;
+ (tables+1)->next_local= (tables+1)->next_global= tables + 2;
+ (tables+2)->next_local= (tables+2)->next_global= tables + 3;
+ (tables+3)->next_local= (tables+3)->next_global= tables + 4;
+ (tables+4)->next_local= (tables+4)->next_global= tables + 5;
#ifdef HAVE_REPLICATION
/*
@@ -5225,18 +5859,17 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables)
The tables must be marked "updating" so that tables_ok() takes them into
account in tests.
*/
- tables[0].updating=tables[1].updating=tables[2].updating=
- tables[3].updating=tables[4].updating=1;
+ tables[0].updating= tables[1].updating= tables[2].updating=
+ tables[3].updating= tables[4].updating= tables[5].updating= 1;
if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
DBUG_RETURN(1);
- tables[0].updating=tables[1].updating=tables[2].updating=
- tables[3].updating=tables[4].updating=0;;
+ tables[0].updating= tables[1].updating= tables[2].updating=
+ tables[3].updating= tables[4].updating= tables[5].updating= 0;
}
#endif
- if (simple_open_n_lock_tables(thd, tables))
+ if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{ // This should never happen
- close_thread_tables(thd);
DBUG_RETURN(-1);
}
@@ -5249,7 +5882,7 @@ ACL_USER *check_acl_user(LEX_USER *user_name,
ACL_USER *acl_user= 0;
uint counter;
- safe_mutex_assert_owner(&acl_cache->lock);
+ mysql_mutex_assert_owner(&acl_cache->lock);
for (counter= 0 ; counter < acl_users.elements ; counter++)
{
@@ -5360,7 +5993,7 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
int error;
TABLE *table= tables[table_no].table;
Field *host_field= table->field[0];
- Field *user_field= table->field[table_no ? 2 : 1];
+ Field *user_field= table->field[table_no && table_no != 5 ? 2 : 1];
char *host_str= user_from->host.str;
char *user_str= user_from->user.str;
const char *host;
@@ -5443,12 +6076,15 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
user= "";
#ifdef EXTRA_DEBUG
- DBUG_PRINT("loop",("scan fields: '%s'@'%s' '%s' '%s' '%s'",
- user, host,
- get_field(thd->mem_root, table->field[1]) /*db*/,
- get_field(thd->mem_root, table->field[3]) /*table*/,
- get_field(thd->mem_root,
- table->field[4]) /*column*/));
+ if (table_no != 5)
+ {
+ DBUG_PRINT("loop",("scan fields: '%s'@'%s' '%s' '%s' '%s'",
+ user, host,
+ get_field(thd->mem_root, table->field[1]) /*db*/,
+ get_field(thd->mem_root, table->field[3]) /*table*/,
+ get_field(thd->mem_root,
+ table->field[4]) /*column*/));
+ }
#endif
if (strcmp(user_str, user) ||
my_strcasecmp(system_charset_info, host_str, host))
@@ -5474,7 +6110,7 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
/**
Handle an in-memory privilege structure.
- @param struct_no The number of the structure to handle (0..4).
+ @param struct_no The number of the structure to handle (0..5).
@param drop If user_from is to be dropped.
@param user_from The the user to be searched/dropped/renamed.
@param user_to The new name for the user if to be renamed, NULL otherwise.
@@ -5491,22 +6127,23 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
2 COLUMN_PRIVILEGES_HASH
3 PROC_PRIVILEGES_HASH
4 FUNC_PRIVILEGES_HASH
+ 5 PROXY_USERS_ACL
@retval > 0 At least one element matched.
@retval 0 OK, but no element matched.
- @retval -1 Wrong arguments to function or Out of Memory
*/
static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
LEX_USER *user_from, LEX_USER *user_to)
{
int result= 0;
- uint idx;
- uint elements;
+ int idx;
+ int elements;
const char *user;
const char *host;
ACL_USER *acl_user= NULL;
ACL_DB *acl_db= NULL;
+ ACL_PROXY_USER *acl_proxy_user= NULL;
GRANT_NAME *grant_name= NULL;
HASH *grant_name_hash= NULL;
DBUG_ENTER("handle_grant_struct");
@@ -5516,7 +6153,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
LINT_INIT(user);
LINT_INIT(host);
- safe_mutex_assert_owner(&acl_cache->lock);
+ mysql_mutex_assert_owner(&acl_cache->lock);
/* Get the number of elements in the in-memory structure. */
switch (struct_no) {
@@ -5538,7 +6175,11 @@ 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 PROXY_USERS_ACL:
+ elements= acl_proxy_users.elements;
+ break;
default:
+ DBUG_ASSERT(0);
return -1;
}
@@ -5546,8 +6187,8 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
DBUG_PRINT("loop",("scan struct: %u search user: '%s' host: '%s'",
struct_no, user_from->user.str, user_from->host.str));
#endif
- /* Loop over all elements. */
- for (idx= 0; idx < elements; idx++)
+ /* Loop over all elements *backwards* (see the comment below). */
+ for (idx= elements - 1; idx >= 0; idx--)
{
/*
Get a pointer to the element.
@@ -5568,13 +6209,19 @@ 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:
- grant_name= (GRANT_NAME*) hash_element(grant_name_hash, idx);
+ grant_name= (GRANT_NAME*) my_hash_element(grant_name_hash, idx);
user= grant_name->user;
host= grant_name->host.hostname;
break;
+ case PROXY_USERS_ACL:
+ acl_proxy_user= dynamic_element(&acl_proxy_users, idx, ACL_PROXY_USER*);
+ user= acl_proxy_user->get_user();
+ host= acl_proxy_user->get_host();
+ break;
+
default:
- MY_ASSERT_UNREACHABLE();
+ DBUG_ASSERT(0);
}
if (! user)
user= "";
@@ -5592,6 +6239,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
result= 1; /* At least one element found. */
if ( drop )
{
+ elements--;
switch ( struct_no ) {
case USER_ACL:
delete_dynamic_element(&acl_users, idx);
@@ -5604,24 +6252,23 @@ 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:
- hash_delete(grant_name_hash, (uchar*) grant_name);
+ my_hash_delete(grant_name_hash, (uchar*) grant_name);
+ /*
+ In our HASH implementation on deletion one elements
+ is moved into a place where a deleted element was,
+ and the last element is moved into the empty space.
+ Thus we need to re-examine the current element, but
+ we don't have to restart the search from the beginning.
+ */
+ if (idx != elements)
+ idx++;
break;
+
+ case PROXY_USERS_ACL:
+ delete_dynamic_element(&acl_proxy_users, idx);
+ break;
+
}
- elements--;
- /*
- - If we are iterating through an array then we just have moved all
- elements after the current element one position closer to its head.
- This means that we have to take another look at the element at
- current position as it is a new element from the array's tail.
- - If we are iterating through a hash the current element was replaced
- with one of elements from the tail. So we also have to take a look
- at the new element in current position.
- Note that in our HASH implementation hash_delete() won't move any
- elements with position after current one to position before the
- current (i.e. from the tail to the head), so it is safe to continue
- iteration without re-starting.
- */
- idx--;
}
else if ( user_to )
{
@@ -5662,17 +6309,22 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
my_hash_update(grant_name_hash, (uchar*) grant_name, (uchar*) old_key,
old_key_length);
/*
- hash_update() operation could have moved element from the tail
- of the hash to the current position. So we need to take a look
- at the element in current position once again.
- Thanks to the fact that hash_update() for our HASH implementation
- won't move any elements from the tail of the hash to the positions
- before the current one (a.k.a. head) it is safe to continue
- iteration without restarting.
+ hash_update() operation could have moved element from the tail or
+ the head of the hash to the current position. But it can never
+ move an element from the head to the tail or from the tail to the
+ head over the current element.
+ So we need to examine the current element once again, but
+ we don't need to restart the search from the beginning.
*/
- idx--;
+ if (idx != elements)
+ idx++;
break;
}
+
+ case PROXY_USERS_ACL:
+ acl_proxy_user->set_user (&mem, user_to->user.str);
+ acl_proxy_user->set_host (&mem, user_to->host.str);
+ break;
}
}
else
@@ -5815,6 +6467,23 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
result= 1; /* At least one record/element found. */
}
}
+
+ /* Handle proxies_priv table. */
+ if (tables[5].table)
+ {
+ if ((found= handle_grant_table(tables, 5, drop, user_from, user_to)) < 0)
+ {
+ /* Handle of table failed, don't touch the in-memory array. */
+ result= -1;
+ }
+ else
+ {
+ /* Handle proxies_priv array. */
+ if ((handle_grant_struct(PROXY_USERS_ACL, drop, user_from, user_to) && !result) ||
+ found)
+ result= 1; /* At least one record/element found. */
+ }
+ }
end:
DBUG_RETURN(result);
}
@@ -5861,19 +6530,21 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list)
row-based replication. The flag will be reset at the end of the
statement.
*/
- save_binlog_row_based= thd->current_stmt_binlog_row_based;
- thd->clear_current_stmt_binlog_row_based();
+ if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
+ thd->clear_current_stmt_binlog_format_row();
/* CREATE USER may be skipped on replication client. */
if ((result= open_grant_tables(thd, tables)))
{
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result != 1);
}
- rw_wrlock(&LOCK_grant);
- VOID(pthread_mutex_lock(&acl_cache->lock));
+ mysql_rwlock_wrlock(&LOCK_grant);
+ mysql_mutex_lock(&acl_cache->lock);
while ((tmp_user_name= user_list++))
{
@@ -5902,7 +6573,7 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list)
}
}
- VOID(pthread_mutex_unlock(&acl_cache->lock));
+ mysql_mutex_unlock(&acl_cache->lock);
if (result)
my_error(ER_CANNOT_USER, MYF(0), "CREATE USER", wrong_users.c_ptr_safe());
@@ -5910,10 +6581,11 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list)
if (some_users_created)
result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
- rw_unlock(&LOCK_grant);
- close_thread_tables(thd);
+ mysql_rwlock_unlock(&LOCK_grant);
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result);
}
@@ -5939,7 +6611,7 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
List_iterator <LEX_USER> user_list(list);
TABLE_LIST tables[GRANT_TABLES];
bool some_users_deleted= FALSE;
- ulong old_sql_mode= thd->variables.sql_mode;
+ ulonglong old_sql_mode= thd->variables.sql_mode;
bool save_binlog_row_based;
DBUG_ENTER("mysql_drop_user");
@@ -5948,21 +6620,23 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
row-based replication. The flag will be reset at the end of the
statement.
*/
- save_binlog_row_based= thd->current_stmt_binlog_row_based;
- thd->clear_current_stmt_binlog_row_based();
+ if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
+ thd->clear_current_stmt_binlog_format_row();
/* DROP USER may be skipped on replication client. */
if ((result= open_grant_tables(thd, tables)))
{
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result != 1);
}
thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
- rw_wrlock(&LOCK_grant);
- VOID(pthread_mutex_lock(&acl_cache->lock));
+ mysql_rwlock_wrlock(&LOCK_grant);
+ mysql_mutex_lock(&acl_cache->lock);
while ((tmp_user_name= user_list++))
{
@@ -5983,7 +6657,7 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
/* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
rebuild_check_host();
- VOID(pthread_mutex_unlock(&acl_cache->lock));
+ mysql_mutex_unlock(&acl_cache->lock);
if (result)
my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe());
@@ -5991,11 +6665,12 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
if (some_users_deleted)
result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
- rw_unlock(&LOCK_grant);
- close_thread_tables(thd);
+ mysql_rwlock_unlock(&LOCK_grant);
thd->variables.sql_mode= old_sql_mode;
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result);
}
@@ -6030,19 +6705,21 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
row-based replication. The flag will be reset at the end of the
statement.
*/
- save_binlog_row_based= thd->current_stmt_binlog_row_based;
- thd->clear_current_stmt_binlog_row_based();
+ if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
+ thd->clear_current_stmt_binlog_format_row();
/* RENAME USER may be skipped on replication client. */
if ((result= open_grant_tables(thd, tables)))
{
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result != 1);
}
- rw_wrlock(&LOCK_grant);
- VOID(pthread_mutex_lock(&acl_cache->lock));
+ mysql_rwlock_wrlock(&LOCK_grant);
+ mysql_mutex_lock(&acl_cache->lock);
while ((tmp_user_from= user_list++))
{
@@ -6076,7 +6753,7 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
/* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
rebuild_check_host();
- VOID(pthread_mutex_unlock(&acl_cache->lock));
+ mysql_mutex_unlock(&acl_cache->lock);
if (result)
my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
@@ -6084,10 +6761,11 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
if (some_users_renamed && mysql_bin_log.is_open())
result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
- rw_unlock(&LOCK_grant);
- close_thread_tables(thd);
+ mysql_rwlock_unlock(&LOCK_grant);
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result);
}
@@ -6120,18 +6798,20 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
row-based replication. The flag will be reset at the end of the
statement.
*/
- save_binlog_row_based= thd->current_stmt_binlog_row_based;
- thd->clear_current_stmt_binlog_row_based();
+ if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
+ thd->clear_current_stmt_binlog_format_row();
if ((result= open_grant_tables(thd, tables)))
{
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result != 1);
}
- rw_wrlock(&LOCK_grant);
- VOID(pthread_mutex_lock(&acl_cache->lock));
+ mysql_rwlock_wrlock(&LOCK_grant);
+ mysql_mutex_lock(&acl_cache->lock);
LEX_USER *lex_user, *tmp_lex_user;
List_iterator <LEX_USER> user_list(list);
@@ -6198,8 +6878,8 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; )
{
const char *user,*host;
- GRANT_TABLE *grant_table= (GRANT_TABLE*)hash_element(&column_priv_hash,
- counter);
+ GRANT_TABLE *grant_table=
+ (GRANT_TABLE*) my_hash_element(&column_priv_hash, counter);
if (!(user=grant_table->user))
user= "";
if (!(host=grant_table->host.hostname))
@@ -6245,7 +6925,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
for (counter= 0, revoked= 0 ; counter < hash->records ; )
{
const char *user,*host;
- GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, counter);
+ GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
if (!(user=grant_proc->user))
user= "";
if (!(host=grant_proc->host.hostname))
@@ -6270,7 +6950,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
} while (revoked);
}
- VOID(pthread_mutex_unlock(&acl_cache->lock));
+ mysql_mutex_unlock(&acl_cache->lock);
if (result)
my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));
@@ -6278,11 +6958,11 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
result= result |
write_bin_log(thd, FALSE, thd->query(), thd->query_length());
- rw_unlock(&LOCK_grant);
- close_thread_tables(thd);
-
+ mysql_rwlock_unlock(&LOCK_grant);
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result);
}
@@ -6308,9 +6988,12 @@ public:
virtual ~Silence_routine_definer_errors()
{}
- virtual bool handle_error(uint sql_errno, const char *message,
- MYSQL_ERROR::enum_warning_level level,
- THD *thd);
+ virtual bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl);
bool has_errors() { return is_grave; }
@@ -6319,18 +7002,23 @@ private:
};
bool
-Silence_routine_definer_errors::handle_error(uint sql_errno,
- const char *message,
- MYSQL_ERROR::enum_warning_level level,
- THD *thd)
-{
+Silence_routine_definer_errors::handle_condition(
+ THD *thd,
+ uint sql_errno,
+ const char*,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl)
+{
+ *cond_hdl= NULL;
if (level == MYSQL_ERROR::WARN_LEVEL_ERROR)
{
switch (sql_errno)
{
case ER_NONEXISTING_PROC_GRANT:
/* Convert the error into a warning. */
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, sql_errno, message);
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ sql_errno, msg);
return TRUE;
default:
is_grave= TRUE;
@@ -6375,23 +7063,23 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
/* Be sure to pop this before exiting this scope! */
thd->push_internal_handler(&error_handler);
- rw_wrlock(&LOCK_grant);
- VOID(pthread_mutex_lock(&acl_cache->lock));
+ mysql_rwlock_wrlock(&LOCK_grant);
+ mysql_mutex_lock(&acl_cache->lock);
/*
This statement will be replicated as a statement, even when using
row-based replication. The flag will be reset at the end of the
statement.
*/
- save_binlog_row_based= thd->current_stmt_binlog_row_based;
- thd->clear_current_stmt_binlog_row_based();
+ if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
+ thd->clear_current_stmt_binlog_format_row();
/* Remove procedure access */
do
{
for (counter= 0, revoked= 0 ; counter < hash->records ; )
{
- GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, counter);
+ GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
if (!my_strcasecmp(&my_charset_utf8_bin, grant_proc->db, sp_db) &&
!my_strcasecmp(system_charset_info, grant_proc->tname, sp_name))
{
@@ -6415,13 +7103,14 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
}
} while (revoked);
- VOID(pthread_mutex_unlock(&acl_cache->lock));
- rw_unlock(&LOCK_grant);
- close_thread_tables(thd);
+ mysql_mutex_unlock(&acl_cache->lock);
+ mysql_rwlock_unlock(&LOCK_grant);
thd->pop_internal_handler();
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(error_handler.has_errors());
}
@@ -6458,7 +7147,7 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
combo->user.str= sctx->user;
- VOID(pthread_mutex_lock(&acl_cache->lock));
+ mysql_mutex_lock(&acl_cache->lock);
if ((au= find_acl_user(combo->host.str=(char*)sctx->host_or_ip,combo->user.str,FALSE)))
goto found_acl;
@@ -6469,11 +7158,11 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
if((au= find_acl_user(combo->host.str=(char*)"%", combo->user.str, FALSE)))
goto found_acl;
- VOID(pthread_mutex_unlock(&acl_cache->lock));
+ mysql_mutex_unlock(&acl_cache->lock);
DBUG_RETURN(TRUE);
found_acl:
- VOID(pthread_mutex_unlock(&acl_cache->lock));
+ mysql_mutex_unlock(&acl_cache->lock);
bzero((char*)tables, sizeof(TABLE_LIST));
user_list.empty();
@@ -6551,6 +7240,140 @@ template class List<LEX_COLUMN>;
template class List<LEX_USER>;
#endif
+/**
+ Validate if a user can proxy as another user
+
+ @thd current thread
+ @param user the logged in user (proxy user)
+ @param authenticated_as the effective user a plugin is trying to
+ impersonate as (proxied user)
+ @return proxy user definition
+ @retval NULL proxy user definition not found or not applicable
+ @retval non-null the proxy user data
+*/
+
+static ACL_PROXY_USER *
+acl_find_proxy_user(const char *user, const char *host, const char *ip,
+ const char *authenticated_as, bool *proxy_used)
+{
+ uint i;
+ /* if the proxied and proxy user are the same return OK */
+ DBUG_ENTER("acl_find_proxy_user");
+ DBUG_PRINT("info", ("user=%s host=%s ip=%s authenticated_as=%s",
+ user, host, ip, authenticated_as));
+
+ if (!strcmp(authenticated_as, user))
+ {
+ DBUG_PRINT ("info", ("user is the same as authenticated_as"));
+ DBUG_RETURN (NULL);
+ }
+
+ *proxy_used= TRUE;
+ for (i=0; i < acl_proxy_users.elements; i++)
+ {
+ ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
+ ACL_PROXY_USER *);
+ if (proxy->matches(host, user, ip, authenticated_as))
+ DBUG_RETURN(proxy);
+ }
+
+ DBUG_RETURN(NULL);
+}
+
+
+bool
+acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
+ bool with_grant)
+{
+ DBUG_ENTER("acl_check_proxy_grant_access");
+ DBUG_PRINT("info", ("user=%s host=%s with_grant=%d", user, host,
+ (int) with_grant));
+ if (!initialized)
+ {
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
+ DBUG_RETURN(1);
+ }
+
+ /* replication slave thread can do anything */
+ if (thd->slave_thread)
+ {
+ DBUG_PRINT("info", ("replication slave"));
+ DBUG_RETURN(FALSE);
+ }
+
+ /*
+ one can grant proxy for self to others.
+ Security context in THD contains two pairs of (user,host):
+ 1. (user,host) pair referring to inbound connection.
+ 2. (priv_user,priv_host) pair obtained from mysql.user table after doing
+ authnetication of incoming connection.
+ Privileges should be checked wrt (priv_user, priv_host) tuple, because
+ (user,host) pair obtained from inbound connection may have different
+ values than what is actually stored in mysql.user table and while granting
+ or revoking proxy privilege, user is expected to provide entries mentioned
+ in mysql.user table.
+ */
+ if (!strcmp(thd->security_ctx->priv_user, user) &&
+ !my_strcasecmp(system_charset_info, host,
+ thd->security_ctx->priv_host))
+ {
+ DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal",
+ thd->security_ctx->priv_user, user,
+ host, thd->security_ctx->priv_host));
+ DBUG_RETURN(FALSE);
+ }
+
+ /* check for matching WITH PROXY rights */
+ for (uint i=0; i < acl_proxy_users.elements; i++)
+ {
+ ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
+ ACL_PROXY_USER *);
+ if (proxy->matches(thd->security_ctx->host,
+ thd->security_ctx->user,
+ thd->security_ctx->ip,
+ user) &&
+ proxy->get_with_grant())
+ {
+ DBUG_PRINT("info", ("found"));
+ DBUG_RETURN(FALSE);
+ }
+ }
+
+ my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
+ thd->security_ctx->user,
+ thd->security_ctx->host_or_ip);
+ DBUG_RETURN(TRUE);
+}
+
+
+static bool
+show_proxy_grants(THD *thd, LEX_USER *user, char *buff, size_t buffsize)
+{
+ Protocol *protocol= thd->protocol;
+ int error= 0;
+
+ for (uint i=0; i < acl_proxy_users.elements; i++)
+ {
+ ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
+ ACL_PROXY_USER *);
+ if (proxy->granted_on(user->host.str, user->user.str))
+ {
+ String global(buff, buffsize, system_charset_info);
+ global.length(0);
+ proxy->print_grant(&global);
+ protocol->prepare_for_resend();
+ protocol->store(global.ptr(), global.length(), global.charset());
+ if (protocol->write())
+ {
+ error= -1;
+ break;
+ }
+ }
+ }
+ return error;
+}
+
+
#endif /*NO_EMBEDDED_ACCESS_CHECKS */
@@ -6609,6 +7432,7 @@ static bool update_schema_privilege(THD *thd, TABLE *table, char *buff,
CHARSET_INFO *cs= system_charset_info;
restore_record(table, s->default_values);
table->field[0]->store(buff, (uint) strlen(buff), cs);
+ table->field[1]->store(STRING_WITH_LEN("def"), cs);
if (db)
table->field[i++]->store(db, (uint) strlen(db), cs);
if (t_name)
@@ -6631,13 +7455,14 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
ulong want_access;
char buff[100];
TABLE *table= tables->table;
- bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
+ bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
+ NULL, NULL, 1, 1);
char *curr_host= thd->security_ctx->priv_host_name();
DBUG_ENTER("fill_schema_user_privileges");
if (!initialized)
DBUG_RETURN(0);
- pthread_mutex_lock(&acl_cache->lock);
+ mysql_mutex_lock(&acl_cache->lock);
for (counter=0 ; counter < acl_users.elements ; counter++)
{
@@ -6687,7 +7512,7 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
}
}
err:
- pthread_mutex_unlock(&acl_cache->lock);
+ mysql_mutex_unlock(&acl_cache->lock);
DBUG_RETURN(error);
#else
@@ -6705,13 +7530,14 @@ int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
ulong want_access;
char buff[100];
TABLE *table= tables->table;
- bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
+ bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
+ NULL, NULL, 1, 1);
char *curr_host= thd->security_ctx->priv_host_name();
DBUG_ENTER("fill_schema_schema_privileges");
if (!initialized)
DBUG_RETURN(0);
- pthread_mutex_lock(&acl_cache->lock);
+ mysql_mutex_lock(&acl_cache->lock);
for (counter=0 ; counter < acl_dbs.elements ; counter++)
{
@@ -6764,7 +7590,7 @@ int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
}
}
err:
- pthread_mutex_unlock(&acl_cache->lock);
+ mysql_mutex_unlock(&acl_cache->lock);
DBUG_RETURN(error);
#else
@@ -6780,16 +7606,17 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
uint index;
char buff[100];
TABLE *table= tables->table;
- bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
+ bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
+ NULL, NULL, 1, 1);
char *curr_host= thd->security_ctx->priv_host_name();
DBUG_ENTER("fill_schema_table_privileges");
- rw_rdlock(&LOCK_grant);
+ mysql_rwlock_rdlock(&LOCK_grant);
for (index=0 ; index < column_priv_hash.records ; index++)
{
const char *user, *host, *is_grantable= "YES";
- GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
+ GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
index);
if (!(user=grant_table->user))
user= "";
@@ -6847,7 +7674,7 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
}
}
err:
- rw_unlock(&LOCK_grant);
+ mysql_rwlock_unlock(&LOCK_grant);
DBUG_RETURN(error);
#else
@@ -6863,16 +7690,17 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
uint index;
char buff[100];
TABLE *table= tables->table;
- bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
+ bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
+ NULL, NULL, 1, 1);
char *curr_host= thd->security_ctx->priv_host_name();
DBUG_ENTER("fill_schema_table_privileges");
- rw_rdlock(&LOCK_grant);
+ mysql_rwlock_rdlock(&LOCK_grant);
for (index=0 ; index < column_priv_hash.records ; index++)
{
const char *user, *host, *is_grantable= "YES";
- GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
+ GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
index);
if (!(user=grant_table->user))
user= "";
@@ -6907,7 +7735,7 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
col_index++)
{
GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
- hash_element(&grant_table->hash_columns,col_index);
+ my_hash_element(&grant_table->hash_columns,col_index);
if ((grant_column->rights & j) && (table_access & j))
{
if (update_schema_privilege(thd, table, buff, grant_table->db,
@@ -6928,7 +7756,7 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
}
}
err:
- rw_unlock(&LOCK_grant);
+ mysql_rwlock_unlock(&LOCK_grant);
DBUG_RETURN(error);
#else
@@ -6980,7 +7808,7 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
/* table privileges */
- rw_rdlock(&LOCK_grant);
+ mysql_rwlock_rdlock(&LOCK_grant);
if (grant->version != grant_version)
{
grant->grant_table=
@@ -6993,7 +7821,7 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
{
grant->privilege|= grant->grant_table->privs;
}
- rw_unlock(&LOCK_grant);
+ mysql_rwlock_unlock(&LOCK_grant);
DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
DBUG_VOID_RETURN;
@@ -7013,6 +7841,106 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name,
#endif
+struct ACL_internal_schema_registry_entry
+{
+ const LEX_STRING *m_name;
+ const ACL_internal_schema_access *m_access;
+};
+
+/**
+ Internal schema registered.
+ Currently, this is only:
+ - performance_schema
+ - information_schema,
+ This can be reused later for:
+ - mysql
+*/
+static ACL_internal_schema_registry_entry registry_array[2];
+static uint m_registry_array_size= 0;
+
+/**
+ Add an internal schema to the registry.
+ @param name the schema name
+ @param access the schema ACL specific rules
+*/
+void ACL_internal_schema_registry::register_schema
+ (const LEX_STRING *name, const ACL_internal_schema_access *access)
+{
+ DBUG_ASSERT(m_registry_array_size < array_elements(registry_array));
+
+ /* Not thread safe, and does not need to be. */
+ registry_array[m_registry_array_size].m_name= name;
+ registry_array[m_registry_array_size].m_access= access;
+ m_registry_array_size++;
+}
+
+/**
+ Search per internal schema ACL by name.
+ @param name a schema name
+ @return per schema rules, or NULL
+*/
+const ACL_internal_schema_access *
+ACL_internal_schema_registry::lookup(const char *name)
+{
+ DBUG_ASSERT(name != NULL);
+
+ uint i;
+
+ for (i= 0; i<m_registry_array_size; i++)
+ {
+ if (my_strcasecmp(system_charset_info, registry_array[i].m_name->str,
+ name) == 0)
+ return registry_array[i].m_access;
+ }
+ return NULL;
+}
+
+/**
+ Get a cached internal schema access.
+ @param grant_internal_info the cache
+ @param schema_name the name of the internal schema
+*/
+const ACL_internal_schema_access *
+get_cached_schema_access(GRANT_INTERNAL_INFO *grant_internal_info,
+ const char *schema_name)
+{
+ if (grant_internal_info)
+ {
+ if (! grant_internal_info->m_schema_lookup_done)
+ {
+ grant_internal_info->m_schema_access=
+ ACL_internal_schema_registry::lookup(schema_name);
+ grant_internal_info->m_schema_lookup_done= TRUE;
+ }
+ return grant_internal_info->m_schema_access;
+ }
+ return ACL_internal_schema_registry::lookup(schema_name);
+}
+
+/**
+ Get a cached internal table access.
+ @param grant_internal_info the cache
+ @param schema_name the name of the internal schema
+ @param table_name the name of the internal table
+*/
+const ACL_internal_table_access *
+get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info,
+ const char *schema_name,
+ const char *table_name)
+{
+ DBUG_ASSERT(grant_internal_info);
+ if (! grant_internal_info->m_table_lookup_done)
+ {
+ const ACL_internal_schema_access *schema_access;
+ schema_access= get_cached_schema_access(grant_internal_info, schema_name);
+ if (schema_access)
+ grant_internal_info->m_table_access= schema_access->lookup(table_name);
+ grant_internal_info->m_table_lookup_done= TRUE;
+ }
+ return grant_internal_info->m_table_access;
+}
+
+
/****************************************************************************
AUTHENTICATION CODE
including initial connect handshake, invoking appropriate plugins,
@@ -7032,14 +7960,13 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name,
#ifndef HAVE_OPENSSL
#define ssl_acceptor_fd 0
#define sslaccept(A,B,C,D) 1
-#define NORMAL_HANDSHAKE_SIZE 6
#endif
/**
The internal version of what plugins know as MYSQL_PLUGIN_VIO,
basically the context of the authentication session
*/
-struct MPVIO_EXT : public MYSQL_PLUGIN_VIO
+struct MPVIO_EXT :public MYSQL_PLUGIN_VIO
{
MYSQL_SERVER_AUTH_INFO auth_info;
THD *thd;
@@ -7063,18 +7990,17 @@ struct MPVIO_EXT : public MYSQL_PLUGIN_VIO
enum { SUCCESS, FAILURE, RESTART } status;
};
-
/**
a helper function to report an access denied error in all the proper places
*/
-
static void login_failed_error(THD *thd)
{
- my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
+ my_error(access_denied_error_code(thd->password), MYF(0),
thd->main_security_ctx.user,
thd->main_security_ctx.host_or_ip,
thd->password ? ER(ER_YES) : ER(ER_NO));
- general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
+ general_log_print(thd, COM_CONNECT,
+ ER(access_denied_error_code(thd->password)),
thd->main_security_ctx.user,
thd->main_security_ctx.host_or_ip,
thd->password ? ER(ER_YES) : ER(ER_NO));
@@ -7086,14 +8012,13 @@ static void login_failed_error(THD *thd)
*/
if (global_system_variables.log_warnings > 1)
{
- sql_print_warning(ER(ER_ACCESS_DENIED_ERROR),
+ sql_print_warning(ER(access_denied_error_code(thd->password)),
thd->main_security_ctx.user,
thd->main_security_ctx.host_or_ip,
thd->password ? ER(ER_YES) : ER(ER_NO));
}
}
-
/**
sends a server handshake initialization packet, the very first packet
after the connection was established
@@ -7119,7 +8044,6 @@ static void login_failed_error(THD *thd)
@retval 0 ok
@retval 1 error
*/
-
static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
const char *data, uint data_len)
{
@@ -7127,14 +8051,26 @@ static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
DBUG_ASSERT(data_len <= 255);
THD *thd= mpvio->thd;
- char *buff= (char *)my_alloca(1 + SERVER_VERSION_LENGTH + 1 + data_len + 64);
+ char *buff= (char *) my_alloca(1 + SERVER_VERSION_LENGTH + 1 + data_len + 64);
char scramble_buf[SCRAMBLE_LENGTH];
char *end= buff;
+ DBUG_ENTER("send_server_handshake_packet");
*end++= protocol_version;
thd->client_capabilities= CLIENT_BASIC_FLAGS;
+ if (opt_using_transactions)
+ thd->client_capabilities|= CLIENT_TRANSACTIONS;
+
+ thd->client_capabilities|= CAN_CLIENT_COMPRESS;
+
+ if (ssl_acceptor_fd)
+ {
+ thd->client_capabilities |= CLIENT_SSL;
+ thd->client_capabilities |= CLIENT_SSL_VERIFY_SERVER_CERT;
+ }
+
if (data_len)
{
mpvio->cached_server_packet.pkt= (char*)thd->memdup(data, data_len);
@@ -7144,12 +8080,13 @@ static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
if (data_len < SCRAMBLE_LENGTH)
{
if (data_len)
- { /*
+ {
+ /*
the first packet *must* have at least 20 bytes of a scramble.
if a plugin provided less, we pad it to 20 with zeros
*/
memcpy(scramble_buf, data, data_len);
- bzero(scramble_buf+data_len, SCRAMBLE_LENGTH-data_len);
+ bzero(scramble_buf + data_len, SCRAMBLE_LENGTH - data_len);
data= scramble_buf;
}
else
@@ -7169,17 +8106,6 @@ static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
data_len= SCRAMBLE_LENGTH;
}
- if (opt_using_transactions)
- thd->client_capabilities|= CLIENT_TRANSACTIONS;
-
- thd->client_capabilities|= CAN_CLIENT_COMPRESS;
-
- if (ssl_acceptor_fd)
- {
- thd->client_capabilities |= CLIENT_SSL;
- thd->client_capabilities |= CLIENT_SSL_VERIFY_SERVER_CERT;
- }
-
end= strnmov(end, server_version, SERVER_VERSION_LENGTH) + 1;
int4store((uchar*) end, mpvio->thd->thread_id);
end+= 4;
@@ -7189,29 +8115,31 @@ static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
tail: that's why first part of the scramble is placed here, and second
part at the end of packet.
*/
- end= (char*)memcpy(end, data, SCRAMBLE_LENGTH_323);
+ end= (char*) memcpy(end, data, SCRAMBLE_LENGTH_323);
end+= SCRAMBLE_LENGTH_323;
*end++= 0;
int2store(end, thd->client_capabilities);
/* write server characteristics: up to 16 bytes allowed */
- end[2]=(char) default_charset_info->number;
+ end[2]= (char) default_charset_info->number;
int2store(end+3, mpvio->thd->server_status);
int2store(end+5, thd->client_capabilities >> 16);
end[7]= data_len;
- bzero(end+8, 10);
+ DBUG_EXECUTE_IF("poison_srv_handshake_scramble_len", end[7]= -100;);
+ bzero(end + 8, 10);
end+= 18;
/* write scramble tail */
- end= (char*)memcpy(end, data + SCRAMBLE_LENGTH_323,
- data_len - SCRAMBLE_LENGTH_323);
+ end= (char*) memcpy(end, data + SCRAMBLE_LENGTH_323,
+ data_len - SCRAMBLE_LENGTH_323);
end+= data_len - SCRAMBLE_LENGTH_323;
end= strmake(end, plugin_name(mpvio->plugin)->str,
plugin_name(mpvio->plugin)->length);
- int res= my_net_write(&mpvio->thd->net, (uchar*) buff, (size_t) (end-buff)) ||
+ int res= my_net_write(&mpvio->thd->net, (uchar*) buff,
+ (size_t) (end - buff + 1)) ||
net_flush(&mpvio->thd->net);
my_afree(buff);
- return res;
+ DBUG_RETURN (res);
}
static bool secure_auth(THD *thd)
@@ -7261,7 +8189,6 @@ static bool secure_auth(THD *thd)
@retval 0 ok
@retval 1 error
*/
-
static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
const uchar *data, uint data_len)
{
@@ -7269,16 +8196,14 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
DBUG_ASSERT(mpvio->packets_read == 1);
NET *net= &mpvio->thd->net;
static uchar switch_plugin_request_buf[]= { 254 };
-
+ DBUG_ENTER("send_plugin_request_packet");
mpvio->status= MPVIO_EXT::FAILURE; // the status is no longer RESTART
const char *client_auth_plugin=
- ((st_mysql_auth *)(plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
+ ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
DBUG_ASSERT(client_auth_plugin);
- DBUG_ASSERT(my_strcasecmp(system_charset_info, client_auth_plugin,
- mpvio->cached_client_reply.plugin));
/*
we send an old "short 4.0 scramble request", if we need to request a
@@ -7294,9 +8219,9 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
client_auth_plugin == old_password_plugin_name.str;
if (switch_from_long_to_short_scramble)
- return secure_auth(mpvio->thd) ||
- my_net_write(net, switch_plugin_request_buf, 1) ||
- net_flush(net);
+ DBUG_RETURN (secure_auth(mpvio->thd) ||
+ my_net_write(net, switch_plugin_request_buf, 1) ||
+ net_flush(net));
/*
We never request a client to switch from a short to long scramble.
@@ -7311,16 +8236,17 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
{
my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
general_log_print(mpvio->thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
- return 1;
+ DBUG_RETURN (1);
}
- return net_write_command(net, switch_plugin_request_buf[0],
- (uchar*)client_auth_plugin,
- strlen(client_auth_plugin)+1,
- (uchar*)data, data_len);
+ DBUG_PRINT("info", ("requesting client to use the %s plugin",
+ client_auth_plugin));
+ DBUG_RETURN(net_write_command(net, switch_plugin_request_buf[0],
+ (uchar*) client_auth_plugin,
+ strlen(client_auth_plugin) + 1,
+ (uchar*) data, data_len));
}
-
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/**
Finds acl entry in user database for authentication purposes.
@@ -7334,15 +8260,16 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
@retval 0 found
@retval 1 error
*/
-
-static bool find_mpvio_user(MPVIO_EXT *mpvio, Security_context *sctx)
+static bool find_mpvio_user(MPVIO_EXT *mpvio)
{
+ Security_context *sctx= mpvio->thd->security_ctx;
+ DBUG_ENTER("find_mpvio_user");
DBUG_ASSERT(mpvio->acl_user == 0);
- pthread_mutex_lock(&acl_cache->lock);
- for (uint i=0 ; i < acl_users.elements ; i++)
+ mysql_mutex_lock(&acl_cache->lock);
+ for (uint i=0; i < acl_users.elements; i++)
{
- ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*);
+ ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
if ((!acl_user_tmp->user || !strcmp(sctx->user, acl_user_tmp->user)) &&
compare_hostname(&acl_user_tmp->host, sctx->host, sctx->ip))
{
@@ -7350,7 +8277,7 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio, Security_context *sctx)
break;
}
}
- pthread_mutex_unlock(&acl_cache->lock);
+ mysql_mutex_unlock(&acl_cache->lock);
if (!mpvio->acl_user)
{
@@ -7368,17 +8295,17 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio, Security_context *sctx)
CHARSET_INFO *cs= &my_charset_latin1;
cs->coll->hash_sort(cs, (uchar*) sctx->user, strlen(sctx->user), &nr1, &nr2);
- pthread_mutex_lock(&acl_cache->lock);
+ mysql_mutex_lock(&acl_cache->lock);
if (!acl_users.elements)
{
- pthread_mutex_unlock(&acl_cache->lock);
+ mysql_mutex_unlock(&acl_cache->lock);
login_failed_error(mpvio->thd);
- return 1;
+ DBUG_RETURN(1);
}
uint i= nr1 % acl_users.elements;
ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
mpvio->acl_user= acl_user_tmp->copy(mpvio->thd->mem_root);
- pthread_mutex_unlock(&acl_cache->lock);
+ mysql_mutex_unlock(&acl_cache->lock);
mpvio->make_it_fail= true;
}
@@ -7394,19 +8321,26 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio, Security_context *sctx)
old_password_plugin_name.str));
my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
general_log_print(mpvio->thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
- return 1;
+ DBUG_RETURN (1);
}
mpvio->auth_info.user_name= sctx->user;
+ mpvio->auth_info.user_name_length= strlen(sctx->user);
mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str;
- strmake(mpvio->auth_info.authenticated_as, mpvio->acl_user->user ?
- mpvio->acl_user->user : "", USERNAME_LENGTH);
-
- return 0;
+ mpvio->auth_info.auth_string_length= (unsigned long) mpvio->acl_user->auth_string.length;
+ strmake_buf(mpvio->auth_info.authenticated_as, mpvio->acl_user->user ?
+ mpvio->acl_user->user : "");
+
+ DBUG_PRINT("info", ("exit: user=%s, auth_string=%s, authenticated as=%s"
+ "plugin=%s",
+ mpvio->auth_info.user_name,
+ mpvio->auth_info.auth_string,
+ mpvio->auth_info.authenticated_as,
+ mpvio->acl_user->plugin.str));
+ DBUG_RETURN(0);
}
#endif
-
/* the packet format is described in send_change_user_packet() */
static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
{
@@ -7417,17 +8351,18 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
char *user= (char*) net->read_pos;
char *end= user + packet_length;
/* Safe because there is always a trailing \0 at the end of the packet */
- char *passwd= strend(user)+1;
+ char *passwd= strend(user) + 1;
uint user_len= passwd - user - 1;
char *db= passwd;
char db_buff[SAFE_NAME_LEN + 1]; // buffer to store db in utf8
char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
uint dummy_errors;
+ DBUG_ENTER ("parse_com_change_user_packet");
if (passwd >= end)
{
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
- return 1;
+ DBUG_RETURN (1);
}
/*
@@ -7441,7 +8376,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++) : strlen(passwd));
db+= passwd_len + 1;
/*
@@ -7451,38 +8386,37 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
if (db >= end)
{
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
- return 1;
+ DBUG_RETURN (1);
}
uint db_len= strlen(db);
char *ptr= db + db_len + 1;
- if (ptr+1 < end)
+ if (ptr + 1 < end)
{
- uint cs_number= uint2korr(ptr);
- if (thd_init_client_charset(thd, cs_number))
- return 1;
+ if (thd_init_client_charset(thd, uint2korr(ptr)))
+ DBUG_RETURN(1);
thd->update_charset();
}
/* Convert database and user names to utf8 */
- db_len= copy_and_convert(db_buff, sizeof(db_buff)-1, system_charset_info,
+ db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
db, db_len, thd->charset(), &dummy_errors);
- user_len= copy_and_convert(user_buff, sizeof(user_buff)-1,
+ user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
system_charset_info, user, user_len,
thd->charset(), &dummy_errors);
if (!(sctx->user= my_strndup(user_buff, user_len, MYF(MY_WME))))
- return 1;
+ DBUG_RETURN(1);
/* Clear variables that are allocated */
thd->user_connect= 0;
- strmake(sctx->priv_user, sctx->user, USERNAME_LENGTH);
+ strmake_buf(sctx->priv_user, sctx->user);
if (thd->make_lex_string(&mpvio->db, db_buff, db_len, 0) == 0)
- return 1; /* The error is set by make_lex_string(). */
+ DBUG_RETURN(1); /* The error is set by make_lex_string(). */
/*
Clear thd->db as it points to something, that will be freed when
@@ -7495,13 +8429,13 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
{
// if mysqld's been started with --skip-grant-tables option
mpvio->status= MPVIO_EXT::SUCCESS;
- return 0;
+ DBUG_RETURN(0);
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
thd->password= passwd_len > 0;
- if (find_mpvio_user(mpvio, sctx))
- return 1;
+ if (find_mpvio_user(mpvio))
+ DBUG_RETURN(1);
char *client_plugin;
if (thd->client_capabilities & CLIENT_PLUGIN_AUTH)
@@ -7510,7 +8444,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
if (client_plugin >= end)
{
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
- return 1;
+ DBUG_RETURN(1);
}
client_plugin= fix_plugin_ptr(client_plugin);
}
@@ -7531,15 +8465,19 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
mpvio->acl_user->plugin= old_password_plugin_name;
}
}
-
- /* remember the data part of the packet, to present it to plugin in read_packet() */
+
+ DBUG_PRINT("info", ("client_plugin=%s, restart", client_plugin));
+ /*
+ Remember the data part of the packet, to present it to plugin in
+ read_packet()
+ */
mpvio->cached_client_reply.pkt= passwd;
mpvio->cached_client_reply.pkt_len= passwd_len;
mpvio->cached_client_reply.plugin= client_plugin;
mpvio->status= MPVIO_EXT::RESTART;
#endif
- return 0;
+ DBUG_RETURN (0);
}
@@ -7563,7 +8501,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
DBUG_ASSERT(net->read_pos[pkt_len] == 0);
if (mpvio->connect_errors)
- reset_host_errors(&net->vio->remote.sin_addr);
+ reset_host_errors(thd->main_security_ctx.ip);
ulong client_capabilities= uint2korr(net->read_pos);
if (client_capabilities & CLIENT_PROTOCOL_41)
@@ -7576,20 +8514,17 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
/* Disable those bits which are not supported by the client. */
thd->client_capabilities&= client_capabilities;
- if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
- thd->variables.sql_mode|= MODE_IGNORE_SPACE;
-
DBUG_PRINT("info", ("client capabilities: %lu", thd->client_capabilities));
if (thd->client_capabilities & CLIENT_SSL)
{
- char error_string[1024] __attribute__((unused));
+ unsigned long errptr __attribute__((unused));
/* Do the SSL layering. */
if (!ssl_acceptor_fd)
return packet_error;
DBUG_PRINT("info", ("IO layer change in progress..."));
- if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout, error_string))
+ if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout, &errptr))
{
DBUG_PRINT("error", ("Failed to accept new SSL connection"));
return packet_error;
@@ -7627,8 +8562,14 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
if (end >= (char*) net->read_pos+ pkt_len +2)
return packet_error;
+ if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
+ thd->variables.sql_mode|= MODE_IGNORE_SPACE;
if (thd->client_capabilities & CLIENT_INTERACTIVE)
thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
+
+ if (end >= (char*) net->read_pos+ pkt_len +2)
+ return packet_error;
+
if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
opt_using_transactions)
net->return_status= &thd->server_status;
@@ -7668,12 +8609,12 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
/* Since 4.1 all database names are stored in utf8 */
if (db)
{
- db_len= copy_and_convert(db_buff, sizeof(db_buff)-1, system_charset_info,
+ db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
db, db_len, thd->charset(), &dummy_errors);
db= db_buff;
}
- user_len= copy_and_convert(user_buff, sizeof(user_buff)-1,
+ user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
system_charset_info, user, user_len,
thd->charset(), &dummy_errors);
user= user_buff;
@@ -7687,14 +8628,15 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
/*
Clip username to allowed length in characters (not bytes). This is
- mostly for backward compatibility.
+ mostly for backward compatibility (to truncate long usernames, as
+ old 5.1 did)
*/
{
CHARSET_INFO *cs= system_charset_info;
int err;
user_len= (uint) cs->cset->well_formed_len(cs, user, user + user_len,
- USERNAME_CHAR_LENGTH, &err);
+ username_char_length, &err);
user[user_len]= '\0';
}
@@ -7702,11 +8644,11 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
if (thd->make_lex_string(&mpvio->db, db, db_len, 0) == 0)
return packet_error; /* The error is set by make_lex_string(). */
- if (sctx->user)
- x_free(sctx->user);
+ my_free(sctx->user);
if (!(sctx->user= my_strndup(user, user_len, MYF(MY_WME))))
return packet_error; /* The error is set by my_strdup(). */
+
/*
Clear thd->db as it points to something, that will be freed when
connection is closed. We don't want to accidentally free a wrong
@@ -7722,10 +8664,8 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
}
thd->password= passwd_len > 0;
- if (find_mpvio_user(mpvio, sctx))
- {
+ if (find_mpvio_user(mpvio))
return packet_error;
- }
if ((thd->client_capabilities & CLIENT_PLUGIN_AUTH) &&
(client_plugin < (char *)net->read_pos + pkt_len))
@@ -7779,14 +8719,14 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
doesn't need to know.
*/
const char *client_auth_plugin=
- ((st_mysql_auth *)(plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
+ ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
if (client_auth_plugin &&
my_strcasecmp(system_charset_info, client_plugin, client_auth_plugin))
{
mpvio->cached_client_reply.plugin= client_plugin;
if (send_plugin_request_packet(mpvio,
- (uchar*)mpvio->cached_server_packet.pkt,
+ (uchar*) mpvio->cached_server_packet.pkt,
mpvio->cached_server_packet.pkt_len))
return packet_error;
@@ -7794,7 +8734,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
passwd= (char*)thd->net.read_pos;
}
- *buff= (uchar*)passwd;
+ *buff= (uchar*) passwd;
return passwd_len;
#else
return 0;
@@ -7812,18 +8752,19 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
and handles plugin negotiation with the client. If necessary,
it escapes the plugin data, if it starts with a mysql protocol packet byte.
*/
-
static int server_mpvio_write_packet(MYSQL_PLUGIN_VIO *param,
const uchar *packet, int packet_len)
{
- MPVIO_EXT *mpvio= (MPVIO_EXT*)param;
+ MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
int res;
+ DBUG_ENTER("server_mpvio_write_packet");
/* reset cached_client_reply */
mpvio->cached_client_reply.pkt= 0;
+
/* for the 1st packet we wrap plugin data into the handshake packet */
if (mpvio->packets_written == 0)
- res= send_server_handshake_packet(mpvio, (char*)packet, packet_len);
+ res= send_server_handshake_packet(mpvio, (char*) packet, packet_len);
else if (mpvio->status == MPVIO_EXT::RESTART)
res= send_plugin_request_packet(mpvio, packet, packet_len);
else if (packet_len > 0 && (*packet == 1 || *packet == 255 || *packet == 254))
@@ -7843,10 +8784,9 @@ static int server_mpvio_write_packet(MYSQL_PLUGIN_VIO *param,
net_flush(&mpvio->thd->net);
}
mpvio->packets_written++;
- return res;
+ DBUG_RETURN(res);
}
-
/**
vio->read_packet() callback method for server authentication plugins
@@ -7857,12 +8797,11 @@ static int server_mpvio_write_packet(MYSQL_PLUGIN_VIO *param,
a client authentication handshake packet, and handles plugin negotiation
with the client, if necessary.
*/
-
static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
{
- MPVIO_EXT *mpvio= (MPVIO_EXT*)param;
+ MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
ulong pkt_len;
-
+ DBUG_ENTER("server_mpvio_read_packet");
if (mpvio->packets_written == 0)
{
/*
@@ -7874,8 +8813,7 @@ static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
else
pkt_len= my_net_read(&mpvio->thd->net);
}
- else
- if (mpvio->cached_client_reply.pkt)
+ else if (mpvio->cached_client_reply.pkt)
{
DBUG_ASSERT(mpvio->status == MPVIO_EXT::RESTART);
DBUG_ASSERT(mpvio->packets_read > 0);
@@ -7886,21 +8824,22 @@ static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
cached data straight away and avoid one round trip.
*/
const char *client_auth_plugin=
- ((st_mysql_auth *)(plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
+ ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
if (client_auth_plugin == 0 ||
my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
client_auth_plugin) == 0)
{
mpvio->status= MPVIO_EXT::FAILURE;
- *buf= (uchar*)mpvio->cached_client_reply.pkt;
+ *buf= (uchar*) mpvio->cached_client_reply.pkt;
mpvio->cached_client_reply.pkt= 0;
mpvio->packets_read++;
if (mpvio->make_it_fail)
goto err;
- return (int)mpvio->cached_client_reply.pkt_len;
+ DBUG_RETURN ((int) mpvio->cached_client_reply.pkt_len);
}
+
/*
But if the client has used the wrong plugin, the cached data are
useless. Furthermore, we have to send a "change plugin" request
@@ -7930,44 +8869,44 @@ static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
goto err;
}
else
- *buf = mpvio->thd->net.read_pos;
+ *buf= mpvio->thd->net.read_pos;
if (mpvio->make_it_fail)
goto err;
- return (int)pkt_len;
+ DBUG_RETURN((int)pkt_len);
err:
- if (mpvio->status == MPVIO_EXT::FAILURE && !mpvio->thd->is_error())
+ if (mpvio->status == MPVIO_EXT::FAILURE)
{
- inc_host_errors(&mpvio->thd->net.vio->remote.sin_addr);
- if (mpvio->make_it_fail)
- login_failed_error(mpvio->thd);
- else
- my_error(ER_HANDSHAKE_ERROR, MYF(0));
+ inc_host_errors(mpvio->thd->security_ctx->ip);
+ if (!mpvio->thd->is_error())
+ {
+ if (mpvio->make_it_fail)
+ login_failed_error(mpvio->thd);
+ else
+ my_error(ER_HANDSHAKE_ERROR, MYF(0));
+ }
}
- return -1;
+ DBUG_RETURN(-1);
}
-
/**
fills MYSQL_PLUGIN_VIO_INFO structure with the information about the
connection
*/
-
static void server_mpvio_info(MYSQL_PLUGIN_VIO *vio,
MYSQL_PLUGIN_VIO_INFO *info)
{
- MPVIO_EXT *mpvio= (MPVIO_EXT*)vio;
+ MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
mpvio_info(mpvio->thd->net.vio, info);
}
-
-static bool acl_check_ssl(THD *thd, ACL_USER *acl_user)
+static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
{
-#if defined(HAVE_OPENSSL)
- Vio *vio=thd->net.vio;
- SSL *ssl= (SSL*) vio->ssl_arg;
+#ifdef HAVE_OPENSSL
+ Vio *vio= thd->net.vio;
+ SSL *ssl= (SSL *) vio->ssl_arg;
X509 *cert;
#endif
@@ -7981,7 +8920,7 @@ static bool acl_check_ssl(THD *thd, ACL_USER *acl_user)
case SSL_TYPE_NOT_SPECIFIED: // Impossible
case SSL_TYPE_NONE: // SSL is not required
return 0;
-#if defined(HAVE_OPENSSL)
+#ifdef HAVE_OPENSSL
case SSL_TYPE_ANY: // Any kind of SSL is ok
return vio_type(vio) != VIO_TYPE_SSL;
case SSL_TYPE_X509: /* Client should have any valid certificate. */
@@ -8007,9 +8946,9 @@ static bool acl_check_ssl(THD *thd, ACL_USER *acl_user)
return 1;
if (acl_user->ssl_cipher)
{
- DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'",
- acl_user->ssl_cipher,SSL_get_cipher(ssl)));
- if (strcmp(acl_user->ssl_cipher,SSL_get_cipher(ssl)))
+ DBUG_PRINT("info", ("comparing ciphers: '%s' and '%s'",
+ acl_user->ssl_cipher, SSL_get_cipher(ssl)));
+ if (strcmp(acl_user->ssl_cipher, SSL_get_cipher(ssl)))
{
if (global_system_variables.log_warnings)
sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'",
@@ -8023,8 +8962,8 @@ static bool acl_check_ssl(THD *thd, ACL_USER *acl_user)
/* If X509 issuer is specified, we check it... */
if (acl_user->x509_issuer)
{
- char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
- DBUG_PRINT("info",("comparing issuers: '%s' and '%s'",
+ char *ptr= X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
+ DBUG_PRINT("info", ("comparing issuers: '%s' and '%s'",
acl_user->x509_issuer, ptr));
if (strcmp(acl_user->x509_issuer, ptr))
{
@@ -8041,9 +8980,9 @@ static bool acl_check_ssl(THD *thd, ACL_USER *acl_user)
if (acl_user->x509_subject)
{
char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
- DBUG_PRINT("info",("comparing subjects: '%s' and '%s'",
+ DBUG_PRINT("info", ("comparing subjects: '%s' and '%s'",
acl_user->x509_subject, ptr));
- if (strcmp(acl_user->x509_subject,ptr))
+ if (strcmp(acl_user->x509_subject, ptr))
{
if (global_system_variables.log_warnings)
sql_print_information("X509 subject mismatch: should be '%s' but is '%s'",
@@ -8068,18 +9007,17 @@ static bool acl_check_ssl(THD *thd, ACL_USER *acl_user)
return 1;
}
-static int do_auth_once(THD *thd, LEX_STRING *auth_plugin_name,
+
+static int do_auth_once(THD *thd, const LEX_STRING *auth_plugin_name,
MPVIO_EXT *mpvio)
{
int res= CR_OK, old_status= MPVIO_EXT::FAILURE;
bool unlock_plugin= false;
- plugin_ref plugin;
+ plugin_ref plugin= NULL;
-#ifdef EMBEDDED_LIBRARY
- plugin= native_password_plugin;
-#else
if (auth_plugin_name->str == native_password_plugin_name.str)
plugin= native_password_plugin;
+#ifndef EMBEDDED_LIBRARY
else if (auth_plugin_name->str == old_password_plugin_name.str)
plugin= old_password_plugin;
else if ((plugin= my_plugin_lock_by_name(thd, auth_plugin_name,
@@ -8092,8 +9030,21 @@ static int do_auth_once(THD *thd, LEX_STRING *auth_plugin_name,
if (plugin)
{
- st_mysql_auth *auth= (st_mysql_auth*)plugin_decl(plugin)->info;
- res= auth->authenticate_user(mpvio, &mpvio->auth_info);
+ st_mysql_auth *auth= (st_mysql_auth *) plugin_decl(plugin)->info;
+ switch (auth->interface_version) {
+ case 0x0200:
+ res= auth->authenticate_user(mpvio, &mpvio->auth_info);
+ break;
+ case 0x0100:
+ {
+ MYSQL_SERVER_AUTH_INFO_0x0100 compat;
+ compat.downgrade(&mpvio->auth_info);
+ res= auth->authenticate_user(mpvio, (MYSQL_SERVER_AUTH_INFO *)&compat);
+ compat.upgrade(&mpvio->auth_info);
+ }
+ break;
+ default: DBUG_ASSERT(0);
+ }
if (unlock_plugin)
plugin_unlock(thd, plugin);
@@ -8119,6 +9070,7 @@ static int do_auth_once(THD *thd, LEX_STRING *auth_plugin_name,
return res;
}
+
/**
Perform the handshake, authorize the client and update thd sctx variables.
@@ -8133,19 +9085,16 @@ static int do_auth_once(THD *thd, LEX_STRING *auth_plugin_name,
@retval 0 success, thd is updated.
@retval 1 error
*/
-
bool acl_authenticate(THD *thd, uint connect_errors,
uint com_change_user_pkt_len)
{
int res= CR_OK;
MPVIO_EXT mpvio;
- LEX_STRING *auth_plugin_name= default_auth_plugin_name;
+ const LEX_STRING *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");
- compile_time_assert(MYSQL_USERNAME_LENGTH == USERNAME_LENGTH);
-
bzero(&mpvio, sizeof(mpvio));
mpvio.read_packet= server_mpvio_read_packet;
mpvio.write_packet= server_mpvio_write_packet;
@@ -8154,6 +9103,11 @@ bool acl_authenticate(THD *thd, uint connect_errors,
mpvio.connect_errors= connect_errors;
mpvio.status= MPVIO_EXT::FAILURE;
mpvio.make_it_fail= false;
+ mpvio.auth_info.host_or_ip= thd->security_ctx->host_or_ip;
+ mpvio.auth_info.host_or_ip_length=
+ (unsigned int) strlen(thd->security_ctx->host_or_ip);
+
+ DBUG_PRINT("info", ("com_change_user_pkt_len=%u", com_change_user_pkt_len));
if (command == COM_CHANGE_USER)
{
@@ -8177,7 +9131,8 @@ bool acl_authenticate(THD *thd, uint connect_errors,
with a user name, and performs the authentication if everyone has used
the correct plugin.
*/
- res= do_auth_once(thd, auth_plugin_name, &mpvio);
+
+ res= do_auth_once(thd, auth_plugin_name, &mpvio);
}
/*
@@ -8200,7 +9155,7 @@ bool acl_authenticate(THD *thd, uint connect_errors,
}
Security_context *sctx= thd->security_ctx;
- ACL_USER *acl_user= mpvio.acl_user;
+ const ACL_USER *acl_user= mpvio.acl_user;
thd->password= mpvio.auth_info.password_used; // remember for error messages
@@ -8234,12 +9189,59 @@ bool acl_authenticate(THD *thd, uint connect_errors,
DBUG_RETURN(1);
}
+ sctx->proxy_user[0]= 0;
+
if (initialized) // if not --skip-grant-tables
{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ bool is_proxy_user= FALSE;
+ const char *auth_user = acl_user->user ? acl_user->user : "";
+ ACL_PROXY_USER *proxy_user;
+ /* check if the user is allowed to proxy as another user */
+ proxy_user= acl_find_proxy_user(auth_user, sctx->host, sctx->ip,
+ mpvio.auth_info.authenticated_as,
+ &is_proxy_user);
+ if (is_proxy_user)
+ {
+ ACL_USER *acl_proxy_user;
+
+ /* we need to find the proxy user, but there was none */
+ if (!proxy_user)
+ {
+ if (!thd->is_error())
+ login_failed_error(thd);
+ DBUG_RETURN(1);
+ }
+
+ my_snprintf(sctx->proxy_user, sizeof(sctx->proxy_user) - 1,
+ "'%s'@'%s'", auth_user,
+ acl_user->host.hostname ? acl_user->host.hostname : "");
+
+ /* we're proxying : find the proxy user definition */
+ mysql_mutex_lock(&acl_cache->lock);
+ acl_proxy_user= find_acl_user(proxy_user->get_proxied_host() ?
+ proxy_user->get_proxied_host() : "",
+ mpvio.auth_info.authenticated_as, TRUE);
+ if (!acl_proxy_user)
+ {
+ mysql_mutex_unlock(&acl_cache->lock);
+ if (!thd->is_error())
+ login_failed_error(thd);
+ DBUG_RETURN(1);
+ }
+ acl_user= acl_proxy_user->copy(thd->mem_root);
+ mysql_mutex_unlock(&acl_cache->lock);
+ }
+#endif
+
sctx->master_access= acl_user->access;
- strmake(sctx->priv_user, mpvio.auth_info.authenticated_as, USERNAME_LENGTH);
+ if (acl_user->user)
+ strmake_buf(sctx->priv_user, acl_user->user);
+ else
+ *sctx->priv_user= 0;
+
if (acl_user->host.hostname)
- strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME);
+ strmake_buf(sctx->priv_host, acl_user->host.hostname);
else
*sctx->priv_host= 0;
@@ -8297,10 +9299,10 @@ bool acl_authenticate(THD *thd, uint connect_errors,
if (command == COM_CONNECT &&
!(thd->main_security_ctx.master_access & SUPER_ACL))
{
- pthread_mutex_lock(&LOCK_connection_count);
+ mysql_mutex_lock(&LOCK_connection_count);
bool count_ok= (*thd->scheduler->connection_count <=
*thd->scheduler->max_connections);
- VOID(pthread_mutex_unlock(&LOCK_connection_count));
+ mysql_mutex_unlock(&LOCK_connection_count);
if (!count_ok)
{ // too many connections
my_error(ER_CON_COUNT_ERROR, MYF(0));
@@ -8328,8 +9330,11 @@ bool acl_authenticate(THD *thd, uint connect_errors,
thd->net.net_skip_rest_factor= 2; // skip at most 2*max_packet_size
+ if (mpvio.auth_info.external_user[0])
+ sctx->external_user= my_strdup(mpvio.auth_info.external_user, MYF(0));
+
if (res == CR_OK_HANDSHAKE_COMPLETE)
- thd->main_da.disable_status();
+ thd->stmt_da->disable_status();
else
my_ok(thd);
@@ -8337,7 +9342,6 @@ bool acl_authenticate(THD *thd, uint connect_errors,
DBUG_RETURN(0);
}
-
/**
MySQL Server Password Authentication Plugin
@@ -8346,14 +9350,14 @@ bool acl_authenticate(THD *thd, uint connect_errors,
2. client sends the encrypted password back to the server
3. the server checks the password.
*/
-
-static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
+static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
MYSQL_SERVER_AUTH_INFO *info)
{
uchar *pkt;
int pkt_len;
- MPVIO_EXT *mpvio=(MPVIO_EXT*)vio;
+ MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
THD *thd=mpvio->thd;
+ DBUG_ENTER("native_password_authenticate");
/* generate the scramble, or reuse the old one */
if (thd->scramble[SCRAMBLE_LENGTH])
@@ -8361,7 +9365,7 @@ static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
/* and send it to the client */
if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
- return CR_ERROR;
+ DBUG_RETURN(CR_ERROR);
}
/* reply and authenticate */
@@ -8403,33 +9407,39 @@ static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
/* read the reply with the encrypted password */
if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
- return CR_ERROR;
+ DBUG_RETURN(CR_ERROR);
+ DBUG_PRINT("info", ("reply read : pkt_len=%d", pkt_len));
#ifdef NO_EMBEDDED_ACCESS_CHECKS
- return CR_OK;
+ DBUG_RETURN(CR_OK);
#endif
if (pkt_len == 0) /* no password */
- return info->auth_string[0] ? CR_ERROR : CR_OK;
+ DBUG_RETURN(info->auth_string[0] ? CR_ERROR : CR_OK);
- info->password_used = 1;
+ info->password_used= PASSWORD_USED_YES;
if (pkt_len == SCRAMBLE_LENGTH)
- return info->auth_string[0] == 0 ||
- check_scramble(pkt, thd->scramble, mpvio->acl_user->salt) ?
- CR_ERROR : CR_OK;
+ {
+ if (!mpvio->acl_user->salt_len)
+ DBUG_RETURN(CR_ERROR);
+
+ if (check_scramble(pkt, thd->scramble, mpvio->acl_user->salt))
+ DBUG_RETURN(CR_ERROR);
+ else
+ DBUG_RETURN(CR_OK);
+ }
- inc_host_errors(&mpvio->thd->net.vio->remote.sin_addr);
+ inc_host_errors(mpvio->thd->security_ctx->ip);
my_error(ER_HANDSHAKE_ERROR, MYF(0));
- return CR_ERROR;
+ DBUG_RETURN(CR_ERROR);
}
-
static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
MYSQL_SERVER_AUTH_INFO *info)
{
uchar *pkt;
int pkt_len;
- MPVIO_EXT *mpvio=(MPVIO_EXT*)vio;
+ MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
THD *thd=mpvio->thd;
/* generate the scramble, or reuse the old one */
@@ -8454,7 +9464,7 @@ static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
the password is sent \0-terminated, the pkt_len is always 9 bytes.
We need to figure out the correct scramble length here.
*/
- if (pkt_len == SCRAMBLE_LENGTH_323+1)
+ if (pkt_len == SCRAMBLE_LENGTH_323 + 1)
pkt_len= strnlen((char*)pkt, pkt_len);
if (pkt_len == 0) /* no password */
@@ -8463,14 +9473,19 @@ static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
if (secure_auth(thd))
return CR_ERROR;
- info->password_used = 1;
+ info->password_used= PASSWORD_USED_YES;
if (pkt_len == SCRAMBLE_LENGTH_323)
- return info->auth_string[0] == 0 ||
- check_scramble_323(pkt, thd->scramble,
- (ulong *)mpvio->acl_user->salt) ? CR_ERROR : CR_OK;
+ {
+ if (!mpvio->acl_user->salt_len)
+ return CR_ERROR;
- inc_host_errors(&mpvio->thd->net.vio->remote.sin_addr);
+ return check_scramble_323(pkt, thd->scramble,
+ (ulong *) mpvio->acl_user->salt) ?
+ CR_ERROR : CR_OK;
+ }
+
+ inc_host_errors(mpvio->thd->security_ctx->ip);
my_error(ER_HANDSHAKE_ERROR, MYF(0));
return CR_ERROR;
}
@@ -8489,37 +9504,6 @@ static struct st_mysql_auth old_password_handler=
old_password_authenticate
};
-mysql_declare_plugin(mysql_password)
-{
- MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
- &native_password_handler, /* type descriptor */
- native_password_plugin_name.str, /* Name */
- "R.J.Silk, Sergei Golubchik", /* Author */
- "Native MySQL authentication", /* Description */
- PLUGIN_LICENSE_GPL, /* License */
- NULL, /* Init function */
- NULL, /* Deinit function */
- 0x0100, /* Version (1.0) */
- NULL, /* status variables */
- NULL, /* system variables */
- NULL /* config options */
-},
-{
- MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
- &old_password_handler, /* type descriptor */
- old_password_plugin_name.str, /* Name */
- "R.J.Silk, Sergei Golubchik", /* Author */
- "Old MySQL-4.0 authentication", /* Description */
- PLUGIN_LICENSE_GPL, /* License */
- NULL, /* Init function */
- NULL, /* Deinit function */
- 0x0100, /* Version (1.0) */
- NULL, /* status variables */
- NULL, /* system variables */
- NULL /* config options */
-}
-mysql_declare_plugin_end;
-
maria_declare_plugin(mysql_password)
{
MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
index 1be2ecd2ef0..3169746419c 100644
--- a/sql/sql_acl.h
+++ b/sql/sql_acl.h
@@ -1,6 +1,7 @@
-/*
- Copyright (c) 2000-2008 MySQL AB, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+#ifndef SQL_ACL_INCLUDED
+#define SQL_ACL_INCLUDED
+
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,10 +14,11 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "slave.h" // for tables_ok(), rpl_filter
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "violite.h" /* SSL_type */
+#include "sql_class.h" /* LEX_COLUMN */
#define SELECT_ACL (1L << 0)
#define INSERT_ACL (1L << 1)
@@ -46,6 +48,7 @@
#define CREATE_USER_ACL (1L << 25)
#define EVENT_ACL (1L << 26)
#define TRIGGER_ACL (1L << 27)
+#define CREATE_TABLESPACE_ACL (1L << 28)
/*
don't forget to update
1. static struct show_privileges_st sys_privileges[]
@@ -54,7 +57,6 @@
4. acl_init() or whatever - to define behaviour for old privilege tables
5. sql_yacc.yy - for GRANT/REVOKE to work
*/
-#define EXTRA_ACL (1L << 29)
#define NO_ACCESS (1L << 30)
#define DB_ACLS \
(UPDATE_ACL | SELECT_ACL | INSERT_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \
@@ -82,11 +84,17 @@
REFERENCES_ACL | INDEX_ACL | ALTER_ACL | SHOW_DB_ACL | SUPER_ACL | \
CREATE_TMP_ACL | LOCK_TABLES_ACL | REPL_SLAVE_ACL | REPL_CLIENT_ACL | \
EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | CREATE_PROC_ACL | \
- ALTER_PROC_ACL | CREATE_USER_ACL | EVENT_ACL | TRIGGER_ACL)
+ ALTER_PROC_ACL | CREATE_USER_ACL | EVENT_ACL | TRIGGER_ACL | \
+ CREATE_TABLESPACE_ACL)
#define DEFAULT_CREATE_PROC_ACLS \
(ALTER_PROC_ACL | EXECUTE_ACL)
+#define SHOW_CREATE_TABLE_ACLS \
+(SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | \
+ CREATE_ACL | DROP_ACL | ALTER_ACL | INDEX_ACL | \
+ TRIGGER_ACL | REFERENCES_ACL | GRANT_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL)
+
/*
Defines to change the above bits to how things are stored in tables
This is needed as the 'host' and 'db' table is missing a few privileges
@@ -163,6 +171,15 @@ enum mysql_db_table_field
};
extern const TABLE_FIELD_DEF mysql_db_table_def;
+extern bool mysql_user_table_is_in_short_password_format;
+
+
+static inline int access_denied_error_code(int passwd_used)
+{
+ return passwd_used == 2 ? ER_ACCESS_DENIED_NO_PASSWORD_ERROR
+ : ER_ACCESS_DENIED_ERROR;
+}
+
/* prototypes */
@@ -181,7 +198,7 @@ int check_change_password(THD *thd, const char *host, const char *user,
bool change_password(THD *thd, const char *host, const char *user,
char *password);
bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &user_list,
- ulong rights, bool revoke);
+ ulong rights, bool revoke, bool is_proxy);
int mysql_table_grant(THD *thd, TABLE_LIST *table, List <LEX_USER> &user_list,
List <LEX_COLUMN> &column_list, ulong rights,
bool revoke);
@@ -192,7 +209,7 @@ my_bool grant_init();
void grant_free(void);
my_bool grant_reload(THD *thd);
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
- uint show_command, uint number, bool dont_print_error);
+ 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);
@@ -223,7 +240,146 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
bool check_routine_level_acl(THD *thd, const char *db, const char *name,
bool is_proc);
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);
+int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
+int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
+int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr);
+
#ifdef NO_EMBEDDED_ACCESS_CHECKS
#define check_grant(A,B,C,D,E,F) 0
#define check_grant_db(A,B) 0
#endif
+
+/**
+ Result of an access check for an internal schema or table.
+ Internal ACL checks are always performed *before* using
+ the grant tables.
+ This mechanism enforces that the server implementation has full
+ control on its internal tables.
+ Depending on the internal check result, the server implementation
+ can choose to:
+ - always allow access,
+ - always deny access,
+ - delegate the decision to the database administrator,
+ by using the grant tables.
+*/
+enum ACL_internal_access_result
+{
+ /**
+ Access granted for all the requested privileges,
+ do not use the grant tables.
+ This flag is used only for the INFORMATION_SCHEMA privileges,
+ for compatibility reasons.
+ */
+ ACL_INTERNAL_ACCESS_GRANTED,
+ /** Access denied, do not use the grant tables. */
+ ACL_INTERNAL_ACCESS_DENIED,
+ /** No decision yet, use the grant tables. */
+ ACL_INTERNAL_ACCESS_CHECK_GRANT
+};
+
+/**
+ Per internal table ACL access rules.
+ This class is an interface.
+ Per table(s) specific access rule should be implemented in a subclass.
+ @sa ACL_internal_schema_access
+*/
+class ACL_internal_table_access
+{
+public:
+ ACL_internal_table_access()
+ {}
+
+ virtual ~ACL_internal_table_access()
+ {}
+
+ /**
+ Check access to an internal table.
+ When a privilege is granted, this method add the requested privilege
+ to save_priv.
+ @param want_access the privileges requested
+ @param [in, out] save_priv the privileges granted
+ @return
+ @retval ACL_INTERNAL_ACCESS_GRANTED All the requested privileges
+ are granted, and saved in save_priv.
+ @retval ACL_INTERNAL_ACCESS_DENIED At least one of the requested
+ privileges was denied.
+ @retval ACL_INTERNAL_ACCESS_CHECK_GRANT No requested privilege
+ was denied, and grant should be checked for at least one
+ privilege. Requested privileges that are granted, if any, are saved
+ in save_priv.
+ */
+ virtual ACL_internal_access_result check(ulong want_access,
+ ulong *save_priv) const= 0;
+};
+
+/**
+ Per internal schema ACL access rules.
+ This class is an interface.
+ Each per schema specific access rule should be implemented
+ in a different subclass, and registered.
+ Per schema access rules can control:
+ - every schema privileges on schema.*
+ - every table privileges on schema.table
+ @sa ACL_internal_schema_registry
+*/
+class ACL_internal_schema_access
+{
+public:
+ ACL_internal_schema_access()
+ {}
+
+ virtual ~ACL_internal_schema_access()
+ {}
+
+ /**
+ Check access to an internal schema.
+ @param want_access the privileges requested
+ @param [in, out] save_priv the privileges granted
+ @return
+ @retval ACL_INTERNAL_ACCESS_GRANTED All the requested privileges
+ are granted, and saved in save_priv.
+ @retval ACL_INTERNAL_ACCESS_DENIED At least one of the requested
+ privileges was denied.
+ @retval ACL_INTERNAL_ACCESS_CHECK_GRANT No requested privilege
+ was denied, and grant should be checked for at least one
+ privilege. Requested privileges that are granted, if any, are saved
+ in save_priv.
+ */
+ virtual ACL_internal_access_result check(ulong want_access,
+ ulong *save_priv) const= 0;
+
+ /**
+ Search for per table ACL access rules by table name.
+ @param name the table name
+ @return per table access rules, or NULL
+ */
+ virtual const ACL_internal_table_access *lookup(const char *name) const= 0;
+};
+
+/**
+ A registry for per internal schema ACL.
+ An 'internal schema' is a database schema maintained by the
+ server implementation, such as 'performance_schema' and 'INFORMATION_SCHEMA'.
+*/
+class ACL_internal_schema_registry
+{
+public:
+ static void register_schema(const LEX_STRING *name,
+ const ACL_internal_schema_access *access);
+ static const ACL_internal_schema_access *lookup(const char *name);
+};
+
+const ACL_internal_schema_access *
+get_cached_schema_access(GRANT_INTERNAL_INFO *grant_internal_info,
+ const char *schema_name);
+
+const ACL_internal_table_access *
+get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info,
+ const char *schema_name,
+ const char *table_name);
+
+bool acl_check_proxy_grant_access (THD *thd, const char *host, const char *user,
+ bool with_grant);
+#endif /* SQL_ACL_INCLUDED */
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc
new file mode 100644
index 00000000000..92aa414b569
--- /dev/null
+++ b/sql/sql_admin.cc
@@ -0,0 +1,1144 @@
+/* Copyright (c) 2010, 2013, 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 "sql_class.h" // THD
+#include "keycaches.h" // get_key_cache
+#include "sql_base.h" // Open_table_context
+#include "lock.h" // MYSQL_OPEN_*
+#include "sql_handler.h" // mysql_ha_rm_tables
+#include "partition_element.h" // PART_ADMIN
+#include "sql_partition.h" // set_part_state
+#include "transaction.h" // trans_rollback_stmt
+#include "sql_view.h" // view_checksum
+#include "sql_table.h" // mysql_recreate_table
+#include "debug_sync.h" // DEBUG_SYNC
+#include "sql_acl.h" // *_ACL
+#include "sp.h" // Sroutine_hash_entry
+#include "sql_parse.h" // check_table_access
+#include "sql_admin.h"
+
+/* Prepare, run and cleanup for mysql_recreate_table() */
+
+static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list)
+{
+ bool result_code;
+ DBUG_ENTER("admin_recreate_table");
+
+ trans_rollback_stmt(thd);
+ trans_rollback(thd);
+ close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
+ DEBUG_SYNC(thd, "ha_admin_try_alter");
+ tmp_disable_binlog(thd); // binlogging is done by caller if wanted
+ result_code= mysql_recreate_table(thd, table_list);
+ reenable_binlog(thd);
+ /*
+ mysql_recreate_table() can push OK or ERROR.
+ Clear 'OK' status. If there is an error, keep it:
+ we will store the error message in a result set row
+ and then clear.
+ */
+ if (thd->stmt_da->is_ok())
+ thd->stmt_da->reset_diagnostics_area();
+ table_list->table= NULL;
+ result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
+ DBUG_RETURN(result_code);
+}
+
+
+static int send_check_errmsg(THD *thd, TABLE_LIST* table,
+ const char* operator_name, const char* errmsg)
+
+{
+ Protocol *protocol= thd->protocol;
+ protocol->prepare_for_resend();
+ protocol->store(table->alias, 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);
+ thd->clear_error();
+ if (protocol->write())
+ return -1;
+ return 1;
+}
+
+
+static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
+ HA_CHECK_OPT *check_opt)
+{
+ int error= 0;
+ TABLE tmp_table, *table;
+ TABLE_LIST *pos_in_locked_tables= 0;
+ TABLE_SHARE *share;
+ bool has_mdl_lock= FALSE;
+ char from[FN_REFLEN],tmp[FN_REFLEN+32];
+ const char **ext;
+ MY_STAT stat_info;
+ Open_table_context ot_ctx(thd, (MYSQL_OPEN_IGNORE_FLUSH |
+ MYSQL_OPEN_FOR_REPAIR |
+ MYSQL_OPEN_HAS_MDL_LOCK |
+ MYSQL_LOCK_IGNORE_TIMEOUT));
+ DBUG_ENTER("prepare_for_repair");
+
+ if (!(check_opt->sql_flags & TT_USEFRM))
+ DBUG_RETURN(0);
+
+ if (!(table= table_list->table))
+ {
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length;
+ /*
+ If the table didn't exist, we have a shared metadata lock
+ on it that is left from mysql_admin_table()'s attempt to
+ open it. Release the shared metadata lock before trying to
+ acquire the exclusive lock to satisfy MDL asserts and avoid
+ deadlocks.
+ */
+ thd->mdl_context.release_transactional_locks();
+ /*
+ Attempt to do full-blown table open in mysql_admin_table() has failed.
+ Let us try to open at least a .FRM for this table.
+ */
+ my_hash_value_type hash_value;
+
+ key_length= create_table_def_key(thd, key, table_list, 0);
+ table_list->mdl_request.init(MDL_key::TABLE,
+ table_list->db, table_list->table_name,
+ MDL_EXCLUSIVE, MDL_TRANSACTION);
+
+ if (lock_table_names(thd, table_list, table_list->next_global,
+ thd->variables.lock_wait_timeout,
+ MYSQL_OPEN_SKIP_TEMPORARY))
+ DBUG_RETURN(0);
+ has_mdl_lock= TRUE;
+
+ hash_value= my_calc_hash(&table_def_cache, (uchar*) key, key_length);
+ mysql_mutex_lock(&LOCK_open);
+ share= get_table_share(thd, table_list, key, key_length, 0,
+ &error, hash_value);
+ mysql_mutex_unlock(&LOCK_open);
+ if (share == NULL)
+ DBUG_RETURN(0); // Can't open frm file
+
+ if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE))
+ {
+ mysql_mutex_lock(&LOCK_open);
+ release_table_share(share);
+ mysql_mutex_unlock(&LOCK_open);
+ DBUG_RETURN(0); // Out of memory
+ }
+ table= &tmp_table;
+ }
+
+ /*
+ REPAIR TABLE ... USE_FRM for temporary tables makes little sense.
+ */
+ if (table->s->tmp_table)
+ {
+ error= send_check_errmsg(thd, table_list, "repair",
+ "Cannot repair temporary table from .frm file");
+ goto end;
+ }
+
+ /*
+ User gave us USE_FRM which means that the header in the index file is
+ trashed.
+ In this case we will try to fix the table the following way:
+ - Rename the data file to a temporary name
+ - Truncate the table
+ - Replace the new data file with the old one
+ - Run a normal repair using the new index file and the old data file
+ */
+
+ if (table->s->frm_version != FRM_VER_TRUE_VARCHAR &&
+ table->s->varchar_fields)
+ {
+ error= send_check_errmsg(thd, table_list, "repair",
+ "Failed repairing a very old .frm file as the data file format has changed between versions. Please dump the table in your old system with mysqldump and read it into this system with mysql or mysqlimport");
+ goto end;
+ }
+
+ /*
+ 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
+ is meta/index file extention. Second element - data file extention.
+ */
+ ext= table->file->bas_ext();
+ if (!ext[0] || !ext[1])
+ goto end; // No data file
+
+ /* A MERGE table must not come here. */
+ DBUG_ASSERT(table->file->ht->db_type != DB_TYPE_MRG_MYISAM);
+
+ // Name of data file
+ strxmov(from, table->s->normalized_path.str, ext[1], NullS);
+ 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",
+ from, current_pid, thd->thread_id);
+
+ if (table_list->table)
+ {
+ /*
+ Table was successfully open in mysql_admin_table(). Now we need
+ to close it, but leave it protected by exclusive metadata lock.
+ */
+ pos_in_locked_tables= table->pos_in_locked_tables;
+ if (wait_while_table_is_used(thd, table,
+ HA_EXTRA_PREPARE_FOR_FORCED_CLOSE,
+ TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE))
+ goto end;
+ /* Close table but don't remove from locked list */
+ close_all_tables_for_name(thd, table_list->table->s,
+ HA_EXTRA_NOT_USED);
+ table_list->table= 0;
+ }
+ /*
+ After this point we have an exclusive metadata lock on our table
+ in both cases when table was successfully open in mysql_admin_table()
+ and when it was open in prepare_for_repair().
+ */
+
+ if (my_rename(from, tmp, MYF(MY_WME)))
+ {
+ error= send_check_errmsg(thd, table_list, "repair",
+ "Failed renaming data file");
+ goto end;
+ }
+ if (dd_recreate_table(thd, table_list->db, table_list->table_name))
+ {
+ error= send_check_errmsg(thd, table_list, "repair",
+ "Failed generating table from .frm file");
+ goto end;
+ }
+ /*
+ 'FALSE' for 'using_transactions' means don't postpone
+ invalidation till the end of a transaction, but do it
+ immediately.
+ */
+ query_cache_invalidate3(thd, table_list, FALSE);
+ if (mysql_file_rename(key_file_misc, tmp, from, MYF(MY_WME)))
+ {
+ error= send_check_errmsg(thd, table_list, "repair",
+ "Failed restoring .MYD file");
+ goto end;
+ }
+
+ if (thd->locked_tables_list.locked_tables())
+ {
+ if (thd->locked_tables_list.reopen_tables(thd))
+ goto end;
+ /* Restore the table in the table list with the new opened table */
+ table_list->table= pos_in_locked_tables->table;
+ }
+ else
+ {
+ /*
+ Now we should be able to open the partially repaired table
+ to finish the repair in the handler later on.
+ */
+ if (open_table(thd, table_list, thd->mem_root, &ot_ctx))
+ {
+ error= send_check_errmsg(thd, table_list, "repair",
+ "Failed to open partially repaired table");
+ goto end;
+ }
+ }
+
+end:
+ thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
+ if (table == &tmp_table)
+ {
+ mysql_mutex_lock(&LOCK_open);
+ closefrm(table, 1); // Free allocated memory
+ mysql_mutex_unlock(&LOCK_open);
+ }
+ /* In case of a temporary table there will be no metadata lock. */
+ if (error && has_mdl_lock)
+ thd->mdl_context.release_transactional_locks();
+
+ DBUG_RETURN(error);
+}
+
+
+/**
+ Check if a given error is something that could occur during
+ open_and_lock_tables() that does not indicate table corruption.
+
+ @param sql_errno Error number to check.
+
+ @retval TRUE Error does not indicate table corruption.
+ @retval FALSE Error could indicate table corruption.
+*/
+
+static inline bool table_not_corrupt_error(uint sql_errno)
+{
+ return (sql_errno == ER_NO_SUCH_TABLE ||
+ sql_errno == ER_NO_SUCH_TABLE_IN_ENGINE ||
+ sql_errno == ER_FILE_NOT_FOUND ||
+ sql_errno == ER_LOCK_WAIT_TIMEOUT ||
+ sql_errno == ER_LOCK_DEADLOCK ||
+ sql_errno == ER_CANT_LOCK_LOG_TABLE ||
+ sql_errno == ER_OPEN_AS_READONLY);
+}
+
+
+/*
+ RETURN VALUES
+ FALSE Message sent to net (admin operation went ok)
+ TRUE Message should be sent by caller
+ (admin operation or network communication failed)
+*/
+static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
+ HA_CHECK_OPT* check_opt,
+ const char *operator_name,
+ thr_lock_type lock_type,
+ bool open_for_modify,
+ bool repair_table_use_frm,
+ uint extra_open_options,
+ int (*prepare_func)(THD *, TABLE_LIST *,
+ HA_CHECK_OPT *),
+ int (handler::*operator_func)(THD *,
+ HA_CHECK_OPT *),
+ int (view_operator_func)(THD *, TABLE_LIST*))
+{
+ TABLE_LIST *table;
+ SELECT_LEX *select= &thd->lex->select_lex;
+ List<Item> field_list;
+ Item *item;
+ Protocol *protocol= thd->protocol;
+ LEX *lex= thd->lex;
+ int result_code;
+ bool need_repair_or_alter= 0;
+ DBUG_ENTER("mysql_admin_table");
+ DBUG_PRINT("enter", ("extra_open_options: %u", extra_open_options));
+
+ field_list.push_back(item = new Item_empty_string("Table", NAME_CHAR_LEN*2));
+ item->maybe_null = 1;
+ field_list.push_back(item = new Item_empty_string("Op", 10));
+ item->maybe_null = 1;
+ field_list.push_back(item = new Item_empty_string("Msg_type", 10));
+ item->maybe_null = 1;
+ field_list.push_back(item = new Item_empty_string("Msg_text",
+ SQL_ADMIN_MSG_TEXT_SIZE));
+ item->maybe_null = 1;
+ if (protocol->send_result_set_metadata(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ DBUG_RETURN(TRUE);
+
+ mysql_ha_rm_tables(thd, tables);
+
+ for (table= tables; table; table= table->next_local)
+ {
+ char table_name[SAFE_NAME_LEN*2+2];
+ char* db = table->db;
+ bool fatal_error=0;
+ bool open_error;
+
+ DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name));
+ strxmov(table_name, db, ".", table->table_name, NullS);
+ thd->open_options|= extra_open_options;
+ table->lock_type= lock_type;
+ /*
+ To make code safe for re-execution we need to reset type of MDL
+ request as code below may change it.
+ To allow concurrent execution of read-only operations we acquire
+ weak metadata lock for them.
+ */
+ table->mdl_request.set_type((lock_type >= TL_WRITE_ALLOW_WRITE) ?
+ MDL_SHARED_NO_READ_WRITE : MDL_SHARED_READ);
+ /* open only one table from local list of command */
+ {
+ TABLE_LIST *save_next_global, *save_next_local;
+ save_next_global= table->next_global;
+ table->next_global= 0;
+ save_next_local= table->next_local;
+ table->next_local= 0;
+ select->table_list.first= table;
+ /*
+ Time zone tables and SP tables can be add to lex->query_tables list,
+ so it have to be prepared.
+ TODO: Investigate if we can put extra tables into argument instead of
+ using lex->query_tables
+ */
+ lex->query_tables= table;
+ lex->query_tables_last= &table->next_global;
+ lex->query_tables_own_last= 0;
+
+ if (view_operator_func == NULL)
+ table->required_type=FRMTYPE_TABLE;
+
+ if (lex->sql_command == SQLCOM_CHECK ||
+ lex->sql_command == SQLCOM_REPAIR ||
+ lex->sql_command == SQLCOM_ANALYZE ||
+ lex->sql_command == SQLCOM_OPTIMIZE)
+ thd->prepare_derived_at_open= TRUE;
+ if (!thd->locked_tables_mode && repair_table_use_frm)
+ {
+ /*
+ If we're not under LOCK TABLES and we're executing REPAIR TABLE
+ USE_FRM, we need to ignore errors from open_and_lock_tables().
+ REPAIR TABLE USE_FRM is a heavy weapon used when a table is
+ critically damaged, so open_and_lock_tables() will most likely
+ report errors. Those errors are not interesting for the user
+ because it's already known that the table is badly damaged.
+ */
+
+ Warning_info wi(thd->query_id, false);
+ Warning_info *wi_saved= thd->warning_info;
+
+ thd->warning_info= &wi;
+
+ open_error= open_and_lock_tables(thd, table, TRUE, 0);
+
+ thd->warning_info= wi_saved;
+ }
+ else
+ {
+ /*
+ It's assumed that even if it is REPAIR TABLE USE_FRM, the table
+ can be opened if we're under LOCK TABLES (otherwise LOCK TABLES
+ would fail). Thus, the only errors we could have from
+ open_and_lock_tables() are logical ones, like incorrect locking
+ mode. It does make sense for the user to see such errors.
+ */
+
+ open_error= open_and_lock_tables(thd, table, TRUE, 0);
+ }
+ thd->prepare_derived_at_open= FALSE;
+
+ table->next_global= save_next_global;
+ table->next_local= save_next_local;
+ thd->open_options&= ~extra_open_options;
+
+ /*
+ If open_and_lock_tables() failed, close_thread_tables() will close
+ the table and table->table can therefore be invalid.
+ */
+ if (open_error)
+ table->table= NULL;
+
+ /*
+ Under locked tables, we know that the table can be opened,
+ 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)
+ {
+ result_code= HA_ADMIN_FAILED;
+ goto send_result;
+ }
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (table->table)
+ {
+ /*
+ Set up which partitions that should be processed
+ if ALTER TABLE t ANALYZE/CHECK/OPTIMIZE/REPAIR PARTITION ..
+ CACHE INDEX/LOAD INDEX for specified partitions
+ */
+ Alter_info *alter_info= &lex->alter_info;
+
+ if (alter_info->flags & ALTER_ADMIN_PARTITION)
+ {
+ if (!table->table->part_info)
+ {
+ my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ if (set_part_state(alter_info, table->table->part_info, PART_ADMIN))
+ {
+ char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
+ size_t length;
+ DBUG_PRINT("admin", ("sending non existent partition error"));
+ protocol->prepare_for_resend();
+ protocol->store(table_name, system_charset_info);
+ protocol->store(operator_name, system_charset_info);
+ protocol->store(STRING_WITH_LEN("error"), system_charset_info);
+ length= my_snprintf(buff, sizeof(buff),
+ ER(ER_DROP_PARTITION_NON_EXISTENT),
+ table_name);
+ protocol->store(buff, length, system_charset_info);
+ if(protocol->write())
+ goto err;
+ my_eof(thd);
+ goto err;
+ }
+ }
+ }
+#endif
+ }
+ DBUG_PRINT("admin", ("table: 0x%lx", (long) table->table));
+
+ if (prepare_func)
+ {
+ DBUG_PRINT("admin", ("calling prepare_func"));
+ switch ((*prepare_func)(thd, table, check_opt)) {
+ case 1: // error, message written to net
+ trans_rollback_stmt(thd);
+ trans_rollback(thd);
+ close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
+ DBUG_PRINT("admin", ("simple error, admin next table"));
+ continue;
+ case -1: // error, message could be written to net
+ /* purecov: begin inspected */
+ DBUG_PRINT("admin", ("severe error, stop"));
+ goto err;
+ /* purecov: end */
+ default: // should be 0 otherwise
+ DBUG_PRINT("admin", ("prepare_func succeeded"));
+ ;
+ }
+ }
+
+ /*
+ CHECK TABLE command is only command where VIEW allowed here and this
+ command use only temporary teble method for VIEWs resolving => there
+ can't be VIEW tree substitition of join view => if opening table
+ succeed then table->table will have real TABLE pointer as value (in
+ case of join view substitution table->table can be 0, but here it is
+ impossible)
+ */
+ if (!table->table)
+ {
+ DBUG_PRINT("admin", ("open table failed"));
+ if (thd->warning_info->is_empty())
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_CHECK_NO_SUCH_TABLE, ER(ER_CHECK_NO_SUCH_TABLE));
+ /* if it was a view will check md5 sum */
+ if (table->view &&
+ view_checksum(thd, table) == HA_ADMIN_WRONG_CHECKSUM)
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_VIEW_CHECKSUM, ER(ER_VIEW_CHECKSUM));
+ if (thd->stmt_da->is_error() &&
+ table_not_corrupt_error(thd->stmt_da->sql_errno()))
+ result_code= HA_ADMIN_FAILED;
+ else
+ /* Default failure code is corrupt table */
+ result_code= HA_ADMIN_CORRUPT;
+ goto send_result;
+ }
+
+ if (table->view)
+ {
+ DBUG_PRINT("admin", ("calling view_operator_func"));
+ result_code= (*view_operator_func)(thd, table);
+ goto send_result;
+ }
+
+ if (table->schema_table)
+ {
+ result_code= HA_ADMIN_NOT_IMPLEMENTED;
+ goto send_result;
+ }
+
+ if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
+ {
+ /* purecov: begin inspected */
+ char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
+ size_t length;
+ enum_sql_command save_sql_command= lex->sql_command;
+ DBUG_PRINT("admin", ("sending error message"));
+ protocol->prepare_for_resend();
+ protocol->store(table_name, system_charset_info);
+ protocol->store(operator_name, system_charset_info);
+ protocol->store(STRING_WITH_LEN("error"), system_charset_info);
+ length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
+ table_name);
+ protocol->store(buff, length, system_charset_info);
+ trans_commit_stmt(thd);
+ trans_commit(thd);
+ close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
+ lex->reset_query_tables_list(FALSE);
+ /*
+ Restore Query_tables_list::sql_command value to make statement
+ safe for re-execution.
+ */
+ lex->sql_command= save_sql_command;
+ table->table=0; // For query cache
+ if (protocol->write())
+ goto err;
+ thd->stmt_da->reset_diagnostics_area();
+ continue;
+ /* purecov: end */
+ }
+
+ /*
+ Close all instances of the table to allow MyISAM "repair"
+ to rename files.
+ @todo: This code does not close all instances of the table.
+ It only closes instances in other connections, but if this
+ connection has LOCK TABLE t1 a READ, t1 b WRITE,
+ both t1 instances will be kept open.
+ There is no need to execute this branch for InnoDB, which does
+ repair by recreate. There is no need to do it for OPTIMIZE,
+ which doesn't move files around.
+ Hence, this code should be moved to prepare_for_repair(),
+ and executed only for MyISAM engine.
+ */
+ if (lock_type == TL_WRITE && !table->table->s->tmp_table)
+ {
+ table->table->s->protect_against_usage();
+ if (wait_while_table_is_used(thd, table->table,
+ HA_EXTRA_PREPARE_FOR_RENAME,
+ TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE))
+ goto err;
+ DEBUG_SYNC(thd, "after_admin_flush");
+ /* Flush entries in the query cache involving this table. */
+ query_cache_invalidate3(thd, table->table, 0);
+ /*
+ XXX: hack: switch off open_for_modify to skip the
+ flush that is made later in the execution flow.
+ */
+ open_for_modify= 0;
+ }
+
+ if (table->table->s->crashed && operator_func == &handler::ha_check)
+ {
+ /* purecov: begin inspected */
+ DBUG_PRINT("admin", ("sending crashed warning"));
+ protocol->prepare_for_resend();
+ protocol->store(table_name, system_charset_info);
+ protocol->store(operator_name, system_charset_info);
+ protocol->store(STRING_WITH_LEN("warning"), system_charset_info);
+ protocol->store(STRING_WITH_LEN("Table is marked as crashed"),
+ system_charset_info);
+ if (protocol->write())
+ goto err;
+ /* purecov: end */
+ }
+
+ if (operator_func == &handler::ha_repair &&
+ !(check_opt->sql_flags & TT_USEFRM))
+ {
+ handler *file= table->table->file;
+ int check_old_types= file->check_old_types();
+ int check_for_upgrade= file->ha_check_for_upgrade(check_opt);
+
+ if (check_old_types == HA_ADMIN_NEEDS_ALTER ||
+ check_for_upgrade == HA_ADMIN_NEEDS_ALTER)
+ {
+ /* We use extra_open_options to be able to open crashed tables */
+ thd->open_options|= extra_open_options;
+ result_code= admin_recreate_table(thd, table);
+ thd->open_options&= ~extra_open_options;
+ goto send_result;
+ }
+ if (check_old_types || check_for_upgrade)
+ {
+ /* If repair is not implemented for the engine, run ALTER TABLE */
+ need_repair_or_alter= 1;
+ }
+ }
+
+ DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name));
+ thd_proc_info(thd, "executing");
+ result_code = (table->table->file->*operator_func)(thd, check_opt);
+ thd_proc_info(thd, "Sending data");
+ DBUG_PRINT("admin", ("operator_func returned: %d", result_code));
+
+ if (result_code == HA_ADMIN_NOT_IMPLEMENTED && need_repair_or_alter)
+ {
+ /*
+ repair was not implemented and we need to upgrade the table
+ to a new version so we recreate the table with ALTER TABLE
+ */
+ result_code= admin_recreate_table(thd, table);
+ }
+send_result:
+
+ lex->cleanup_after_one_table_open();
+ thd->clear_error(); // these errors shouldn't get client
+ {
+ List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
+ MYSQL_ERROR *err;
+ while ((err= it++))
+ {
+ protocol->prepare_for_resend();
+ protocol->store(table_name, system_charset_info);
+ protocol->store((char*) operator_name, system_charset_info);
+ protocol->store(warning_level_names[err->get_level()].str,
+ warning_level_names[err->get_level()].length,
+ system_charset_info);
+ protocol->store(err->get_message_text(), system_charset_info);
+ if (protocol->write())
+ goto err;
+ }
+ thd->warning_info->clear_warning_info(thd->query_id);
+ }
+ protocol->prepare_for_resend();
+ protocol->store(table_name, system_charset_info);
+ protocol->store(operator_name, system_charset_info);
+
+send_result_message:
+
+ DBUG_PRINT("info", ("result_code: %d", result_code));
+ switch (result_code) {
+ case HA_ADMIN_NOT_IMPLEMENTED:
+ {
+ char buf[MYSQL_ERRMSG_SIZE];
+ size_t length=my_snprintf(buf, sizeof(buf),
+ ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
+ protocol->store(STRING_WITH_LEN("note"), system_charset_info);
+ protocol->store(buf, length, system_charset_info);
+ }
+ break;
+
+ case HA_ADMIN_NOT_BASE_TABLE:
+ {
+ char buf[MYSQL_ERRMSG_SIZE];
+ size_t length= my_snprintf(buf, sizeof(buf),
+ ER(ER_BAD_TABLE_ERROR), table_name);
+ protocol->store(STRING_WITH_LEN("note"), system_charset_info);
+ protocol->store(buf, length, system_charset_info);
+ }
+ break;
+
+ case HA_ADMIN_OK:
+ protocol->store(STRING_WITH_LEN("status"), system_charset_info);
+ protocol->store(STRING_WITH_LEN("OK"), system_charset_info);
+ break;
+
+ case HA_ADMIN_FAILED:
+ protocol->store(STRING_WITH_LEN("status"), system_charset_info);
+ protocol->store(STRING_WITH_LEN("Operation failed"),
+ system_charset_info);
+ break;
+
+ case HA_ADMIN_REJECT:
+ protocol->store(STRING_WITH_LEN("status"), system_charset_info);
+ protocol->store(STRING_WITH_LEN("Operation need committed state"),
+ system_charset_info);
+ open_for_modify= FALSE;
+ break;
+
+ case HA_ADMIN_ALREADY_DONE:
+ protocol->store(STRING_WITH_LEN("status"), system_charset_info);
+ protocol->store(STRING_WITH_LEN("Table is already up to date"),
+ system_charset_info);
+ break;
+
+ case HA_ADMIN_CORRUPT:
+ protocol->store(STRING_WITH_LEN("error"), system_charset_info);
+ protocol->store(STRING_WITH_LEN("Corrupt"), system_charset_info);
+ fatal_error=1;
+ break;
+
+ case HA_ADMIN_INVALID:
+ protocol->store(STRING_WITH_LEN("error"), system_charset_info);
+ protocol->store(STRING_WITH_LEN("Invalid argument"),
+ system_charset_info);
+ break;
+
+ case HA_ADMIN_TRY_ALTER:
+ {
+ uint save_flags;
+ Alter_info *alter_info= &lex->alter_info;
+
+ /* Store the original value of alter_info->flags */
+ save_flags= alter_info->flags;
+ /*
+ This is currently used only by InnoDB. ha_innobase::optimize() answers
+ "try with alter", so here we close the table, do an ALTER TABLE,
+ reopen the table and do ha_innobase::analyze() on it.
+ We have to end the row, so analyze could return more rows.
+ */
+ protocol->store(STRING_WITH_LEN("note"), system_charset_info);
+ if(alter_info->flags & ALTER_ADMIN_PARTITION)
+ {
+ protocol->store(STRING_WITH_LEN(
+ "Table does not support optimize on partitions. All partitions "
+ "will be rebuilt and analyzed."),system_charset_info);
+ }
+ else
+ {
+ protocol->store(STRING_WITH_LEN(
+ "Table does not support optimize, doing recreate + analyze instead"),
+ system_charset_info);
+ }
+ if (protocol->write())
+ goto err;
+ thd_proc_info(thd, "recreating table");
+ DBUG_PRINT("info", ("HA_ADMIN_TRY_ALTER, trying analyze..."));
+ TABLE_LIST *save_next_local= table->next_local,
+ *save_next_global= table->next_global;
+ table->next_local= table->next_global= 0;
+ result_code= admin_recreate_table(thd, table);
+
+ trans_commit_stmt(thd);
+ trans_commit(thd);
+ close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
+
+ if (!result_code) // recreation went ok
+ {
+ /* Clear the ticket released above. */
+ table->mdl_request.ticket= NULL;
+ DEBUG_SYNC(thd, "ha_admin_open_ltable");
+ table->mdl_request.set_type(MDL_SHARED_WRITE);
+ /*
+ Reset the ALTER_ADMIN_PARTITION bit in alter_info->flags
+ to force analyze on all partitions.
+ */
+ alter_info->flags &= ~(ALTER_ADMIN_PARTITION);
+ if ((table->table= open_ltable(thd, table, lock_type, 0)))
+ {
+ result_code= table->table->file->ha_analyze(thd, check_opt);
+ if (result_code == HA_ADMIN_ALREADY_DONE)
+ result_code= HA_ADMIN_OK;
+ else if (result_code) // analyze failed
+ table->table->file->print_error(result_code, MYF(0));
+ }
+ else
+ result_code= -1; // open failed
+ alter_info->flags= save_flags;
+ }
+ /* Start a new row for the final status row */
+ protocol->prepare_for_resend();
+ protocol->store(table_name, system_charset_info);
+ protocol->store(operator_name, system_charset_info);
+ if (result_code) // either mysql_recreate_table or analyze failed
+ {
+ DBUG_ASSERT(thd->is_error() || thd->killed);
+ if (thd->is_error())
+ {
+ const char *err_msg= thd->stmt_da->message();
+ if (!thd->vio_ok())
+ {
+ sql_print_error("%s", err_msg);
+ }
+ else
+ {
+ /* Hijack the row already in-progress. */
+ protocol->store(STRING_WITH_LEN("error"), system_charset_info);
+ protocol->store(err_msg, system_charset_info);
+ if (protocol->write())
+ goto err;
+ /* Start off another row for HA_ADMIN_FAILED */
+ protocol->prepare_for_resend();
+ protocol->store(table_name, system_charset_info);
+ protocol->store(operator_name, system_charset_info);
+ }
+ thd->clear_error();
+ }
+ }
+ result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
+ table->next_local= save_next_local;
+ table->next_global= save_next_global;
+ goto send_result_message;
+ }
+ case HA_ADMIN_WRONG_CHECKSUM:
+ {
+ protocol->store(STRING_WITH_LEN("note"), system_charset_info);
+ protocol->store(ER(ER_VIEW_CHECKSUM), strlen(ER(ER_VIEW_CHECKSUM)),
+ system_charset_info);
+ break;
+ }
+
+ case HA_ADMIN_NEEDS_UPGRADE:
+ case HA_ADMIN_NEEDS_ALTER:
+ {
+ char buf[MYSQL_ERRMSG_SIZE];
+ size_t length;
+
+ protocol->store(STRING_WITH_LEN("error"), system_charset_info);
+ if (table->table->file->ha_table_flags() & HA_CAN_REPAIR)
+ length= my_snprintf(buf, sizeof(buf), ER(ER_TABLE_NEEDS_UPGRADE),
+ table->table_name);
+ else
+ length= my_snprintf(buf, sizeof(buf), ER(ER_TABLE_NEEDS_REBUILD),
+ table->table_name);
+ protocol->store(buf, length, system_charset_info);
+ fatal_error=1;
+ break;
+ }
+
+ default: // Probably HA_ADMIN_INTERNAL_ERROR
+ {
+ char buf[MYSQL_ERRMSG_SIZE];
+ size_t length=my_snprintf(buf, sizeof(buf),
+ "Unknown - internal error %d during operation",
+ result_code);
+ protocol->store(STRING_WITH_LEN("error"), system_charset_info);
+ protocol->store(buf, length, system_charset_info);
+ fatal_error=1;
+ break;
+ }
+ }
+ if (table->table)
+ {
+ if (table->table->s->tmp_table)
+ {
+ /*
+ If the table was not opened successfully, do not try to get
+ status information. (Bug#47633)
+ */
+ if (open_for_modify && !open_error)
+ table->table->file->info(HA_STATUS_CONST);
+ }
+ else if (open_for_modify || fatal_error)
+ {
+ tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED,
+ table->db, table->table_name, FALSE);
+ /*
+ May be something modified. Consequently, we have to
+ invalidate the query cache.
+ */
+ table->table= 0; // For query cache
+ query_cache_invalidate3(thd, table, 0);
+ }
+ }
+ /* Error path, a admin command failed. */
+ if (thd->transaction_rollback_request)
+ {
+ /*
+ Unlikely, but transaction rollback was requested by one of storage
+ engines (e.g. due to deadlock). Perform it.
+ */
+ if (trans_rollback_stmt(thd) || trans_rollback_implicit(thd))
+ goto err;
+ }
+ else
+ {
+ if (trans_commit_stmt(thd) || trans_commit_implicit(thd))
+ goto err;
+ }
+ close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
+
+ /*
+ If it is CHECK TABLE v1, v2, v3, and v1, v2, v3 are views, we will run
+ separate open_tables() for each CHECK TABLE argument.
+ Right now we do not have a separate method to reset the prelocking
+ state in the lex to the state after parsing, so each open will pollute
+ this state: add elements to lex->srotuines_list, TABLE_LISTs to
+ lex->query_tables. Below is a lame attempt to recover from this
+ pollution.
+ @todo: have a method to reset a prelocking context, or use separate
+ contexts for each open.
+ */
+ for (Sroutine_hash_entry *rt=
+ (Sroutine_hash_entry*)thd->lex->sroutines_list.first;
+ rt; rt= rt->next)
+ rt->mdl_request.ticket= NULL;
+
+ if (protocol->write())
+ goto err;
+ }
+
+ my_eof(thd);
+ DBUG_RETURN(FALSE);
+
+err:
+ /* Make sure this table instance is not reused after the failure. */
+ if (table && table->table)
+ table->table->m_needs_reopen= true;
+ trans_rollback_stmt(thd);
+ trans_rollback(thd);
+ close_thread_tables(thd); // Shouldn't be needed
+ thd->mdl_context.release_transactional_locks();
+ if (table)
+ table->table=0;
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Assigned specified indexes for a table into key cache
+
+ SYNOPSIS
+ mysql_assign_to_keycache()
+ thd Thread object
+ tables Table list (one table only)
+
+ RETURN VALUES
+ FALSE ok
+ TRUE error
+*/
+
+bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
+ LEX_STRING *key_cache_name)
+{
+ HA_CHECK_OPT check_opt;
+ KEY_CACHE *key_cache;
+ DBUG_ENTER("mysql_assign_to_keycache");
+
+ thd_proc_info(thd, "Finding key cache");
+ check_opt.init();
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ if (!(key_cache= get_key_cache(key_cache_name)))
+ {
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ my_error(ER_UNKNOWN_KEY_CACHE, MYF(0), key_cache_name->str);
+ DBUG_RETURN(TRUE);
+ }
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ if (!key_cache->key_cache_inited)
+ {
+ my_error(ER_UNKNOWN_KEY_CACHE, MYF(0), key_cache_name->str);
+ DBUG_RETURN(true);
+ }
+ check_opt.key_cache= key_cache;
+ DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
+ "assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
+ 0, 0, &handler::assign_to_keycache, 0));
+}
+
+
+/*
+ Preload specified indexes for a table into key cache
+
+ SYNOPSIS
+ mysql_preload_keys()
+ thd Thread object
+ tables Table list (one table only)
+
+ RETURN VALUES
+ FALSE ok
+ TRUE error
+*/
+
+bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
+{
+ DBUG_ENTER("mysql_preload_keys");
+ /*
+ We cannot allow concurrent inserts. The storage engine reads
+ directly from the index file, bypassing the cache. It could read
+ outdated information if parallel inserts into cache blocks happen.
+ */
+ DBUG_RETURN(mysql_admin_table(thd, tables, 0,
+ "preload_keys", TL_READ_NO_INSERT, 0, 0, 0, 0,
+ &handler::preload_keys, 0));
+}
+
+
+bool Analyze_table_statement::execute(THD *thd)
+{
+ TABLE_LIST *first_table= m_lex->select_lex.table_list.first;
+ bool res= TRUE;
+ thr_lock_type lock_type = TL_READ_NO_INSERT;
+ DBUG_ENTER("Analyze_table_statement::execute");
+
+ if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table,
+ FALSE, UINT_MAX, FALSE))
+ goto error;
+ thd->enable_slow_log= opt_log_slow_admin_statements;
+ res= mysql_admin_table(thd, first_table, &m_lex->check_opt,
+ "analyze", lock_type, 1, 0, 0, 0,
+ &handler::ha_analyze, 0);
+ /* ! we write after unlocking the table */
+ if (!res && !m_lex->no_write_to_binlog)
+ {
+ /*
+ Presumably, ANALYZE and binlog writing doesn't require synchronization
+ */
+ res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
+ }
+ m_lex->select_lex.table_list.first= first_table;
+ m_lex->query_tables= first_table;
+
+error:
+ DBUG_RETURN(res);
+}
+
+
+bool Check_table_statement::execute(THD *thd)
+{
+ TABLE_LIST *first_table= m_lex->select_lex.table_list.first;
+ thr_lock_type lock_type = TL_READ_NO_INSERT;
+ bool res= TRUE;
+ DBUG_ENTER("Check_table_statement::execute");
+
+ if (check_table_access(thd, SELECT_ACL, first_table,
+ TRUE, UINT_MAX, FALSE))
+ goto error; /* purecov: inspected */
+ thd->enable_slow_log= opt_log_slow_admin_statements;
+
+ 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_checksum);
+
+ m_lex->select_lex.table_list.first= first_table;
+ m_lex->query_tables= first_table;
+
+error:
+ DBUG_RETURN(res);
+}
+
+
+bool Optimize_table_statement::execute(THD *thd)
+{
+ TABLE_LIST *first_table= m_lex->select_lex.table_list.first;
+ bool res= TRUE;
+ DBUG_ENTER("Optimize_table_statement::execute");
+
+ if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table,
+ FALSE, UINT_MAX, FALSE))
+ goto error; /* purecov: inspected */
+ thd->enable_slow_log= opt_log_slow_admin_statements;
+ res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
+ mysql_recreate_table(thd, first_table) :
+ 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)
+ {
+ /*
+ Presumably, OPTIMIZE and binlog writing doesn't require synchronization
+ */
+ res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
+ }
+ m_lex->select_lex.table_list.first= first_table;
+ m_lex->query_tables= first_table;
+
+error:
+ DBUG_RETURN(res);
+}
+
+
+bool Repair_table_statement::execute(THD *thd)
+{
+ TABLE_LIST *first_table= m_lex->select_lex.table_list.first;
+ bool res= TRUE;
+ DBUG_ENTER("Repair_table_statement::execute");
+
+ if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table,
+ FALSE, UINT_MAX, FALSE))
+ goto error; /* purecov: inspected */
+ thd->enable_slow_log= opt_log_slow_admin_statements;
+ res= mysql_admin_table(thd, first_table, &m_lex->check_opt, "repair",
+ TL_WRITE, 1,
+ test(m_lex->check_opt.sql_flags & TT_USEFRM),
+ HA_OPEN_FOR_REPAIR, &prepare_for_repair,
+ &handler::ha_repair, 0);
+
+ /* ! we write after unlocking the table */
+ if (!res && !m_lex->no_write_to_binlog)
+ {
+ /*
+ Presumably, REPAIR and binlog writing doesn't require synchronization
+ */
+ res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
+ }
+ m_lex->select_lex.table_list.first= first_table;
+ m_lex->query_tables= first_table;
+
+error:
+ DBUG_RETURN(res);
+}
diff --git a/sql/sql_admin.h b/sql/sql_admin.h
new file mode 100644
index 00000000000..43d8f70c6f4
--- /dev/null
+++ b/sql/sql_admin.h
@@ -0,0 +1,134 @@
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_TABLE_MAINTENANCE_H
+#define SQL_TABLE_MAINTENANCE_H
+
+/* Must be able to hold ALTER TABLE t PARTITION BY ... KEY ALGORITHM = 1 ... */
+#define SQL_ADMIN_MSG_TEXT_SIZE 128 * 1024
+
+bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* table_list,
+ LEX_STRING *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);
+
+/**
+ Analyze_statement represents the ANALYZE TABLE statement.
+*/
+class Analyze_table_statement : public Sql_statement
+{
+public:
+ /**
+ Constructor, used to represent a ANALYZE TABLE statement.
+ @param lex the LEX structure for this statement.
+ */
+ Analyze_table_statement(LEX *lex)
+ : Sql_statement(lex)
+ {}
+
+ ~Analyze_table_statement()
+ {}
+
+ /**
+ Execute a ANALYZE TABLE statement at runtime.
+ @param thd the current thread.
+ @return false on success.
+ */
+ bool execute(THD *thd);
+};
+
+
+
+/**
+ Check_table_statement represents the CHECK TABLE statement.
+*/
+class Check_table_statement : public Sql_statement
+{
+public:
+ /**
+ Constructor, used to represent a CHECK TABLE statement.
+ @param lex the LEX structure for this statement.
+ */
+ Check_table_statement(LEX *lex)
+ : Sql_statement(lex)
+ {}
+
+ ~Check_table_statement()
+ {}
+
+ /**
+ Execute a CHECK TABLE statement at runtime.
+ @param thd the current thread.
+ @return false on success.
+ */
+ bool execute(THD *thd);
+};
+
+
+
+/**
+ Optimize_table_statement represents the OPTIMIZE TABLE statement.
+*/
+class Optimize_table_statement : public Sql_statement
+{
+public:
+ /**
+ Constructor, used to represent a OPTIMIZE TABLE statement.
+ @param lex the LEX structure for this statement.
+ */
+ Optimize_table_statement(LEX *lex)
+ : Sql_statement(lex)
+ {}
+
+ ~Optimize_table_statement()
+ {}
+
+ /**
+ Execute a OPTIMIZE TABLE statement at runtime.
+ @param thd the current thread.
+ @return false on success.
+ */
+ bool execute(THD *thd);
+};
+
+
+
+/**
+ Repair_table_statement represents the REPAIR TABLE statement.
+*/
+class Repair_table_statement : public Sql_statement
+{
+public:
+ /**
+ Constructor, used to represent a REPAIR TABLE statement.
+ @param lex the LEX structure for this statement.
+ */
+ Repair_table_statement(LEX *lex)
+ : Sql_statement(lex)
+ {}
+
+ ~Repair_table_statement()
+ {}
+
+ /**
+ Execute a REPAIR TABLE statement at runtime.
+ @param thd the current thread.
+ @return false on success.
+ */
+ bool execute(THD *thd);
+};
+
+#endif
diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc
new file mode 100644
index 00000000000..c6c02773286
--- /dev/null
+++ b/sql/sql_alter.cc
@@ -0,0 +1,109 @@
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "sql_parse.h" // check_access
+#include "sql_table.h" // mysql_alter_table,
+ // mysql_exchange_partition
+#include "sql_alter.h"
+
+bool Alter_table_statement::execute(THD *thd)
+{
+ LEX *lex= thd->lex;
+ /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
+ SELECT_LEX *select_lex= &lex->select_lex;
+ /* first table of first SELECT_LEX */
+ TABLE_LIST *first_table= (TABLE_LIST*) select_lex->table_list.first;
+ /*
+ Code in mysql_alter_table() may modify its HA_CREATE_INFO argument,
+ so we have to use a copy of this structure to make execution
+ prepared statement- safe. A shallow copy is enough as no memory
+ referenced from this structure will be modified.
+ @todo move these into constructor...
+ */
+ HA_CREATE_INFO create_info(lex->create_info);
+ Alter_info alter_info(lex->alter_info, thd->mem_root);
+ ulong priv=0;
+ ulong priv_needed= ALTER_ACL;
+ bool result;
+
+ DBUG_ENTER("Alter_table_statement::execute");
+
+ if (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_DROP_PARTITION | ALTER_RENAME))
+ priv_needed|= DROP_ACL;
+
+ /* Must be set in the parser */
+ DBUG_ASSERT(select_lex->db);
+ DBUG_ASSERT(!(alter_info.flags & ALTER_ADMIN_PARTITION));
+ if (check_access(thd, priv_needed, first_table->db,
+ &first_table->grant.privilege,
+ &first_table->grant.m_internal,
+ 0, 0) ||
+ check_access(thd, INSERT_ACL | CREATE_ACL, select_lex->db,
+ &priv,
+ NULL, /* Don't use first_tab->grant with sel_lex->db */
+ 0, 0))
+ DBUG_RETURN(TRUE); /* purecov: inspected */
+
+ /* If it is a merge table, check privileges for merge children. */
+ if (create_info.merge_list.first &&
+ check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
+ create_info.merge_list.first, FALSE, UINT_MAX, FALSE))
+ DBUG_RETURN(TRUE);
+
+ if (check_grant(thd, priv_needed, first_table, FALSE, UINT_MAX, FALSE))
+ DBUG_RETURN(TRUE); /* purecov: inspected */
+
+ if (lex->name.str && !test_all_bits(priv, INSERT_ACL | CREATE_ACL))
+ {
+ // Rename of table
+ TABLE_LIST tmp_table;
+ bzero((char*) &tmp_table,sizeof(tmp_table));
+ tmp_table.table_name= lex->name.str;
+ tmp_table.db= select_lex->db;
+ tmp_table.grant.privilege= priv;
+ if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, FALSE,
+ UINT_MAX, FALSE))
+ DBUG_RETURN(TRUE); /* purecov: inspected */
+ }
+
+ /* Don't yet allow changing of symlinks with ALTER TABLE */
+ if (create_info.data_file_name)
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
+ "DATA DIRECTORY");
+ if (create_info.index_file_name)
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
+ "INDEX DIRECTORY");
+ create_info.data_file_name= create_info.index_file_name= NULL;
+
+ thd->enable_slow_log= opt_log_slow_admin_statements;
+
+ result= mysql_alter_table(thd, select_lex->db, lex->name.str,
+ &create_info,
+ first_table,
+ &alter_info,
+ select_lex->order_list.elements,
+ select_lex->order_list.first,
+ lex->ignore, lex->online);
+
+ DBUG_RETURN(result);
+}
diff --git a/sql/sql_alter.h b/sql/sql_alter.h
new file mode 100644
index 00000000000..6660748f666
--- /dev/null
+++ b/sql/sql_alter.h
@@ -0,0 +1,66 @@
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_ALTER_TABLE_H
+#define SQL_ALTER_TABLE_H
+
+/**
+ Alter_table_common represents the common properties of the ALTER TABLE
+ statements.
+ @todo move Alter_info and other ALTER generic structures from Lex here.
+*/
+class Alter_table_common : public Sql_statement
+{
+protected:
+ /**
+ Constructor.
+ @param lex the LEX structure for this statement.
+ */
+ Alter_table_common(LEX *lex)
+ : Sql_statement(lex)
+ {}
+
+ virtual ~Alter_table_common()
+ {}
+
+};
+
+/**
+ Alter_table_statement represents the generic ALTER TABLE statement.
+ @todo move Alter_info and other ALTER specific structures from Lex here.
+*/
+class Alter_table_statement : public Alter_table_common
+{
+public:
+ /**
+ Constructor, used to represent a ALTER TABLE statement.
+ @param lex the LEX structure for this statement.
+ */
+ Alter_table_statement(LEX *lex)
+ : Alter_table_common(lex)
+ {}
+
+ ~Alter_table_statement()
+ {}
+
+ /**
+ Execute a ALTER TABLE statement at runtime.
+ @param thd the current thread.
+ @return false on success.
+ */
+ bool execute(THD *thd);
+};
+
+#endif
diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc
index f345096b3be..36026a39616 100644
--- a/sql/sql_analyse.cc
+++ b/sql/sql_analyse.cc
@@ -12,8 +12,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
/* Analyse database */
@@ -30,7 +29,7 @@
#define MYSQL_LEX 1
-#include "mysql_priv.h"
+#include "sql_priv.h"
#include "procedure.h"
#include "sql_analyse.h"
#include <m_ctype.h>
@@ -411,7 +410,7 @@ void field_real::add()
if ((decs = decimals()) == NOT_FIXED_DEC)
{
- length= my_sprintf(buff, (buff, "%g", num));
+ length= sprintf(buff, "%g", num);
if (rint(num) != num)
max_notzero_dec_len = 1;
}
@@ -422,7 +421,7 @@ void field_real::add()
snprintf(buff, sizeof(buff)-1, "%-.*f", (int) decs, num);
length = (uint) strlen(buff);
#else
- length= my_sprintf(buff, (buff, "%-.*f", (int) decs, num));
+ length= sprintf(buff, "%-.*f", (int) decs, num);
#endif
// We never need to check further than this
@@ -1004,9 +1003,9 @@ void field_decimal::get_opt_type(String *answer,
my_decimal_set_zero(&zero);
my_bool is_unsigned= (my_decimal_cmp(&zero, &min_arg) >= 0);
- length= my_sprintf(buff, (buff, "DECIMAL(%d, %d)",
- (int) (max_length - (item->decimals ? 1 : 0)),
- item->decimals));
+ length= sprintf(buff, "DECIMAL(%d, %d)",
+ (int) (max_length - (item->decimals ? 1 : 0)),
+ item->decimals);
if (is_unsigned)
length= (uint) (strmov(buff+length, " UNSIGNED")- buff);
answer->append(buff, length);
diff --git a/sql/sql_analyse.h b/sql/sql_analyse.h
index 7616002cd44..34c8e2da3d4 100644
--- a/sql/sql_analyse.h
+++ b/sql/sql_analyse.h
@@ -1,4 +1,7 @@
-/* Copyright (c) 2000-2003, 2005-2008 MySQL AB
+#ifndef SQL_ANALYSE_INCLUDED
+#define SQL_ANALYSE_INCLUDED
+
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,7 +14,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* Analyse database */
@@ -20,6 +23,8 @@
#pragma interface /* gcc class implementation */
#endif
+#include "procedure.h" /* Procedure */
+
#define my_thd_charset default_charset_info
#define DEC_IN_AVG 4
@@ -355,3 +360,7 @@ public:
select_result *result,
List<Item> &field_list);
};
+
+bool append_escaped(String *to_str, String *from_str);
+
+#endif /* SQL_ANALYSE_INCLUDED */
diff --git a/sql/sql_array.h b/sql/sql_array.h
index 2f6957496dd..67f1f1c2ad7 100644
--- a/sql/sql_array.h
+++ b/sql/sql_array.h
@@ -1,4 +1,8 @@
-/* Copyright (c) 2003, 2005, 2007 MySQL AB
+#ifndef SQL_ARRAY_INCLUDED
+#define SQL_ARRAY_INCLUDED
+
+/* Copyright (c) 2003, 2005-2007 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
@@ -11,7 +15,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include <my_sys.h>
@@ -66,7 +70,6 @@ public:
}
};
-
/*
Array of pointers to Elem that uses memory from MEM_ROOT
@@ -137,4 +140,4 @@ public:
}
};
-
+#endif /* SQL_ARRAY_INCLUDED */
diff --git a/sql/sql_audit.cc b/sql/sql_audit.cc
new file mode 100644
index 00000000000..108b74cac5f
--- /dev/null
+++ b/sql/sql_audit.cc
@@ -0,0 +1,584 @@
+/* Copyright (c) 2007, 2013, 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 "sql_priv.h"
+#include "sql_audit.h"
+
+extern int initialize_audit_plugin(st_plugin_int *plugin);
+extern int finalize_audit_plugin(st_plugin_int *plugin);
+
+#ifndef EMBEDDED_LIBRARY
+
+struct st_mysql_event_generic
+{
+ unsigned int event_class;
+ const void *event;
+};
+
+unsigned long mysql_global_audit_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
+
+static mysql_mutex_t LOCK_audit_mask;
+
+static void event_class_dispatch(THD *, unsigned int, const void *);
+
+
+static inline
+void set_audit_mask(unsigned long *mask, uint event_class)
+{
+ mask[0]= 1;
+ mask[0]<<= event_class;
+}
+
+static inline
+void add_audit_mask(unsigned long *mask, const unsigned long *rhs)
+{
+ mask[0]|= rhs[0];
+}
+
+static inline
+bool check_audit_mask(const unsigned long *lhs,
+ const unsigned long *rhs)
+{
+ return !(lhs[0] & rhs[0]);
+}
+
+
+typedef void (*audit_handler_t)(THD *thd, uint event_subtype, va_list ap);
+
+/**
+ MYSQL_AUDIT_GENERAL_CLASS handler
+
+ @param[in] thd
+ @param[in] event_subtype
+ @param[in] error_code
+ @param[in] ap
+
+*/
+
+static void general_class_handler(THD *thd, uint event_subtype, va_list ap)
+{
+ mysql_event_general event;
+ event.event_subclass= event_subtype;
+ event.general_error_code= va_arg(ap, int);
+ event.general_thread_id= thd ? thd->thread_id : 0;
+ event.general_time= va_arg(ap, time_t);
+ event.general_user= va_arg(ap, const char *);
+ event.general_user_length= va_arg(ap, unsigned int);
+ event.general_command= va_arg(ap, const char *);
+ event.general_command_length= va_arg(ap, unsigned int);
+ event.general_query= va_arg(ap, const char *);
+ event.general_query_length= va_arg(ap, unsigned int);
+ event.general_charset= va_arg(ap, struct charset_info_st *);
+ event.general_rows= (unsigned long long) va_arg(ap, ha_rows);
+ event.database= va_arg(ap, const char *);
+ event.database_length= va_arg(ap, unsigned int);
+ event.query_id= (unsigned long long) (thd ? thd->query_id : 0);
+ event_class_dispatch(thd, MYSQL_AUDIT_GENERAL_CLASS, &event);
+}
+
+
+static void connection_class_handler(THD *thd, uint event_subclass, va_list ap)
+{
+ mysql_event_connection event;
+ event.event_subclass= event_subclass;
+ event.status= va_arg(ap, int);
+ event.thread_id= va_arg(ap, unsigned long);
+ event.user= va_arg(ap, const char *);
+ event.user_length= va_arg(ap, unsigned int);
+ event.priv_user= va_arg(ap, const char *);
+ event.priv_user_length= va_arg(ap, unsigned int);
+ event.external_user= va_arg(ap, const char *);
+ event.external_user_length= va_arg(ap, unsigned int);
+ event.proxy_user= va_arg(ap, const char *);
+ event.proxy_user_length= va_arg(ap, unsigned int);
+ event.host= va_arg(ap, const char *);
+ event.host_length= va_arg(ap, unsigned int);
+ event.ip= va_arg(ap, const char *);
+ event.ip_length= va_arg(ap, unsigned int);
+ event.database= va_arg(ap, const char *);
+ event.database_length= va_arg(ap, unsigned int);
+ event_class_dispatch(thd, MYSQL_AUDIT_CONNECTION_CLASS, &event);
+}
+
+
+static void table_class_handler(THD *thd, uint event_subclass, va_list ap)
+{
+ mysql_event_table event;
+ event.event_subclass= event_subclass;
+ event.read_only= va_arg(ap, int);
+ event.thread_id= va_arg(ap, unsigned long);
+ event.user= va_arg(ap, const char *);
+ event.priv_user= va_arg(ap, const char *);
+ event.priv_host= va_arg(ap, const char *);
+ event.external_user= va_arg(ap, const char *);
+ event.proxy_user= va_arg(ap, const char *);
+ event.host= va_arg(ap, const char *);
+ event.ip= va_arg(ap, const char *);
+ event.database= va_arg(ap, const char *);
+ event.database_length= va_arg(ap, unsigned int);
+ event.table= va_arg(ap, const char *);
+ event.table_length= va_arg(ap, unsigned int);
+ event.new_database= va_arg(ap, const char *);
+ event.new_database_length= va_arg(ap, unsigned int);
+ event.new_table= va_arg(ap, const char *);
+ event.new_table_length= va_arg(ap, unsigned int);
+ event.query_id= (unsigned long long) (thd ? thd->query_id : 0);
+ event_class_dispatch(thd, MYSQL_AUDIT_TABLE_CLASS, &event);
+}
+
+
+static audit_handler_t audit_handlers[] =
+{
+ general_class_handler, connection_class_handler,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0, /* placeholders */
+ table_class_handler
+};
+
+static const uint audit_handlers_count=
+ (sizeof(audit_handlers) / sizeof(audit_handler_t));
+
+
+/**
+ Acquire and lock any additional audit plugins as required
+
+ @param[in] thd
+ @param[in] plugin
+ @param[in] arg
+
+ @retval FALSE Always
+*/
+
+static my_bool acquire_plugins(THD *thd, plugin_ref plugin, void *arg)
+{
+ ulong *event_class_mask= (ulong*) arg;
+ st_mysql_audit *data= plugin_data(plugin, struct st_mysql_audit *);
+
+ /* Check if this plugin is interested in the event */
+ if (check_audit_mask(data->class_mask, event_class_mask))
+ return 0;
+
+ /*
+ Check if this plugin may already be registered. This will fail to
+ acquire a newly installed plugin on a specific corner case where
+ one or more event classes already in use by the calling thread
+ are an event class of which the audit plugin has interest.
+ */
+ if (!check_audit_mask(data->class_mask, thd->audit_class_mask))
+ return 0;
+
+ /* Check if we need to initialize the array of acquired plugins */
+ if (unlikely(!thd->audit_class_plugins.buffer))
+ {
+ /* specify some reasonable initialization defaults */
+ my_init_dynamic_array(&thd->audit_class_plugins,
+ sizeof(plugin_ref), 16, 16);
+ }
+
+ /* lock the plugin and add it to the list */
+ plugin= my_plugin_lock(NULL, plugin);
+ insert_dynamic(&thd->audit_class_plugins, (uchar*) &plugin);
+
+ return 0;
+}
+
+
+/**
+ @brief Acquire audit plugins
+
+ @param[in] thd MySQL thread handle
+ @param[in] event_class Audit event class
+
+ @details Ensure that audit plugins interested in given event
+ class are locked by current thread.
+*/
+void mysql_audit_acquire_plugins(THD *thd, ulong *event_class_mask)
+{
+ DBUG_ENTER("mysql_audit_acquire_plugins");
+ if (thd && !check_audit_mask(mysql_global_audit_mask, event_class_mask) &&
+ check_audit_mask(thd->audit_class_mask, event_class_mask))
+ {
+ plugin_foreach(thd, acquire_plugins, MYSQL_AUDIT_PLUGIN, event_class_mask);
+ add_audit_mask(thd->audit_class_mask, event_class_mask);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Notify the audit system of an event
+
+ @param[in] thd
+ @param[in] event_class
+ @param[in] event_subtype
+ @param[in] error_code
+
+*/
+
+void mysql_audit_notify(THD *thd, uint event_class, uint event_subtype, ...)
+{
+ va_list ap;
+ audit_handler_t *handlers= audit_handlers + event_class;
+ DBUG_ASSERT(event_class < audit_handlers_count);
+ unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
+ set_audit_mask(event_class_mask, event_class);
+ mysql_audit_acquire_plugins(thd, event_class_mask);
+ va_start(ap, event_subtype);
+ (*handlers)(thd, event_subtype, ap);
+ va_end(ap);
+}
+
+
+/**
+ Release any resources associated with the current thd.
+
+ @param[in] thd
+
+*/
+
+void mysql_audit_release(THD *thd)
+{
+ plugin_ref *plugins, *plugins_last;
+
+ if (!thd || !(thd->audit_class_plugins.elements))
+ return;
+
+ plugins= (plugin_ref*) thd->audit_class_plugins.buffer;
+ plugins_last= plugins + thd->audit_class_plugins.elements;
+ for (; plugins < plugins_last; plugins++)
+ {
+ st_mysql_audit *data= plugin_data(*plugins, struct st_mysql_audit *);
+
+ /* Check to see if the plugin has a release method */
+ if (!(data->release_thd))
+ continue;
+
+ /* Tell the plugin to release its resources */
+ data->release_thd(thd);
+ }
+
+ /* Now we actually unlock the plugins */
+ plugin_unlock_list(NULL, (plugin_ref*) thd->audit_class_plugins.buffer,
+ thd->audit_class_plugins.elements);
+
+ /* Reset the state of thread values */
+ reset_dynamic(&thd->audit_class_plugins);
+ bzero(thd->audit_class_mask, sizeof(thd->audit_class_mask));
+}
+
+
+/**
+ Initialize thd variables used by Audit
+
+ @param[in] thd
+
+*/
+
+void mysql_audit_init_thd(THD *thd)
+{
+ bzero(&thd->audit_class_plugins, sizeof(thd->audit_class_plugins));
+ bzero(thd->audit_class_mask, sizeof(thd->audit_class_mask));
+}
+
+
+/**
+ Free thd variables used by Audit
+
+ @param[in] thd
+ @param[in] plugin
+ @param[in] arg
+
+ @retval FALSE Always
+*/
+
+void mysql_audit_free_thd(THD *thd)
+{
+ mysql_audit_release(thd);
+ DBUG_ASSERT(thd->audit_class_plugins.elements == 0);
+ delete_dynamic(&thd->audit_class_plugins);
+}
+
+#ifdef HAVE_PSI_INTERFACE
+static PSI_mutex_key key_LOCK_audit_mask;
+
+static PSI_mutex_info all_audit_mutexes[]=
+{
+ { &key_LOCK_audit_mask, "LOCK_audit_mask", PSI_FLAG_GLOBAL}
+};
+
+static void init_audit_psi_keys(void)
+{
+ const char* category= "sql";
+ int count;
+
+ if (PSI_server == NULL)
+ return;
+
+ count= array_elements(all_audit_mutexes);
+ PSI_server->register_mutex(category, all_audit_mutexes, count);
+}
+#endif /* HAVE_PSI_INTERFACE */
+
+/**
+ Initialize Audit global variables
+*/
+
+void mysql_audit_initialize()
+{
+#ifdef HAVE_PSI_INTERFACE
+ init_audit_psi_keys();
+#endif
+
+ mysql_mutex_init(key_LOCK_audit_mask, &LOCK_audit_mask, MY_MUTEX_INIT_FAST);
+ bzero(mysql_global_audit_mask, sizeof(mysql_global_audit_mask));
+}
+
+
+/**
+ Finalize Audit global variables
+*/
+
+void mysql_audit_finalize()
+{
+ mysql_mutex_destroy(&LOCK_audit_mask);
+}
+
+
+/**
+ Initialize an Audit plug-in
+
+ @param[in] plugin
+
+ @retval FALSE OK
+ @retval TRUE There was an error.
+*/
+
+int initialize_audit_plugin(st_plugin_int *plugin)
+{
+ st_mysql_audit *data= (st_mysql_audit*) plugin->plugin->info;
+
+ if (!data->class_mask || !data->event_notify ||
+ !data->class_mask[0])
+ {
+ sql_print_error("Plugin '%s' has invalid data.",
+ plugin->name.str);
+ return 1;
+ }
+
+ if (plugin->plugin->init && plugin->plugin->init(NULL))
+ {
+ sql_print_error("Plugin '%s' init function returned error.",
+ plugin->name.str);
+ return 1;
+ }
+
+ /* Make the interface info more easily accessible */
+ plugin->data= plugin->plugin->info;
+
+ /* Add the bits the plugin is interested in to the global mask */
+ mysql_mutex_lock(&LOCK_audit_mask);
+ add_audit_mask(mysql_global_audit_mask, data->class_mask);
+ mysql_mutex_unlock(&LOCK_audit_mask);
+
+ /*
+ Pre-acquire the newly inslalled audit plugin for events that
+ may potentially occur further during INSTALL PLUGIN.
+
+ When audit event is triggered, audit subsystem acquires interested
+ plugins by walking through plugin list. Evidently plugin list
+ iterator protects plugin list by acquiring LOCK_plugin, see
+ plugin_foreach_with_mask().
+
+ On the other hand [UN]INSTALL PLUGIN is acquiring LOCK_plugin
+ rather for a long time.
+
+ When audit event is triggered during [UN]INSTALL PLUGIN, plugin
+ list iterator acquires the same lock (within the same thread)
+ second time.
+
+ This hack should be removed when LOCK_plugin is fixed so it
+ protects only what it supposed to protect.
+
+ See also mysql_install_plugin() and mysql_uninstall_plugin()
+ */
+ THD *thd= current_thd;
+ if (thd)
+ {
+ acquire_plugins(thd, plugin_int_to_ref(plugin), data->class_mask);
+ add_audit_mask(thd->audit_class_mask, data->class_mask);
+ }
+
+ return 0;
+}
+
+
+/**
+ Performs a bitwise OR of the installed plugins event class masks
+
+ @param[in] thd
+ @param[in] plugin
+ @param[in] arg
+
+ @retval FALSE always
+*/
+static my_bool calc_class_mask(THD *thd, plugin_ref plugin, void *arg)
+{
+ st_mysql_audit *data= plugin_data(plugin, struct st_mysql_audit *);
+ if ((data= plugin_data(plugin, struct st_mysql_audit *)))
+ add_audit_mask((unsigned long *) arg, data->class_mask);
+ return 0;
+}
+
+
+/**
+ Finalize an Audit plug-in
+
+ @param[in] plugin
+
+ @retval FALSE OK
+ @retval TRUE There was an error.
+*/
+int finalize_audit_plugin(st_plugin_int *plugin)
+{
+ unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
+
+ if (plugin->plugin->deinit && plugin->plugin->deinit(NULL))
+ {
+ DBUG_PRINT("warning", ("Plugin '%s' deinit function returned error.",
+ plugin->name.str));
+ DBUG_EXECUTE("finalize_audit_plugin", return 1; );
+ }
+
+ plugin->data= NULL;
+ bzero(&event_class_mask, sizeof(event_class_mask));
+
+ /* Iterate through all the installed plugins to create new mask */
+
+ /*
+ LOCK_audit_mask/LOCK_plugin order is not fixed, but serialized with table
+ lock on mysql.plugin.
+ */
+ mysql_mutex_lock(&LOCK_audit_mask);
+ plugin_foreach(current_thd, calc_class_mask, MYSQL_AUDIT_PLUGIN,
+ &event_class_mask);
+
+ /* Set the global audit mask */
+ bmove(mysql_global_audit_mask, event_class_mask, sizeof(event_class_mask));
+ mysql_mutex_unlock(&LOCK_audit_mask);
+
+ return 0;
+}
+
+
+/**
+ Dispatches an event by invoking the plugin's event_notify method.
+
+ @param[in] thd
+ @param[in] plugin
+ @param[in] arg
+
+ @retval FALSE always
+*/
+
+static my_bool plugins_dispatch(THD *thd, plugin_ref plugin, void *arg)
+{
+ const struct st_mysql_event_generic *event_generic=
+ (const struct st_mysql_event_generic *) arg;
+ unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
+ st_mysql_audit *data= plugin_data(plugin, struct st_mysql_audit *);
+
+ set_audit_mask(event_class_mask, event_generic->event_class);
+
+ /* Check to see if the plugin is interested in this event */
+ if (check_audit_mask(data->class_mask, event_class_mask))
+ return 0;
+
+ /* Actually notify the plugin */
+ data->event_notify(thd, event_generic->event_class, event_generic->event);
+
+ return 0;
+}
+
+
+/**
+ Distributes an audit event to plug-ins
+
+ @param[in] thd
+ @param[in] event
+*/
+
+static void event_class_dispatch(THD *thd, unsigned int event_class,
+ const void *event)
+{
+ struct st_mysql_event_generic event_generic;
+ event_generic.event_class= event_class;
+ event_generic.event= event;
+ /*
+ Check if we are doing a slow global dispatch. This event occurs when
+ thd == NULL as it is not associated with any particular thread.
+ */
+ if (unlikely(!thd))
+ {
+ plugin_foreach(thd, plugins_dispatch, MYSQL_AUDIT_PLUGIN, &event_generic);
+ }
+ else
+ {
+ plugin_ref *plugins, *plugins_last;
+
+ /* Use the cached set of audit plugins */
+ plugins= (plugin_ref*) thd->audit_class_plugins.buffer;
+ plugins_last= plugins + thd->audit_class_plugins.elements;
+
+ for (; plugins < plugins_last; plugins++)
+ plugins_dispatch(thd, *plugins, &event_generic);
+ }
+}
+
+
+#else /* EMBEDDED_LIBRARY */
+
+
+void mysql_audit_acquire_plugins(THD *thd, ulong *event_class_mask)
+{
+}
+
+
+void mysql_audit_initialize()
+{
+}
+
+
+void mysql_audit_finalize()
+{
+}
+
+
+int initialize_audit_plugin(st_plugin_int *plugin)
+{
+ return 1;
+}
+
+
+int finalize_audit_plugin(st_plugin_int *plugin)
+{
+ return 0;
+}
+
+
+void mysql_audit_release(THD *thd)
+{
+}
+
+
+#endif /* EMBEDDED_LIBRARY */
diff --git a/sql/sql_audit.h b/sql/sql_audit.h
new file mode 100644
index 00000000000..3f3f97a2730
--- /dev/null
+++ b/sql/sql_audit.h
@@ -0,0 +1,280 @@
+#ifndef SQL_AUDIT_INCLUDED
+#define SQL_AUDIT_INCLUDED
+
+/* Copyright (c) 2007, 2013, 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 <mysql/plugin_audit.h>
+#include "sql_class.h"
+
+extern unsigned long mysql_global_audit_mask[];
+
+
+extern void mysql_audit_initialize();
+extern void mysql_audit_finalize();
+
+
+extern void mysql_audit_init_thd(THD *thd);
+extern void mysql_audit_free_thd(THD *thd);
+extern void mysql_audit_acquire_plugins(THD *thd, ulong *event_class_mask);
+
+
+#ifndef EMBEDDED_LIBRARY
+extern void mysql_audit_notify(THD *thd, uint event_class,
+ uint event_subtype, ...);
+
+static inline bool mysql_audit_general_enabled()
+{
+ return mysql_global_audit_mask[0] & MYSQL_AUDIT_GENERAL_CLASSMASK;
+}
+
+static inline bool mysql_audit_table_enabled()
+{
+ return mysql_global_audit_mask[0] & MYSQL_AUDIT_TABLE_CLASSMASK;
+}
+
+#else
+static inline void mysql_audit_notify(THD *thd, uint event_class,
+ uint event_subtype, ...) { }
+#define mysql_audit_general_enabled() 0
+#define mysql_audit_table_enabled() 0
+#endif
+extern void mysql_audit_release(THD *thd);
+
+#define MAX_USER_HOST_SIZE 512
+static inline uint make_user_name(THD *thd, char *buf)
+{
+ const Security_context *sctx= thd->security_ctx;
+ return strxnmov(buf, MAX_USER_HOST_SIZE,
+ sctx->priv_user[0] ? sctx->priv_user : "", "[",
+ sctx->user ? sctx->user : "", "] @ ",
+ sctx->host ? sctx->host : "", " [",
+ sctx->ip ? sctx->ip : "", "]", NullS) - buf;
+}
+
+/**
+ Call audit plugins of GENERAL audit class, MYSQL_AUDIT_GENERAL_LOG subtype.
+
+ @param[in] thd
+ @param[in] time time that event occurred
+ @param[in] user User name
+ @param[in] userlen User name length
+ @param[in] cmd Command name
+ @param[in] cmdlen Command name length
+ @param[in] query Query string
+ @param[in] querylen Query string length
+*/
+
+static inline
+void mysql_audit_general_log(THD *thd, time_t time,
+ const char *user, uint userlen,
+ const char *cmd, uint cmdlen,
+ const char *query, uint querylen)
+{
+ if (mysql_audit_general_enabled())
+ {
+ CHARSET_INFO *clientcs= thd ? thd->variables.character_set_client
+ : global_system_variables.character_set_client;
+ const char *db= thd ? thd->db : "";
+ size_t db_length= thd ? thd->db_length : 0;
+
+ mysql_audit_notify(thd, MYSQL_AUDIT_GENERAL_CLASS, MYSQL_AUDIT_GENERAL_LOG,
+ 0, time, user, userlen, cmd, cmdlen,
+ query, querylen, clientcs, (ha_rows) 0,
+ db, db_length);
+ }
+}
+
+/**
+ Call audit plugins of GENERAL audit class.
+ event_subtype should be set to one of:
+ MYSQL_AUDIT_GENERAL_ERROR
+ MYSQL_AUDIT_GENERAL_RESULT
+ MYSQL_AUDIT_GENERAL_STATUS
+
+ @param[in] thd
+ @param[in] event_subtype Type of general audit event.
+ @param[in] error_code Error code
+ @param[in] msg Message
+*/
+static inline
+void mysql_audit_general(THD *thd, uint event_subtype,
+ int error_code, const char *msg)
+{
+ if (mysql_audit_general_enabled())
+ {
+ time_t time= my_time(0);
+ uint msglen= msg ? strlen(msg) : 0;
+ const char *user;
+ uint userlen;
+ char user_buff[MAX_USER_HOST_SIZE];
+ CSET_STRING query;
+ ha_rows rows;
+ const char *db;
+ size_t db_length;
+
+ if (thd)
+ {
+ query= thd->query_string;
+ user= user_buff;
+ userlen= make_user_name(thd, user_buff);
+ rows= thd->warning_info->current_row_for_warning();
+ db= thd->db;
+ db_length= thd->db_length;
+ }
+ else
+ {
+ user= 0;
+ userlen= 0;
+ rows= 0;
+ db= "";
+ db_length= 0;
+ }
+
+ mysql_audit_notify(thd, MYSQL_AUDIT_GENERAL_CLASS, event_subtype,
+ error_code, time, user, userlen, msg, msglen,
+ query.str(), query.length(), query.charset(), rows,
+ db, db_length);
+ }
+}
+
+#define MYSQL_AUDIT_NOTIFY_CONNECTION_CONNECT(thd) mysql_audit_notify(\
+ (thd), MYSQL_AUDIT_CONNECTION_CLASS, MYSQL_AUDIT_CONNECTION_CONNECT,\
+ (thd)->stmt_da->is_error() ? (thd)->stmt_da->sql_errno() : 0,\
+ (thd)->thread_id, (thd)->security_ctx->user,\
+ (thd)->security_ctx->user ? strlen((thd)->security_ctx->user) : 0,\
+ (thd)->security_ctx->priv_user, strlen((thd)->security_ctx->priv_user),\
+ (thd)->security_ctx->external_user,\
+ (thd)->security_ctx->external_user ?\
+ strlen((thd)->security_ctx->external_user) : 0,\
+ (thd)->security_ctx->proxy_user, strlen((thd)->security_ctx->proxy_user),\
+ (thd)->security_ctx->host,\
+ (thd)->security_ctx->host ? strlen((thd)->security_ctx->host) : 0,\
+ (thd)->security_ctx->ip,\
+ (thd)->security_ctx->ip ? strlen((thd)->security_ctx->ip) : 0,\
+ (thd)->db, (thd)->db ? strlen((thd)->db) : 0)
+
+#define MYSQL_AUDIT_NOTIFY_CONNECTION_DISCONNECT(thd, errcode)\
+ mysql_audit_notify(\
+ (thd), MYSQL_AUDIT_CONNECTION_CLASS, MYSQL_AUDIT_CONNECTION_DISCONNECT,\
+ (errcode), (thd)->thread_id, (thd)->security_ctx->user,\
+ (thd)->security_ctx->user ? strlen((thd)->security_ctx->user) : 0,\
+ 0, 0, 0, 0, 0, 0, (thd)->security_ctx->host,\
+ (thd)->security_ctx->host ? strlen((thd)->security_ctx->host) : 0,\
+ 0, 0, 0, 0)
+
+#define MYSQL_AUDIT_NOTIFY_CONNECTION_CHANGE_USER(thd) mysql_audit_notify(\
+ (thd), MYSQL_AUDIT_CONNECTION_CLASS, MYSQL_AUDIT_CONNECTION_CHANGE_USER,\
+ (thd)->stmt_da->is_error() ? (thd)->stmt_da->sql_errno() : 0,\
+ (thd)->thread_id, (thd)->security_ctx->user,\
+ (thd)->security_ctx->user ? strlen((thd)->security_ctx->user) : 0,\
+ (thd)->security_ctx->priv_user, strlen((thd)->security_ctx->priv_user),\
+ (thd)->security_ctx->external_user,\
+ (thd)->security_ctx->external_user ?\
+ strlen((thd)->security_ctx->external_user) : 0,\
+ (thd)->security_ctx->proxy_user, strlen((thd)->security_ctx->proxy_user),\
+ (thd)->security_ctx->host,\
+ (thd)->security_ctx->host ? strlen((thd)->security_ctx->host) : 0,\
+ (thd)->security_ctx->ip,\
+ (thd)->security_ctx->ip ? strlen((thd)->security_ctx->ip) : 0,\
+ (thd)->db, (thd)->db ? strlen((thd)->db) : 0)
+
+static inline
+void mysql_audit_external_lock(THD *thd, TABLE_SHARE *share, int lock)
+{
+ if (lock != F_UNLCK && mysql_audit_table_enabled())
+ {
+ const Security_context *sctx= thd->security_ctx;
+ mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, MYSQL_AUDIT_TABLE_LOCK,
+ (int)(lock == F_RDLCK), (ulong)thd->thread_id,
+ sctx->user, sctx->priv_user, sctx->priv_host,
+ sctx->external_user, sctx->proxy_user, sctx->host,
+ sctx->ip, share->db.str, (uint)share->db.length,
+ share->table_name.str, (uint)share->table_name.length,
+ 0,0,0,0);
+ }
+}
+
+static inline
+void mysql_audit_create_table(TABLE *table)
+{
+ if (mysql_audit_table_enabled())
+ {
+ THD *thd= table->in_use;
+ const TABLE_SHARE *share= table->s;
+ const Security_context *sctx= thd->security_ctx;
+ mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, MYSQL_AUDIT_TABLE_CREATE,
+ 0, (ulong)thd->thread_id,
+ sctx->user, sctx->priv_user, sctx->priv_host,
+ sctx->external_user, sctx->proxy_user, sctx->host,
+ sctx->ip, share->db.str, (uint)share->db.length,
+ share->table_name.str, (uint)share->table_name.length,
+ 0,0,0,0);
+ }
+}
+
+static inline
+void mysql_audit_drop_table(THD *thd, TABLE_LIST *table)
+{
+ if (mysql_audit_table_enabled())
+ {
+ const Security_context *sctx= thd->security_ctx;
+ mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, MYSQL_AUDIT_TABLE_DROP,
+ 0, (ulong)thd->thread_id,
+ sctx->user, sctx->priv_user, sctx->priv_host,
+ sctx->external_user, sctx->proxy_user, sctx->host,
+ sctx->ip, table->db, (uint)table->db_length,
+ table->table_name, (uint)table->table_name_length,
+ 0,0,0,0);
+ }
+}
+
+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)
+{
+ if (mysql_audit_table_enabled())
+ {
+ const Security_context *sctx= thd->security_ctx;
+ mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, MYSQL_AUDIT_TABLE_RENAME,
+ 0, (ulong)thd->thread_id,
+ sctx->user, sctx->priv_user, sctx->priv_host,
+ sctx->external_user, sctx->proxy_user, sctx->host,
+ sctx->ip,
+ old_db, (uint)strlen(old_db), old_tb, (uint)strlen(old_tb),
+ new_db, (uint)strlen(new_db), new_tb, (uint)strlen(new_tb));
+ }
+}
+
+static inline
+void mysql_audit_alter_table(THD *thd, TABLE_LIST *table)
+{
+ if (mysql_audit_table_enabled())
+ {
+ const Security_context *sctx= thd->security_ctx;
+ mysql_audit_notify(thd, MYSQL_AUDIT_TABLE_CLASS, MYSQL_AUDIT_TABLE_ALTER,
+ 0, (ulong)thd->thread_id,
+ sctx->user, sctx->priv_user, sctx->priv_host,
+ sctx->external_user, sctx->proxy_user, sctx->host,
+ sctx->ip, table->db, (uint)table->db_length,
+ table->table_name, (uint)table->table_name_length,
+ 0,0,0,0);
+ }
+}
+
+#endif /* SQL_AUDIT_INCLUDED */
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index fe7b8a664b2..90a12eb366d 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1,4 +1,5 @@
-/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2013 Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,56 +17,60 @@
/* Basic functions needed by many modules */
-#include "mysql_priv.h"
+#include "sql_base.h" // setup_table_map
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_priv.h"
+#include "unireg.h"
#include "debug_sync.h"
+#include "lock.h" // mysql_lock_remove,
+ // mysql_unlock_tables,
+ // mysql_lock_have_duplicate
+#include "sql_show.h" // append_identifier
+#include "strfunc.h" // find_type
+#include "parse_file.h" // sql_parse_prepare, File_parser
+#include "sql_view.h" // mysql_make_view, VIEW_ANY_ACL
+#include "sql_parse.h" // check_table_access
+#include "sql_insert.h" // kill_delayed_threads
+#include "sql_acl.h" // *_ACL, check_grant_all_columns,
+ // check_column_grant_in_table_ref,
+ // get_column_grant
+#include "sql_partition.h" // ALTER_PARTITION_PARAM_TYPE
+#include "sql_derived.h" // mysql_derived_prepare,
+ // mysql_handle_derived,
+ // mysql_derived_filling
+#include "sql_handler.h" // mysql_ha_flush
+#include "sql_test.h"
+#include "sql_partition.h" // ALTER_PARTITION_PARAM_TYPE
+#include "log_event.h" // Query_log_event
#include "sql_select.h"
#include "sp_head.h"
#include "sp.h"
+#include "sp_cache.h"
#include "sql_trigger.h"
-#include "sql_handler.h"
+#include "transaction.h"
+#include "sql_prepare.h"
#include <m_ctype.h>
#include <my_dir.h>
#include <hash.h>
#include "rpl_filter.h"
+#include "sql_table.h" // build_table_filename
+#include "datadict.h" // dd_frm_type()
+#include "sql_hset.h" // Hash_set
#ifdef __WIN__
#include <io.h>
#endif
-#define FLAGSTR(S,F) ((S) & (F) ? #F " " : "")
-
-/**
- This internal handler is used to trap internally
- errors that can occur when executing open table
- during the prelocking phase.
-*/
-class Prelock_error_handler : public Internal_error_handler
-{
-public:
- Prelock_error_handler()
- : m_handled_errors(0), m_unhandled_errors(0)
- {}
-
- virtual ~Prelock_error_handler() {}
-
- virtual bool handle_error(uint sql_errno, const char *message,
- MYSQL_ERROR::enum_warning_level level,
- THD *thd);
-
- bool safely_trapped_errors();
-
-private:
- int m_handled_errors;
- int m_unhandled_errors;
-};
-
bool
-Prelock_error_handler::handle_error(uint sql_errno,
- const char * /* message */,
- MYSQL_ERROR::enum_warning_level level,
- THD * /* thd */)
+No_such_table_error_handler::handle_condition(THD *,
+ uint sql_errno,
+ const char*,
+ MYSQL_ERROR::enum_warning_level level,
+ const char*,
+ MYSQL_ERROR ** cond_hdl)
{
- if (sql_errno == ER_NO_SUCH_TABLE)
+ *cond_hdl= NULL;
+ if (sql_errno == ER_NO_SUCH_TABLE || sql_errno == ER_NO_SUCH_TABLE_IN_ENGINE)
{
m_handled_errors++;
return TRUE;
@@ -77,7 +82,7 @@ Prelock_error_handler::handle_error(uint sql_errno,
}
-bool Prelock_error_handler::safely_trapped_errors()
+bool No_such_table_error_handler::safely_trapped_errors()
{
/*
If m_unhandled_errors != 0, something else, unanticipated, happened,
@@ -87,80 +92,150 @@ bool Prelock_error_handler::safely_trapped_errors()
return ((m_handled_errors > 0) && (m_unhandled_errors == 0));
}
+
+/**
+ This internal handler is used to trap ER_NO_SUCH_TABLE and
+ ER_WRONG_MRG_TABLE errors during CHECK/REPAIR TABLE for MERGE
+ tables.
+*/
+
+class Repair_mrg_table_error_handler : public Internal_error_handler
+{
+public:
+ Repair_mrg_table_error_handler()
+ : m_handled_errors(false), m_unhandled_errors(false)
+ {}
+
+ bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl);
+
+ /**
+ Returns TRUE if there were ER_NO_SUCH_/WRONG_MRG_TABLE and there
+ were no unhandled errors. FALSE otherwise.
+ */
+ bool safely_trapped_errors()
+ {
+ /*
+ Check for m_handled_errors is here for extra safety.
+ It can be useful in situation when call to open_table()
+ fails because some error which was suppressed by another
+ error handler (e.g. in case of MDL deadlock which we
+ decided to solve by back-off and retry).
+ */
+ return (m_handled_errors && (! m_unhandled_errors));
+ }
+
+private:
+ bool m_handled_errors;
+ bool m_unhandled_errors;
+};
+
+
+bool
+Repair_mrg_table_error_handler::handle_condition(THD *,
+ uint sql_errno,
+ const char*,
+ MYSQL_ERROR::enum_warning_level level,
+ const char*,
+ MYSQL_ERROR ** cond_hdl)
+{
+ *cond_hdl= NULL;
+ if (sql_errno == ER_NO_SUCH_TABLE ||
+ sql_errno == ER_NO_SUCH_TABLE_IN_ENGINE ||
+ sql_errno == ER_WRONG_MRG_TABLE)
+ {
+ m_handled_errors= true;
+ return TRUE;
+ }
+
+ m_unhandled_errors= true;
+ return FALSE;
+}
+
+
/**
@defgroup Data_Dictionary Data Dictionary
@{
*/
-TABLE *unused_tables; /* Used by mysql_test */
-HASH open_cache; /* Used by mysql_test */
-static HASH table_def_cache;
-static TABLE_SHARE *oldest_unused_share, end_of_unused_share;
-static pthread_mutex_t LOCK_table_share;
-static bool table_def_inited= 0;
/**
- Dummy TABLE instance which is used in reopen_tables() and reattach_merge()
- functions to mark MERGE tables and their children with which there is some
- kind of problem and which therefore we need to close.
+ Protects table_def_hash, used and unused lists in the
+ TABLE_SHARE object, LRU lists of used TABLEs and used
+ TABLE_SHAREs, refresh_version and the table id counter.
*/
-static TABLE bad_merge_marker;
+mysql_mutex_t LOCK_open;
-static int open_unireg_entry(THD *thd, TABLE *entry, TABLE_LIST *table_list,
- const char *alias,
- char *cache_key, uint cache_key_length,
- MEM_ROOT *mem_root, uint flags);
-static void free_cache_entry(TABLE *entry);
-static bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias,
- uint db_stat, uint prgflag,
- uint ha_open_flags, TABLE *outparam,
- TABLE_LIST *table_desc, MEM_ROOT *mem_root);
-static void close_old_data_files(THD *thd, TABLE *table, bool morph_locks,
- bool send_refresh);
-static bool
-has_write_table_with_auto_increment(TABLE_LIST *tables);
-static bool
-has_write_table_auto_increment_not_first_in_pk(TABLE_LIST *tables);
+#ifdef HAVE_PSI_INTERFACE
+static PSI_mutex_key key_LOCK_open;
+static PSI_mutex_info all_tdc_mutexes[]= {
+ { &key_LOCK_open, "LOCK_open", PSI_FLAG_GLOBAL }
+};
+/**
+ Initialize performance schema instrumentation points
+ used by the table cache.
+*/
-extern "C" uchar *table_cache_key(const uchar *record, size_t *length,
- my_bool not_used __attribute__((unused)))
+static void init_tdc_psi_keys(void)
{
- TABLE *entry=(TABLE*) record;
- *length= entry->s->table_cache_key.length;
- return (uchar*) entry->s->table_cache_key.str;
-}
+ const char *category= "sql";
+ int count;
+ if (PSI_server == NULL)
+ return;
-bool table_cache_init(void)
-{
- return hash_init(&open_cache, &my_charset_bin, table_cache_size+16,
- 0, 0, table_cache_key,
- (hash_free_key) free_cache_entry, 0) != 0;
+ count= array_elements(all_tdc_mutexes);
+ PSI_server->register_mutex(category, all_tdc_mutexes, count);
}
+#endif /* HAVE_PSI_INTERFACE */
-void table_cache_free(void)
-{
- DBUG_ENTER("table_cache_free");
- if (table_def_inited)
- {
- close_cached_tables(NULL, NULL, FALSE, FALSE, FALSE);
- if (!open_cache.records) // Safety first
- hash_free(&open_cache);
- }
- DBUG_VOID_RETURN;
-}
+
+/**
+ Total number of TABLE instances for tables in the table definition cache
+ (both in use by threads and not in use). This value is accessible to user
+ as "Open_tables" status variable.
+*/
+uint table_cache_count= 0;
+/**
+ List that contains all TABLE instances for tables in the table definition
+ cache that are not in use by any thread. Recently used TABLE instances are
+ appended to the end of the list. Thus the beginning of the list contains
+ tables which have been least recently used.
+*/
+TABLE *unused_tables;
+HASH table_def_cache;
+static TABLE_SHARE *oldest_unused_share, end_of_unused_share;
+static bool table_def_inited= 0;
+static bool table_def_shutdown_in_progress= 0;
+
+static bool check_and_update_table_version(THD *thd, TABLE_LIST *tables,
+ TABLE_SHARE *table_share);
+static bool open_table_entry_fini(THD *thd, TABLE_SHARE *share, TABLE *entry);
+static bool auto_repair_table(THD *thd, TABLE_LIST *table_list);
+static void free_cache_entry(TABLE *entry);
+static bool
+has_write_table_with_auto_increment(TABLE_LIST *tables);
+static bool
+has_write_table_with_auto_increment_and_select(TABLE_LIST *tables);
+static bool has_write_table_auto_increment_not_first_in_pk(TABLE_LIST *tables);
uint cached_open_tables(void)
{
- return open_cache.records;
+ return table_cache_count;
}
#ifdef EXTRA_DEBUG
-static void check_unused(void)
+static void check_unused(THD *thd)
{
uint count= 0, open_files= 0, idx= 0;
- TABLE *cur_link,*start_link;
+ TABLE *cur_link, *start_link, *entry;
+ TABLE_SHARE *share;
+ DBUG_ENTER("check_unused");
if ((start_link=cur_link=unused_tables))
{
@@ -169,50 +244,58 @@ static void check_unused(void)
if (cur_link != cur_link->next->prev || cur_link != cur_link->prev->next)
{
DBUG_PRINT("error",("Unused_links aren't linked properly")); /* purecov: inspected */
- return; /* purecov: inspected */
+ DBUG_VOID_RETURN; /* purecov: inspected */
}
- } while (count++ < open_cache.records &&
+ } while (count++ < table_cache_count &&
(cur_link=cur_link->next) != start_link);
if (cur_link != start_link)
{
DBUG_PRINT("error",("Unused_links aren't connected")); /* purecov: inspected */
}
}
- for (idx=0 ; idx < open_cache.records ; idx++)
+ for (idx=0 ; idx < table_def_cache.records ; idx++)
{
- TABLE *entry=(TABLE*) hash_element(&open_cache,idx);
- if (!entry->in_use)
+ share= (TABLE_SHARE*) my_hash_element(&table_def_cache, idx);
+
+ I_P_List_iterator<TABLE, TABLE_share> it(share->free_tables);
+ while ((entry= it++))
+ {
+ /*
+ We must not have TABLEs in the free list that have their file closed.
+ */
+ DBUG_ASSERT(entry->db_stat && entry->file);
+ /* Merge children should be detached from a merge parent */
+ if (entry->in_use)
+ {
+ DBUG_PRINT("error",("Used table is in share's list of unused tables")); /* purecov: inspected */
+ }
+ /* extra() may assume that in_use is set */
+ entry->in_use= thd;
+ DBUG_ASSERT(! entry->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
+ entry->in_use= 0;
+
count--;
- if (entry->file)
open_files++;
+ }
+ it.init(share->used_tables);
+ while ((entry= it++))
+ {
+ if (!entry->in_use)
+ {
+ DBUG_PRINT("error",("Unused table is in share's list of used tables")); /* purecov: inspected */
+ }
+ open_files++;
+ }
}
if (count != 0)
{
DBUG_PRINT("error",("Unused_links doesn't match open_cache: diff: %d", /* purecov: inspected */
count)); /* purecov: inspected */
}
-
-#ifdef NOT_SAFE_FOR_REPAIR
- /*
- check that open cache and table definition cache has same number of
- aktive tables
- */
- count= 0;
- for (idx=0 ; idx < table_def_cache.records ; idx++)
- {
- TABLE_SHARE *entry= (TABLE_SHARE*) hash_element(&table_def_cache,idx);
- count+= entry->ref_count;
- }
- if (count != open_files)
- {
- DBUG_PRINT("error", ("table_def ref_count: %u open_cache: %u",
- count, open_files));
- DBUG_ASSERT(count == open_files);
- }
-#endif
+ DBUG_VOID_RETURN;
}
#else
-#define check_unused()
+#define check_unused(A)
#endif
@@ -241,7 +324,8 @@ static void check_unused(void)
Length of key
*/
-uint create_table_def_key(THD *thd, char *key, TABLE_LIST *table_list,
+uint create_table_def_key(THD *thd, char *key,
+ const TABLE_LIST *table_list,
bool tmp_table)
{
uint key_length= create_table_def_key(key, table_list->db,
@@ -274,13 +358,12 @@ extern "C" uchar *table_def_key(const uchar *record, size_t *length,
static void table_def_free_entry(TABLE_SHARE *share)
{
DBUG_ENTER("table_def_free_entry");
+ mysql_mutex_assert_owner(&LOCK_open);
if (share->prev)
{
/* remove from old_unused_share list */
- pthread_mutex_lock(&LOCK_table_share);
*share->prev= share->next;
share->next->prev= share->prev;
- pthread_mutex_unlock(&LOCK_table_share);
}
free_table_share(share);
DBUG_VOID_RETURN;
@@ -290,13 +373,42 @@ static void table_def_free_entry(TABLE_SHARE *share)
bool table_def_init(void)
{
table_def_inited= 1;
- pthread_mutex_init(&LOCK_table_share, MY_MUTEX_INIT_FAST);
+#ifdef HAVE_PSI_INTERFACE
+ init_tdc_psi_keys();
+#endif
+ mysql_mutex_init(key_LOCK_open, &LOCK_open, MY_MUTEX_INIT_FAST);
oldest_unused_share= &end_of_unused_share;
end_of_unused_share.prev= &oldest_unused_share;
- return hash_init(&table_def_cache, &my_charset_bin, table_def_size,
- 0, 0, table_def_key,
- (hash_free_key) table_def_free_entry, 0) != 0;
+
+ return my_hash_init(&table_def_cache, &my_charset_bin, table_def_size,
+ 0, 0, table_def_key,
+ (my_hash_free_key) table_def_free_entry, 0) != 0;
+}
+
+
+/**
+ Notify table definition cache that process of shutting down server
+ has started so it has to keep number of TABLE and TABLE_SHARE objects
+ minimal in order to reduce number of references to pluggable engines.
+*/
+
+void table_def_start_shutdown(void)
+{
+ if (table_def_inited)
+ {
+ mysql_mutex_lock(&LOCK_open);
+ /*
+ Ensure that TABLE and TABLE_SHARE objects which are created for
+ tables that are open during process of plugins' shutdown are
+ immediately released. This keeps number of references to engine
+ plugins minimal and allows shutdown to proceed smoothly.
+ */
+ table_def_shutdown_in_progress= TRUE;
+ mysql_mutex_unlock(&LOCK_open);
+ /* Free all cached but unused TABLEs and TABLE_SHAREs. */
+ close_cached_tables(NULL, NULL, FALSE, LONG_TIMEOUT);
+ }
}
@@ -306,8 +418,9 @@ void table_def_free(void)
if (table_def_inited)
{
table_def_inited= 0;
- pthread_mutex_destroy(&LOCK_table_share);
- hash_free(&table_def_cache);
+ /* Free table definitions. */
+ my_hash_free(&table_def_cache);
+ mysql_mutex_destroy(&LOCK_open);
}
DBUG_VOID_RETURN;
}
@@ -320,6 +433,122 @@ uint cached_table_definitions(void)
/*
+ Auxiliary routines for manipulating with per-share used/unused and
+ global unused lists of TABLE objects and table_cache_count counter.
+ Responsible for preserving invariants between those lists, counter
+ and TABLE::in_use member.
+ In fact those routines implement sort of implicit table cache as
+ part of table definition cache.
+*/
+
+
+/**
+ Add newly created TABLE object for table share which is going
+ to be used right away.
+*/
+
+static void table_def_add_used_table(THD *thd, TABLE *table)
+{
+ DBUG_ASSERT(table->in_use == thd);
+ table->s->used_tables.push_front(table);
+ table_cache_count++;
+}
+
+
+/**
+ Prepare used or unused TABLE instance for destruction by removing
+ it from share's and global list.
+*/
+
+static void table_def_remove_table(TABLE *table)
+{
+ if (table->in_use)
+ {
+ /* Remove from per-share chain of used TABLE objects. */
+ table->s->used_tables.remove(table);
+ }
+ else
+ {
+ /* Remove from per-share chain of unused TABLE objects. */
+ table->s->free_tables.remove(table);
+
+ /* And global unused chain. */
+ table->next->prev=table->prev;
+ table->prev->next=table->next;
+ if (table == unused_tables)
+ {
+ unused_tables=unused_tables->next;
+ if (table == unused_tables)
+ unused_tables=0;
+ }
+ check_unused(current_thd);
+ }
+ table_cache_count--;
+}
+
+
+/**
+ Mark already existing TABLE instance as used.
+*/
+
+static void table_def_use_table(THD *thd, TABLE *table)
+{
+ DBUG_ASSERT(!table->in_use);
+
+ /* Unlink table from list of unused tables for this share. */
+ table->s->free_tables.remove(table);
+ /* Unlink able from global unused tables list. */
+ if (table == unused_tables)
+ { // First unused
+ unused_tables=unused_tables->next; // Remove from link
+ if (table == unused_tables)
+ unused_tables=0;
+ }
+ table->prev->next=table->next; /* Remove from unused list */
+ table->next->prev=table->prev;
+ check_unused(thd);
+ /* Add table to list of used tables for this share. */
+ table->s->used_tables.push_front(table);
+ table->in_use= thd;
+ /* The ex-unused table must be fully functional. */
+ DBUG_ASSERT(table->db_stat && table->file);
+ /* The children must be detached from the table. */
+ DBUG_ASSERT(! table->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
+}
+
+
+/**
+ Mark already existing used TABLE instance as unused.
+*/
+
+static void table_def_unuse_table(TABLE *table)
+{
+ THD *thd __attribute__((unused))= table->in_use;
+ DBUG_ASSERT(table->in_use);
+
+ /* We shouldn't put the table to 'unused' list if the share is old. */
+ DBUG_ASSERT(! table->s->has_old_version());
+
+ table->in_use= 0;
+ /* Remove table from the list of tables used in this share. */
+ table->s->used_tables.remove(table);
+ /* Add table to the list of unused TABLE objects for this share. */
+ table->s->free_tables.push_front(table);
+ /* Also link it last in the global list of unused TABLE objects. */
+ if (unused_tables)
+ {
+ table->next=unused_tables;
+ table->prev=unused_tables->prev;
+ unused_tables->prev=table;
+ table->prev->next=table;
+ }
+ else
+ unused_tables=table->next=table->prev=table;
+ check_unused(thd);
+}
+
+
+/*
Get TABLE_SHARE for a table.
get_table_share()
@@ -345,16 +574,26 @@ uint cached_table_definitions(void)
*/
TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key,
- uint key_length, uint db_flags, int *error)
+ uint key_length, uint db_flags, int *error,
+ my_hash_value_type hash_value)
{
TABLE_SHARE *share;
DBUG_ENTER("get_table_share");
*error= 0;
+ /*
+ To be able perform any operation on table we should own
+ some kind of metadata lock on it.
+ */
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE,
+ table_list->db,
+ table_list->table_name,
+ MDL_SHARED));
+
/* Read table definition from cache */
- if ((share= (TABLE_SHARE*) hash_search(&table_def_cache,(uchar*) key,
- key_length)))
+ if ((share= (TABLE_SHARE*) my_hash_search_using_hash_value(&table_def_cache,
+ hash_value, (uchar*) key, key_length)))
goto found;
if (!(share= alloc_table_share(table_list, key, key_length)))
@@ -363,16 +602,10 @@ TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key,
}
/*
- Lock mutex to be able to read table definition from file without
- conflicts
- */
- (void) pthread_mutex_lock(&share->mutex);
-
- /*
- We assign a new table id under the protection of the LOCK_open and
- the share's own mutex. We do this insted of creating a new mutex
+ We assign a new table id under the protection of LOCK_open.
+ We do this instead of creating a new mutex
and using it for the sole purpose of serializing accesses to a
- static variable, we assign the table id here. We assign it to the
+ static variable, we assign the table id here. We assign it to the
share before inserting it into the table_def_cache to be really
sure that it cannot be read from the cache without having a table
id assigned.
@@ -391,60 +624,50 @@ TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key,
if (open_table_def(thd, share, db_flags))
{
*error= share->error;
- (void) hash_delete(&table_def_cache, (uchar*) share);
+ (void) my_hash_delete(&table_def_cache, (uchar*) share);
DBUG_RETURN(0);
}
share->ref_count++; // Mark in use
DBUG_PRINT("exit", ("share: 0x%lx ref_count: %u",
(ulong) share, share->ref_count));
- (void) pthread_mutex_unlock(&share->mutex);
DBUG_RETURN(share);
found:
- /*
+ /*
We found an existing table definition. Return it if we didn't get
an error when reading the table definition from file.
*/
-
- /* We must do a lock to ensure that the structure is initialized */
- (void) pthread_mutex_lock(&share->mutex);
if (share->error)
{
/* Table definition contained an error */
open_table_error(share, share->error, share->open_errno, share->errarg);
- (void) pthread_mutex_unlock(&share->mutex);
DBUG_RETURN(0);
}
if (share->is_view && !(db_flags & OPEN_VIEW))
{
open_table_error(share, 1, ENOENT, 0);
- (void) pthread_mutex_unlock(&share->mutex);
DBUG_RETURN(0);
}
- if (!share->ref_count++ && share->prev)
+ ++share->ref_count;
+
+ if (share->ref_count == 1 && share->prev)
{
/*
Share was not used before and it was in the old_unused_share list
Unlink share from this list
*/
DBUG_PRINT("info", ("Unlinking from not used list"));
- pthread_mutex_lock(&LOCK_table_share);
*share->prev= share->next;
share->next->prev= share->prev;
share->next= 0;
share->prev= 0;
- pthread_mutex_unlock(&LOCK_table_share);
}
- (void) pthread_mutex_unlock(&share->mutex);
/* Free cache if too big */
while (table_def_cache.records > table_def_size &&
oldest_unused_share->next)
- {
- pthread_mutex_lock(&oldest_unused_share->mutex);
- VOID(hash_delete(&table_def_cache, (uchar*) oldest_unused_share));
- }
+ my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share);
DBUG_PRINT("exit", ("share: 0x%lx ref_count: %u",
(ulong) share, share->ref_count));
@@ -452,22 +675,25 @@ found:
}
-/*
+/**
Get a table share. If it didn't exist, try creating it from engine
- For arguments and return values, see get_table_from_share()
+ For arguments and return values, see get_table_share()
*/
-static TABLE_SHARE
-*get_table_share_with_create(THD *thd, TABLE_LIST *table_list,
- char *key, uint key_length,
- uint db_flags, int *error)
+static TABLE_SHARE *
+get_table_share_with_discover(THD *thd, TABLE_LIST *table_list,
+ char *key, uint key_length,
+ uint db_flags, int *error,
+ my_hash_value_type hash_value)
+
{
TABLE_SHARE *share;
- int tmp;
- DBUG_ENTER("get_table_share_with_create");
+ bool exists;
+ DBUG_ENTER("get_table_share_with_discover");
- share= get_table_share(thd, table_list, key, key_length, db_flags, error);
+ share= get_table_share(thd, table_list, key, key_length, db_flags, error,
+ hash_value);
/*
If share is not NULL, we found an existing share.
@@ -477,10 +703,15 @@ static TABLE_SHARE
from the pre-locking list. In this case we still need to try
auto-discover before returning a NULL share.
+ Or, we're inside SHOW CREATE VIEW, which
+ also installs a silencer for ER_NO_SUCH_TABLE error.
+
If share is NULL and the error is ER_NO_SUCH_TABLE, this is
- the same as above, only that the error was not silenced by
- pre-locking. Once again, we need to try to auto-discover
- the share.
+ the same as above, only that the error was not silenced by
+ pre-locking or SHOW CREATE VIEW.
+
+ In both these cases it won't harm to try to discover the
+ table.
Finally, if share is still NULL, it's a real error and we need
to abort.
@@ -488,20 +719,27 @@ static TABLE_SHARE
@todo Rework alternative ways to deal with ER_NO_SUCH TABLE.
*/
if (share ||
- (thd->is_error() && thd->main_da.sql_errno() != ER_NO_SUCH_TABLE))
+ (thd->is_error() && thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE &&
+ thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE_IN_ENGINE))
DBUG_RETURN(share);
+ *error= 0;
+
/* Table didn't exist. Check if some engine can provide it */
- tmp= ha_create_table_from_engine(thd, table_list->db,
- table_list->table_name);
- if (tmp < 0)
+ if (ha_check_if_table_exists(thd, table_list->db, table_list->table_name,
+ &exists))
+ {
+ thd->clear_error();
+ /* Conventionally, the storage engine API does not report errors. */
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ }
+ else if (! exists)
{
/*
No such table in any engine.
Hide "Table doesn't exist" errors if the table belongs to a view.
The check for thd->is_error() is necessary to not push an
- unwanted error in case of pre-locking, which silences
- "no such table" errors.
+ unwanted error in case the error was already silenced.
@todo Rework the alternative ways to deal with ER_NO_SUCH TABLE.
*/
if (thd->is_error())
@@ -519,89 +757,60 @@ static TABLE_SHARE
view->view_db.str, view->view_name.str);
}
}
- DBUG_RETURN(0);
}
- if (tmp)
+ else
{
- /* Give right error message */
thd->clear_error();
- DBUG_PRINT("error", ("Discovery of %s/%s failed", table_list->db,
- table_list->table_name));
- my_printf_error(ER_UNKNOWN_ERROR,
- "Failed to open '%-.64s', error while "
- "unpacking from engine",
- MYF(0), table_list->table_name);
- DBUG_RETURN(0);
+ *error= 7; /* Run auto-discover. */
}
- /* Table existed in engine. Let's open it */
- mysql_reset_errors(thd, 1); // Clear warnings
- thd->clear_error(); // Clear error message
- DBUG_RETURN(get_table_share(thd, table_list, key, key_length,
- db_flags, error));
+ DBUG_RETURN(NULL);
}
-/*
- Mark that we are not using table share anymore.
+/**
+ Mark that we are not using table share anymore.
- SYNOPSIS
- release_table_share()
- share Table share
- release_type How the release should be done:
- RELEASE_NORMAL
- - Release without checking
- RELEASE_WAIT_FOR_DROP
- - Don't return until we get a signal that the
- table is deleted or the thread is killed.
-
- IMPLEMENTATION
- If ref_count goes to zero and (we have done a refresh or if we have
- already too many open table shares) then delete the definition.
-
- If type == RELEASE_WAIT_FOR_DROP then don't return until we get a signal
- that the table is deleted or the thread is killed.
+ @param share Table share
+
+ If the share has no open tables and (we have done a refresh or
+ if we have already too many open table shares) then delete the
+ definition.
*/
-void release_table_share(TABLE_SHARE *share, enum release_type type)
+void release_table_share(TABLE_SHARE *share)
{
- bool to_be_deleted= 0;
DBUG_ENTER("release_table_share");
DBUG_PRINT("enter",
("share: 0x%lx table: %s.%s ref_count: %u version: %lu",
(ulong) share, share->db.str, share->table_name.str,
share->ref_count, share->version));
- safe_mutex_assert_owner(&LOCK_open);
+ mysql_mutex_assert_owner(&LOCK_open);
- pthread_mutex_lock(&share->mutex);
+ DBUG_ASSERT(share->ref_count);
if (!--share->ref_count)
{
- if (share->version != refresh_version)
- to_be_deleted=1;
+ if (share->has_old_version() || table_def_shutdown_in_progress)
+ my_hash_delete(&table_def_cache, (uchar*) share);
else
{
/* Link share last in used_table_share list */
DBUG_PRINT("info",("moving share to unused list"));
DBUG_ASSERT(share->next == 0);
- pthread_mutex_lock(&LOCK_table_share);
share->prev= end_of_unused_share.prev;
*end_of_unused_share.prev= share;
end_of_unused_share.prev= &share->next;
share->next= &end_of_unused_share;
- pthread_mutex_unlock(&LOCK_table_share);
- to_be_deleted= (table_def_cache.records > table_def_size);
+ if (table_def_cache.records > table_def_size)
+ {
+ /* Delete the least used share to preserve LRU order. */
+ my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share);
+ }
}
}
- if (to_be_deleted)
- {
- DBUG_PRINT("info", ("Deleting share"));
- hash_delete(&table_def_cache, (uchar*) share);
- DBUG_VOID_RETURN;
- }
- pthread_mutex_unlock(&share->mutex);
DBUG_VOID_RETURN;
}
@@ -623,74 +832,15 @@ TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name)
{
char key[SAFE_NAME_LEN*2+2];
uint key_length;
- safe_mutex_assert_owner(&LOCK_open);
+ mysql_mutex_assert_owner(&LOCK_open);
key_length= create_table_def_key(key, db, table_name);
- return (TABLE_SHARE*) hash_search(&table_def_cache,(uchar*) key, key_length);
+ return (TABLE_SHARE*) my_hash_search(&table_def_cache,
+ (uchar*) key, key_length);
}
/*
- Close file handle, but leave the table in the table cache
-
- SYNOPSIS
- close_handle_and_leave_table_as_lock()
- table Table handler
-
- NOTES
- By leaving the table in the table cache, it disallows any other thread
- to open the table
-
- thd->killed will be set if we run out of memory
-
- If closing a MERGE child, the calling function has to take care for
- closing the parent too, if necessary.
-*/
-
-
-void close_handle_and_leave_table_as_lock(TABLE *table)
-{
- TABLE_SHARE *share, *old_share= table->s;
- char *key_buff;
- MEM_ROOT *mem_root= &table->mem_root;
- DBUG_ENTER("close_handle_and_leave_table_as_lock");
-
- DBUG_ASSERT(table->db_stat);
-
- /*
- Make a local copy of the table share and free the current one.
- This has to be done to ensure that the table share is removed from
- the table defintion cache as soon as the last instance is removed
- */
- if (multi_alloc_root(mem_root,
- &share, sizeof(*share),
- &key_buff, old_share->table_cache_key.length,
- NULL))
- {
- bzero((char*) share, sizeof(*share));
- share->set_table_cache_key(key_buff, old_share->table_cache_key.str,
- old_share->table_cache_key.length);
- share->tmp_table= INTERNAL_TMP_TABLE; // for intern_close_table()
- }
-
- /*
- When closing a MERGE parent or child table, detach the children first.
- Do not clear child table references to allow for reopen.
- */
- if (table->child_l || table->parent)
- detach_merge_children(table, FALSE);
- table->file->ha_close();
- table->db_stat= 0; // Mark file closed
- release_table_share(table->s, RELEASE_NORMAL);
- table->s= share;
- table->file->change_table_ptr(table, table->s);
-
- DBUG_VOID_RETURN;
-}
-
-
-
-/*
Create a list for all open tables matching SQL expression
SYNOPSIS
@@ -715,16 +865,14 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
TABLE_LIST table_list;
DBUG_ENTER("list_open_tables");
- VOID(pthread_mutex_lock(&LOCK_open));
+ mysql_mutex_lock(&LOCK_open);
bzero((char*) &table_list,sizeof(table_list));
start_list= &open_list;
open_list=0;
- for (uint idx=0 ; result == 0 && idx < open_cache.records; idx++)
+ for (uint idx=0 ; result == 0 && idx < table_def_cache.records; idx++)
{
- OPEN_TABLE_LIST *table;
- TABLE *entry=(TABLE*) hash_element(&open_cache,idx);
- TABLE_SHARE *share= entry->s;
+ TABLE_SHARE *share= (TABLE_SHARE *)my_hash_element(&table_def_cache, idx);
if (db && my_strcasecmp(system_charset_info, db, share->db.str))
continue;
@@ -736,23 +884,9 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
table_list.table_name= share->table_name.str;
table_list.grant.privilege=0;
- if (check_table_access(thd,SELECT_ACL | EXTRA_ACL,&table_list, 1, TRUE))
- continue;
- /* need to check if we haven't already listed it */
- for (table= open_list ; table ; table=table->next)
- {
- if (!strcmp(table->table, share->table_name.str) &&
- !strcmp(table->db, share->db.str))
- {
- if (entry->in_use)
- table->in_use++;
- if (entry->locked_by_name)
- table->locked++;
- break;
- }
- }
- if (table)
+ if (check_table_access(thd,SELECT_ACL,&table_list, TRUE, 1, TRUE))
continue;
+
if (!(*start_list = (OPEN_TABLE_LIST *)
sql_alloc(sizeof(**start_list)+share->table_cache_key.length)))
{
@@ -763,12 +897,15 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
strmov(((*start_list)->db= (char*) ((*start_list)+1)),
share->db.str)+1,
share->table_name.str);
- (*start_list)->in_use= entry->in_use ? 1 : 0;
- (*start_list)->locked= entry->locked_by_name ? 1 : 0;
+ (*start_list)->in_use= 0;
+ I_P_List_iterator<TABLE, TABLE_share> it(share->used_tables);
+ while (it++)
+ ++(*start_list)->in_use;
+ (*start_list)->locked= 0; /* Obsolete. */
start_list= &(*start_list)->next;
*start_list=0;
}
- VOID(pthread_mutex_unlock(&LOCK_open));
+ mysql_mutex_unlock(&LOCK_open);
DBUG_RETURN(open_list);
}
@@ -787,8 +924,8 @@ void intern_close_table(TABLE *table)
free_io_cache(table);
delete table->triggers;
- if (table->file) // Not true if name lock
- VOID(closefrm(table, 1)); // close file
+ if (table->file) // Not true if placeholder
+ (void) closefrm(table, 1); // close file
table->alias.free();
DBUG_VOID_RETURN;
}
@@ -808,23 +945,12 @@ static void free_cache_entry(TABLE *table)
{
DBUG_ENTER("free_cache_entry");
- /* Assert that MERGE children are not attached before final close. */
- DBUG_ASSERT(!table->is_children_attached());
+ /* This should be done before releasing table share. */
+ table_def_remove_table(table);
intern_close_table(table);
- if (!table->in_use)
- {
- table->next->prev=table->prev; /* remove from used chain */
- table->prev->next=table->next;
- if (table == unused_tables)
- {
- unused_tables=unused_tables->next;
- if (table == unused_tables)
- unused_tables=0;
- }
- check_unused(); // consisty check
- }
- my_free((uchar*) table,MYF(0));
+
+ my_free(table);
DBUG_VOID_RETURN;
}
@@ -836,239 +962,252 @@ void free_io_cache(TABLE *table)
if (table->sort.io_cache)
{
close_cached_file(table->sort.io_cache);
- my_free((uchar*) table->sort.io_cache,MYF(0));
+ my_free(table->sort.io_cache);
table->sort.io_cache=0;
}
DBUG_VOID_RETURN;
}
+/**
+ Auxiliary function which allows to kill delayed threads for
+ particular table identified by its share.
+
+ @param share Table share.
+
+ @pre Caller should have LOCK_open mutex.
+*/
+
+static void kill_delayed_threads_for_table(TABLE_SHARE *share)
+{
+ I_P_List_iterator<TABLE, TABLE_share> it(share->used_tables);
+ TABLE *tab;
+
+ mysql_mutex_assert_owner(&LOCK_open);
+
+ while ((tab= it++))
+ {
+ THD *in_use= tab->in_use;
+
+ if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) &&
+ ! in_use->killed)
+ {
+ in_use->killed= KILL_SYSTEM_THREAD;
+ mysql_mutex_lock(&in_use->mysys_var->mutex);
+ if (in_use->mysys_var->current_cond)
+ {
+ mysql_mutex_lock(in_use->mysys_var->current_mutex);
+ mysql_cond_broadcast(in_use->mysys_var->current_cond);
+ mysql_mutex_unlock(in_use->mysys_var->current_mutex);
+ }
+ mysql_mutex_unlock(&in_use->mysys_var->mutex);
+ }
+ }
+}
+
+
/*
Close all tables which aren't in use by any thread
@param thd Thread context
@param tables List of tables to remove from the cache
- @param have_lock If LOCK_open is locked
@param wait_for_refresh Wait for a impending flush
- @param wait_for_placeholders Wait for tables being reopened so that the GRL
- won't proceed while write-locked tables are being reopened by other
- threads.
+ @param timeout Timeout for waiting for flush to be completed.
+
+ @note THD can be NULL, but then wait_for_refresh must be FALSE
+ and tables must be NULL.
- @remark THD can be NULL, but then wait_for_refresh must be FALSE
- and tables must be NULL.
+ @note When called as part of FLUSH TABLES WITH READ LOCK this function
+ ignores metadata locks held by other threads. In order to avoid
+ situation when FLUSH TABLES WITH READ LOCK sneaks in at the moment
+ when some write-locked table is being reopened (by FLUSH TABLES or
+ ALTER TABLE) we have to rely on additional global shared metadata
+ lock taken by thread trying to obtain global read lock.
*/
-static bool int_close_cached_tables(THD *thd, TABLE_LIST *tables,
- bool have_lock,
- bool wait_for_refresh, bool wait_for_placeholders,
- bool set_readonly_mode)
+bool close_cached_tables(THD *thd, TABLE_LIST *tables,
+ bool wait_for_refresh, ulong timeout)
{
- bool result=0;
+ bool result= FALSE;
+ bool found= TRUE;
+ struct timespec abstime;
DBUG_ENTER("close_cached_tables");
DBUG_ASSERT(thd || (!wait_for_refresh && !tables));
- if (!have_lock)
- VOID(pthread_mutex_lock(&LOCK_open));
+ mysql_mutex_lock(&LOCK_open);
if (!tables)
{
- /* No need to close the open tables if we just set the readonly state */
- if (!set_readonly_mode)
- refresh_version++; // Force close of open tables
+ /*
+ Force close of all open tables.
- while (unused_tables)
- {
-#ifdef EXTRA_DEBUG
- if (hash_delete(&open_cache,(uchar*) unused_tables))
- printf("Warning: Couldn't delete open table from hash\n");
-#else
- VOID(hash_delete(&open_cache,(uchar*) unused_tables));
-#endif
- }
- /* Free table shares */
- while (oldest_unused_share->next)
- {
- pthread_mutex_lock(&oldest_unused_share->mutex);
- VOID(hash_delete(&table_def_cache, (uchar*) oldest_unused_share));
- }
+ Note that code in TABLE_SHARE::wait_for_old_version() assumes that
+ incrementing of refresh_version and removal of unused tables and
+ shares from TDC happens atomically under protection of LOCK_open,
+ or putting it another way that TDC does not contain old shares
+ which don't have any tables used.
+ */
+ refresh_version++;
DBUG_PRINT("tcache", ("incremented global refresh_version to: %lu",
refresh_version));
- if (wait_for_refresh)
- {
- /*
- Other threads could wait in a loop in open_and_lock_tables(),
- trying to lock one or more of our tables.
-
- If they wait for the locks in thr_multi_lock(), their lock
- request is aborted. They loop in open_and_lock_tables() and
- enter open_table(). Here they notice the table is refreshed and
- wait for COND_refresh. Then they loop again in
- open_and_lock_tables() and this time open_table() succeeds. At
- this moment, if we (the FLUSH TABLES thread) are scheduled and
- on another FLUSH TABLES enter close_cached_tables(), they could
- awake while we sleep below, waiting for others threads (us) to
- close their open tables. If this happens, the other threads
- would find the tables unlocked. They would get the locks, one
- after the other, and could do their destructive work. This is an
- issue if we have LOCK TABLES in effect.
-
- The problem is that the other threads passed all checks in
- open_table() before we refresh the table.
-
- The fix for this problem is to set some_tables_deleted for all
- threads with open tables. These threads can still get their
- locks, but will immediately release them again after checking
- this variable. They will then loop in open_and_lock_tables()
- again. There they will wait until we update all tables version
- below.
-
- Setting some_tables_deleted is done by remove_table_from_cache()
- in the other branch.
-
- In other words (reviewer suggestion): You need this setting of
- some_tables_deleted for the case when table was opened and all
- related checks were passed before incrementing refresh_version
- (which you already have) but attempt to lock the table happened
- after the call to close_old_data_files() i.e. after removal of
- current thread locks.
- */
- for (uint idx=0 ; idx < open_cache.records ; idx++)
- {
- TABLE *table=(TABLE*) hash_element(&open_cache,idx);
- /*
- We don't increment the refresh_version when set_readonly_mode,
- but we still need non-transactional tables to be reopened.
- So we set their versions as 'refresh_version - 1', which marks
- them for the 'needs_reopen_or_table_lock()'
- */
- if (set_readonly_mode && !table->file->has_transactions())
- table->s->version= 0;
- if (table->in_use &&
- (!set_readonly_mode || !table->file->has_transactions()))
- table->in_use->some_tables_deleted= 1;
- }
- }
+ kill_delayed_threads();
+ /*
+ Get rid of all unused TABLE and TABLE_SHARE instances. By doing
+ this we automatically close all tables which were marked as "old".
+ */
+ while (unused_tables)
+ free_cache_entry(unused_tables);
+ /* Free table shares which were not freed implicitly by loop above. */
+ while (oldest_unused_share->next)
+ (void) my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share);
}
else
{
bool found=0;
for (TABLE_LIST *table= tables; table; table= table->next_local)
{
- if (remove_table_from_cache(thd, table->db, table->table_name,
- RTFC_OWNED_BY_THD_FLAG, table->deleting))
+ TABLE_SHARE *share= get_cached_table_share(table->db, table->table_name);
+
+ if (share)
+ {
+ kill_delayed_threads_for_table(share);
+ /* tdc_remove_table() calls share->remove_from_cache_at_close() */
+ tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, table->db,
+ table->table_name, TRUE);
found=1;
+ }
}
if (!found)
wait_for_refresh=0; // Nothing to wait for
}
-#ifndef EMBEDDED_LIBRARY
- if (!tables)
- kill_delayed_threads();
-#endif
- if (wait_for_refresh)
+
+ mysql_mutex_unlock(&LOCK_open);
+
+ if (!wait_for_refresh)
+ DBUG_RETURN(result);
+
+ set_timespec(abstime, timeout);
+
+ if (thd->locked_tables_mode)
{
/*
- If there is any table that has a lower refresh_version, wait until
- this is closed (or this thread is killed) before returning
+ If we are under LOCK TABLES, we need to reopen the tables without
+ opening a door for any concurrent threads to sneak in and get
+ lock on our tables. To achieve this we use exclusive metadata
+ locks.
*/
- thd->mysys_var->current_mutex= &LOCK_open;
- thd->mysys_var->current_cond= &COND_refresh;
- thd_proc_info(thd, "Flushing tables");
+ TABLE_LIST *tables_to_reopen= (tables ? tables :
+ thd->locked_tables_list.locked_tables());
- close_old_data_files(thd,thd->open_tables,1,1);
- mysql_ha_flush(thd);
- DEBUG_SYNC(thd, "after_flush_unlock");
+ /* Close open HANDLER instances to avoid self-deadlock. */
+ mysql_ha_flush_tables(thd, tables_to_reopen);
- bool found=1;
- /* Wait until all threads has closed all the tables we had locked */
- DBUG_PRINT("info",
- ("Waiting for other threads to close their open tables"));
- while (found && ! thd->killed)
+ for (TABLE_LIST *table_list= tables_to_reopen; table_list;
+ table_list= table_list->next_global)
{
- found=0;
- for (uint idx=0 ; idx < open_cache.records ; idx++)
+ /* 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, TRUE);
+
+ /* May return NULL if this table has already been closed via an alias. */
+ if (! table)
+ continue;
+
+ if (wait_while_table_is_used(thd, table,
+ HA_EXTRA_PREPARE_FOR_FORCED_CLOSE))
{
- TABLE *table=(TABLE*) hash_element(&open_cache,idx);
- /* Avoid a self-deadlock. */
- if (table->in_use == thd)
- continue;
- /*
- Note that we wait here only for tables which are actually open, and
- not for placeholders with TABLE::open_placeholder set. Waiting for
- latter will cause deadlock in the following scenario, for example:
-
- conn1: lock table t1 write;
- conn2: lock table t2 write;
- conn1: flush tables;
- conn2: flush tables;
-
- It also does not make sense to wait for those of placeholders that
- are employed by CREATE TABLE as in this case table simply does not
- exist yet.
- */
- if (table->needs_reopen_or_name_lock() && (table->db_stat ||
- (table->open_placeholder && wait_for_placeholders)))
- {
- found=1;
- DBUG_PRINT("signal", ("Waiting for COND_refresh"));
- pthread_cond_wait(&COND_refresh,&LOCK_open);
- break;
- }
+ result= TRUE;
+ goto err_with_reopen;
}
+ close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED);
}
+ }
+
+ /* Wait until all threads have closed all the tables we are flushing. */
+ DBUG_PRINT("info", ("Waiting for other threads to close their open tables"));
+
+ while (found && ! thd->killed)
+ {
+ TABLE_SHARE *share;
+ found= FALSE;
/*
- No other thread has the locked tables open; reopen them and get the
- old locks. This should always succeed (unless some external process
- has removed the tables)
+ To a self-deadlock or deadlocks with other FLUSH threads
+ waiting on our open HANDLERs, we have to flush them.
*/
- thd->in_lock_tables=1;
- result=reopen_tables(thd,1,1);
- thd->in_lock_tables=0;
- /* Set version for table */
- for (TABLE *table=thd->open_tables; table ; table= table->next)
+ mysql_ha_flush(thd);
+ DEBUG_SYNC(thd, "after_flush_unlock");
+
+ mysql_mutex_lock(&LOCK_open);
+
+ if (!tables)
+ {
+ for (uint idx=0 ; idx < table_def_cache.records ; idx++)
+ {
+ share= (TABLE_SHARE*) my_hash_element(&table_def_cache, idx);
+ if (share->has_old_version())
+ {
+ found= TRUE;
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (TABLE_LIST *table= tables; table; table= table->next_local)
+ {
+ share= get_cached_table_share(table->db, table->table_name);
+ if (share && share->has_old_version())
+ {
+ found= TRUE;
+ break;
+ }
+ }
+ }
+
+ if (found)
{
/*
- Preserve the version (0) of write locked tables so that a impending
- global read lock won't sneak in.
+ The method below temporarily unlocks LOCK_open and frees
+ share's memory.
*/
- if (table->reginfo.lock_type < TL_WRITE_ALLOW_WRITE)
- table->s->version= refresh_version;
+ if (share->wait_for_old_version(thd, &abstime,
+ MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DDL))
+ {
+ mysql_mutex_unlock(&LOCK_open);
+ result= TRUE;
+ goto err_with_reopen;
+ }
}
+
+ mysql_mutex_unlock(&LOCK_open);
}
- if (!have_lock)
- VOID(pthread_mutex_unlock(&LOCK_open));
- if (wait_for_refresh)
+
+err_with_reopen:
+ if (thd->locked_tables_mode)
{
- pthread_mutex_lock(&thd->mysys_var->mutex);
- thd->mysys_var->current_mutex= 0;
- thd->mysys_var->current_cond= 0;
- thd_proc_info(thd, 0);
- pthread_mutex_unlock(&thd->mysys_var->mutex);
+ /*
+ No other thread has the locked tables open; reopen them and get the
+ old locks. This should always succeed (unless some external process
+ has removed the tables)
+ */
+ thd->locked_tables_list.reopen_tables(thd);
+ /*
+ Since downgrade_exclusive_lock() won't do anything with shared
+ metadata lock it is much simpler to go through all open tables rather
+ than picking only those tables that were flushed.
+ */
+ for (TABLE *tab= thd->open_tables; tab; tab= tab->next)
+ tab->mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
}
DBUG_RETURN(result);
}
-bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock,
- bool wait_for_refresh, bool wait_for_placeholders)
-{
- return int_close_cached_tables(thd, tables, have_lock, wait_for_refresh,
- wait_for_placeholders, FALSE);
-}
-
-
-bool close_cached_tables_set_readonly(THD *thd)
-{
- return int_close_cached_tables(thd, NULL, FALSE, TRUE, TRUE, TRUE);
-}
-
-
-/*
+/**
Close all tables which match specified connection string or
if specified string is NULL, then any table with a connection string.
*/
-bool close_cached_connection_tables(THD *thd, bool if_wait_for_refresh,
- LEX_STRING *connection, bool have_lock)
+bool close_cached_connection_tables(THD *thd, LEX_STRING *connection)
{
uint idx;
TABLE_LIST tmp, *tables= NULL;
@@ -1078,12 +1217,11 @@ bool close_cached_connection_tables(THD *thd, bool if_wait_for_refresh,
bzero(&tmp, sizeof(TABLE_LIST));
- if (!have_lock)
- VOID(pthread_mutex_lock(&LOCK_open));
+ mysql_mutex_lock(&LOCK_open);
for (idx= 0; idx < table_def_cache.records; idx++)
{
- TABLE_SHARE *share= (TABLE_SHARE *) hash_element(&table_def_cache, idx);
+ TABLE_SHARE *share= (TABLE_SHARE *) my_hash_element(&table_def_cache, idx);
/* Ignore if table is not open or does not have a connect_string */
if (!share->connect_string.length || !share->ref_count)
@@ -1107,21 +1245,10 @@ bool close_cached_connection_tables(THD *thd, bool if_wait_for_refresh,
tables= (TABLE_LIST *) memdup_root(thd->mem_root, (char*)&tmp,
sizeof(TABLE_LIST));
}
+ mysql_mutex_unlock(&LOCK_open);
if (tables)
- result= close_cached_tables(thd, tables, TRUE, FALSE, FALSE);
-
- if (!have_lock)
- VOID(pthread_mutex_unlock(&LOCK_open));
-
- if (if_wait_for_refresh)
- {
- pthread_mutex_lock(&thd->mysys_var->mutex);
- thd->mysys_var->current_mutex= 0;
- thd->mysys_var->current_cond= 0;
- thd->proc_info=0;
- pthread_mutex_unlock(&thd->mysys_var->mutex);
- }
+ result= close_cached_tables(thd, tables, FALSE, LONG_TIMEOUT);
DBUG_RETURN(result);
}
@@ -1142,43 +1269,54 @@ static void mark_temp_tables_as_free_for_reuse(THD *thd)
for (TABLE *table= thd->temporary_tables ; table ; table= table->next)
{
if ((table->query_id == thd->query_id) && ! table->open_by_handler)
- {
- table->query_id= 0;
- table->file->ha_reset();
- /*
- Detach temporary MERGE children from temporary parent to allow new
- attach at next open. Do not do the detach, if close_thread_tables()
- is called from a sub-statement. The temporary table might still be
- used in the top-level statement.
- */
- if (table->child_l || table->parent)
- detach_merge_children(table, TRUE);
- /*
- Reset temporary table lock type to it's default value (TL_WRITE).
-
- Statements such as INSERT INTO .. SELECT FROM tmp, CREATE TABLE
- .. SELECT FROM tmp and UPDATE may under some circumstances modify
- the lock type of the tables participating in the statement. This
- isn't a problem for non-temporary tables since their lock type is
- reset at every open, but the same does not occur for temporary
- tables for historical reasons.
-
- Furthermore, the lock type of temporary tables is not really that
- important because they can only be used by one query at a time and
- not even twice in a query -- a temporary table is represented by
- only one TABLE object. Nonetheless, it's safer from a maintenance
- point of view to reset the lock type of this singleton TABLE object
- as to not cause problems when the table is reused.
-
- Even under LOCK TABLES mode its okay to reset the lock type as
- LOCK TABLES is allowed (but ignored) for a temporary table.
- */
- table->reginfo.lock_type= TL_WRITE;
- }
+ mark_tmp_table_for_reuse(table);
}
}
+/**
+ Reset a single temporary table.
+ Effectively this "closes" one temporary table,
+ in a session.
+
+ @param table Temporary table.
+*/
+
+void mark_tmp_table_for_reuse(TABLE *table)
+{
+ DBUG_ASSERT(table->s->tmp_table);
+
+ table->query_id= 0;
+ table->file->ha_reset();
+
+ /* Detach temporary MERGE children from temporary parent. */
+ DBUG_ASSERT(table->file);
+ table->file->extra(HA_EXTRA_DETACH_CHILDREN);
+
+ /*
+ Reset temporary table lock type to it's default value (TL_WRITE).
+
+ Statements such as INSERT INTO .. SELECT FROM tmp, CREATE TABLE
+ .. SELECT FROM tmp and UPDATE may under some circumstances modify
+ the lock type of the tables participating in the statement. This
+ isn't a problem for non-temporary tables since their lock type is
+ reset at every open, but the same does not occur for temporary
+ tables for historical reasons.
+
+ Furthermore, the lock type of temporary tables is not really that
+ important because they can only be used by one query at a time and
+ not even twice in a query -- a temporary table is represented by
+ only one TABLE object. Nonetheless, it's safer from a maintenance
+ point of view to reset the lock type of this singleton TABLE object
+ as to not cause problems when the table is reused.
+
+ Even under LOCK TABLES mode its okay to reset the lock type as
+ LOCK TABLES is allowed (but ignored) for a temporary table.
+ */
+ table->reginfo.lock_type= TL_WRITE;
+}
+
+
/*
Mark all tables in the list which were used by current substatement
as free for reuse.
@@ -1206,6 +1344,8 @@ static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table)
{
for (; table ; table= table->next)
{
+ DBUG_ASSERT(table->pos_in_locked_tables == NULL ||
+ table->pos_in_locked_tables->table == table);
if (table->query_id == thd->query_id)
{
table->query_id= 0;
@@ -1225,29 +1365,82 @@ static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table)
static void close_open_tables(THD *thd)
{
- bool found_old_table= 0;
-
- safe_mutex_assert_not_owner(&LOCK_open);
-
- VOID(pthread_mutex_lock(&LOCK_open));
+ mysql_mutex_assert_not_owner(&LOCK_open);
DBUG_PRINT("info", ("thd->open_tables: 0x%lx", (long) thd->open_tables));
while (thd->open_tables)
- found_old_table|= close_thread_table(thd, &thd->open_tables);
- thd->some_tables_deleted= 0;
+ (void) close_thread_table(thd, &thd->open_tables);
+}
+
+
+/**
+ Close all open instances of the table but keep the MDL lock.
+
+ Works both under LOCK TABLES and in the normal mode.
+ Removes all closed instances of the table from the table cache.
+
+ @param thd thread handle
+ @param[in] share table share, but is just a handy way to
+ access the table cache key
+
+ @param[in] extra
+ HA_EXTRA_PREPRE_FOR_DROP if the table is being dropped
+ HA_EXTRA_PREPARE_FOR_REANME if the table is being renamed
+ HA_EXTRA_NOT_USED no drop/rename
+ In case of drop/reanme the documented behaviour is to
+ implicitly remove the table from LOCK TABLES
+ list.
+
+ @pre Must be called with an X MDL lock on the table.
+*/
+
+void
+close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
+ ha_extra_function extra)
+{
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length= share->table_cache_key.length;
+ const char *db= key;
+ const char *table_name= db + share->db.length + 1;
- /* Free tables to hold down open files */
- while (open_cache.records > table_cache_size && unused_tables)
- VOID(hash_delete(&open_cache,(uchar*) unused_tables)); /* purecov: tested */
- check_unused();
- if (found_old_table)
+ memcpy(key, share->table_cache_key.str, key_length);
+
+ mysql_mutex_assert_not_owner(&LOCK_open);
+ for (TABLE **prev= &thd->open_tables; *prev; )
{
- /* Tell threads waiting for refresh that something has happened */
- broadcast_refresh();
- }
+ TABLE *table= *prev;
- VOID(pthread_mutex_unlock(&LOCK_open));
+ if (table->s->table_cache_key.length == key_length &&
+ !memcmp(table->s->table_cache_key.str, key, key_length))
+ {
+ thd->locked_tables_list.unlink_from_list(thd,
+ table->pos_in_locked_tables,
+ extra != HA_EXTRA_NOT_USED);
+ /* Inform handler that there is a drop table or a rename going on */
+ if (extra != HA_EXTRA_NOT_USED && table->db_stat)
+ {
+ table->file->extra(extra);
+ extra= HA_EXTRA_NOT_USED; // Call extra once!
+ }
+
+ /*
+ Does nothing if the table is not locked.
+ This allows one to use this function after a table
+ has been unlocked, e.g. in partition management.
+ */
+ mysql_lock_remove(thd, thd->lock, table);
+ close_thread_table(thd, prev);
+ }
+ else
+ {
+ /* Step to next entry in open_tables list. */
+ prev= &table->next;
+ }
+ }
+ /* Remove the table share from the cache. */
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db, table_name,
+ FALSE);
}
@@ -1272,9 +1465,10 @@ static void close_open_tables(THD *thd)
void close_thread_tables(THD *thd)
{
TABLE *table;
- prelocked_mode_type prelocked_mode= thd->prelocked_mode;
DBUG_ENTER("close_thread_tables");
+ thd_proc_info(thd, "closing tables");
+
#ifdef EXTRA_DEBUG
DBUG_PRINT("tcache", ("open tables:"));
for (table= thd->open_tables; table; table= table->next)
@@ -1288,6 +1482,23 @@ void close_thread_tables(THD *thd)
DEBUG_SYNC(thd, "before_close_thread_tables");
#endif
+ DBUG_ASSERT(thd->transaction.stmt.is_empty() || thd->in_sub_stmt ||
+ (thd->state_flags & Open_tables_state::BACKUPS_AVAIL));
+
+ /* Detach MERGE children after every statement. Even under LOCK TABLES. */
+ for (table= thd->open_tables; table; table= table->next)
+ {
+ /* Table might be in use by some outer statement. */
+ DBUG_PRINT("tcache", ("table: '%s' query_id: %lu",
+ table->s->table_name.str, (ulong) table->query_id));
+ if (thd->locked_tables_mode <= LTM_LOCK_TABLES ||
+ table->query_id == thd->query_id)
+ {
+ DBUG_ASSERT(table->file);
+ table->file->extra(HA_EXTRA_DETACH_CHILDREN);
+ }
+ }
+
/*
We are assuming here that thd->derived_tables contains ONLY derived
tables for this substatement. i.e. instead of approach which uses
@@ -1318,29 +1529,8 @@ void close_thread_tables(THD *thd)
Mark all temporary tables used by this statement as free for reuse.
*/
mark_temp_tables_as_free_for_reuse(thd);
- /*
- Let us commit transaction for statement. Since in 5.0 we only have
- one statement transaction and don't allow several nested statement
- transactions this call will do nothing if we are inside of stored
- function or trigger (i.e. statement transaction is already active and
- does not belong to statement for which we do close_thread_tables()).
- TODO: This should be fixed in later releases.
- */
- if (!(thd->state_flags & Open_tables_state::BACKUPS_AVAIL))
- {
- thd->main_da.can_overwrite_status= TRUE;
- ha_autocommit_or_rollback(thd, thd->is_error());
- thd->main_da.can_overwrite_status= FALSE;
-
- /*
- Reset transaction state, but only if we're not inside a
- sub-statement of a prelocked statement.
- */
- if (! prelocked_mode || thd->lex->requires_prelocking())
- thd->transaction.stmt.reset();
- }
- if (thd->locked_tables || prelocked_mode)
+ if (thd->locked_tables_mode)
{
/* Ensure we are calling ha_reset() for all used tables */
@@ -1349,8 +1539,13 @@ void close_thread_tables(THD *thd)
/*
We are under simple LOCK TABLES or we're inside a sub-statement
of a prelocked statement, so should not do anything else.
+
+ Note that even if we are in LTM_LOCK_TABLES mode and statement
+ requires prelocking (e.g. when we are closing tables after
+ failing ot "open" all tables required for statement execution)
+ we will exit this function a few lines below.
*/
- if (!prelocked_mode || !thd->lex->requires_prelocking())
+ if (! thd->lex->requires_prelocking())
DBUG_VOID_RETURN;
/*
@@ -1358,14 +1553,14 @@ void close_thread_tables(THD *thd)
so we have to leave the prelocked mode now with doing implicit
UNLOCK TABLES if needed.
*/
- DBUG_PRINT("info",("thd->prelocked_mode= NON_PRELOCKED"));
- thd->prelocked_mode= NON_PRELOCKED;
+ if (thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)
+ thd->locked_tables_mode= LTM_LOCK_TABLES;
- if (prelocked_mode == PRELOCKED_UNDER_LOCK_TABLES)
+ if (thd->locked_tables_mode == LTM_LOCK_TABLES)
DBUG_VOID_RETURN;
- thd->lock= thd->locked_tables;
- thd->locked_tables= 0;
+ thd->leave_locked_tables_mode();
+
/* Fallthrough */
}
@@ -1385,25 +1580,12 @@ void close_thread_tables(THD *thd)
thd->lock=0;
}
/*
- Note that we need to hold LOCK_open while changing the
- open_tables list. Another thread may work on it.
- (See: remove_table_from_cache(), mysql_wait_completed_table())
Closing a MERGE child before the parent would be fatal if the
other thread tries to abort the MERGE lock in between.
*/
if (thd->open_tables)
close_open_tables(thd);
- if (prelocked_mode == PRELOCKED)
- {
- /*
- If we are here then we are leaving normal prelocked mode, so it is
- good idea to turn off OPTION_TABLE_LOCK flag.
- */
- DBUG_ASSERT(thd->lex->requires_prelocking());
- thd->options&= ~(OPTION_TABLE_LOCK);
- }
-
DBUG_VOID_RETURN;
}
@@ -1419,53 +1601,57 @@ bool close_thread_table(THD *thd, TABLE **table_ptr)
table->s->table_name.str, (long) table));
DBUG_ASSERT(table->key_read == 0);
DBUG_ASSERT(!table->file || table->file->inited == handler::NONE);
+ mysql_mutex_assert_not_owner(&LOCK_open);
- if (table->file)
- {
- table->file->update_global_table_stats();
- table->file->update_global_index_stats();
- }
-
- *table_ptr=table->next;
/*
- When closing a MERGE parent or child table, detach the children first.
- Clear child table references to force new assignment at next open.
+ The metadata lock must be released after giving back
+ the table to the table cache.
*/
- if (table->child_l || table->parent)
- detach_merge_children(table, TRUE);
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE,
+ table->s->db.str,
+ table->s->table_name.str,
+ MDL_SHARED));
+ table->mdl_ticket= NULL;
- if (table->needs_reopen_or_name_lock() ||
- thd->version != refresh_version || !table->db_stat)
+ if (table->file)
{
- VOID(hash_delete(&open_cache,(uchar*) table));
- found_old_table=1;
+ table->file->update_global_table_stats();
+ table->file->update_global_index_stats();
+ }
+
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ *table_ptr=table->next;
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+
+ if (! table->needs_reopen())
+ {
+ /* Avoid having MERGE tables with attached children in unused_tables. */
+ table->file->extra(HA_EXTRA_DETACH_CHILDREN);
+ /* Free memory and reset for next loop. */
+ free_field_buffers_larger_than(table, MAX_TDC_BLOB_SIZE);
+ table->file->ha_reset();
+ }
+
+ mysql_mutex_lock(&LOCK_open);
+
+ if (table->s->has_old_version() || table->needs_reopen() ||
+ table_def_shutdown_in_progress)
+ {
+ free_cache_entry(table);
+ found_old_table= 1;
}
else
{
+ DBUG_ASSERT(table->file);
+ table_def_unuse_table(table);
/*
- Open placeholders have TABLE::db_stat set to 0, so they should be
- handled by the first alternative.
+ We free the least used table, not the subject table,
+ to keep the LRU order.
*/
- DBUG_ASSERT(!table->open_placeholder);
-
- /* Assert that MERGE children are not attached in unused_tables. */
- DBUG_ASSERT(!table->is_children_attached());
-
- /* Free memory and reset for next loop */
- free_field_buffers_larger_than(table,MAX_TDC_BLOB_SIZE);
-
- table->file->ha_reset();
- table->in_use=0;
- if (unused_tables)
- {
- table->next=unused_tables; /* Link in last */
- table->prev=unused_tables->prev;
- unused_tables->prev=table;
- table->prev->next=table;
- }
- else
- unused_tables=table->next=table->prev=table;
+ if (table_cache_count > table_cache_size)
+ free_cache_entry(unused_tables);
}
+ mysql_mutex_unlock(&LOCK_open);
DBUG_RETURN(found_old_table);
}
@@ -1482,19 +1668,20 @@ static inline uint tmpkeyval(THD *thd, TABLE *table)
creates one DROP TEMPORARY TABLE binlog event for each pseudo-thread
*/
-void close_temporary_tables(THD *thd)
+bool close_temporary_tables(THD *thd)
{
+ DBUG_ENTER("close_temporary_tables");
TABLE *table;
TABLE *next= NULL;
TABLE *prev_table;
- /* Assume thd->options has OPTION_QUOTE_SHOW_CREATE */
+ /* Assume thd->variables.option_bits has OPTION_QUOTE_SHOW_CREATE */
bool was_quote_show= TRUE;
+ bool error= 0;
if (!thd->temporary_tables)
- return;
+ DBUG_RETURN(FALSE);
- if (!mysql_bin_log.is_open() ||
- (thd->current_stmt_binlog_row_based && thd->variables.binlog_format == BINLOG_FORMAT_ROW))
+ if (!mysql_bin_log.is_open())
{
TABLE *tmp_next;
for (table= thd->temporary_tables; table; table= tmp_next)
@@ -1503,7 +1690,7 @@ void close_temporary_tables(THD *thd)
close_temporary(table, 1, 1);
}
thd->temporary_tables= 0;
- return;
+ DBUG_RETURN(FALSE);
}
/* Better add "if exists", in case a RESET MASTER has been done */
@@ -1554,9 +1741,9 @@ void close_temporary_tables(THD *thd)
/* We always quote db,table names though it is slight overkill */
if (found_user_tables &&
- !(was_quote_show= test(thd->options & OPTION_QUOTE_SHOW_CREATE)))
+ !(was_quote_show= test(thd->variables.option_bits & OPTION_QUOTE_SHOW_CREATE)))
{
- thd->options |= OPTION_QUOTE_SHOW_CREATE;
+ thd->variables.option_bits |= OPTION_QUOTE_SHOW_CREATE;
}
/* scan sorted tmps to generate sequence of DROP */
@@ -1603,15 +1790,31 @@ void close_temporary_tables(THD *thd)
thd->thread_specific_used= TRUE;
Query_log_event qinfo(thd, s_query.ptr(),
s_query.length() - 1 /* to remove trailing ',' */,
- 0, FALSE, 0);
+ FALSE, TRUE, FALSE, 0);
qinfo.db= db.ptr();
qinfo.db_len= db.length();
thd->variables.character_set_client= cs_save;
- if (mysql_bin_log.write(&qinfo))
+
+ thd->stmt_da->can_overwrite_status= TRUE;
+ if ((error= (mysql_bin_log.write(&qinfo) || error)))
{
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, MYF(0),
- "Failed to write the DROP statement for temporary tables to binary log");
+ /*
+ If we're here following THD::cleanup, thence the connection
+ has been closed already. So lets print a message to the
+ error log instead of pushing yet another error into the
+ stmt_da.
+
+ Also, we keep the error flag so that we propagate the error
+ up in the stack. This way, if we're the SQL thread we notice
+ that close_temporary_tables failed. (Actually, the SQL
+ thread only calls close_temporary_tables while applying old
+ Start_log_event_v3 events.)
+ */
+ sql_print_error("Failed to write the DROP statement for "
+ "temporary tables to binary log");
}
+ thd->stmt_da->can_overwrite_status= FALSE;
+
thd->variables.pseudo_thread_id= save_pseudo_thread_id;
thd->thread_specific_used= save_thread_specific_used;
}
@@ -1622,8 +1825,10 @@ void close_temporary_tables(THD *thd)
}
}
if (!was_quote_show)
- thd->options&= ~OPTION_QUOTE_SHOW_CREATE; /* restore option */
+ thd->variables.option_bits&= ~OPTION_QUOTE_SHOW_CREATE; /* restore option */
thd->temporary_tables=0;
+
+ DBUG_RETURN(error);
}
/*
@@ -1661,15 +1866,13 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table,
}
-/*
+/**
Test that table is unique (It's only exists once in the table list)
- SYNOPSIS
- unique_table()
- thd thread handle
- table table which should be checked
- table_list list of tables
- check_alias whether to check tables' aliases
+ @param thd thread handle
+ @param table table which should be checked
+ @param table_list list of tables
+ @param check_alias whether to check tables' aliases
NOTE: to exclude derived tables from check we use following mechanism:
a) during derived table processing set THD::derived_tables_processing
@@ -1680,7 +1883,7 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table,
processing loop, because multi-update call fix_fields() for some its
items (which mean JOIN::prepare for subqueries) before unique_table
call to detect which tables should be locked for write).
- c) unique_table skip all tables which belong to SELECT with
+ c) find_dup_table skip all tables which belong to SELECT with
SELECT::exclude_from_table_unique_test set.
Also SELECT::exclude_from_table_unique_test used to exclude from check
tables of main SELECT of multi-delete and multi-update
@@ -1692,17 +1895,17 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table,
TODO: when we will have table/view change detection we can do this check
only once for PS/SP
- RETURN
- found duplicate
- 0 if table is unique
+ @retval !=0 found duplicate
+ @retval 0 if table is unique
*/
-TABLE_LIST* unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
- bool check_alias)
+static
+TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
+ bool check_alias)
{
TABLE_LIST *res;
const char *d_name, *t_name, *t_alias;
- DBUG_ENTER("unique_table");
+ DBUG_ENTER("find_dup_table");
DBUG_PRINT("enter", ("table alias: %s", table->alias));
/*
@@ -1717,6 +1920,9 @@ TABLE_LIST* unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
*/
if (table->table)
{
+ /* All MyISAMMRG children are plain MyISAM tables. */
+ DBUG_ASSERT(table->table->file->ht->db_type != DB_TYPE_MRG_MYISAM);
+
/* temporary table is always unique */
if (table->table && table->table->s->tmp_table != NO_TMP_TABLE)
DBUG_RETURN(0);
@@ -1746,20 +1952,41 @@ retry:
tl= tl->next_global;
continue;
}
- if (((! (res= find_table_in_global_list(tl, d_name, t_name))) &&
- (! (res= mysql_lock_have_duplicate(thd, table, tl)))) ||
- ((!res->table || res->table != table->table) &&
- (!check_alias || !(lower_case_table_names ?
+ /*
+ Table is unique if it is present only once in the global list
+ of tables and once in the list of table locks.
+ */
+ if (! (res= find_table_in_global_list(tl, d_name, t_name)))
+ break;
+
+ /* Skip if same underlying table. */
+ if (res->table && (res->table == table->table))
+ goto next;
+
+ /* Skip if table alias does not match. */
+ if (check_alias)
+ {
+ if (lower_case_table_names ?
my_strcasecmp(files_charset_info, t_alias, res->alias) :
- strcmp(t_alias, res->alias))) &&
- res->select_lex && !res->select_lex->exclude_from_table_unique_test &&
- !res->prelocking_placeholder))
+ strcmp(t_alias, res->alias))
+ goto next;
+ }
+
+ /*
+ Skip if marked to be excluded (could be a derived table) or if
+ entry is a prelocking placeholder.
+ */
+ if (res->select_lex &&
+ !res->select_lex->exclude_from_table_unique_test &&
+ !res->prelocking_placeholder)
break;
+
/*
If we found entry of this table or table of SELECT which already
processed in derived table or top select of multi-update/multi-delete
(exclude_from_table_unique_test) or prelocking placeholder.
*/
+next:
tl= res->next_global;
DBUG_PRINT("info",
("found same copy of table or table which we should skip"));
@@ -1781,6 +2008,42 @@ retry:
}
+/**
+ Test that the subject table of INSERT/UPDATE/DELETE/CREATE
+ or (in case of MyISAMMRG) one of its children are not used later
+ in the query.
+
+ For MyISAMMRG tables, it is assumed that all the underlying
+ tables of @c table (if any) are listed right after it and that
+ their @c parent_l field points at the main table.
+
+
+ @retval non-NULL The table list element for the table that
+ represents the duplicate.
+ @retval NULL No duplicates found.
+*/
+
+TABLE_LIST*
+unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
+ bool check_alias)
+{
+ TABLE_LIST *dup;
+ if (table->table && table->table->file->ht->db_type == DB_TYPE_MRG_MYISAM)
+ {
+ TABLE_LIST *child;
+ dup= NULL;
+ /* Check duplicates of all merge children. */
+ for (child= table->next_global; child && child->parent_l == table;
+ child= child->next_global)
+ {
+ if ((dup= find_dup_table(thd, child, child->next_global, check_alias)))
+ break;
+ }
+ }
+ else
+ dup= find_dup_table(thd, table, table_list, check_alias);
+ return dup;
+}
/*
Issue correct error message in case we found 2 duplicate tables which
prevent some update operation
@@ -1838,39 +2101,60 @@ void update_non_unique_table_error(TABLE_LIST *update,
}
+/**
+ Find temporary table specified by database and table names in the
+ THD::temporary_tables list.
+
+ @return TABLE instance if a temporary table has been found; NULL otherwise.
+*/
+
TABLE *find_temporary_table(THD *thd, const char *db, const char *table_name)
{
- TABLE_LIST table_list;
+ TABLE_LIST tl;
- table_list.db= (char*) db;
- table_list.table_name= (char*) table_name;
- return find_temporary_table(thd, &table_list);
+ tl.db= (char*) db;
+ tl.table_name= (char*) table_name;
+
+ return find_temporary_table(thd, &tl);
}
-TABLE *find_temporary_table(THD *thd, TABLE_LIST *table_list)
+/**
+ Find a temporary table specified by TABLE_LIST instance in the
+ THD::temporary_tables list.
+
+ @return TABLE instance if a temporary table has been found; NULL otherwise.
+*/
+
+TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl)
{
- char key[MAX_DBKEY_LENGTH];
- uint key_length;
- TABLE *table;
- DBUG_ENTER("find_temporary_table");
- DBUG_PRINT("enter", ("table: '%s'.'%s'",
- table_list->db, table_list->table_name));
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length= create_table_def_key(thd, key, tl, 1);
- key_length= create_table_def_key(thd, key, table_list, 1);
- for (table=thd->temporary_tables ; table ; table= table->next)
+ return find_temporary_table(thd, key, key_length);
+}
+
+
+/**
+ Find a temporary table specified by a key in the THD::temporary_tables list.
+
+ @return TABLE instance if a temporary table has been found; NULL otherwise.
+*/
+
+TABLE *find_temporary_table(THD *thd,
+ const char *table_key,
+ uint table_key_length)
+{
+ for (TABLE *table= thd->temporary_tables; table; table= table->next)
{
- if (table->s->table_cache_key.length == key_length &&
- !memcmp(table->s->table_cache_key.str, key, key_length))
+ if (table->s->table_cache_key.length == table_key_length &&
+ !memcmp(table->s->table_cache_key.str, table_key, table_key_length))
{
- DBUG_PRINT("info",
- ("Found table. server_id: %u pseudo_thread_id: %lu",
- (uint) thd->server_id,
- (ulong) thd->variables.pseudo_thread_id));
- DBUG_RETURN(table);
+ return table;
}
}
- DBUG_RETURN(0); // Not a temporary table
+
+ return NULL;
}
@@ -1880,9 +2164,10 @@ TABLE *find_temporary_table(THD *thd, TABLE_LIST *table_list)
Try to locate the table in the list of thd->temporary_tables.
If the table is found:
- if the table is being used by some outer statement, fail.
- - if the table is in thd->locked_tables, unlock it and
- remove it from the list of locked tables. Currently only transactional
- temporary tables are present in the locked_tables list.
+ - if the table is locked with LOCK TABLES or by prelocking,
+ unlock it and remove it from the list of locked tables
+ (THD::lock). Currently only transactional temporary tables
+ are locked.
- Close the temporary table, remove its .FRM
- remove the table from the list of temporary tables
@@ -1894,13 +2179,17 @@ TABLE *find_temporary_table(THD *thd, TABLE_LIST *table_list)
thd->temporary_tables list, it's impossible to tell here whether
we're dealing with an internal or a user temporary table.
+ If is_trans is not null, we return the type of the table:
+ either transactional (e.g. innodb) as TRUE or non-transactional
+ (e.g. myisam) as FALSE.
+
@retval 0 the table was found and dropped successfully.
@retval 1 the table was not found in the list of temporary tables
of this thread
@retval -1 the table is in use by a outer query
*/
-int drop_temporary_table(THD *thd, TABLE_LIST *table_list)
+int drop_temporary_table(THD *thd, TABLE_LIST *table_list, bool *is_trans)
{
TABLE *table;
DBUG_ENTER("drop_temporary_table");
@@ -1917,11 +2206,14 @@ int drop_temporary_table(THD *thd, TABLE_LIST *table_list)
DBUG_RETURN(-1);
}
+ if (is_trans != NULL)
+ *is_trans= table->file->has_transactions();
+
/*
If LOCK TABLES list is not empty and contains this table,
unlock the table and remove the table from this list.
*/
- mysql_lock_remove(thd, thd->locked_tables, table, FALSE);
+ mysql_lock_remove(thd, thd->lock, table);
close_temporary_table(thd, table, 1, 1);
DBUG_RETURN(0);
}
@@ -1938,19 +2230,6 @@ void close_temporary_table(THD *thd, TABLE *table,
table->s->db.str, table->s->table_name.str,
(long) table, table->alias.c_ptr()));
- /*
- When closing a MERGE parent or child table, detach the children
- first. Clear child table references as MERGE table cannot be
- reopened after final close of one of its tables.
-
- This is necessary here because it is sometimes called with attached
- tables and without prior close_thread_tables(). E.g. in
- mysql_alter_table(), mysql_rm_table_part2(), mysql_truncate(),
- drop_open_table().
- */
- if (table->child_l || table->parent)
- detach_merge_children(table, TRUE);
-
if (table->prev)
{
table->prev->next= table->next;
@@ -2010,7 +2289,7 @@ void close_temporary(TABLE *table, bool free_share, bool delete_table)
if (free_share)
{
free_table_share(table->s);
- my_free((char*) table,MYF(0));
+ my_free(table);
}
DBUG_VOID_RETURN;
}
@@ -2044,181 +2323,64 @@ bool rename_temporary_table(THD* thd, TABLE *table, const char *db,
}
- /* move table first in unused links */
-
-static void relink_unused(TABLE *table)
-{
- /* Assert that MERGE children are not attached in unused_tables. */
- DBUG_ASSERT(!table->is_children_attached());
-
- if (table != unused_tables)
- {
- table->prev->next=table->next; /* Remove from unused list */
- table->next->prev=table->prev;
- table->next=unused_tables; /* Link in unused tables */
- table->prev=unused_tables->prev;
- unused_tables->prev->next=table;
- unused_tables->prev=table;
- unused_tables=table;
- check_unused();
- }
-}
-
-
/**
- Prepare an open merge table for close.
+ Force all other threads to stop using the table by upgrading
+ metadata lock on it and remove unused TABLE instances from cache.
- @param[in] thd thread context
- @param[in] table table to prepare
- @param[in,out] prev_pp pointer to pointer of previous table
+ @param thd Thread handler
+ @param table Table to remove from cache
+ @param function HA_EXTRA_PREPARE_FOR_DROP if table is to be deleted
+ HA_EXTRA_FORCE_REOPEN if table is not be used
+ HA_EXTRA_PREPARE_FOR_RENAME if table is to be renamed
+ HA_EXTRA_NOT_USED Don't call extra()
- @detail
- If the table is a MERGE parent, just detach the children.
- If the table is a MERGE child, close the parent (incl. detach).
-*/
+ @note When returning, the table will be unusable for other threads
+ until metadata lock is downgraded.
-static void unlink_open_merge(THD *thd, TABLE *table, TABLE ***prev_pp)
-{
- DBUG_ENTER("unlink_open_merge");
-
- if (table->parent)
- {
- /*
- If MERGE child, close parent too. Closing includes detaching.
-
- This is used for example in ALTER TABLE t1 RENAME TO t5 under
- LOCK TABLES where t1 is a MERGE child:
- CREATE TABLE t1 (c1 INT);
- CREATE TABLE t2 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1);
- LOCK TABLES t1 WRITE, t2 WRITE;
- ALTER TABLE t1 RENAME TO t5;
- */
- TABLE *parent= table->parent;
- TABLE **prv_p;
-
- /* Find parent in open_tables list. */
- for (prv_p= &thd->open_tables;
- *prv_p && (*prv_p != parent);
- prv_p= &(*prv_p)->next) {}
- if (*prv_p)
- {
- /* Special treatment required if child follows parent in list. */
- if (*prev_pp == &parent->next)
- *prev_pp= prv_p;
- /*
- Remove parent from open_tables list and close it.
- This includes detaching and hence clearing parent references.
- */
- DBUG_PRINT("info", ("Closing parent to '%s'.'%s'",
- table->s->db.str, table->s->table_name.str));
- close_thread_table(thd, prv_p);
- }
- }
- else if (table->child_l)
- {
- /*
- When closing a MERGE parent, detach the children first. It is
- not necessary to clear the child or parent table reference of
- this table because the TABLE is freed. But we need to clear
- the child or parent references of the other belonging tables
- so that they cannot be moved into the unused_tables chain with
- these pointers set.
-
- This is used for example in ALTER TABLE t2 RENAME TO t5 under
- LOCK TABLES where t2 is a MERGE parent:
- CREATE TABLE t1 (c1 INT);
- CREATE TABLE t2 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1);
- LOCK TABLES t1 WRITE, t2 WRITE;
- ALTER TABLE t2 RENAME TO t5;
- */
- detach_merge_children(table, TRUE);
- }
-
- DBUG_VOID_RETURN;
-}
-
-
-/**
- Remove all instances of table from thread's open list and
- table cache.
-
- @param thd Thread context
- @param find Table to remove
- @param unlock TRUE - free all locks on tables removed that are
- done with LOCK TABLES
- FALSE - otherwise
-
- @note When unlock parameter is FALSE or current thread doesn't have
- any tables locked with LOCK TABLES, tables are assumed to be
- not locked (for example already unlocked).
+ @retval FALSE Success.
+ @retval TRUE Failure (e.g. because thread was killed).
*/
-void unlink_open_table(THD *thd, TABLE *find, bool unlock)
+bool wait_while_table_is_used(THD *thd, TABLE *table,
+ enum ha_extra_function function,
+ enum_tdc_remove_table_type remove_type)
{
- char key[MAX_DBKEY_LENGTH];
- uint key_length= find->s->table_cache_key.length;
- TABLE *list, **prev;
- DBUG_ENTER("unlink_open_table");
-
- safe_mutex_assert_owner(&LOCK_open);
-
- memcpy(key, find->s->table_cache_key.str, key_length);
- /*
- Note that we need to hold LOCK_open while changing the
- open_tables list. Another thread may work on it.
- (See: remove_table_from_cache(), mysql_wait_completed_table())
- Closing a MERGE child before the parent would be fatal if the
- other thread tries to abort the MERGE lock in between.
- */
- for (prev= &thd->open_tables; *prev; )
- {
- list= *prev;
-
- if (list->s->table_cache_key.length == key_length &&
- !memcmp(list->s->table_cache_key.str, key, key_length))
- {
- if (unlock && thd->locked_tables)
- mysql_lock_remove(thd, thd->locked_tables,
- list->parent ? list->parent : list, TRUE);
-
- /* Prepare MERGE table for close. Close parent if necessary. */
- unlink_open_merge(thd, list, &prev);
+ DBUG_ENTER("wait_while_table_is_used");
+ DBUG_PRINT("enter", ("table: '%s' share: 0x%lx db_stat: %u version: %lu",
+ table->s->table_name.str, (ulong) table->s,
+ table->db_stat, table->s->version));
- /* Remove table from open_tables list. */
- *prev= list->next;
- /* Close table. */
- VOID(hash_delete(&open_cache,(uchar*) list)); // Close table
- }
- else
- {
- /* Step to next entry in open_tables list. */
- prev= &list->next;
- }
- }
+ if (thd->mdl_context.upgrade_shared_lock_to_exclusive(
+ table->mdl_ticket, thd->variables.lock_wait_timeout))
+ DBUG_RETURN(TRUE);
- // Notify any 'refresh' threads
- broadcast_refresh();
- DBUG_VOID_RETURN;
+ tdc_remove_table(thd, remove_type,
+ table->s->db.str, table->s->table_name.str,
+ FALSE);
+ /* extra() call must come only after all instances above are closed */
+ if (function != HA_EXTRA_NOT_USED)
+ (void) table->file->extra(function);
+ DBUG_RETURN(FALSE);
}
/**
- Auxiliary routine which closes and drops open table.
-
- @param thd Thread handle
- @param table TABLE object for table to be dropped
- @param db_name Name of database for this table
- @param table_name Name of this table
-
- @note This routine assumes that table to be closed is open only
- by calling thread so we needn't wait until other threads
- will close the table. Also unless called under implicit or
- explicit LOCK TABLES mode it assumes that table to be
- dropped is already unlocked. In the former case it will
- also remove lock on the table. But one should not rely on
- this behaviour as it may change in future.
- Currently, however, this function is never called for a
- table that was locked with LOCK TABLES.
+ Close a and drop a just created table in CREATE TABLE ... SELECT.
+
+ @param thd Thread handle
+ @param table TABLE object for the table to be dropped
+ @param db_name Name of database for this table
+ @param table_name Name of this table
+
+ This routine assumes that the table to be closed is open only
+ by the calling thread, so we needn't wait until other threads
+ close the table. It also assumes that the table is first
+ in thd->open_ables and a data lock on it, if any, has been
+ released. To sum up, it's tuned to work with
+ CREATE TABLE ... SELECT and CREATE TABLE .. SELECT only.
+ Note, that currently CREATE TABLE ... SELECT is not supported
+ under LOCK TABLES. This function, still, can be called in
+ prelocked mode, e.g. if we do CREATE TABLE .. SELECT f1();
*/
void drop_open_table(THD *thd, TABLE *table, const char *db_name,
@@ -2229,364 +2391,307 @@ void drop_open_table(THD *thd, TABLE *table, const char *db_name,
close_temporary_table(thd, table, 1, 1);
else
{
+ DBUG_ASSERT(table == thd->open_tables);
+
handlerton *table_type= table->s->db_type();
- VOID(pthread_mutex_lock(&LOCK_open));
- /*
- unlink_open_table() also tells threads waiting for refresh or close
- that something has happened.
- */
table->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
- unlink_open_table(thd, table, FALSE);
+ 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,
+ FALSE);
+ /* Remove the table from the storage engine and rm the .frm. */
quick_rm_table(table_type, db_name, table_name, 0);
- VOID(pthread_mutex_unlock(&LOCK_open));
}
DBUG_VOID_RETURN;
}
-/*
- Wait for condition but allow the user to send a kill to mysqld
-
- SYNOPSIS
- wait_for_condition()
- thd Thread handler
- mutex mutex that is currently hold that is associated with condition
- Will be unlocked on return
- cond Condition to wait for
-*/
-
-void wait_for_condition(THD *thd, pthread_mutex_t *mutex, pthread_cond_t *cond)
-{
- /* Wait until the current table is up to date */
- const char *proc_info;
- thd->mysys_var->current_mutex= mutex;
- thd->mysys_var->current_cond= cond;
- proc_info=thd->proc_info;
- thd_proc_info(thd, "Waiting for table");
- DBUG_ENTER("wait_for_condition");
- DEBUG_SYNC(thd, "waiting_for_table");
- if (!thd->killed)
- (void) pthread_cond_wait(cond, mutex);
-
- /*
- We must unlock mutex first to avoid deadlock becasue conditions are
- sent to this thread by doing locks in the following order:
- lock(mysys_var->mutex)
- lock(mysys_var->current_mutex)
-
- One by effect of this that one can only use wait_for_condition with
- condition variables that are guranteed to not disapper (freed) even if this
- mutex is unlocked
- */
-
- pthread_mutex_unlock(mutex);
- DEBUG_SYNC(thd, "waiting_for_table_unlock");
- DBUG_EXECUTE_IF("sleep_after_waiting_for_table", my_sleep(1000000););
- pthread_mutex_lock(&thd->mysys_var->mutex);
- thd->mysys_var->current_mutex= 0;
- thd->mysys_var->current_cond= 0;
- thd_proc_info(thd, proc_info);
- pthread_mutex_unlock(&thd->mysys_var->mutex);
- DBUG_VOID_RETURN;
-}
-
-
/**
- Exclusively name-lock a table that is already write-locked by the
- current thread.
-
- @param thd current thread context
- @param tables table list containing one table to open.
-
- @return FALSE on success, TRUE otherwise.
-*/
-
-bool name_lock_locked_table(THD *thd, TABLE_LIST *tables)
-{
- DBUG_ENTER("name_lock_locked_table");
-
- /* Under LOCK TABLES we must only accept write locked tables. */
- tables->table= find_locked_table(thd, tables->db, tables->table_name);
-
- if (!tables->table)
- my_error(ER_TABLE_NOT_LOCKED, MYF(0), tables->alias);
- else if (tables->table->reginfo.lock_type < TL_WRITE_LOW_PRIORITY)
- my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), tables->alias);
- else
- {
- /*
- Ensures that table is opened only by this thread and that no
- other statement will open this table.
- */
- wait_while_table_is_used(thd, tables->table, HA_EXTRA_FORCE_REOPEN);
- DBUG_RETURN(FALSE);
- }
-
- DBUG_RETURN(TRUE);
-}
-
+ Check that table exists in table definition cache, on disk
+ or in some storage engine.
-/*
- Open table which is already name-locked by this thread.
+ @param thd Thread context
+ @param table Table list element
+ @param fast_check Check only if share or .frm file exists
+ @param[out] exists Out parameter which is set to TRUE if table
+ exists and to FALSE otherwise.
- SYNOPSIS
- reopen_name_locked_table()
- thd Thread handle
- table_list TABLE_LIST object for table to be open, TABLE_LIST::table
- member should point to TABLE object which was used for
- name-locking.
- link_in TRUE - if TABLE object for table to be opened should be
- linked into THD::open_tables list.
- FALSE - placeholder used for name-locking is already in
- this list so we only need to preserve TABLE::next
- pointer.
+ @note This function acquires LOCK_open internally.
- NOTE
- This function assumes that its caller already acquired LOCK_open mutex.
+ @note If there is no .FRM file for the table but it exists in one
+ of engines (e.g. it was created on another node of NDB cluster)
+ this function will fetch and create proper .FRM file for it.
- RETURN VALUE
- FALSE - Success
- TRUE - Error
+ @retval TRUE Some error occurred
+ @retval FALSE No error. 'exists' out parameter set accordingly.
*/
-bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in)
+bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool fast_check,
+ bool *exists)
{
- TABLE *table= table_list->table;
+ char path[FN_REFLEN + 1];
TABLE_SHARE *share;
- char *table_name= table_list->table_name;
- TABLE orig_table;
- DBUG_ENTER("reopen_name_locked_table");
+ DBUG_ENTER("check_if_table_exists");
- safe_mutex_assert_owner(&LOCK_open);
+ *exists= TRUE;
- if (thd->killed || !table)
- DBUG_RETURN(TRUE);
+ DBUG_ASSERT(fast_check ||
+ thd->mdl_context.
+ is_lock_owner(MDL_key::TABLE, table->db,
+ table->table_name, MDL_SHARED));
- /*
- make a copy. we may need to restore it later.
- don't use orig_table=*table, because we need an exact replica,
- not a C++ copy that may modify the data in the copy constructor.
- */
- memcpy(&orig_table, table, sizeof(*table));
+ mysql_mutex_lock(&LOCK_open);
+ share= get_cached_table_share(table->db, table->table_name);
+ mysql_mutex_unlock(&LOCK_open);
- if (open_unireg_entry(thd, table, table_list, table_name,
- table->s->table_cache_key.str,
- table->s->table_cache_key.length, thd->mem_root, 0))
- {
- intern_close_table(table);
- /*
- If there was an error during opening of table (for example if it
- does not exist) '*table' object can be wiped out. To be able
- properly release name-lock in this case we should restore this
- object to its original state.
- */
- memcpy(table, &orig_table, sizeof(*table));
- bzero(&orig_table, sizeof(orig_table)); // Ensure alias is not freed
- DBUG_RETURN(TRUE);
- }
+ if (share)
+ goto end;
- share= table->s;
- /*
- We want to prevent other connections from opening this table until end
- of statement as it is likely that modifications of table's metadata are
- not yet finished (for example CREATE TRIGGER have to change .TRG file,
- or we might want to drop table if CREATE TABLE ... SELECT fails).
- This also allows us to assume that no other connection will sneak in
- before we will get table-level lock on this table.
- */
- share->version=0;
- table->in_use = thd;
- check_unused();
+ build_table_filename(path, sizeof(path) - 1, table->db, table->table_name,
+ reg_ext, 0);
- if (link_in)
+ if (!access(path, F_OK))
+ goto end;
+
+ if (fast_check)
{
- table->next= thd->open_tables;
- thd->open_tables= table;
+ *exists= FALSE;
+ goto end;
}
- else
+
+ /* .FRM file doesn't exist. Check if some engine can provide it. */
+ if (ha_check_if_table_exists(thd, table->db, table->table_name, exists))
{
- /*
- TABLE object should be already in THD::open_tables list so we just
- need to set TABLE::next correctly.
- */
- table->next= orig_table.next;
+ my_printf_error(ER_OUT_OF_RESOURCES, "Failed to open '%-.64s', error while "
+ "unpacking from engine", MYF(0), table->table_name);
+ DBUG_RETURN(TRUE);
}
-
- table->tablenr=thd->current_tablenr++;
- table->used_fields=0;
- table->const_table=0;
- table->null_row= 0;
- table->maybe_null= 0;
- table->force_index= table->force_index_order= table->force_index_group= 0;
- table->status=STATUS_NO_RECORD;
+end:
DBUG_RETURN(FALSE);
}
/**
- Create and insert into table cache placeholder for table
- which will prevent its opening (or creation) (a.k.a lock
- table name).
-
- @param thd Thread context
- @param key Table cache key for name to be locked
- @param key_length Table cache key length
-
- @return Pointer to TABLE object used for name locking or 0 in
- case of failure.
+ An error handler which converts, if possible, ER_LOCK_DEADLOCK error
+ that can occur when we are trying to acquire a metadata lock to
+ a request for back-off and re-start of open_tables() process.
*/
-TABLE *table_cache_insert_placeholder(THD *thd, const char *key,
- uint key_length)
+class MDL_deadlock_handler : public Internal_error_handler
{
- TABLE *table;
- TABLE_SHARE *share;
- char *key_buff;
- DBUG_ENTER("table_cache_insert_placeholder");
+public:
+ MDL_deadlock_handler(Open_table_context *ot_ctx_arg)
+ : m_ot_ctx(ot_ctx_arg), m_is_active(FALSE)
+ {}
- safe_mutex_assert_owner(&LOCK_open);
+ virtual ~MDL_deadlock_handler() {}
- /*
- Create a table entry with the right key and with an old refresh version
- Note that we must use my_multi_malloc() here as this is freed by the
- table cache
+ virtual bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl);
+
+private:
+ /** Open table context to be used for back-off request. */
+ Open_table_context *m_ot_ctx;
+ /**
+ Indicates that we are already in the process of handling
+ ER_LOCK_DEADLOCK error. Allows to re-emit the error from
+ the error handler without falling into infinite recursion.
*/
- if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
- &table, sizeof(*table),
- &share, sizeof(*share),
- &key_buff, key_length,
- NULL))
- DBUG_RETURN(NULL);
+ bool m_is_active;
+};
- table->s= share;
- share->set_table_cache_key(key_buff, key, key_length);
- share->tmp_table= INTERNAL_TMP_TABLE; // for intern_close_table
- table->in_use= thd;
- table->locked_by_name=1;
- if (my_hash_insert(&open_cache, (uchar*)table))
+bool MDL_deadlock_handler::handle_condition(THD *,
+ uint sql_errno,
+ const char*,
+ MYSQL_ERROR::enum_warning_level,
+ const char*,
+ MYSQL_ERROR ** cond_hdl)
+{
+ *cond_hdl= NULL;
+ if (! m_is_active && sql_errno == ER_LOCK_DEADLOCK)
{
- my_free((uchar*) table, MYF(0));
- DBUG_RETURN(NULL);
+ /* Disable the handler to avoid infinite recursion. */
+ m_is_active= TRUE;
+ (void) m_ot_ctx->request_backoff_action(
+ Open_table_context::OT_BACKOFF_AND_RETRY,
+ NULL);
+ m_is_active= FALSE;
+ /*
+ If the above back-off request failed, a new instance of
+ ER_LOCK_DEADLOCK error was emitted. Thus the current
+ instance of error condition can be treated as handled.
+ */
+ return TRUE;
}
-
- DBUG_RETURN(table);
+ return FALSE;
}
/**
- Obtain an exclusive name lock on the table if it is not cached
- in the table cache.
-
- @param thd Thread context
- @param db Name of database
- @param table_name Name of table
- @param[out] table Out parameter which is either:
- - set to NULL if table cache contains record for
- the table or
- - set to point to the TABLE instance used for
- name-locking.
-
- @note This function takes into account all records for table in table
- cache, even placeholders used for name-locking. This means that
- 'table' parameter can be set to NULL for some situations when
- table does not really exist.
-
- @retval TRUE Error occured (OOM)
- @retval FALSE Success. 'table' parameter set according to above rules.
+ Try to acquire an MDL lock for a table being opened.
+
+ @param[in,out] thd Session context, to report errors.
+ @param[out] ot_ctx Open table context, to hold the back off
+ state. If we failed to acquire a lock
+ due to a lock conflict, we add the
+ failed request to the open table context.
+ @param[in,out] mdl_request A request for an MDL lock.
+ If we managed to acquire a ticket
+ (no errors or lock conflicts occurred),
+ contains a reference to it on
+ return. However, is not modified if MDL
+ lock type- modifying flags were provided.
+ @param[in] flags flags MYSQL_OPEN_FORCE_SHARED_MDL,
+ MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL or
+ MYSQL_OPEN_FAIL_ON_MDL_CONFLICT
+ @sa open_table().
+ @param[out] mdl_ticket Only modified if there was no error.
+ If we managed to acquire an MDL
+ lock, contains a reference to the
+ ticket, otherwise is set to NULL.
+
+ @retval TRUE An error occurred.
+ @retval FALSE No error, but perhaps a lock conflict, check mdl_ticket.
*/
-bool lock_table_name_if_not_cached(THD *thd, const char *db,
- const char *table_name, TABLE **table)
+static bool
+open_table_get_mdl_lock(THD *thd, Open_table_context *ot_ctx,
+ MDL_request *mdl_request,
+ uint flags,
+ MDL_ticket **mdl_ticket)
{
- char key[MAX_DBKEY_LENGTH];
- uint key_length;
- DBUG_ENTER("lock_table_name_if_not_cached");
+ MDL_request mdl_request_shared;
- key_length= create_table_def_key(key, db, table_name);
- VOID(pthread_mutex_lock(&LOCK_open));
+ if (flags & (MYSQL_OPEN_FORCE_SHARED_MDL |
+ MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL))
+ {
+ /*
+ MYSQL_OPEN_FORCE_SHARED_MDL flag means that we are executing
+ PREPARE for a prepared statement and want to override
+ the type-of-operation aware metadata lock which was set
+ in the parser/during view opening with a simple shared
+ metadata lock.
+ This is necessary to allow concurrent execution of PREPARE
+ and LOCK TABLES WRITE statement against the same table.
+
+ MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL flag means that we open
+ the table in order to get information about it for one of I_S
+ queries and also want to override the type-of-operation aware
+ shared metadata lock which was set earlier (e.g. during view
+ opening) with a high-priority shared metadata lock.
+ This is necessary to avoid unnecessary waiting and extra
+ ER_WARN_I_S_SKIPPED_TABLE warnings when accessing I_S tables.
+
+ These two flags are mutually exclusive.
+ */
+ DBUG_ASSERT(!(flags & MYSQL_OPEN_FORCE_SHARED_MDL) ||
+ !(flags & MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL));
+
+ mdl_request_shared.init(&mdl_request->key,
+ (flags & MYSQL_OPEN_FORCE_SHARED_MDL) ?
+ MDL_SHARED : MDL_SHARED_HIGH_PRIO,
+ MDL_TRANSACTION);
+ mdl_request= &mdl_request_shared;
+ }
- if (hash_search(&open_cache, (uchar *)key, key_length))
+ if (flags & MYSQL_OPEN_FAIL_ON_MDL_CONFLICT)
{
- VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_PRINT("info", ("Table is cached, name-lock is not obtained"));
- *table= 0;
- DBUG_RETURN(FALSE);
+ /*
+ When table is being open in order to get data for I_S table,
+ we might have some tables not only open but also locked (e.g. when
+ this happens under LOCK TABLES or in a stored function).
+ As a result by waiting on a conflicting metadata lock to go away
+ we may create a deadlock which won't entirely belong to the
+ MDL subsystem and thus won't be detectable by this subsystem's
+ deadlock detector.
+ To avoid such situation we skip the trouble-making table if
+ there is a conflicting lock.
+ */
+ if (thd->mdl_context.try_acquire_lock(mdl_request))
+ return TRUE;
+ if (mdl_request->ticket == NULL)
+ {
+ my_error(ER_WARN_I_S_SKIPPED_TABLE, MYF(0),
+ mdl_request->key.db_name(), mdl_request->key.name());
+ return TRUE;
+ }
}
- if (!(*table= table_cache_insert_placeholder(thd, key, key_length)))
+ else
{
- VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(TRUE);
+ /*
+ We are doing a normal table open. Let us try to acquire a metadata
+ lock on the table. If there is a conflicting lock, acquire_lock()
+ will wait for it to go away. Sometimes this waiting may lead to a
+ deadlock, with the following results:
+ 1) If a deadlock is entirely within MDL subsystem, it is
+ detected by the deadlock detector of this subsystem.
+ ER_LOCK_DEADLOCK error is produced. Then, the error handler
+ that is installed prior to the call to acquire_lock() attempts
+ to request a back-off and retry. Upon success, ER_LOCK_DEADLOCK
+ error is suppressed, otherwise propagated up the calling stack.
+ 2) Otherwise, a deadlock may occur when the wait-for graph
+ includes edges not visible to the MDL deadlock detector.
+ One such example is a wait on an InnoDB row lock, e.g. when:
+ conn C1 gets SR MDL lock on t1 with SELECT * FROM t1
+ conn C2 gets a row lock on t2 with SELECT * FROM t2 FOR UPDATE
+ conn C3 gets in and waits on C1 with DROP TABLE t0, t1
+ conn C2 continues and blocks on C3 with SELECT * FROM t0
+ conn C1 deadlocks by waiting on C2 by issuing SELECT * FROM
+ t2 LOCK IN SHARE MODE.
+ Such circular waits are currently only resolved by timeouts,
+ e.g. @@innodb_lock_wait_timeout or @@lock_wait_timeout.
+ */
+ MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);
+
+ thd->push_internal_handler(&mdl_deadlock_handler);
+ bool result= thd->mdl_context.acquire_lock(mdl_request,
+ ot_ctx->get_timeout());
+ thd->pop_internal_handler();
+
+ if (result && !ot_ctx->can_recover_from_failed_open())
+ return TRUE;
}
- (*table)->open_placeholder= 1;
- (*table)->next= thd->open_tables;
- thd->open_tables= *table;
- VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(FALSE);
+ *mdl_ticket= mdl_request->ticket;
+ return FALSE;
}
/**
- Check that table exists in table definition cache, on disk
- or in some storage engine.
-
- @param thd Thread context
- @param table Table list element
- @param[out] exists Out parameter which is set to TRUE if table
- exists and to FALSE otherwise.
+ Check if table's share is being removed from the table definition
+ cache and, if yes, wait until the flush is complete.
- @note This function assumes that caller owns LOCK_open mutex.
- It also assumes that the fact that there are no name-locks
- on the table was checked beforehand.
-
- @note If there is no .FRM file for the table but it exists in one
- of engines (e.g. it was created on another node of NDB cluster)
- this function will fetch and create proper .FRM file for it.
+ @param thd Thread context.
+ @param table_list Table which share should be checked.
+ @param timeout Timeout for waiting.
+ @param deadlock_weight Weight of this wait for deadlock detector.
- @retval TRUE Some error occured
- @retval FALSE No error. 'exists' out parameter set accordingly.
+ @retval FALSE Success. Share is up to date or has been flushed.
+ @retval TRUE Error (OOM, our was killed, the wait resulted
+ in a deadlock or timeout). Reported.
*/
-bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists)
+static bool
+tdc_wait_for_old_version(THD *thd, const char *db, const char *table_name,
+ ulong wait_timeout, uint deadlock_weight)
{
- char path[FN_REFLEN + 1];
- int rc;
- DBUG_ENTER("check_if_table_exists");
-
- safe_mutex_assert_owner(&LOCK_open);
-
- *exists= TRUE;
-
- if (get_cached_table_share(table->db, table->table_name))
- DBUG_RETURN(FALSE);
-
- build_table_filename(path, sizeof(path) - 1, table->db, table->table_name,
- reg_ext, 0);
-
- if (!access(path, F_OK))
- DBUG_RETURN(FALSE);
-
- /* .FRM file doesn't exist. Check if some engine can provide it. */
-
- rc= ha_create_table_from_engine(thd, table->db, table->table_name);
+ TABLE_SHARE *share;
+ bool res= FALSE;
- if (rc < 0)
- {
- /* Table does not exists in engines as well. */
- *exists= FALSE;
- DBUG_RETURN(FALSE);
- }
- else if (!rc)
- {
- /* Table exists in some engine and .FRM for it was created. */
- DBUG_RETURN(FALSE);
- }
- else /* (rc > 0) */
+ mysql_mutex_lock(&LOCK_open);
+ if ((share= get_cached_table_share(db, table_name)) &&
+ share->has_old_version())
{
- my_printf_error(ER_UNKNOWN_ERROR, "Failed to open '%-.64s', error while "
- "unpacking from engine", MYF(0), table->table_name);
- DBUG_RETURN(TRUE);
+ struct timespec abstime;
+ set_timespec(abstime, wait_timeout);
+ res= share->wait_for_old_version(thd, &abstime, deadlock_weight);
}
+ mysql_mutex_unlock(&LOCK_open);
+ return res;
}
@@ -2597,55 +2702,59 @@ bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists)
open_table()
thd Thread context.
table_list Open first table in list.
- refresh INOUT Pointer to memory that will be set to 1 if
- we need to close all tables and reopen them.
- If this is a NULL pointer, then the table is not
- put in the thread-open-list.
+ action INOUT Pointer to variable of enum_open_table_action type
+ which will be set according to action which is
+ required to remedy problem appeared during attempt
+ to open table.
flags Bitmap of flags to modify how open works:
- MYSQL_LOCK_IGNORE_FLUSH - Open table even if
- someone has done a flush on it.
+ MYSQL_OPEN_IGNORE_FLUSH - Open table even if
+ someone has done a flush or there is a pending
+ exclusive metadata lock requests against it
+ (i.e. request high priority metadata lock).
No version number checking is done.
MYSQL_OPEN_TEMPORARY_ONLY - Open only temporary
table not the base table or view.
+ MYSQL_OPEN_TAKE_UPGRADABLE_MDL - Obtain upgradable
+ metadata lock for tables on which we are going to
+ take some kind of write table-level lock.
IMPLEMENTATION
Uses a cache of open tables to find a table not in use.
- If table list element for the table to be opened has "create" flag
- set and table does not exist, this function will automatically insert
- a placeholder for exclusive name lock into the open tables cache and
- will return the TABLE instance that corresponds to this placeholder.
+ If TABLE_LIST::open_strategy is set to OPEN_IF_EXISTS, the table is opened
+ only if it exists. If the open strategy is OPEN_STUB, the underlying table
+ is never opened. In both cases, metadata locks are always taken according
+ to the lock strategy.
RETURN
- NULL Open failed. If refresh is set then one should close
- all other tables and retry the open.
- # Success. Pointer to TABLE object for open table.
+ TRUE Open failed. "action" parameter may contain type of action
+ needed to remedy problem before retrying again.
+ FALSE Success. Members of TABLE_LIST structure are filled properly (e.g.
+ TABLE_LIST::table is set for real tables and TABLE_LIST::view is
+ set for views).
*/
-TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
- bool *refresh, uint flags)
+bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
+ Open_table_context *ot_ctx)
{
reg1 TABLE *table;
char key[MAX_DBKEY_LENGTH];
uint key_length;
char *alias= table_list->alias;
- HASH_SEARCH_STATE state;
+ uint flags= ot_ctx->get_flags();
+ MDL_ticket *mdl_ticket;
+ int error;
+ TABLE_SHARE *share;
+ my_hash_value_type hash_value;
DBUG_ENTER("open_table");
- /* Parsing of partitioning information from .frm needs thd->lex set up. */
- DBUG_ASSERT(thd->lex->is_lex_started);
-
- /* find a unused table in the open table cache */
- if (refresh)
- *refresh=0;
-
/* an open table operation needs a lot of the stack space */
if (check_stack_overrun(thd, STACK_MIN_SIZE_FOR_OPEN, (uchar *)&alias))
- DBUG_RETURN(0);
+ DBUG_RETURN(TRUE);
if (thd->killed)
- DBUG_RETURN(0);
+ DBUG_RETURN(TRUE);
key_length= (create_table_def_key(thd, key, table_list, 1) -
TMP_TABLE_KEY_EXTRA);
@@ -2657,7 +2766,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
same name. This block implements the behaviour.
TODO: move this block into a separate function.
*/
- if (!table_list->skip_temporary)
+ if (table_list->open_type != OT_BASE_ONLY &&
+ ! (flags & MYSQL_OPEN_SKIP_TEMPORARY))
{
for (table= thd->temporary_tables; table ; table=table->next)
{
@@ -2679,7 +2789,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
(ulong) table->query_id, (uint) thd->server_id,
(ulong) thd->variables.pseudo_thread_id));
my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr());
- DBUG_RETURN(0);
+ DBUG_RETURN(TRUE);
}
table->query_id= thd->query_id;
thd->thread_specific_used= TRUE;
@@ -2689,10 +2799,16 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
}
}
- if (flags & MYSQL_OPEN_TEMPORARY_ONLY)
+ if (table_list->open_type == OT_TEMPORARY_ONLY ||
+ (flags & MYSQL_OPEN_TEMPORARY_ONLY))
{
- my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->table_name);
- DBUG_RETURN(0);
+ if (table_list->open_strategy == TABLE_LIST::OPEN_NORMAL)
+ {
+ my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->table_name);
+ DBUG_RETURN(TRUE);
+ }
+ else
+ DBUG_RETURN(FALSE);
}
/*
@@ -2702,7 +2818,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
open not pre-opened tables in pre-locked/LOCK TABLES mode.
TODO: move this block into a separate function.
*/
- if (thd->locked_tables || thd->prelocked_mode)
+ if (thd->locked_tables_mode &&
+ ! (flags & MYSQL_OPEN_GET_NEW_TABLE))
{ // Using table locks
TABLE *best_table= 0;
int best_distance= INT_MIN;
@@ -2711,17 +2828,14 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
if (table->s->table_cache_key.length == key_length &&
!memcmp(table->s->table_cache_key.str, key, key_length))
{
- /*
- When looking for a usable TABLE, ignore MERGE children, as they
- belong to their parent and cannot be used explicitly.
- */
if (!my_strcasecmp(system_charset_info, table->alias.c_ptr(), alias) &&
table->query_id != thd->query_id && /* skip tables already used */
- !(thd->prelocked_mode && table->query_id) &&
- !table->parent)
+ (thd->locked_tables_mode == LTM_LOCK_TABLES ||
+ table->query_id == 0))
{
int distance= ((int) table->reginfo.lock_type -
(int) table_list->lock_type);
+
/*
Find a table that either has the exact lock type requested,
or has the best suitable lock. In case there is no locked
@@ -2764,29 +2878,35 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
Is this table a view and not a base table?
(it is work around to allow to open view with locked tables,
real fix will be made after definition cache will be made)
+
+ Since opening of view which was not explicitly locked by LOCK
+ TABLES breaks metadata locking protocol (potentially can lead
+ to deadlocks) it should be disallowed.
*/
+ if (thd->mdl_context.is_lock_owner(MDL_key::TABLE,
+ table_list->db,
+ table_list->table_name,
+ MDL_SHARED))
{
char path[FN_REFLEN + 1];
enum legacy_db_type not_used;
build_table_filename(path, sizeof(path) - 1,
table_list->db, table_list->table_name, reg_ext, 0);
- if (mysql_frm_type(thd, path, &not_used) == FRMTYPE_VIEW)
+ /*
+ 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
+ instance in THD::open_tables list or were unable to open table
+ during prelocking process (in this case in theory we still
+ should hold shared metadata lock on it).
+ */
+ if (dd_frm_type(thd, path, &not_used) == FRMTYPE_VIEW)
{
- /*
- Will not be used (because it's VIEW) but has to be passed.
- Also we will not free it (because it is a stack variable).
- */
- TABLE tab;
- table= &tab;
- VOID(pthread_mutex_lock(&LOCK_open));
- if (!open_unireg_entry(thd, table, table_list, alias,
- key, key_length, mem_root, 0))
+ if (!tdc_open_view(thd, table_list, alias, key, key_length,
+ mem_root, 0))
{
DBUG_ASSERT(table_list->view != 0);
- VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(0); // VIEW
+ DBUG_RETURN(FALSE); // VIEW
}
- VOID(pthread_mutex_unlock(&LOCK_open));
}
}
/*
@@ -2796,1120 +2916,742 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
so we may only end up here if the table did not exist when
locked tables list was created.
*/
- if (thd->prelocked_mode == PRELOCKED)
+ if (thd->locked_tables_mode == LTM_PRELOCKED)
my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias);
else
my_error(ER_TABLE_NOT_LOCKED, MYF(0), alias);
- DBUG_RETURN(0);
- }
-
- /*
- Non pre-locked/LOCK TABLES mode, and the table is not temporary:
- this is the normal use case.
- Now we should:
- - try to find the table in the table cache.
- - if one of the discovered TABLE instances is name-locked
- (table->s->version == 0) or some thread has started FLUSH TABLES
- (refresh_version > table->s->version), back off -- we have to wait
- until no one holds a name lock on the table.
- - if there is no such TABLE in the name cache, read the table definition
- and insert it into the cache.
- We perform all of the above under LOCK_open which currently protects
- the open cache (also known as table cache) and table definitions stored
- on disk.
- */
-
- VOID(pthread_mutex_lock(&LOCK_open));
-
- /*
- If it's the first table from a list of tables used in a query,
- remember refresh_version (the version of open_cache state).
- If the version changes while we're opening the remaining tables,
- we will have to back off, close all the tables opened-so-far,
- and try to reopen them.
- Note: refresh_version is currently changed only during FLUSH TABLES.
- */
- if (!thd->open_tables)
- thd->version=refresh_version;
- else if ((thd->version != refresh_version) &&
- ! (flags & MYSQL_LOCK_IGNORE_FLUSH))
- {
- /* Someone did a refresh while thread was opening tables */
- if (refresh)
- *refresh=1;
- VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(0);
+ DBUG_RETURN(TRUE);
}
/*
- In order for the back off and re-start process to work properly,
- handler tables having old versions (due to FLUSH TABLES or pending
- name-lock) MUST be closed. This is specially important if a name-lock
- is pending for any table of the handler_tables list, otherwise a
- deadlock may occur.
+ Non pre-locked/LOCK TABLES mode, and the table is not temporary.
+ This is the normal use case.
*/
- if (thd->handler_tables)
- mysql_ha_flush(thd);
- /*
- Actually try to find the table in the open_cache.
- The cache may contain several "TABLE" instances for the same
- physical table. The instances that are currently "in use" by
- some thread have their "in_use" member != NULL.
- There is no good reason for having more than one entry in the
- hash for the same physical table, except that we use this as
- an implicit "pending locks queue" - see
- wait_for_locked_table_names for details.
- */
- for (table= (TABLE*) hash_first(&open_cache, (uchar*) key, key_length,
- &state);
- table && table->in_use ;
- table= (TABLE*) hash_next(&open_cache, (uchar*) key, key_length,
- &state))
+ if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK))
{
- DBUG_PRINT("tcache", ("in_use table: '%s'.'%s' 0x%lx", table->s->db.str,
- table->s->table_name.str, (long) table));
/*
- Here we flush tables marked for flush.
- Normally, table->s->version contains the value of
- refresh_version from the moment when this table was
- (re-)opened and added to the cache.
- If since then we did (or just started) FLUSH TABLES
- statement, refresh_version has been increased.
- For "name-locked" TABLE instances, table->s->version is set
- to 0 (see lock_table_name for details).
- In case there is a pending FLUSH TABLES or a name lock, we
- need to back off and re-start opening tables.
- If we do not back off now, we may dead lock in case of lock
- order mismatch with some other thread:
- c1: name lock t1; -- sort of exclusive lock
- c2: open t2; -- sort of shared lock
- c1: name lock t2; -- blocks
- c2: open t1; -- blocks
+ We are not under LOCK TABLES and going to acquire write-lock/
+ modify the base table. We need to acquire protection against
+ global read lock until end of this statement in order to have
+ this statement blocked by active FLUSH TABLES WITH READ LOCK.
+
+ We don't block acquire this protection under LOCK TABLES as
+ such protection already acquired at LOCK TABLES time and
+ not released until UNLOCK TABLES.
+
+ We don't block statements which modify only temporary tables
+ as these tables are not preserved by backup by any form of
+ backup which uses FLUSH TABLES WITH READ LOCK.
+
+ TODO: The fact that we sometimes acquire protection against
+ GRL only when we encounter table to be write-locked
+ slightly increases probability of deadlock.
+ This problem will be solved once Alik pushes his
+ temporary table refactoring patch and we can start
+ pre-acquiring metadata locks at the beggining of
+ open_tables() call.
*/
- if (table->needs_reopen_or_name_lock())
+ if (table_list->mdl_request.type >= MDL_SHARED_WRITE &&
+ ! (flags & (MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK |
+ MYSQL_OPEN_FORCE_SHARED_MDL |
+ MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
+ MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK)) &&
+ ! ot_ctx->has_protection_against_grl())
{
- DBUG_PRINT("note",
- ("Found table '%s.%s' with different refresh version",
- table_list->db, table_list->table_name));
+ MDL_request protection_request;
+ MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);
- /* Ignore FLUSH and pending name locks, but not acquired name locks! */
- if (flags & MYSQL_LOCK_IGNORE_FLUSH && !table->open_placeholder)
- {
- /* Force close at once after usage */
- thd->version= table->s->version;
- continue;
- }
+ if (thd->global_read_lock.can_acquire_protection())
+ DBUG_RETURN(TRUE);
- /* Avoid self-deadlocks by detecting self-dependencies. */
- if (table->open_placeholder && table->in_use == thd)
- {
- VOID(pthread_mutex_unlock(&LOCK_open));
- my_error(ER_UPDATE_TABLE_USED, MYF(0), table->s->table_name.str);
- DBUG_RETURN(0);
- }
+ protection_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
+ MDL_STATEMENT);
/*
- Back off, part 1: mark the table as "unused" for the
- purpose of name-locking by setting table->db_stat to 0. Do
- that only for the tables in this thread that have an old
- table->s->version (this is an optimization (?)).
- table->db_stat == 0 signals wait_for_locked_table_names
- that the tables in question are not used any more. See
- table_is_used call for details.
-
- Notice that HANDLER tables were already taken care of by
- the earlier call to mysql_ha_flush() in this same critical
- section.
- */
- close_old_data_files(thd,thd->open_tables,0,0);
- /*
- Back-off part 2: try to avoid "busy waiting" on the table:
- if the table is in use by some other thread, we suspend
- and wait till the operation is complete: when any
- operation that juggles with table->s->version completes,
- it broadcasts COND_refresh condition variable.
- If 'old' table we met is in use by current thread we return
- without waiting since in this situation it's this thread
- which is responsible for broadcasting on COND_refresh
- (and this was done already in close_old_data_files()).
- Good example of such situation is when we have statement
- that needs two instances of table and FLUSH TABLES comes
- after we open first instance but before we open second
- instance.
+ Install error handler which if possible will convert deadlock error
+ into request to back-off and restart process of opening tables.
*/
- if (table->in_use != thd)
- {
- /* wait_for_conditionwill unlock LOCK_open for us */
- wait_for_condition(thd, &LOCK_open, &COND_refresh);
- }
- else
- {
- VOID(pthread_mutex_unlock(&LOCK_open));
- }
- /*
- There is a refresh in progress for this table.
- Signal the caller that it has to try again.
- */
- if (refresh)
- *refresh=1;
- DBUG_RETURN(0);
+ thd->push_internal_handler(&mdl_deadlock_handler);
+ bool result= thd->mdl_context.acquire_lock(&protection_request,
+ ot_ctx->get_timeout());
+ thd->pop_internal_handler();
+
+ if (result)
+ DBUG_RETURN(TRUE);
+
+ ot_ctx->set_has_protection_against_grl();
}
- }
- if (table)
- {
- DBUG_PRINT("tcache", ("unused table: '%s'.'%s' 0x%lx", table->s->db.str,
- table->s->table_name.str, (long) table));
- /* Unlink the table from "unused_tables" list. */
- if (table == unused_tables)
- { // First unused
- unused_tables=unused_tables->next; // Remove from link
- if (table == unused_tables)
- unused_tables=0;
+
+ if (open_table_get_mdl_lock(thd, ot_ctx, &table_list->mdl_request,
+ flags, &mdl_ticket) ||
+ mdl_ticket == NULL)
+ {
+ DEBUG_SYNC(thd, "before_open_table_wait_refresh");
+ DBUG_RETURN(TRUE);
}
- table->prev->next=table->next; /* Remove from unused list */
- table->next->prev=table->prev;
- table->in_use= thd;
+ DEBUG_SYNC(thd, "after_open_table_mdl_shared");
}
else
{
- /* Insert a new TABLE instance into the open cache */
- int error;
- DBUG_PRINT("tcache", ("opening new table"));
- /* Free cache if too big */
- while (open_cache.records > table_cache_size && unused_tables)
- VOID(hash_delete(&open_cache,(uchar*) unused_tables)); /* purecov: tested */
+ /*
+ Grab reference to the MDL lock ticket that was acquired
+ by the caller.
+ */
+ mdl_ticket= table_list->mdl_request.ticket;
+ }
- if (table_list->create)
- {
- bool exists;
+ hash_value= my_calc_hash(&table_def_cache, (uchar*) key, key_length);
- if (check_if_table_exists(thd, table_list, &exists))
- {
- VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(NULL);
- }
- if (!exists)
- {
- /*
- Table to be created, so we need to create placeholder in table-cache.
- */
- if (!(table= table_cache_insert_placeholder(thd, key, key_length)))
- {
- VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(NULL);
- }
- /*
- Link placeholder to the open tables list so it will be automatically
- removed once tables are closed. Also mark it so it won't be ignored
- by other trying to take name-lock.
- */
- table->open_placeholder= 1;
- table->next= thd->open_tables;
- thd->open_tables= table;
- VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(table);
- }
- /* Table exists. Let us try to open it. */
+ if (table_list->open_strategy == TABLE_LIST::OPEN_IF_EXISTS)
+ {
+ bool exists;
+
+ if (check_if_table_exists(thd, table_list, 0, &exists))
+ DBUG_RETURN(TRUE);
+
+ if (!exists)
+ DBUG_RETURN(FALSE);
+
+ /* Table exists. Let us try to open it. */
+ }
+ else if (table_list->open_strategy == TABLE_LIST::OPEN_STUB)
+ DBUG_RETURN(FALSE);
+
+retry_share:
+
+ mysql_mutex_lock(&LOCK_open);
+
+ if (!(share= get_table_share_with_discover(thd, table_list, key,
+ key_length, OPEN_VIEW,
+ &error,
+ hash_value)))
+ {
+ mysql_mutex_unlock(&LOCK_open);
+ /*
+ If thd->is_error() is not set, we either need discover
+ (error == 7), or the error was silenced by the prelocking
+ handler (error == 0), in which case we should skip this
+ table.
+ */
+ if (error == 7 && !thd->is_error())
+ {
+ (void) ot_ctx->request_backoff_action(Open_table_context::OT_DISCOVER,
+ table_list);
}
+ DBUG_RETURN(TRUE);
+ }
- /* make a new table */
- if (!(table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME))))
+ if (share->is_view)
+ {
+ /*
+ If parent_l of the table_list is non null then a merge table
+ has this view as child table, which is not supported.
+ */
+ if (table_list->parent_l)
{
- VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(NULL);
+ my_error(ER_WRONG_MRG_TABLE, MYF(0));
+ goto err_unlock;
}
- error= open_unireg_entry(thd, table, table_list, alias, key, key_length,
- mem_root,
- (flags & (OPEN_VIEW_NO_PARSE |
- MYSQL_LOCK_IGNORE_FLUSH)));
- if (error > 0)
+ /*
+ This table is a view. Validate its metadata version: in particular,
+ that it was a view when the statement was prepared.
+ */
+ if (check_and_update_table_version(thd, table_list, share))
+ goto err_unlock;
+ if (table_list->i_s_requested_object & OPEN_TABLE_ONLY)
{
- my_free((uchar*)table, MYF(0));
- VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(NULL);
+ my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db,
+ table_list->table_name);
+ goto err_unlock;
}
- if (table_list->view || error < 0)
+
+ /* Open view */
+ if (open_new_frm(thd, share, alias,
+ (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
+ HA_GET_INDEX | HA_TRY_READ_ONLY),
+ READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
+ thd->open_options,
+ 0, table_list, mem_root))
+ goto err_unlock;
+
+ /* TODO: Don't free this */
+ release_table_share(share);
+
+ DBUG_ASSERT(table_list->view);
+
+ mysql_mutex_unlock(&LOCK_open);
+ DBUG_RETURN(FALSE);
+ }
+
+ /*
+ Note that situation when we are trying to open a table for what
+ was a view during previous execution of PS will be handled in by
+ the caller. Here we should simply open our table even if
+ TABLE_LIST::view is true.
+ */
+
+ if (table_list->i_s_requested_object & OPEN_VIEW_ONLY)
+ {
+ my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db,
+ table_list->table_name);
+ goto err_unlock;
+ }
+
+ if (!(flags & MYSQL_OPEN_IGNORE_FLUSH) ||
+ (share->protected_against_usage() &&
+ !(flags & MYSQL_OPEN_FOR_REPAIR)))
+ {
+ if (share->has_old_version())
{
/*
- VIEW not really opened, only frm were read.
- Set 1 as a flag here
+ We already have an MDL lock. But we have encountered an old
+ version of table in the table definition cache which is possible
+ when someone changes the table version directly in the cache
+ without acquiring a metadata lock (e.g. this can happen during
+ "rolling" FLUSH TABLE(S)).
+ Release our reference to share, wait until old version of
+ share goes away and then try to get new version of table share.
*/
- if (error < 0)
- table_list->view= (st_lex*)1;
+ MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);
+ bool wait_result;
+
+ DBUG_PRINT("info", ("old version of table share found"));
+ release_table_share(share);
+ mysql_mutex_unlock(&LOCK_open);
+
+ thd->push_internal_handler(&mdl_deadlock_handler);
+ wait_result= tdc_wait_for_old_version(thd, table_list->db,
+ table_list->table_name,
+ ot_ctx->get_timeout(),
+ mdl_ticket->get_deadlock_weight());
+ thd->pop_internal_handler();
+
+ if (wait_result)
+ DBUG_RETURN(TRUE);
- my_free((uchar*)table, MYF(0));
- VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(0); // VIEW
+ goto retry_share;
}
- DBUG_PRINT("info", ("inserting table '%s'.'%s' 0x%lx into the cache",
- table->s->db.str, table->s->table_name.str,
- (long) table));
- if (my_hash_insert(&open_cache,(uchar*) table))
+
+ if (thd->open_tables && thd->open_tables->s->version != share->version)
{
- my_free(table, MYF(0));
- VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(NULL);
+ /*
+ If the version changes while we're opening the tables,
+ we have to back off, close all the tables opened-so-far,
+ and try to reopen them. Note: refresh_version is currently
+ changed only during FLUSH TABLES.
+ */
+ DBUG_PRINT("info", ("share version differs between tables"));
+ release_table_share(share);
+ mysql_mutex_unlock(&LOCK_open);
+ (void)ot_ctx->request_backoff_action(Open_table_context::OT_REOPEN_TABLES,
+ NULL);
+ DBUG_RETURN(TRUE);
}
}
- check_unused(); // Debugging call
-
- VOID(pthread_mutex_unlock(&LOCK_open));
- if (refresh)
+ if (!share->free_tables.is_empty())
{
- table->next=thd->open_tables; /* Link into simple list */
- thd->open_tables=table;
+ table= share->free_tables.front();
+ table_def_use_table(thd, table);
+ /*
+ We need to release share as we have EXTRA reference to it in our hands.
+ */
+ DBUG_PRINT("info", ("release temporarily acquired table share"));
+ release_table_share(share);
}
+ else
+ {
+ /*
+ We have too many TABLE instances around let us try to get rid of them.
+ */
+ while (table_cache_count > table_cache_size && unused_tables)
+ free_cache_entry(unused_tables);
+
+ mysql_mutex_unlock(&LOCK_open);
+
+ /* make a new table */
+ if (!(table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME))))
+ goto err_lock;
+
+ error= open_table_from_share(thd, share, alias,
+ (uint) (HA_OPEN_KEYFILE |
+ HA_OPEN_RNDFILE |
+ HA_GET_INDEX |
+ HA_TRY_READ_ONLY),
+ (READ_KEYINFO | COMPUTE_TYPES |
+ EXTRA_RECORD),
+ thd->open_options, table, FALSE);
+
+ if (error)
+ {
+ my_free(table);
+
+ if (error == 7)
+ (void) ot_ctx->request_backoff_action(Open_table_context::OT_DISCOVER,
+ table_list);
+ else if (share->crashed)
+ (void) ot_ctx->request_backoff_action(Open_table_context::OT_REPAIR,
+ table_list);
+
+ goto err_lock;
+ }
+
+ if (open_table_entry_fini(thd, share, table))
+ {
+ closefrm(table, 0);
+ my_free(table);
+ goto err_lock;
+ }
+
+ mysql_mutex_lock(&LOCK_open);
+ /* Add table to the share's used tables list. */
+ table_def_add_used_table(thd, table);
+ }
+
+ mysql_mutex_unlock(&LOCK_open);
+
+ table->mdl_ticket= mdl_ticket;
+
+ table->next= thd->open_tables; /* Link into simple list */
+ thd->set_open_tables(table);
+
table->reginfo.lock_type=TL_READ; /* Assume read */
reset:
- DBUG_ASSERT(table->s->ref_count > 0 || table->s->tmp_table != NO_TMP_TABLE);
-
- if (thd->lex->need_correct_ident())
- table->alias_name_used= my_strcasecmp(table_alias_charset,
- table->s->table_name.str, alias);
- /* Fix alias if table name changes */
- if (strcmp(table->alias.c_ptr(), alias))
- table->alias.copy(alias, strlen(alias), table->alias.charset());
-
- /* These variables are also set in reopen_table() */
- table->tablenr=thd->current_tablenr++;
- table->used_fields=0;
- table->const_table=0;
- table->null_row= 0;
- table->maybe_null= 0;
- table->force_index= table->force_index_order= table->force_index_group= 0;
- table->status=STATUS_NO_RECORD;
- table->insert_values= 0;
- table->fulltext_searched= 0;
- table->file->ft_handler= 0;
/*
Check that there is no reference to a condition from an earlier query
(cf. Bug#58553).
*/
DBUG_ASSERT(table->file->pushed_cond == NULL);
- table->reginfo.impossible_range= 0;
- table->created= TRUE;
- /* Catch wrong handling of the auto_increment_field_not_null. */
- DBUG_ASSERT(!table->auto_increment_field_not_null);
- table->auto_increment_field_not_null= FALSE;
- if (table->timestamp_field)
- table->timestamp_field_type= table->timestamp_field->get_auto_set_type();
- table->pos_in_table_list= table_list;
table_list->updatable= 1; // It is not derived table nor non-updatable VIEW
- table->clear_column_bitmaps();
- /*
- Fill record with random values to find bugs where we access fields
- without first reading them.
- */
- TRASH(table->record[0], table->s->reclength);
- /*
- Initialize the null marker bits, to ensure that if we are doing a read
- of only selected columns (like in keyread), all null markers are
- initialized.
- */
- bfill(table->record[0], table->s->null_bytes, 255);
- bfill(table->record[1], table->s->null_bytes, 255);
+ table_list->table= table;
- DBUG_ASSERT(table->key_read == 0);
- DBUG_RETURN(table);
-}
+ table->init(thd, table_list);
+ DBUG_RETURN(FALSE);
-TABLE *find_locked_table(THD *thd, const char *db,const char *table_name)
-{
- char key[MAX_DBKEY_LENGTH];
- uint key_length= create_table_def_key(key, db, table_name);
+err_lock:
+ mysql_mutex_lock(&LOCK_open);
+err_unlock:
+ release_table_share(share);
+ mysql_mutex_unlock(&LOCK_open);
- for (TABLE *table=thd->open_tables; table ; table=table->next)
- {
- if (table->s->table_cache_key.length == key_length &&
- !memcmp(table->s->table_cache_key.str, key, key_length))
- return table;
- }
- return(0);
+ DBUG_PRINT("exit", ("failed"));
+ DBUG_RETURN(TRUE);
}
-/*
- Reopen an table because the definition has changed.
+/**
+ Find table in the list of open tables.
- SYNOPSIS
- reopen_table()
- table Table object
+ @param list List of TABLE objects to be inspected.
+ @param db Database name
+ @param table_name Table name
- NOTES
- The data file for the table is already closed and the share is released
- The table has a 'dummy' share that mainly contains database and table name.
-
- RETURN
- 0 ok
- 1 error. The old table object is not changed.
+ @return Pointer to the TABLE object found, 0 if no table found.
*/
-bool reopen_table(TABLE *table)
+TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name)
{
- TABLE tmp;
- bool error= 1;
- Field **field;
- uint key,part;
- TABLE_LIST table_list;
- THD *thd= table->in_use;
- DBUG_ENTER("reopen_table");
- DBUG_PRINT("tcache", ("table: '%s'.'%s' table: 0x%lx share: 0x%lx",
- table->s->db.str, table->s->table_name.str,
- (long) table, (long) table->s));
-
- DBUG_ASSERT(table->s->ref_count == 0);
- DBUG_ASSERT(!table->sort.io_cache);
- DBUG_ASSERT(!table->children_attached);
-
-#ifdef EXTRA_DEBUG
- if (table->db_stat)
- sql_print_error("Table %s had a open data handler in reopen_table",
- table->alias.c_ptr());
-#endif
- bzero((char*) &table_list, sizeof(TABLE_LIST));
- table_list.db= table->s->db.str;
- table_list.table_name= table->s->table_name.str;
- table_list.table= table;
-
- if (wait_for_locked_table_names(thd, &table_list))
- DBUG_RETURN(1); // Thread was killed
-
- if (open_unireg_entry(thd, &tmp, &table_list,
- table->alias.c_ptr(),
- table->s->table_cache_key.str,
- table->s->table_cache_key.length,
- thd->mem_root, 0))
- goto end;
-
- /* This list copies variables set by open_table */
- tmp.tablenr= table->tablenr;
- tmp.used_fields= table->used_fields;
- tmp.const_table= table->const_table;
- tmp.null_row= table->null_row;
- tmp.maybe_null= table->maybe_null;
- tmp.status= table->status;
-
- /* Get state */
- tmp.in_use= thd;
- tmp.reginfo.lock_type=table->reginfo.lock_type;
- tmp.grant= table->grant;
-
- /* Replace table in open list */
- tmp.next= table->next;
- tmp.prev= table->prev;
-
- /* Preserve MERGE parent. */
- tmp.parent= table->parent;
- /* Fix MERGE child list and check for unchanged union. */
- if ((table->child_l || tmp.child_l) &&
- fix_merge_after_open(table->child_l, table->child_last_l,
- tmp.child_l, tmp.child_last_l))
- {
- VOID(closefrm(&tmp, 1)); // close file, free everything
- goto end;
- }
-
- delete table->triggers;
- if (table->file)
- VOID(closefrm(table, 1)); // close file, free everything
-
- *table= tmp;
- table->alias.move(tmp.alias);
- table->default_column_bitmaps();
- table->file->change_table_ptr(table, table->s);
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length= create_table_def_key(key, db, table_name);
- DBUG_ASSERT(table->alias.ptr() != 0);
- for (field=table->field ; *field ; field++)
+ for (TABLE *table= list; table ; table=table->next)
{
- (*field)->init(table);
- }
- for (key=0 ; key < table->s->keys ; key++)
- {
- for (part=0 ; part < table->key_info[key].usable_key_parts ; part++)
- {
- table->key_info[key].key_part[part].field->table= table;
- table->key_info[key].key_part[part].field->orig_table= table;
- }
+ if (table->s->table_cache_key.length == key_length &&
+ !memcmp(table->s->table_cache_key.str, key, key_length))
+ return table;
}
- if (table->triggers)
- table->triggers->set_table(table);
- /*
- Do not attach MERGE children here. The children might be reopened
- after the parent. Attach children after reopening all tables that
- require reopen. See for example reopen_tables().
- */
-
- broadcast_refresh();
- error=0;
-
- end:
- DBUG_RETURN(error);
+ return(0);
}
/**
- Close all instances of a table open by this thread and replace
- them with exclusive name-locks.
-
- @param thd Thread context
- @param db Database name for the table to be closed
- @param table_name Name of the table to be closed
-
- @note This function assumes that if we are not under LOCK TABLES,
- then there is only one table open and locked. This means that
- the function probably has to be adjusted before it can be used
- anywhere outside ALTER TABLE.
-
- @note Must not use TABLE_SHARE::table_name/db of the table being closed,
- the strings are used in a loop even after the share may be freed.
+ Find instance of TABLE with upgradable or exclusive metadata
+ lock from the list of open tables, emit error if no such table
+ found.
+
+ @param thd Thread context
+ @param db Database name.
+ @param table_name Name of table.
+ @param no_error Don't emit error if no suitable TABLE
+ instance were found.
+
+ @note This function checks if the connection holds a global IX
+ metadata lock. If no such lock is found, it is not safe to
+ upgrade the lock and ER_TABLE_NOT_LOCKED_FOR_WRITE will be
+ reported.
+
+ @return Pointer to TABLE instance with MDL_SHARED_NO_WRITE,
+ MDL_SHARED_NO_READ_WRITE, or MDL_EXCLUSIVE metadata
+ lock, NULL otherwise.
*/
-void close_data_files_and_morph_locks(THD *thd, const char *db,
- const char *table_name)
+TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db,
+ const char *table_name, bool no_error)
{
- TABLE *table;
- DBUG_ENTER("close_data_files_and_morph_locks");
-
- safe_mutex_assert_owner(&LOCK_open);
+ TABLE *tab= find_locked_table(thd->open_tables, db, table_name);
- if (thd->lock)
+ if (!tab)
{
- /*
- If we are not under LOCK TABLES we should have only one table
- open and locked so it makes sense to remove the lock at once.
- */
- mysql_unlock_tables(thd, thd->lock);
- thd->lock= 0;
+ if (!no_error)
+ my_error(ER_TABLE_NOT_LOCKED, MYF(0), table_name);
+ return NULL;
}
/*
- Note that open table list may contain a name-lock placeholder
- for target table name if we process ALTER TABLE ... RENAME.
- So loop below makes sense even if we are not under LOCK TABLES.
+ It is not safe to upgrade the metadata lock without a global IX lock.
+ This can happen with FLUSH TABLES <list> WITH READ LOCK as we in these
+ cases don't take a global IX lock in order to be compatible with
+ global read lock.
*/
- for (table=thd->open_tables; table ; table=table->next)
+ if (!thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, "", "",
+ MDL_INTENTION_EXCLUSIVE))
{
- if (!strcmp(table->s->table_name.str, table_name) &&
- !strcmp(table->s->db.str, db))
- {
- if (thd->locked_tables)
- {
- if (table->parent)
- {
- /*
- If MERGE child, need to reopen parent too. This means that
- the first child to be closed will detach all children from
- the parent and close it. OTOH in most cases a MERGE table
- won't have multiple children with the same db.table_name.
- */
- mysql_lock_remove(thd, thd->locked_tables, table->parent, TRUE);
- table->parent->open_placeholder= 1;
- close_handle_and_leave_table_as_lock(table->parent);
- }
- else
- mysql_lock_remove(thd, thd->locked_tables, table, TRUE);
- }
- table->open_placeholder= 1;
- close_handle_and_leave_table_as_lock(table);
- }
+ if (!no_error)
+ my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), table_name);
+ return NULL;
}
- DBUG_VOID_RETURN;
-}
+ while (tab->mdl_ticket != NULL &&
+ !tab->mdl_ticket->is_upgradable_or_exclusive() &&
+ (tab= find_locked_table(tab->next, db, table_name)))
+ continue;
-/**
- @brief Mark merge parent and children with bad_merge_marker
-
- @param[in,out] parent the TABLE object of the parent
-*/
+ if (!tab && !no_error)
+ my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), table_name);
-static void mark_merge_parent_and_children_as_bad(TABLE *parent)
-{
- TABLE_LIST *child_l;
- DBUG_ENTER("mark_merge_parent_and_children_as_bad");
- parent->parent= &bad_merge_marker;
- for (child_l= parent->child_l; ; child_l= child_l->next_global)
- {
- child_l->table->parent= &bad_merge_marker;
- child_l->table= NULL;
- if (&child_l->next_global == parent->child_last_l)
- break;
- }
- DBUG_VOID_RETURN;
+ return tab;
}
+/***********************************************************************
+ class Locked_tables_list implementation. Declared in sql_class.h
+************************************************************************/
+
/**
- Reattach MERGE children after reopen.
+ Enter LTM_LOCK_TABLES mode.
- @param[in] thd thread context
+ Enter the LOCK TABLES mode using all the tables that are
+ currently open and locked in this connection.
+ Initializes a TABLE_LIST instance for every locked table.
- @note If reattach failed for certain MERGE table, the table (and all
- it's children) are marked with bad_merge_marker.
+ @param thd thread handle
- @return status
- @retval FALSE OK
- @retval TRUE Error
+ @return TRUE if out of memory.
*/
-static bool reattach_merge(THD *thd)
-{
- TABLE *table;
- bool error= FALSE;
- DBUG_ENTER("reattach_merge");
+bool
+Locked_tables_list::init_locked_tables(THD *thd)
+{
+ DBUG_ASSERT(thd->locked_tables_mode == LTM_NONE);
+ DBUG_ASSERT(m_locked_tables == NULL);
+ DBUG_ASSERT(m_reopen_array == NULL);
+ DBUG_ASSERT(m_locked_tables_count == 0);
+
+ for (TABLE *table= thd->open_tables; table;
+ 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= src_table_list->db_length;
+ size_t table_name_len= src_table_list->table_name_length;
+ size_t alias_len= strlen(src_table_list->alias);
+ 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,
+ NullS))
+ {
+ unlock_locked_tables(0);
+ return TRUE;
+ }
- for (table= thd->open_tables; table; table= table->next)
- {
- DBUG_PRINT("tcache", ("check table: '%s'.'%s' 0x%lx",
- table->s->db.str, table->s->table_name.str,
- (long) table));
- /*
- Reattach children only for MERGE tables that had children or parent
- with "closed data files" and were reopen. For extra safety skip MERGE
- tables which we failed to reopen (should not happen with current code).
+ memcpy(db, src_table_list->db, db_len + 1);
+ memcpy(table_name, src_table_list->table_name, table_name_len + 1);
+ memcpy(alias, src_table_list->alias, alias_len + 1);
+ /**
+ Sic: remember the *actual* table level lock type taken, to
+ acquire the exact same type in reopen_tables().
+ E.g. if the table was locked for write, src_table_list->lock_type is
+ TL_WRITE_DEFAULT, whereas reginfo.lock_type has been updated from
+ thd->update_lock_default.
+ */
+ dst_table_list->init_one_table(db, db_len, table_name, table_name_len,
+ alias,
+ src_table_list->table->reginfo.lock_type);
+ dst_table_list->table= table;
+ dst_table_list->mdl_request.ticket= src_table_list->mdl_request.ticket;
+
+ /* Link last into the list of tables */
+ *(dst_table_list->prev_global= m_locked_tables_last)= dst_table_list;
+ m_locked_tables_last= &dst_table_list->next_global;
+ table->pos_in_locked_tables= dst_table_list;
+ }
+ if (m_locked_tables_count)
+ {
+ /**
+ Allocate an auxiliary array to pass to mysql_lock_tables()
+ in reopen_tables(). reopen_tables() is a critical
+ path and we don't want to complicate it with extra allocations.
*/
- if (table->child_l && table->parent != &bad_merge_marker &&
- !table->children_attached)
+ m_reopen_array= (TABLE**)alloc_root(&m_locked_tables_root,
+ sizeof(TABLE*) *
+ (m_locked_tables_count+1));
+ if (m_reopen_array == NULL)
{
- DBUG_PRINT("tcache", ("MERGE parent, attach children"));
- if (table->file->extra(HA_EXTRA_ATTACH_CHILDREN))
- {
- my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr());
- error= TRUE;
- mark_merge_parent_and_children_as_bad(table);
- }
- else
- {
- table->children_attached= TRUE;
- DBUG_PRINT("myrg", ("attached parent: '%s'.'%s' 0x%lx",
- table->s->db.str,
- table->s->table_name.str, (long) table));
- }
+ unlock_locked_tables(0);
+ return TRUE;
}
}
- DBUG_RETURN(error);
+ thd->enter_locked_tables_mode(LTM_LOCK_TABLES);
+
+ return FALSE;
}
/**
- Reopen all tables with closed data files.
-
- @param thd Thread context
- @param get_locks Should we get locks after reopening tables ?
- @param mark_share_as_old Mark share as old to protect from a impending
- global read lock.
+ Leave LTM_LOCK_TABLES mode if it's been entered.
- @note Since this function can't properly handle prelocking and
- create placeholders it should be used in very special
- situations like FLUSH TABLES or ALTER TABLE. In general
- case one should just repeat open_tables()/lock_tables()
- combination when one needs tables to be reopened (for
- example see open_and_lock_tables()).
+ Close all locked tables, free memory, and leave the mode.
- @note One should have lock on LOCK_open when calling this.
-
- @return FALSE in case of success, TRUE - otherwise.
+ @note This function is a no-op if we're not in LOCK TABLES.
*/
-bool reopen_tables(THD *thd, bool get_locks, bool mark_share_as_old)
+void
+Locked_tables_list::unlock_locked_tables(THD *thd)
{
- TABLE *table,*next,**prev;
- TABLE **tables,**tables_ptr; // For locks
- bool error=0, not_used;
- bool merge_table_found= FALSE;
- const uint flags= MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN |
- MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK |
- MYSQL_LOCK_IGNORE_FLUSH;
- DBUG_ENTER("reopen_tables");
-
- if (!thd->open_tables)
- DBUG_RETURN(0);
-
- safe_mutex_assert_owner(&LOCK_open);
- if (get_locks)
+ if (thd)
{
+ DBUG_ASSERT(!thd->in_sub_stmt &&
+ !(thd->state_flags & Open_tables_state::BACKUPS_AVAIL));
/*
- The ptr is checked later
- Do not handle locks of MERGE children.
+ Sic: we must be careful to not close open tables if
+ we're not in LOCK TABLES mode: unlock_locked_tables() is
+ sometimes called implicitly, expecting no effect on
+ open tables, e.g. from begin_trans().
*/
- uint opens=0;
- for (table= thd->open_tables; table ; table=table->next)
- if (!table->parent)
- opens++;
- DBUG_PRINT("tcache", ("open tables to lock: %u", opens));
- tables= (TABLE**) my_alloca(sizeof(TABLE*)*opens);
- }
- else
- tables= &thd->open_tables;
- tables_ptr =tables;
+ if (thd->locked_tables_mode != LTM_LOCK_TABLES)
+ return;
- prev= &thd->open_tables;
- for (table=thd->open_tables; table ; table=next)
- {
- uint db_stat=table->db_stat;
- TABLE *parent= table->child_l ? table : table->parent;
- next=table->next;
- DBUG_PRINT("tcache", ("open table: '%s'.'%s' 0x%lx "
- "parent: 0x%lx db_stat: %u",
- table->s->db.str, table->s->table_name.str,
- (long) table, (long) table->parent, db_stat));
- /*
- If we need to reopen child or parent table in a MERGE table, then
- children in this MERGE table has to be already detached at this
- point.
- */
- DBUG_ASSERT(db_stat || !parent || !parent->children_attached);
- /*
- Thanks to the above assumption the below condition will guarantee that
- merge_table_found is TRUE when we need to reopen child or parent table.
- Note that it works even in situation when it is only a child and not a
- parent that needs reopen (this can happen when get_locks == FALSE).
- */
- if (table->child_l && !table->children_attached)
- merge_table_found= TRUE;
- if (!tables)
+ for (TABLE_LIST *table_list= m_locked_tables;
+ table_list; table_list= table_list->next_global)
{
/*
- If we could not allocate 'tables' we close ALL open tables here.
- Before closing MERGE child or parent we need to detach children
- and/or clear references in/to them.
+ Clear the position in the list, the TABLE object will be
+ returned to the table cache.
*/
- if (parent)
- detach_merge_children(table, TRUE);
+ if (table_list->table) // If not closed
+ table_list->table->pos_in_locked_tables= NULL;
}
- else if (table->parent == &bad_merge_marker)
- {
- /*
- This is either a child or a parent of a MERGE table for which
- we already decided that we are unable to reopen it. Close it.
+ thd->leave_locked_tables_mode();
- Reset parent reference, it may be used while freeing the table.
- */
- table->parent= NULL;
- }
- else if (!db_stat && reopen_table(table))
- {
- /*
- If we fail to reopen a child or a parent in a MERGE table and the
- MERGE table is affected for the first time, mark all relevant tables
- invalid. Otherwise handle it as usual.
-
- All in all we must end up with:
- - child tables are detached from parent. This was done earlier,
- but child<->parent references were kept valid for reopen.
- - parent is not in the to-be-locked tables
- - all child tables and parent are not in the THD::open_tables.
- - all child tables and parent are not in the open_cache.
-
- Please note that below we do additional pass through THD::open_tables
- list to achieve the last three points.
- */
- if (parent)
- {
- mark_merge_parent_and_children_as_bad(parent);
- table->parent= NULL;
- }
- }
- else
- {
- DBUG_PRINT("tcache", ("opened. need lock: %d",
- get_locks && !db_stat && !table->parent));
- *prev= table;
- prev= &table->next;
- /* Do not handle locks of MERGE children. */
- if (get_locks && !db_stat && !table->parent)
- *tables_ptr++= table; // need new lock on this
- if (mark_share_as_old)
- {
- table->s->version=0;
- table->open_placeholder= 0;
- }
- continue;
- }
- my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr());
- unlink_open_table(thd, table, 0);
- /* Restart loop, as one of the used tables may now be closed */
- prev= &thd->open_tables;
- next= *prev;
- error=1;
- }
- *prev=0;
- /*
- When all tables are open again, we can re-attach MERGE children to
- their parents.
-
- If there was an error while reopening a child or a parent of a MERGE
- table, or while reattaching child tables to their parents, some tables
- may have been kept open but marked for close with bad_merge_marker.
- Close these tables now.
- */
- if (tables && merge_table_found && (error|= reattach_merge(thd)))
- {
- prev= &thd->open_tables;
- for (table= thd->open_tables; table; table= next)
- {
- next= table->next;
- if (table->parent == &bad_merge_marker)
- {
- /* Remove merge parent from to-be-locked tables array. */
- if (get_locks && table->child_l)
- {
- TABLE **t;
- for (t= tables; t < tables_ptr; t++)
- {
- if (*t == table)
- {
- tables_ptr--;
- memmove(t, t + 1, (tables_ptr - t) * sizeof(TABLE *));
- break;
- }
- }
- }
- /* Reset parent reference, it may be used while freeing the table. */
- table->parent= NULL;
- /* Free table. */
- VOID(hash_delete(&open_cache, (uchar *) table));
- }
- else
- {
- *prev= table;
- prev= &table->next;
- }
- }
- *prev= 0;
- }
- DBUG_PRINT("tcache", ("open tables to lock: %u",
- (uint) (tables_ptr - tables)));
- if (tables != tables_ptr) // Should we get back old locks
- {
- MYSQL_LOCK *lock;
+ DBUG_ASSERT(thd->transaction.stmt.is_empty());
+ close_thread_tables(thd);
/*
- We should always get these locks. Anyway, we must not go into
- wait_for_tables() as it tries to acquire LOCK_open, which is
- already locked.
+ We rely on the caller to implicitly commit the
+ transaction and release transactional locks.
*/
- thd->some_tables_deleted=0;
- if ((lock= mysql_lock_tables(thd, tables, (uint) (tables_ptr - tables),
- flags, &not_used)))
- {
- thd->locked_tables=mysql_lock_merge(thd->locked_tables,lock);
- }
- else
- {
- /*
- This case should only happen if there is a bug in the reopen logic.
- Need to issue error message to have a reply for the application.
- Not exactly what happened though, but close enough.
- */
- my_error(ER_LOCK_DEADLOCK, MYF(0));
- error=1;
- }
- }
- if (get_locks && tables)
- {
- my_afree((uchar*) tables);
}
- broadcast_refresh();
- DBUG_RETURN(error);
+ /*
+ After closing tables we can free memory used for storing lock
+ request for metadata locks and TABLE_LIST elements.
+ */
+ free_root(&m_locked_tables_root, MYF(0));
+ m_locked_tables= NULL;
+ m_locked_tables_last= &m_locked_tables;
+ m_reopen_array= NULL;
+ m_locked_tables_count= 0;
}
/**
- Close handlers for tables in list, but leave the TABLE structure
- intact so that we can re-open these quickly.
-
- @param thd Thread context
- @param table Head of the list of TABLE objects
- @param morph_locks TRUE - remove locks which we have on tables being closed
- but ensure that no DML or DDL will sneak in before
- we will re-open the table (i.e. temporarily morph
- our table-level locks into name-locks).
- FALSE - otherwise
- @param send_refresh Should we awake waiters even if we didn't close any tables?
+ Unlink a locked table from the locked tables list, either
+ temporarily or permanently.
+
+ @param thd thread handle
+ @param table_list the element of locked tables list.
+ The implementation assumes that this argument
+ points to a TABLE_LIST element linked into
+ the locked tables list. Passing a TABLE_LIST
+ instance that is not part of locked tables
+ list will lead to a crash.
+ @param remove_from_locked_tables
+ TRUE if the table is removed from the list
+ permanently.
+
+ This function is a no-op if we're not under LOCK TABLES.
+
+ @sa Locked_tables_list::reopen_tables()
*/
-static void close_old_data_files(THD *thd, TABLE *table, bool morph_locks,
- bool send_refresh)
-{
- bool found= send_refresh;
- DBUG_ENTER("close_old_data_files");
-
- for (; table ; table=table->next)
- {
- DBUG_PRINT("tcache", ("checking table: '%s'.'%s' 0x%lx",
- table->s->db.str, table->s->table_name.str,
- (long) table));
- DBUG_PRINT("tcache", ("needs refresh: %d is open: %u",
- table->needs_reopen_or_name_lock(), table->db_stat));
- /*
- Reopen marked for flush.
- */
- if (table->needs_reopen_or_name_lock())
- {
- found=1;
- if (table->db_stat)
- {
- if (morph_locks)
- {
- /*
- Forward lock handling to MERGE parent. But unlock parent
- once only.
- */
- TABLE *ulcktbl= table->parent ? table->parent : table;
- if (ulcktbl->lock_count)
- {
- /*
- Inform handler that we will do a close even if the table may be
- locked or part of a transaction
- */
- table->file->extra(HA_EXTRA_PREPARE_FOR_FORCED_CLOSE);
- /*
- Wake up threads waiting for table-level lock on this table
- so they won't sneak in when we will temporarily remove our
- lock on it. This will also give them a chance to close their
- instances of this table.
- */
- mysql_lock_abort(thd, ulcktbl, TRUE);
- mysql_lock_remove(thd, thd->locked_tables, ulcktbl, TRUE);
- ulcktbl->lock_count= 0;
- }
- if ((ulcktbl != table) && ulcktbl->db_stat)
- {
- /*
- Close the parent too. Note that parent can come later in
- the list of tables. It will then be noticed as closed and
- as a placeholder. When this happens, do not clear the
- placeholder flag. See the branch below ("***").
- */
- ulcktbl->open_placeholder= 1;
- close_handle_and_leave_table_as_lock(ulcktbl);
- }
- /*
- We want to protect the table from concurrent DDL operations
- (like RENAME TABLE) until we will re-open and re-lock it.
- */
- table->open_placeholder= 1;
- }
- close_handle_and_leave_table_as_lock(table);
- }
- else if (table->open_placeholder && !morph_locks)
- {
- /*
- We come here only in close-for-back-off scenario. So we have to
- "close" create placeholder here to avoid deadlocks (for example,
- in case of concurrent execution of CREATE TABLE t1 SELECT * FROM t2
- and RENAME TABLE t2 TO t1). In close-for-re-open scenario we will
- probably want to let it stay.
-
- Note "***": We must not enter this branch if the placeholder
- flag has been set because of a former close through a child.
- See above the comment that refers to this note.
- */
- table->open_placeholder= 0;
- }
- }
- }
- if (found)
- broadcast_refresh();
- DBUG_VOID_RETURN;
-}
-
-
-/*
- Wait until all threads has closed the tables in the list
- We have also to wait if there is thread that has a lock on this table even
- if the table is closed
-*/
-bool table_is_used(TABLE *table, bool wait_for_name_lock)
-{
- DBUG_ENTER("table_is_used");
- do
- {
- char *key= table->s->table_cache_key.str;
- uint key_length= table->s->table_cache_key.length;
-
- DBUG_PRINT("loop", ("table_name: %s.%s", key, strend(key)+1));
- HASH_SEARCH_STATE state;
- for (TABLE *search= (TABLE*) hash_first(&open_cache, (uchar*) key,
- key_length, &state);
- search ;
- search= (TABLE*) hash_next(&open_cache, (uchar*) key,
- key_length, &state))
- {
- DBUG_PRINT("info", ("share: 0x%lx "
- "open_placeholder: %d locked_by_name: %d "
- "db_stat: %u version: %lu",
- (ulong) search->s,
- search->open_placeholder, search->locked_by_name,
- search->db_stat,
- search->s->version));
- if (search->in_use == table->in_use)
- continue; // Name locked by this thread
- /*
- We can't use the table under any of the following conditions:
- - There is an name lock on it (Table is to be deleted or altered)
- - If we are in flush table and we didn't execute the flush
- - If the table engine is open and it's an old version
- (We must wait until all engines are shut down to use the table)
- */
- if ( (search->locked_by_name && wait_for_name_lock) ||
- (search->is_name_opened() && search->needs_reopen_or_name_lock()))
- DBUG_RETURN(1);
- }
- } while ((table=table->next));
- DBUG_RETURN(0);
-}
+void Locked_tables_list::unlink_from_list(THD *thd,
+ TABLE_LIST *table_list,
+ bool remove_from_locked_tables)
+{
+ /*
+ If mode is not LTM_LOCK_TABLES, we needn't do anything. Moreover,
+ outside this mode pos_in_locked_tables value is not trustworthy.
+ */
+ if (thd->locked_tables_mode != LTM_LOCK_TABLES)
+ return;
+ /*
+ table_list must be set and point to pos_in_locked_tables of some
+ table.
+ */
+ DBUG_ASSERT(table_list->table->pos_in_locked_tables == table_list);
-/* Wait until all used tables are refreshed */
+ /* Clear the pointer, the table will be returned to the table cache. */
+ table_list->table->pos_in_locked_tables= NULL;
-bool wait_for_tables(THD *thd)
-{
- bool result;
- DBUG_ENTER("wait_for_tables");
+ /* Mark the table as closed in the locked tables list. */
+ table_list->table= NULL;
- thd_proc_info(thd, "Waiting for tables");
- pthread_mutex_lock(&LOCK_open);
- while (!thd->killed)
- {
- thd->some_tables_deleted=0;
- close_old_data_files(thd,thd->open_tables,0,dropping_tables != 0);
- mysql_ha_flush(thd);
- if (!table_is_used(thd->open_tables,1))
- break;
- (void) pthread_cond_wait(&COND_refresh,&LOCK_open);
- }
- if (thd->killed)
- result= 1; // aborted
- else
+ /*
+ If the table is being dropped or renamed, remove it from
+ the locked tables list (implicitly drop the LOCK TABLES lock
+ on it).
+ */
+ if (remove_from_locked_tables)
{
- /* Now we can open all tables without any interference */
- thd_proc_info(thd, "Reopen tables");
- thd->version= refresh_version;
- result=reopen_tables(thd,0,0);
+ *table_list->prev_global= table_list->next_global;
+ if (table_list->next_global == NULL)
+ m_locked_tables_last= table_list->prev_global;
+ else
+ table_list->next_global->prev_global= table_list->prev_global;
}
- pthread_mutex_unlock(&LOCK_open);
- thd_proc_info(thd, 0);
- DBUG_RETURN(result);
}
+/**
+ This is an attempt to recover (somewhat) in case of an error.
+ If we failed to reopen a closed table, let's unlink it from the
+ list and forget about it. From a user perspective that would look
+ as if the server "lost" the lock on one of the locked tables.
-/*
- drop tables from locked list
-
- SYNOPSIS
- drop_locked_tables()
- thd Thread thandler
- db Database
- table_name Table name
-
- INFORMATION
- This is only called on drop tables
-
- The TABLE object for the dropped table is unlocked but still kept around
- as a name lock, which means that the table will be available for other
- thread as soon as we call unlock_table_names().
- If there is multiple copies of the table locked, all copies except
- the first, which acts as a name lock, is removed.
-
- RETURN
- # If table existed, return table
- 0 Table was not locked
+ @note This function is a no-op if we're not under LOCK TABLES.
*/
-
-TABLE *drop_locked_tables(THD *thd,const char *db, const char *table_name)
+void Locked_tables_list::
+unlink_all_closed_tables(THD *thd, MYSQL_LOCK *lock, size_t reopen_count)
{
- TABLE *table,*next,**prev, *found= 0;
- prev= &thd->open_tables;
- DBUG_ENTER("drop_locked_tables");
-
+ /* If we managed to take a lock, unlock tables and free the lock. */
+ if (lock)
+ mysql_unlock_tables(thd, lock);
/*
- Note that we need to hold LOCK_open while changing the
- open_tables list. Another thread may work on it.
- (See: remove_table_from_cache(), mysql_wait_completed_table())
- Closing a MERGE child before the parent would be fatal if the
- other thread tries to abort the MERGE lock in between.
+ If a failure happened in reopen_tables(), we may have succeeded
+ reopening some tables, but not all.
+ This works when the connection was killed in mysql_lock_tables().
*/
- for (table= thd->open_tables; table ; table=next)
+ if (reopen_count)
{
- next=table->next;
- if (!strcmp(table->s->table_name.str, table_name) &&
- !strcmp(table->s->db.str, db))
+ while (reopen_count--)
{
- /* Inform handler that table will be dropped after close */
- table->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
-
- /* If MERGE child, forward lock handling to parent. */
- mysql_lock_remove(thd, thd->locked_tables,
- table->parent ? table->parent : table, TRUE);
/*
- When closing a MERGE parent or child table, detach the children first.
- Clear child table references in case this object is opened again.
+ When closing the table, we must remove it
+ from thd->open_tables list.
+ We rely on the fact that open_table() that was used
+ in reopen_tables() always links the opened table
+ to the beginning of the open_tables list.
*/
- if (table->child_l || table->parent)
- detach_merge_children(table, TRUE);
+ DBUG_ASSERT(thd->open_tables == m_reopen_array[reopen_count]);
- if (!found)
- {
- found= table;
- /* Close engine table, but keep object around as a name lock */
- if (table->db_stat)
- {
- table->db_stat= 0;
- table->file->ha_close();
- }
- }
- else
- {
- /* We already have a name lock, remove copy */
- VOID(hash_delete(&open_cache,(uchar*) table));
- }
- }
- else
- {
- *prev=table;
- prev= &table->next;
+ thd->open_tables->pos_in_locked_tables->table= NULL;
+
+ close_thread_table(thd, &thd->open_tables);
}
}
- *prev=0;
- if (found)
- broadcast_refresh();
- if (thd->locked_tables && thd->locked_tables->table_count == 0)
+ /* Exclude all closed tables from the LOCK TABLES list. */
+ for (TABLE_LIST *table_list= m_locked_tables; table_list; table_list=
+ table_list->next_global)
{
- my_free((uchar*) thd->locked_tables,MYF(0));
- thd->locked_tables=0;
+ if (table_list->table == NULL)
+ {
+ /* Unlink from list. */
+ *table_list->prev_global= table_list->next_global;
+ if (table_list->next_global == NULL)
+ m_locked_tables_last= table_list->prev_global;
+ else
+ table_list->next_global->prev_global= table_list->prev_global;
+ }
}
- DBUG_RETURN(found);
}
-/*
- If we have the table open, which only happens when a LOCK TABLE has been
- done on the table, change the lock type to a lock that will abort all
- other threads trying to get the lock.
+/**
+ Reopen the tables locked with LOCK TABLES and temporarily closed
+ by a DDL statement or FLUSH TABLES.
+
+ @note This function is a no-op if we're not under LOCK TABLES.
+
+ @return TRUE if an error reopening the tables. May happen in
+ case of some fatal system error only, e.g. a disk
+ corruption, out of memory or a serious bug in the
+ locking.
*/
-void abort_locked_tables(THD *thd,const char *db, const char *table_name)
+bool
+Locked_tables_list::reopen_tables(THD *thd)
{
- TABLE *table;
- for (table= thd->open_tables; table ; table= table->next)
+ Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN);
+ size_t reopen_count= 0;
+ MYSQL_LOCK *lock;
+ MYSQL_LOCK *merged_lock;
+
+ for (TABLE_LIST *table_list= m_locked_tables;
+ table_list; table_list= table_list->next_global)
{
- if (!strcmp(table->s->table_name.str, table_name) &&
- !strcmp(table->s->db.str, db))
+ if (table_list->table) /* The table was not closed */
+ continue;
+
+ /* Links into thd->open_tables upon success */
+ if (open_table(thd, table_list, thd->mem_root, &ot_ctx))
{
- /* If MERGE child, forward lock handling to parent. */
- mysql_lock_abort(thd, table->parent ? table->parent : table, TRUE);
- break;
+ unlink_all_closed_tables(thd, 0, reopen_count);
+ return TRUE;
+ }
+ table_list->table->pos_in_locked_tables= table_list;
+ /* See also the comment on lock type in init_locked_tables(). */
+ table_list->table->reginfo.lock_type= table_list->lock_type;
+
+ DBUG_ASSERT(reopen_count < m_locked_tables_count);
+ m_reopen_array[reopen_count++]= table_list->table;
+ }
+ if (reopen_count)
+ {
+ thd->in_lock_tables= 1;
+ /*
+ We re-lock all tables with mysql_lock_tables() at once rather
+ than locking one table at a time because of the case
+ reported in Bug#45035: when the same table is present
+ in the list many times, thr_lock.c fails to grant READ lock
+ on a table that is already locked by WRITE lock, even if
+ WRITE lock is taken by the same thread. If READ and WRITE
+ lock are passed to thr_lock.c in the same list, everything
+ works fine. Patching legacy code of thr_lock.c is risking to
+ break something else.
+ */
+ lock= mysql_lock_tables(thd, m_reopen_array, reopen_count,
+ MYSQL_OPEN_REOPEN);
+ thd->in_lock_tables= 0;
+ if (lock == NULL || (merged_lock=
+ mysql_lock_merge(thd->lock, lock)) == NULL)
+ {
+ unlink_all_closed_tables(thd, lock, reopen_count);
+ if (! thd->killed)
+ my_error(ER_LOCK_DEADLOCK, MYF(0));
+ return TRUE;
}
+ thd->lock= merged_lock;
}
+ return FALSE;
}
@@ -3930,7 +3672,7 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name)
PRE-CONDITION(S)
share is non-NULL
- The LOCK_open mutex is locked
+ The LOCK_open mutex is locked.
POST-CONDITION(S)
@@ -3951,7 +3693,7 @@ void assign_new_table_id(TABLE_SHARE *share)
/* Preconditions */
DBUG_ASSERT(share != NULL);
- safe_mutex_assert_owner(&LOCK_open);
+ mysql_mutex_assert_owner(&LOCK_open);
ulong tid= ++last_table_id; /* get next id */
/*
@@ -4015,7 +3757,7 @@ static bool inject_reprepare(THD *thd)
@retval FALSE success, version in TABLE_LIST has been updated
*/
-bool
+static bool
check_and_update_table_version(THD *thd,
TABLE_LIST *tables, TABLE_SHARE *table_share)
{
@@ -4037,221 +3779,137 @@ check_and_update_table_version(THD *thd,
}
DBUG_EXECUTE_IF("reprepare_each_statement", return inject_reprepare(thd););
+ return FALSE;
+}
+
+
+/**
+ Compares versions of a stored routine obtained from the sp cache
+ and the version used at prepare.
+
+ @details If the new and the old values mismatch, invoke
+ Metadata_version_observer.
+ At prepared statement prepare, all Sroutine_hash_entry version values
+ are NULL and we always have a mismatch. But there is no observer set
+ in THD, and therefore no error is reported. Instead, we update
+ the value in Sroutine_hash_entry, effectively recording the original
+ version.
+ At prepared statement execute, an observer may be installed. If
+ there is a version mismatch, we push an error and return TRUE.
+
+ For conventional execution (no prepared statements), the
+ observer is never installed.
+ @param[in] thd used to report errors
+ @param[in/out] rt pointer to stored routine entry in the
+ parse tree
+ @param[in] sp pointer to stored routine cache entry.
+ Can be NULL if there is no such routine.
+ @retval TRUE an error, which has been reported
+ @retval FALSE success, version in Sroutine_hash_entry has been updated
+*/
+
+static bool
+check_and_update_routine_version(THD *thd, Sroutine_hash_entry *rt,
+ sp_head *sp)
+{
+ ulong spc_version= sp_cache_version();
+ /* sp is NULL if there is no such routine. */
+ ulong version= sp ? sp->sp_cache_version() : spc_version;
+ /*
+ If the version in the parse tree is stale,
+ or the version in the cache is stale and sp is not used,
+ we need to reprepare.
+ Sic: version != spc_version <--> sp is not NULL.
+ */
+ if (rt->m_sp_cache_version != version ||
+ (version != spc_version && !sp->is_invoked()))
+ {
+ if (thd->m_reprepare_observer &&
+ thd->m_reprepare_observer->report_error(thd))
+ {
+ /*
+ Version of the sp cache is different from the
+ previous execution of the prepared statement, and it is
+ unacceptable for this SQLCOM. Error has been reported.
+ */
+ DBUG_ASSERT(thd->is_error());
+ return TRUE;
+ }
+ /* Always maintain the latest cache version. */
+ rt->m_sp_cache_version= version;
+ }
return FALSE;
}
-/*
- Load a table definition from file and open unireg table
- SYNOPSIS
- open_unireg_entry()
- thd Thread handle
- entry Store open table definition here
- table_list TABLE_LIST with db, table_name & belong_to_view
- alias Alias name
- cache_key Key for share_cache
- cache_key_length length of cache_key
- mem_root temporary mem_root for parsing
- flags the OPEN_VIEW_NO_PARSE flag to be passed to
- openfrm()/open_new_frm()
+/**
+ Open view by getting its definition from disk (and table cache in future).
- NOTES
- Extra argument for open is taken from thd->open_options
- One must have a lock on LOCK_open when calling this function
+ @param thd Thread handle
+ @param table_list TABLE_LIST with db, table_name & belong_to_view
+ @param alias Alias name
+ @param cache_key Key for table definition cache
+ @param cache_key_length Length of cache_key
+ @param mem_root Memory to be used for .frm parsing.
+ @param flags Flags which modify how we open the view
- RETURN
- 0 ok
- # Error
+ @todo This function is needed for special handling of views under
+ LOCK TABLES. We probably should get rid of it in long term.
+
+ @return FALSE if success, TRUE - otherwise.
*/
-static int open_unireg_entry(THD *thd, TABLE *entry, TABLE_LIST *table_list,
- const char *alias,
- char *cache_key, uint cache_key_length,
- MEM_ROOT *mem_root, uint flags)
+bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias,
+ char *cache_key, uint cache_key_length,
+ MEM_ROOT *mem_root, uint flags)
{
+ TABLE not_used;
int error;
+ my_hash_value_type hash_value;
TABLE_SHARE *share;
- uint discover_retry_count= 0;
- bool locked_table;
- DBUG_ENTER("open_unireg_entry");
- safe_mutex_assert_owner(&LOCK_open);
-retry:
- if (!(share= get_table_share_with_create(thd, table_list, cache_key,
- cache_key_length,
- OPEN_VIEW |
- table_list->i_s_requested_object,
- &error)))
- DBUG_RETURN(1);
+ hash_value= my_calc_hash(&table_def_cache, (uchar*) cache_key,
+ cache_key_length);
+ mysql_mutex_lock(&LOCK_open);
- if (share->is_view)
- {
- /*
- If parent_l of the table_list is non null then a merge table
- has this view as child table, which is not supported.
- */
- if (table_list->parent_l)
- {
- my_error(ER_WRONG_MRG_TABLE, MYF(0));
- goto err;
- }
-
- /*
- This table is a view. Validate its metadata version: in particular,
- that it was a view when the statement was prepared.
- */
- if (check_and_update_table_version(thd, table_list, share))
- goto err;
- if (table_list->i_s_requested_object & OPEN_TABLE_ONLY)
- goto err;
+ if (!(share= get_table_share(thd, table_list, cache_key,
+ cache_key_length,
+ OPEN_VIEW, &error,
+ hash_value)))
+ goto err;
- /* Open view */
- error= (int) open_new_frm(thd, share, alias,
- (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
- HA_GET_INDEX | HA_TRY_READ_ONLY),
- READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD |
- (flags & OPEN_VIEW_NO_PARSE),
- thd->open_options |
- (thd->version == 0 &&
- (flags & MYSQL_LOCK_IGNORE_FLUSH) ?
- HA_OPEN_FOR_STATUS : 0),
- entry, table_list, mem_root);
- if (error)
- goto err;
- /* TODO: Don't free this */
- release_table_share(share, RELEASE_NORMAL);
- DBUG_RETURN((flags & OPEN_VIEW_NO_PARSE)? -1 : 0);
- }
- else if (table_list->view)
+ if (share->is_view &&
+ !open_new_frm(thd, share, alias,
+ (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
+ HA_GET_INDEX | HA_TRY_READ_ONLY),
+ READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD |
+ flags, thd->open_options, &not_used, table_list,
+ mem_root))
{
- /*
- We're trying to open a table for what was a view.
- This can only happen during (re-)execution.
- At prepared statement prepare the view has been opened and
- merged into the statement parse tree. After that, someone
- performed a DDL and replaced the view with a base table.
- Don't try to open the table inside a prepared statement,
- invalidate it instead.
-
- Note, the assert below is known to fail inside stored
- procedures (Bug#27011).
- */
- DBUG_ASSERT(thd->m_reprepare_observer);
- check_and_update_table_version(thd, table_list, share);
- /* Always an error. */
- DBUG_ASSERT(thd->is_error());
- goto err;
+ release_table_share(share);
+ mysql_mutex_unlock(&LOCK_open);
+ return FALSE;
}
- if (table_list->i_s_requested_object & OPEN_VIEW_ONLY)
- goto err;
+ my_error(ER_WRONG_OBJECT, MYF(0), share->db.str, share->table_name.str, "VIEW");
+ release_table_share(share);
+err:
+ mysql_mutex_unlock(&LOCK_open);
+ return TRUE;
+}
- while ((error= open_table_from_share(thd, share, alias,
- (uint) (HA_OPEN_KEYFILE |
- HA_OPEN_RNDFILE |
- HA_GET_INDEX |
- HA_TRY_READ_ONLY),
- (READ_KEYINFO | COMPUTE_TYPES |
- EXTRA_RECORD),
- thd->open_options |
- (thd->version == 0 &&
- (flags & MYSQL_LOCK_IGNORE_FLUSH) ?
- HA_OPEN_FOR_STATUS : 0),
- entry, FALSE)))
- {
- if (error == 7) // Table def changed
- {
- share->version= 0; // Mark share as old
- if (discover_retry_count++) // Retry once
- goto err;
- /*
- TODO:
- Here we should wait until all threads has released the table.
- For now we do one retry. This may cause a deadlock if there
- is other threads waiting for other tables used by this thread.
-
- Proper fix would be to if the second retry failed:
- - Mark that table def changed
- - Return from open table
- - Close all tables used by this thread
- - Start waiting that the share is released
- - Retry by opening all tables again
- */
- if (ha_create_table_from_engine(thd, table_list->db,
- table_list->table_name))
- goto err;
- /*
- TO BE FIXED
- To avoid deadlock, only wait for release if no one else is
- using the share.
- */
- if (share->ref_count != 1)
- goto err;
- /* Free share and wait until it's released by all threads */
- release_table_share(share, RELEASE_WAIT_FOR_DROP);
- if (!thd->killed)
- {
- mysql_reset_errors(thd, 1); // Clear warnings
- thd->clear_error(); // Clear error message
- goto retry;
- }
- DBUG_RETURN(1);
- }
- if (!entry->s || !entry->s->crashed)
- goto err;
-
- // Code below is for repairing a crashed file
- locked_table= table_list->table != 0;
- if (! locked_table && (error= lock_table_name(thd, table_list, TRUE)))
- {
- if (error < 0)
- goto err;
- if (wait_for_locked_table_names(thd, table_list))
- {
- unlock_table_name(thd, table_list);
- goto err;
- }
- }
- pthread_mutex_unlock(&LOCK_open);
- thd->clear_error(); // Clear error message
- error= 0;
- if (open_table_from_share(thd, share, alias,
- (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
- HA_GET_INDEX |
- HA_TRY_READ_ONLY),
- READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
- ha_open_options | HA_OPEN_FOR_REPAIR,
- entry, FALSE) || ! entry->file ||
- (entry->file->is_crashed() && entry->file->ha_check_and_repair(thd)))
- {
- /* Give right error message */
- thd->clear_error();
- my_error(ER_NOT_KEYFILE, MYF(0), share->table_name.str);
- sql_print_error("Couldn't repair table: %s.%s", share->db.str,
- share->table_name.str);
- if (entry->file)
- closefrm(entry, 0);
- error=1;
- }
- else
- thd->clear_error(); // Clear error message
- pthread_mutex_lock(&LOCK_open);
- if (!locked_table)
- unlock_table_name(thd, table_list);
-
- if (error)
- goto err;
- break;
- }
+/**
+ Finalize the process of TABLE creation by loading table triggers
+ and taking action if a HEAP table content was emptied implicitly.
+*/
+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))
- {
- closefrm(entry, 0);
- goto err;
- }
+ return TRUE;
/*
If we are here, there was no fatal error (but error may be still
@@ -4264,820 +3922,1532 @@ retry:
{
char query_buf[2*FN_REFLEN + 21];
String query(query_buf, sizeof(query_buf), system_charset_info);
+
query.length(0);
- /* this DELETE FROM is needed even with row-based binlogging */
query.append("DELETE FROM ");
append_identifier(thd, &query, share->db.str, share->db.length);
query.append(".");
append_identifier(thd, &query, share->table_name.str,
- share->table_name.length);
- int errcode= query_error_code(thd, TRUE);
- if (thd->binlog_query(THD::STMT_QUERY_TYPE,
- query.ptr(), query.length(),
- FALSE, FALSE, errcode))
- goto err;
+ share->table_name.length);
+
+ /*
+ we bypass thd->binlog_query() here,
+ as it does a lot of extra work, that is simply wrong in this case
+ */
+ Query_log_event qinfo(thd, query.ptr(), query.length(),
+ FALSE, TRUE, TRUE, 0);
+ if (mysql_bin_log.write(&qinfo))
+ return TRUE;
}
}
- DBUG_RETURN(0);
-
-err:
- release_table_share(share, RELEASE_NORMAL);
- DBUG_RETURN(1);
+ return FALSE;
}
/**
- @brief Add list of MERGE children to a TABLE_LIST list.
+ Auxiliary routine which is used for performing automatical table repair.
+*/
- @param[in] tlist the parent TABLE_LIST object just opened
+static bool auto_repair_table(THD *thd, TABLE_LIST *table_list)
+{
+ char cache_key[MAX_DBKEY_LENGTH];
+ uint cache_key_length;
+ TABLE_SHARE *share;
+ TABLE *entry;
+ int not_used;
+ bool result= TRUE;
+ my_hash_value_type hash_value;
- @return status
- @retval 0 OK
- @retval != 0 Error
+ cache_key_length= create_table_def_key(thd, cache_key, table_list, 0);
- @detail
- When a MERGE parent table has just been opened, insert the
- TABLE_LIST chain from the MERGE handle into the table list used for
- opening tables for this statement. This lets the children be opened
- too.
-*/
+ thd->clear_error();
-static int add_merge_table_list(TABLE_LIST *tlist)
-{
- TABLE *parent= tlist->table;
- TABLE_LIST *child_l;
- DBUG_ENTER("add_merge_table_list");
- DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", parent->s->db.str,
- parent->s->table_name.str, (long) parent));
+ hash_value= my_calc_hash(&table_def_cache, (uchar*) cache_key,
+ cache_key_length);
+ mysql_mutex_lock(&LOCK_open);
+
+ if (!(share= get_table_share(thd, table_list, cache_key,
+ cache_key_length,
+ OPEN_VIEW, &not_used,
+ hash_value)))
+ goto end_unlock;
- /* Must not call this with attached children. */
- DBUG_ASSERT(!parent->children_attached);
- /* Must not call this with children list in place. */
- DBUG_ASSERT(tlist->next_global != parent->child_l);
- /* Prevent inclusion of another MERGE table. Could make infinite recursion. */
- if (tlist->parent_l)
+ if (share->is_view)
{
- my_error(ER_ADMIN_WRONG_MRG_TABLE, MYF(0), tlist->alias);
- DBUG_RETURN(1);
+ release_table_share(share);
+ goto end_unlock;
}
- /* Fix children.*/
- for (child_l= parent->child_l; ; child_l= child_l->next_global)
+ if (!(entry= (TABLE*)my_malloc(sizeof(TABLE), MYF(MY_WME))))
{
- /*
- Note: child_l->table may still be set if this parent was taken
- from the unused_tables chain. Ignore this fact here. The
- reference will be replaced by the handler in
- ::extra(HA_EXTRA_ATTACH_CHILDREN).
- */
+ release_table_share(share);
+ goto end_unlock;
+ }
+ mysql_mutex_unlock(&LOCK_open);
+
+ if (open_table_from_share(thd, share, table_list->alias,
+ (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
+ HA_GET_INDEX |
+ HA_TRY_READ_ONLY),
+ READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
+ ha_open_options | HA_OPEN_FOR_REPAIR,
+ entry, FALSE) || ! entry->file ||
+ (entry->file->is_crashed() && entry->file->ha_check_and_repair(thd)))
+ {
+ /* Give right error message */
+ thd->clear_error();
+ my_error(ER_NOT_KEYFILE, MYF(0), share->table_name.str);
+ sql_print_error("Couldn't repair table: %s.%s", share->db.str,
+ share->table_name.str);
+ if (entry->file)
+ closefrm(entry, 0);
+ }
+ else
+ {
+ thd->clear_error(); // Clear error message
+ closefrm(entry, 0);
+ result= FALSE;
+ }
+ my_free(entry);
+
+ mysql_mutex_lock(&LOCK_open);
+ release_table_share(share);
+ /* Remove the repaired share from the table cache. */
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL,
+ table_list->db, table_list->table_name,
+ TRUE);
+end_unlock:
+ mysql_mutex_unlock(&LOCK_open);
+ return result;
+}
- /* Set lock type. */
- child_l->lock_type= tlist->lock_type;
- /* Set parent reference. */
- child_l->parent_l= tlist;
+/** Open_table_context */
- /* Break when this was the last child. */
- if (&child_l->next_global == parent->child_last_l)
- break;
- }
+Open_table_context::Open_table_context(THD *thd, uint flags)
+ :m_thd(thd),
+ m_failed_table(NULL),
+ m_start_of_statement_svp(thd->mdl_context.mdl_savepoint()),
+ m_timeout(flags & MYSQL_LOCK_IGNORE_TIMEOUT ?
+ LONG_TIMEOUT : thd->variables.lock_wait_timeout),
+ m_flags(flags),
+ m_action(OT_NO_ACTION),
+ m_has_locks(thd->mdl_context.has_locks()),
+ m_has_protection_against_grl(FALSE)
+{}
- /* Insert children into the table list. */
- *parent->child_last_l= tlist->next_global;
- tlist->next_global= parent->child_l;
+/**
+ Check if we can back-off and set back off action if we can.
+ Otherwise report and return error.
+
+ @retval TRUE if back-off is impossible.
+ @retval FALSE if we can back off. Back off action has been set.
+*/
+
+bool
+Open_table_context::
+request_backoff_action(enum_open_table_action action_arg,
+ TABLE_LIST *table)
+{
/*
- Do not fix the prev_global pointers. We will remove the
- chain soon anyway.
+ A back off action may be one of three kinds:
+
+ * We met a broken table that needs repair, or a table that
+ is not present on this MySQL server and needs re-discovery.
+ To perform the action, we need an exclusive metadata lock on
+ the table. Acquiring an X lock while holding other shared
+ locks is very deadlock-prone. If this is a multi- statement
+ transaction that holds metadata locks for completed
+ statements, we don't do it, and report an error instead.
+ The action type in this case is OT_DISCOVER or OT_REPAIR.
+ * Our attempt to acquire an MDL lock lead to a deadlock,
+ detected by the MDL deadlock detector. The current
+ session was chosen a victim. If this is a multi-statement
+ transaction that holds metadata locks taken by completed
+ statements, restarting locking for the current statement
+ may lead to a livelock. Releasing locks of completed
+ statements can not be done as will lead to violation
+ of ACID. Thus, again, if m_has_locks is set,
+ we report an error. Otherwise, when there are no metadata
+ locks other than which belong to this statement, we can
+ try to recover from error by releasing all locks and
+ restarting the pre-locking.
+ Similarly, a deadlock error can occur when the
+ pre-locking process met a TABLE_SHARE that is being
+ flushed, and unsuccessfully waited for the flush to
+ complete. A deadlock in this case can happen, e.g.,
+ when our session is holding a metadata lock that
+ is being waited on by a session which is using
+ the table which is being flushed. The only way
+ to recover from this error is, again, to close all
+ open tables, release all locks, and retry pre-locking.
+ Action type name is OT_REOPEN_TABLES. Re-trying
+ while holding some locks may lead to a livelock,
+ and thus we don't do it.
+ * Finally, this session has open TABLEs from different
+ "generations" of the table cache. This can happen, e.g.,
+ when, after this session has successfully opened one
+ table used for a statement, FLUSH TABLES interfered and
+ expelled another table used in it. FLUSH TABLES then
+ blocks and waits on the table already opened by this
+ statement.
+ We detect this situation by ensuring that table cache
+ version of all tables used in a statement is the same.
+ If it isn't, all tables needs to be reopened.
+ Note, that we can always perform a reopen in this case,
+ even if we already have metadata locks, since we don't
+ keep tables open between statements and a livelock
+ is not possible.
*/
-
- DBUG_RETURN(0);
+ if (action_arg != OT_REOPEN_TABLES && m_has_locks)
+ {
+ my_error(ER_LOCK_DEADLOCK, MYF(0));
+ mark_transaction_to_rollback(m_thd, true);
+ return TRUE;
+ }
+ /*
+ If auto-repair or discovery are requested, a pointer to table
+ list element must be provided.
+ */
+ if (table)
+ {
+ DBUG_ASSERT(action_arg == OT_DISCOVER || action_arg == OT_REPAIR);
+ 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->mdl_request.set_type(MDL_EXCLUSIVE);
+ }
+ m_action= action_arg;
+ return FALSE;
}
/**
- @brief Attach MERGE children to the parent.
-
- @param[in] tlist the child TABLE_LIST object just opened
+ Recover from failed attempt of open table by performing requested action.
- @return status
- @retval 0 OK
- @retval != 0 Error
+ @pre This function should be called only with "action" != OT_NO_ACTION
+ and after having called @sa close_tables_for_reopen().
- @note
- This is called when the last MERGE child has just been opened, let
- the handler attach the MyISAM tables to the MERGE table. Remove
- MERGE TABLE_LIST chain from the statement list so that it cannot be
- changed or freed.
+ @retval FALSE - Success. One should try to open tables once again.
+ @retval TRUE - Error
*/
-static int attach_merge_children(TABLE_LIST *tlist)
+bool
+Open_table_context::
+recover_from_failed_open()
{
- TABLE *parent= tlist->parent_l->table;
- int error;
- DBUG_ENTER("attach_merge_children");
- DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", parent->s->db.str,
- parent->s->table_name.str, (long) parent));
+ bool result= FALSE;
+ /* Execute the action. */
+ switch (m_action)
+ {
+ case OT_BACKOFF_AND_RETRY:
+ break;
+ case OT_REOPEN_TABLES:
+ break;
+ case OT_DISCOVER:
+ {
+ if ((result= lock_table_names(m_thd, m_failed_table, NULL,
+ get_timeout(),
+ MYSQL_OPEN_SKIP_TEMPORARY)))
+ break;
+
+ tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
+ m_failed_table->table_name, FALSE);
+ ha_create_table_from_engine(m_thd, m_failed_table->db,
+ m_failed_table->table_name);
- /* Must not call this with attached children. */
- DBUG_ASSERT(!parent->children_attached);
- /* Must call this with children list in place. */
- DBUG_ASSERT(tlist->parent_l->next_global == parent->child_l);
+ m_thd->warning_info->clear_warning_info(m_thd->query_id);
+ m_thd->clear_error(); // Clear error message
+ m_thd->mdl_context.release_transactional_locks();
+ break;
+ }
+ case OT_REPAIR:
+ {
+ if ((result= lock_table_names(m_thd, m_failed_table, NULL,
+ get_timeout(),
+ MYSQL_OPEN_SKIP_TEMPORARY)))
+ break;
- /* Attach MyISAM tables to MERGE table. */
- error= parent->file->extra(HA_EXTRA_ATTACH_CHILDREN);
+ tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
+ m_failed_table->table_name, FALSE);
+ result= auto_repair_table(m_thd, m_failed_table);
+ m_thd->mdl_context.release_transactional_locks();
+ break;
+ }
+ default:
+ DBUG_ASSERT(0);
+ }
/*
- Remove children from the table list. Even in case of an error.
- This should prevent tampering with them.
+ Reset the pointers to conflicting MDL request and the
+ TABLE_LIST element, set when we need auto-discovery or repair,
+ for safety.
*/
- tlist->parent_l->next_global= *parent->child_last_l;
-
+ m_failed_table= NULL;
/*
- Do not fix the last childs next_global pointer. It is needed for
- stepping to the next table in the enclosing loop in open_tables().
- Do not fix prev_global pointers. We did not set them.
+ Reset flag indicating that we have already acquired protection
+ against GRL. It is no longer valid as the corresponding lock was
+ released by close_tables_for_reopen().
*/
+ m_has_protection_against_grl= FALSE;
+ /* Prepare for possible another back-off. */
+ m_action= OT_NO_ACTION;
+ return result;
+}
- if (error)
- {
- DBUG_PRINT("error", ("attaching MERGE children failed: %d", my_errno));
- parent->file->print_error(error, MYF(0));
- DBUG_RETURN(1);
- }
- parent->children_attached= TRUE;
- DBUG_PRINT("myrg", ("attached parent: '%s'.'%s' 0x%lx", parent->s->db.str,
- parent->s->table_name.str, (long) parent));
+/*
+ Return a appropriate read lock type given a table object.
+ @param thd Thread context
+ @param prelocking_ctx Prelocking context.
+ @param table_list Table list element for table to be locked.
+
+ @remark Due to a statement-based replication limitation, statements such as
+ INSERT INTO .. SELECT FROM .. and CREATE TABLE .. SELECT FROM need
+ to grab a TL_READ_NO_INSERT lock on the source table in order to
+ prevent the replication of a concurrent statement that modifies the
+ source table. If such a statement gets applied on the slave before
+ the INSERT .. SELECT statement finishes, data on the master could
+ differ from data on the slave and end-up with a discrepancy between
+ the binary log and table state.
+ This also applies to SELECT/SET/DO statements which use stored
+ functions. Calls to such functions are going to be logged as a
+ whole and thus should be serialized against concurrent changes
+ to tables used by those functions. This can be avoided if functions
+ only read data but doing so requires more complex analysis than it
+ is done now.
+ Furthermore, this does not apply to I_S and log tables as it's
+ always unsafe to replicate such tables under statement-based
+ replication as the table on the slave might contain other data
+ (ie: general_log is enabled on the slave). The statement will
+ be marked as unsafe for SBR in decide_logging_format().
+ @remark Note that even in prelocked mode it is important to correctly
+ determine lock type value. In this mode lock type is passed to
+ handler::start_stmt() method and can be used by storage engine,
+ for example, to determine what kind of row locks it should acquire
+ when reading data from the table.
+*/
+
+thr_lock_type read_lock_type_for_table(THD *thd,
+ Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list)
+{
/*
- Note that we have the cildren in the thd->open_tables list at this
- point.
+ In cases when this function is called for a sub-statement executed in
+ prelocked mode we can't rely on OPTION_BIN_LOG flag in THD::options
+ bitmap to determine that binary logging is turned on as this bit can
+ be cleared before executing sub-statement. So instead we have to look
+ at THD::variables::sql_log_bin member.
*/
-
- DBUG_RETURN(0);
+ bool log_on= mysql_bin_log.is_open() && thd->variables.sql_log_bin;
+ ulong binlog_format= thd->variables.binlog_format;
+ if ((log_on == FALSE) || (binlog_format == BINLOG_FORMAT_ROW) ||
+ (table_list->table->s->table_category == TABLE_CATEGORY_LOG) ||
+ (table_list->table->s->table_category == TABLE_CATEGORY_PERFORMANCE) ||
+ !(is_update_query(prelocking_ctx->sql_command) ||
+ table_list->prelocking_placeholder ||
+ (thd->locked_tables_mode > LTM_LOCK_TABLES)))
+ return TL_READ;
+ else
+ return TL_READ_NO_INSERT;
}
-/**
- @brief Detach MERGE children from the parent.
+/*
+ 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.
+
+ @param[in] thd Thread context.
+ @param[in] prelocking_ctx Prelocking context.
+ @param[in] rt Element of prelocking set to be processed.
+ @param[in] prelocking_strategy Strategy which specifies how the
+ prelocking set should be extended when
+ one of its elements is processed.
+ @param[in] has_prelocking_list Indicates that prelocking set/list for
+ this statement has already been built.
+ @param[in] ot_ctx Context of open_table used to recover from
+ locking failures.
+ @param[out] need_prelocking Set to TRUE if it was detected that this
+ statement will require prelocked mode for
+ its execution, not touched otherwise.
+
+ @retval FALSE Success.
+ @retval TRUE Failure (Conflicting metadata lock, OOM, other errors).
+*/
- @note
- Call this before the first table of a MERGE table (parent or child)
- is closed.
+static bool
+open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx,
+ Sroutine_hash_entry *rt,
+ Prelocking_strategy *prelocking_strategy,
+ bool has_prelocking_list,
+ Open_table_context *ot_ctx,
+ bool *need_prelocking)
+{
+ MDL_key::enum_mdl_namespace mdl_type= rt->mdl_request.key.mdl_namespace();
+ DBUG_ENTER("open_and_process_routine");
- When closing thread tables at end of statement, both parent and
- children are in thd->open_tables and will be closed. In most cases
- the children will be closed before the parent. They are opened after
- the parent and thus stacked into thd->open_tables before it.
+ switch (mdl_type)
+ {
+ case MDL_key::FUNCTION:
+ case MDL_key::PROCEDURE:
+ {
+ sp_head *sp;
+ /*
+ Try to get MDL lock on the routine.
+ Note that we do not take locks on top-level CALLs as this can
+ lead to a deadlock. Not locking top-level CALLs does not break
+ the binlog as only the statements in the called procedure show
+ up there, not the CALL itself.
+ */
+ if (rt != (Sroutine_hash_entry*)prelocking_ctx->sroutines_list.first ||
+ 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);
- To avoid that we touch a closed children in any way, we must detach
- the children from the parent when the first belonging table is
- closed (parent or child).
+ /*
+ 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);
- All references to the children should be removed on handler level
- and optionally on table level.
+ 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();
- @note
- Assure that you call it for a MERGE parent or child only.
- Either table->child_l or table->parent must be set.
-
- @param[in] table the TABLE object of the parent
- @param[in] clear_refs if to clear TABLE references
- this must be true when called from
- close_thread_tables() to enable fresh
- open in open_tables()
- it must be false when called in preparation
- for reopen_tables()
+ if (result)
+ 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))
+ DBUG_RETURN(TRUE);
+
+ /* Remember the version of the routine in the parse tree. */
+ if (check_and_update_routine_version(thd, rt, sp))
+ DBUG_RETURN(TRUE);
+
+ /* 'sp' is NULL when there is no such routine. */
+ if (sp && !has_prelocking_list)
+ {
+ prelocking_strategy->handle_routine(thd, prelocking_ctx, rt, sp,
+ need_prelocking);
+ }
+ }
+ else
+ {
+ /*
+ If it's a top level call, just make sure we have a recent
+ version of the routine, if it exists.
+ Validating routine version is unnecessary, since CALL
+ does not affect the prepared statement prelocked list.
+ */
+ if (sp_cache_routine(thd, rt, FALSE, &sp))
+ DBUG_RETURN(TRUE);
+ }
+ }
+ break;
+ case MDL_key::TRIGGER:
+ /**
+ We add trigger entries to lex->sroutines_list, but we don't
+ load them here. The trigger entry is only used when building
+ a transitive closure of objects used in a statement, to avoid
+ adding to this closure objects that are used in the trigger more
+ than once.
+ E.g. if a trigger trg refers to table t2, and the trigger table t1
+ is used multiple times in the statement (say, because it's used in
+ function f1() twice), we will only add t2 once to the list of
+ tables to prelock.
+
+ We don't take metadata locks on triggers either: they are protected
+ by a respective lock on the table, on which the trigger is defined.
+
+ The only two cases which give "trouble" are SHOW CREATE TRIGGER
+ and DROP TRIGGER statements. For these, statement syntax doesn't
+ specify the table on which this trigger is defined, so we have
+ to make a "dirty" read in the data dictionary to find out the
+ table name. Once we discover the table name, we take a metadata
+ lock on it, and this protects all trigger operations.
+ Of course the table, in theory, may disappear between the dirty
+ read and metadata lock acquisition, but in that case we just return
+ a run-time error.
+
+ Grammar of other trigger DDL statements (CREATE, DROP) requires
+ the table to be specified explicitly, so we use the table metadata
+ lock to protect trigger metadata in these statements. Similarly, in
+ DML we always use triggers together with their tables, and thus don't
+ need to take separate metadata locks on them.
+ */
+ break;
+ default:
+ /* Impossible type value. */
+ DBUG_ASSERT(0);
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Handle table list element by obtaining metadata lock, opening table or view
+ and, if prelocking strategy prescribes so, extending the prelocking set with
+ tables and routines used by it.
+
+ @param[in] thd Thread context.
+ @param[in] lex LEX structure for statement.
+ @param[in] tables Table list element to be processed.
+ @param[in,out] counter Number of tables which are open.
+ @param[in] flags Bitmap of flags to modify how the tables
+ will be open, see open_table() description
+ for details.
+ @param[in] prelocking_strategy Strategy which specifies how the
+ prelocking set should be extended
+ when table or view is processed.
+ @param[in] has_prelocking_list Indicates that prelocking set/list for
+ this statement has already been built.
+ @param[in] ot_ctx Context used to recover from a failed
+ open_table() attempt.
+ @param[in] new_frm_mem Temporary MEM_ROOT to be used for
+ parsing .FRMs for views.
+
+ @retval FALSE Success.
+ @retval TRUE Error, reported unless there is a chance to recover from it.
*/
-void detach_merge_children(TABLE *table, bool clear_refs)
+static bool
+open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables,
+ uint *counter, uint flags,
+ Prelocking_strategy *prelocking_strategy,
+ bool has_prelocking_list,
+ Open_table_context *ot_ctx,
+ MEM_ROOT *new_frm_mem)
{
- TABLE_LIST *child_l;
- TABLE *parent= table->child_l ? table : table->parent;
- DBUG_ENTER("detach_merge_children");
+ bool error= FALSE;
+ bool safe_to_ignore_table= FALSE;
+ DBUG_ENTER("open_and_process_table");
+ DEBUG_SYNC(thd, "open_and_process_table");
+
/*
- Either table->child_l or table->parent must be set. Parent must have
- child_l set.
+ Ignore placeholders for derived tables. After derived tables
+ processing, link to created temporary table will be put here.
+ If this is derived table for view then we still want to process
+ routines used by this view.
*/
- DBUG_ASSERT(parent && parent->child_l);
- DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx clear_refs: %d",
- table->s->db.str, table->s->table_name.str,
- (long) table, clear_refs));
- DBUG_PRINT("myrg", ("parent: '%s'.'%s' 0x%lx", parent->s->db.str,
- parent->s->table_name.str, (long) parent));
-
+ if (tables->derived)
+ {
+ if (!tables->view)
+ goto end;
+ /*
+ We restore view's name and database wiped out by derived tables
+ processing and fall back to standard open process in order to
+ 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;
+ }
/*
- In a open_tables() loop it can happen that not all tables have their
- children attached yet. Also this is called for every child and the
- parent from close_thread_tables().
+ If this TABLE_LIST object is a placeholder for an information_schema
+ table, create a temporary table to represent the information_schema
+ table in the query. Do not fill it yet - will be filled during
+ execution.
*/
- if (parent->children_attached)
+ if (tables->schema_table)
+ {
+ /*
+ If this information_schema table is merged into a mergeable
+ view, ignore it for now -- it will be filled when its respective
+ TABLE_LIST is processed. This code works only during re-execution.
+ */
+ if (tables->view)
+ {
+ MDL_ticket *mdl_ticket;
+ /*
+ We still need to take a MDL lock on the merged view to protect
+ it from concurrent changes.
+ */
+ if (!open_table_get_mdl_lock(thd, ot_ctx, &tables->mdl_request,
+ flags, &mdl_ticket) &&
+ mdl_ticket != NULL)
+ goto process_view_routines;
+ /* Fall-through to return error. */
+ }
+ else if (!mysql_schema_table(thd, lex, tables) &&
+ !check_and_update_table_version(thd, tables, tables->table->s))
+ {
+ goto end;
+ }
+ error= TRUE;
+ goto end;
+ }
+ DBUG_PRINT("tcache", ("opening table: '%s'.'%s' item: %p",
+ tables->db, tables->table_name, tables)); //psergey: invalid read of size 1 here
+ (*counter)++;
+
+ /* Not a placeholder: must be a base table or a view. Let us open it. */
+ DBUG_ASSERT(!tables->table);
+
+ if (tables->prelocking_placeholder)
+ {
+ /*
+ For the tables added by the pre-locking code, attempt to open
+ the table but fail silently if the table does not exist.
+ The real failure will occur when/if a statement attempts to use
+ that table.
+ */
+ No_such_table_error_handler no_such_table_handler;
+ thd->push_internal_handler(&no_such_table_handler);
+ error= open_table(thd, tables, new_frm_mem, ot_ctx);
+ thd->pop_internal_handler();
+ safe_to_ignore_table= no_such_table_handler.safely_trapped_errors();
+ }
+ else if (tables->parent_l && (thd->open_options & HA_OPEN_FOR_REPAIR))
{
- VOID(parent->file->extra(HA_EXTRA_DETACH_CHILDREN));
- parent->children_attached= FALSE;
- DBUG_PRINT("myrg", ("detached parent: '%s'.'%s' 0x%lx", parent->s->db.str,
- parent->s->table_name.str, (long) parent));
+ /*
+ Also fail silently for underlying tables of a MERGE table if this
+ table is opened for CHECK/REPAIR TABLE statement. This is needed
+ to provide complete list of problematic underlying tables in
+ CHECK/REPAIR TABLE output.
+ */
+ Repair_mrg_table_error_handler repair_mrg_table_handler;
+ thd->push_internal_handler(&repair_mrg_table_handler);
+ error= open_table(thd, tables, new_frm_mem, ot_ctx);
+ thd->pop_internal_handler();
+ safe_to_ignore_table= repair_mrg_table_handler.safely_trapped_errors();
}
else
- DBUG_PRINT("myrg", ("parent is already detached"));
+ error= open_table(thd, tables, new_frm_mem, ot_ctx);
- if (clear_refs)
+ free_root(new_frm_mem, MYF(MY_KEEP_PREALLOC));
+
+ if (error)
{
- if (table->parent)
+ if (! ot_ctx->can_recover_from_failed_open() && safe_to_ignore_table)
{
- /* In any case clear the own parent reference. (***) */
- table->parent= NULL;
- table->file->extra(HA_EXTRA_DETACH_CHILD);
+ DBUG_PRINT("info", ("open_table: ignoring table '%s'.'%s'",
+ tables->db, tables->alias));
+ error= FALSE;
}
+ goto end;
+ }
+
+ /*
+ We can't rely on simple check for TABLE_LIST::view to determine
+ that this is a view since during re-execution we might reopen
+ ordinary table in place of view and thus have TABLE_LIST::view
+ set from repvious execution and TABLE_LIST::table set from
+ current.
+ */
+ if (!tables->table && tables->view)
+ {
+ /* VIEW placeholder */
+ (*counter)--;
/*
- Clear all references. If this table is the parent, we still may
- need to clear the child references. The first detach might not
- have done this.
+ tables->next_global list consists of two parts:
+ 1) Query tables and underlying tables of views.
+ 2) Tables used by all stored routines that this statement invokes on
+ execution.
+ We need to know where the bound between these two parts is. If we've
+ just opened a view, which was the last table in part #1, and it
+ has added its base tables after itself, adjust the boundary pointer
+ accordingly.
*/
- for (child_l= parent->child_l; ; child_l= child_l->next_global)
- {
- /*
- Do not DBUG_ASSERT(child_l->table); open_tables might be
- incomplete or we may have been called twice.
+ if (lex->query_tables_own_last == &(tables->next_global) &&
+ tables->view->query_tables)
+ lex->query_tables_own_last= tables->view->query_tables_last;
+ /*
+ Let us free memory used by 'sroutines' hash here since we never
+ call destructor for this LEX.
+ */
+ my_hash_free(&tables->view->sroutines);
+ goto process_view_routines;
+ }
- Clear the parent reference of the children only on the first
- detach. The children might already be closed. They will clear
- it themselves when this function is called for them with
- 'clear_refs' true. See above "(***)".
- */
- if (child_l->table)
- {
- if (child_l->table->parent)
- {
- child_l->table->parent= NULL;
- if (child_l->table->db_stat)
- child_l->table->file->extra(HA_EXTRA_DETACH_CHILD);
- }
- /*
- Set alias to "" to ensure that table is not used if we are in
- LOCK TABLES
- */
- child_l->table->alias.length(0);
+ /*
+ Special types of open can succeed but still don't set
+ TABLE_LIST::table to anything.
+ */
+ if (tables->open_strategy && !tables->table)
+ goto end;
- /* Clear the table reference to force new assignment at next open. */
- child_l->table= NULL;
- }
+ /*
+ If we are not already in prelocked mode and extended table list is not
+ yet built we might have to build the prelocking set for this statement.
- /* Break when this was the last child. */
- if (&child_l->next_global == parent->child_last_l)
- break;
- }
+ Since currently no prelocking strategy prescribes doing anything for
+ tables which are only read, we do below checks only if table is going
+ to be changed.
+ */
+ if (thd->locked_tables_mode <= LTM_LOCK_TABLES &&
+ ! has_prelocking_list &&
+ tables->lock_type >= TL_WRITE_ALLOW_WRITE)
+ {
+ bool need_prelocking= FALSE;
+ TABLE_LIST **save_query_tables_last= lex->query_tables_last;
+ /*
+ Extend statement's table list and the prelocking set with
+ tables and routines according to the current prelocking
+ strategy.
+
+ For example, for DML statements we need to add tables and routines
+ used by triggers which are going to be invoked for this element of
+ table list and also add tables required for handling of foreign keys.
+ */
+ error= prelocking_strategy->handle_table(thd, lex, tables,
+ &need_prelocking);
+
+ if (need_prelocking && ! lex->requires_prelocking())
+ lex->mark_as_requiring_prelocking(save_query_tables_last);
+
+ if (error)
+ goto end;
}
- DBUG_VOID_RETURN;
-}
+ if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables_mode)
+ {
+ if (tables->lock_type == TL_WRITE_DEFAULT)
+ tables->table->reginfo.lock_type= thd->update_lock_default;
+ else if (tables->lock_type == TL_READ_DEFAULT)
+ tables->table->reginfo.lock_type=
+ read_lock_type_for_table(thd, lex, tables);
+ else
+ tables->table->reginfo.lock_type= tables->lock_type;
+ }
+ tables->table->grant= tables->grant;
+ /* Check and update metadata version of a base table. */
+ error= check_and_update_table_version(thd, tables, tables->table->s);
-/**
- @brief Fix MERGE children after open.
+ if (error)
+ goto end;
+ /*
+ After opening a MERGE table add the children to the query list of
+ tables, so that they are opened too.
+ Note that placeholders don't have the handler open.
+ */
+ /* MERGE tables need to access parent and child TABLE_LISTs. */
+ DBUG_ASSERT(tables->table->pos_in_table_list == tables);
+ /* Non-MERGE tables ignore this call. */
+ if (tables->table->file->extra(HA_EXTRA_ADD_CHILDREN_LIST))
+ {
+ error= TRUE;
+ goto end;
+ }
- @param[in] old_child_list first list member from original table
- @param[in] old_last pointer to &next_global of last list member
- @param[in] new_child_list first list member from freshly opened table
- @param[in] new_last pointer to &next_global of last list member
+process_view_routines:
+ /*
+ Again we may need cache all routines used by this view and add
+ tables used by them to table list.
+ */
+ if (tables->view &&
+ thd->locked_tables_mode <= LTM_LOCK_TABLES &&
+ ! has_prelocking_list)
+ {
+ bool need_prelocking= FALSE;
+ TABLE_LIST **save_query_tables_last= lex->query_tables_last;
- @return mismatch
- @retval FALSE OK, no mismatch
- @retval TRUE Error, lists mismatch
+ error= prelocking_strategy->handle_view(thd, lex, tables,
+ &need_prelocking);
- @detail
- Main action is to copy TABLE reference for each member of original
- child list to new child list. After a fresh open these references
- are NULL. Assign the old children to the new table. Some of them
- might also be reopened or will be reopened soon.
+ if (need_prelocking && ! lex->requires_prelocking())
+ lex->mark_as_requiring_prelocking(save_query_tables_last);
- Other action is to verify that the table definition with respect to
- the UNION list did not change.
+ if (error)
+ goto end;
+ }
- @note
- This function terminates the child list if the respective '*_last'
- pointer is non-NULL. Do not call it from a place where the list is
- embedded in another list and this would break it.
+end:
+ DBUG_RETURN(error);
+}
- Terminating the list is required for example in the first
- reopen_table() after open_tables(). open_tables() requires the end
- of the list not to be terminated because other tables could follow
- behind the child list.
+extern "C" uchar *schema_set_get_key(const uchar *record, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ TABLE_LIST *table=(TABLE_LIST*) record;
+ *length= table->db_length;
+ return (uchar*) table->db;
+}
- If a '*_last' pointer is NULL, the respective list is assumed to be
- NULL terminated.
+/**
+ Acquire upgradable (SNW, SNRW) metadata locks on tables used by
+ LOCK TABLES or by a DDL statement. Under LOCK TABLES, we can't take
+ new locks, so use open_tables_check_upgradable_mdl() instead.
+
+ @param thd Thread context.
+ @param tables_start Start of list of tables on which upgradable locks
+ should be acquired.
+ @param tables_end End of list of tables.
+ @param lock_wait_timeout Seconds to wait before timeout.
+ @param flags Bitmap of flags to modify how the tables will be
+ open, see open_table() description for details.
+
+ @retval FALSE Success.
+ @retval TRUE Failure (e.g. connection was killed) or table existed
+ for a CREATE TABLE.
+
+ @notes
+ In case of CREATE TABLE we avoid a wait for tables that are in use
+ by first trying to do a meta data lock with timeout == 0. If we get a
+ timeout we will check if table exists (it should) and retry with
+ normal timeout if it didn't exists.
+ Note that for CREATE TABLE IF EXISTS we only generate a warning
+ but still return TRUE (to abort the calling open_table() function).
+ On must check THD->is_error() if one wants to distinguish between warning
+ and error.
*/
-bool fix_merge_after_open(TABLE_LIST *old_child_list, TABLE_LIST **old_last,
- TABLE_LIST *new_child_list, TABLE_LIST **new_last)
+bool
+lock_table_names(THD *thd,
+ TABLE_LIST *tables_start, TABLE_LIST *tables_end,
+ ulong lock_wait_timeout, uint flags)
{
- bool mismatch= FALSE;
- DBUG_ENTER("fix_merge_after_open");
- DBUG_PRINT("myrg", ("old last addr: 0x%lx new last addr: 0x%lx",
- (long) old_last, (long) new_last));
+ MDL_request_list mdl_requests;
+ TABLE_LIST *table;
+ MDL_request global_request;
+ Hash_set<TABLE_LIST, schema_set_get_key> schema_set;
+ ulong org_lock_wait_timeout= lock_wait_timeout;
+ /* Check if we are using CREATE TABLE ... IF NOT EXISTS */
+ bool create_table;
+ Dummy_error_handler error_handler;
+ DBUG_ENTER("lock_table_names");
+
+ DBUG_ASSERT(!thd->locked_tables_mode);
+
+ for (table= tables_start; table && table != tables_end;
+ table= table->next_global)
+ {
+ if (table->mdl_request.type >= MDL_SHARED_NO_WRITE &&
+ !(table->open_type == OT_TEMPORARY_ONLY ||
+ (flags & MYSQL_OPEN_TEMPORARY_ONLY) ||
+ (table->open_type != OT_BASE_ONLY &&
+ ! (flags & MYSQL_OPEN_SKIP_TEMPORARY) &&
+ find_temporary_table(thd, table))))
+ {
+ if (! (flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK) &&
+ schema_set.insert(table))
+ DBUG_RETURN(TRUE);
+ mdl_requests.push_front(&table->mdl_request);
+ }
+ }
- /* Terminate the lists for easier check of list end. */
- if (old_last)
- *old_last= NULL;
- if (new_last)
- *new_last= NULL;
+ if (mdl_requests.is_empty())
+ DBUG_RETURN(FALSE);
- for (;;)
+ /* Check if CREATE TABLE IF NOT EXISTS was used */
+ create_table= (tables_start && tables_start->open_strategy ==
+ TABLE_LIST::OPEN_IF_EXISTS);
+
+ if (!(flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK))
{
- DBUG_PRINT("myrg", ("old list item: 0x%lx new list item: 0x%lx",
- (long) old_child_list, (long) new_child_list));
- /* Break if one of the list is at its end. */
- if (!old_child_list || !new_child_list)
- break;
- /* Old table has references to child TABLEs. */
- DBUG_ASSERT(old_child_list->table);
- /* New table does not yet have references to child TABLEs. */
- DBUG_ASSERT(!new_child_list->table);
- DBUG_PRINT("myrg", ("old table: '%s'.'%s' new table: '%s'.'%s'",
- old_child_list->db, old_child_list->table_name,
- new_child_list->db, new_child_list->table_name));
- /* Child db.table names must match. */
- if (strcmp(old_child_list->table_name, new_child_list->table_name) ||
- strcmp(old_child_list->db, new_child_list->db))
- break;
/*
- Copy TABLE reference. Child TABLE objects are still in place
- though not necessarily open yet.
+ Scoped locks: Take intention exclusive locks on all involved
+ schemas.
*/
- DBUG_PRINT("myrg", ("old table ref: 0x%lx replaces new table ref: 0x%lx",
- (long) old_child_list->table,
- (long) new_child_list->table));
- new_child_list->table= old_child_list->table;
- /* Step both lists. */
- old_child_list= old_child_list->next_global;
- new_child_list= new_child_list->next_global;
- }
- DBUG_PRINT("myrg", ("end of list, mismatch: %d", mismatch));
- /*
- If the list pointers are not both NULL after the loop, then the
- lists differ. If the are both identical, but not NULL, then they
- have at least one table in common and hence the rest of the list
- would be identical too. But in this case the loop woul run until the
- list end, where both pointers would become NULL.
- */
- if (old_child_list != new_child_list)
- mismatch= TRUE;
- if (mismatch)
- my_error(ER_TABLE_DEF_CHANGED, MYF(0));
+ Hash_set<TABLE_LIST, schema_set_get_key>::Iterator it(schema_set);
+ while ((table= it++))
+ {
+ 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, "",
+ MDL_INTENTION_EXCLUSIVE,
+ MDL_TRANSACTION);
+ mdl_requests.push_front(schema_request);
+ }
- DBUG_RETURN(mismatch);
-}
+ /*
+ Protect this statement against concurrent global read lock
+ by acquiring global intention exclusive lock with statement
+ duration.
+ */
+ if (thd->global_read_lock.can_acquire_protection())
+ DBUG_RETURN(TRUE);
+ global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
+ MDL_STATEMENT);
+ mdl_requests.push_front(&global_request);
+ if (create_table)
+ lock_wait_timeout= 0; // Don't wait for timeout
+ }
-/*
- Return a appropriate read lock type given a table object.
+ for (;;)
+ {
+ bool exists= TRUE;
+ bool res;
- @param thd Thread context
- @param lex LEX for the current statement.
- @param table_list Table list element for table to be locked.
+ if (create_table)
+ thd->push_internal_handler(&error_handler); // Avoid warnings & errors
+ res= thd->mdl_context.acquire_locks(&mdl_requests, lock_wait_timeout);
+ if (create_table)
+ thd->pop_internal_handler();
+ if (!res)
+ DBUG_RETURN(FALSE); // Got locks
- @remark Due to a statement-based replication limitation, statements such as
- INSERT INTO .. SELECT FROM .. and CREATE TABLE .. SELECT FROM need
- to grab a TL_READ_NO_INSERT lock on the source table in order to
- prevent the replication of a concurrent statement that modifies the
- source table. If such a statement gets applied on the slave before
- the INSERT .. SELECT statement finishes, data on the master could
- differ from data on the slave and end-up with a discrepancy between
- the binary log and table state.
- This also applies to SELECT/SET/DO statements which use stored
- functions. Calls to such functions are going to be logged as a
- whole and thus should be serialized against concurrent changes
- to tables used by those functions. This can be avoided if functions
- only read data but doing so requires more complex analysis than it
- is done now (unfortunately, due to bug #53921 "Wrong locks for
- SELECTs used stored functions may lead to broken SBR" this rule
- is not followed in cases when stored function or trigger use
- simple SELECT and not a subselect in their body).
- Furthermore, this does not apply to I_S and log tables as it's
- always unsafe to replicate such tables under statement-based
- replication as the table on the slave might contain other data
- (ie: general_log is enabled on the slave). The statement will
- be marked as unsafe for SBR in decide_logging_format().
+ if (!create_table)
+ DBUG_RETURN(TRUE); // Return original error
+
+ /*
+ We come here in the case of lock timeout when executing
+ CREATE TABLE IF NOT EXISTS.
+ Verify that table really exists (it should as we got a lock conflict)
+ */
+ if (check_if_table_exists(thd, tables_start, 1, &exists))
+ DBUG_RETURN(TRUE); // Should never happen
+ if (exists)
+ {
+ if (thd->lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
+ tables_start->table_name);
+ }
+ else
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), tables_start->table_name);
+ DBUG_RETURN(TRUE);
+ }
+ /* purecov: begin inspected */
+ /*
+ We got error from acquire_locks but table didn't exists.
+ In theory this should never happen, except maybe in
+ CREATE or DROP DATABASE scenario.
+ We play safe and restart the original acquire_locks with the
+ original timeout
+ */
+ create_table= 0;
+ lock_wait_timeout= org_lock_wait_timeout;
+ /* purecov: end */
+ }
+}
+
+
+/**
+ Check for upgradable (SNW, SNRW) metadata locks on tables to be opened
+ for a DDL statement. Under LOCK TABLES, we can't take new locks, so we
+ must check if appropriate locks were pre-acquired.
+
+ @param thd Thread context.
+ @param tables_start Start of list of tables on which upgradable locks
+ should be searched for.
+ @param tables_end End of list of tables.
+ @param flags Bitmap of flags to modify how the tables will be
+ open, see open_table() description for details.
+
+ @retval FALSE Success.
+ @retval TRUE Failure (e.g. connection was killed)
*/
-thr_lock_type read_lock_type_for_table(THD *thd, LEX *lex,
- TABLE_LIST *table_list)
+static bool
+open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
+ TABLE_LIST *tables_end, uint flags)
{
- bool log_on= mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG);
- ulong binlog_format= thd->variables.binlog_format;
- if ((log_on == FALSE) || (binlog_format == BINLOG_FORMAT_ROW) ||
- (table_list->table->s->table_category == TABLE_CATEGORY_PERFORMANCE) ||
- (lex->sql_command == SQLCOM_SELECT &&
- ! table_list->prelocking_placeholder))
- return TL_READ;
- else
- return TL_READ_NO_INSERT;
+ TABLE_LIST *table;
+
+ DBUG_ASSERT(thd->locked_tables_mode);
+
+ for (table= tables_start; table && table != tables_end;
+ table= table->next_global)
+ {
+ if (table->mdl_request.type >= MDL_SHARED_NO_WRITE &&
+ !(table->open_type == OT_TEMPORARY_ONLY ||
+ (flags & MYSQL_OPEN_TEMPORARY_ONLY) ||
+ (table->open_type != OT_BASE_ONLY &&
+ ! (flags & MYSQL_OPEN_SKIP_TEMPORARY) &&
+ find_temporary_table(thd, table))))
+ {
+ /*
+ We don't need to do anything about the found TABLE instance as it
+ will be handled later in open_tables(), we only need to check that
+ an upgradable lock is already acquired. When we enter LOCK TABLES
+ mode, SNRW locks are acquired before all other locks. So if under
+ LOCK TABLES we find that there is TABLE instance with upgradeable
+ lock, all other instances of TABLE for the same table will have the
+ same ticket.
+
+ Note that this works OK even for CREATE TABLE statements which
+ request X type of metadata lock. This is because under LOCK TABLES
+ such statements don't create the table but only check if it exists
+ or, in most complex case, only insert into it.
+ Thus SNRW lock should be enough.
+
+ Note that find_table_for_mdl_upgrade() will report an error if
+ no suitable ticket is found.
+ */
+ if (!find_table_for_mdl_upgrade(thd, table->db, table->table_name, false))
+ return TRUE;
+ }
+ }
+
+ return FALSE;
}
-/*
+/**
Open all tables in list
- SYNOPSIS
- open_tables()
- thd - thread handler
- start - list of tables in/out
- counter - number of opened tables will be return using this parameter
- flags - bitmap of flags to modify how the tables will be open:
- MYSQL_LOCK_IGNORE_FLUSH - open table even if someone has
- done a flush on it.
+ @param[in] thd Thread context.
+ @param[in,out] start List of tables to be open (it can be adjusted for
+ statement that uses tables only implicitly, e.g.
+ for "SELECT f1()").
+ @param[out] counter Number of tables which were open.
+ @param[in] flags Bitmap of flags to modify how the tables will be
+ open, see open_table() description for details.
+ @param[in] prelocking_strategy Strategy which specifies how prelocking
+ algorithm should work for this statement.
- NOTE
- Unless we are already in prelocked mode, this function will also precache
- all SP/SFs explicitly or implicitly (via views and triggers) used by the
- query and add tables needed for their execution to table list. If resulting
- tables list will be non empty it will mark query as requiring precaching.
+ @note
+ Unless we are already in prelocked mode and prelocking strategy prescribes
+ so this function will also precache all SP/SFs explicitly or implicitly
+ (via views and triggers) used by the query and add tables needed for their
+ execution to table list. Statement that uses SFs, invokes triggers or
+ requires foreign key checks will be marked as requiring prelocking.
Prelocked mode will be enabled for such query during lock_tables() call.
If query for which we are opening tables is already marked as requiring
prelocking it won't do such precaching and will simply reuse table list
which is already built.
- If any table has a trigger and start->trg_event_map is non-zero
- the final lock will end up in thd->locked_tables, otherwise, the
- lock will be placed in thd->lock. See also comments in
- st_lex::set_trg_event_type_for_tables().
-
- RETURN
- 0 - OK
- -1 - error
+ @retval FALSE Success.
+ @retval TRUE Error, reported.
*/
-int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
+bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags,
+ Prelocking_strategy *prelocking_strategy)
{
- TABLE_LIST *tables= NULL;
- bool refresh;
- int result=0;
+ /*
+ We use pointers to "next_global" member in the last processed TABLE_LIST
+ element and to the "next" member in the last processed Sroutine_hash_entry
+ element as iterators over, correspondingly, the table list and stored routines
+ list which stay valid and allow to continue iteration when new elements are
+ added to the tail of the lists.
+ */
+ TABLE_LIST **table_to_open;
+ Sroutine_hash_entry **sroutine_to_open;
+ TABLE_LIST *tables;
+ Open_table_context ot_ctx(thd, flags);
+ bool error= FALSE;
MEM_ROOT new_frm_mem;
- /* Also used for indicating that prelocking is need */
- TABLE_LIST **query_tables_last_own;
- bool safe_to_ignore_table;
+ bool has_prelocking_list;
DBUG_ENTER("open_tables");
+ /* Accessing data in XA_IDLE or XA_PREPARED is not allowed. */
+ enum xa_states xa_state= thd->transaction.xid_state.xa_state;
+ if (*start && (xa_state == XA_IDLE || xa_state == XA_PREPARED))
+ {
+ my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
+ DBUG_RETURN(true);
+ }
+
/*
- temporary mem_root for new .frm parsing.
- TODO: variables for size
+ Initialize temporary MEM_ROOT for new .FRM parsing. Do not allocate
+ anything yet, to avoid penalty for statements which don't use views
+ and thus new .FRM format.
*/
init_sql_alloc(&new_frm_mem, 8024, 0);
thd->current_tablenr= 0;
- restart:
+restart:
+ /*
+ Close HANDLER tables which are marked for flush or against which there
+ are pending exclusive metadata locks. This is needed both in order to
+ avoid deadlocks and to have a point during statement execution at
+ which such HANDLERs are closed even if they don't create problems for
+ the current session (i.e. to avoid having a DDL blocked by HANDLERs
+ opened for a long time).
+ */
+ if (thd->handler_tables_hash.records)
+ mysql_ha_flush(thd);
+
+ has_prelocking_list= thd->lex->requires_prelocking();
+ table_to_open= start;
+ sroutine_to_open= (Sroutine_hash_entry**) &thd->lex->sroutines_list.first;
*counter= 0;
- query_tables_last_own= 0;
thd_proc_info(thd, "Opening tables");
/*
- If we are not already executing prelocked statement and don't have
- statement for which table list for prelocking is already built, let
- us cache routines and try to build such table list.
-
+ If we are executing LOCK TABLES statement or a DDL statement
+ (in non-LOCK TABLES mode) we might have to acquire upgradable
+ semi-exclusive metadata locks (SNW or SNRW) on some of the
+ tables to be opened.
+ When executing CREATE TABLE .. If NOT EXISTS .. SELECT, the
+ table may not yet exist, in which case we acquire an exclusive
+ lock.
+ We acquire all such locks at once here as doing this in one
+ by one fashion may lead to deadlocks or starvation. Later when
+ we will be opening corresponding table pre-acquired metadata
+ lock will be reused (thanks to the fact that in recursive case
+ metadata locks are acquired without waiting).
*/
-
- if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
- thd->lex->uses_stored_routines())
+ if (! (flags & (MYSQL_OPEN_HAS_MDL_LOCK |
+ MYSQL_OPEN_FORCE_SHARED_MDL |
+ MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL)))
{
- bool first_no_prelocking, need_prelocking;
- TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last;
-
- DBUG_ASSERT(thd->lex->query_tables == *start);
- sp_get_prelocking_info(thd, &need_prelocking, &first_no_prelocking);
-
- if (sp_cache_routines_and_add_tables(thd, thd->lex, first_no_prelocking))
+ if (thd->locked_tables_mode)
{
/*
- Serious error during reading stored routines from mysql.proc table.
- Something's wrong with the table or its contents, and an error has
- been emitted; we must abort.
+ Under LOCK TABLES, we can't acquire new locks, so we instead
+ need to check if appropriate locks were pre-acquired.
*/
- result= -1;
- goto err;
+ if (open_tables_check_upgradable_mdl(thd, *start,
+ thd->lex->first_not_own_table(),
+ flags))
+ {
+ error= TRUE;
+ goto err;
+ }
}
- else if (need_prelocking)
+ else
{
- query_tables_last_own= save_query_tables_last;
- *start= thd->lex->query_tables;
+ TABLE_LIST *table;
+ if (lock_table_names(thd, *start, thd->lex->first_not_own_table(),
+ ot_ctx.get_timeout(), flags))
+ {
+ error= TRUE;
+ goto err;
+ }
+ for (table= *start; table && table != thd->lex->first_not_own_table();
+ table= table->next_global)
+ {
+ if (table->mdl_request.type >= MDL_SHARED_NO_WRITE)
+ table->mdl_request.ticket= NULL;
+ }
}
}
/*
- For every table in the list of tables to open, try to find or open
- a table.
+ Perform steps of prelocking algorithm until there are unprocessed
+ elements in prelocking list/set.
*/
- for (tables= *start; tables ;tables= tables->next_global)
+ while (*table_to_open ||
+ (thd->locked_tables_mode <= LTM_LOCK_TABLES &&
+ *sroutine_to_open))
{
- safe_to_ignore_table= FALSE;
-
/*
- Ignore placeholders for derived tables. After derived tables
- processing, link to created temporary table will be put here.
- If this is derived table for view then we still want to process
- routines used by this view.
- */
- if (tables->derived)
- {
- if (tables->view)
- goto process_view_routines;
- DBUG_PRINT("tcache", ("ignoring placeholder for derived table"));
- continue;
- }
- DBUG_PRINT("tcache", ("opening table: '%s'.'%s' item: 0x%lx",
- tables->db, tables->table_name, (long) tables));
- /*
- If this TABLE_LIST object is a placeholder for an information_schema
- table, create a temporary table to represent the information_schema
- table in the query. Do not fill it yet - will be filled during
- execution.
+ For every table in the list of tables to open, try to find or open
+ a table.
*/
- if (tables->schema_table)
+ for (tables= *table_to_open; tables;
+ table_to_open= &tables->next_global, tables= tables->next_global)
{
- /*
- If this information_schema table is merged into a mergeable
- view, ignore it for now -- it will be filled when its respective
- TABLE_LIST is processed. This code works only during re-execution.
- */
- if (tables->view)
- goto process_view_routines;
- if (!mysql_schema_table(thd, thd->lex, tables) &&
- !check_and_update_table_version(thd, tables, tables->table->s))
- {
- continue;
- }
- DBUG_RETURN(-1);
- }
- (*counter)++;
+ error= open_and_process_table(thd, thd->lex, tables, counter,
+ flags, prelocking_strategy,
+ has_prelocking_list, &ot_ctx,
+ &new_frm_mem);
- /*
- Not a placeholder: must be a base table or a view, and the table is
- not opened yet. Try to open the table.
- */
- if (!tables->table)
- {
- if (tables->prelocking_placeholder)
+ if (error)
{
- /*
- For the tables added by the pre-locking code, attempt to open
- the table but fail silently if the table does not exist.
- The real failure will occur when/if a statement attempts to use
- that table.
- */
- Prelock_error_handler prelock_handler;
- thd->push_internal_handler(& prelock_handler);
- tables->table= open_table(thd, tables, &new_frm_mem, &refresh, flags);
- thd->pop_internal_handler();
- safe_to_ignore_table= prelock_handler.safely_trapped_errors();
- }
- else
- {
- tables->table= open_table(thd, tables, &new_frm_mem, &refresh, flags);
-
- /*
- Skip further processing if there has been a fatal error while
- trying to open a table. For example, this might happen due to
- stack shortage, unknown definer in views, etc.
- */
- if (!tables->table && thd->is_error())
+ if (ot_ctx.can_recover_from_failed_open())
{
- result= -1;
- goto err;
+ /*
+ We have met exclusive metadata lock or old version of table.
+ Now we have to close all tables and release metadata locks.
+ We also have to throw away set of prelocked tables (and thus
+ close tables from this set that were open by now) since it
+ is possible that one of tables which determined its content
+ was changed.
+
+ Instead of implementing complex/non-robust logic mentioned
+ above we simply close and then reopen all tables.
+
+ We have to save pointer to table list element for table which we
+ have failed to open since closing tables can trigger removal of
+ elements from the table list (if MERGE tables are involved),
+ */
+ close_tables_for_reopen(thd, start, ot_ctx.start_of_statement_svp());
+
+ /*
+ Here we rely on the fact that 'tables' still points to the valid
+ TABLE_LIST element. Altough currently this assumption is valid
+ it may change in future.
+ */
+ if (ot_ctx.recover_from_failed_open())
+ goto err;
+
+ error= FALSE;
+ goto restart;
}
+ goto err;
}
+
+ DEBUG_SYNC(thd, "open_tables_after_open_and_process_table");
}
- else
- DBUG_PRINT("tcache", ("referenced table: '%s'.'%s' 0x%lx",
- tables->db, tables->table_name,
- (long) tables->table));
- if (!tables->table)
+ /*
+ If we are not already in prelocked mode and extended table list is
+ not yet built for our statement we need to cache routines it uses
+ and build the prelocking list for it.
+ If we are not in prelocked mode but have built the extended table
+ list, we still need to call open_and_process_routine() to take
+ MDL locks on the routines.
+ */
+ if (thd->locked_tables_mode <= LTM_LOCK_TABLES)
{
- free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC));
-
- if (tables->view)
- {
- /* VIEW placeholder */
- (*counter)--;
-
- /*
- tables->next_global list consists of two parts:
- 1) Query tables and underlying tables of views.
- 2) Tables used by all stored routines that this statement invokes on
- execution.
- We need to know where the bound between these two parts is. If we've
- just opened a view, which was the last table in part #1, and it
- has added its base tables after itself, adjust the boundary pointer
- accordingly.
- */
- if (query_tables_last_own == &(tables->next_global) &&
- tables->view->query_tables)
- query_tables_last_own= tables->view->query_tables_last;
- /*
- Let us free memory used by 'sroutines' hash here since we never
- call destructor for this LEX.
- */
- hash_free(&tables->view->sroutines);
- goto process_view_routines;
- }
-
/*
- If in a MERGE table open, we need to remove the children list
- from statement table list before restarting. Otherwise the list
- will be inserted another time.
+ Process elements of the prelocking set which are present there
+ since parsing stage or were added to it by invocations of
+ Prelocking_strategy methods in the above loop over tables.
+
+ For example, if element is a routine, cache it and then,
+ if prelocking strategy prescribes so, add tables it uses to the
+ table list and routines it might invoke to the prelocking set.
*/
- if (tables->parent_l)
+ for (Sroutine_hash_entry *rt= *sroutine_to_open; rt;
+ sroutine_to_open= &rt->next, rt= rt->next)
{
- TABLE_LIST *parent_l= tables->parent_l;
- /* The parent table should be correctly open at this point. */
- DBUG_ASSERT(parent_l->table);
- parent_l->next_global= *parent_l->table->child_last_l;
- }
+ bool need_prelocking= false;
+ TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last;
- if (refresh) // Refresh in progress
- {
- /*
- We have met name-locked or old version of table. Now we have
- to close all tables which are not up to date. We also have to
- throw away set of prelocked tables (and thus close tables from
- this set that were open by now) since it possible that one of
- tables which determined its content was changed.
-
- Instead of implementing complex/non-robust logic mentioned
- above we simply close and then reopen all tables.
-
- In order to prepare for recalculation of set of prelocked tables
- we pretend that we have finished calculation which we were doing
- currently.
- */
- if (query_tables_last_own)
- thd->lex->mark_as_requiring_prelocking(query_tables_last_own);
- close_tables_for_reopen(thd, start);
- goto restart;
- }
+ error= open_and_process_routine(thd, thd->lex, rt, prelocking_strategy,
+ has_prelocking_list, &ot_ctx,
+ &need_prelocking);
- if (safe_to_ignore_table)
- {
- DBUG_PRINT("info", ("open_table: ignoring table '%s'.'%s'",
- tables->db, tables->alias));
- continue;
- }
+ if (need_prelocking && ! thd->lex->requires_prelocking())
+ thd->lex->mark_as_requiring_prelocking(save_query_tables_last);
- result= -1; // Fatal error
- break;
- }
- else
- {
- /*
- If we are not already in prelocked mode and extended table list is not
- yet built and we have trigger for table being opened then we should
- cache all routines used by its triggers and add their tables to
- prelocking list.
- If we lock table for reading we won't update it so there is no need to
- process its triggers since they never will be activated.
- */
- if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
- tables->trg_event_map && tables->table->triggers &&
- tables->lock_type >= TL_WRITE_ALLOW_WRITE)
- {
- if (!query_tables_last_own)
- query_tables_last_own= thd->lex->query_tables_last;
- if (sp_cache_routines_and_add_tables_for_triggers(thd, thd->lex,
- tables))
+ if (need_prelocking && ! *start)
+ *start= thd->lex->query_tables;
+
+ if (error)
{
+ if (ot_ctx.can_recover_from_failed_open())
+ {
+ close_tables_for_reopen(thd, start,
+ ot_ctx.start_of_statement_svp());
+ if (ot_ctx.recover_from_failed_open())
+ goto err;
+
+ error= FALSE;
+ goto restart;
+ }
/*
Serious error during reading stored routines from mysql.proc table.
- Something's wrong with the table or its contents, and an error has
+ Something is wrong with the table or its contents, and an error has
been emitted; we must abort.
*/
- result= -1;
goto err;
}
}
- free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC));
}
+ }
- if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables)
- {
- if (tables->lock_type == TL_WRITE_DEFAULT)
- tables->table->reginfo.lock_type= thd->update_lock_default;
- else if (tables->lock_type == TL_READ_DEFAULT)
- tables->table->reginfo.lock_type=
- read_lock_type_for_table(thd, thd->lex, tables);
- else
- tables->table->reginfo.lock_type= tables->lock_type;
- }
- tables->table->grant= tables->grant;
+ /*
+ After successful open of all tables, including MERGE parents and
+ children, attach the children to their parents. At end of statement,
+ the children are detached. Attaching and detaching are always done,
+ even under LOCK TABLES.
+ */
+ for (tables= *start; tables; tables= tables->next_global)
+ {
+ TABLE *tbl= tables->table;
- /* Check and update metadata version of a base table. */
- if (check_and_update_table_version(thd, tables, tables->table->s))
+ /* Schema tables may not have a TABLE object here. */
+ if (tbl && tbl->file->ht->db_type == DB_TYPE_MRG_MYISAM)
{
- result= -1;
- goto err;
- }
-
- /* Attach MERGE children if not locked already. */
- DBUG_PRINT("tcache", ("is parent: %d is child: %d",
- test(tables->table->child_l),
- test(tables->parent_l)));
- DBUG_PRINT("tcache", ("in lock tables: %d in prelock mode: %d",
- test(thd->locked_tables), test(thd->prelocked_mode)));
- if (((!thd->locked_tables && !thd->prelocked_mode) ||
- tables->table->s->tmp_table) &&
- ((tables->table->child_l &&
- add_merge_table_list(tables)) ||
- (tables->parent_l &&
- (&tables->next_global == tables->parent_l->table->child_last_l) &&
- attach_merge_children(tables))))
- {
- result= -1;
- goto err;
- }
-
-process_view_routines:
- /*
- Again we may need cache all routines used by this view and add
- tables used by them to table list.
- */
- if (tables->view && !thd->prelocked_mode &&
- !thd->lex->requires_prelocking() &&
- tables->view->uses_stored_routines())
- {
- /* We have at least one table in TL here. */
- if (!query_tables_last_own)
- query_tables_last_own= thd->lex->query_tables_last;
- if (sp_cache_routines_and_add_tables_for_view(thd, thd->lex, tables))
+ /* MERGE tables need to access parent and child TABLE_LISTs. */
+ DBUG_ASSERT(tbl->pos_in_table_list == tables);
+ if (tbl->file->extra(HA_EXTRA_ATTACH_CHILDREN))
{
- /*
- Serious error during reading stored routines from mysql.proc table.
- Something is wrong with the table or its contents, and an error has
- been emitted; we must abort.
- */
- result= -1;
+ error= TRUE;
goto err;
}
}
}
- err:
- thd_proc_info(thd, 0);
+err:
+ thd_proc_info(thd, "After opening tables");
free_root(&new_frm_mem, MYF(0)); // Free pre-alloced block
- if (query_tables_last_own)
- thd->lex->mark_as_requiring_prelocking(query_tables_last_own);
+ if (error && *table_to_open)
+ {
+ (*table_to_open)->table= NULL;
+ }
+ DBUG_PRINT("open_tables", ("returning: %d", (int) error));
+ DBUG_RETURN(error);
+}
+
+
+/**
+ Defines how prelocking algorithm for DML statements should handle routines:
+ - For CALL statements we do unrolling (i.e. open and lock tables for each
+ sub-statement individually). So for such statements prelocking is enabled
+ only if stored functions are used in parameter list and only for period
+ during which we calculate values of parameters. Thus in this strategy we
+ ignore procedure which is directly called by such statement and extend
+ the prelocking set only with tables/functions used by SF called from the
+ parameter list.
+ - For any other statement any routine which is directly or indirectly called
+ by statement is going to be executed in prelocked mode. So in this case we
+ simply add all tables and routines used by it to the prelocking set.
+
+ @param[in] thd Thread context.
+ @param[in] prelocking_ctx Prelocking context of the statement.
+ @param[in] rt Prelocking set element describing routine.
+ @param[in] sp Routine body.
+ @param[out] need_prelocking Set to TRUE if method detects that prelocking
+ required, not changed otherwise.
+
+ @retval FALSE Success.
+ @retval TRUE Failure (OOM).
+*/
+
+bool DML_prelocking_strategy::
+handle_routine(THD *thd, Query_tables_list *prelocking_ctx,
+ Sroutine_hash_entry *rt, sp_head *sp, bool *need_prelocking)
+{
+ /*
+ We assume that for any "CALL proc(...)" statement sroutines_list will
+ have 'proc' as first element (it may have several, consider e.g.
+ "proc(sp_func(...)))". This property is currently guaranted by the
+ parser.
+ */
- if (result && tables)
+ if (rt != (Sroutine_hash_entry*)prelocking_ctx->sroutines_list.first ||
+ rt->mdl_request.key.mdl_namespace() != MDL_key::PROCEDURE)
{
- /*
- Some functions determine success as (tables->table != NULL).
- tables->table is in thd->open_tables. It won't go lost. If the
- error happens on a MERGE child, clear the parents TABLE reference.
- */
- if (tables->parent_l)
+ *need_prelocking= TRUE;
+ sp_update_stmt_used_routines(thd, prelocking_ctx, &sp->m_sroutines,
+ rt->belong_to_view);
+ (void)sp->add_used_tables_to_table_list(thd,
+ &prelocking_ctx->query_tables_last,
+ rt->belong_to_view);
+ }
+ sp->propagate_attributes(prelocking_ctx);
+ return FALSE;
+}
+
+
+/**
+ Defines how prelocking algorithm for DML statements should handle table list
+ elements:
+ - If table has triggers we should add all tables and routines
+ used by them to the prelocking set.
+
+ We do not need to acquire metadata locks on trigger names
+ in DML statements, since all DDL statements
+ that change trigger metadata always lock their
+ subject tables.
+
+ @param[in] thd Thread context.
+ @param[in] prelocking_ctx Prelocking context of the statement.
+ @param[in] table_list Table list element for table.
+ @param[in] sp Routine body.
+ @param[out] need_prelocking Set to TRUE if method detects that prelocking
+ required, not changed otherwise.
+
+ @retval FALSE Success.
+ @retval TRUE Failure (OOM).
+*/
+
+bool DML_prelocking_strategy::
+handle_table(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list, bool *need_prelocking)
+{
+ /* We rely on a caller to check that table is going to be changed. */
+ DBUG_ASSERT(table_list->lock_type >= TL_WRITE_ALLOW_WRITE);
+
+ if (table_list->trg_event_map)
+ {
+ if (table_list->table->triggers)
{
- if (tables->parent_l->next_global == tables->parent_l->table->child_l)
- tables->parent_l->next_global= *tables->parent_l->table->child_last_l;
- tables->parent_l->table= NULL;
+ *need_prelocking= TRUE;
+
+ if (table_list->table->triggers->
+ add_tables_and_routines_for_triggers(thd, prelocking_ctx, table_list))
+ return TRUE;
}
- tables->table= NULL;
}
- DBUG_PRINT("tcache", ("returning: %d", result));
- DBUG_RETURN(result);
+
+ return FALSE;
}
-/*
+/**
+ Defines how prelocking algorithm for DML statements should handle view -
+ all view routines should be added to the prelocking set.
+
+ @param[in] thd Thread context.
+ @param[in] prelocking_ctx Prelocking context of the statement.
+ @param[in] table_list Table list element for view.
+ @param[in] sp Routine body.
+ @param[out] need_prelocking Set to TRUE if method detects that prelocking
+ required, not changed otherwise.
+
+ @retval FALSE Success.
+ @retval TRUE Failure (OOM).
+*/
+
+bool DML_prelocking_strategy::
+handle_view(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list, bool *need_prelocking)
+{
+ if (table_list->view->uses_stored_routines())
+ {
+ *need_prelocking= TRUE;
+
+ sp_update_stmt_used_routines(thd, prelocking_ctx,
+ &table_list->view->sroutines_list,
+ table_list->top_table());
+ }
+ return FALSE;
+}
+
+
+/**
+ Defines how prelocking algorithm for LOCK TABLES statement should handle
+ table list elements.
+
+ @param[in] thd Thread context.
+ @param[in] prelocking_ctx Prelocking context of the statement.
+ @param[in] table_list Table list element for table.
+ @param[in] sp Routine body.
+ @param[out] need_prelocking Set to TRUE if method detects that prelocking
+ required, not changed otherwise.
+
+ @retval FALSE Success.
+ @retval TRUE Failure (OOM).
+*/
+
+bool Lock_tables_prelocking_strategy::
+handle_table(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list, bool *need_prelocking)
+{
+ if (DML_prelocking_strategy::handle_table(thd, prelocking_ctx, table_list,
+ need_prelocking))
+ return TRUE;
+
+ /* We rely on a caller to check that table is going to be changed. */
+ DBUG_ASSERT(table_list->lock_type >= TL_WRITE_ALLOW_WRITE);
+
+ return FALSE;
+}
+
+
+/**
+ Defines how prelocking algorithm for ALTER TABLE statement should handle
+ routines - do nothing as this statement is not supposed to call routines.
+
+ We still can end up in this method when someone tries
+ to define a foreign key referencing a view, and not just
+ a simple view, but one that uses stored routines.
+*/
+
+bool Alter_table_prelocking_strategy::
+handle_routine(THD *thd, Query_tables_list *prelocking_ctx,
+ Sroutine_hash_entry *rt, sp_head *sp, bool *need_prelocking)
+{
+ return FALSE;
+}
+
+
+/**
+ Defines how prelocking algorithm for ALTER TABLE statement should handle
+ table list elements.
+
+ Unlike in DML, we do not process triggers here.
+
+ @param[in] thd Thread context.
+ @param[in] prelocking_ctx Prelocking context of the statement.
+ @param[in] table_list Table list element for table.
+ @param[in] sp Routine body.
+ @param[out] need_prelocking Set to TRUE if method detects that prelocking
+ required, not changed otherwise.
+
+
+ @retval FALSE Success.
+ @retval TRUE Failure (OOM).
+*/
+
+bool Alter_table_prelocking_strategy::
+handle_table(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list, bool *need_prelocking)
+{
+ return FALSE;
+}
+
+
+/**
+ Defines how prelocking algorithm for ALTER TABLE statement
+ should handle view - do nothing. We don't need to add view
+ routines to the prelocking set in this case as view is not going
+ to be materialized.
+*/
+
+bool Alter_table_prelocking_strategy::
+handle_view(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list, bool *need_prelocking)
+{
+ return FALSE;
+}
+
+
+/**
Check that lock is ok for tables; Call start stmt if ok
- SYNOPSIS
- check_lock_and_start_stmt()
- thd Thread handle
- table_list Table to check
- lock_type Lock used for table
+ @param thd Thread handle.
+ @param prelocking_ctx Prelocking context.
+ @param table_list Table list element for table to be checked.
- RETURN VALUES
- 0 ok
- 1 error
+ @retval FALSE - Ok.
+ @retval TRUE - Error.
*/
-static bool check_lock_and_start_stmt(THD *thd, TABLE *table,
- thr_lock_type lock_type)
+static bool check_lock_and_start_stmt(THD *thd,
+ Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list)
{
int error;
+ thr_lock_type lock_type;
DBUG_ENTER("check_lock_and_start_stmt");
- if ((int) lock_type >= (int) TL_WRITE_ALLOW_READ &&
- (int) table->reginfo.lock_type < (int) TL_WRITE_ALLOW_READ)
+ /*
+ Prelocking placeholder is not set for TABLE_LIST that
+ are directly used by TOP level statement.
+ */
+ DBUG_ASSERT(table_list->prelocking_placeholder == false);
+
+ /*
+ TL_WRITE_DEFAULT and TL_READ_DEFAULT are supposed to be parser only
+ types of locks so they should be converted to appropriate other types
+ to be passed to storage engine. The exact lock type passed to the
+ engine is important as, for example, InnoDB uses it to determine
+ what kind of row locks should be acquired when executing statement
+ in prelocked mode or under LOCK TABLES with @@innodb_table_locks = 0.
+ */
+ if (table_list->lock_type == TL_WRITE_DEFAULT)
+ lock_type= thd->update_lock_default;
+ else if (table_list->lock_type == TL_READ_DEFAULT)
+ lock_type= read_lock_type_for_table(thd, prelocking_ctx, table_list);
+ 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)
{
- my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0),table->alias.c_ptr());
+ my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0),
+ table_list->table->alias.c_ptr());
DBUG_RETURN(1);
}
- if ((error=table->file->start_stmt(thd, lock_type)))
+ if ((error= table_list->table->file->start_stmt(thd, lock_type)))
{
- table->file->print_error(error,MYF(0));
+ table_list->table->file->print_error(error, MYF(0));
DBUG_RETURN(1);
}
DBUG_RETURN(0);
@@ -5090,6 +5460,10 @@ static bool check_lock_and_start_stmt(THD *thd, TABLE *table,
@param[in] thd thread handle
@param[in] table_l table to open is first table in this list
@param[in] lock_type lock to use for table
+ @param[in] flags options to be used while opening and locking
+ table (see open_table(), mysql_lock_tables())
+ @param[in] prelocking_strategy Strategy which specifies how prelocking
+ algorithm should work for this statement.
@return table
@retval != NULL OK, opened table returned
@@ -5110,12 +5484,13 @@ static bool check_lock_and_start_stmt(THD *thd, TABLE *table,
There may be more differences between open_n_lock_single_table() and
open_ltable(). One known difference is that open_ltable() does
- neither call decide_logging_format() nor handle some other logging
+ neither call thd->decide_logging_format() nor handle some other logging
and locking issues because it does not call lock_tables().
*/
TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l,
- thr_lock_type lock_type)
+ thr_lock_type lock_type, uint flags,
+ Prelocking_strategy *prelocking_strategy)
{
TABLE_LIST *save_next_global;
DBUG_ENTER("open_n_lock_single_table");
@@ -5131,7 +5506,8 @@ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l,
table_l->required_type= FRMTYPE_TABLE;
/* Open the table. */
- if (simple_open_n_lock_tables(thd, table_l))
+ if (open_and_lock_tables(thd, table_l, FALSE, flags,
+ prelocking_strategy))
table_l->table= NULL; /* Just to be sure. */
/* Restore list. */
@@ -5169,23 +5545,44 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
uint lock_flags)
{
TABLE *table;
- bool refresh;
+ Open_table_context ot_ctx(thd, lock_flags);
+ bool error;
DBUG_ENTER("open_ltable");
/* should not be used in a prelocked_mode context, see NOTE above */
- DBUG_ASSERT(!thd->prelocked_mode);
+ DBUG_ASSERT(thd->locked_tables_mode < LTM_PRELOCKED);
thd_proc_info(thd, "Opening table");
thd->current_tablenr= 0;
/* open_ltable can be used only for BASIC TABLEs */
table_list->required_type= FRMTYPE_TABLE;
- while (!(table= open_table(thd, table_list, thd->mem_root, &refresh, 0)) &&
- refresh)
- ;
- if (table)
+ /* This function can't properly handle requests for such metadata locks. */
+ DBUG_ASSERT(table_list->mdl_request.type < MDL_SHARED_NO_WRITE);
+
+ while ((error= open_table(thd, table_list, thd->mem_root, &ot_ctx)) &&
+ ot_ctx.can_recover_from_failed_open())
+ {
+ /*
+ Even though we have failed to open table we still need to
+ call release_transactional_locks() to release metadata locks which
+ might have been acquired successfully.
+ */
+ thd->mdl_context.rollback_to_savepoint(ot_ctx.start_of_statement_svp());
+ table_list->mdl_request.ticket= 0;
+ if (ot_ctx.recover_from_failed_open())
+ break;
+ }
+
+ if (!error)
{
- if (table->child_l)
+ /*
+ We can't have a view or some special "open_strategy" in this function
+ so there should be a TABLE instance.
+ */
+ DBUG_ASSERT(table_list->table);
+ table= table_list->table;
+ if (table->file->ht->db_type == DB_TYPE_MRG_MYISAM)
{
/* A MERGE table must not come here. */
/* purecov: begin tested */
@@ -5197,11 +5594,10 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
}
table_list->lock_type= lock_type;
- table_list->table= table;
table->grant= table_list->grant;
- if (thd->locked_tables)
+ if (thd->locked_tables_mode)
{
- if (check_lock_and_start_stmt(thd, table, lock_type))
+ if (check_lock_and_start_stmt(thd, thd->lex, table_list))
table= 0;
}
else
@@ -5209,72 +5605,85 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
DBUG_ASSERT(thd->lock == 0); // You must lock everything at once
if ((table->reginfo.lock_type= lock_type) != TL_UNLOCK)
if (! (thd->lock= mysql_lock_tables(thd, &table_list->table, 1,
- lock_flags, &refresh)))
- table= 0;
+ lock_flags)))
+ {
+ table= 0;
+ }
}
}
+ else
+ table= 0;
- end:
- thd_proc_info(thd, 0);
+end:
+ if (table == NULL)
+ {
+ if (!thd->in_sub_stmt)
+ trans_rollback_stmt(thd);
+ close_thread_tables(thd);
+ }
+ thd_proc_info(thd, "After opening table");
DBUG_RETURN(table);
}
-/*
+/**
Open all tables in list, locks them and optionally process derived tables.
- SYNOPSIS
- open_and_lock_tables_derived()
- thd - thread handler
- tables - list of tables for open&locking
- derived - if to handle derived tables
-
- RETURN
- FALSE - ok
- TRUE - error
+ @param thd Thread context.
+ @param tables List of tables for open and locking.
+ @param derived If to handle derived tables.
+ @param flags Bitmap of options to be used to open and lock
+ tables (see open_tables() and mysql_lock_tables()
+ for details).
+ @param prelocking_strategy Strategy which specifies how prelocking algorithm
+ should work for this statement.
- NOTE
- The lock will automaticaly be freed by close_thread_tables()
+ @note
+ The thr_lock locks will automatically be freed by
+ close_thread_tables().
- NOTE
- There are two convenience functions:
- - simple_open_n_lock_tables(thd, tables) without derived handling
- - open_and_lock_tables(thd, tables) with derived handling
- Both inline functions call open_and_lock_tables_derived() with
- the third argument set appropriately.
+ @retval FALSE OK.
+ @retval TRUE Error
*/
-int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived)
+bool open_and_lock_tables(THD *thd, TABLE_LIST *tables,
+ bool derived, uint flags,
+ Prelocking_strategy *prelocking_strategy)
{
uint counter;
- bool need_reopen;
- DBUG_ENTER("open_and_lock_tables_derived");
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
+ DBUG_ENTER("open_and_lock_tables");
DBUG_PRINT("enter", ("derived handling: %d", derived));
- for ( ; ; )
- {
- if (open_tables(thd, &tables, &counter, 0))
- DBUG_RETURN(-1);
+ if (open_tables(thd, &tables, &counter, flags, prelocking_strategy))
+ goto err;
- DBUG_EXECUTE_IF("sleep_open_and_lock_after_open", {
- const char *old_proc_info= thd->proc_info;
- thd->proc_info= "DBUG sleep";
- my_sleep(6000000);
- thd->proc_info= old_proc_info;});
+ DBUG_EXECUTE_IF("sleep_open_and_lock_after_open", {
+ const char *old_proc_info= thd->proc_info;
+ thd->proc_info= "DBUG sleep";
+ my_sleep(6000000);
+ thd->proc_info= old_proc_info;});
- if (!lock_tables(thd, tables, counter, &need_reopen))
- break;
- if (!need_reopen)
- DBUG_RETURN(-1);
- close_tables_for_reopen(thd, &tables);
- }
- if (derived &&
- (mysql_handle_derived(thd->lex, DT_INIT)))
- DBUG_RETURN(TRUE); /* purecov: inspected */
- if (thd->prepare_derived_at_open && derived &&
- (mysql_handle_derived(thd->lex, DT_PREPARE)))
- DBUG_RETURN(TRUE); /* purecov: inspected */
- DBUG_RETURN(0);
+ if (lock_tables(thd, tables, counter, flags))
+ goto err;
+
+ if (derived)
+ {
+ if (mysql_handle_derived(thd->lex, DT_INIT))
+ goto err;
+ if (thd->prepare_derived_at_open &&
+ (mysql_handle_derived(thd->lex, DT_PREPARE)))
+ goto err;
+ }
+
+ DBUG_RETURN(FALSE);
+err:
+ if (! thd->in_sub_stmt)
+ trans_rollback_stmt(thd); /* Necessary if derived handling failed. */
+ close_thread_tables(thd);
+ /* Don't keep locks for a failed statement. */
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+ DBUG_RETURN(TRUE);
}
@@ -5302,13 +5711,30 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived)
bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags,
uint dt_phases)
{
+ DML_prelocking_strategy prelocking_strategy;
uint counter;
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
DBUG_ENTER("open_normal_and_derived_tables");
DBUG_ASSERT(!thd->fill_derived_tables());
- if (open_tables(thd, &tables, &counter, flags) ||
+ if (open_tables(thd, &tables, &counter, flags, &prelocking_strategy) ||
mysql_handle_derived(thd->lex, dt_phases))
- DBUG_RETURN(TRUE); /* purecov: inspected */
+ goto end;
+
DBUG_RETURN(0);
+end:
+ /*
+ No need to commit/rollback the statement transaction: it's
+ either not started or we're filling in an INFORMATION_SCHEMA
+ table on the fly, and thus mustn't manipulate with the
+ transaction of the enclosing statement.
+ */
+ DBUG_ASSERT(thd->transaction.stmt.is_empty() ||
+ (thd->state_flags & Open_tables_state::BACKUPS_AVAIL));
+ close_thread_tables(thd);
+ /* Don't keep locks for a failed statement. */
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+
+ DBUG_RETURN(TRUE); /* purecov: inspected */
}
@@ -5325,236 +5751,50 @@ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags,
or schema tables) as free for reuse.
*/
-static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table)
+static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table_list)
{
- DBUG_ENTER("mark_real_tables_as_free_for_reuse");
- for (; table; table= table->next_global)
+ TABLE_LIST *table;
+ for (table= table_list; table; table= table->next_global)
if (!table->placeholder())
- table->table->query_id= 0;
- DBUG_VOID_RETURN;
-}
-
-
-/**
- Decide on logging format to use for the statement.
-
- Compute the capabilities vector for the involved storage engines
- and mask out the flags for the binary log. Right now, the binlog
- flags only include the capabilities of the storage engines, so this
- is safe.
-
- We now have three alternatives that prevent the statement from
- being loggable:
-
- 1. If there are no capabilities left (all flags are clear) it is
- not possible to log the statement at all, so we roll back the
- statement and report an error.
-
- 2. Statement mode is set, but the capabilities indicate that
- statement format is not possible.
-
- 3. Row mode is set, but the capabilities indicate that row
- format is not possible.
-
- 4. Statement is unsafe, but the capabilities indicate that row
- format is not possible.
-
- If we are in MIXED mode, we then decide what logging format to use:
-
- 1. If the statement is unsafe, row-based logging is used.
-
- 2. If statement-based logging is not possible, row-based logging is
- used.
-
- 3. Otherwise, statement-based logging is used.
-
- @param thd Client thread
- @param tables Tables involved in the query
- */
-
-int decide_logging_format(THD *thd, TABLE_LIST *tables)
-{
- /*
- In SBR mode, we are only proceeding if we are binlogging this
- statement, ie, the filtering rules won't later filter this out.
-
- This check here is needed to prevent some spurious error to be
- raised in some cases (See BUG#42829).
- */
- if (mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG) &&
- (thd->variables.binlog_format != BINLOG_FORMAT_STMT ||
- binlog_filter->db_ok(thd->db)))
- {
- /*
- Compute the starting vectors for the computations by creating a
- set with all the capabilities bits set and one with no
- capabilities bits set.
- */
- handler::Table_flags flags_write_some_set= 0;
- handler::Table_flags flags_access_some_set= 0;
- handler::Table_flags flags_write_all_set=
- HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE;
-
- /*
- If different types of engines are about to be updated.
- For example: Innodb and Falcon; Innodb and MyIsam.
- */
- my_bool multi_write_engine= FALSE;
- void* prev_write_ht= NULL;
-
- /*
- If different types of engines are about to be accessed
- and any of them is about to be updated. For example:
- Innodb and Falcon; Innodb and MyIsam.
- */
- my_bool multi_access_engine= FALSE;
- void* prev_access_ht= NULL;
- for (TABLE_LIST *table= tables; table; table= table->next_global)
- {
- if (table->placeholder())
- continue;
- if (table->table->s->table_category == TABLE_CATEGORY_PERFORMANCE)
- thd->lex->set_stmt_unsafe();
- ulonglong const flags= table->table->file->ha_table_flags();
- if (table->lock_type >= TL_WRITE_ALLOW_WRITE)
- {
- DBUG_PRINT("info", ("table: %s; ha_table_flags: %s%s",
- table->table_name,
- FLAGSTR(flags, HA_BINLOG_STMT_CAPABLE),
- FLAGSTR(flags, HA_BINLOG_ROW_CAPABLE)));
- if (prev_write_ht && prev_write_ht != table->table->file->ht)
- multi_write_engine= TRUE;
- prev_write_ht= table->table->file->ht;
- flags_write_all_set &= flags;
- flags_write_some_set |= flags;
- }
- if (prev_access_ht && prev_access_ht != table->table->file->ht)
- multi_access_engine= TRUE;
- prev_access_ht= table->table->file->ht;
- flags_access_some_set |= flags;
- }
-
- DBUG_PRINT("info", ("flags_write_all_set: %s%s",
- FLAGSTR(flags_write_all_set, HA_BINLOG_STMT_CAPABLE),
- FLAGSTR(flags_write_all_set, HA_BINLOG_ROW_CAPABLE)));
- DBUG_PRINT("info", ("flags_write_some_set: %s%s",
- FLAGSTR(flags_write_some_set, HA_BINLOG_STMT_CAPABLE),
- FLAGSTR(flags_write_some_set, HA_BINLOG_ROW_CAPABLE)));
- DBUG_PRINT("info", ("flags_access_some_set: %s%s",
- FLAGSTR(flags_access_some_set, HA_BINLOG_STMT_CAPABLE),
- FLAGSTR(flags_access_some_set, HA_BINLOG_ROW_CAPABLE)));
- DBUG_PRINT("info", ("multi_write_engine: %s",
- multi_write_engine ? "TRUE" : "FALSE"));
- DBUG_PRINT("info", ("multi_access_engine: %s",
- multi_access_engine ? "TRUE" : "FALSE"));
- DBUG_PRINT("info", ("thd->variables.binlog_format: %ld",
- thd->variables.binlog_format));
-
- int error= 0;
- if (flags_write_all_set == 0)
- {
- my_error((error= ER_BINLOG_LOGGING_IMPOSSIBLE), MYF(0),
- "Statement cannot be logged to the binary log in"
- " row-based nor statement-based format");
- }
- else if (thd->variables.binlog_format == BINLOG_FORMAT_STMT &&
- (flags_write_all_set & HA_BINLOG_STMT_CAPABLE) == 0)
- {
- my_error((error= ER_BINLOG_LOGGING_IMPOSSIBLE), MYF(0),
- "Statement-based format required for this statement,"
- " but not allowed by this combination of engines");
- }
- else if ((thd->variables.binlog_format == BINLOG_FORMAT_ROW ||
- thd->lex->is_stmt_unsafe()) &&
- (flags_write_all_set & HA_BINLOG_ROW_CAPABLE) == 0)
- {
- my_error((error= ER_BINLOG_LOGGING_IMPOSSIBLE), MYF(0),
- "Row-based format required for this statement,"
- " but not allowed by this combination of engines");
- }
-
- /*
- If more than one engine is involved in the statement and at
- least one is doing it's own logging (is *self-logging*), the
- statement cannot be logged atomically, so we generate an error
- rather than allowing the binlog to become corrupt.
- */
- if (multi_write_engine &&
- (flags_write_some_set & HA_HAS_OWN_BINLOGGING))
{
- error= ER_BINLOG_LOGGING_IMPOSSIBLE;
- my_error(error, MYF(0),
- "Statement cannot be written atomically since more"
- " than one engine involved and at least one engine"
- " is self-logging");
+ table->table->query_id= 0;
}
- /*
- Reading from a self-logging engine and updating another engine
- generates changes that are written to the binary log in the
- statement format and may make slaves to diverge. In the mixed
- mode, such changes should be written to the binary log in the
- row format.
- */
- else if (multi_access_engine &&
- (flags_access_some_set & HA_HAS_OWN_BINLOGGING))
- thd->lex->set_stmt_unsafe();
-
- DBUG_PRINT("info", ("error: %d", error));
-
- if (error)
- return -1;
-
- /*
- We switch to row-based format if we are in mixed mode and one of
- the following are true:
-
- 1. If the statement is unsafe
- 2. If statement format cannot be used
-
- Observe that point to cannot be decided before the tables
- involved in a statement has been checked, i.e., we cannot put
- this code in reset_current_stmt_binlog_row_based(), it has to be
- here.
- */
- if (thd->lex->is_stmt_unsafe() ||
- (flags_write_all_set & HA_BINLOG_STMT_CAPABLE) == 0)
+ for (table= table_list; table; table= table->next_global)
+ if (!table->placeholder())
{
- thd->set_current_stmt_binlog_row_based_if_mixed();
+ /*
+ Detach children of MyISAMMRG tables used in
+ sub-statements, they will be reattached at open.
+ This has to be done in a separate loop to make sure
+ that children have had their query_id cleared.
+ */
+ table->table->file->extra(HA_EXTRA_DETACH_CHILDREN);
}
- }
-
- return 0;
}
-/*
- Lock all tables in list
- SYNOPSIS
- lock_tables()
- thd Thread handler
- tables Tables to lock
- count Number of opened tables
- need_reopen Out parameter which if TRUE indicates that some
- tables were dropped or altered during this call
- and therefore invoker should reopen tables and
- try to lock them once again (in this case
- lock_tables() will also return error).
+/**
+ Lock all tables in a list.
- NOTES
- You can't call lock_tables twice, as this would break the dead-lock-free
- handling thr_lock gives us. You most always get all needed locks at
- once.
+ @param thd Thread handler
+ @param tables Tables to lock
+ @param count Number of opened tables
+ @param flags Options (see mysql_lock_tables() for details)
- If query for which we are calling this function marked as requring
- prelocking, this function will do implicit LOCK TABLES and change
- thd::prelocked_mode accordingly.
+ You can't call lock_tables() while holding thr_lock locks, as
+ this would break the dead-lock-free handling thr_lock gives us.
+ You must always get all needed locks at once.
- RETURN VALUES
- 0 ok
- -1 Error
+ If the query for which we are calling this function is marked as
+ requiring prelocking, this function will change
+ locked_tables_mode to LTM_PRELOCKED.
+
+ @retval FALSE Success.
+ @retval TRUE A lock wait timeout, deadlock or out of memory.
*/
-int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
+bool lock_tables(THD *thd, TABLE_LIST *tables, uint count,
+ uint flags)
{
TABLE_LIST *table;
@@ -5563,73 +5803,97 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
We can't meet statement requiring prelocking if we already
in prelocked mode.
*/
- DBUG_ASSERT(!thd->prelocked_mode || !thd->lex->requires_prelocking());
- *need_reopen= FALSE;
+ DBUG_ASSERT(thd->locked_tables_mode <= LTM_LOCK_TABLES ||
+ !thd->lex->requires_prelocking());
if (!tables && !thd->lex->requires_prelocking())
- DBUG_RETURN(decide_logging_format(thd, tables));
+ DBUG_RETURN(thd->decide_logging_format(tables));
/*
- We need this extra check for thd->prelocked_mode because we want to avoid
- attempts to lock tables in substatements. Checking for thd->locked_tables
- is not enough in some situations. For example for SP containing
+ Check for thd->locked_tables_mode to avoid a redundant
+ and harmful attempt to lock the already locked tables again.
+ Checking for thd->lock is not enough in some situations. For example,
+ if a stored function contains
"drop table t3; create temporary t3 ..; insert into t3 ...;"
- thd->locked_tables may be 0 after drop tables, and without this extra
- check insert will try to lock temporary table t3, that will lead
- to memory leak...
+ thd->lock may be 0 after drop tables, whereas locked_tables_mode
+ is still on. In this situation an attempt to lock temporary
+ table t3 will lead to a memory leak.
*/
- if (!thd->locked_tables && !thd->prelocked_mode)
+ if (! thd->locked_tables_mode)
{
DBUG_ASSERT(thd->lock == 0); // You must lock everything at once
TABLE **start,**ptr;
- uint lock_flag= MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN;
if (!(ptr=start=(TABLE**) thd->alloc(sizeof(TABLE*)*count)))
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
for (table= tables; table; table= table->next_global)
{
if (!table->placeholder())
*(ptr++)= table->table;
}
+ /*
+ DML statements that modify a table with an auto_increment column based on
+ rows selected from a table are unsafe as the order in which the rows are
+ fetched fron the select tables cannot be determined and may differ on
+ master and slave.
+ */
+ if (thd->variables.binlog_format != BINLOG_FORMAT_ROW && tables &&
+ has_write_table_with_auto_increment_and_select(tables))
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_WRITE_AUTOINC_SELECT);
+ /* Todo: merge all has_write_table_auto_inc with decide_logging_format */
if (thd->variables.binlog_format != BINLOG_FORMAT_ROW && tables)
{
if (has_write_table_auto_increment_not_first_in_pk(tables))
- thd->lex->set_stmt_unsafe();
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_AUTOINC_NOT_FIRST);
}
+ /*
+ INSERT...ON DUPLICATE KEY UPDATE on a table with more than one unique keys
+ can be unsafe.
+ */
+ uint unique_keys= 0;
+ for (TABLE_LIST *query_table= tables; query_table && unique_keys <= 1;
+ query_table= query_table->next_global)
+ if(query_table->table)
+ {
+ uint keys= query_table->table->s->keys, i= 0;
+ unique_keys= 0;
+ for (KEY* keyinfo= query_table->table->s->key_info;
+ i < keys && unique_keys <= 1; i++, keyinfo++)
+ {
+ if (keyinfo->flags & HA_NOSAME)
+ unique_keys++;
+ }
+ if (!query_table->placeholder() &&
+ query_table->lock_type >= TL_WRITE_ALLOW_WRITE &&
+ unique_keys > 1 && thd->lex->sql_command == SQLCOM_INSERT &&
+ /* Duplicate key update is not supported by INSERT DELAYED */
+ thd->command != COM_DELAYED_INSERT &&
+ thd->lex->duplicates == DUP_UPDATE)
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_TWO_KEYS);
+ }
+
/* We have to emulate LOCK TABLES if we are statement needs prelocking. */
if (thd->lex->requires_prelocking())
{
- thd->in_lock_tables=1;
- thd->options|= OPTION_TABLE_LOCK;
+
/*
A query that modifies autoinc column in sub-statement can make the
master and slave inconsistent.
We can solve these problems in mixed mode by switching to binlogging
if at least one updated table is used by sub-statement
*/
- /* The BINLOG_FORMAT_MIXED judgement is saved for suppressing
- warnings, but it will be removed by fixing bug#45827 */
- if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED && tables &&
+ if (thd->variables.binlog_format != BINLOG_FORMAT_ROW && tables &&
has_write_table_with_auto_increment(thd->lex->first_not_own_table()))
- {
- thd->lex->set_stmt_unsafe();
- }
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_AUTOINC_COLUMNS);
}
DEBUG_SYNC(thd, "before_lock_tables_takes_lock");
if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start),
- lock_flag, need_reopen)))
- {
- if (thd->lex->requires_prelocking())
- {
- thd->options&= ~(OPTION_TABLE_LOCK);
- thd->in_lock_tables=0;
- }
- DBUG_RETURN(-1);
- }
+ flags)))
+ DBUG_RETURN(TRUE);
DEBUG_SYNC(thd, "after_lock_tables_takes_lock");
@@ -5641,17 +5905,6 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
We just have done implicit LOCK TABLES, and now we have
to emulate first open_and_lock_tables() after it.
- Note that "LOCK TABLES" can also be marked as requiring prelocking
- (e.g. if one locks view which uses functions). We should not emulate
- such open_and_lock_tables() in this case. We also should not set
- THD::prelocked_mode or first close_thread_tables() call will do
- "UNLOCK TABLES".
- */
- thd->locked_tables= thd->lock;
- thd->lock= 0;
- thd->in_lock_tables=0;
-
- /*
When open_and_lock_tables() is called for a single table out of
a table list, the 'next_global' chain is temporarily broken. We
may not find 'first_not_own' before the end of the "list".
@@ -5666,12 +5919,11 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
if (!table->placeholder())
{
table->table->query_id= thd->query_id;
- if (check_lock_and_start_stmt(thd, table->table, table->lock_type))
+ if (check_lock_and_start_stmt(thd, thd->lex, table))
{
- mysql_unlock_tables(thd, thd->locked_tables);
- thd->locked_tables= 0;
- thd->options&= ~(OPTION_TABLE_LOCK);
- DBUG_RETURN(-1);
+ mysql_unlock_tables(thd, thd->lock);
+ thd->lock= 0;
+ DBUG_RETURN(TRUE);
}
}
}
@@ -5680,8 +5932,8 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
and was marked as occupied during open_tables() as free for reuse.
*/
mark_real_tables_as_free_for_reuse(first_not_own);
- DBUG_PRINT("info",("prelocked_mode= PRELOCKED"));
- thd->prelocked_mode= PRELOCKED;
+ DBUG_PRINT("info",("locked_tables_mode= LTM_PRELOCKED"));
+ thd->enter_locked_tables_mode(LTM_PRELOCKED);
}
}
else
@@ -5706,7 +5958,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
In a stored function or trigger we should ensure that we won't change
a table that is already used by the calling statement.
*/
- if (thd->prelocked_mode &&
+ if (thd->locked_tables_mode >= LTM_PRELOCKED &&
table->lock_type >= TL_WRITE_ALLOW_WRITE)
{
for (TABLE* opentab= thd->open_tables; opentab; opentab= opentab->next)
@@ -5716,14 +5968,14 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
{
my_error(ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG, MYF(0),
table->table->s->table_name.str);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
}
}
- if (check_lock_and_start_stmt(thd, table->table, table->lock_type))
+ if (check_lock_and_start_stmt(thd, thd->lex, table))
{
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
}
/*
@@ -5734,71 +5986,102 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
if (thd->lex->requires_prelocking())
{
mark_real_tables_as_free_for_reuse(first_not_own);
- DBUG_PRINT("info", ("thd->prelocked_mode= PRELOCKED_UNDER_LOCK_TABLES"));
- thd->prelocked_mode= PRELOCKED_UNDER_LOCK_TABLES;
+ DBUG_PRINT("info",
+ ("thd->locked_tables_mode= LTM_PRELOCKED_UNDER_LOCK_TABLES"));
+ thd->locked_tables_mode= LTM_PRELOCKED_UNDER_LOCK_TABLES;
}
}
- DBUG_RETURN(decide_logging_format(thd, tables));
+ DBUG_RETURN(thd->decide_logging_format(tables));
}
-/*
+/**
Prepare statement for reopening of tables and recalculation of set of
prelocked tables.
- SYNOPSIS
- close_tables_for_reopen()
- thd in Thread context
- tables in/out List of tables which we were trying to open and lock
-
+ @param[in] thd Thread context.
+ @param[in,out] tables List of tables which we were trying to open
+ and lock.
+ @param[in] start_of_statement_svp MDL savepoint which represents the set
+ of metadata locks which the current transaction
+ managed to acquire before execution of the current
+ statement and to which we should revert before
+ trying to reopen tables. NULL if no metadata locks
+ were held and thus all metadata locks should be
+ released.
*/
-void close_tables_for_reopen(THD *thd, TABLE_LIST **tables)
+void close_tables_for_reopen(THD *thd, TABLE_LIST **tables,
+ const MDL_savepoint &start_of_statement_svp)
{
+ TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
+ TABLE_LIST *tmp;
+
/*
If table list consists only from tables from prelocking set, table list
for new attempt should be empty, so we have to update list's root pointer.
*/
- if (thd->lex->first_not_own_table() == *tables)
+ if (first_not_own_table == *tables)
*tables= 0;
thd->lex->chop_off_not_own_tables();
+ /* Reset MDL tickets for procedures/functions */
+ for (Sroutine_hash_entry *rt=
+ (Sroutine_hash_entry*)thd->lex->sroutines_list.first;
+ rt; rt= rt->next)
+ rt->mdl_request.ticket= NULL;
sp_remove_not_own_routines(thd->lex);
- for (TABLE_LIST *tmp= *tables; tmp; tmp= tmp->next_global)
+ for (tmp= *tables; tmp; tmp= tmp->next_global)
+ {
tmp->table= 0;
+ tmp->mdl_request.ticket= NULL;
+ /* We have to cleanup translation tables of views. */
+ tmp->cleanup_items();
+ }
+ /*
+ No need to commit/rollback the statement transaction: it's
+ either not started or we're filling in an INFORMATION_SCHEMA
+ table on the fly, and thus mustn't manipulate with the
+ transaction of the enclosing statement.
+ */
+ DBUG_ASSERT(thd->transaction.stmt.is_empty() ||
+ (thd->state_flags & Open_tables_state::BACKUPS_AVAIL));
close_thread_tables(thd);
+ thd->mdl_context.rollback_to_savepoint(start_of_statement_svp);
}
-/*
- Open a single table without table caching and don't set it in open_list
-
- SYNPOSIS
- open_temporary_table()
- thd Thread object
- path Path (without .frm)
- db database
- table_name Table name
- link_in_list 1 if table should be linked into thd->temporary_tables
-
- NOTES:
- Used by alter_table to open a temporary table and when creating
- a temporary table with CREATE TEMPORARY ...
-
- RETURN
- 0 Error
- # TABLE object
+/**
+ Open a single table without table caching and don't add it to
+ THD::open_tables. Depending on the 'add_to_temporary_tables_list' value,
+ the opened TABLE instance will be addded to THD::temporary_tables list.
+
+ @param thd Thread context.
+ @param path Path (without .frm)
+ @param db Database name.
+ @param table_name Table name.
+ @param add_to_temporary_tables_list Specifies if the opened TABLE
+ instance should be linked into
+ THD::temporary_tables list.
+
+ @note This function is used:
+ - by alter_table() to open a temporary table;
+ - when creating a temporary table with CREATE TEMPORARY TABLE.
+
+ @return TABLE instance for opened table.
+ @retval NULL on error.
*/
-TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
- const char *table_name, bool link_in_list)
+TABLE *open_table_uncached(THD *thd, const char *path, const char *db,
+ const char *table_name,
+ bool add_to_temporary_tables_list)
{
TABLE *tmp_table;
TABLE_SHARE *share;
char cache_key[MAX_DBKEY_LENGTH], *saved_cache_key, *tmp_path;
uint key_length;
TABLE_LIST table_list;
- DBUG_ENTER("open_temporary_table");
+ DBUG_ENTER("open_table_uncached");
DBUG_PRINT("enter",
("table: '%s'.'%s' path: '%s' server_id: %u "
"pseudo_thread_id: %lu",
@@ -5833,7 +6116,7 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
{
/* No need to lock share->mutex as this is not needed for tmp tables */
free_table_share(share);
- my_free((char*) tmp_table,MYF(0));
+ my_free(tmp_table);
DBUG_RETURN(0);
}
@@ -5841,7 +6124,7 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
share->tmp_table= (tmp_table->file->has_transactions() ?
TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE);
- if (link_in_list)
+ if (add_to_temporary_tables_list)
{
/* growing temp list at the head */
tmp_table->next= thd->temporary_tables;
@@ -5878,7 +6161,7 @@ bool rm_temporary_table(handlerton *base, const char *path)
DBUG_ENTER("rm_temporary_table");
strxnmov(frm_path, sizeof(frm_path) - 1, path, reg_ext, NullS);
- if (my_delete(frm_path, MYF(0)))
+ if (mysql_file_delete(key_file_frm, frm_path, MYF(0)))
error=1; /* purecov: inspected */
file= get_new_handler((TABLE_SHARE*) 0, current_thd->mem_root, base);
if (file && file->ha_delete_table(path))
@@ -6227,8 +6510,8 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
field_ptr= table->field + cached_field_index;
else if (table->s->name_hash.records)
{
- field_ptr= (Field**) hash_search(&table->s->name_hash, (uchar*) name,
- length);
+ field_ptr= (Field**) my_hash_search(&table->s->name_hash, (uchar*) name,
+ length);
if (field_ptr)
{
/*
@@ -6357,7 +6640,9 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
table_name && table_name[0] &&
(my_strcasecmp(table_alias_charset, table_list->alias, table_name) ||
(db_name && db_name[0] && table_list->db && table_list->db[0] &&
- strcmp(db_name, table_list->db))))
+ (table_list->schema_table ?
+ my_strcasecmp(system_charset_info, db_name, table_list->db) :
+ strcmp(db_name, table_list->db)))))
DBUG_RETURN(0);
*actual_table= NULL;
@@ -6480,8 +6765,8 @@ Field *find_field_in_table_sef(TABLE *table, const char *name)
Field **field_ptr;
if (table->s->name_hash.records)
{
- field_ptr= (Field**)hash_search(&table->s->name_hash,(uchar*) name,
- strlen(name));
+ field_ptr= (Field**)my_hash_search(&table->s->name_hash,(uchar*) name,
+ strlen(name));
if (field_ptr)
{
/*
@@ -6652,7 +6937,7 @@ find_field_in_tables(THD *thd, Item_ident *item,
We can't do this in Item_field as this would change the
'name' of the item which may be used in the select list
*/
- strmake(name_buff, db, sizeof(name_buff)-1);
+ strmake_buf(name_buff, db);
my_casedn_str(files_charset_info, name_buff);
db= name_buff;
}
@@ -6711,7 +6996,7 @@ find_field_in_tables(THD *thd, Item_ident *item,
*/
if (db)
return cur_field;
-
+
if (found)
{
if (report_error == REPORT_ALL_ERRORS ||
@@ -6726,7 +7011,7 @@ find_field_in_tables(THD *thd, Item_ident *item,
if (found)
return found;
-
+
/*
If the field was qualified and there were no tables to search, issue
an error that an unknown table was given. The situation is detected
@@ -7653,17 +7938,37 @@ err:
order, thus when we iterate over it, we are moving from the right
to the left in the FROM clause.
+ NOTES
+ We can't run this many times as the first_name_resolution_table would
+ be different for subsequent runs when sub queries has been optimized
+ away.
+
RETURN
TRUE Error
FALSE OK
*/
+
static bool setup_natural_join_row_types(THD *thd,
List<TABLE_LIST> *from_clause,
Name_resolution_context *context)
{
+ DBUG_ENTER("setup_natural_join_row_types");
thd->where= "from clause";
if (from_clause->elements == 0)
- return FALSE; /* We come here in the case of UNIONs. */
+ DBUG_RETURN(false); /* We come here in the case of UNIONs. */
+
+ /*
+ Do not redo work if already done:
+ 1) for stored procedures,
+ 2) for multitable update after lock failure and table reopening.
+ */
+ if (!context->select_lex->first_natural_join_processing)
+ {
+ context->first_name_resolution_table= context->natural_join_first_table;
+ DBUG_PRINT("info", ("using cached setup_natural_join_row_types"));
+ DBUG_RETURN(false);
+ }
+ context->select_lex->first_natural_join_processing= false;
List_iterator_fast<TABLE_LIST> table_ref_it(*from_clause);
TABLE_LIST *table_ref; /* Current table reference. */
@@ -7671,10 +7976,6 @@ static bool setup_natural_join_row_types(THD *thd,
TABLE_LIST *left_neighbor;
/* Table reference to the right of the current. */
TABLE_LIST *right_neighbor= NULL;
- bool save_first_natural_join_processing=
- context->select_lex->first_natural_join_processing;
-
- context->select_lex->first_natural_join_processing= FALSE;
/* Note that tables in the list are in reversed order */
for (left_neighbor= table_ref_it++; left_neighbor ; )
@@ -7685,23 +7986,15 @@ static bool setup_natural_join_row_types(THD *thd,
left_neighbor= table_ref_it++;
}
while (left_neighbor && left_neighbor->sj_subq_pred);
- /*
- Do not redo work if already done:
- 1) for stored procedures,
- 2) for multitable update after lock failure and table reopening.
- */
- if (save_first_natural_join_processing)
+
+ if (store_top_level_join_columns(thd, table_ref,
+ left_neighbor, right_neighbor))
+ DBUG_RETURN(true);
+ if (left_neighbor)
{
- context->select_lex->first_natural_join_processing= FALSE;
- if (store_top_level_join_columns(thd, table_ref,
- left_neighbor, right_neighbor))
- return TRUE;
- if (left_neighbor)
- {
- TABLE_LIST *first_leaf_on_the_right;
- first_leaf_on_the_right= table_ref->first_leaf_for_name_resolution();
- left_neighbor->next_name_resolution_table= first_leaf_on_the_right;
- }
+ TABLE_LIST *first_leaf_on_the_right;
+ first_leaf_on_the_right= table_ref->first_leaf_for_name_resolution();
+ left_neighbor->next_name_resolution_table= first_leaf_on_the_right;
}
right_neighbor= table_ref;
}
@@ -7715,8 +8008,12 @@ static bool setup_natural_join_row_types(THD *thd,
DBUG_ASSERT(right_neighbor);
context->first_name_resolution_table=
right_neighbor->first_leaf_for_name_resolution();
-
- return FALSE;
+ /*
+ This is only to ensure that first_name_resolution_table doesn't
+ change on re-execution
+ */
+ context->natural_join_first_table= context->first_name_resolution_table;
+ DBUG_RETURN (false);
}
@@ -7826,7 +8123,8 @@ bool setup_fields(THD *thd, Item **ref_pointer_array,
thd->mark_used_columns= mark_used_columns;
DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
if (allow_sum_func)
- thd->lex->allow_sum_func|= 1 << thd->lex->current_select->nest_level;
+ thd->lex->allow_sum_func|=
+ (nesting_map)1 << 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;
@@ -8026,7 +8324,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
}
if (tablenr > MAX_TABLES)
{
- my_error(ER_TOO_MANY_TABLES,MYF(0), (int) MAX_TABLES);
+ my_error(ER_TOO_MANY_TABLES,MYF(0), static_cast<int>(MAX_TABLES));
DBUG_RETURN(1);
}
}
@@ -8075,7 +8373,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
Item *item= table_list->jtbm_subselect->optimizer;
if (table_list->jtbm_subselect->optimizer->fix_fields(thd, &item))
{
- my_error(ER_TOO_MANY_TABLES,MYF(0),MAX_TABLES); /* psergey-todo: WHY ER_TOO_MANY_TABLES ???*/
+ my_error(ER_TOO_MANY_TABLES,MYF(0), static_cast<int>(MAX_TABLES)); /* psergey-todo: WHY ER_TOO_MANY_TABLES ???*/
DBUG_RETURN(1);
}
DBUG_ASSERT(item == table_list->jtbm_subselect->optimizer);
@@ -8224,7 +8522,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
We can't do this in Item_field as this would change the
'name' of the item which may be used in the select list
*/
- strmake(name_buff, db_name, sizeof(name_buff)-1);
+ strmake_buf(name_buff, db_name);
my_casedn_str(files_charset_info, name_buff);
db_name= name_buff;
}
@@ -8313,7 +8611,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
if (!(item= field_iterator.create_item(thd)))
DBUG_RETURN(TRUE);
-// DBUG_ASSERT(item->fixed);
+
/* cache the table for the Item_fields inserted by expanding stars */
if (item->type() == Item::FIELD_ITEM && tables->cacheable_table)
((Item_field *)item)->cached_table= tables;
@@ -8462,9 +8760,7 @@ void wrap_ident(THD *thd, Item **conds)
bool setup_on_expr(THD *thd, TABLE_LIST *table, bool is_update)
{
-#ifndef EMBEDDED_LIBRARY // Avoid compiler warning
uchar buff[STACK_BUFF_ALLOC]; // Max argument in function
-#endif
if (check_stack_overrun(thd, STACK_MIN_SIZE, buff))
return TRUE; // Fatal error flag is set!
for(; table; table= table->next_local)
@@ -8474,7 +8770,6 @@ bool setup_on_expr(THD *thd, TABLE_LIST *table, bool is_update)
do
{
embedded= embedding;
- DBUG_PRINT("XXX", ("check: %s", table->alias));
if (embedded->on_expr)
{
thd->where="on clause";
@@ -8543,7 +8838,6 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List<TABLE_LIST> &leaves,
COND **conds)
{
SELECT_LEX *select_lex= thd->lex->current_select;
- Query_arena *arena= thd->stmt_arena, backup;
TABLE_LIST *table= NULL; // For HP compilers
/*
it_is_update set to TRUE when tables of primary SELECT_LEX (SELECT_LEX
@@ -8565,10 +8859,6 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List<TABLE_LIST> &leaves,
select_lex->is_item_list_lookup= 0;
- if (select_lex->conds_processed_with_permanent_arena ||
- arena->is_conventional())
- arena= 0; // For easier test
-
thd->mark_used_columns= MARK_COLUMNS_READ;
DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
select_lex->cond_count= 0;
@@ -8619,7 +8909,6 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List<TABLE_LIST> &leaves,
We do this ON -> WHERE transformation only once per PS/SP statement.
*/
select_lex->where= *conds;
- select_lex->conds_processed_with_permanent_arena= 1;
}
thd->lex->current_select->is_item_list_lookup= save_is_item_list_lookup;
DBUG_RETURN(test(thd->is_error()));
@@ -8732,7 +9021,6 @@ fill_record(THD * thd, List<Item> &fields, List<Item> &values,
thd->abort_on_warning= save_abort_on_warning;
thd->no_errors= save_no_errors;
DBUG_RETURN(thd->is_error());
-
err:
thd->abort_on_warning= save_abort_on_warning;
thd->no_errors= save_no_errors;
@@ -9005,7 +9293,7 @@ my_bool mysql_rm_tmp_tables(void)
So we hide error messages which happnes during deleting of these
files(MYF(0)).
*/
- VOID(my_delete(filePath, MYF(0)));
+ (void) mysql_file_delete(key_file_misc, filePath, MYF(0));
}
}
my_dirend(dirp);
@@ -9016,47 +9304,11 @@ my_bool mysql_rm_tmp_tables(void)
}
-
/*****************************************************************************
unireg support functions
*****************************************************************************/
/*
- Invalidate any cache entries that are for some DB
-
- SYNOPSIS
- remove_db_from_cache()
- db Database name. This will be in lower case if
- lower_case_table_name is set
-
- NOTE:
- We can't use hash_delete when looping hash_elements. We mark them first
- and afterwards delete those marked unused.
-*/
-
-void remove_db_from_cache(const char *db)
-{
- for (uint idx=0 ; idx < open_cache.records ; idx++)
- {
- TABLE *table=(TABLE*) hash_element(&open_cache,idx);
- if (!strcmp(table->s->db.str, db))
- {
- table->s->version= 0L; /* Free when thread is ready */
- /*
- This functions only called from DROP DATABASE code, so we are going
- to drop all tables so we mark them as deleting
- */
- table->s->deleting= TRUE;
- if (!table->in_use)
- relink_unused(table);
- }
- }
- while (unused_tables && !unused_tables->s->version)
- VOID(hash_delete(&open_cache,(uchar*) unused_tables));
-}
-
-
-/*
free all unused tables
NOTE
@@ -9064,181 +9316,190 @@ void remove_db_from_cache(const char *db)
all not used tables.
*/
-void flush_tables()
+void tdc_flush_unused_tables()
{
- (void) pthread_mutex_lock(&LOCK_open);
+ mysql_mutex_lock(&LOCK_open);
while (unused_tables)
- hash_delete(&open_cache,(uchar*) unused_tables);
- (void) pthread_mutex_unlock(&LOCK_open);
+ free_cache_entry(unused_tables);
+ mysql_mutex_unlock(&LOCK_open);
}
-/*
- Mark all entries with the table as deleted to force an reopen of the table
+/**
+ A callback to the server internals that is used to address
+ special cases of the locking protocol.
+ Invoked when acquiring an exclusive lock, for each thread that
+ has a conflicting shared metadata lock.
+
+ This function:
+ - aborts waiting of the thread on a data lock, to make it notice
+ the pending exclusive lock and back off.
+ - if the thread is an INSERT DELAYED thread, sends it a KILL
+ signal to terminate it.
+
+ @note This function does not wait for the thread to give away its
+ locks. Waiting is done outside for all threads at once.
+
+ @param thd Current thread context
+ @param in_use The thread to wake up
+ @param needs_thr_lock_abort Indicates that to wake up thread
+ this call needs to abort its waiting
+ on table-level lock.
+
+ @retval TRUE if the thread was woken up
+ @retval FALSE otherwise.
+
+ @note It is one of two places where border between MDL and the
+ rest of the server is broken.
+*/
- The table will be closed (not stored in cache) by the current thread when
- close_thread_tables() is called.
+bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use,
+ bool needs_thr_lock_abort)
+{
+ bool signalled= FALSE;
+ if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) &&
+ !in_use->killed)
+ {
+ in_use->killed= KILL_SYSTEM_THREAD;
+ mysql_mutex_lock(&in_use->mysys_var->mutex);
+ if (in_use->mysys_var->current_cond)
+ {
+ mysql_mutex_lock(in_use->mysys_var->current_mutex);
+ mysql_cond_broadcast(in_use->mysys_var->current_cond);
+ mysql_mutex_unlock(in_use->mysys_var->current_mutex);
+ }
+ mysql_mutex_unlock(&in_use->mysys_var->mutex);
+ signalled= TRUE;
+ }
- PREREQUISITES
- Lock on LOCK_open()
+ if (needs_thr_lock_abort)
+ {
+ mysql_mutex_lock(&in_use->LOCK_thd_data);
+ for (TABLE *thd_table= in_use->open_tables;
+ thd_table ;
+ thd_table= thd_table->next)
+ {
+ /*
+ Check for TABLE::needs_reopen() is needed since in some places we call
+ handler::close() for table instance (and set TABLE::db_stat to 0)
+ and do not remove such instances from the THD::open_tables
+ for some time, during which other thread can see those instances
+ (e.g. see partitioning code).
+ */
+ if (!thd_table->needs_reopen())
+ signalled|= mysql_lock_abort_for_thread(thd, thd_table);
+ }
+ mysql_mutex_unlock(&in_use->LOCK_thd_data);
+ }
+ return signalled;
+}
- RETURN
- 0 This thread now have exclusive access to this table and no other thread
- can access the table until close_thread_tables() is called.
- 1 Table is in use by another thread
+
+/**
+ Remove all or some (depending on parameter) instances of TABLE and
+ TABLE_SHARE from the table definition cache.
+
+ @param thd Thread context
+ @param remove_type Type of removal:
+ TDC_RT_REMOVE_ALL - remove all TABLE instances and
+ TABLE_SHARE instance. There
+ should be no used TABLE objects
+ and caller should have exclusive
+ metadata lock on the table.
+ TDC_RT_REMOVE_NOT_OWN - remove all TABLE instances
+ except those that belong to
+ this thread. There should be
+ no TABLE objects used by other
+ threads and caller should have
+ exclusive metadata lock on the
+ table.
+ TDC_RT_REMOVE_UNUSED - remove all unused TABLE
+ instances (if there are no
+ used instances will also
+ remove TABLE_SHARE).
+ @param db Name of database
+ @param table_name Name of table
+ @param has_lock If TRUE, LOCK_open is already acquired
+
+ @note It assumes that table instances are already not used by any
+ (other) thread (this should be achieved by using meta-data locks).
*/
-bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
- uint flags, my_bool deleting)
+void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
+ const char *db, const char *table_name,
+ bool has_lock)
{
char key[MAX_DBKEY_LENGTH];
uint key_length;
TABLE *table;
TABLE_SHARE *share;
- bool result= 0, signalled= 0;
- DBUG_ENTER("remove_table_from_cache");
- DBUG_PRINT("enter", ("table: '%s'.'%s' flags: %u", db, table_name, flags));
+ DBUG_ENTER("tdc_remove_table");
+ DBUG_PRINT("enter",("name: %s remove_type: %d", table_name, remove_type));
- key_length= create_table_def_key(key, db, table_name);
- for (;;)
+ if (! has_lock)
+ mysql_mutex_lock(&LOCK_open);
+ else
{
- HASH_SEARCH_STATE state;
- result= signalled= 0;
+ mysql_mutex_assert_owner(&LOCK_open);
+ }
- for (table= (TABLE*) hash_first(&open_cache, (uchar*) key, key_length,
- &state);
- table;
- table= (TABLE*) hash_next(&open_cache, (uchar*) key, key_length,
- &state))
- {
- THD *in_use;
- DBUG_PRINT("tcache", ("found table: '%s'.'%s' 0x%lx", table->s->db.str,
- table->s->table_name.str, (long) table));
+ DBUG_ASSERT(remove_type == TDC_RT_REMOVE_UNUSED ||
+ thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
+ MDL_EXCLUSIVE));
+
+ key_length= create_table_def_key(key, db, table_name);
- table->s->version=0L; /* Free when thread is ready */
- if (!(in_use=table->in_use))
+ if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache,(uchar*) key,
+ key_length)))
+ {
+ if (share->ref_count)
+ {
+ I_P_List_iterator<TABLE, TABLE_share> it(share->free_tables);
+#ifndef DBUG_OFF
+ if (remove_type == TDC_RT_REMOVE_ALL)
{
- DBUG_PRINT("info",("Table was not in use"));
- relink_unused(table);
+ DBUG_ASSERT(share->used_tables.is_empty());
}
- else if (in_use != thd)
+ else if (remove_type == TDC_RT_REMOVE_NOT_OWN ||
+ remove_type == TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE)
{
- DBUG_PRINT("info", ("Table was in use by other thread"));
- /*
- Mark that table is going to be deleted from cache. This will
- force threads that are in mysql_lock_tables() (but not yet
- in thr_multi_lock()) to abort it's locks, close all tables and retry
- */
- in_use->some_tables_deleted= 1;
- if (table->is_name_opened())
- {
- DBUG_PRINT("info", ("Found another active instance of the table"));
- result=1;
- }
- /* Kill delayed insert threads */
- if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT))
- {
- if (!in_use->killed)
+ I_P_List_iterator<TABLE, TABLE_share> it2(share->used_tables);
+ while ((table= it2++))
+ if (table->in_use != thd)
{
- in_use->killed= KILL_SYSTEM_THREAD;
- pthread_mutex_lock(&in_use->mysys_var->mutex);
- if (in_use->mysys_var->current_cond)
- {
- pthread_mutex_lock(in_use->mysys_var->current_mutex);
- signalled= 1;
- pthread_cond_broadcast(in_use->mysys_var->current_cond);
- pthread_mutex_unlock(in_use->mysys_var->current_mutex);
- }
- pthread_mutex_unlock(&in_use->mysys_var->mutex);
+ DBUG_ASSERT(0);
}
- /*
- Don't abort locks. Instead give the delayed insert thread
- time to finish it's inserts and die gracefully.
- */
- continue;
- }
- /*
- Now we must abort all tables locks used by this thread
- as the thread may be waiting to get a lock for another table.
- Note that we need to hold LOCK_open while going through the
- list. So that the other thread cannot change it. The other
- thread must also hold LOCK_open whenever changing the
- open_tables list. Aborting the MERGE lock after a child was
- closed and before the parent is closed would be fatal.
- */
- for (TABLE *thd_table= in_use->open_tables;
- thd_table ;
- thd_table= thd_table->next)
- {
- /* Do not handle locks of MERGE children. */
- if (thd_table->db_stat && !thd_table->parent) // If table is open
- signalled|= mysql_lock_abort_for_thread(thd, thd_table);
- }
}
- else
- {
- DBUG_PRINT("info", ("Table was in use by current thread. db_stat: %u",
- table->db_stat));
- result= result || (flags & RTFC_OWNED_BY_THD_FLAG);
- }
- }
- while (unused_tables && !unused_tables->s->version)
- {
- unused_tables->s->deleting= deleting;
- VOID(hash_delete(&open_cache,(uchar*) unused_tables));
- }
-
- DBUG_PRINT("info", ("Removing table from table_def_cache"));
- /* Remove table from table definition cache if it's not in use */
- if ((share= (TABLE_SHARE*) hash_search(&table_def_cache,(uchar*) key,
- key_length)))
- {
- DBUG_PRINT("info", ("share version: %lu ref_count: %u",
- share->version, share->ref_count));
- share->version= 0; // Mark for delete
- if (share->ref_count == 0)
- {
- pthread_mutex_lock(&share->mutex);
- VOID(hash_delete(&table_def_cache, (uchar*) share));
- }
- }
-
- if (result && (flags & RTFC_WAIT_OTHER_THREAD_FLAG))
- {
+#endif
/*
- Signal any thread waiting for tables to be freed to
- reopen their tables
+ Mark share to ensure that it gets automatically deleted once
+ it is no longer referenced.
+
+ Note that code in TABLE_SHARE::wait_for_old_version() assumes
+ that marking share as old and removal of its unused tables
+ and of the share itself from TDC happens atomically under
+ protection of LOCK_open, or, putting it another way, that
+ TDC does not contain old shares which don't have any tables
+ used.
*/
- broadcast_refresh();
- DBUG_PRINT("info", ("Waiting for refresh signal"));
- if (!(flags & RTFC_CHECK_KILLED_FLAG) || !thd->killed)
+ if (remove_type == TDC_RT_REMOVE_NOT_OWN)
+ share->remove_from_cache_at_close();
+ else
{
- dropping_tables++;
- if (likely(signalled))
- (void) pthread_cond_wait(&COND_refresh, &LOCK_open);
- else
- {
- struct timespec abstime;
- /*
- It can happen that another thread has opened the
- table but has not yet locked any table at all. Since
- it can be locked waiting for a table that our thread
- has done LOCK TABLE x WRITE on previously, we need to
- ensure that the thread actually hears our signal
- before we go to sleep. Thus we wait for a short time
- and then we retry another loop in the
- remove_table_from_cache routine.
- */
- set_timespec(abstime, 10);
- pthread_cond_timedwait(&COND_refresh, &LOCK_open, &abstime);
- }
- dropping_tables--;
- continue;
+ /* Ensure that no can open the table while it's used */
+ share->protect_against_usage();
}
+
+ while ((table= it++))
+ free_cache_entry(table);
}
- break;
+ else
+ (void) my_hash_delete(&table_def_cache, (uchar*) share);
}
- DBUG_RETURN(result);
+
+ if (! has_lock)
+ mysql_mutex_unlock(&LOCK_open);
+ DBUG_VOID_RETURN;
}
@@ -9298,7 +9559,7 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order)
mem_root temporary MEM_ROOT for parsing
*/
-static bool
+bool
open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias,
uint db_stat, uint prgflag,
uint ha_open_flags, TABLE *outparam, TABLE_LIST *table_desc,
@@ -9329,6 +9590,7 @@ open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias,
if (mysql_make_view(thd, parser, table_desc,
(prgflag & OPEN_VIEW_NO_PARSE)))
goto err;
+ status_var_increment(thd->status_var.opened_views);
}
else
{
@@ -9340,7 +9602,6 @@ open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias,
}
err:
- bzero(outparam, sizeof(TABLE)); // do not run repair
DBUG_RETURN(1);
}
@@ -9352,179 +9613,12 @@ bool is_equal(const LEX_STRING *a, const LEX_STRING *b)
/*
- SYNOPSIS
- abort_and_upgrade_lock_and_close_table()
- lpt Parameter passing struct
- All parameters passed through the ALTER_PARTITION_PARAM_TYPE object
- RETURN VALUE
- 0
- DESCRIPTION
- Remember old lock level (for possible downgrade later on), abort all
- waiting threads and ensure that all keeping locks currently are
- completed such that we own the lock exclusively and no other interaction
- is ongoing. Close the table and hold the name lock.
-
- thd Thread object
- table Table object
- db Database name
- table_name Table name
- old_lock_level Old lock level
-*/
-
-int abort_and_upgrade_lock_and_close_table(ALTER_PARTITION_PARAM_TYPE *lpt)
-{
- uint flags= RTFC_WAIT_OTHER_THREAD_FLAG | RTFC_CHECK_KILLED_FLAG;
- const char *db= lpt->db;
- const char *table_name= lpt->table_name;
- THD *thd= lpt->thd;
- DBUG_ENTER("abort_and_upgrade_lock_and_close_table");
-
- lpt->old_lock_type= lpt->table->reginfo.lock_type;
- safe_mutex_assert_not_owner(&LOCK_open);
- VOID(pthread_mutex_lock(&LOCK_open));
- /* If MERGE child, forward lock handling to parent. */
- mysql_lock_abort(thd, lpt->table->parent ? lpt->table->parent : lpt->table,
- TRUE);
- if (remove_table_from_cache(thd, db, table_name, flags, FALSE))
- {
- VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(1);
- }
- close_data_files_and_morph_locks(thd, db, table_name);
- VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(0);
-}
-
-
-/*
- SYNOPSIS
- close_open_tables_and_downgrade()
- RESULT VALUES
- NONE
- DESCRIPTION
- We need to ensure that any thread that has managed to open the table
- but not yet encountered our lock on the table is also thrown out to
- ensure that no threads see our frm changes premature to the final
- version. The intermediate versions are only meant for use after a
- crash and later REPAIR TABLE.
- We also downgrade locks after the upgrade to WRITE_ONLY
-*/
-
-/* purecov: begin deadcode */
-void close_open_tables_and_downgrade(ALTER_PARTITION_PARAM_TYPE *lpt)
-{
- VOID(pthread_mutex_lock(&LOCK_open));
- remove_table_from_cache(lpt->thd, lpt->db, lpt->table_name,
- RTFC_WAIT_OTHER_THREAD_FLAG, FALSE);
- VOID(pthread_mutex_unlock(&LOCK_open));
- /* If MERGE child, forward lock handling to parent. */
- mysql_lock_downgrade_write(lpt->thd, lpt->table->parent ? lpt->table->parent :
- lpt->table, lpt->old_lock_type);
-}
-/* purecov: end */
-
+ Tells if two (or more) tables have auto_increment columns and we want to
+ lock those tables with a write lock.
-/*
SYNOPSIS
- mysql_wait_completed_table()
- lpt Parameter passing struct
- my_table My table object
- All parameters passed through the ALTER_PARTITION_PARAM object
- RETURN VALUES
- TRUE Failure
- FALSE Success
- DESCRIPTION
- We have changed the frm file and now we want to wait for all users of
- the old frm to complete before proceeding to ensure that no one
- remains that uses the old frm definition.
- Start by ensuring that all users of the table will be removed from cache
- once they are done. Then abort all that have stumbled on locks and
- haven't been started yet.
-
- thd Thread object
- table Table object
- db Database name
- table_name Table name
-*/
-
-void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table)
-{
- char key[MAX_DBKEY_LENGTH];
- uint key_length;
- TABLE *table;
- DBUG_ENTER("mysql_wait_completed_table");
-
- key_length= create_table_def_key(key, lpt->db, lpt->table_name);
- VOID(pthread_mutex_lock(&LOCK_open));
- HASH_SEARCH_STATE state;
- for (table= (TABLE*) hash_first(&open_cache,(uchar*) key,key_length,
- &state) ;
- table;
- table= (TABLE*) hash_next(&open_cache,(uchar*) key,key_length,
- &state))
- {
- THD *in_use= table->in_use;
- table->s->version= 0L;
- if (!in_use)
- {
- relink_unused(table);
- }
- else
- {
- /* Kill delayed insert threads */
- if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) &&
- ! in_use->killed)
- {
- in_use->killed= KILL_SYSTEM_THREAD;
- pthread_mutex_lock(&in_use->mysys_var->mutex);
- if (in_use->mysys_var->current_cond)
- {
- pthread_mutex_lock(in_use->mysys_var->current_mutex);
- pthread_cond_broadcast(in_use->mysys_var->current_cond);
- pthread_mutex_unlock(in_use->mysys_var->current_mutex);
- }
- pthread_mutex_unlock(&in_use->mysys_var->mutex);
- }
- /*
- Now we must abort all tables locks used by this thread
- as the thread may be waiting to get a lock for another table.
- Note that we need to hold LOCK_open while going through the
- list. So that the other thread cannot change it. The other
- thread must also hold LOCK_open whenever changing the
- open_tables list. Aborting the MERGE lock after a child was
- closed and before the parent is closed would be fatal.
- */
- for (TABLE *thd_table= in_use->open_tables;
- thd_table ;
- thd_table= thd_table->next)
- {
- /* Do not handle locks of MERGE children. */
- if (thd_table->db_stat && !thd_table->parent) // If table is open
- mysql_lock_abort_for_thread(lpt->thd, thd_table);
- }
- }
- }
- /*
- We start by removing all unused objects from the cache and marking
- those in use for removal after completion. Now we also need to abort
- all that are locked and are not progressing due to being locked
- by our lock. We don't upgrade our lock here.
- If MERGE child, forward lock handling to parent.
- */
- mysql_lock_abort(lpt->thd, my_table->parent ? my_table->parent : my_table,
- FALSE);
- VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_VOID_RETURN;
-}
-
-
-/*
- Check if one (or more) write tables have auto_increment columns.
-
- @param[in] tables Table list
-
- @retval 0 if at least one write tables has an auto_increment column
- @retval 1 otherwise
+ has_two_write_locked_tables_with_auto_increment
+ tables Table list
NOTES:
Call this function only when you have established the list of all tables
@@ -9548,6 +9642,40 @@ has_write_table_with_auto_increment(TABLE_LIST *tables)
}
/*
+ checks if we have select tables in the table list and write tables
+ with auto-increment column.
+
+ SYNOPSIS
+ has_two_write_locked_tables_with_auto_increment_and_select
+ tables Table list
+
+ RETURN VALUES
+
+ -true if the table list has atleast one table with auto-increment column
+
+
+ and atleast one table to select from.
+ -false otherwise
+*/
+
+static bool
+has_write_table_with_auto_increment_and_select(TABLE_LIST *tables)
+{
+ bool has_select= false;
+ bool has_auto_increment_tables = has_write_table_with_auto_increment(tables);
+ for(TABLE_LIST *table= tables; table; table= table->next_global)
+ {
+ if (!table->placeholder() &&
+ (table->lock_type <= TL_READ_NO_INSERT))
+ {
+ has_select= true;
+ break;
+ }
+ }
+ return(has_select && has_auto_increment_tables);
+}
+
+/*
Tells if there is a table whose auto_increment column is a part
of a compound primary key while is not the first column in
the table definition.
@@ -9574,6 +9702,7 @@ has_write_table_auto_increment_not_first_in_pk(TABLE_LIST *tables)
}
+
/*
Open and lock system tables for read.
@@ -9600,45 +9729,40 @@ has_write_table_auto_increment_not_first_in_pk(TABLE_LIST *tables)
bool
open_system_tables_for_read(THD *thd, TABLE_LIST *table_list,
- Open_tables_state *backup)
+ Open_tables_backup *backup)
{
+ Query_tables_list query_tables_list_backup;
+ LEX *lex= thd->lex;
+
DBUG_ENTER("open_system_tables_for_read");
+ /*
+ Besides using new Open_tables_state for opening system tables,
+ we also have to backup and reset/and then restore part of LEX
+ which is accessed by open_tables() in order to determine if
+ prelocking is needed and what tables should be added for it.
+ close_system_tables() doesn't require such treatment.
+ */
+ lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
thd->reset_n_backup_open_tables_state(backup);
- uint count= 0;
- bool not_used;
- for (TABLE_LIST *tables= table_list; tables; tables= tables->next_global)
+ if (open_and_lock_tables(thd, table_list, FALSE,
+ MYSQL_OPEN_IGNORE_FLUSH |
+ MYSQL_LOCK_IGNORE_TIMEOUT))
{
- TABLE *table= open_table(thd, tables, thd->mem_root, &not_used,
- MYSQL_LOCK_IGNORE_FLUSH);
- if (!table)
- goto error;
-
- DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_SYSTEM);
-
- table->use_all_columns();
- table->reginfo.lock_type= tables->lock_type;
- tables->table= table;
- count++;
+ lex->restore_backup_query_tables_list(&query_tables_list_backup);
+ thd->restore_backup_open_tables_state(backup);
+ DBUG_RETURN(TRUE);
}
+ for (TABLE_LIST *tables= table_list; tables; tables= tables->next_global)
{
- TABLE **list= (TABLE**) thd->alloc(sizeof(TABLE*) * count);
- TABLE **ptr= list;
- for (TABLE_LIST *tables= table_list; tables; tables= tables->next_global)
- *(ptr++)= tables->table;
-
- thd->lock= mysql_lock_tables(thd, list, count,
- MYSQL_LOCK_IGNORE_FLUSH, &not_used);
+ DBUG_ASSERT(tables->table->s->table_category == TABLE_CATEGORY_SYSTEM);
+ tables->table->use_all_columns();
}
- if (thd->lock)
- DBUG_RETURN(FALSE);
+ lex->restore_backup_query_tables_list(&query_tables_list_backup);
-error:
- close_system_tables(thd, backup);
-
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(FALSE);
}
@@ -9648,19 +9772,45 @@ error:
SYNOPSIS
close_system_tables()
thd Thread context
- backup Pointer to Open_tables_state instance which holds
+ backup Pointer to Open_tables_backup instance which holds
information about tables which were open before we
decided to access system tables.
*/
void
-close_system_tables(THD *thd, Open_tables_state *backup)
+close_system_tables(THD *thd, Open_tables_backup *backup)
{
close_thread_tables(thd);
thd->restore_backup_open_tables_state(backup);
}
+/**
+ A helper function to close a mysql.* table opened
+ in an auxiliary THD during bootstrap or in the main
+ connection, when we know that there are no locks
+ held by the connection due to a preceding implicit
+ commit.
+
+ We need this function since we'd like to not
+ just close the system table, but also release
+ the metadata lock on it.
+
+ Note, that in LOCK TABLES mode this function
+ does not release the metadata lock. But in this
+ mode the table can be opened only if it is locked
+ explicitly with LOCK TABLES.
+*/
+
+void
+close_mysql_tables(THD *thd)
+{
+ if (! thd->in_sub_stmt)
+ trans_commit_stmt(thd);
+ close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
+}
+
/*
Open and lock one system table for update.
@@ -9682,7 +9832,8 @@ open_system_table_for_update(THD *thd, TABLE_LIST *one_table)
{
DBUG_ENTER("open_system_table_for_update");
- TABLE *table= open_ltable(thd, one_table, one_table->lock_type, 0);
+ TABLE *table= open_ltable(thd, one_table, one_table->lock_type,
+ MYSQL_LOCK_IGNORE_TIMEOUT);
if (table)
{
DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_SYSTEM);
@@ -9693,34 +9844,34 @@ open_system_table_for_update(THD *thd, TABLE_LIST *one_table)
}
/**
- Open a performance schema table.
+ Open a log table.
Opening such tables is performed internally in the server
implementation, and is a 'nested' open, since some tables
might be already opened by the current thread.
The thread context before this call is saved, and is restored
- when calling close_performance_schema_table().
+ when calling close_log_table().
@param thd The current thread
- @param one_table Performance schema table to open
+ @param one_table Log table to open
@param backup [out] Temporary storage used to save the thread context
*/
TABLE *
-open_performance_schema_table(THD *thd, TABLE_LIST *one_table,
- Open_tables_state *backup)
+open_log_table(THD *thd, TABLE_LIST *one_table, Open_tables_backup *backup)
{
- uint flags= ( MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK |
+ uint flags= ( MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK |
MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY |
- MYSQL_LOCK_IGNORE_FLUSH |
- MYSQL_LOCK_PERF_SCHEMA);
+ MYSQL_OPEN_IGNORE_FLUSH |
+ MYSQL_LOCK_IGNORE_TIMEOUT |
+ MYSQL_LOCK_LOG_TABLE);
TABLE *table;
/* Save value that is changed in mysql_lock_tables() */
ulonglong save_utime_after_lock= thd->utime_after_lock;
- DBUG_ENTER("open_performance_schema_table");
+ DBUG_ENTER("open_log_table");
thd->reset_n_backup_open_tables_state(backup);
if ((table= open_ltable(thd, one_table, one_table->lock_type, flags)))
{
- DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_PERFORMANCE);
+ DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_LOG);
/* Make sure all columns get assigned to a default value */
table->use_all_columns();
table->no_replicate= 1;
@@ -9731,71 +9882,22 @@ open_performance_schema_table(THD *thd, TABLE_LIST *one_table,
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
}
else
- {
- /*
- This can happen during a thd->kill or while we are trying to log
- data for a stored procedure/trigger and someone causes the table
- to be flushed (for example by creating a new trigger for the
- table)
- */
- close_thread_tables(thd);
thd->restore_backup_open_tables_state(backup);
- }
thd->utime_after_lock= save_utime_after_lock;
DBUG_RETURN(table);
}
/**
- Close a performance schema table.
- The last table opened by open_performance_schema_table()
+ Close a log table.
+ The last table opened by open_log_table()
is closed, then the thread context is restored.
@param thd The current thread
@param backup [in] the context to restore.
*/
-void close_performance_schema_table(THD *thd, Open_tables_state *backup)
+void close_log_table(THD *thd, Open_tables_backup *backup)
{
- bool found_old_table;
-
- /*
- If open_performance_schema_table() fails,
- this function should not be called.
- */
- DBUG_ASSERT(thd->lock != NULL);
-
- /*
- Note:
- We do not create explicitly a separate transaction for the
- performance table I/O, but borrow the current transaction.
- lock + unlock will autocommit the change done in the
- performance schema table: this is the expected result.
- The current transaction should not be affected by this code.
- TODO: Note that if a transactional engine is used for log tables,
- this code will need to be revised, as a separate transaction
- might be needed.
- */
- mysql_unlock_tables(thd, thd->lock);
- thd->lock= 0;
-
- pthread_mutex_lock(&LOCK_open);
-
- found_old_table= false;
- /*
- Note that we need to hold LOCK_open while changing the
- open_tables list. Another thread may work on it.
- (See: remove_table_from_cache(), mysql_wait_completed_table())
- Closing a MERGE child before the parent would be fatal if the
- other thread tries to abort the MERGE lock in between.
- */
- while (thd->open_tables)
- found_old_table|= close_thread_table(thd, &thd->open_tables);
-
- if (found_old_table)
- broadcast_refresh();
-
- pthread_mutex_unlock(&LOCK_open);
-
- thd->restore_backup_open_tables_state(backup);
+ close_system_tables(thd, backup);
}
diff --git a/sql/sql_base.h b/sql/sql_base.h
new file mode 100644
index 00000000000..d49554d5473
--- /dev/null
+++ b/sql/sql_base.h
@@ -0,0 +1,648 @@
+/* Copyright (c) 2010, 2013, 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 SQL_BASE_INCLUDED
+#define SQL_BASE_INCLUDED
+
+#include "unireg.h" // REQUIRED: for other includes
+#include "sql_trigger.h" /* trg_event_type */
+#include "sql_class.h" /* enum_mark_columns */
+#include "mysqld.h" /* key_map */
+
+class Item_ident;
+struct Name_resolution_context;
+class Open_table_context;
+class Open_tables_state;
+class Prelocking_strategy;
+struct TABLE_LIST;
+class THD;
+struct handlerton;
+struct TABLE;
+
+typedef class st_select_lex SELECT_LEX;
+
+typedef struct st_lock_param_type ALTER_PARTITION_PARAM_TYPE;
+
+/*
+ This enumeration type is used only by the function find_item_in_list
+ to return the info on how an item has been resolved against a list
+ of possibly aliased items.
+ The item can be resolved:
+ - against an alias name of the list's element (RESOLVED_AGAINST_ALIAS)
+ - against non-aliased field name of the list (RESOLVED_WITH_NO_ALIAS)
+ - against an aliased field name of the list (RESOLVED_BEHIND_ALIAS)
+ - ignoring the alias name in cases when SQL requires to ignore aliases
+ (e.g. when the resolved field reference contains a table name or
+ when the resolved item is an expression) (RESOLVED_IGNORING_ALIAS)
+*/
+enum enum_resolution_type {
+ NOT_RESOLVED=0,
+ RESOLVED_IGNORING_ALIAS,
+ RESOLVED_BEHIND_ALIAS,
+ RESOLVED_WITH_NO_ALIAS,
+ RESOLVED_AGAINST_ALIAS
+};
+
+enum find_item_error_report_type {REPORT_ALL_ERRORS, REPORT_EXCEPT_NOT_FOUND,
+ IGNORE_ERRORS, REPORT_EXCEPT_NON_UNIQUE,
+ IGNORE_EXCEPT_NON_UNIQUE};
+
+enum enum_tdc_remove_table_type {TDC_RT_REMOVE_ALL, TDC_RT_REMOVE_NOT_OWN,
+ TDC_RT_REMOVE_UNUSED,
+ TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE};
+
+/* bits for last argument to remove_table_from_cache() */
+#define RTFC_NO_FLAG 0x0000
+#define RTFC_OWNED_BY_THD_FLAG 0x0001
+#define RTFC_WAIT_OTHER_THREAD_FLAG 0x0002
+#define RTFC_CHECK_KILLED_FLAG 0x0004
+
+bool check_dup(const char *db, const char *name, TABLE_LIST *tables);
+extern mysql_mutex_t LOCK_open;
+bool table_cache_init(void);
+void table_cache_free(void);
+bool table_def_init(void);
+void table_def_free(void);
+void table_def_start_shutdown(void);
+void assign_new_table_id(TABLE_SHARE *share);
+uint cached_open_tables(void);
+uint cached_table_definitions(void);
+uint create_table_def_key(THD *thd, char *key,
+ const TABLE_LIST *table_list,
+ bool tmp_table);
+
+/**
+ Create a table cache key for non-temporary table.
+
+ @param key Buffer for key (must be at least NAME_LEN*2+2 bytes).
+ @param db Database name.
+ @param table_name Table name.
+
+ @return Length of key.
+
+ @sa create_table_def_key(thd, char *, table_list, bool)
+*/
+
+inline uint
+create_table_def_key(char *key, const char *db, const char *table_name)
+{
+ /*
+ In theory caller should ensure that both db and table_name are
+ not longer than NAME_LEN bytes. In practice we play safe to avoid
+ buffer overruns.
+ */
+ return (uint)(strmake(strmake(key, db, NAME_LEN) + 1, table_name,
+ NAME_LEN) - key + 1);
+}
+
+TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key,
+ uint key_length, uint db_flags, int *error,
+ my_hash_value_type hash_value);
+void release_table_share(TABLE_SHARE *share);
+TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name);
+
+TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update,
+ uint lock_flags);
+
+/* mysql_lock_tables() and open_table() flags bits */
+#define MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK 0x0001
+#define MYSQL_OPEN_IGNORE_FLUSH 0x0002
+#define MYSQL_OPEN_TEMPORARY_ONLY 0x0004
+#define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0008
+#define MYSQL_LOCK_LOG_TABLE 0x0010
+/**
+ Do not try to acquire a metadata lock on the table: we
+ already have one.
+*/
+#define MYSQL_OPEN_HAS_MDL_LOCK 0x0020
+/**
+ If in locked tables mode, ignore the locked tables and get
+ a new instance of the table.
+*/
+#define MYSQL_OPEN_GET_NEW_TABLE 0x0040
+/** Don't look up the table in the list of temporary tables. */
+#define MYSQL_OPEN_SKIP_TEMPORARY 0x0080
+/** Fail instead of waiting when conficting metadata lock is discovered. */
+#define MYSQL_OPEN_FAIL_ON_MDL_CONFLICT 0x0100
+/** Open tables using MDL_SHARED lock instead of one specified in parser. */
+#define MYSQL_OPEN_FORCE_SHARED_MDL 0x0200
+/**
+ Open tables using MDL_SHARED_HIGH_PRIO lock instead of one specified
+ in parser.
+*/
+#define MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL 0x0400
+/**
+ When opening or locking the table, use the maximum timeout
+ (LONG_TIMEOUT = 1 year) rather than the user-supplied timeout value.
+*/
+#define MYSQL_LOCK_IGNORE_TIMEOUT 0x0800
+/**
+ When acquiring "strong" (SNW, SNRW, X) metadata locks on tables to
+ be open do not acquire global and schema-scope IX locks.
+*/
+#define MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK 0x1000
+#define MYSQL_LOCK_NOT_TEMPORARY 0x2000
+#define MYSQL_OPEN_FOR_REPAIR 0x4000
+
+/** Please refer to the internals manual. */
+#define MYSQL_OPEN_REOPEN (MYSQL_OPEN_IGNORE_FLUSH |\
+ MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK |\
+ MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY |\
+ MYSQL_LOCK_IGNORE_TIMEOUT |\
+ MYSQL_OPEN_GET_NEW_TABLE |\
+ MYSQL_OPEN_SKIP_TEMPORARY |\
+ MYSQL_OPEN_HAS_MDL_LOCK)
+
+bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
+ Open_table_context *ot_ctx);
+bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias,
+ uint db_stat, uint prgflag,
+ uint ha_open_flags, TABLE *outparam, TABLE_LIST *table_desc,
+ MEM_ROOT *mem_root);
+
+bool get_key_map_from_key_list(key_map *map, TABLE *table,
+ List<String> *index_list);
+TABLE *open_table_uncached(THD *thd, const char *path, const char *db,
+ const char *table_name,
+ bool add_to_temporary_tables_list);
+TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name);
+TABLE *find_write_locked_table(TABLE *list, const char *db,
+ const char *table_name);
+thr_lock_type read_lock_type_for_table(THD *thd,
+ Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list);
+
+my_bool mysql_rm_tmp_tables(void);
+bool rm_temporary_table(handlerton *base, const char *path);
+void close_tables_for_reopen(THD *thd, TABLE_LIST **tables,
+ const MDL_savepoint &start_of_statement_svp);
+TABLE_LIST *find_table_in_list(TABLE_LIST *table,
+ TABLE_LIST *TABLE_LIST::*link,
+ const char *db_name,
+ const char *table_name);
+TABLE *find_temporary_table(THD *thd, const char *db, const char *table_name);
+TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl);
+TABLE *find_temporary_table(THD *thd, const char *table_key,
+ uint table_key_length);
+void close_thread_tables(THD *thd);
+bool fill_record_n_invoke_before_triggers(THD *thd, List<Item> &fields,
+ List<Item> &values,
+ bool ignore_errors,
+ Table_triggers_list *triggers,
+ enum trg_event_type event);
+bool fill_record_n_invoke_before_triggers(THD *thd, Field **field,
+ List<Item> &values,
+ bool ignore_errors,
+ Table_triggers_list *triggers,
+ enum trg_event_type event);
+bool insert_fields(THD *thd, Name_resolution_context *context,
+ const char *db_name, const char *table_name,
+ List_iterator<Item> *it, bool any_privileges);
+void make_leaves_list(List<TABLE_LIST> &list, TABLE_LIST *tables,
+ bool full_table_list, TABLE_LIST *boundary);
+int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
+ List<Item> *sum_func_list, uint wild_num);
+bool setup_fields(THD *thd, Item** ref_pointer_array,
+ List<Item> &item, enum_mark_columns mark_used_columns,
+ List<Item> *sum_func_list, bool allow_sum_func);
+void unfix_fields(List<Item> &items);
+bool fill_record(THD *thd, Field **field, List<Item> &values,
+ bool ignore_errors, bool use_value);
+
+Field *
+find_field_in_tables(THD *thd, Item_ident *item,
+ TABLE_LIST *first_table, TABLE_LIST *last_table,
+ Item **ref, find_item_error_report_type report_error,
+ 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 *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,
+ bool allow_rowid, uint *cached_field_index_ptr);
+Field *
+find_field_in_table_sef(TABLE *table, const char *name);
+Item ** find_item_in_list(Item *item, List<Item> &items, uint *counter,
+ find_item_error_report_type report_error,
+ enum_resolution_type *resolution);
+bool setup_tables(THD *thd, Name_resolution_context *context,
+ List<TABLE_LIST> *from_clause, TABLE_LIST *tables,
+ List<TABLE_LIST> &leaves, bool select_insert,
+ bool full_table_list);
+bool setup_tables_and_check_access(THD *thd,
+ Name_resolution_context *context,
+ List<TABLE_LIST> *from_clause,
+ TABLE_LIST *tables,
+ List<TABLE_LIST> &leaves,
+ bool select_insert,
+ ulong want_access_first,
+ ulong want_access,
+ bool full_table_list);
+bool wait_while_table_is_used(THD *thd, TABLE *table,
+ enum ha_extra_function function,
+ enum_tdc_remove_table_type remove_type=
+ TDC_RT_REMOVE_NOT_OWN);
+
+void drop_open_table(THD *thd, TABLE *table, const char *db_name,
+ const char *table_name);
+void update_non_unique_table_error(TABLE_LIST *update,
+ const char *operation,
+ TABLE_LIST *duplicate);
+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);
+int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order);
+bool lock_table_names(THD *thd, TABLE_LIST *table_list,
+ TABLE_LIST *table_list_end, ulong lock_wait_timeout,
+ uint flags);
+bool open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags,
+ Prelocking_strategy *prelocking_strategy);
+/* open_and_lock_tables with optional derived handling */
+bool open_and_lock_tables(THD *thd, TABLE_LIST *tables,
+ bool derived, uint flags,
+ Prelocking_strategy *prelocking_strategy);
+/* simple open_and_lock_tables without derived handling for single table */
+TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l,
+ thr_lock_type lock_type, uint flags,
+ Prelocking_strategy *prelocking_strategy);
+bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags,
+ uint dt_phases);
+bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags);
+int decide_logging_format(THD *thd, TABLE_LIST *tables);
+void free_io_cache(TABLE *entry);
+void intern_close_table(TABLE *entry);
+bool close_thread_table(THD *thd, TABLE **table_ptr);
+bool close_temporary_tables(THD *thd);
+TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
+ bool check_alias);
+int drop_temporary_table(THD *thd, TABLE_LIST *table_list, bool *is_trans);
+void close_temporary_table(THD *thd, TABLE *table, bool free_share,
+ bool delete_table);
+void close_temporary(TABLE *table, bool free_share, bool delete_table);
+bool rename_temporary_table(THD* thd, TABLE *table, const char *new_db,
+ const char *table_name);
+bool is_equal(const LEX_STRING *a, const LEX_STRING *b);
+
+/* Functions to work with system tables. */
+bool open_system_tables_for_read(THD *thd, TABLE_LIST *table_list,
+ Open_tables_backup *backup);
+void close_system_tables(THD *thd, Open_tables_backup *backup);
+void close_mysql_tables(THD *thd);
+TABLE *open_system_table_for_update(THD *thd, TABLE_LIST *one_table);
+TABLE *open_log_table(THD *thd, TABLE_LIST *one_table, Open_tables_backup *backup);
+void close_log_table(THD *thd, Open_tables_backup *backup);
+
+TABLE *open_performance_schema_table(THD *thd, TABLE_LIST *one_table,
+ Open_tables_state *backup);
+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);
+void close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
+ ha_extra_function extra);
+OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild);
+void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
+ const char *db, const char *table_name,
+ bool has_lock);
+bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias,
+ char *cache_key, uint cache_key_length,
+ MEM_ROOT *mem_root, uint flags);
+void tdc_flush_unused_tables();
+TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db,
+ const char *table_name,
+ bool no_error);
+void mark_tmp_table_for_reuse(TABLE *table);
+bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool fast_check,
+ bool *exists);
+int update_virtual_fields(THD *thd, TABLE *table,
+ enum enum_vcol_update_mode vcol_update_mode= VCOL_UPDATE_FOR_READ);
+int dynamic_column_error_message(enum_dyncol_func_result rc);
+
+extern TABLE *unused_tables;
+extern Item **not_found_item;
+extern Field *not_found_field;
+extern Field *view_ref_found;
+extern HASH table_def_cache;
+
+/**
+ clean/setup table fields and map.
+
+ @param table TABLE structure pointer (which should be setup)
+ @param table_list TABLE_LIST structure pointer (owner of TABLE)
+ @param tablenr table number
+*/
+
+
+inline void setup_table_map(TABLE *table, TABLE_LIST *table_list, uint tablenr)
+{
+ table->used_fields= 0;
+ table_list->reset_const_table();
+ table->null_row= 0;
+ table->status= STATUS_NO_RECORD;
+ table->maybe_null= table_list->outer_join;
+ TABLE_LIST *embedding= table_list->embedding;
+ while (!table->maybe_null && embedding)
+ {
+ table->maybe_null= embedding->outer_join;
+ embedding= embedding->embedding;
+ }
+ table->tablenr= tablenr;
+ table->map= (table_map) 1 << tablenr;
+ table->force_index= table_list->force_index;
+ table->force_index_order= table->force_index_group= 0;
+ table->covering_keys= table->s->keys_for_keyread;
+ table->merge_keys.clear_all();
+ TABLE_LIST *orig= table_list->select_lex ?
+ table_list->select_lex->master_unit()->derived : 0;
+ if (!orig || !orig->is_merged_derived())
+ {
+ /* Tables merged from derived were set up already.*/
+ table->covering_keys= table->s->keys_for_keyread;
+ table->merge_keys.clear_all();
+ }
+}
+
+inline TABLE_LIST *find_table_in_global_list(TABLE_LIST *table,
+ const char *db_name,
+ const char *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, Item **ref_pointer_array,
+ List<Item> &item,
+ enum_mark_columns mark_used_columns,
+ 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,
+ sum_func_list, allow_sum_func);
+ thd->lex->select_lex.no_wrap_view_item= FALSE;
+ return res;
+}
+
+/**
+ An abstract class for a strategy specifying how the prelocking
+ algorithm should extend the prelocking set while processing
+ already existing elements in the set.
+*/
+
+class Prelocking_strategy
+{
+public:
+ virtual ~Prelocking_strategy() { }
+
+ virtual bool handle_routine(THD *thd, Query_tables_list *prelocking_ctx,
+ Sroutine_hash_entry *rt, sp_head *sp,
+ bool *need_prelocking) = 0;
+ virtual bool handle_table(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list, bool *need_prelocking) = 0;
+ virtual bool handle_view(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list, bool *need_prelocking)= 0;
+};
+
+
+/**
+ A Strategy for prelocking algorithm suitable for DML statements.
+
+ Ensures that all tables used by all statement's SF/SP/triggers and
+ required for foreign key checks are prelocked and SF/SPs used are
+ cached.
+*/
+
+class DML_prelocking_strategy : public Prelocking_strategy
+{
+public:
+ virtual bool handle_routine(THD *thd, Query_tables_list *prelocking_ctx,
+ Sroutine_hash_entry *rt, sp_head *sp,
+ bool *need_prelocking);
+ virtual bool handle_table(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list, bool *need_prelocking);
+ virtual bool handle_view(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list, bool *need_prelocking);
+};
+
+
+/**
+ A strategy for prelocking algorithm to be used for LOCK TABLES
+ statement.
+*/
+
+class Lock_tables_prelocking_strategy : public DML_prelocking_strategy
+{
+ virtual bool handle_table(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list, bool *need_prelocking);
+};
+
+
+/**
+ Strategy for prelocking algorithm to be used for ALTER TABLE statements.
+
+ Unlike DML or LOCK TABLES strategy, it doesn't
+ prelock triggers, views or stored routines, since they are not
+ used during ALTER.
+*/
+
+class Alter_table_prelocking_strategy : public Prelocking_strategy
+{
+public:
+
+ Alter_table_prelocking_strategy(Alter_info *alter_info)
+ : m_alter_info(alter_info)
+ {}
+
+ virtual bool handle_routine(THD *thd, Query_tables_list *prelocking_ctx,
+ Sroutine_hash_entry *rt, sp_head *sp,
+ bool *need_prelocking);
+ virtual bool handle_table(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list, bool *need_prelocking);
+ virtual bool handle_view(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list, bool *need_prelocking);
+
+private:
+ Alter_info *m_alter_info;
+};
+
+
+inline bool
+open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags)
+{
+ DML_prelocking_strategy prelocking_strategy;
+
+ return open_tables(thd, tables, counter, flags, &prelocking_strategy);
+}
+
+
+inline TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l,
+ thr_lock_type lock_type, uint flags)
+{
+ DML_prelocking_strategy prelocking_strategy;
+
+ return open_n_lock_single_table(thd, table_l, lock_type, flags,
+ &prelocking_strategy);
+}
+
+
+/* open_and_lock_tables with derived handling */
+inline bool open_and_lock_tables(THD *thd, TABLE_LIST *tables,
+ bool derived, uint flags)
+{
+ DML_prelocking_strategy prelocking_strategy;
+
+ return open_and_lock_tables(thd, tables, derived, flags,
+ &prelocking_strategy);
+}
+
+
+/**
+ A context of open_tables() function, used to recover
+ from a failed open_table() or open_routine() attempt.
+*/
+
+class Open_table_context
+{
+public:
+ enum enum_open_table_action
+ {
+ OT_NO_ACTION= 0,
+ OT_BACKOFF_AND_RETRY,
+ OT_REOPEN_TABLES,
+ OT_DISCOVER,
+ OT_REPAIR
+ };
+ Open_table_context(THD *thd, uint flags);
+
+ bool recover_from_failed_open();
+ bool request_backoff_action(enum_open_table_action action_arg,
+ TABLE_LIST *table);
+
+ bool can_recover_from_failed_open() const
+ { return m_action != OT_NO_ACTION; }
+
+ /**
+ When doing a back-off, we close all tables acquired by this
+ statement. Return an MDL savepoint taken at the beginning of
+ the statement, so that we can rollback to it before waiting on
+ locks.
+ */
+ const MDL_savepoint &start_of_statement_svp() const
+ {
+ return m_start_of_statement_svp;
+ }
+
+ inline ulong get_timeout() const
+ {
+ return m_timeout;
+ }
+
+ uint get_flags() const { return m_flags; }
+
+ /**
+ Set flag indicating that we have already acquired metadata lock
+ protecting this statement against GRL while opening tables.
+ */
+ void set_has_protection_against_grl()
+ {
+ m_has_protection_against_grl= TRUE;
+ }
+
+ bool has_protection_against_grl() const
+ {
+ return m_has_protection_against_grl;
+ }
+
+private:
+ /* THD for which tables are opened. */
+ THD *m_thd;
+ /**
+ For OT_DISCOVER and OT_REPAIR actions, the table list element for
+ the table which definition should be re-discovered or which
+ should be repaired.
+ */
+ TABLE_LIST *m_failed_table;
+ MDL_savepoint m_start_of_statement_svp;
+ /**
+ Lock timeout in seconds. Initialized to LONG_TIMEOUT when opening system
+ tables or to the "lock_wait_timeout" system variable for regular tables.
+ */
+ ulong m_timeout;
+ /* open_table() flags. */
+ uint m_flags;
+ /** Back off action. */
+ enum enum_open_table_action m_action;
+ /**
+ Whether we had any locks when this context was created.
+ If we did, they are from the previous statement of a transaction,
+ and we can't safely do back-off (and release them).
+ */
+ bool m_has_locks;
+ /**
+ Indicates that in the process of opening tables we have acquired
+ protection against global read lock.
+ */
+ bool m_has_protection_against_grl;
+};
+
+
+/**
+ This internal handler is used to trap ER_NO_SUCH_TABLE.
+*/
+
+class No_such_table_error_handler : public Internal_error_handler
+{
+public:
+ No_such_table_error_handler()
+ : m_handled_errors(0), m_unhandled_errors(0)
+ {}
+
+ bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl);
+
+ /**
+ Returns TRUE if one or more ER_NO_SUCH_TABLE errors have been
+ trapped and no other errors have been seen. FALSE otherwise.
+ */
+ bool safely_trapped_errors();
+
+private:
+ int m_handled_errors;
+ int m_unhandled_errors;
+};
+
+
+#endif /* SQL_BASE_INCLUDED */
diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc
index 58683a5f1e9..420bd0eb2f0 100644
--- a/sql/sql_binlog.cc
+++ b/sql/sql_binlog.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2005, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2005, 2013, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,13 +12,22 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "sql_binlog.h"
+#include "sql_parse.h" // check_global_access
+#include "sql_acl.h" // *_ACL
#include "rpl_rli.h"
#include "base64.h"
-
+#include "slave.h" // apply_event_and_update_pos
+#include "log_event.h" // Format_description_log_event,
+ // EVENT_LEN_OFFSET,
+ // EVENT_TYPE_OFFSET,
+ // FORMAT_DESCRIPTION_LOG_EVENT,
+ // START_EVENT_V3,
+ // Log_event_type,
+ // Log_event
/**
Execute a BINLOG statement.
@@ -53,11 +62,11 @@ void mysql_client_binlog_statement(THD* thd)
size_t decoded_len= base64_needed_decoded_length(coded_len);
/*
- thd->options will be changed when applying the event. But we don't expect
+ option_bits will be changed when applying the event. But we don't expect
it be changed permanently after BINLOG statement, so backup it first.
It will be restored at the end of this function.
*/
- ulonglong thd_options= thd->options;
+ ulonglong thd_options= thd->variables.option_bits;
/*
Allocation
@@ -74,7 +83,7 @@ void mysql_client_binlog_statement(THD* thd)
rli= thd->rli_fake;
if (!rli)
{
- rli= thd->rli_fake= new Relay_log_info;
+ rli= thd->rli_fake= new Relay_log_info(FALSE);
#ifdef HAVE_valgrind
rli->is_fake= TRUE;
#endif
@@ -98,7 +107,7 @@ void mysql_client_binlog_statement(THD* thd)
rli->relay_log.description_event_for_exec &&
buf))
{
- my_error(ER_OUTOFMEMORY, MYF(0), 1); /* needed 1 bytes */
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), 1); /* needed 1 bytes */
goto end;
}
@@ -216,7 +225,18 @@ void mysql_client_binlog_statement(THD* thd)
reporting.
*/
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+ ulonglong save_skip_replication=
+ thd->variables.option_bits & OPTION_SKIP_REPLICATION;
+ thd->variables.option_bits=
+ (thd->variables.option_bits & ~OPTION_SKIP_REPLICATION) |
+ (ev->flags & LOG_EVENT_SKIP_REPLICATION_F ?
+ OPTION_SKIP_REPLICATION : 0);
+
err= ev->apply_event(rli);
+
+ thd->variables.option_bits=
+ (thd->variables.option_bits & ~OPTION_SKIP_REPLICATION) |
+ save_skip_replication;
#else
err= 0;
#endif
@@ -246,8 +266,8 @@ void mysql_client_binlog_statement(THD* thd)
my_ok(thd);
end:
- thd->options= thd_options;
- rli->clear_tables_to_lock();
- my_free(buf, MYF(MY_ALLOW_ZERO_PTR));
+ thd->variables.option_bits= thd_options;
+ rli->slave_close_thread_tables(thd);
+ my_free(buf);
DBUG_VOID_RETURN;
}
diff --git a/sql/sql_binlog.h b/sql/sql_binlog.h
new file mode 100644
index 00000000000..3a6d561701a
--- /dev/null
+++ b/sql/sql_binlog.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_BINLOG_INCLUDED
+#define SQL_BINLOG_INCLUDED
+
+class THD;
+
+void mysql_client_binlog_statement(THD *thd);
+
+#endif /* SQL_BINLOG_INCLUDED */
diff --git a/sql/sql_bitmap.h b/sql/sql_bitmap.h
index f38307935ca..d286d8e1ef0 100644
--- a/sql/sql_bitmap.h
+++ b/sql/sql_bitmap.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2003-2006, 2008 MySQL AB
+/* Copyright (c) 2003, 2013, 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
@@ -11,7 +11,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
Implementation of a bitmap type.
@@ -19,6 +19,10 @@
also be able to use 32 or 64 bits bitmaps very efficiently
*/
+#ifndef SQL_BITMAP_INCLUDED
+#define SQL_BITMAP_INCLUDED
+
+#include <my_sys.h>
#include <my_bitmap.h>
template <uint default_width> class Bitmap
@@ -67,6 +71,7 @@ public:
bool is_subset(const Bitmap& map2) const { return bitmap_is_subset(&map, &map2.map); }
bool is_overlapping(const Bitmap& map2) const { return bitmap_is_overlapping(&map, &map2.map); }
bool operator==(const Bitmap& map2) const { return bitmap_cmp(&map, &map2.map); }
+ bool operator!=(const Bitmap& map2) const { return !(*this == map2); }
char *print(char *buf) const
{
char *s=buf;
@@ -130,16 +135,7 @@ template <> class Bitmap<64>
ulonglong map;
public:
Bitmap<64>() { }
-#if defined(__NETWARE__) || defined(__MWERKS__)
- /*
- Metwork compiler gives error on Bitmap<64>
- Changed to Bitmap, since in this case also it will proper construct
- this class
- */
- explicit Bitmap(uint prefix_to_set) { set_prefix(prefix_to_set); }
-#else
explicit Bitmap<64>(uint prefix_to_set) { set_prefix(prefix_to_set); }
-#endif
void init() { }
void init(uint prefix_to_set) { set_prefix(prefix_to_set); }
uint length() const { return 64; }
@@ -166,7 +162,7 @@ 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,1); return buf; }
+ char *print(char *buf) const { longlong2str(map,buf,16); return buf; }
ulonglong to_ulonglong() const { return map; }
class Iterator : public Table_map_iterator
{
@@ -186,3 +182,5 @@ public:
}
};
+
+#endif /* SQL_BITMAP_INCLUDED */
diff --git a/sql/sql_builtin.cc.in b/sql/sql_builtin.cc.in
index 3ba47e3a14e..63850650ac9 100644
--- a/sql/sql_builtin.cc.in
+++ b/sql/sql_builtin.cc.in
@@ -1,4 +1,4 @@
-/* Copyright (C) 2006 MySQL AB
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,10 +18,22 @@
typedef struct st_maria_plugin builtin_maria_plugin[];
-extern builtin_maria_plugin
- builtin_maria_binlog_plugin, builtin_maria_mysql_password_plugin@maria_plugin_defs@;
+#ifdef _MSC_VER
+extern "C"
+#else
+extern
+#endif
+builtin_maria_plugin
+ @mysql_mandatory_plugins@ @mysql_optional_plugins@
+ builtin_maria_binlog_plugin, builtin_maria_mysql_password_plugin;
-struct st_maria_plugin *mariadb_builtins[]=
+struct st_maria_plugin *mysql_optional_plugins[]=
{
- builtin_maria_binlog_plugin, builtin_maria_mysql_password_plugin@maria_plugin_defs@,(struct st_maria_plugin *)0
+ @mysql_optional_plugins@ 0
+};
+
+struct st_maria_plugin *mysql_mandatory_plugins[]=
+{
+ builtin_maria_binlog_plugin, builtin_maria_mysql_password_plugin,
+ @mysql_mandatory_plugins@ 0
};
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index b4a5020275a..ad0472cfc2c 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -1,5 +1,5 @@
-/*
- Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2013, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +12,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
Description of the query cache:
@@ -329,13 +328,23 @@ TODO list:
(This could be done with almost no speed penalty)
*/
-#include "mysql_priv.h"
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_priv.h"
+#include "sql_cache.h"
+#include "sql_parse.h" // check_table_access
+#include "tztime.h" // struct Time_zone
+#include "sql_acl.h" // SELECT_ACL
+#include "sql_base.h" // TMP_TABLE_KEY_EXTRA
+#include "debug_sync.h" // DEBUG_SYNC
#ifdef HAVE_QUERY_CACHE
#include <m_ctype.h>
#include <my_dir.h>
#include <hash.h>
#include "../storage/myisammrg/ha_myisammrg.h"
#include "../storage/myisammrg/myrg_def.h"
+#include "probes_mysql.h"
+#include "log_slow.h"
+#include "transaction.h"
const uchar *query_state_map;
@@ -344,18 +353,14 @@ const uchar *query_state_map;
#endif
#if !defined(EXTRA_DBUG) && !defined(DBUG_OFF)
-#define MUTEX_LOCK(M) { DBUG_PRINT("lock", ("mutex lock 0x%lx", (ulong)(M))); \
- pthread_mutex_lock(M);}
-#define MUTEX_UNLOCK(M) {DBUG_PRINT("lock", ("mutex unlock 0x%lx",\
- (ulong)(M))); pthread_mutex_unlock(M);}
#define RW_WLOCK(M) {DBUG_PRINT("lock", ("rwlock wlock 0x%lx",(ulong)(M))); \
- if (!rw_wrlock(M)) DBUG_PRINT("lock", ("rwlock wlock ok")); \
+ if (!mysql_rwlock_wrlock(M)) DBUG_PRINT("lock", ("rwlock wlock ok")); \
else DBUG_PRINT("lock", ("rwlock wlock FAILED %d", errno)); }
#define RW_RLOCK(M) {DBUG_PRINT("lock", ("rwlock rlock 0x%lx", (ulong)(M))); \
- if (!rw_rdlock(M)) DBUG_PRINT("lock", ("rwlock rlock ok")); \
+ if (!mysql_rwlock_rdlock(M)) DBUG_PRINT("lock", ("rwlock rlock ok")); \
else DBUG_PRINT("lock", ("rwlock wlock FAILED %d", errno)); }
#define RW_UNLOCK(M) {DBUG_PRINT("lock", ("rwlock unlock 0x%lx",(ulong)(M))); \
- if (!rw_unlock(M)) DBUG_PRINT("lock", ("rwlock unlock ok")); \
+ if (!mysql_rwlock_unlock(M)) DBUG_PRINT("lock", ("rwlock unlock ok")); \
else DBUG_PRINT("lock", ("rwlock unlock FAILED %d", errno)); }
#define BLOCK_LOCK_WR(B) {DBUG_PRINT("lock", ("%d LOCK_WR 0x%lx",\
__LINE__,(ulong)(B))); \
@@ -371,48 +376,10 @@ const uchar *query_state_map;
__LINE__,(ulong)(B)));B->query()->unlock_reading();}
#define DUMP(C) DBUG_EXECUTE("qcache", {\
(C)->cache_dump(); (C)->queries_dump();(C)->tables_dump();})
-
-
-/**
- Causes the thread to wait in a spin lock for a query kill signal.
- This function is used by the test frame work to identify race conditions.
-
- The signal is caught and ignored and the thread is not killed.
-*/
-
-static void debug_wait_for_kill(const char *info)
-{
- const char *prev_info;
- THD *thd;
- char buff[1024];
- DBUG_ENTER("debug_wait_for_kill");
-
- thd= current_thd;
- prev_info= thd->proc_info;
- thd->proc_info= info;
- sql_print_information("%s", info);
- while(!thd->killed)
- my_sleep(1000);
- thd->killed= NOT_KILLED;
- /*
- Remove the set debug variable, to ensure we don't get stuck on it again
- This is needed as for MyISAM, invalidate_table() may be called twice
- (Once from mysql_delete() and once from mi_update_status())
- */
- sprintf(buff, "-d,%s", info);
- DBUG_SET(buff);
- sql_print_information("Exit debug_wait_for_kill");
- thd->proc_info= prev_info;
-
- DBUG_VOID_RETURN;
-}
-
#else
-#define MUTEX_LOCK(M) pthread_mutex_lock(M)
-#define MUTEX_UNLOCK(M) pthread_mutex_unlock(M)
-#define RW_WLOCK(M) rw_wrlock(M)
-#define RW_RLOCK(M) rw_rdlock(M)
-#define RW_UNLOCK(M) rw_unlock(M)
+#define RW_WLOCK(M) mysql_rwlock_wrlock(M)
+#define RW_RLOCK(M) mysql_rwlock_rdlock(M)
+#define RW_UNLOCK(M) mysql_rwlock_unlock(M)
#define BLOCK_LOCK_WR(B) B->query()->lock_writing()
#define BLOCK_LOCK_RD(B) B->query()->lock_reading()
#define BLOCK_UNLOCK_WR(B) B->query()->unlock_writing()
@@ -420,10 +387,49 @@ static void debug_wait_for_kill(const char *info)
#define DUMP(C)
#endif
-const char *query_cache_type_names[]= { "OFF", "ON", "DEMAND",NullS };
-TYPELIB query_cache_type_typelib=
+
+/**
+ Macro that executes the requested action at a synchronization point
+ only if the thread has a associated THD session.
+*/
+#if defined(ENABLED_DEBUG_SYNC)
+#define QC_DEBUG_SYNC(name) \
+ do { \
+ THD *thd= current_thd; \
+ if (thd) \
+ DEBUG_SYNC(thd, name); \
+ } while (0)
+#else
+#define QC_DEBUG_SYNC(name)
+#endif
+
+
+/**
+ Thread state to be used when the query cache lock needs to be acquired.
+ Sets the thread state name in the constructor, resets on destructor.
+*/
+
+struct Query_cache_wait_state
{
- array_elements(query_cache_type_names)-1,"", query_cache_type_names, NULL
+ THD *m_thd;
+ const char *m_proc_info;
+
+ Query_cache_wait_state(THD *thd, const char *func,
+ const char *file, unsigned int line)
+ : m_thd(thd),
+ m_proc_info(NULL)
+ {
+ if (m_thd)
+ m_proc_info= set_thd_proc_info(m_thd,
+ "Waiting for query cache lock",
+ func, file, line);
+ }
+
+ ~Query_cache_wait_state()
+ {
+ if (m_thd)
+ set_thd_proc_info(m_thd, m_proc_info, NULL, NULL, 0);
+ }
};
@@ -461,6 +467,8 @@ static void make_base_query(String *new_query,
/* The following is guaranteed by the query_cache interface */
DBUG_ASSERT(query[query_length] == 0);
DBUG_ASSERT(!is_white_space(query[0]));
+ /* We do not support UCS2, UTF16, UTF32 as a client character set */
+ DBUG_ASSERT(current_thd->variables.character_set_client->mbminlen == 1);
new_query->length(0); // Don't copy anything from old buffer
if (new_query->realloc(query_length + additional_length))
@@ -585,24 +593,19 @@ void inline fix_local_query_cache_mode(THD *thd)
bool Query_cache::try_lock(THD *thd, Cache_try_lock_mode mode)
{
bool interrupt= TRUE;
- const char* old_proc_info;
+ Query_cache_wait_state wait_state(thd, __func__, __FILE__, __LINE__);
DBUG_ENTER("Query_cache::try_lock");
- old_proc_info= thd->proc_info;
- thd_proc_info(thd,"Waiting on query cache mutex");
-
- pthread_mutex_lock(&structure_guard_mutex);
- DBUG_EXECUTE_IF("status_wait_query_cache_mutex_sleep", {
- sleep(5);
- });
+ mysql_mutex_lock(&structure_guard_mutex);
+ DBUG_EXECUTE_IF("status_wait_query_cache_mutex_sleep", { sleep(5); });
if (m_cache_status == DISABLED)
{
- pthread_mutex_unlock(&structure_guard_mutex);
- thd->proc_info= old_proc_info;
+ mysql_mutex_unlock(&structure_guard_mutex);
DBUG_RETURN(TRUE);
}
m_requests_in_progress++;
fix_local_query_cache_mode(thd);
+
while (1)
{
if (m_cache_lock_status == Query_cache::UNLOCKED)
@@ -631,14 +634,14 @@ bool Query_cache::try_lock(THD *thd, Cache_try_lock_mode mode)
*/
if (mode == WAIT)
{
- pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
+ mysql_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
}
else if (mode == TIMEOUT)
{
struct timespec waittime;
set_timespec_nsec(waittime,(ulong)(50000000L)); /* Wait for 50 msec */
- int res= pthread_cond_timedwait(&COND_cache_status_changed,
- &structure_guard_mutex,&waittime);
+ int res= mysql_cond_timedwait(&COND_cache_status_changed,
+ &structure_guard_mutex, &waittime);
if (res == ETIMEDOUT)
break;
}
@@ -657,8 +660,7 @@ bool Query_cache::try_lock(THD *thd, Cache_try_lock_mode mode)
}
if (interrupt)
m_requests_in_progress--;
- pthread_mutex_unlock(&structure_guard_mutex);
- thd->proc_info = old_proc_info;
+ mysql_mutex_unlock(&structure_guard_mutex);
DBUG_RETURN(interrupt);
}
@@ -677,22 +679,23 @@ bool Query_cache::try_lock(THD *thd, Cache_try_lock_mode mode)
void Query_cache::lock_and_suspend(void)
{
+ THD *thd= current_thd;
+ Query_cache_wait_state wait_state(thd, __func__, __FILE__, __LINE__);
DBUG_ENTER("Query_cache::lock_and_suspend");
- pthread_mutex_lock(&structure_guard_mutex);
+ mysql_mutex_lock(&structure_guard_mutex);
m_requests_in_progress++;
while (m_cache_lock_status != Query_cache::UNLOCKED)
- pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
+ mysql_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
m_cache_lock_status= Query_cache::LOCKED_NO_WAIT;
#ifndef DBUG_OFF
/* Here thd may not be set during shutdown */
- THD *thd= current_thd;
if (thd)
m_cache_lock_thread_id= thd->thread_id;
#endif
/* Wake up everybody, a whole cache flush is starting! */
- pthread_cond_broadcast(&COND_cache_status_changed);
- pthread_mutex_unlock(&structure_guard_mutex);
+ mysql_cond_broadcast(&COND_cache_status_changed);
+ mysql_mutex_unlock(&structure_guard_mutex);
DBUG_VOID_RETURN;
}
@@ -707,18 +710,19 @@ void Query_cache::lock_and_suspend(void)
void Query_cache::lock(THD *thd)
{
+ Query_cache_wait_state wait_state(thd, __func__, __FILE__, __LINE__);
DBUG_ENTER("Query_cache::lock");
- pthread_mutex_lock(&structure_guard_mutex);
+ mysql_mutex_lock(&structure_guard_mutex);
m_requests_in_progress++;
fix_local_query_cache_mode(thd);
while (m_cache_lock_status != Query_cache::UNLOCKED)
- pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
+ mysql_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
m_cache_lock_status= Query_cache::LOCKED;
#ifndef DBUG_OFF
m_cache_lock_thread_id= thd->thread_id;
#endif
- pthread_mutex_unlock(&structure_guard_mutex);
+ mysql_mutex_unlock(&structure_guard_mutex);
DBUG_VOID_RETURN;
}
@@ -731,7 +735,7 @@ void Query_cache::lock(THD *thd)
void Query_cache::unlock(void)
{
DBUG_ENTER("Query_cache::unlock");
- pthread_mutex_lock(&structure_guard_mutex);
+ mysql_mutex_lock(&structure_guard_mutex);
#ifndef DBUG_OFF
/* Thd may not be set in resize() at mysqld start */
THD *thd= current_thd;
@@ -742,7 +746,7 @@ void Query_cache::unlock(void)
m_cache_lock_status == Query_cache::LOCKED_NO_WAIT);
m_cache_lock_status= Query_cache::UNLOCKED;
DBUG_PRINT("Query_cache",("Sending signal"));
- pthread_cond_signal(&COND_cache_status_changed);
+ mysql_cond_signal(&COND_cache_status_changed);
DBUG_ASSERT(m_requests_in_progress > 0);
m_requests_in_progress--;
if (m_requests_in_progress == 0 && m_cache_status == DISABLE_REQUEST)
@@ -751,7 +755,7 @@ void Query_cache::unlock(void)
free_cache();
m_cache_status= DISABLED;
}
- pthread_mutex_unlock(&structure_guard_mutex);
+ mysql_mutex_unlock(&structure_guard_mutex);
DBUG_VOID_RETURN;
}
@@ -827,18 +831,18 @@ void Query_cache_block::destroy()
DBUG_VOID_RETURN;
}
-inline uint Query_cache_block::headers_len()
+uint Query_cache_block::headers_len()
{
return (ALIGN_SIZE(sizeof(Query_cache_block_table)*n_tables) +
ALIGN_SIZE(sizeof(Query_cache_block)));
}
-inline uchar* Query_cache_block::data(void)
+uchar* Query_cache_block::data(void)
{
return (uchar*)( ((uchar*)this) + headers_len() );
}
-inline Query_cache_query * Query_cache_block::query()
+Query_cache_query * Query_cache_block::query()
{
#ifndef DBUG_OFF
if (type != QUERY)
@@ -847,7 +851,7 @@ inline Query_cache_query * Query_cache_block::query()
return (Query_cache_query *) data();
}
-inline Query_cache_table * Query_cache_block::table()
+Query_cache_table * Query_cache_block::table()
{
#ifndef DBUG_OFF
if (type != TABLE)
@@ -856,7 +860,7 @@ inline Query_cache_table * Query_cache_block::table()
return (Query_cache_table *) data();
}
-inline Query_cache_result * Query_cache_block::result()
+Query_cache_result * Query_cache_block::result()
{
#ifndef DBUG_OFF
if (type != RESULT && type != RES_CONT && type != RES_BEG &&
@@ -866,7 +870,7 @@ inline Query_cache_result * Query_cache_block::result()
return (Query_cache_result *) data();
}
-inline Query_cache_block_table * Query_cache_block::table(TABLE_COUNTER_TYPE n)
+Query_cache_block_table * Query_cache_block::table(TABLE_COUNTER_TYPE n)
{
return ((Query_cache_block_table *)
(((uchar*)this)+ALIGN_SIZE(sizeof(Query_cache_block)) +
@@ -919,7 +923,7 @@ inline void Query_cache_query::lock_writing()
bool Query_cache_query::try_lock_writing()
{
DBUG_ENTER("Query_cache_block::try_lock_writing");
- if (rw_trywrlock(&lock)!=0)
+ if (mysql_rwlock_trywrlock(&lock) != 0)
{
DBUG_PRINT("info", ("can't lock rwlock"));
DBUG_RETURN(0);
@@ -951,7 +955,7 @@ void Query_cache_query::init_n_lock()
{
DBUG_ENTER("Query_cache_query::init_n_lock");
res=0; wri = 0; len = 0;
- my_rwlock_init(&lock, NULL);
+ mysql_rwlock_init(key_rwlock_query_cache_query_lock, &lock);
lock_writing();
DBUG_PRINT("qcache", ("inited & locked query for block 0x%lx",
(long) (((uchar*) this) -
@@ -971,7 +975,7 @@ void Query_cache_query::unlock_n_destroy()
active semaphore
*/
this->unlock_writing();
- rwlock_destroy(&lock);
+ mysql_rwlock_destroy(&lock);
DBUG_VOID_RETURN;
}
@@ -997,19 +1001,20 @@ uchar *query_cache_query_get_key(const uchar *record, size_t *length,
Note on double-check locking (DCL) usage.
Below, in query_cache_insert(), query_cache_abort() and
- query_cache_end_of_result() we use what is called double-check
- locking (DCL) for NET::query_cache_query. I.e. we test it first
- without a lock, and, if positive, test again under the lock.
+ Query_cache::end_of_result() we use what is called double-check
+ locking (DCL) for Query_cache_tls::first_query_block.
+ I.e. we test it first without a lock, and, if positive, test again
+ under the lock.
- This means that if we see 'NET::query_cache_query == 0' without a
+ This means that if we see 'first_query_block == 0' without a
lock we will skip the operation. But this is safe here: when we
started to cache a query, we called Query_cache::store_query(), and
- NET::query_cache_query was set to non-zero in this thread (and the
+ 'first_query_block' was set to non-zero in this thread (and the
thread always sees results of its memory operations, mutex or not).
- If later we see 'NET::query_cache_query == 0' without locking a
+ If later we see 'first_query_block == 0' without locking a
mutex, that may only mean that some other thread have reset it by
invalidating the query. Skipping the operation in this case is the
- right thing to do, as NET::query_cache_query won't get non-zero for
+ right thing to do, as first_query_block won't get non-zero for
this query again.
See also comments in Query_cache::store_query() and
@@ -1018,65 +1023,75 @@ uchar *query_cache_query_get_key(const uchar *record, size_t *length,
NOTE, however, that double-check locking is not applicable in
'invalidate' functions, as we may erroneously skip invalidation,
because the thread doing invalidation may never see non-zero
- NET::query_cache_query.
+ 'first_query_block'.
*/
-void query_cache_init_query(NET *net)
+/**
+ libmysql convenience wrapper to insert data into query cache.
+*/
+void query_cache_insert(const char *packet, ulong length,
+ unsigned pkt_nr)
{
+ THD *thd= current_thd;
+
/*
- It is safe to initialize 'NET::query_cache_query' without a lock
- here, because before it will be accessed from different threads it
- will be set in this thread under a lock, and access from the same
- thread is always safe.
+ Current_thd can be NULL when a new connection is immediately ended
+ due to "Too many connections". thd->store_globals() has not been
+ called at this time and hence my_pthread_setspecific_ptr(THR_THD,
+ this) has not been called for this thread.
*/
- net->query_cache_query= 0;
+
+ if (!thd)
+ return;
+
+ query_cache.insert(&thd->query_cache_tls,
+ packet, length,
+ pkt_nr);
}
-/*
+/**
Insert the packet into the query cache.
*/
-void query_cache_insert(NET *net, const char *packet, ulong length)
+void
+Query_cache::insert(Query_cache_tls *query_cache_tls,
+ const char *packet, ulong length,
+ unsigned pkt_nr)
{
- DBUG_ENTER("query_cache_insert");
+ DBUG_ENTER("Query_cache::insert");
- if (net->query_cache_query == 0)
+ /* First we check if query cache is disable without doing a mutex lock */
+ if (is_disabled() || query_cache_tls->first_query_block == NULL)
DBUG_VOID_RETURN;
DBUG_ASSERT(current_thd);
- DBUG_EXECUTE_IF("wait_in_query_cache_insert",
- debug_wait_for_kill("wait_in_query_cache_insert"); );
-
- /* First we check if query cache is disable without doing a mutex lock */
- if(query_cache.is_disabled())
- DBUG_VOID_RETURN;
+ QC_DEBUG_SYNC("wait_in_query_cache_insert");
/*
Lock the cache with try_lock(). try_lock() will fail if
cache was disabled between the above test and lock.
*/
- if (query_cache.try_lock(current_thd, Query_cache::WAIT))
+ if (try_lock(current_thd, Query_cache::WAIT))
DBUG_VOID_RETURN;
- Query_cache_block *query_block= (Query_cache_block*)net->query_cache_query;
- if (!query_block)
+ Query_cache_block *query_block = query_cache_tls->first_query_block;
+ if (query_block == NULL)
{
/*
We lost the writer and the currently processed query has been
invalidated; there is nothing left to do.
*/
- query_cache.unlock();
+ unlock();
DBUG_VOID_RETURN;
}
-
BLOCK_LOCK_WR(query_block);
Query_cache_query *header= query_block->query();
Query_cache_block *result= header->result();
- DUMP(&query_cache);
+ DUMP(this);
DBUG_PRINT("qcache", ("insert packet %lu bytes long",length));
/*
@@ -1084,8 +1099,8 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
still need structure_guard_mutex to free the query, and therefore unlock
it later in this function.
*/
- if (!query_cache.append_result_data(&result, length, (uchar*) packet,
- query_block))
+ if (!append_result_data(&result, length, (uchar*) packet,
+ query_block))
{
DBUG_PRINT("warning", ("Can't append data"));
header->result(result);
@@ -1094,89 +1109,84 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
query_cache.free_query(query_block);
query_cache.refused++;
// append_result_data no success => we need unlock
- query_cache.unlock();
+ unlock();
DBUG_VOID_RETURN;
}
header->result(result);
- header->last_pkt_nr= net->pkt_nr;
+ header->last_pkt_nr= pkt_nr;
BLOCK_UNLOCK_WR(query_block);
- DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0););
+ DBUG_EXECUTE("check_querycache",check_integrity(0););
DBUG_VOID_RETURN;
}
-void query_cache_abort(NET *net)
+void
+Query_cache::abort(Query_cache_tls *query_cache_tls)
{
THD *thd;
DBUG_ENTER("query_cache_abort");
/* See the comment on double-check locking usage above. */
- if (net->query_cache_query == 0)
+ if (is_disabled() || query_cache_tls->first_query_block == NULL)
DBUG_VOID_RETURN;
- if (query_cache.try_lock(current_thd, Query_cache::WAIT))
- {
- net->query_cache_query = 0;
+ if (try_lock(current_thd, Query_cache::WAIT))
DBUG_VOID_RETURN;
- }
/*
While we were waiting another thread might have changed the status
of the writer. Make sure the writer still exists before continue.
*/
- Query_cache_block *query_block= ((Query_cache_block*)
- net->query_cache_query);
+ Query_cache_block *query_block= query_cache_tls->first_query_block;
if (query_block)
{
thd= current_thd;
thd_proc_info(thd, "storing result in query cache");
- DUMP(&query_cache);
+ DUMP(this);
BLOCK_LOCK_WR(query_block);
// The following call will remove the lock on query_block
- query_cache.free_query(query_block);
- net->query_cache_query= 0;
- DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
+ free_query(query_block);
+ query_cache_tls->first_query_block= NULL;
+ DBUG_EXECUTE("check_querycache", check_integrity(1););
}
- DBUG_ASSERT(!net->query_cache_query);
- query_cache.unlock();
+ unlock();
+
DBUG_VOID_RETURN;
}
-void query_cache_end_of_result(THD *thd)
+void Query_cache::end_of_result(THD *thd)
{
Query_cache_block *query_block;
- DBUG_ENTER("query_cache_end_of_result");
+ Query_cache_tls *query_cache_tls= &thd->query_cache_tls;
+ ulonglong limit_found_rows= thd->limit_found_rows;
+ DBUG_ENTER("Query_cache::end_of_result");
/* See the comment on double-check locking usage above. */
- if (thd->net.query_cache_query == 0)
+ if (query_cache_tls->first_query_block == NULL)
DBUG_VOID_RETURN;
/* Ensure that only complete results are cached. */
- DBUG_ASSERT(thd->main_da.is_eof());
+ DBUG_ASSERT(thd->stmt_da->is_eof());
if (thd->killed)
{
- query_cache_abort(&thd->net);
+ query_cache_abort(&thd->query_cache_tls);
DBUG_VOID_RETURN;
}
#ifdef EMBEDDED_LIBRARY
- query_cache_insert(&thd->net, (char*)thd,
- emb_count_querycache_size(thd));
+ insert(query_cache_tls, (char*)thd,
+ emb_count_querycache_size(thd), 0);
#endif
- if (query_cache.try_lock(thd, Query_cache::WAIT))
- {
- thd->net.query_cache_query= 0;
+ if (try_lock(thd, Query_cache::WAIT))
DBUG_VOID_RETURN;
- }
- /* thd->net.query_cache_query may have changed during resize */
- query_block= ((Query_cache_block*) thd->net.query_cache_query);
+ query_block= query_cache_tls->first_query_block;
if (query_block)
{
/*
@@ -1185,7 +1195,7 @@ void query_cache_end_of_result(THD *thd)
block, the writer should be dropped.
*/
thd_proc_info(thd, "storing result in query cache");
- DUMP(&query_cache);
+ DUMP(this);
BLOCK_LOCK_WR(query_block);
Query_cache_query *header= query_block->query();
Query_cache_block *last_result_block;
@@ -1201,9 +1211,9 @@ void query_cache_end_of_result(THD *thd)
to this function. In the release version that query should be ignored
and removed from QC.
*/
- query_cache.free_query(query_block);
- thd->net.query_cache_query= 0;
- query_cache.unlock();
+ DBUG_ASSERT(0);
+ free_query(query_block);
+ unlock();
DBUG_VOID_RETURN;
}
last_result_block= header->result()->prev;
@@ -1212,17 +1222,17 @@ void query_cache_end_of_result(THD *thd)
if (last_result_block->length >= query_cache.min_allocation_unit + len)
query_cache.split_block(last_result_block,len);
- header->found_rows(current_thd->limit_found_rows);
+ header->found_rows(limit_found_rows);
header->result()->type= Query_cache_block::RESULT;
/* Drop the writer. */
header->writer(0);
- thd->net.query_cache_query= 0;
+ query_cache_tls->first_query_block= NULL;
BLOCK_UNLOCK_WR(query_block);
- DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
-
+ DBUG_EXECUTE("check_querycache", check_integrity(1););
}
- query_cache.unlock();
+
+ unlock();
DBUG_VOID_RETURN;
}
@@ -1284,6 +1294,7 @@ ulong Query_cache::resize(ulong query_cache_size_arg)
if (global_system_variables.query_cache_type == 0)
{
+ DBUG_ASSERT(query_cache_size_arg == 0);
if (query_cache_size_arg != 0)
my_error(ER_QUERY_CACHE_IS_DISABLED, MYF(0));
DBUG_RETURN(0);
@@ -1302,19 +1313,20 @@ ulong Query_cache::resize(ulong query_cache_size_arg)
{
BLOCK_LOCK_WR(block);
Query_cache_query *query= block->query();
- if (query && query->writer())
+ if (query->writer())
{
/*
- Drop the writer; this will cancel any attempts to store
+ Drop the writer; this will cancel any attempts to store
the processed statement associated with this writer.
*/
- query->writer()->query_cache_query= 0;
+ query->writer()->first_query_block= NULL;
query->writer(0);
refused++;
}
- BLOCK_UNLOCK_WR(block);
+ query->unlock_n_destroy();
block= block->next;
} while (block != queries_blocks);
+ queries_blocks= NULL; // avoid second destroying by free_cache
}
free_cache();
@@ -1340,9 +1352,10 @@ ulong Query_cache::resize(ulong query_cache_size_arg)
ulong Query_cache::set_min_res_unit(ulong size)
{
+ DBUG_ASSERT(size % 8 == 0);
if (size < min_allocation_unit)
- size= min_allocation_unit;
- return (min_result_data_size= ALIGN_SIZE(size));
+ size= ALIGN_SIZE(min_allocation_unit);
+ return (min_result_data_size= size);
}
@@ -1393,10 +1406,12 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
protocol (COM_EXECUTE) cannot be served to statements asking for results
in the text protocol (COM_QUERY) and vice-versa.
*/
- flags.result_in_binary_protocol= (unsigned int) thd->protocol->type();
+ flags.protocol_type= (unsigned int) thd->protocol->type();
+ /* PROTOCOL_LOCAL results are not cached. */
+ DBUG_ASSERT(flags.protocol_type != (unsigned int) Protocol::PROTOCOL_LOCAL);
flags.more_results_exists= test(thd->server_status &
SERVER_MORE_RESULTS_EXISTS);
- flags.in_trans= test(thd->server_status & SERVER_STATUS_IN_TRANS);
+ flags.in_trans= thd->in_active_multi_stmt_transaction();
flags.autocommit= test(thd->server_status & SERVER_STATUS_AUTOCOMMIT);
flags.pkt_nr= net->pkt_nr;
flags.character_set_client_num=
@@ -1418,11 +1433,11 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
DBUG_PRINT("qcache", ("\
long %d, 4.1: %d, bin_proto: %d, more results %d, pkt_nr: %d, \
CS client: %u, CS result: %u, CS conn: %u, limit: %lu, TZ: 0x%lx, \
-sql mode: 0x%lx, sort len: %lu, conncat len: %lu, div_precision: %lu, \
+sql mode: 0x%llx, sort len: %lu, conncat len: %lu, div_precision: %lu, \
def_week_frmt: %lu, in_trans: %d, autocommit: %d",
(int)flags.client_long_flag,
(int)flags.client_protocol_41,
- (int)flags.result_in_binary_protocol,
+ (int)flags.protocol_type,
(int)flags.more_results_exists,
flags.pkt_nr,
flags.character_set_client_num,
@@ -1497,7 +1512,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
/* Check if another thread is processing the same query? */
Query_cache_block *competitor = (Query_cache_block *)
- hash_search(&queries, (uchar*) query, tot_length);
+ my_hash_search(&queries, (uchar*) query, tot_length);
DBUG_PRINT("qcache", ("competitor 0x%lx", (ulong) competitor));
if (competitor == 0)
{
@@ -1522,11 +1537,11 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
unlock();
goto end;
}
- if (!register_all_tables(query_block, tables_used, local_tables))
+ if (!register_all_tables(thd, query_block, tables_used, local_tables))
{
refused++;
DBUG_PRINT("warning", ("tables list including failed"));
- hash_delete(&queries, (uchar *) query_block);
+ my_hash_delete(&queries, (uchar *) query_block);
header->unlock_n_destroy();
free_memory_block(query_block);
unlock();
@@ -1535,8 +1550,8 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
double_linked_list_simple_include(query_block, &queries_blocks);
inserts++;
queries_in_cache++;
- net->query_cache_query= (uchar*) query_block;
- header->writer(net);
+ thd->query_cache_tls.first_query_block= query_block;
+ header->writer(&thd->query_cache_tls);
header->tables_type(tables_type);
unlock();
@@ -1623,14 +1638,18 @@ send_data_in_chunks(NET *net, const uchar *packet, ulong len)
Check if the query is in the cache. If it was cached, send it
to the user.
- RESULTS
- 0 Query was not cached.
- 1 The query was cached and user was sent the result.
- -1 The query was cached but we didn't have rights to use it.
- No error is sent to the client yet.
+ @param thd Pointer to the thread handler
+ @param org_sql A pointer to the sql statement *
+ @param query_length Length of the statement in characters
- NOTE
- This method requires that sql points to allocated memory of size:
+ @return status code
+ @retval 0 Query was not cached.
+ @retval 1 The query was cached and user was sent the result.
+ @retval -1 The query was cached but we didn't have rights to use it.
+
+ 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;
*/
@@ -1650,13 +1669,13 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length)
DBUG_ENTER("Query_cache::send_result_to_client");
/*
- Testing 'query_cache_size' without a lock here is safe: the thing
+ Testing without a lock here is safe: the thing
we may loose is that the query won't be served from cache, but we
save on mutex locking in the case when query cache is disabled.
See also a note on double-check locking usage above.
*/
- if (is_disabled() || thd->locked_tables ||
+ if (is_disabled() || thd->locked_tables_mode ||
thd->variables.query_cache_type == 0)
goto err;
@@ -1670,8 +1689,6 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length)
goto err;
}
- DBUG_ASSERT(query_cache_size != 0); // otherwise cache would be disabled
-
thd->query_cache_is_applicable= 1;
sql= org_sql; sql_end= sql + query_length;
@@ -1798,16 +1815,13 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length)
goto err;
if (query_cache_size == 0)
+ {
+ thd->query_cache_is_applicable= 0; // Query can't be cached
goto err_unlock;
-
- /*
- Check that we haven't forgot to reset the query cache variables;
- make sure there are no attached query cache writer to this thread.
- */
- DBUG_ASSERT(thd->net.query_cache_query == 0);
+ }
Query_cache_block *query_block;
- if (opt_query_cache_strip_comments)
+ if (thd->variables.query_cache_strip_comments)
{
if (found_brace)
sql= found_brace;
@@ -1845,10 +1859,10 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length)
flags.client_long_flag= test(thd->client_capabilities & CLIENT_LONG_FLAG);
flags.client_protocol_41= test(thd->client_capabilities &
CLIENT_PROTOCOL_41);
- flags.result_in_binary_protocol= (unsigned int)thd->protocol->type();
+ flags.protocol_type= (unsigned int) thd->protocol->type();
flags.more_results_exists= test(thd->server_status &
SERVER_MORE_RESULTS_EXISTS);
- flags.in_trans= test(thd->server_status & SERVER_STATUS_IN_TRANS);
+ flags.in_trans= thd->in_active_multi_stmt_transaction();
flags.autocommit= test(thd->server_status & SERVER_STATUS_AUTOCOMMIT);
flags.pkt_nr= thd->net.pkt_nr;
flags.character_set_client_num= thd->variables.character_set_client->number;
@@ -1868,11 +1882,11 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length)
DBUG_PRINT("qcache", ("\
long %d, 4.1: %d, bin_proto: %d, more results %d, pkt_nr: %d, \
CS client: %u, CS result: %u, CS conn: %u, limit: %lu, TZ: 0x%lx, \
-sql mode: 0x%lx, sort len: %lu, conncat len: %lu, div_precision: %lu, \
+sql mode: 0x%llx, sort len: %lu, conncat len: %lu, div_precision: %lu, \
def_week_frmt: %lu, in_trans: %d, autocommit: %d",
(int)flags.client_long_flag,
(int)flags.client_protocol_41,
- (int)flags.result_in_binary_protocol,
+ (int)flags.protocol_type,
(int)flags.more_results_exists,
flags.pkt_nr,
flags.character_set_client_num,
@@ -1889,8 +1903,8 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
(int)flags.autocommit));
memcpy((uchar *)(sql + (tot_length - QUERY_CACHE_FLAGS_SIZE)),
(uchar*) &flags, QUERY_CACHE_FLAGS_SIZE);
- query_block = (Query_cache_block *) hash_search(&queries, (uchar*) sql,
- tot_length);
+ query_block = (Query_cache_block *) my_hash_search(&queries, (uchar*) sql,
+ tot_length);
/* Quick abort on unlocked data */
if (query_block == 0 ||
query_block->query()->result() == 0 ||
@@ -1919,7 +1933,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
}
DBUG_PRINT("qcache", ("Query have result 0x%lx", (ulong) query));
- if ((thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) &&
+ if (thd->in_multi_stmt_transaction_mode() &&
(query->tables_type() & HA_CACHE_TBL_TRANSACT))
{
DBUG_PRINT("qcache",
@@ -1971,7 +1985,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
table_list.db = table->db();
table_list.alias= table_list.table_name= table->table();
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (check_table_access(thd,SELECT_ACL,&table_list, 1, TRUE))
+ 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",
@@ -2019,6 +2033,13 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
*/
thd->query_cache_is_applicable= 0; // Query can't be cached
}
+ /*
+ End the statement transaction potentially started by engine.
+ Currently our engines do not request rollback from callbacks.
+ If this is going to change code needs to be reworked.
+ */
+ DBUG_ASSERT(! thd->transaction_rollback_request);
+ trans_rollback_stmt(thd);
goto err_unlock; // Parse query
}
else
@@ -2058,17 +2079,31 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
}
#endif /*!EMBEDDED_LIBRARY*/
- thd->limit_found_rows = query->found_rows();
+ thd->sent_row_count= thd->limit_found_rows = query->found_rows();
thd->status_var.last_query_cost= 0.0;
thd->query_plan_flags= (thd->query_plan_flags & ~QPLAN_QC_NO) | QPLAN_QC;
- if (!thd->main_da.is_set())
- thd->main_da.disable_status();
+ if (!thd->sent_row_count)
+ status_var_increment(thd->status_var.empty_queries);
+ else
+ status_var_add(thd->status_var.rows_sent, thd->sent_row_count);
+
+ /*
+ End the statement transaction potentially started by an
+ engine callback. We ignore the return value for now,
+ since as long as EOF packet is part of the query cache
+ response, we can't handle it anyway.
+ */
+ (void) trans_commit_stmt(thd);
+ if (!thd->stmt_da->is_set())
+ thd->stmt_da->disable_status();
BLOCK_UNLOCK_RD(query_block);
+ MYSQL_QUERY_CACHE_HIT(thd->query(), (ulong) thd->limit_found_rows);
DBUG_RETURN(1); // Result sent to client
err_unlock:
unlock();
+ MYSQL_QUERY_CACHE_MISS(thd->query());
/*
query_plan_flags doesn't have to be changed here as it contains
QPLAN_QC_NO by default
@@ -2092,8 +2127,7 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used,
if (is_disabled())
DBUG_VOID_RETURN;
- using_transactions= using_transactions &&
- (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
+ using_transactions= using_transactions && thd->in_multi_stmt_transaction_mode();
for (; tables_used; tables_used= tables_used->next_local)
{
DBUG_ASSERT(!using_transactions || tables_used->table!=0);
@@ -2112,8 +2146,7 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used,
invalidate_table(thd, tables_used);
}
- DBUG_EXECUTE_IF("wait_after_query_cache_invalidate",
- debug_wait_for_kill("wait_after_query_cache_invalidate"););
+ DEBUG_SYNC(thd, "wait_after_query_cache_invalidate");
DBUG_VOID_RETURN;
}
@@ -2176,8 +2209,7 @@ void Query_cache::invalidate(THD *thd, TABLE *table,
if (is_disabled())
DBUG_VOID_RETURN;
- using_transactions= using_transactions &&
- (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
+ using_transactions= using_transactions && thd->in_multi_stmt_transaction_mode();
if (using_transactions &&
(table->file->table_cache_type() == HA_CACHE_TBL_TRANSACT))
thd->add_changed_table(table);
@@ -2195,8 +2227,7 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length,
if (is_disabled())
DBUG_VOID_RETURN;
- using_transactions= using_transactions &&
- (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
+ using_transactions= using_transactions && thd->in_multi_stmt_transaction_mode();
if (using_transactions) // used for innodb => has_transactions() is TRUE
thd->add_changed_table(key, key_length);
else
@@ -2212,12 +2243,11 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length,
void Query_cache::invalidate(THD *thd, char *db)
{
- bool restart;
DBUG_ENTER("Query_cache::invalidate (db)");
if (is_disabled())
DBUG_VOID_RETURN;
- restart= FALSE;
+ bool restart= FALSE;
/*
Lock the query cache and queue all invalidation attempts to avoid
the risk of a race between invalidation, cache inserts and flushes.
@@ -2303,8 +2333,7 @@ void Query_cache::flush()
if (is_disabled())
DBUG_VOID_RETURN;
- DBUG_EXECUTE_IF("wait_in_query_cache_flush1",
- debug_wait_for_kill("wait_in_query_cache_flush1"););
+ QC_DEBUG_SYNC("wait_in_query_cache_flush1");
lock_and_suspend();
if (query_cache_size > 0)
@@ -2334,6 +2363,9 @@ void Query_cache::pack(THD *thd, ulong join_limit, uint iteration_limit)
{
DBUG_ENTER("Query_cache::pack");
+ if (is_disabled())
+ DBUG_VOID_RETURN;
+
/*
If the entire qc is being invalidated we can bail out early
instead of waiting for the lock.
@@ -2372,8 +2404,8 @@ void Query_cache::destroy()
free_cache();
unlock();
- pthread_cond_destroy(&COND_cache_status_changed);
- pthread_mutex_destroy(&structure_guard_mutex);
+ mysql_cond_destroy(&COND_cache_status_changed);
+ mysql_mutex_destroy(&structure_guard_mutex);
initialized = 0;
DBUG_ASSERT(m_requests_in_progress == 0);
}
@@ -2401,13 +2433,36 @@ void Query_cache::disable_query_cache(THD *thd)
void Query_cache::init()
{
DBUG_ENTER("Query_cache::init");
- pthread_mutex_init(&structure_guard_mutex,MY_MUTEX_INIT_FAST);
- pthread_cond_init(&COND_cache_status_changed, NULL);
+ mysql_mutex_init(key_structure_guard_mutex,
+ &structure_guard_mutex, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_COND_cache_status_changed,
+ &COND_cache_status_changed, NULL);
m_cache_lock_status= Query_cache::UNLOCKED;
m_cache_status= Query_cache::OK;
m_requests_in_progress= 0;
initialized = 1;
- query_state_map= default_charset_info->state_map;
+ /*
+ Using state_map from latin1 should be fine in all cases:
+ 1. We do not support UCS2, UTF16, UTF32 as a client character set.
+ 2. The other character sets are compatible on the lower ASCII-range
+ 0x00-0x20, and have the following characters marked as spaces:
+
+ 0x09 TAB
+ 0x0A LINE FEED
+ 0x0B VERTICAL TAB
+ 0x0C FORM FEED
+ 0x0D CARRIAGE RETUR
+ 0x20 SPACE
+
+ Additionally, only some of the ASCII-compatible character sets
+ (including latin1) can have 0xA0 mapped to "NON-BREAK SPACE"
+ and thus marked as space.
+ That should not be a problem for those charsets that map 0xA0
+ to something else: the parser will just return syntax error
+ if this character appears straight in the query
+ (i.e. not inside a string literal or comment).
+ */
+ query_state_map= my_charset_latin1.state_map;
/*
If we explicitly turn off query cache from the command line query
cache will be disabled for the reminder of the server life
@@ -2557,9 +2612,9 @@ ulong Query_cache::init_cache()
DUMP(this);
- VOID(hash_init(&queries, &my_charset_bin, def_query_hash_size, 0, 0,
- query_cache_query_get_key, 0, 0));
-#ifndef FN_NO_CASE_SENCE
+ (void) my_hash_init(&queries, &my_charset_bin, def_query_hash_size, 0, 0,
+ query_cache_query_get_key, 0, 0);
+#ifndef FN_NO_CASE_SENSE
/*
If lower_case_table_names!=0 then db and table names are already
converted to lower case and we can use binary collation for their
@@ -2568,8 +2623,8 @@ ulong Query_cache::init_cache()
lower_case_table_names == 0 then we should distinguish my_table
and MY_TABLE cases and so again can use binary collation.
*/
- VOID(hash_init(&tables, &my_charset_bin, def_table_hash_size, 0, 0,
- query_cache_table_get_key, 0, 0));
+ (void) my_hash_init(&tables, &my_charset_bin, def_table_hash_size, 0, 0,
+ query_cache_table_get_key, 0, 0);
#else
/*
On windows, OS/2, MacOS X with HFS+ or any other case insensitive
@@ -2579,10 +2634,11 @@ ulong Query_cache::init_cache()
file system) and so should use case insensitive collation for
comparison.
*/
- VOID(hash_init(&tables,
- lower_case_table_names ? &my_charset_bin :
- files_charset_info,
- def_table_hash_size, 0, 0,query_cache_table_get_key, 0, 0));
+ (void) my_hash_init(&tables,
+ lower_case_table_names ? &my_charset_bin :
+ files_charset_info,
+ def_table_hash_size, 0, 0,query_cache_table_get_key,
+ 0, 0);
#endif
queries_in_cache = 0;
@@ -2637,15 +2693,15 @@ void Query_cache::free_cache()
do
{
Query_cache_query *query= block->query();
- rwlock_destroy(&query->lock);
+ mysql_rwlock_destroy(&query->lock);
block= block->next;
} while (block != queries_blocks);
}
- my_free((uchar*) cache, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(cache);
make_disabled();
- hash_free(&queries);
- hash_free(&tables);
+ my_hash_free(&queries);
+ my_hash_free(&tables);
DBUG_VOID_RETURN;
}
@@ -2669,9 +2725,7 @@ void Query_cache::free_cache()
void Query_cache::flush_cache()
{
-
- DBUG_EXECUTE_IF("wait_in_query_cache_flush2",
- debug_wait_for_kill("wait_in_query_cache_flush2"););
+ QC_DEBUG_SYNC("wait_in_query_cache_flush2");
my_hash_reset(&queries);
while (queries_blocks != 0)
@@ -2757,7 +2811,7 @@ void Query_cache::free_query_internal(Query_cache_block *query_block)
if (query->writer() != 0)
{
/* Tell MySQL that this query should not be cached anymore */
- query->writer()->query_cache_query= 0;
+ query->writer()->first_query_block= NULL;
query->writer(0);
}
double_linked_list_exclude(query_block, &queries_blocks);
@@ -2820,7 +2874,7 @@ void Query_cache::free_query(Query_cache_block *query_block)
(ulong) query_block,
query_block->query()->length() ));
- hash_delete(&queries,(uchar *) query_block);
+ my_hash_delete(&queries,(uchar *) query_block);
free_query_internal(query_block);
DBUG_VOID_RETURN;
@@ -3117,8 +3171,7 @@ void Query_cache::invalidate_table(THD *thd, TABLE *table)
void Query_cache::invalidate_table(THD *thd, uchar * key, uint32 key_length)
{
- DBUG_EXECUTE_IF("wait_in_query_cache_invalidate1",
- debug_wait_for_kill("wait_in_query_cache_invalidate1"); );
+ DEBUG_SYNC(thd, "wait_in_query_cache_invalidate1");
/*
Lock the query cache and queue all invalidation attempts to avoid
@@ -3126,9 +3179,7 @@ void Query_cache::invalidate_table(THD *thd, uchar * key, uint32 key_length)
*/
lock(thd);
- DBUG_EXECUTE_IF("wait_in_query_cache_invalidate2",
- debug_wait_for_kill("wait_in_query_cache_invalidate2"); );
-
+ DEBUG_SYNC(thd, "wait_in_query_cache_invalidate2");
if (query_cache_size > 0)
invalidate_table_internal(thd, key, key_length);
@@ -3149,7 +3200,7 @@ void
Query_cache::invalidate_table_internal(THD *thd, uchar *key, uint32 key_length)
{
Query_cache_block *table_block=
- (Query_cache_block*)hash_search(&tables, key, key_length);
+ (Query_cache_block*)my_hash_search(&tables, key, key_length);
if (table_block)
{
Query_cache_block_table *list_root= table_block->table(0);
@@ -3178,7 +3229,6 @@ Query_cache::invalidate_query_block_list(THD *thd,
Query_cache_block *query_block= list_root->next->block();
BLOCK_LOCK_WR(query_block);
free_query(query_block);
- DBUG_EXECUTE_IF("debug_cache_locks", sleep(10););
}
}
@@ -3188,6 +3238,7 @@ Query_cache::invalidate_query_block_list(THD *thd,
SYNOPSIS
Query_cache::register_tables_from_list
+ thd thread handle
tables_used given table list
counter number current position in table of tables of block
block_table pointer to current position in tables table of block
@@ -3198,24 +3249,24 @@ Query_cache::invalidate_query_block_list(THD *thd,
*/
TABLE_COUNTER_TYPE
-Query_cache::register_tables_from_list(TABLE_LIST *tables_used,
+Query_cache::register_tables_from_list(THD *thd, TABLE_LIST *tables_used,
TABLE_COUNTER_TYPE counter,
- Query_cache_block_table *block_table)
+ Query_cache_block_table **block_table)
{
TABLE_COUNTER_TYPE n;
DBUG_ENTER("Query_cache::register_tables_from_list");
for (n= counter;
tables_used;
- tables_used= tables_used->next_global, n++, block_table++)
+ tables_used= tables_used->next_global, n++, (*block_table)++)
{
if (tables_used->is_anonymous_derived_table())
{
DBUG_PRINT("qcache", ("derived table skipped"));
n--;
- block_table--;
+ (*block_table)--;
continue;
}
- block_table->n= n;
+ (*block_table)->n= n;
if (tables_used->view)
{
char key[MAX_DBKEY_LENGTH];
@@ -3228,9 +3279,9 @@ Query_cache::register_tables_from_list(TABLE_LIST *tables_used,
/*
There are not callback function for for VIEWs
*/
- if (!insert_table(key_length, key, block_table,
- tables_used->view_db.length + 1,
- HA_CACHE_TBL_NONTRANSACT, 0, 0))
+ if (!insert_table(key_length, key, (*block_table),
+ tables_used->view_db.length,
+ HA_CACHE_TBL_NONTRANSACT, 0, 0, TRUE))
DBUG_RETURN(0);
/*
We do not need to register view tables here because they are already
@@ -3249,42 +3300,17 @@ Query_cache::register_tables_from_list(TABLE_LIST *tables_used,
if (!insert_table(tables_used->table->s->table_cache_key.length,
tables_used->table->s->table_cache_key.str,
- block_table,
+ (*block_table),
tables_used->db_length,
tables_used->table->file->table_cache_type(),
tables_used->callback_func,
- tables_used->engine_data))
+ tables_used->engine_data,
+ TRUE))
DBUG_RETURN(0);
-#ifdef WITH_MYISAMMRG_STORAGE_ENGINE
- /*
- XXX FIXME: Some generic mechanism is required here instead of this
- MYISAMMRG-specific implementation.
- */
- if (tables_used->table->s->db_type()->db_type == DB_TYPE_MRG_MYISAM)
- {
- ha_myisammrg *handler = (ha_myisammrg *) tables_used->table->file;
- MYRG_INFO *file = handler->myrg_info();
- for (MYRG_TABLE *table = file->open_tables;
- table != file->end_table ;
- table++)
- {
- char key[MAX_DBKEY_LENGTH];
- uint32 db_length;
- uint key_length= filename_2_table_key(key, table->table->filename,
- &db_length);
- (++block_table)->n= ++n;
- /*
- There are not callback function for for MyISAM, and engine data
- */
- if (!insert_table(key_length, key, block_table,
- db_length,
- tables_used->table->file->table_cache_type(),
- 0, 0))
- DBUG_RETURN(0);
- }
- }
-#endif
+ if (tables_used->table->file->
+ register_query_cache_dependant_tables(thd, this, block_table, &n))
+ DBUG_RETURN(0);
}
}
DBUG_RETURN(n - counter);
@@ -3295,12 +3321,14 @@ Query_cache::register_tables_from_list(TABLE_LIST *tables_used,
SYNOPSIS
register_all_tables()
+ thd Thread handle
block Store tables in this block
tables_used List if used tables
tables_arg Not used ?
*/
-my_bool Query_cache::register_all_tables(Query_cache_block *block,
+my_bool Query_cache::register_all_tables(THD *thd,
+ Query_cache_block *block,
TABLE_LIST *tables_used,
TABLE_COUNTER_TYPE tables_arg)
{
@@ -3311,7 +3339,7 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block,
Query_cache_block_table *block_table = block->table(0);
- n= register_tables_from_list(tables_used, 0, block_table);
+ n= register_tables_from_list(thd, tables_used, 0, &block_table);
if (n==0)
{
@@ -3320,6 +3348,8 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block,
tmp != block_table;
tmp++)
unlink_table(tmp);
+ if (block_table->parent)
+ unlink_table(block_table);
}
return test(n);
}
@@ -3338,7 +3368,8 @@ Query_cache::insert_table(uint key_len, char *key,
Query_cache_block_table *node,
uint32 db_length, uint8 cache_type,
qc_engine_callback callback,
- ulonglong engine_data)
+ ulonglong engine_data,
+ my_bool hash)
{
DBUG_ENTER("Query_cache::insert_table");
DBUG_PRINT("qcache", ("insert table node 0x%lx, len %d",
@@ -3346,8 +3377,10 @@ Query_cache::insert_table(uint key_len, char *key,
THD *thd= current_thd;
- Query_cache_block *table_block=
- (Query_cache_block *)hash_search(&tables, (uchar*) key, key_len);
+ Query_cache_block *table_block=
+ (hash ?
+ (Query_cache_block *) my_hash_search(&tables, (uchar*) key, key_len) :
+ NULL);
if (table_block &&
table_block->table()->engine_data() != engine_data)
@@ -3397,7 +3430,8 @@ Query_cache::insert_table(uint key_len, char *key,
*/
list_root->next= list_root->prev= list_root;
- if (my_hash_insert(&tables, (const uchar *) table_block))
+ if (hash &&
+ my_hash_insert(&tables, (const uchar *) table_block))
{
DBUG_PRINT("qcache", ("Can't insert table to hash"));
// write_block_data return locked block
@@ -3410,6 +3444,7 @@ Query_cache::insert_table(uint key_len, char *key,
header->type(cache_type);
header->callback(callback);
header->engine_data(engine_data);
+ header->set_hashed(hash);
/*
We insert this table without the assumption that it isn't refrenenced by
@@ -3463,7 +3498,9 @@ void Query_cache::unlink_table(Query_cache_block_table *node)
Query_cache_block *table_block= neighbour->block();
double_linked_list_exclude(table_block,
&tables_blocks);
- hash_delete(&tables,(uchar *) table_block);
+ Query_cache_table *header= table_block->table();
+ if (header->is_hashed())
+ my_hash_delete(&tables,(uchar *) table_block);
free_memory_block(table_block);
}
DBUG_VOID_RETURN;
@@ -3936,6 +3973,9 @@ Query_cache::process_and_count_tables(THD *thd, TABLE_LIST *tables_used,
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 &&
@@ -3948,18 +3988,6 @@ Query_cache::process_and_count_tables(THD *thd, TABLE_LIST *tables_used,
"other non-cacheable table(s)"));
DBUG_RETURN(0);
}
-#ifdef WITH_MYISAMMRG_STORAGE_ENGINE
- /*
- XXX FIXME: Some generic mechanism is required here instead of this
- MYISAMMRG-specific implementation.
- */
- if (tables_used->table->s->db_type()->db_type == DB_TYPE_MRG_MYISAM)
- {
- ha_myisammrg *handler = (ha_myisammrg *)tables_used->table->file;
- MYRG_INFO *file = handler->myrg_info();
- table_count+= (file->end_table - file->open_tables);
- }
-#endif
}
}
DBUG_RETURN(table_count);
@@ -3967,6 +3995,18 @@ Query_cache::process_and_count_tables(THD *thd, TABLE_LIST *tables_used,
/*
+In non-embedded QC intercepts result in net_real_write
+but if we have no net.vio then net_real_write
+will not be called, so QC can't get results of the query
+*/
+#ifdef EMBEDDED_LIBRARY
+#define qc_is_able_to_intercept_result(T) 1
+#else
+#define qc_is_able_to_intercept_result(T) ((T)->net.vio)
+#endif
+
+
+/*
If query is cacheable return number tables in query
(query without tables are not cached)
*/
@@ -3981,7 +4021,8 @@ Query_cache::is_cacheable(THD *thd, LEX *lex,
if (thd->lex->safe_to_cache_query &&
(thd->variables.query_cache_type == 1 ||
(thd->variables.query_cache_type == 2 && (lex->select_lex.options &
- OPTION_TO_QUERY_CACHE))))
+ OPTION_TO_QUERY_CACHE))) &&
+ qc_is_able_to_intercept_result(thd))
{
DBUG_PRINT("qcache", ("options: %lx %lx type: %u",
(long) OPTION_TO_QUERY_CACHE,
@@ -3992,7 +4033,7 @@ Query_cache::is_cacheable(THD *thd, LEX *lex,
tables_type)))
DBUG_RETURN(0);
- if ((thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) &&
+ if (thd->in_multi_stmt_transaction_mode() &&
((*tables_type)&HA_CACHE_TBL_TRANSACT))
{
DBUG_PRINT("qcache", ("not in autocommin mode"));
@@ -4003,11 +4044,12 @@ Query_cache::is_cacheable(THD *thd, LEX *lex,
}
DBUG_PRINT("qcache",
- ("not interesting query: %d or not cacheable, options %lx %lx type: %u",
+ ("not interesting query: %d or not cacheable, options %lx %lx type: %u net->vio present: %u",
(int) lex->sql_command,
(long) OPTION_TO_QUERY_CACHE,
(long) lex->select_lex.options,
- (int) thd->variables.query_cache_type));
+ (int) thd->variables.query_cache_type,
+ (uint) test(qc_is_able_to_intercept_result(thd))));
DBUG_RETURN(0);
}
@@ -4153,7 +4195,7 @@ my_bool Query_cache::move_by_type(uchar **border,
uchar *key;
size_t key_length;
key=query_cache_table_get_key((uchar*) block, &key_length, 0);
- hash_first(&tables, (uchar*) key, key_length, &record_idx);
+ my_hash_first(&tables, (uchar*) key, key_length, &record_idx);
block->destroy();
new_block->init(len);
@@ -4187,7 +4229,7 @@ my_bool Query_cache::move_by_type(uchar **border,
/* Fix pointer to table name */
new_block->table()->table(new_block->table()->db() + tablename_offset);
/* Fix hash to point at moved block */
- hash_replace(&tables, &record_idx, (uchar*) new_block);
+ my_hash_replace(&tables, &record_idx, (uchar*) new_block);
DBUG_PRINT("qcache", ("moved %lu bytes to 0x%lx, new gap at 0x%lx",
len, (ulong) new_block, (ulong) *border));
@@ -4213,12 +4255,12 @@ my_bool Query_cache::move_by_type(uchar **border,
uchar *key;
size_t key_length;
key=query_cache_query_get_key((uchar*) block, &key_length, 0);
- hash_first(&queries, (uchar*) key, key_length, &record_idx);
- // Move table of used tables
- memmove((char*) new_block->table(0), (char*) block->table(0),
- ALIGN_SIZE(n_tables*sizeof(Query_cache_block_table)));
+ my_hash_first(&queries, (uchar*) key, key_length, &record_idx);
block->query()->unlock_n_destroy();
block->destroy();
+ // Move table of used tables
+ memmove((char*) new_block->table(0), (char*) block->table(0),
+ ALIGN_SIZE(n_tables*sizeof(Query_cache_block_table)));
new_block->init(len);
new_block->type=Query_cache_block::QUERY;
new_block->used=used;
@@ -4269,19 +4311,19 @@ my_bool Query_cache::move_by_type(uchar **border,
} while ( result_block != first_result_block );
}
Query_cache_query *new_query= ((Query_cache_query *) new_block->data());
- my_rwlock_init(&new_query->lock, NULL);
+ mysql_rwlock_init(key_rwlock_query_cache_query_lock, &new_query->lock);
/*
If someone is writing to this block, inform the writer that the block
has been moved.
*/
- NET *net = new_block->query()->writer();
- if (net != 0)
+ Query_cache_tls *query_cache_tls= new_block->query()->writer();
+ if (query_cache_tls != NULL)
{
- net->query_cache_query= (uchar*) new_block;
+ query_cache_tls->first_query_block= new_block;
}
/* Fix hash to point at moved block */
- hash_replace(&queries, &record_idx, (uchar*) new_block);
+ my_hash_replace(&queries, &record_idx, (uchar*) new_block);
DBUG_PRINT("qcache", ("moved %lu bytes to 0x%lx, new gap at 0x%lx",
len, (ulong) new_block, (ulong) *border));
break;
@@ -4692,13 +4734,13 @@ my_bool Query_cache::check_integrity(bool locked)
if (!locked)
lock_and_suspend();
- if (hash_check(&queries))
+ if (my_hash_check(&queries))
{
DBUG_PRINT("error", ("queries hash is damaged"));
result = 1;
}
- if (hash_check(&tables))
+ if (my_hash_check(&tables))
{
DBUG_PRINT("error", ("tables hash is damaged"));
result = 1;
@@ -4865,7 +4907,7 @@ my_bool Query_cache::check_integrity(bool locked)
(ulong) block, (uint) block->type));
size_t length;
uchar *key = query_cache_query_get_key((uchar*) block, &length, 0);
- uchar* val = hash_search(&queries, key, length);
+ uchar* val = my_hash_search(&queries, key, length);
if (((uchar*)block) != val)
{
DBUG_PRINT("error", ("block 0x%lx found in queries hash like 0x%lx",
@@ -4900,7 +4942,7 @@ my_bool Query_cache::check_integrity(bool locked)
(ulong) block, (uint) block->type));
size_t length;
uchar *key = query_cache_table_get_key((uchar*) block, &length, 0);
- uchar* val = hash_search(&tables, key, length);
+ uchar* val = my_hash_search(&tables, key, length);
if (((uchar*)block) != val)
{
DBUG_PRINT("error", ("block 0x%lx found in tables hash like 0x%lx",
diff --git a/sql/sql_cache.h b/sql/sql_cache.h
index b2e88a3c619..f35ac889b23 100644
--- a/sql/sql_cache.h
+++ b/sql/sql_cache.h
@@ -1,6 +1,4 @@
-/*
- Copyright (c) 2001-2008 MySQL AB, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2001, 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
@@ -13,12 +11,21 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef _SQL_CACHE_H
#define _SQL_CACHE_H
+#include "hash.h"
+#include "my_base.h" /* ha_rows */
+
+class MY_LOCALE;
+struct TABLE_LIST;
+class Time_zone;
+struct LEX;
+struct TABLE;
+typedef struct st_changed_table_list CHANGED_TABLE_LIST;
+
/* Query cache */
/*
@@ -34,7 +41,7 @@
#define QUERY_CACHE_DEF_TABLE_HASH_SIZE 1024
/* minimal result data size when data allocated */
-#define QUERY_CACHE_MIN_RESULT_DATA_SIZE 1024*4
+#define QUERY_CACHE_MIN_RESULT_DATA_SIZE (1024*4)
/*
start estimation of first result block size only when number of queries
@@ -67,6 +74,13 @@ struct Query_cache_table;
struct Query_cache_query;
struct Query_cache_result;
class Query_cache;
+struct Query_cache_tls;
+struct LEX;
+class THD;
+
+typedef my_bool (*qc_engine_callback)(THD *thd, char *table_key,
+ uint key_length,
+ ulonglong *engine_data);
/**
This class represents a node in the linked chain of queries
@@ -127,20 +141,20 @@ struct Query_cache_block
inline bool is_free(void) { return type == FREE; }
void init(ulong length);
void destroy();
- inline uint headers_len();
- inline uchar* data(void);
- inline Query_cache_query *query();
- inline Query_cache_table *table();
- inline Query_cache_result *result();
- inline Query_cache_block_table *table(TABLE_COUNTER_TYPE n);
+ uint headers_len();
+ uchar* data(void);
+ Query_cache_query *query();
+ Query_cache_table *table();
+ Query_cache_result *result();
+ Query_cache_block_table *table(TABLE_COUNTER_TYPE n);
};
struct Query_cache_query
{
ulonglong limit_found_rows;
- rw_lock_t lock;
+ mysql_rwlock_t lock;
Query_cache_block *res;
- NET *wri;
+ Query_cache_tls *wri;
ulong len;
uint8 tbls_type;
unsigned int last_pkt_nr;
@@ -152,8 +166,8 @@ struct Query_cache_query
inline void found_rows(ulonglong rows) { limit_found_rows= rows; }
inline Query_cache_block *result() { return res; }
inline void result(Query_cache_block *p) { res= p; }
- inline NET *writer() { return wri; }
- inline void writer(NET *p) { wri= p; }
+ inline Query_cache_tls *writer() { return wri; }
+ 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; }
@@ -186,6 +200,10 @@ struct Query_cache_table
The number of queries depending of this table.
*/
int32 m_cached_query_count;
+ /**
+ If table included in the table hash to be found by other queries
+ */
+ my_bool hashed;
inline char *db() { return (char *) data(); }
inline char *table() { return tbl; }
@@ -198,6 +216,8 @@ struct Query_cache_table
inline void callback(qc_engine_callback fn){ callback_func= fn; }
inline ulonglong engine_data() { return engine_data_buff; }
inline void engine_data(ulonglong data_arg){ engine_data_buff= data_arg; }
+ inline my_bool is_hashed() { return hashed; }
+ inline void set_hashed(my_bool hash) { hashed= hash; }
inline uchar* data()
{
return (uchar*)(((uchar*)this)+
@@ -278,7 +298,7 @@ private:
#ifndef DBUG_OFF
my_thread_id m_cache_lock_thread_id;
#endif
- pthread_cond_t COND_cache_status_changed;
+ mysql_cond_t COND_cache_status_changed;
uint m_requests_in_progress;
enum Cache_lock_status { UNLOCKED, LOCKED_NO_WAIT, LOCKED };
Cache_lock_status m_cache_lock_status;
@@ -303,7 +323,7 @@ protected:
is other threads that were going to do cache flush---they'll wait
till the end of a flush operation.
*/
- pthread_mutex_t structure_guard_mutex;
+ mysql_mutex_t structure_guard_mutex;
uchar *cache; // cache memory
Query_cache_block *first_block; // physical location block list
Query_cache_block *queries_blocks; // query list (LIFO)
@@ -329,10 +349,6 @@ protected:
static void double_linked_list_join(Query_cache_block *head_tail,
Query_cache_block *tail_head);
- /* Table key generation */
- static uint filename_2_table_key (char *key, const char *filename,
- uint32 *db_langth);
-
/* The following functions require that structure_guard_mutex is locked */
void flush_cache();
my_bool free_old_query();
@@ -349,17 +365,12 @@ protected:
Query_cache_block_table *list_root);
TABLE_COUNTER_TYPE
- register_tables_from_list(TABLE_LIST *tables_used,
+ register_tables_from_list(THD *thd, TABLE_LIST *tables_used,
TABLE_COUNTER_TYPE counter,
- Query_cache_block_table *block_table);
- my_bool register_all_tables(Query_cache_block *block,
+ Query_cache_block_table **block_table);
+ my_bool register_all_tables(THD *thd, Query_cache_block *block,
TABLE_LIST *tables_used,
TABLE_COUNTER_TYPE tables);
- my_bool insert_table(uint key_len, char *key,
- Query_cache_block_table *node,
- uint32 db_length, uint8 cache_type,
- qc_engine_callback callback,
- ulonglong engine_data);
void unlink_table(Query_cache_block_table *node);
Query_cache_block *get_free_block (ulong len, my_bool not_less,
ulong min);
@@ -473,10 +484,19 @@ protected:
void destroy();
- friend void query_cache_init_query(NET *net);
- friend void query_cache_insert(NET *net, const char *packet, ulong length);
- friend void query_cache_end_of_result(THD *thd);
- friend void query_cache_abort(NET *net);
+ void insert(Query_cache_tls *query_cache_tls,
+ const char *packet,
+ ulong length,
+ unsigned pkt_nr);
+ my_bool insert_table(uint key_len, char *key,
+ Query_cache_block_table *node,
+ uint32 db_length, uint8 cache_type,
+ qc_engine_callback callback,
+ ulonglong engine_data,
+ my_bool hash);
+
+ void end_of_result(THD *thd);
+ void abort(Query_cache_tls *query_cache_tls);
/*
The following functions are only used when debugging
@@ -496,6 +516,10 @@ protected:
const char *name);
my_bool in_blocks(Query_cache_block * point);
+ /* Table key generation */
+ static uint filename_2_table_key (char *key, const char *filename,
+ uint32 *db_langth);
+
enum Cache_try_lock_mode {WAIT, TIMEOUT, TRY};
bool try_lock(THD *thd, Cache_try_lock_mode mode= WAIT);
void lock(THD *thd);
@@ -505,11 +529,68 @@ protected:
void disable_query_cache(THD *thd);
};
-extern Query_cache query_cache;
-extern TYPELIB query_cache_type_typelib;
-void query_cache_init_query(NET *net);
-void query_cache_insert(NET *net, const char *packet, ulong length);
-void query_cache_end_of_result(THD *thd);
-void query_cache_abort(NET *net);
+#ifdef HAVE_QUERY_CACHE
+struct Query_cache_query_flags
+{
+ unsigned int client_long_flag:1;
+ unsigned int client_protocol_41:1;
+ unsigned int protocol_type:2;
+ unsigned int more_results_exists:1;
+ unsigned int in_trans:1;
+ unsigned int autocommit:1;
+ unsigned int pkt_nr;
+ uint character_set_client_num;
+ uint character_set_results_num;
+ uint collation_connection_num;
+ ha_rows limit;
+ Time_zone *time_zone;
+ ulonglong sql_mode;
+ ulong max_sort_length;
+ ulong group_concat_max_len;
+ ulong default_week_format;
+ ulong div_precision_increment;
+ MY_LOCALE *lc_time_names;
+};
+#define QUERY_CACHE_FLAGS_SIZE sizeof(Query_cache_query_flags)
+#define QUERY_CACHE_DB_LENGTH_SIZE 2
+#include "sql_cache.h"
+#define query_cache_abort(A) query_cache.abort(A)
+#define query_cache_end_of_result(A) query_cache.end_of_result(A)
+#define query_cache_store_query(A, B) query_cache.store_query(A, B)
+#define query_cache_destroy() query_cache.destroy()
+#define query_cache_result_size_limit(A) query_cache.result_size_limit(A)
+#define query_cache_init() query_cache.init()
+#define query_cache_resize(A) query_cache.resize(A)
+#define query_cache_set_min_res_unit(A) query_cache.set_min_res_unit(A)
+#define query_cache_invalidate3(A, B, C) query_cache.invalidate(A, B, C)
+#define query_cache_invalidate1(A, B) query_cache.invalidate(A, B)
+#define query_cache_send_result_to_client(A, B, C) \
+ query_cache.send_result_to_client(A, B, C)
+#define query_cache_invalidate_by_MyISAM_filename_ref \
+ &query_cache_invalidate_by_MyISAM_filename
+/* note the "maybe": it's a read without mutex */
+#define query_cache_maybe_disabled(T) \
+ (T->variables.query_cache_type == 0 || query_cache.query_cache_size == 0)
+#define query_cache_is_cacheable_query(L) \
+ (((L)->sql_command == SQLCOM_SELECT) && (L)->safe_to_cache_query)
+#else
+#define QUERY_CACHE_FLAGS_SIZE 0
+#define query_cache_store_query(A, B) do { } while(0)
+#define query_cache_destroy() do { } while(0)
+#define query_cache_result_size_limit(A) do { } while(0)
+#define query_cache_init() do { } while(0)
+#define query_cache_resize(A) do { } while(0)
+#define query_cache_set_min_res_unit(A) do { } while(0)
+#define query_cache_invalidate3(A, B, C) do { } while(0)
+#define query_cache_invalidate1(A,B) do { } while(0)
+#define query_cache_send_result_to_client(A, B, C) 0
+#define query_cache_invalidate_by_MyISAM_filename_ref NULL
+
+#define query_cache_abort(A) do { } while(0)
+#define query_cache_end_of_result(A) do { } while(0)
+#define query_cache_maybe_disabled(T) 1
+#define query_cache_is_cacheable_query(L) 0
+#endif /*HAVE_QUERY_CACHE*/
+extern Query_cache query_cache;
#endif
diff --git a/sql/sql_callback.h b/sql/sql_callback.h
new file mode 100644
index 00000000000..316f94a0213
--- /dev/null
+++ b/sql/sql_callback.h
@@ -0,0 +1,42 @@
+/*
+ Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef SQL_CALLBACK_INCLUDED
+#define SQL_CALLBACK_INCLUDED
+
+/**
+ Macro used for an internal callback.
+
+ The macro will check that the object exists and that the function
+ is defined. If that is the case, it will call the function with the
+ given parameters.
+
+ If the object or the function is not defined, the callback will be
+ considered successful (nothing needed to be done) and will
+ therefore return no error.
+ */
+
+#define MYSQL_CALLBACK(OBJ, FUNC, PARAMS) \
+ do { \
+ if ((OBJ) && ((OBJ)->FUNC)) \
+ (OBJ)->FUNC PARAMS; \
+ } while (0)
+
+#define MYSQL_CALLBACK_ELSE(OBJ, FUNC, PARAMS, ELSE) \
+ (((OBJ) && ((OBJ)->FUNC)) ? (OBJ)->FUNC PARAMS : (ELSE))
+
+#endif /* SQL_CALLBACK_INCLUDED */
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index c7f87df0082..475069d5ea2 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1,6 +1,6 @@
/*
- Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 2008, 2014, Monty Program Ab.
+ Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,6 +16,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+
/*****************************************************************************
**
** This file implements classes defined in sql_class.h
@@ -27,13 +28,24 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_priv.h"
+#include "unireg.h" // REQUIRED: for other includes
+#include "sql_class.h"
+#include "sql_cache.h" // query_cache_abort
+#include "sql_base.h" // close_thread_tables
+#include "sql_time.h" // date_time_format_copy
+#include "sql_acl.h" // NO_ACCESS,
+ // acl_getroot_no_password
+#include "sql_base.h" // close_temporary_tables
+#include "sql_handler.h" // mysql_ha_cleanup
#include "rpl_rli.h"
#include "rpl_filter.h"
#include "rpl_record.h"
#include "slave.h"
#include <my_bitmap.h>
#include "log_event.h"
+#include "sql_audit.h"
#include <m_ctype.h>
#include <sys/stat.h>
#include <thr_alarm.h>
@@ -41,12 +53,16 @@
#include <io.h>
#endif
#include <mysys_err.h>
+#include <limits.h>
#include "sp_rcontext.h"
#include "sp_cache.h"
+#include "transaction.h"
#include "sql_select.h" /* declares create_tmp_table() */
#include "debug_sync.h"
-#include "sql_handler.h"
+#include "sql_parse.h" // is_update_query
+#include "sql_callback.h"
+#include "sql_connect.h"
/*
The following is used to initialise Table_ident with a internal
@@ -89,15 +105,15 @@ extern "C" void free_user_var(user_var_entry *entry)
{
char *pos= (char*) entry+ALIGN_SIZE(sizeof(*entry));
if (entry->value && entry->value != pos)
- my_free(entry->value, MYF(0));
- my_free((char*) entry,MYF(0));
+ my_free(entry->value);
+ my_free(entry);
}
bool Key_part_spec::operator==(const Key_part_spec& other) const
{
return length == other.length &&
- !my_strcasecmp(system_charset_info, field_name,
- other.field_name);
+ !my_strcasecmp(system_charset_info, field_name.str,
+ other.field_name.str);
}
/**
@@ -224,7 +240,7 @@ bool Foreign_key::validate(List<Create_field> &table_fields)
it.rewind();
while ((sql_field= it++) &&
my_strcasecmp(system_charset_info,
- column->field_name,
+ column->field_name.str,
sql_field->field_name)) {}
if (!sql_field)
{
@@ -259,24 +275,248 @@ bool Foreign_key::validate(List<Create_field> &table_fields)
/****************************************************************************
** Thread specific functions
****************************************************************************/
+#ifdef ONLY_FOR_MYSQL_CLOSED_SOURCE_SCHEDULED
+/**
+ Get reference to scheduler data object
-/** Push an error to the error stack and return TRUE for now. */
+ @param thd THD object
-bool
-Reprepare_observer::report_error(THD *thd)
+ @retval Scheduler data object on THD
+*/
+void *thd_get_scheduler_data(THD *thd)
{
- my_error(ER_NEED_REPREPARE, MYF(ME_NO_WARNING_FOR_ERROR|ME_NO_SP_HANDLER));
+ return thd->scheduler.data;
+}
+
+/**
+ Set reference to Scheduler data object for THD object
- m_invalidated= TRUE;
+ @param thd THD object
+ @param psi Scheduler data object to set on THD
+*/
+void thd_set_scheduler_data(THD *thd, void *data)
+{
+ thd->scheduler.data= data;
+}
- return TRUE;
+/**
+ Get reference to Performance Schema object for THD object
+
+ @param thd THD object
+
+ @retval Performance schema object for thread on THD
+*/
+PSI_thread *thd_get_psi(THD *thd)
+{
+ return thd->scheduler.m_psi;
+}
+
+/**
+ Get net_wait_timeout for THD object
+
+ @param thd THD object
+
+ @retval net_wait_timeout value for thread on THD
+*/
+ulong thd_get_net_wait_timeout(THD* thd)
+{
+ return thd->variables.net_wait_timeout;
+}
+
+/**
+ Set reference to Performance Schema object for THD object
+
+ @param thd THD object
+ @param psi Performance schema object for thread
+*/
+void thd_set_psi(THD *thd, PSI_thread *psi)
+{
+ thd->scheduler.m_psi= psi;
+}
+
+/**
+ Set the state on connection to killed
+
+ @param thd THD object
+*/
+void thd_set_killed(THD *thd)
+{
+ thd->killed= KILL_CONNECTION;
+}
+
+/**
+ Clear errors from the previous THD
+
+ @param thd THD object
+*/
+void thd_clear_errors(THD *thd)
+{
+ my_errno= 0;
+ thd->mysys_var->abort= 0;
+}
+
+/**
+ Set thread stack in THD object
+
+ @param thd Thread object
+ @param stack_start Start of stack to set in THD object
+*/
+void thd_set_thread_stack(THD *thd, char *stack_start)
+{
+ thd->thread_stack= stack_start;
+}
+
+/**
+ Lock connection data for the set of connections this connection
+ belongs to
+
+ @param thd THD object
+*/
+void thd_lock_thread_count(THD *)
+{
+ mysql_mutex_lock(&LOCK_thread_count);
+}
+
+/**
+ Lock connection data for the set of connections this connection
+ belongs to
+
+ @param thd THD object
+*/
+void thd_unlock_thread_count(THD *)
+{
+ mysql_cond_broadcast(&COND_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
+}
+
+/**
+ Close the socket used by this connection
+
+ @param thd THD object
+*/
+void thd_close_connection(THD *thd)
+{
+ if (thd->net.vio)
+ vio_close(thd->net.vio);
+}
+
+/**
+ Get current THD object from thread local data
+
+ @retval The THD object for the thread, NULL if not connection thread
+*/
+THD *thd_get_current_thd()
+{
+ return current_thd;
+}
+
+/**
+ Lock data that needs protection in THD object
+
+ @param thd THD object
+*/
+void thd_lock_data(THD *thd)
+{
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+}
+
+/**
+ Unlock data that needs protection in THD object
+
+ @param thd THD object
+*/
+void thd_unlock_data(THD *thd)
+{
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+}
+
+/**
+ Support method to check if connection has already started transcaction
+
+ @param client_cntx Low level client context
+
+ @retval TRUE if connection already started transaction
+*/
+bool thd_is_transaction_active(THD *thd)
+{
+ return thd->transaction.is_active();
+}
+
+/**
+ Check if there is buffered data on the socket representing the connection
+
+ @param thd THD object
+*/
+int thd_connection_has_data(THD *thd)
+{
+ Vio *vio= thd->net.vio;
+ return vio->has_data(vio);
}
+/**
+ Set reading/writing on socket, used by SHOW PROCESSLIST
+
+ @param thd THD object
+ @param val Value to set it to (0 or 1)
+*/
+void thd_set_net_read_write(THD *thd, uint val)
+{
+ thd->net.reading_or_writing= val;
+}
+
+/**
+ Get reading/writing on socket from THD object
+ @param thd THD object
+
+ @retval net.reading_or_writing value for thread on THD.
+*/
+uint thd_get_net_read_write(THD *thd)
+{
+ return thd->net.reading_or_writing;
+}
+
+/**
+ Set reference to mysys variable in THD object
+
+ @param thd THD object
+ @param mysys_var Reference to set
+*/
+void thd_set_mysys_var(THD *thd, st_my_thread_var *mysys_var)
+{
+ thd->set_mysys_var(mysys_var);
+}
+
+/**
+ Get socket file descriptor for this connection
+
+ @param thd THD object
+
+ @retval Socket of the connection
+*/
+my_socket thd_get_fd(THD *thd)
+{
+ return thd->net.vio->sd;
+}
+#endif
+
+/**
+ Get thread attributes for connection threads
+
+ @retval Reference to thread attribute for connection threads
+*/
+pthread_attr_t *get_connection_attrib(void)
+{
+ return &connection_attrib;
+}
+
+/**
+ Get max number of connections
-Open_tables_state::Open_tables_state(ulong version_arg)
- :version(version_arg), state_flags(0U)
+ @retval Max number of connections for MySQL Server
+*/
+ulong get_max_connections(void)
{
- reset_open_tables_state();
+ return max_connections;
}
/*
@@ -332,21 +572,47 @@ const char *set_thd_proc_info(THD *thd, const char *info,
thd= current_thd;
const char *old_info= thd->proc_info;
- DBUG_PRINT("proc_info", ("%s:%d %s", calling_file, calling_line,
- (info != NULL) ? info : ""));
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
- thd->profiling.status_change(info, calling_function, calling_file, calling_line);
+ DBUG_PRINT("proc_info", ("%s:%d %s", calling_file, calling_line, info));
+
+#if defined(ENABLED_PROFILING)
+ thd->profiling.status_change(info,
+ calling_function, calling_file, calling_line);
#endif
thd->proc_info= info;
return old_info;
}
extern "C"
+const char* thd_enter_cond(MYSQL_THD thd, mysql_cond_t *cond,
+ mysql_mutex_t *mutex, const char *msg)
+{
+ if (!thd)
+ thd= current_thd;
+
+ return thd->enter_cond(cond, mutex, msg);
+}
+
+extern "C"
+void thd_exit_cond(MYSQL_THD thd, const char *old_msg)
+{
+ if (!thd)
+ thd= current_thd;
+
+ thd->exit_cond(old_msg);
+ return;
+}
+
+extern "C"
void **thd_ha_data(const THD *thd, const struct handlerton *hton)
{
return (void **) &thd->ha_data[hton->slot].ha_ptr;
}
+extern "C"
+void thd_storage_lock_wait(THD *thd, long long value)
+{
+ thd->utime_after_lock+= value;
+}
/**
Provide a handler data getter to simplify coding
@@ -381,7 +647,7 @@ void thd_set_ha_data(THD *thd, const struct handlerton *hton,
extern "C"
long long thd_test_options(const THD *thd, long long test_options)
{
- return thd->options & test_options;
+ return thd->variables.option_bits & test_options;
}
extern "C"
@@ -393,13 +659,13 @@ int thd_sql_command(const THD *thd)
extern "C"
int thd_tx_isolation(const THD *thd)
{
- return (int) thd->variables.tx_isolation;
+ return (int) thd->tx_isolation;
}
extern "C"
void thd_inc_row_count(THD *thd)
{
- thd->row_count++;
+ thd->warning_info->inc_current_row_for_warning();
}
@@ -425,10 +691,9 @@ extern "C"
char *thd_security_context(THD *thd, char *buffer, unsigned int length,
unsigned int max_query_len)
{
- DEBUG_SYNC(thd, "thd_security_context");
String str(buffer, length, &my_charset_latin1);
const Security_context *sctx= &thd->main_security_ctx;
- char header[64];
+ char header[256];
int len;
/*
The pointers thd->query and thd->proc_info might change since they are
@@ -436,14 +701,14 @@ char *thd_security_context(THD *thd, char *buffer, unsigned int length,
values doesn't have to very accurate and the memory it points to is static,
but we need to attempt a snapshot on the pointer values to avoid using NULL
values. The pointer to thd->query however, doesn't point to static memory
- and has to be protected by LOCK_thread_count or risk pointing to
+ and has to be protected by thd->LOCK_thd_data or risk pointing to
uninitialized memory.
*/
const char *proc_info= thd->proc_info;
len= my_snprintf(header, sizeof(header),
- "MySQL thread id %lu, query id %lu",
- thd->thread_id, (ulong) thd->query_id);
+ "MySQL thread id %lu, OS thread handle 0x%lx, query id %lu",
+ thd->thread_id, (ulong) thd->real_id, (ulong) thd->query_id);
str.length(0);
str.append(header, len);
@@ -471,20 +736,21 @@ char *thd_security_context(THD *thd, char *buffer, unsigned int length,
str.append(proc_info);
}
- pthread_mutex_lock(&thd->LOCK_thd_data);
-
- if (thd->query())
+ /* Don't wait if LOCK_thd_data is used as this could cause a deadlock */
+ if (!mysql_mutex_trylock(&thd->LOCK_thd_data))
{
- if (max_query_len < 1)
- len= thd->query_length();
- else
- len= min(thd->query_length(), max_query_len);
- str.append('\n');
- str.append(thd->query(), len);
+ if (thd->query())
+ {
+ if (max_query_len < 1)
+ len= thd->query_length();
+ else
+ len= min(thd->query_length(), max_query_len);
+ str.append('\n');
+ str.append(thd->query(), len);
+ }
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
}
- pthread_mutex_unlock(&thd->LOCK_thd_data);
-
if (str.c_ptr_safe() == buffer)
return buffer;
@@ -502,7 +768,7 @@ char *thd_security_context(THD *thd, char *buffer, unsigned int length,
/**
- Implementation of Drop_table_error_handler::handle_error().
+ Implementation of Drop_table_error_handler::handle_condition().
The reason in having this implementation is to silence technical low-level
warnings during DROP TABLE operation. Currently we don't want to expose
the following warnings during DROP TABLE:
@@ -515,160 +781,26 @@ char *thd_security_context(THD *thd, char *buffer, unsigned int length,
@return TRUE if the condition is handled.
*/
-bool Drop_table_error_handler::handle_error(uint sql_errno,
- const char *message,
- MYSQL_ERROR::enum_warning_level level,
- THD *thd)
-{
+bool Drop_table_error_handler::handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl)
+{
+ *cond_hdl= NULL;
return ((sql_errno == EE_DELETE && my_errno == ENOENT) ||
sql_errno == ER_TRG_NO_DEFINER);
}
-/**
- Clear this diagnostics area.
-
- Normally called at the end of a statement.
-*/
-
-void
-Diagnostics_area::reset_diagnostics_area()
-{
- DBUG_ENTER("reset_diagnostics_area");
-#ifdef DBUG_OFF
- can_overwrite_status= FALSE;
- /** Don't take chances in production */
- m_message[0]= '\0';
- m_sql_errno= 0;
- m_server_status= 0;
- m_affected_rows= 0;
- m_last_insert_id= 0;
- m_total_warn_count= 0;
-#endif
- is_sent= FALSE;
- /** Tiny reset in debug mode to see garbage right away */
- m_status= DA_EMPTY;
- DBUG_VOID_RETURN;
-}
-
-
-/**
- Set OK status -- ends commands that do not return a
- result set, e.g. INSERT/UPDATE/DELETE.
-*/
-
-void
-Diagnostics_area::set_ok_status(THD *thd, ha_rows affected_rows_arg,
- ulonglong last_insert_id_arg,
- const char *message_arg)
-{
- DBUG_ENTER("set_ok_status");
- DBUG_ASSERT(! is_set());
- /*
- In production, refuse to overwrite an error or a custom response
- with an OK packet.
- */
- if (is_error() || is_disabled())
- return;
-
- m_server_status= thd->server_status;
- m_total_warn_count= thd->total_warn_count;
- m_affected_rows= affected_rows_arg;
- m_last_insert_id= last_insert_id_arg;
- if (message_arg)
- strmake(m_message, message_arg, sizeof(m_message) - 1);
- else
- m_message[0]= '\0';
- m_status= DA_OK;
- DBUG_VOID_RETURN;
-}
-
-
-/**
- Set EOF status.
-*/
-
-void
-Diagnostics_area::set_eof_status(THD *thd)
-{
- DBUG_ENTER("set_eof_status");
- /* Only allowed to report eof if has not yet reported an error */
- DBUG_ASSERT(! is_set());
- /*
- In production, refuse to overwrite an error or a custom response
- with an EOF packet.
- */
- if (is_error() || is_disabled())
- return;
-
- m_server_status= thd->server_status;
- /*
- If inside a stored procedure, do not return the total
- number of warnings, since they are not available to the client
- anyway.
- */
- m_total_warn_count= thd->spcont ? 0 : thd->total_warn_count;
-
- m_status= DA_EOF;
- DBUG_VOID_RETURN;
-}
-
-/**
- Set ERROR status.
-*/
-
-void
-Diagnostics_area::set_error_status(THD *thd, uint sql_errno_arg,
- const char *message_arg)
-{
- DBUG_ENTER("set_error_status");
- /*
- Only allowed to report error if has not yet reported a success
- The only exception is when we flush the message to the client,
- an error can happen during the flush.
- */
- DBUG_ASSERT(! is_set() || can_overwrite_status);
-#ifdef DBUG_OFF
- /*
- In production, refuse to overwrite a custom response with an
- ERROR packet.
- */
- if (is_disabled())
- return;
-#endif
-
- m_sql_errno= sql_errno_arg;
- strmake(m_message, message_arg, sizeof(m_message)-1);
-
- m_status= DA_ERROR;
- DBUG_VOID_RETURN;
-}
-
-
-/**
- Mark the diagnostics area as 'DISABLED'.
-
- This is used in rare cases when the COM_ command at hand sends a response
- in a custom format. One example is the query cache, another is
- COM_STMT_PREPARE.
-*/
-
-void
-Diagnostics_area::disable_status()
-{
- DBUG_ASSERT(! is_set());
- m_status= DA_DISABLED;
-}
-
-
THD::THD()
- :Statement(&main_lex, &main_mem_root, CONVENTIONAL_EXECUTION,
+ :Statement(&main_lex, &main_mem_root, STMT_CONVENTIONAL_EXECUTION,
/* statement id */ 0),
- Open_tables_state(refresh_version), rli_fake(NULL), rli_slave(NULL),
- lock_id(&main_lock_id),
- in_sub_stmt(0),
- sql_log_bin_toplevel(false), log_all_errors(0),
- binlog_table_maps(0), binlog_flags(0UL),
+ rli_fake(0), rli_slave(NULL),
+ in_sub_stmt(0), log_all_errors(0),
+ binlog_unsafe_warning_flags(0),
+ binlog_table_maps(0),
table_map_for_update(0),
arg_of_last_insert_id_function(FALSE),
first_successful_insert_id_in_prev_stmt(0),
@@ -676,7 +808,9 @@ THD::THD()
first_successful_insert_id_in_cur_stmt(0),
stmt_depends_on_first_successful_insert_id_in_prev_stmt(FALSE),
examined_row_count(0),
- global_read_lock(0),
+ accessed_rows_and_keys(0),
+ warning_info(&main_warning_info),
+ stmt_da(&main_da),
global_disable_checkpoint(0),
failed_com_change_user(0),
is_fatal_error(0),
@@ -688,13 +822,15 @@ THD::THD()
bootstrap(0),
derived_tables_processing(FALSE),
spcont(NULL),
- m_parser_state(NULL)
+ m_parser_state(NULL),
#if defined(ENABLED_DEBUG_SYNC)
- , debug_sync_control(0)
+ debug_sync_control(0),
#endif /* defined(ENABLED_DEBUG_SYNC) */
+ main_warning_info(0, false)
{
ulong tmp;
+ mdl_context.init(this);
/*
Pass nominal parameters to init_alloc_root only to ensure that
the destructor works OK in case of an error. The main_mem_root
@@ -703,31 +839,34 @@ THD::THD()
init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
stmt_arena= this;
thread_stack= 0;
- scheduler= &thread_scheduler; // Will be fixed later
+ scheduler= thread_scheduler; // Will be fixed later
+ event_scheduler.data= 0;
+ event_scheduler.m_psi= 0;
+ skip_wait_timeout= false;
extra_port= 0;
catalog= (char*)"std"; // the only catalog we have for now
main_security_ctx.init();
security_ctx= &main_security_ctx;
- some_tables_deleted=no_errors=password= 0;
+ no_errors= 0;
+ password= 0;
query_start_used= query_start_sec_part_used= 0;
count_cuted_fields= CHECK_FIELD_IGNORE;
killed= NOT_KILLED;
col_access=0;
is_slave_error= thread_specific_used= FALSE;
- hash_clear(&handler_tables_hash);
+ my_hash_clear(&handler_tables_hash);
tmp_table=0;
- cuted_fields= sent_row_count= row_count= 0L;
+ cuted_fields= 0L;
+ sent_row_count= 0L;
limit_found_rows= 0;
- row_count_func= -1;
+ m_row_count_func= -1;
statement_id_counter= 0UL;
-#ifdef ERROR_INJECT_SUPPORT
- error_inject_value= 0UL;
-#endif
// Must be reset to handle error with THD's created for init of mysqld
lex->current_select= 0;
user_time.val= start_time= start_time_sec_part= 0;
start_utime= prior_thr_create_utime= 0L;
utime_after_lock= 0L;
+ progress.arena= 0;
progress.report_to_client= 0;
progress.max_counter= 0;
current_linfo = 0;
@@ -738,7 +877,6 @@ THD::THD()
file_id = 0;
query_id= 0;
query_name_consts= 0;
- warn_id= 0;
db_charset= global_system_variables.collation_database;
bzero(ha_data, sizeof(ha_data));
mysys_var=0;
@@ -749,15 +887,13 @@ THD::THD()
dbug_sentry=THD_SENTRY_MAGIC;
#endif
#ifndef EMBEDDED_LIBRARY
- net.vio=0;
+ mysql_audit_init_thd(this);
#endif
+ net.vio=0;
client_capabilities= 0; // minimalistic client
-#ifdef HAVE_QUERY_CACHE
- query_cache_init_query(&net); // If error on boot
-#endif
ull=0;
system_thread= NON_SYSTEM_THREAD;
- cleanup_done= abort_on_warning= no_warnings_for_error= 0;
+ cleanup_done= abort_on_warning= 0;
peer_port= 0; // For SHOW PROCESSLIST
transaction.m_pending_rows_event= 0;
transaction.on= 1;
@@ -768,10 +904,14 @@ THD::THD()
#ifdef SIGNAL_WITH_VIO_CLOSE
active_vio = 0;
#endif
- pthread_mutex_init(&LOCK_thd_data, MY_MUTEX_INIT_FAST);
- pthread_mutex_init(&LOCK_wakeup_ready, MY_MUTEX_INIT_FAST);
- pthread_cond_init(&COND_wakeup_ready, 0);
- pthread_mutex_init(&LOCK_thd_kill, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_thd_data, &LOCK_thd_data, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_wakeup_ready, &LOCK_wakeup_ready, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_COND_wakeup_ready, &COND_wakeup_ready, 0);
+ /*
+ LOCK_thread_count goes before LOCK_thd_data - the former is called around
+ 'delete thd', the latter - in THD::~THD
+ */
+ mysql_mutex_record_order(&LOCK_thread_count, &LOCK_thd_data);
/* Variables with default values */
proc_info="login";
@@ -781,16 +921,17 @@ THD::THD()
command=COM_CONNECT;
*scramble= '\0';
+ /* Call to init() below requires fully initialized Open_tables_state. */
+ reset_open_tables_state(this);
+
init();
- /* Initialize sub structures */
- init_sql_alloc(&warn_root, WARN_ALLOC_BLOCK_SIZE, WARN_ALLOC_PREALLOC_SIZE);
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+#if defined(ENABLED_PROFILING)
profiling.set_thd(this);
#endif
user_connect=(USER_CONN *)0;
- hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0,
- (hash_get_key) get_var_key,
- (hash_free_key) free_user_var, 0);
+ my_hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0,
+ (my_hash_get_key) get_var_key,
+ (my_hash_free_key) free_user_var, 0);
sp_proc_cache= NULL;
sp_func_cache= NULL;
@@ -812,11 +953,10 @@ THD::THD()
my_rnd_init(&rand, tmp + (ulong) &rand, tmp + (ulong) ::global_query_id);
substitute_null_with_insert_id = FALSE;
thr_lock_info_init(&lock_info); /* safety: will be reset after start */
- thr_lock_owner_init(&main_lock_id, &lock_info);
m_internal_handler= NULL;
- arena_for_cached_items= 0;
m_binlog_invoker= FALSE;
+ arena_for_cached_items= 0;
memset(&invoker_user, 0, sizeof(invoker_user));
memset(&invoker_host, 0, sizeof(invoker_host));
prepare_derived_at_open= FALSE;
@@ -840,16 +980,27 @@ void THD::push_internal_handler(Internal_error_handler *handler)
DBUG_VOID_RETURN;
}
-
-bool THD::handle_error(uint sql_errno, const char *message,
- MYSQL_ERROR::enum_warning_level level)
+bool THD::handle_condition(uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl)
{
+ if (!m_internal_handler)
+ {
+ *cond_hdl= NULL;
+ return FALSE;
+ }
+
for (Internal_error_handler *error_handler= m_internal_handler;
error_handler;
error_handler= error_handler->m_prev_internal_handler)
{
- if (error_handler->handle_error(sql_errno, message, level, this))
+ if (error_handler->handle_condition(this, sql_errno, sqlstate, level, msg,
+ cond_hdl))
+ {
return TRUE;
+ }
}
return FALSE;
}
@@ -864,6 +1015,172 @@ Internal_error_handler *THD::pop_internal_handler()
DBUG_RETURN(popped_handler);
}
+
+void THD::raise_error(uint sql_errno)
+{
+ const char* msg= ER(sql_errno);
+ (void) raise_condition(sql_errno,
+ NULL,
+ MYSQL_ERROR::WARN_LEVEL_ERROR,
+ msg);
+}
+
+void THD::raise_error_printf(uint sql_errno, ...)
+{
+ va_list args;
+ char ebuff[MYSQL_ERRMSG_SIZE];
+ DBUG_ENTER("THD::raise_error_printf");
+ DBUG_PRINT("my", ("nr: %d errno: %d", sql_errno, errno));
+ const char* format= ER(sql_errno);
+ va_start(args, sql_errno);
+ my_vsnprintf(ebuff, sizeof(ebuff), format, args);
+ va_end(args);
+ (void) raise_condition(sql_errno,
+ NULL,
+ MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ebuff);
+ DBUG_VOID_RETURN;
+}
+
+void THD::raise_warning(uint sql_errno)
+{
+ const char* msg= ER(sql_errno);
+ (void) raise_condition(sql_errno,
+ NULL,
+ MYSQL_ERROR::WARN_LEVEL_WARN,
+ msg);
+}
+
+void THD::raise_warning_printf(uint sql_errno, ...)
+{
+ va_list args;
+ char ebuff[MYSQL_ERRMSG_SIZE];
+ DBUG_ENTER("THD::raise_warning_printf");
+ DBUG_PRINT("enter", ("warning: %u", sql_errno));
+ const char* format= ER(sql_errno);
+ va_start(args, sql_errno);
+ my_vsnprintf(ebuff, sizeof(ebuff), format, args);
+ va_end(args);
+ (void) raise_condition(sql_errno,
+ NULL,
+ MYSQL_ERROR::WARN_LEVEL_WARN,
+ ebuff);
+ DBUG_VOID_RETURN;
+}
+
+void THD::raise_note(uint sql_errno)
+{
+ DBUG_ENTER("THD::raise_note");
+ DBUG_PRINT("enter", ("code: %d", sql_errno));
+ if (!(variables.option_bits & OPTION_SQL_NOTES))
+ DBUG_VOID_RETURN;
+ const char* msg= ER(sql_errno);
+ (void) raise_condition(sql_errno,
+ NULL,
+ MYSQL_ERROR::WARN_LEVEL_NOTE,
+ msg);
+ DBUG_VOID_RETURN;
+}
+
+void THD::raise_note_printf(uint sql_errno, ...)
+{
+ va_list args;
+ char ebuff[MYSQL_ERRMSG_SIZE];
+ DBUG_ENTER("THD::raise_note_printf");
+ DBUG_PRINT("enter",("code: %u", sql_errno));
+ if (!(variables.option_bits & OPTION_SQL_NOTES))
+ DBUG_VOID_RETURN;
+ const char* format= ER(sql_errno);
+ va_start(args, sql_errno);
+ my_vsnprintf(ebuff, sizeof(ebuff), format, args);
+ va_end(args);
+ (void) raise_condition(sql_errno,
+ NULL,
+ MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ebuff);
+ DBUG_VOID_RETURN;
+}
+
+MYSQL_ERROR* THD::raise_condition(uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg)
+{
+ MYSQL_ERROR *cond= NULL;
+ DBUG_ENTER("THD::raise_condition");
+
+ if (!(variables.option_bits & OPTION_SQL_NOTES) &&
+ (level == MYSQL_ERROR::WARN_LEVEL_NOTE))
+ DBUG_RETURN(NULL);
+
+ warning_info->opt_clear_warning_info(query_id);
+
+ /*
+ TODO: replace by DBUG_ASSERT(sql_errno != 0) once all bugs similar to
+ Bug#36768 are fixed: a SQL condition must have a real (!=0) error number
+ so that it can be caught by handlers.
+ */
+ if (sql_errno == 0)
+ sql_errno= ER_UNKNOWN_ERROR;
+ if (msg == NULL)
+ msg= ER(sql_errno);
+ if (sqlstate == NULL)
+ sqlstate= mysql_errno_to_sqlstate(sql_errno);
+
+ if ((level == MYSQL_ERROR::WARN_LEVEL_WARN) &&
+ really_abort_on_warning())
+ {
+ /*
+ FIXME:
+ push_warning and strict SQL_MODE case.
+ */
+ level= MYSQL_ERROR::WARN_LEVEL_ERROR;
+ killed= KILL_BAD_DATA;
+ }
+
+ switch (level)
+ {
+ case MYSQL_ERROR::WARN_LEVEL_NOTE:
+ case MYSQL_ERROR::WARN_LEVEL_WARN:
+ got_warning= 1;
+ break;
+ case MYSQL_ERROR::WARN_LEVEL_ERROR:
+ break;
+ default:
+ DBUG_ASSERT(FALSE);
+ }
+
+ if (handle_condition(sql_errno, sqlstate, level, msg, &cond))
+ DBUG_RETURN(cond);
+
+ if (level == MYSQL_ERROR::WARN_LEVEL_ERROR)
+ {
+ mysql_audit_general(this, MYSQL_AUDIT_GENERAL_ERROR, sql_errno, msg);
+
+ is_slave_error= 1; // needed to catch query errors during replication
+
+ if (! stmt_da->is_error())
+ {
+ set_row_count_func(-1);
+ stmt_da->set_error_status(this, sql_errno, msg, sqlstate);
+ }
+ }
+
+ query_cache_abort(&query_cache_tls);
+
+ /*
+ Avoid pushing a condition for fatal out of memory errors as this will
+ 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)))
+ {
+ cond= warning_info->push_warning(this, sql_errno, sqlstate, level, msg);
+ }
+ DBUG_RETURN(cond);
+}
+
extern "C"
void *thd_alloc(MYSQL_THD thd, unsigned int size)
{
@@ -921,45 +1238,37 @@ extern "C" THD *_current_thd_noinline(void)
void THD::init(void)
{
- pthread_mutex_lock(&LOCK_global_system_variables);
+ mysql_mutex_lock(&LOCK_global_system_variables);
plugin_thdvar_init(this);
- variables.time_format= date_time_format_copy((THD*) 0,
- variables.time_format);
- variables.date_format= date_time_format_copy((THD*) 0,
- variables.date_format);
- variables.datetime_format= date_time_format_copy((THD*) 0,
- variables.datetime_format);
/*
variables= global_system_variables above has reset
variables.pseudo_thread_id to 0. We need to correct it here to
avoid temporary tables replication failure.
*/
variables.pseudo_thread_id= thread_id;
- pthread_mutex_unlock(&LOCK_global_system_variables);
+ mysql_mutex_unlock(&LOCK_global_system_variables);
server_status= SERVER_STATUS_AUTOCOMMIT;
if (variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES)
server_status|= SERVER_STATUS_NO_BACKSLASH_ESCAPES;
- options= thd_startup_options;
- if (variables.max_join_size == HA_POS_ERROR)
- options |= OPTION_BIG_SELECTS;
- else
- options &= ~OPTION_BIG_SELECTS;
-
- transaction.all.modified_non_trans_table= transaction.stmt.modified_non_trans_table= FALSE;
+ transaction.all.modified_non_trans_table=
+ transaction.stmt.modified_non_trans_table= FALSE;
open_options=ha_open_options;
update_lock_default= (variables.low_priority_updates ?
TL_WRITE_LOW_PRIORITY :
TL_WRITE);
- session_tx_isolation= (enum_tx_isolation) variables.tx_isolation;
- warn_list.empty();
- bzero((char*) warn_count, sizeof(warn_count));
- total_warn_count= 0;
+ tx_isolation= (enum_tx_isolation) variables.tx_isolation;
update_charset();
- reset_current_stmt_binlog_row_based();
+ reset_current_stmt_binlog_format_row();
bzero((char *) &status_var, sizeof(status_var));
bzero((char *) &org_status_var, sizeof(org_status_var));
- sql_log_bin_toplevel= options & OPTION_BIN_LOG;
+ start_bytes_received= 0;
+
+ if (variables.sql_log_bin)
+ variables.option_bits|= OPTION_BIN_LOG;
+ else
+ variables.option_bits&= ~OPTION_BIN_LOG;
+
select_commands= update_commands= other_commands= 0;
/* Set to handle counting of aborted connections */
userstat_running= opt_userstat_running;
@@ -998,11 +1307,6 @@ void THD::update_all_stats()
ulonglong end_cpu_time, end_utime;
double busy_time, cpu_time;
- /* Reset status variables used by information_schema.processlist */
- progress.max_counter= 0;
- progress.max_stage= 0;
- progress.report= 0;
-
/* This is set at start of query if opt_userstat_running was set */
if (!userstat_running)
return;
@@ -1036,11 +1340,9 @@ void THD::init_for_queries()
reset_root_defaults(mem_root, variables.query_alloc_block_size,
variables.query_prealloc_size);
-#ifdef USING_TRANSACTIONS
reset_root_defaults(&transaction.mem_root,
variables.trans_alloc_block_size,
variables.trans_prealloc_size);
-#endif
transaction.xid_state.xid.null();
transaction.xid_state.in_thd=1;
}
@@ -1059,18 +1361,16 @@ void THD::init_for_queries()
void THD::change_user(void)
{
- pthread_mutex_lock(&LOCK_status);
- add_to_status(&global_status_var, &status_var);
- pthread_mutex_unlock(&LOCK_status);
+ add_status_to_global();
cleanup();
- killed= NOT_KILLED;
+ reset_killed();
cleanup_done= 0;
init();
stmt_map.reset();
- hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0,
- (hash_get_key) get_var_key,
- (hash_free_key) free_user_var, 0);
+ my_hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0,
+ (my_hash_get_key) get_var_key,
+ (my_hash_free_key) free_user_var, 0);
sp_cache_clear(&sp_proc_cache);
sp_cache_clear(&sp_func_cache);
}
@@ -1090,15 +1390,31 @@ void THD::cleanup(void)
#error xid_state in the cache should be replaced by the allocated value
}
#endif
- {
- ha_rollback(this);
- xid_cache_delete(&transaction.xid_state);
- }
- if (locked_tables)
- {
- lock=locked_tables; locked_tables=0;
- close_thread_tables(this);
- }
+
+ mysql_ha_cleanup(this);
+ locked_tables_list.unlock_locked_tables(this);
+
+ close_temporary_tables(this);
+
+ transaction.xid_state.xa_state= XA_NOTR;
+ trans_rollback(this);
+ xid_cache_delete(&transaction.xid_state);
+
+ DBUG_ASSERT(open_tables == NULL);
+ /*
+ If the thread was in the middle of an ongoing transaction (rolled
+ back a few lines above) or under LOCK TABLES (unlocked the tables
+ and left the mode a few lines above), there will be outstanding
+ metadata locks. Release them.
+ */
+ mdl_context.release_transactional_locks();
+
+ /* Release the global read lock, if acquired. */
+ if (global_read_lock.is_acquired())
+ global_read_lock.unlock_global_read_lock(this);
+
+ /* All metadata locks must have been released by now. */
+ DBUG_ASSERT(!mdl_context.has_locks());
if (user_connect)
{
decrease_user_connections(user_connect);
@@ -1111,24 +1427,16 @@ void THD::cleanup(void)
debug_sync_end_thread(this);
#endif /* defined(ENABLED_DEBUG_SYNC) */
- mysql_ha_cleanup(this);
delete_dynamic(&user_var_events);
- hash_free(&user_vars);
- close_temporary_tables(this);
- my_free((char*) variables.time_format, MYF(MY_ALLOW_ZERO_PTR));
- my_free((char*) variables.date_format, MYF(MY_ALLOW_ZERO_PTR));
- my_free((char*) variables.datetime_format, MYF(MY_ALLOW_ZERO_PTR));
-
+ my_hash_free(&user_vars);
sp_cache_clear(&sp_proc_cache);
sp_cache_clear(&sp_func_cache);
- if (global_read_lock)
- unlock_global_read_lock(this);
if (ull)
{
- pthread_mutex_lock(&LOCK_user_locks);
+ mysql_mutex_lock(&LOCK_user_locks);
item_user_lock_release(ull);
- pthread_mutex_unlock(&LOCK_user_locks);
+ mysql_mutex_unlock(&LOCK_user_locks);
ull= NULL;
}
@@ -1142,11 +1450,8 @@ THD::~THD()
THD_CHECK_SENTRY(this);
DBUG_ENTER("~THD()");
/* Ensure that no one is using THD */
- pthread_mutex_lock(&LOCK_thd_data);
- pthread_mutex_unlock(&LOCK_thd_data);
- pthread_mutex_lock(&LOCK_thd_kill);
- pthread_mutex_unlock(&LOCK_thd_kill);
- add_to_status(&global_status_var, &status_var);
+ mysql_mutex_lock(&LOCK_thd_data);
+ mysql_mutex_unlock(&LOCK_thd_data);
/* Close connection */
#ifndef EMBEDDED_LIBRARY
@@ -1157,26 +1462,22 @@ THD::~THD()
}
#endif
stmt_map.reset(); /* close all prepared statements */
- DBUG_ASSERT(lock_info.n_cursors == 0);
-
if (!cleanup_done)
cleanup();
+ mdl_context.destroy();
ha_close_connection(this);
+ mysql_audit_release(this);
plugin_thdvar_cleanup(this);
DBUG_PRINT("info", ("freeing security context"));
main_security_ctx.destroy();
- safeFree(db);
- free_root(&warn_root,MYF(0));
-#ifdef USING_TRANSACTIONS
+ my_free(db);
+ db= NULL;
free_root(&transaction.mem_root,MYF(0));
-#endif
- mysys_var=0; // Safety (shouldn't be needed)
- pthread_cond_destroy(&COND_wakeup_ready);
- pthread_mutex_destroy(&LOCK_wakeup_ready);
- pthread_mutex_destroy(&LOCK_thd_data);
- pthread_mutex_destroy(&LOCK_thd_kill);
+ mysql_cond_destroy(&COND_wakeup_ready);
+ mysql_mutex_destroy(&LOCK_wakeup_ready);
+ mysql_mutex_destroy(&LOCK_thd_data);
#ifndef DBUG_OFF
dbug_sentry= THD_SENTRY_GONE;
#endif
@@ -1186,6 +1487,8 @@ THD::~THD()
delete rli_fake;
rli_fake= NULL;
}
+
+ mysql_audit_free_thd(this);
if (rli_slave)
rli_slave->cleanup_after_session();
#endif
@@ -1275,52 +1578,54 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
#endif
+/**
+ Awake a thread.
+
+ @param[in] state_to_set value for THD::killed
+
+ This is normally called from another thread's THD object.
+
+ @note Do always call this while holding LOCK_thd_data.
+*/
+
void THD::awake(killed_state state_to_set)
{
DBUG_ENTER("THD::awake");
- DBUG_PRINT("enter", ("this: 0x%lx thread_id=%lu killed_state=%d",
- (long) this, thread_id, state_to_set));
+ DBUG_PRINT("enter", ("this: %p current_thd: %p", this, current_thd));
THD_CHECK_SENTRY(this);
- safe_mutex_assert_not_owner(&LOCK_thd_data);
- safe_mutex_assert_owner(&LOCK_thd_kill);
+ mysql_mutex_assert_owner(&LOCK_thd_data);
- if (global_system_variables.log_warnings > 3)
- {
- Security_context *sctx= security_ctx;
- sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
- thread_id,(db ? db : "unconnected"),
- sctx->user ? sctx->user : "unauthenticated",
- sctx->host_or_ip,
- "KILLED");
- }
+ print_aborted_warning(3, "KILLED");
+
+ /* Set the 'killed' flag of 'this', which is the target THD object. */
killed= state_to_set;
- if (state_to_set >= KILL_CONNECTION)
+
+ if (state_to_set >= KILL_CONNECTION || state_to_set == NOT_KILLED)
{
- thr_alarm_kill(thread_id);
- if (!slave_thread)
- thread_scheduler.post_kill_notification(this);
#ifdef SIGNAL_WITH_VIO_CLOSE
if (this != current_thd)
{
- /*
- In addition to a signal, let's close the socket of the thread that
- is being killed. This is to make sure it does not block if the
- signal is lost. This needs to be done only on platforms where
- signals are not a reliable interruption mechanism.
+ if(active_vio)
+ vio_shutdown(active_vio, SHUT_RDWR);
+ }
+#endif
- If we're killing ourselves, we know that we're not blocked, so this
- hack is not used.
- */
+ /* Mark the target thread's alarm request expired, and signal alarm. */
+ thr_alarm_kill(thread_id);
- pthread_mutex_lock(&LOCK_thd_data);
- close_active_vio();
- pthread_mutex_unlock(&LOCK_thd_data);
- }
-#endif
+ /* Send an event to the scheduler that a thread should be killed. */
+ if (!slave_thread)
+ MYSQL_CALLBACK(scheduler, post_kill_notification, (this));
}
+
+ /* Interrupt target waiting inside a storage engine. */
+ if (state_to_set != NOT_KILLED)
+ ha_kill_query(this, thd_kill_level(this));
+
+ /* Broadcast a condition to kick the target if it is waiting on it. */
if (mysys_var)
{
- pthread_mutex_lock(&mysys_var->mutex);
+ mysql_mutex_lock(&mysys_var->mutex);
if (!system_thread) // Don't abort locks
mysys_var->abort=1;
/*
@@ -1342,6 +1647,11 @@ void THD::awake(killed_state state_to_set)
It's true that we have set its thd->killed but it may not
see it immediately and so may have time to reach the cond_wait().
+ However, where possible, we test for killed once again after
+ enter_cond(). This should make the signaling as safe as possible.
+ However, there is still a small chance of failure on platforms with
+ instruction or memory write reordering.
+
We have to do the loop with trylock, because if we would use
pthread_mutex_lock(), we can cause a deadlock as we are here locking
the mysys_var->mutex and mysys_var->current_mutex in a different order
@@ -1360,23 +1670,56 @@ void THD::awake(killed_state state_to_set)
uint i;
for (i= 0; i < WAIT_FOR_KILL_TRY_TIMES * SECONDS_TO_WAIT_FOR_KILL; i++)
{
- int ret= pthread_mutex_trylock(mysys_var->current_mutex);
- pthread_cond_broadcast(mysys_var->current_cond);
+ int ret= mysql_mutex_trylock(mysys_var->current_mutex);
+ mysql_cond_broadcast(mysys_var->current_cond);
if (!ret)
{
/* Signal is sure to get through */
- pthread_mutex_unlock(mysys_var->current_mutex);
+ mysql_mutex_unlock(mysys_var->current_mutex);
break;
}
+ my_sleep(1000000L / WAIT_FOR_KILL_TRY_TIMES);
}
- my_sleep(1000000L / WAIT_FOR_KILL_TRY_TIMES);
}
- pthread_mutex_unlock(&mysys_var->mutex);
+ mysql_mutex_unlock(&mysys_var->mutex);
}
DBUG_VOID_RETURN;
}
+/**
+ Close the Vio associated this session.
+
+ @remark LOCK_thd_data is taken due to the fact that
+ the Vio might be disassociated concurrently.
+*/
+
+void THD::disconnect()
+{
+ Vio *vio= NULL;
+
+ mysql_mutex_lock(&LOCK_thd_data);
+
+ killed= KILL_CONNECTION;
+
+#ifdef SIGNAL_WITH_VIO_CLOSE
+ /*
+ Since a active vio might might have not been set yet, in
+ any case save a reference to avoid closing a inexistent
+ one or closing the vio twice if there is a active one.
+ */
+ vio= active_vio;
+ close_active_vio();
+#endif
+
+ /* Disconnect even if a active vio is not associated. */
+ if (net.vio != vio)
+ vio_close(net.vio);
+
+ mysql_mutex_unlock(&LOCK_thd_data);
+}
+
+
/*
Get error number for killed state
Note that the error message can't have any parameters.
@@ -1385,26 +1728,31 @@ void THD::awake(killed_state state_to_set)
int killed_errno(killed_state killed)
{
+ DBUG_ENTER("killed_errno");
+ DBUG_PRINT("enter", ("killed: %d", killed));
+
switch (killed) {
case NOT_KILLED:
case KILL_HARD_BIT:
- return 0; // Probably wrong usage
+ DBUG_RETURN(0); // Probably wrong usage
case KILL_BAD_DATA:
case KILL_BAD_DATA_HARD:
- return 0; // Not a real error
+ case ABORT_QUERY_HARD:
+ case ABORT_QUERY:
+ DBUG_RETURN(0); // Not a real error
case KILL_CONNECTION:
case KILL_CONNECTION_HARD:
case KILL_SYSTEM_THREAD:
case KILL_SYSTEM_THREAD_HARD:
- return ER_CONNECTION_KILLED;
+ DBUG_RETURN(ER_CONNECTION_KILLED);
case KILL_QUERY:
case KILL_QUERY_HARD:
- return ER_QUERY_INTERRUPTED;
+ DBUG_RETURN(ER_QUERY_INTERRUPTED);
case KILL_SERVER:
case KILL_SERVER_HARD:
- return ER_SERVER_SHUTDOWN;
+ DBUG_RETURN(ER_SERVER_SHUTDOWN);
}
- return 0; // Keep compiler happy
+ DBUG_RETURN(0); // Keep compiler happy
}
@@ -1424,6 +1772,15 @@ bool THD::store_globals()
if (my_pthread_setspecific_ptr(THR_THD, this) ||
my_pthread_setspecific_ptr(THR_MALLOC, &mem_root))
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
+ 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
+ have the mysys_var reference (which in fact refers to the worker
+ threads local storage with key THR_KEY_mysys.
+ */
mysys_var=my_thread_var;
/*
Let mysqld define the thread id (not mysys)
@@ -1433,44 +1790,16 @@ bool THD::store_globals()
real_id= pthread_self(); // For debugging
mysys_var->stack_ends_here= thread_stack + // for consistency, see libevent_thread_proc
STACK_DIRECTION * (long)my_thread_stack_size;
-
+ vio_set_thread_id(net.vio, real_id);
/*
We have to call thr_lock_info_init() again here as THD may have been
created in another thread
*/
thr_lock_info_init(&lock_info);
-#ifdef SAFE_MUTEX
- /* Register order of mutex for wrong mutex deadlock detector */
- pthread_mutex_lock(&LOCK_thd_data);
- pthread_mutex_lock(&mysys_var->mutex);
-
- pthread_mutex_unlock(&mysys_var->mutex);
- pthread_mutex_unlock(&LOCK_thd_data);
-#endif
- return 0;
-}
-
-/*
- Remove the thread specific info (THD and mem_root pointer) stored during
- store_global call for this thread.
-*/
-bool THD::restore_globals()
-{
- /*
- Assert that thread_stack is initialized: it's necessary to be able
- to track stack overrun.
- */
- DBUG_ASSERT(thread_stack);
-
- /* Undocking the thread specific data. */
- my_pthread_setspecific_ptr(THR_THD, NULL);
- my_pthread_setspecific_ptr(THR_MALLOC, NULL);
-
return 0;
}
-
/**
Untie THD from current thread
@@ -1479,9 +1808,14 @@ bool THD::restore_globals()
void THD::reset_globals()
{
- pthread_mutex_lock(&LOCK_thd_data);
+ mysql_mutex_lock(&LOCK_thd_data);
mysys_var= 0;
- pthread_mutex_unlock(&LOCK_thd_data);
+ mysql_mutex_unlock(&LOCK_thd_data);
+
+ /* Undocking the thread specific data. */
+ my_pthread_setspecific_ptr(THR_THD, NULL);
+ my_pthread_setspecific_ptr(THR_MALLOC, NULL);
+
}
/*
@@ -1502,6 +1836,10 @@ void THD::reset_globals()
void THD::cleanup_after_query()
{
+ DBUG_ENTER("THD::cleanup_after_query");
+
+ thd_progress_end(this);
+
/*
Reset rand_used so that detection of calls to rand() will save random
seeds if needed by the slave.
@@ -1518,6 +1856,19 @@ void THD::cleanup_after_query()
stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;
auto_inc_intervals_in_cur_stmt_for_binlog.empty();
rand_used= 0;
+#ifndef EMBEDDED_LIBRARY
+ /*
+ Clean possible unused INSERT_ID events by current statement.
+ is_update_query() is needed to ignore SET statements:
+ Statements that don't update anything directly and don't
+ used stored functions. This is mostly necessary to ignore
+ statements in binlog between SET INSERT_ID and DML statement
+ which is intended to consume its event (there can be other
+ SET statements between them).
+ */
+ if ((rli_slave || rli_fake) && is_update_query(lex->sql_command))
+ auto_inc_intervals_forced.empty();
+#endif
}
if (first_successful_insert_id_in_cur_stmt > 0)
{
@@ -1535,10 +1886,13 @@ void THD::cleanup_after_query()
/* reset table map for multi-table update */
table_map_for_update= 0;
m_binlog_invoker= FALSE;
+
#ifndef EMBEDDED_LIBRARY
if (rli_slave)
rli_slave->cleanup_after_query();
#endif
+
+ DBUG_VOID_RETURN;
}
@@ -1557,7 +1911,7 @@ LEX_STRING *THD::make_lex_string(LEX_STRING *lex_str,
bool allocate_lex_string)
{
if (allocate_lex_string)
- if (!(lex_str= (LEX_STRING *)alloc(sizeof(LEX_STRING))))
+ if (!(lex_str= (LEX_STRING *)alloc_root(mem_root, sizeof(LEX_STRING))))
return 0;
if (!(lex_str->str= strmake_root(mem_root, str, length)))
return 0;
@@ -1643,15 +1997,21 @@ bool THD::convert_string(String *s, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs)
void THD::update_charset()
{
uint32 not_used;
- charset_is_system_charset= !String::needs_conversion(0,charset(),
- system_charset_info,
- &not_used);
+ charset_is_system_charset=
+ !String::needs_conversion(0,
+ variables.character_set_client,
+ system_charset_info,
+ &not_used);
charset_is_collation_connection=
- !String::needs_conversion(0,charset(),variables.collation_connection,
+ !String::needs_conversion(0,
+ variables.character_set_client,
+ variables.collation_connection,
&not_used);
charset_is_character_set_filesystem=
- !String::needs_conversion(0, charset(),
- variables.character_set_filesystem, &not_used);
+ !String::needs_conversion(0,
+ variables.character_set_client,
+ variables.character_set_filesystem,
+ &not_used);
}
@@ -1674,8 +2034,7 @@ void THD::add_changed_table(TABLE *table)
{
DBUG_ENTER("THD::add_changed_table(table)");
- DBUG_ASSERT((options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) &&
- table->file->has_transactions());
+ DBUG_ASSERT(in_multi_stmt_transaction_mode() && table->file->has_transactions());
add_changed_table(table->s->table_cache_key.str,
(long) table->s->table_cache_key.length);
DBUG_VOID_RETURN;
@@ -1731,7 +2090,7 @@ CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, long key_length)
key_length + 1);
if (!new_table)
{
- my_error(EE_OUTOFMEMORY, MYF(ME_BELL),
+ my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_FATALERROR),
ALIGN_SIZE(sizeof(TABLE_LIST)) + key_length + 1);
killed= KILL_CONNECTION;
return 0;
@@ -1750,7 +2109,8 @@ int THD::send_explain_fields(select_result *result)
List<Item> field_list;
Item *item;
CHARSET_INFO *cs= system_charset_info;
- field_list.push_back(new Item_return_int("id",3, MYSQL_TYPE_LONGLONG));
+ field_list.push_back(item= new Item_return_int("id",3, MYSQL_TYPE_LONGLONG));
+ item->maybe_null= 1;
field_list.push_back(new Item_empty_string("select_type", 19, cs));
field_list.push_back(item= new Item_empty_string("table", NAME_CHAR_LEN, cs));
item->maybe_null= 1;
@@ -1785,15 +2145,15 @@ int THD::send_explain_fields(select_result *result)
}
item->maybe_null= 1;
field_list.push_back(new Item_empty_string("Extra", 255, cs));
- return (result->send_fields(field_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF));
+ return (result->send_result_set_metadata(field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF));
}
#ifdef SIGNAL_WITH_VIO_CLOSE
void THD::close_active_vio()
{
DBUG_ENTER("close_active_vio");
- safe_mutex_assert_owner(&LOCK_thd_data);
+ mysql_mutex_assert_owner(&LOCK_thd_data);
#ifndef EMBEDDED_LIBRARY
if (active_vio)
{
@@ -1899,7 +2259,6 @@ void THD::rollback_item_tree_changes()
select_result::select_result()
{
thd=current_thd;
- nest_level= (uint) -1;
}
void select_result::send_error(uint errcode,const char *err)
@@ -1924,13 +2283,17 @@ static String default_line_term("\n",default_charset_info);
static String default_escaped("\\",default_charset_info);
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(char *name, bool flag,
+ enum enum_filetype filetype_arg)
:file_name(name), opt_enclosed(0), dumpfile(flag), skip_lines(0)
{
+ filetype= filetype_arg;
field_term= &default_field_term;
enclosed= line_start= &default_enclosed_and_line_start;
- line_term= &default_line_term;
+ line_term= filetype == FILETYPE_CSV ?
+ &default_line_term : &default_xml_row_term;
escaped= &default_escaped;
cs= NULL;
}
@@ -1941,32 +2304,30 @@ bool sql_exchange::escaped_given(void)
}
-bool select_send::send_fields(List<Item> &list, uint flags)
+bool select_send::send_result_set_metadata(List<Item> &list, uint flags)
{
bool res;
- if (!(res= thd->protocol->send_fields(&list, flags)))
+ if (!(res= thd->protocol->send_result_set_metadata(&list, flags)))
is_result_set_started= 1;
return res;
}
-void select_send::abort()
+void select_send::abort_result_set()
{
- DBUG_ENTER("select_send::abort");
- if (is_result_set_started && thd->spcont &&
- thd->spcont->find_handler(thd, thd->main_da.sql_errno(),
- MYSQL_ERROR::WARN_LEVEL_ERROR))
+ DBUG_ENTER("select_send::abort_result_set");
+
+ if (is_result_set_started && thd->spcont)
{
/*
We're executing a stored procedure, have an open result
- set, an SQL exception condition and a handler for it.
- In this situation we must abort the current statement,
- silence the error and start executing the continue/exit
- handler.
+ set and an SQL exception condition. In this situation we
+ must abort the current statement, silence the error and
+ start executing the continue/exit handler if one is found.
Before aborting the statement, let's end the open result set, as
otherwise the client will hang due to the violation of the
client/server protocol.
*/
- thd->protocol->end_partial_result_set(thd);
+ thd->spcont->end_partial_result_set= TRUE;
}
DBUG_VOID_RETURN;
}
@@ -1987,11 +2348,16 @@ void select_send::cleanup()
int select_send::send_data(List<Item> &items)
{
+ Protocol *protocol= thd->protocol;
+ DBUG_ENTER("select_send::send_data");
+
if (unit->offset_limit_cnt)
{ // using limit offset,count
unit->offset_limit_cnt--;
- return 0;
+ DBUG_RETURN(FALSE);
}
+ if (thd->killed == ABORT_QUERY)
+ DBUG_RETURN(FALSE);
/*
We may be passing the control from mysqld to the client: release the
@@ -2000,36 +2366,18 @@ int select_send::send_data(List<Item> &items)
*/
ha_release_temporary_latches(thd);
- List_iterator_fast<Item> li(items);
- Protocol *protocol= thd->protocol;
- char buff[MAX_FIELD_WIDTH];
- String buffer(buff, sizeof(buff), &my_charset_bin);
- DBUG_ENTER("select_send::send_data");
-
protocol->prepare_for_resend();
- Item *item;
- while ((item=li++))
- {
- if (item->send(protocol, &buffer))
- {
- protocol->free(); // Free used buffer
- my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
- break;
- }
- /*
- Reset buffer to its original state, as it may have been altered in
- Item::send().
- */
- buffer.set(buff, sizeof(buff), &my_charset_bin);
- }
- thd->sent_row_count++;
- if (thd->is_error())
+ if (protocol->send_result_set_row(&items))
{
protocol->remove_last_row();
- DBUG_RETURN(1);
+ DBUG_RETURN(TRUE);
}
+
+ thd->sent_row_count++;
+
if (thd->vio_ok())
DBUG_RETURN(protocol->write());
+
DBUG_RETURN(0);
}
@@ -2042,12 +2390,6 @@ bool select_send::send_eof()
*/
ha_release_temporary_latches(thd);
- /* Unlock tables before sending packet to gain some speed */
- if (thd->lock)
- {
- mysql_unlock_tables(thd, thd->lock);
- thd->lock=0;
- }
/*
Don't send EOF if we're in error condition (which implies we've already
sent or are sending an error)
@@ -2070,8 +2412,9 @@ void select_to_file::send_error(uint errcode,const char *err)
if (file > 0)
{
(void) end_io_cache(&cache);
- (void) my_close(file,MYF(0));
- (void) my_delete(path,MYF(0)); // Delete file on error
+ mysql_file_close(file, MYF(0));
+ /* Delete file on error */
+ mysql_file_delete(key_select_to_file, path, MYF(0));
file= -1;
}
}
@@ -2080,15 +2423,11 @@ void select_to_file::send_error(uint errcode,const char *err)
bool select_to_file::send_eof()
{
int error= test(end_io_cache(&cache));
- if (my_close(file,MYF(MY_WME)))
- error= 1;
+ if (mysql_file_close(file, MYF(MY_WME)) || thd->is_error())
+ error= true;
+
if (!error)
{
- /*
- In order to remember the value of affected rows for ROW_COUNT()
- function, SELECT INTO has to have an own SQLCOM.
- TODO: split from SQLCOM_SELECT
- */
::my_ok(thd,row_count);
}
file= -1;
@@ -2102,7 +2441,7 @@ void select_to_file::cleanup()
if (file >= 0)
{
(void) end_io_cache(&cache);
- (void) my_close(file,MYF(0));
+ mysql_file_close(file, MYF(0));
file= -1;
}
path[0]= '\0';
@@ -2115,7 +2454,7 @@ select_to_file::~select_to_file()
if (file >= 0)
{ // This only happens in case of error
(void) end_io_cache(&cache);
- (void) my_close(file,MYF(0));
+ mysql_file_close(file, MYF(0));
file= -1;
}
}
@@ -2178,7 +2517,8 @@ static File create_file(THD *thd, char *path, sql_exchange *exchange,
return -1;
}
/* Create the file world readable */
- if ((file= my_create(path, 0666, O_WRONLY|O_EXCL, MYF(MY_WME))) < 0)
+ if ((file= mysql_file_create(key_select_to_file,
+ path, 0666, O_WRONLY|O_EXCL, MYF(MY_WME))) < 0)
return file;
#ifdef HAVE_FCHMOD
(void) fchmod(file, 0666); // Because of umask()
@@ -2187,8 +2527,9 @@ static File create_file(THD *thd, char *path, sql_exchange *exchange,
#endif
if (init_io_cache(cache, file, 0L, WRITE_CACHE, 0L, 1, MYF(MY_WME)))
{
- my_close(file, MYF(0));
- my_delete(path, MYF(0)); // Delete file on error, it was just created
+ mysql_file_close(file, MYF(0));
+ /* Delete file on error, it was just created */
+ mysql_file_delete(key_select_to_file, path, MYF(0));
return -1;
}
return file;
@@ -2202,7 +2543,7 @@ select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
bool string_results= FALSE, non_string_results= FALSE;
unit= u;
if ((uint) strlen(exchange->file_name) + NAME_LEN >= FN_REFLEN)
- strmake(path,exchange->file_name,FN_REFLEN-1);
+ strmake_buf(path,exchange->file_name);
write_cs= exchange->cs ? exchange->cs : &my_charset_bin;
@@ -2310,6 +2651,8 @@ int select_export::send_data(List<Item> &items)
unit->offset_limit_cnt--;
DBUG_RETURN(0);
}
+ if (thd->killed == ABORT_QUERY)
+ DBUG_RETURN(0);
row_count++;
Item *item;
uint used_length=0,items_left=items.elements;
@@ -2338,7 +2681,7 @@ int select_export::send_data(List<Item> &items)
set_if_smaller(estimated_bytes, UINT_MAX32);
if (cvt_str.realloc((uint32) estimated_bytes))
{
- my_error(ER_OUTOFMEMORY, MYF(0), (uint32) estimated_bytes);
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), (uint32) estimated_bytes);
goto err;
}
@@ -2362,7 +2705,7 @@ int select_export::send_data(List<Item> &items)
ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
"string", printable_buff,
- item->name, (ulong) row_count);
+ item->name, static_cast<long>(row_count));
}
else if (from_end_pos < res->ptr() + res->length())
{
@@ -2565,6 +2908,9 @@ int select_dump::send_data(List<Item> &items)
unit->offset_limit_cnt--;
DBUG_RETURN(0);
}
+ if (thd->killed == ABORT_QUERY)
+ DBUG_RETURN(0);
+
if (row_count++ > 1)
{
my_message(ER_TOO_MANY_ROWS, ER(ER_TOO_MANY_ROWS), MYF(0));
@@ -2611,6 +2957,8 @@ int select_singlerow_subselect::send_data(List<Item> &items)
unit->offset_limit_cnt--;
DBUG_RETURN(0);
}
+ if (thd->killed == ABORT_QUERY)
+ DBUG_RETURN(0);
List_iterator_fast<Item> li(items);
Item *val_item;
for (uint i= 0; (val_item= li++); i++)
@@ -2754,6 +3102,8 @@ int select_exists_subselect::send_data(List<Item> &items)
unit->offset_limit_cnt--;
DBUG_RETURN(0);
}
+ if (thd->killed == ABORT_QUERY)
+ DBUG_RETURN(0);
it->value= 1;
it->assigned(1);
DBUG_RETURN(0);
@@ -2837,12 +3187,9 @@ Statement::Statement(LEX *lex_arg, MEM_ROOT *mem_root_arg,
id(id_arg),
mark_used_columns(MARK_COLUMNS_READ),
lex(lex_arg),
- cursor(0),
db(NULL),
db_length(0)
{
- query_string.length= 0;
- query_string.str= NULL;
name.str= NULL;
}
@@ -2859,7 +3206,6 @@ void Statement::set_statement(Statement *stmt)
mark_used_columns= stmt->mark_used_columns;
lex= stmt->lex;
query_string= stmt->query_string;
- cursor= stmt->cursor;
}
@@ -2882,17 +3228,9 @@ void Statement::restore_backup_statement(Statement *stmt, Statement *backup)
}
-/** Assign a new value to thd->query. */
-
-void Statement::set_query_inner(char *query_arg, uint32 query_length_arg)
-{
- query_string.str= query_arg;
- query_string.length= query_length_arg;
-}
-
-
void THD::end_statement()
{
+ DBUG_ENTER("THD::end_statement");
/* Cleanup SQL processing state to reuse this statement in next query. */
lex_end(lex);
delete lex->result;
@@ -2903,6 +3241,7 @@ void THD::end_statement()
Don't free mem_root, as mem_root is freed in the end of dispatch_command
(once for any command).
*/
+ DBUG_VOID_RETURN;
}
@@ -2969,12 +3308,12 @@ Statement_map::Statement_map() :
START_STMT_HASH_SIZE = 16,
START_NAME_HASH_SIZE = 16
};
- hash_init(&st_hash, &my_charset_bin, START_STMT_HASH_SIZE, 0, 0,
- get_statement_id_as_hash_key,
- delete_statement_as_hash_key, MYF(0));
- hash_init(&names_hash, system_charset_info, START_NAME_HASH_SIZE, 0, 0,
- (hash_get_key) get_stmt_name_hash_key,
- NULL,MYF(0));
+ my_hash_init(&st_hash, &my_charset_bin, START_STMT_HASH_SIZE, 0, 0,
+ get_statement_id_as_hash_key,
+ delete_statement_as_hash_key, MYF(0));
+ my_hash_init(&names_hash, system_charset_info, START_NAME_HASH_SIZE, 0, 0,
+ (my_hash_get_key) get_stmt_name_hash_key,
+ NULL,MYF(0));
}
@@ -3016,7 +3355,7 @@ int Statement_map::insert(THD *thd, Statement *statement)
my_error(ER_OUT_OF_RESOURCES, MYF(0));
goto err_names_hash;
}
- pthread_mutex_lock(&LOCK_prepared_stmt_count);
+ mysql_mutex_lock(&LOCK_prepared_stmt_count);
/*
We don't check that prepared_stmt_count is <= max_prepared_stmt_count
because we would like to allow to lower the total limit
@@ -3026,22 +3365,22 @@ int Statement_map::insert(THD *thd, Statement *statement)
*/
if (prepared_stmt_count >= max_prepared_stmt_count)
{
- pthread_mutex_unlock(&LOCK_prepared_stmt_count);
+ mysql_mutex_unlock(&LOCK_prepared_stmt_count);
my_error(ER_MAX_PREPARED_STMT_COUNT_REACHED, MYF(0),
max_prepared_stmt_count);
goto err_max;
}
prepared_stmt_count++;
- pthread_mutex_unlock(&LOCK_prepared_stmt_count);
+ mysql_mutex_unlock(&LOCK_prepared_stmt_count);
last_found_statement= statement;
return 0;
err_max:
if (statement->name.str)
- hash_delete(&names_hash, (uchar*) statement);
+ my_hash_delete(&names_hash, (uchar*) statement);
err_names_hash:
- hash_delete(&st_hash, (uchar*) statement);
+ my_hash_delete(&st_hash, (uchar*) statement);
err_st_hash:
return 1;
}
@@ -3062,23 +3401,23 @@ void Statement_map::erase(Statement *statement)
if (statement == last_found_statement)
last_found_statement= 0;
if (statement->name.str)
- hash_delete(&names_hash, (uchar *) statement);
+ my_hash_delete(&names_hash, (uchar *) statement);
- hash_delete(&st_hash, (uchar *) statement);
- pthread_mutex_lock(&LOCK_prepared_stmt_count);
+ my_hash_delete(&st_hash, (uchar *) statement);
+ mysql_mutex_lock(&LOCK_prepared_stmt_count);
DBUG_ASSERT(prepared_stmt_count > 0);
prepared_stmt_count--;
- pthread_mutex_unlock(&LOCK_prepared_stmt_count);
+ mysql_mutex_unlock(&LOCK_prepared_stmt_count);
}
void Statement_map::reset()
{
/* Must be first, hash_free will reset st_hash.records */
- pthread_mutex_lock(&LOCK_prepared_stmt_count);
+ mysql_mutex_lock(&LOCK_prepared_stmt_count);
DBUG_ASSERT(prepared_stmt_count >= st_hash.records);
prepared_stmt_count-= st_hash.records;
- pthread_mutex_unlock(&LOCK_prepared_stmt_count);
+ mysql_mutex_unlock(&LOCK_prepared_stmt_count);
my_hash_reset(&names_hash);
my_hash_reset(&st_hash);
@@ -3089,13 +3428,13 @@ void Statement_map::reset()
Statement_map::~Statement_map()
{
/* Must go first, hash_free will reset st_hash.records */
- pthread_mutex_lock(&LOCK_prepared_stmt_count);
+ mysql_mutex_lock(&LOCK_prepared_stmt_count);
DBUG_ASSERT(prepared_stmt_count >= st_hash.records);
prepared_stmt_count-= st_hash.records;
- pthread_mutex_unlock(&LOCK_prepared_stmt_count);
+ mysql_mutex_unlock(&LOCK_prepared_stmt_count);
- hash_free(&names_hash);
- hash_free(&st_hash);
+ my_hash_free(&names_hash);
+ my_hash_free(&st_hash);
}
int select_dumpvar::send_data(List<Item> &items)
@@ -3142,10 +3481,12 @@ bool select_dumpvar::send_eof()
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_SP_FETCH_NO_DATA, ER(ER_SP_FETCH_NO_DATA));
/*
- In order to remember the value of affected rows for ROW_COUNT()
- function, SELECT INTO has to have an own SQLCOM.
- TODO: split from SQLCOM_SELECT
+ 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())
+ return true;
+
::my_ok(thd,row_count);
return 0;
}
@@ -3156,7 +3497,8 @@ 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,
- bool create_table)
+ bool create_table,
+ bool keep_row_order)
{
DBUG_ASSERT(table == 0);
tmp_table_param.field_count= column_types->elements;
@@ -3164,7 +3506,8 @@ 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, (char*) table_alias,
+ keep_row_order)))
return TRUE;
col_stat= (Column_statistics*) table->in_use->alloc(table->s->fields *
@@ -3295,9 +3638,9 @@ void THD::set_status_var_init()
void Security_context::init()
{
- host= user= ip= 0;
+ host= user= ip= external_user= 0;
host_or_ip= "connecting host";
- priv_user[0]= priv_host[0]= '\0';
+ priv_user[0]= priv_host[0]= proxy_user[0]= '\0';
master_access= 0;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
db_access= NO_ACCESS;
@@ -3309,10 +3652,24 @@ void Security_context::destroy()
{
// If not pointer to constant
if (host != my_localhost)
- safeFree(host);
+ {
+ my_free(host);
+ host= NULL;
+ }
if (user != delayed_user)
- safeFree(user);
- safeFree(ip);
+ {
+ my_free(user);
+ user= NULL;
+ }
+
+ if (external_user)
+ {
+ my_free(external_user);
+ user= NULL;
+ }
+
+ my_free(ip);
+ ip= NULL;
}
@@ -3327,7 +3684,7 @@ void Security_context::skip_grants()
bool Security_context::set_user(char *user_arg)
{
- safeFree(user);
+ my_free(user);
user= my_strdup(user_arg, MYF(0));
return user == 0;
}
@@ -3442,48 +3799,69 @@ bool Security_context::user_matches(Security_context *them)
access to mysql.proc table to find definitions of stored routines.
****************************************************************************/
-void THD::reset_n_backup_open_tables_state(Open_tables_state *backup)
+void THD::reset_n_backup_open_tables_state(Open_tables_backup *backup)
{
DBUG_ENTER("reset_n_backup_open_tables_state");
backup->set_open_tables_state(this);
- reset_open_tables_state();
+ backup->mdl_system_tables_svp= mdl_context.mdl_savepoint();
+ reset_open_tables_state(this);
state_flags|= Open_tables_state::BACKUPS_AVAIL;
DBUG_VOID_RETURN;
}
-void THD::restore_backup_open_tables_state(Open_tables_state *backup)
+void THD::restore_backup_open_tables_state(Open_tables_backup *backup)
{
DBUG_ENTER("restore_backup_open_tables_state");
+ mdl_context.rollback_to_savepoint(backup->mdl_system_tables_svp);
/*
Before we will throw away current open tables state we want
to be sure that it was properly cleaned up.
*/
DBUG_ASSERT(open_tables == 0 && temporary_tables == 0 &&
- handler_tables == 0 && derived_tables == 0 &&
- lock == 0 && locked_tables == 0 &&
- prelocked_mode == NON_PRELOCKED &&
+ derived_tables == 0 &&
+ lock == 0 &&
+ locked_tables_mode == LTM_NONE &&
m_reprepare_observer == NULL);
+
set_open_tables_state(backup);
DBUG_VOID_RETURN;
}
+#if MARIA_PLUGIN_INTERFACE_VERSION < 0x0200
/**
- Check the killed state of a user thread
- @param thd user thread
- @retval 0 the user thread is active
- @retval 1 the user thread has been killed
-
- This is used to signal a storage engine if it should be killed.
+ This is a backward compatibility method, made obsolete
+ by the thd_kill_statement service. Keep it here to avoid breaking the
+ ABI in case some binary plugins still use it.
*/
-
+#undef thd_killed
extern "C" int thd_killed(const MYSQL_THD thd)
{
+ if (!thd)
+ thd= current_thd;
+
if (!(thd->killed & KILL_HARD_BIT))
return 0;
- return thd->killed;
+ return thd->killed != 0;
}
+#else
+#error now thd_killed() function can go away
+#endif
+
+/*
+ return thd->killed status to the client,
+ mapped to the API enum thd_kill_levels values.
+*/
+extern "C" enum thd_kill_levels thd_kill_level(const MYSQL_THD thd)
+{
+ if (!thd)
+ thd= current_thd;
+ if (likely(thd->killed == NOT_KILLED))
+ return THD_IS_NOT_KILLED;
+
+ return thd->killed & KILL_HARD_BIT ? THD_ABORT_ASAP : THD_ABORT_SOFTLY;
+}
/**
Send an out-of-band progress report to the client
@@ -3518,6 +3896,9 @@ static void thd_send_progress(THD *thd)
extern "C" void thd_progress_init(MYSQL_THD thd, uint max_stage)
{
+ DBUG_ASSERT(thd->stmt_arena != thd->progress.arena);
+ if (thd->progress.arena)
+ return; // already initialized
/*
Send progress reports to clients that supports it, if the command
is a high level command (like ALTER TABLE) and we are not in a
@@ -3530,6 +3911,7 @@ extern "C" void thd_progress_init(MYSQL_THD thd, uint max_stage)
thd->progress.stage= 0;
thd->progress.counter= thd->progress.max_counter= 0;
thd->progress.max_stage= max_stage;
+ thd->progress.arena= thd->stmt_arena;
}
@@ -3538,12 +3920,14 @@ extern "C" void thd_progress_init(MYSQL_THD thd, uint max_stage)
extern "C" void thd_progress_report(MYSQL_THD thd,
ulonglong progress, ulonglong max_progress)
{
+ if (thd->stmt_arena != thd->progress.arena)
+ return;
if (thd->progress.max_counter != max_progress) // Simple optimization
{
- pthread_mutex_lock(&thd->LOCK_thd_data);
+ mysql_mutex_lock(&thd->LOCK_thd_data);
thd->progress.counter= progress;
thd->progress.max_counter= max_progress;
- pthread_mutex_unlock(&thd->LOCK_thd_data);
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
}
else
thd->progress.counter= progress;
@@ -3561,11 +3945,13 @@ extern "C" void thd_progress_report(MYSQL_THD thd,
extern "C" void thd_progress_next_stage(MYSQL_THD thd)
{
- pthread_mutex_lock(&thd->LOCK_thd_data);
+ if (thd->stmt_arena != thd->progress.arena)
+ return;
+ mysql_mutex_lock(&thd->LOCK_thd_data);
thd->progress.stage++;
thd->progress.counter= 0;
DBUG_ASSERT(thd->progress.stage < thd->progress.max_stage);
- pthread_mutex_unlock(&thd->LOCK_thd_data);
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
if (thd->progress.report)
{
thd->progress.next_report_time= 0; // Send new stage info
@@ -3587,11 +3973,14 @@ extern "C" void thd_progress_next_stage(MYSQL_THD thd)
extern "C" void thd_progress_end(MYSQL_THD thd)
{
+ if (thd->stmt_arena != thd->progress.arena)
+ return;
/*
It's enough to reset max_counter to set disable progress indicator
in processlist.
*/
thd->progress.max_counter= 0;
+ thd->progress.arena= 0;
}
@@ -3605,6 +3994,10 @@ extern "C" unsigned long thd_get_thread_id(const MYSQL_THD thd)
return((unsigned long)thd->thread_id);
}
+extern "C" enum_tx_isolation thd_get_trx_isolation(const MYSQL_THD thd)
+{
+ return thd->tx_isolation;
+}
#ifdef INNODB_COMPATIBILITY_HOOKS
extern "C" const struct charset_info_st *thd_charset(MYSQL_THD thd)
@@ -3618,7 +4011,7 @@ extern "C" const struct charset_info_st *thd_charset(MYSQL_THD thd)
*/
extern "C" char **thd_query(MYSQL_THD thd)
{
- return(&thd->query_string.str);
+ return (&thd->query_string.string.str);
}
/**
@@ -3629,7 +4022,7 @@ extern "C" char **thd_query(MYSQL_THD thd)
*/
extern "C" LEX_STRING * thd_query_string (MYSQL_THD thd)
{
- return(&thd->query_string);
+ return(&thd->query_string.string);
}
extern "C" int thd_slave_thread(const MYSQL_THD thd)
@@ -3644,7 +4037,7 @@ extern "C" int thd_non_transactional_update(const MYSQL_THD thd)
extern "C" int thd_binlog_format(const MYSQL_THD thd)
{
- if (mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG))
+ if (mysql_bin_log.is_open() && (thd->variables.option_bits & OPTION_BIN_LOG))
return (int) thd->variables.binlog_format;
else
return BINLOG_FORMAT_UNSPEC;
@@ -3659,6 +4052,65 @@ extern "C" bool thd_binlog_filter_ok(const MYSQL_THD thd)
{
return binlog_filter->db_ok(thd->db);
}
+
+extern "C" bool thd_sqlcom_can_generate_row_events(const MYSQL_THD thd)
+{
+ return sqlcom_can_generate_row_events(thd);
+}
+
+
+
+/*
+ Interface for MySQL Server, plugins and storage engines to report
+ when they are going to sleep/stall.
+
+ SYNOPSIS
+ thd_wait_begin()
+ thd Thread object
+ Can be NULL, in this case current THD is used.
+ wait_type Type of wait
+ 1 -- short wait (e.g. for mutex)
+ 2 -- medium wait (e.g. for disk io)
+ 3 -- large wait (e.g. for locked row/table)
+ NOTES
+ This is used by the threadpool to have better knowledge of which
+ threads that currently are actively running on CPUs. When a thread
+ reports that it's going to sleep/stall, the threadpool scheduler is
+ free to start another thread in the pool most likely. The expected wait
+ time is simply an indication of how long the wait is expected to
+ become, the real wait time could be very different.
+
+ thd_wait_end MUST be called immediately after waking up again.
+*/
+extern "C" void thd_wait_begin(MYSQL_THD thd, int wait_type)
+{
+ if (!thd)
+ {
+ thd= current_thd;
+ if (unlikely(!thd))
+ return;
+ }
+ MYSQL_CALLBACK(thd->scheduler, thd_wait_begin, (thd, wait_type));
+}
+
+/**
+ Interface for MySQL Server, plugins and storage engines to report
+ when they waking up from a sleep/stall.
+
+ @param thd Thread handle
+ Can be NULL, in this case current THD is used.
+*/
+extern "C" void thd_wait_end(MYSQL_THD thd)
+{
+ if (!thd)
+ {
+ thd= current_thd;
+ if (unlikely(!thd))
+ return;
+ }
+ MYSQL_CALLBACK(thd->scheduler, thd_wait_end, (thd));
+}
+
#endif // INNODB_COMPATIBILITY_HOOKS */
/****************************************************************************
@@ -3705,8 +4157,8 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup,
}
#endif
+ backup->option_bits= variables.option_bits;
backup->count_cuted_fields= count_cuted_fields;
- backup->options= options;
backup->in_sub_stmt= in_sub_stmt;
backup->enable_slow_log= enable_slow_log;
backup->query_plan_flags= query_plan_flags;
@@ -3722,13 +4174,14 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup,
first_successful_insert_id_in_cur_stmt;
if ((!lex->requires_prelocking() || is_update_query(lex->sql_command)) &&
- !current_stmt_binlog_row_based)
+ !is_current_stmt_binlog_format_row())
{
- options&= ~OPTION_BIN_LOG;
+ variables.option_bits&= ~OPTION_BIN_LOG;
}
- if ((backup->options & OPTION_BIN_LOG) && is_update_query(lex->sql_command)&&
- !current_stmt_binlog_row_based)
+ if ((backup->option_bits & OPTION_BIN_LOG) &&
+ is_update_query(lex->sql_command) &&
+ !is_current_stmt_binlog_format_row())
mysql_bin_log.start_union_events(this, this->query_id);
/* Disable result sets */
@@ -3773,7 +4226,7 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
}
count_cuted_fields= backup->count_cuted_fields;
transaction.savepoints= backup->savepoints;
- options= backup->options;
+ 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;
@@ -3792,8 +4245,8 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
if (!in_sub_stmt)
is_fatal_sub_stmt_error= FALSE;
- if ((options & OPTION_BIN_LOG) && is_update_query(lex->sql_command) &&
- !current_stmt_binlog_row_based)
+ if ((variables.option_bits & OPTION_BIN_LOG) && is_update_query(lex->sql_command) &&
+ !is_current_stmt_binlog_format_row())
mysql_bin_log.stop_union_events(this);
/*
@@ -3808,19 +4261,75 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
void THD::set_statement(Statement *stmt)
{
- pthread_mutex_lock(&LOCK_thd_data);
+ mysql_mutex_lock(&LOCK_thd_data);
Statement::set_statement(stmt);
- pthread_mutex_unlock(&LOCK_thd_data);
+ mysql_mutex_unlock(&LOCK_thd_data);
}
/** Assign a new value to thd->query. */
-void THD::set_query(char *query_arg, uint32 query_length_arg)
+void THD::set_query(const CSET_STRING &string_arg)
+{
+ mysql_mutex_lock(&LOCK_thd_data);
+ set_query_inner(string_arg);
+ mysql_mutex_unlock(&LOCK_thd_data);
+}
+
+/** Assign a new value to thd->query and thd->query_id. */
+
+void THD::set_query_and_id(char *query_arg, uint32 query_length_arg,
+ CHARSET_INFO *cs,
+ query_id_t new_query_id)
+{
+ mysql_mutex_lock(&LOCK_thd_data);
+ set_query_inner(query_arg, query_length_arg, cs);
+ query_id= new_query_id;
+ mysql_mutex_unlock(&LOCK_thd_data);
+}
+
+/** Assign a new value to thd->query_id. */
+
+void THD::set_query_id(query_id_t new_query_id)
+{
+ mysql_mutex_lock(&LOCK_thd_data);
+ query_id= new_query_id;
+ mysql_mutex_unlock(&LOCK_thd_data);
+}
+
+/** Assign a new value to thd->mysys_var. */
+void THD::set_mysys_var(struct st_my_thread_var *new_mysys_var)
+{
+ mysql_mutex_lock(&LOCK_thd_data);
+ mysys_var= new_mysys_var;
+ mysql_mutex_unlock(&LOCK_thd_data);
+}
+
+/**
+ Leave explicit LOCK TABLES or prelocked mode and restore value of
+ transaction sentinel in MDL subsystem.
+*/
+
+void THD::leave_locked_tables_mode()
{
- pthread_mutex_lock(&LOCK_thd_data);
- set_query_inner(query_arg, query_length_arg);
- pthread_mutex_unlock(&LOCK_thd_data);
+ if (locked_tables_mode == LTM_LOCK_TABLES)
+ {
+ /*
+ When leaving LOCK TABLES mode we have to change the duration of most
+ of the metadata locks being held, except for HANDLER and GRL locks,
+ to transactional for them to be properly released at UNLOCK TABLES.
+ */
+ mdl_context.set_transaction_duration_for_all_locks();
+ /*
+ Make sure we don't release the global read lock and commit blocker
+ when leaving LTM.
+ */
+ global_read_lock.set_explicit_lock_duration(this);
+ /* Also ensure that we don't release metadata locks for open HANDLERs. */
+ if (handler_tables_hash.records)
+ mysql_ha_set_explicit_lock_duration(this);
+ }
+ locked_tables_mode= LTM_NONE;
}
void THD::get_definer(LEX_USER *definer)
@@ -3860,7 +4369,7 @@ void mark_transaction_to_rollback(THD *thd, bool all)
Handling of XA id cacheing
***************************************************************************/
-pthread_mutex_t LOCK_xid_cache;
+mysql_mutex_t LOCK_xid_cache;
HASH xid_cache;
extern "C" uchar *xid_get_hash_key(const uchar *, size_t *, my_bool);
@@ -3876,30 +4385,56 @@ uchar *xid_get_hash_key(const uchar *ptr, size_t *length,
void xid_free_hash(void *ptr)
{
if (!((XID_STATE*)ptr)->in_thd)
- my_free((uchar*)ptr, MYF(0));
+ my_free(ptr);
+}
+
+#ifdef HAVE_PSI_INTERFACE
+static PSI_mutex_key key_LOCK_xid_cache;
+
+static PSI_mutex_info all_xid_mutexes[]=
+{
+ { &key_LOCK_xid_cache, "LOCK_xid_cache", PSI_FLAG_GLOBAL}
+};
+
+static void init_xid_psi_keys(void)
+{
+ const char* category= "sql";
+ int count;
+
+ if (PSI_server == NULL)
+ return;
+
+ count= array_elements(all_xid_mutexes);
+ PSI_server->register_mutex(category, all_xid_mutexes, count);
}
+#endif /* HAVE_PSI_INTERFACE */
bool xid_cache_init()
{
- pthread_mutex_init(&LOCK_xid_cache, MY_MUTEX_INIT_FAST);
- return hash_init(&xid_cache, &my_charset_bin, 100, 0, 0,
- xid_get_hash_key, xid_free_hash, 0) != 0;
+#ifdef HAVE_PSI_INTERFACE
+ init_xid_psi_keys();
+#endif
+
+ mysql_mutex_init(key_LOCK_xid_cache, &LOCK_xid_cache, MY_MUTEX_INIT_FAST);
+ return my_hash_init(&xid_cache, &my_charset_bin, 100, 0, 0,
+ xid_get_hash_key, xid_free_hash, 0) != 0;
}
void xid_cache_free()
{
- if (hash_inited(&xid_cache))
+ if (my_hash_inited(&xid_cache))
{
- hash_free(&xid_cache);
- pthread_mutex_destroy(&LOCK_xid_cache);
+ my_hash_free(&xid_cache);
+ mysql_mutex_destroy(&LOCK_xid_cache);
}
}
XID_STATE *xid_cache_search(XID *xid)
{
- pthread_mutex_lock(&LOCK_xid_cache);
- XID_STATE *res=(XID_STATE *)hash_search(&xid_cache, xid->key(), xid->key_length());
- pthread_mutex_unlock(&LOCK_xid_cache);
+ mysql_mutex_lock(&LOCK_xid_cache);
+ XID_STATE *res=(XID_STATE *)my_hash_search(&xid_cache, xid->key(),
+ xid->key_length());
+ mysql_mutex_unlock(&LOCK_xid_cache);
return res;
}
@@ -3908,8 +4443,8 @@ bool xid_cache_insert(XID *xid, enum xa_states xa_state)
{
XID_STATE *xs;
my_bool res;
- pthread_mutex_lock(&LOCK_xid_cache);
- if (hash_search(&xid_cache, xid->key(), xid->key_length()))
+ mysql_mutex_lock(&LOCK_xid_cache);
+ if (my_hash_search(&xid_cache, xid->key(), xid->key_length()))
res=0;
else if (!(xs=(XID_STATE *)my_malloc(sizeof(*xs), MYF(MY_WME))))
res=1;
@@ -3921,33 +4456,454 @@ bool xid_cache_insert(XID *xid, enum xa_states xa_state)
xs->rm_error=0;
res=my_hash_insert(&xid_cache, (uchar*)xs);
}
- pthread_mutex_unlock(&LOCK_xid_cache);
+ mysql_mutex_unlock(&LOCK_xid_cache);
return res;
}
bool xid_cache_insert(XID_STATE *xid_state)
{
- pthread_mutex_lock(&LOCK_xid_cache);
- if (hash_search(&xid_cache, xid_state->xid.key(), xid_state->xid.key_length()))
+ mysql_mutex_lock(&LOCK_xid_cache);
+ if (my_hash_search(&xid_cache, xid_state->xid.key(),
+ xid_state->xid.key_length()))
{
- pthread_mutex_unlock(&LOCK_xid_cache);
+ mysql_mutex_unlock(&LOCK_xid_cache);
my_error(ER_XAER_DUPID, MYF(0));
- return TRUE;
+ return true;
}
- my_bool res= my_hash_insert(&xid_cache, (uchar*)xid_state);
- pthread_mutex_unlock(&LOCK_xid_cache);
+ bool res= my_hash_insert(&xid_cache, (uchar*)xid_state);
+ mysql_mutex_unlock(&LOCK_xid_cache);
return res;
}
void xid_cache_delete(XID_STATE *xid_state)
{
- pthread_mutex_lock(&LOCK_xid_cache);
- hash_delete(&xid_cache, (uchar *)xid_state);
- pthread_mutex_unlock(&LOCK_xid_cache);
+ mysql_mutex_lock(&LOCK_xid_cache);
+ my_hash_delete(&xid_cache, (uchar *)xid_state);
+ mysql_mutex_unlock(&LOCK_xid_cache);
}
+
+/**
+ Decide on logging format to use for the statement and issue errors
+ or warnings as needed. The decision depends on the following
+ parameters:
+
+ - The logging mode, i.e., the value of binlog_format. Can be
+ statement, mixed, or row.
+
+ - The type of statement. There are three types of statements:
+ "normal" safe statements; unsafe statements; and row injections.
+ An unsafe statement is one that, if logged in statement format,
+ might produce different results when replayed on the slave (e.g.,
+ INSERT DELAYED). A row injection is either a BINLOG statement, or
+ a row event executed by the slave's SQL thread.
+
+ - The capabilities of tables modified by the statement. The
+ *capabilities vector* for a table is a set of flags associated
+ with the table. Currently, it only includes two flags: *row
+ capability flag* and *statement capability flag*.
+
+ The row capability flag is set if and only if the engine can
+ handle row-based logging. The statement capability flag is set if
+ and only if the table can handle statement-based logging.
+
+ Decision table for logging format
+ ---------------------------------
+
+ The following table summarizes how the format and generated
+ warning/error depends on the tables' capabilities, the statement
+ type, and the current binlog_format.
+
+ Row capable N NNNNNNNNN YYYYYYYYY YYYYYYYYY
+ Statement capable N YYYYYYYYY NNNNNNNNN YYYYYYYYY
+
+ Statement type * SSSUUUIII SSSUUUIII SSSUUUIII
+
+ binlog_format * SMRSMRSMR SMRSMRSMR SMRSMRSMR
+
+ Logged format - SS-S----- -RR-RR-RR SRRSRR-RR
+ Warning/Error 1 --2732444 5--5--6-- ---7--6--
+
+ Legend
+ ------
+
+ Row capable: N - Some table not row-capable, Y - All tables row-capable
+ Stmt capable: N - Some table not stmt-capable, Y - All tables stmt-capable
+ Statement type: (S)afe, (U)nsafe, or Row (I)njection
+ binlog_format: (S)TATEMENT, (M)IXED, or (R)OW
+ Logged format: (S)tatement or (R)ow
+ Warning/Error: Warnings and error messages are as follows:
+
+ 1. Error: Cannot execute statement: binlogging impossible since both
+ row-incapable engines and statement-incapable engines are
+ involved.
+
+ 2. Error: Cannot execute statement: binlogging impossible since
+ BINLOG_FORMAT = ROW and at least one table uses a storage engine
+ limited to statement-logging.
+
+ 3. Error: Cannot execute statement: binlogging of unsafe statement
+ is impossible when storage engine is limited to statement-logging
+ and BINLOG_FORMAT = MIXED.
+
+ 4. Error: Cannot execute row injection: binlogging impossible since
+ at least one table uses a storage engine limited to
+ statement-logging.
+
+ 5. Error: Cannot execute statement: binlogging impossible since
+ BINLOG_FORMAT = STATEMENT and at least one table uses a storage
+ engine limited to row-logging.
+
+ 6. Error: Cannot execute row injection: binlogging impossible since
+ BINLOG_FORMAT = STATEMENT.
+
+ 7. Warning: Unsafe statement binlogged in statement format since
+ BINLOG_FORMAT = STATEMENT.
+
+ In addition, we can produce the following error (not depending on
+ the variables of the decision diagram):
+
+ 8. Error: Cannot execute statement: binlogging impossible since more
+ than one engine is involved and at least one engine is
+ self-logging.
+
+ For each error case above, the statement is prevented from being
+ logged, we report an error, and roll back the statement. For
+ warnings, we set the thd->binlog_flags variable: the warning will be
+ printed only if the statement is successfully logged.
+
+ @see THD::binlog_query
+
+ @param[in] thd Client thread
+ @param[in] tables Tables involved in the query
+
+ @retval 0 No error; statement can be logged.
+ @retval -1 One of the error conditions above applies (1, 2, 4, 5, or 6).
+*/
+
+int THD::decide_logging_format(TABLE_LIST *tables)
+{
+ DBUG_ENTER("THD::decide_logging_format");
+ DBUG_PRINT("info", ("Query: %s", query()));
+ DBUG_PRINT("info", ("variables.binlog_format: %lu",
+ variables.binlog_format));
+ DBUG_PRINT("info", ("lex->get_stmt_unsafe_flags(): 0x%x",
+ lex->get_stmt_unsafe_flags()));
+
+ /*
+ We should not decide logging format if the binlog is closed or
+ binlogging is off, or if the statement is filtered out from the
+ binlog by filtering rules.
+ */
+ if (mysql_bin_log.is_open() && (variables.option_bits & OPTION_BIN_LOG) &&
+ !(variables.binlog_format == BINLOG_FORMAT_STMT &&
+ !binlog_filter->db_ok(db)))
+ {
+ /*
+ Compute one bit field with the union of all the engine
+ capabilities, and one with the intersection of all the engine
+ capabilities.
+ */
+ handler::Table_flags flags_write_some_set= 0;
+ handler::Table_flags flags_access_some_set= 0;
+ handler::Table_flags flags_write_all_set=
+ HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE;
+
+ /*
+ If different types of engines are about to be updated.
+ For example: Innodb and Falcon; Innodb and MyIsam.
+ */
+ my_bool multi_write_engine= FALSE;
+ /*
+ If different types of engines are about to be accessed
+ and any of them is about to be updated. For example:
+ Innodb and Falcon; Innodb and MyIsam.
+ */
+ my_bool multi_access_engine= FALSE;
+ /*
+ Identifies if a table is changed.
+ */
+ my_bool is_write= FALSE;
+ /*
+ A pointer to a previous table that was changed.
+ */
+ TABLE* prev_write_table= NULL;
+ /*
+ A pointer to a previous table that was accessed.
+ */
+ TABLE* prev_access_table= NULL;
+
+#ifndef DBUG_OFF
+ {
+ static const char *prelocked_mode_name[] = {
+ "NON_PRELOCKED",
+ "PRELOCKED",
+ "PRELOCKED_UNDER_LOCK_TABLES",
+ };
+ DBUG_PRINT("debug", ("prelocked_mode: %s",
+ prelocked_mode_name[locked_tables_mode]));
+ }
+#endif
+
+ /*
+ 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)
+ {
+ if (table->placeholder())
+ continue;
+
+ if (table->table->s->table_category == TABLE_CATEGORY_PERFORMANCE ||
+ table->table->s->table_category == TABLE_CATEGORY_LOG)
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_TABLE);
+
+ handler::Table_flags const flags= table->table->file->ha_table_flags();
+
+ DBUG_PRINT("info", ("table: %s; ha_table_flags: 0x%llx",
+ table->table_name, flags));
+ if (table->lock_type >= TL_WRITE_ALLOW_WRITE)
+ {
+ if (prev_write_table && prev_write_table->file->ht !=
+ table->table->file->ht)
+ multi_write_engine= TRUE;
+
+ my_bool trans= table->table->file->has_transactions();
+
+ if (table->table->s->tmp_table)
+ lex->set_stmt_accessed_table(trans ? LEX::STMT_WRITES_TEMP_TRANS_TABLE :
+ LEX::STMT_WRITES_TEMP_NON_TRANS_TABLE);
+ else
+ lex->set_stmt_accessed_table(trans ? LEX::STMT_WRITES_TRANS_TABLE :
+ LEX::STMT_WRITES_NON_TRANS_TABLE);
+
+ flags_write_all_set &= flags;
+ flags_write_some_set |= flags;
+ is_write= TRUE;
+
+ prev_write_table= table->table;
+
+ }
+ flags_access_some_set |= flags;
+
+ if (lex->sql_command != SQLCOM_CREATE_TABLE ||
+ (lex->sql_command == SQLCOM_CREATE_TABLE &&
+ (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)))
+ {
+ my_bool trans= table->table->file->has_transactions();
+
+ if (table->table->s->tmp_table)
+ lex->set_stmt_accessed_table(trans ? LEX::STMT_READS_TEMP_TRANS_TABLE :
+ LEX::STMT_READS_TEMP_NON_TRANS_TABLE);
+ else
+ lex->set_stmt_accessed_table(trans ? LEX::STMT_READS_TRANS_TABLE :
+ LEX::STMT_READS_NON_TRANS_TABLE);
+ }
+
+ if (prev_access_table && prev_access_table->file->ht !=
+ table->table->file->ht)
+ multi_access_engine= TRUE;
+
+ prev_access_table= table->table;
+ }
+
+ DBUG_PRINT("info", ("flags_write_all_set: 0x%llx", flags_write_all_set));
+ DBUG_PRINT("info", ("flags_write_some_set: 0x%llx", flags_write_some_set));
+ DBUG_PRINT("info", ("flags_access_some_set: 0x%llx", flags_access_some_set));
+ DBUG_PRINT("info", ("multi_write_engine: %d", multi_write_engine));
+ DBUG_PRINT("info", ("multi_access_engine: %d", multi_access_engine));
+
+ int error= 0;
+ int unsafe_flags;
+
+ bool multi_stmt_trans= in_multi_stmt_transaction_mode();
+ bool trans_table= trans_has_updated_trans_table(this);
+ bool binlog_direct= variables.binlog_direct_non_trans_update;
+
+ if (lex->is_mixed_stmt_unsafe(multi_stmt_trans, binlog_direct,
+ trans_table, tx_isolation))
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_MIXED_STATEMENT);
+ else if (multi_stmt_trans && trans_table && !binlog_direct &&
+ lex->stmt_accessed_table(LEX::STMT_WRITES_NON_TRANS_TABLE))
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_NONTRANS_AFTER_TRANS);
+
+ /*
+ If more than one engine is involved in the statement and at
+ least one is doing it's own logging (is *self-logging*), the
+ statement cannot be logged atomically, so we generate an error
+ rather than allowing the binlog to become corrupt.
+ */
+ if (multi_write_engine &&
+ (flags_write_some_set & HA_HAS_OWN_BINLOGGING))
+ my_error((error= ER_BINLOG_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE),
+ MYF(0));
+ else if (multi_access_engine && flags_access_some_set & HA_HAS_OWN_BINLOGGING)
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE);
+
+ /* both statement-only and row-only engines involved */
+ if ((flags_write_all_set & (HA_BINLOG_STMT_CAPABLE | HA_BINLOG_ROW_CAPABLE)) == 0)
+ {
+ /*
+ 1. Error: Binary logging impossible since both row-incapable
+ engines and statement-incapable engines are involved
+ */
+ my_error((error= ER_BINLOG_ROW_ENGINE_AND_STMT_ENGINE), MYF(0));
+ }
+ /* statement-only engines involved */
+ else if ((flags_write_all_set & HA_BINLOG_ROW_CAPABLE) == 0)
+ {
+ if (lex->is_stmt_row_injection())
+ {
+ /*
+ 4. Error: Cannot execute row injection since table uses
+ storage engine limited to statement-logging
+ */
+ my_error((error= ER_BINLOG_ROW_INJECTION_AND_STMT_ENGINE), MYF(0));
+ }
+ else if (variables.binlog_format == BINLOG_FORMAT_ROW &&
+ sqlcom_can_generate_row_events(this))
+ {
+ /*
+ 2. Error: Cannot modify table that uses a storage engine
+ limited to statement-logging when BINLOG_FORMAT = ROW
+ */
+ my_error((error= ER_BINLOG_ROW_MODE_AND_STMT_ENGINE), MYF(0));
+ }
+ else if ((unsafe_flags= lex->get_stmt_unsafe_flags()) != 0)
+ {
+ /*
+ 3. Error: Cannot execute statement: binlogging of unsafe
+ statement is impossible when storage engine is limited to
+ statement-logging and BINLOG_FORMAT = MIXED.
+ */
+ for (int unsafe_type= 0;
+ unsafe_type < LEX::BINLOG_STMT_UNSAFE_COUNT;
+ unsafe_type++)
+ if (unsafe_flags & (1 << unsafe_type))
+ my_error((error= ER_BINLOG_UNSAFE_AND_STMT_ENGINE), MYF(0),
+ ER(LEX::binlog_stmt_unsafe_errcode[unsafe_type]));
+ }
+ /* log in statement format! */
+ }
+ /* no statement-only engines */
+ else
+ {
+ /* binlog_format = STATEMENT */
+ if (variables.binlog_format == BINLOG_FORMAT_STMT)
+ {
+ if (lex->is_stmt_row_injection())
+ {
+ /*
+ 6. Error: Cannot execute row injection since
+ BINLOG_FORMAT = STATEMENT
+ */
+ my_error((error= ER_BINLOG_ROW_INJECTION_AND_STMT_MODE), MYF(0));
+ }
+ else if ((flags_write_all_set & HA_BINLOG_STMT_CAPABLE) == 0 &&
+ sqlcom_can_generate_row_events(this))
+ {
+ /*
+ 5. Error: Cannot modify table that uses a storage engine
+ limited to row-logging when binlog_format = STATEMENT
+ */
+ my_error((error= ER_BINLOG_STMT_MODE_AND_ROW_ENGINE), MYF(0), "");
+ }
+ else if (is_write && (unsafe_flags= lex->get_stmt_unsafe_flags()) != 0)
+ {
+ /*
+ 7. Warning: Unsafe statement logged as statement due to
+ binlog_format = STATEMENT
+ */
+ binlog_unsafe_warning_flags|= unsafe_flags;
+
+ DBUG_PRINT("info", ("Scheduling warning to be issued by "
+ "binlog_query: '%s'",
+ ER(ER_BINLOG_UNSAFE_STATEMENT)));
+ DBUG_PRINT("info", ("binlog_unsafe_warning_flags: 0x%x",
+ binlog_unsafe_warning_flags));
+ }
+ /* log in statement format! */
+ }
+ /* No statement-only engines and binlog_format != STATEMENT.
+ I.e., nothing prevents us from row logging if needed. */
+ else
+ {
+ if (lex->is_stmt_unsafe() || lex->is_stmt_row_injection()
+ || (flags_write_all_set & HA_BINLOG_STMT_CAPABLE) == 0)
+ {
+ /* log in row format! */
+ set_current_stmt_binlog_format_row_if_mixed();
+ }
+ }
+ }
+
+ if (error) {
+ DBUG_PRINT("info", ("decision: no logging since an error was generated"));
+ DBUG_RETURN(-1);
+ }
+ DBUG_PRINT("info", ("decision: logging in %s format",
+ is_current_stmt_binlog_format_row() ?
+ "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))
+ {
+ String table_names;
+ /*
+ Generate a warning for UPDATE/DELETE statements that modify a
+ BLACKHOLE table, as row events are not logged in row format.
+ */
+ for (TABLE_LIST *table= tables; table; table= table->next_global)
+ {
+ if (table->placeholder())
+ continue;
+ 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(",");
+ }
+ }
+ if (!table_names.is_empty())
+ {
+ bool is_update= (lex->sql_command == SQLCOM_UPDATE ||
+ lex->sql_command == SQLCOM_UPDATE_MULTI);
+ /*
+ Replace the last ',' with '.' for table_names
+ */
+ table_names.replace(table_names.length()-1, 1, ".", 1);
+ push_warning_printf(this, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_UNKNOWN_ERROR,
+ "Row events are not logged for %s statements "
+ "that modify BLACKHOLE tables in row format. "
+ "Table(s): '%-.192s'",
+ is_update ? "UPDATE" : "DELETE",
+ table_names.c_ptr());
+ }
+ }
+ }
+#ifndef DBUG_OFF
+ else
+ DBUG_PRINT("info", ("decision: no logging since "
+ "mysql_bin_log.is_open() = %d "
+ "and (options & OPTION_BIN_LOG) = 0x%llx "
+ "and binlog_format = %lu "
+ "and binlog_filter->db_ok(db) = %d",
+ mysql_bin_log.is_open(),
+ (variables.option_bits & OPTION_BIN_LOG),
+ variables.binlog_format,
+ binlog_filter->db_ok(db)));
+#endif
+
+ DBUG_RETURN(0);
+}
+
+
/*
Implementation of interface to write rows to the binary log through the
thread. The thread is responsible for writing the rows it has
@@ -3996,10 +4952,10 @@ THD::binlog_prepare_pending_rows_event(TABLE* table, uint32 serv_id,
There is no good place to set up the transactional data, so we
have to do it here.
*/
- if (binlog_setup_trx_data())
+ if (binlog_setup_trx_data() == NULL)
DBUG_RETURN(NULL);
- Rows_log_event* pending= binlog_get_pending_rows_event();
+ Rows_log_event* pending= binlog_get_pending_rows_event(is_transactional);
if (unlikely(pending && !pending->is_valid()))
DBUG_RETURN(NULL);
@@ -4033,7 +4989,9 @@ THD::binlog_prepare_pending_rows_event(TABLE* table, uint32 serv_id,
flush the pending event and replace it with the newly created
event...
*/
- if (unlikely(mysql_bin_log.flush_and_set_pending_rows_event(this, ev)))
+ if (unlikely(
+ mysql_bin_log.flush_and_set_pending_rows_event(this, ev,
+ is_transactional)))
{
delete ev;
DBUG_RETURN(NULL);
@@ -4065,8 +5023,8 @@ THD::binlog_prepare_pending_rows_event(TABLE*, uint32, MY_BITMAP const*,
Update_rows_log_event *);
#endif
-
-namespace {
+/* Declare in unnamed namespace. */
+CPP_UNNAMED_NS_START
/**
Class to handle temporary allocation of memory for row data.
@@ -4119,7 +5077,7 @@ namespace {
~Row_data_memory()
{
if (m_memory != 0 && m_release_memory_on_destruction)
- my_free((uchar*) m_memory, MYF(MY_WME));
+ my_free(m_memory);
}
/**
@@ -4185,14 +5143,14 @@ namespace {
uchar *m_memory;
uchar *m_ptr[2];
};
-}
+CPP_UNNAMED_NS_END
int THD::binlog_write_row(TABLE* table, bool is_trans,
MY_BITMAP const* cols, size_t colcnt,
uchar const *record)
{
- DBUG_ASSERT(current_stmt_binlog_row_based && mysql_bin_log.is_open());
+ DBUG_ASSERT(is_current_stmt_binlog_format_row() && mysql_bin_log.is_open());
/*
Pack records into format for transfer. We are allocating more
@@ -4222,7 +5180,7 @@ int THD::binlog_update_row(TABLE* table, bool is_trans,
const uchar *before_record,
const uchar *after_record)
{
- DBUG_ASSERT(current_stmt_binlog_row_based && mysql_bin_log.is_open());
+ DBUG_ASSERT(is_current_stmt_binlog_format_row() && mysql_bin_log.is_open());
size_t const before_maxlen = max_row_length(table, before_record);
size_t const after_maxlen = max_row_length(table, after_record);
@@ -4267,7 +5225,7 @@ int THD::binlog_delete_row(TABLE* table, bool is_trans,
MY_BITMAP const* cols, size_t colcnt,
uchar const *record)
{
- DBUG_ASSERT(current_stmt_binlog_row_based && mysql_bin_log.is_open());
+ DBUG_ASSERT(is_current_stmt_binlog_format_row() && mysql_bin_log.is_open());
/*
Pack records into format for transfer. We are allocating more
@@ -4293,14 +5251,15 @@ int THD::binlog_delete_row(TABLE* table, bool is_trans,
}
-int THD::binlog_remove_pending_rows_event(bool clear_maps)
+int THD::binlog_remove_pending_rows_event(bool clear_maps,
+ bool is_transactional)
{
DBUG_ENTER("THD::binlog_remove_pending_rows_event");
if (!mysql_bin_log.is_open())
DBUG_RETURN(0);
- mysql_bin_log.remove_pending_rows_event(this);
+ mysql_bin_log.remove_pending_rows_event(this, is_transactional);
if (clear_maps)
binlog_table_maps= 0;
@@ -4308,7 +5267,7 @@ int THD::binlog_remove_pending_rows_event(bool clear_maps)
DBUG_RETURN(0);
}
-int THD::binlog_flush_pending_rows_event(bool stmt_end)
+int THD::binlog_flush_pending_rows_event(bool stmt_end, bool is_transactional)
{
DBUG_ENTER("THD::binlog_flush_pending_rows_event");
/*
@@ -4324,7 +5283,7 @@ int THD::binlog_flush_pending_rows_event(bool stmt_end)
flag is set.
*/
int error= 0;
- if (Rows_log_event *pending= binlog_get_pending_rows_event())
+ if (Rows_log_event *pending= binlog_get_pending_rows_event(is_transactional))
{
if (stmt_end)
{
@@ -4332,7 +5291,8 @@ int THD::binlog_flush_pending_rows_event(bool stmt_end)
binlog_table_maps= 0;
}
- error= mysql_bin_log.flush_and_set_pending_rows_event(this, 0);
+ error= mysql_bin_log.flush_and_set_pending_rows_event(this, 0,
+ is_transactional);
}
DBUG_RETURN(error);
@@ -4348,8 +5308,6 @@ show_query_type(THD::enum_binlog_query_type qtype)
return "ROW";
case THD::STMT_QUERY_TYPE:
return "STMT";
- case THD::MYSQL_QUERY_TYPE:
- return "MYSQL";
case THD::QUERY_TYPE_COUNT:
default:
DBUG_ASSERT(0 <= qtype && qtype < THD::QUERY_TYPE_COUNT);
@@ -4360,33 +5318,207 @@ show_query_type(THD::enum_binlog_query_type qtype)
}
#endif
-
/*
- Member function that will log query, either row-based or
- statement-based depending on the value of the 'current_stmt_binlog_row_based'
- the value of the 'qtype' flag.
+ Constants required for the limit unsafe warnings suppression
+*/
+//seconds after which the limit unsafe warnings suppression will be activated
+#define LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT 50
+//number of limit unsafe warnings after which the suppression will be activated
+#define LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT 50
- This function should be called after the all calls to ha_*_row()
- functions have been issued, but before tables are unlocked and
- closed.
+static ulonglong limit_unsafe_suppression_start_time= 0;
+static bool unsafe_warning_suppression_is_activated= false;
+static int limit_unsafe_warning_count= 0;
- OBSERVE
- There shall be no writes to any system table after calling
- binlog_query(), so these writes has to be moved to before the call
- of binlog_query() for correct functioning.
+/**
+ Auxiliary function to reset the limit unsafety warning suppression.
+*/
+static void reset_binlog_unsafe_suppression()
+{
+ DBUG_ENTER("reset_binlog_unsafe_suppression");
+ unsafe_warning_suppression_is_activated= false;
+ limit_unsafe_warning_count= 0;
+ limit_unsafe_suppression_start_time= my_interval_timer()/10000000;
+ DBUG_VOID_RETURN;
+}
- This is necessesary not only for RBR, but the master might crash
- after binlogging the query but before changing the system tables.
- This means that the slave and the master are not in the same state
- (after the master has restarted), so therefore we have to
- eliminate this problem.
+/**
+ Auxiliary function to print warning in the error log.
+*/
+static void print_unsafe_warning_to_log(int unsafe_type, char* buf,
+ char* query)
+{
+ DBUG_ENTER("print_unsafe_warning_in_log");
+ sprintf(buf, ER(ER_BINLOG_UNSAFE_STATEMENT),
+ ER(LEX::binlog_stmt_unsafe_errcode[unsafe_type]));
+ sql_print_warning(ER(ER_MESSAGE_AND_STATEMENT), buf, query);
+ DBUG_VOID_RETURN;
+}
- RETURN VALUE
- Error code, or 0 if no error.
+/**
+ Auxiliary function to check if the warning for limit unsafety should be
+ thrown or suppressed. Details of the implementation can be found in the
+ comments inline.
+ SYNOPSIS:
+ @params
+ buf - buffer to hold the warning message text
+ unsafe_type - The type of unsafety.
+ query - The actual query statement.
+
+ TODO: Remove this function and implement a general service for all warnings
+ that would prevent flooding the error log.
+*/
+static void do_unsafe_limit_checkout(char* buf, int unsafe_type, char* query)
+{
+ ulonglong now= 0;
+ DBUG_ENTER("do_unsafe_limit_checkout");
+ DBUG_ASSERT(unsafe_type == LEX::BINLOG_STMT_UNSAFE_LIMIT);
+ limit_unsafe_warning_count++;
+ /*
+ INITIALIZING:
+ If this is the first time this function is called with log warning
+ enabled, the monitoring the unsafe warnings should start.
+ */
+ if (limit_unsafe_suppression_start_time == 0)
+ {
+ limit_unsafe_suppression_start_time= my_interval_timer()/10000000;
+ print_unsafe_warning_to_log(unsafe_type, buf, query);
+ }
+ else
+ {
+ if (!unsafe_warning_suppression_is_activated)
+ print_unsafe_warning_to_log(unsafe_type, buf, query);
+
+ if (limit_unsafe_warning_count >=
+ LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT)
+ {
+ now= my_interval_timer()/10000000;
+ if (!unsafe_warning_suppression_is_activated)
+ {
+ /*
+ ACTIVATION:
+ We got LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT warnings in
+ less than LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT we activate the
+ suppression.
+ */
+ if ((now-limit_unsafe_suppression_start_time) <=
+ LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT)
+ {
+ unsafe_warning_suppression_is_activated= true;
+ DBUG_PRINT("info",("A warning flood has been detected and the limit \
+unsafety warning suppression has been activated."));
+ }
+ else
+ {
+ /*
+ there is no flooding till now, therefore we restart the monitoring
+ */
+ limit_unsafe_suppression_start_time= my_interval_timer()/10000000;
+ limit_unsafe_warning_count= 0;
+ }
+ }
+ else
+ {
+ /*
+ Print the suppression note and the unsafe warning.
+ */
+ sql_print_information("The following warning was suppressed %d times \
+during the last %d seconds in the error log",
+ limit_unsafe_warning_count,
+ (int)
+ (now-limit_unsafe_suppression_start_time));
+ print_unsafe_warning_to_log(unsafe_type, buf, query);
+ /*
+ DEACTIVATION: We got LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT
+ warnings in more than LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT, the
+ suppression should be deactivated.
+ */
+ if ((now - limit_unsafe_suppression_start_time) >
+ LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT)
+ {
+ reset_binlog_unsafe_suppression();
+ DBUG_PRINT("info",("The limit unsafety warning supression has been \
+deactivated"));
+ }
+ }
+ limit_unsafe_warning_count= 0;
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Auxiliary method used by @c binlog_query() to raise warnings.
+
+ The type of warning and the type of unsafeness is stored in
+ THD::binlog_unsafe_warning_flags.
+*/
+void THD::issue_unsafe_warnings()
+{
+ char buf[MYSQL_ERRMSG_SIZE * 2];
+ DBUG_ENTER("issue_unsafe_warnings");
+ /*
+ Ensure that binlog_unsafe_warning_flags is big enough to hold all
+ bits. This is actually a constant expression.
+ */
+ DBUG_ASSERT(LEX::BINLOG_STMT_UNSAFE_COUNT <=
+ sizeof(binlog_unsafe_warning_flags) * CHAR_BIT);
+
+ uint32 unsafe_type_flags= binlog_unsafe_warning_flags;
+ /*
+ For each unsafe_type, check if the statement is unsafe in this way
+ and issue a warning.
+ */
+ for (int unsafe_type=0;
+ unsafe_type < LEX::BINLOG_STMT_UNSAFE_COUNT;
+ unsafe_type++)
+ {
+ if ((unsafe_type_flags & (1 << unsafe_type)) != 0)
+ {
+ push_warning_printf(this, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_BINLOG_UNSAFE_STATEMENT,
+ ER(ER_BINLOG_UNSAFE_STATEMENT),
+ ER(LEX::binlog_stmt_unsafe_errcode[unsafe_type]));
+ if (global_system_variables.log_warnings)
+ {
+ if (unsafe_type == LEX::BINLOG_STMT_UNSAFE_LIMIT)
+ do_unsafe_limit_checkout( buf, unsafe_type, query());
+ else //cases other than LIMIT unsafety
+ print_unsafe_warning_to_log(unsafe_type, buf, query());
+ }
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Log the current query.
+
+ The query will be logged in either row format or statement format
+ depending on the value of @c current_stmt_binlog_format_row field and
+ the value of the @c qtype parameter.
+
+ This function must be called:
+
+ - After the all calls to ha_*_row() functions have been issued.
+
+ - After any writes to system tables. Rationale: if system tables
+ were written after a call to this function, and the master crashes
+ after the call to this function and before writing the system
+ tables, then the master and slave get out of sync.
+
+ - Before tables are unlocked and closed.
+
+ @see decide_logging_format
+
+ @retval 0 Success
+
+ @retval nonzero 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 suppress_use,
- int errcode)
+ ulong query_len, bool is_trans, bool direct,
+ bool suppress_use, int errcode)
{
DBUG_ENTER("THD::binlog_query");
DBUG_PRINT("enter", ("qtype: %s query: '%-.*s'",
@@ -4402,60 +5534,71 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
If we are in prelocked mode, the flushing will be done inside the
top-most close_thread_tables().
*/
- if (this->prelocked_mode == NON_PRELOCKED)
- if (int error= binlog_flush_pending_rows_event(TRUE))
+ if (this->locked_tables_mode <= LTM_LOCK_TABLES)
+ if (int error= binlog_flush_pending_rows_event(TRUE, is_trans))
DBUG_RETURN(error);
/*
- If we are in statement mode and trying to log an unsafe statement,
- we should print a warning.
+ Warnings for unsafe statements logged in statement format are
+ printed in three places instead of in decide_logging_format().
+ This is because the warnings should be printed only if the statement
+ is actually logged. When executing decide_logging_format(), we cannot
+ know for sure if the statement will be logged:
+
+ 1 - sp_head::execute_procedure which prints out warnings for calls to
+ stored procedures.
+
+ 2 - sp_head::execute_function which prints out warnings for calls
+ involving functions.
+
+ 3 - THD::binlog_query (here) which prints warning for top level
+ statements not covered by the two cases above: i.e., if not insided a
+ procedure and a function.
+
+ Besides, we should not try to print these warnings if it is not
+ possible to write statements to the binary log as it happens when
+ the execution is inside a function, or generaly speaking, when
+ the variables.option_bits & OPTION_BIN_LOG is false.
+
*/
- if (sql_log_bin_toplevel && lex->is_stmt_unsafe() &&
- variables.binlog_format == BINLOG_FORMAT_STMT &&
- binlog_filter->db_ok(this->db))
- {
- /*
- A warning can be elevated a error when STRICT sql mode.
- But we don't want to elevate binlog warning to error here.
- */
- push_warning(this, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_BINLOG_UNSAFE_STATEMENT,
- ER(ER_BINLOG_UNSAFE_STATEMENT));
- if (global_system_variables.log_warnings &&
- !(binlog_flags & BINLOG_FLAG_UNSAFE_STMT_PRINTED))
- {
- sql_print_warning("%s Statement: %.*s",
- ER(ER_BINLOG_UNSAFE_STATEMENT),
- (int) min(MYSQL_ERRMSG_SIZE, query_len), query_arg);
- binlog_flags|= BINLOG_FLAG_UNSAFE_STMT_PRINTED;
- }
- }
+ if ((variables.option_bits & OPTION_BIN_LOG) &&
+ spcont == NULL && !binlog_evt_union.do_union)
+ issue_unsafe_warnings();
switch (qtype) {
+ /*
+ ROW_QUERY_TYPE means that the statement may be logged either in
+ row format or in statement format. If
+ current_stmt_binlog_format is row, it means that the
+ statement has already been logged in row format and hence shall
+ not be logged again.
+ */
case THD::ROW_QUERY_TYPE:
DBUG_PRINT("debug",
- ("current_stmt_binlog_row_based: %d",
- current_stmt_binlog_row_based));
- if (current_stmt_binlog_row_based)
+ ("is_current_stmt_binlog_format_row: %d",
+ is_current_stmt_binlog_format_row()));
+ if (is_current_stmt_binlog_format_row())
DBUG_RETURN(0);
- /* Otherwise, we fall through */
- case THD::MYSQL_QUERY_TYPE:
- /*
- Using this query type is a conveniece hack, since we have been
- moving back and forth between using RBR for replication of
- system tables and not using it.
+ /* Fall through */
- Make sure to change in check_table_binlog_row_based() according
- to how you treat this.
+ /*
+ STMT_QUERY_TYPE means that the query must be logged in statement
+ format; it cannot be logged in row format. This is typically
+ used by DDL statements. It is an error to use this query type
+ if current_stmt_binlog_format_row is row.
+
+ @todo Currently there are places that call this method with
+ STMT_QUERY_TYPE and current_stmt_binlog_format is row. Fix those
+ places and add assert to ensure correct behavior. /Sven
*/
case THD::STMT_QUERY_TYPE:
/*
The MYSQL_LOG::write() function will set the STMT_END_F flag and
flush the pending rows event if necessary.
- */
+ */
{
- Query_log_event qinfo(this, query_arg, query_len, is_trans, suppress_use,
- errcode);
+ Query_log_event qinfo(this, query_arg, query_len, is_trans, direct,
+ suppress_use, errcode);
/*
Binlog table maps will be irrelevant after a Query_log_event
(they are just removed on the slave side) so after the query
@@ -4477,19 +5620,19 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
void
THD::wait_for_wakeup_ready()
{
- pthread_mutex_lock(&LOCK_wakeup_ready);
+ mysql_mutex_lock(&LOCK_wakeup_ready);
while (!wakeup_ready)
- pthread_cond_wait(&COND_wakeup_ready, &LOCK_wakeup_ready);
- pthread_mutex_unlock(&LOCK_wakeup_ready);
+ mysql_cond_wait(&COND_wakeup_ready, &LOCK_wakeup_ready);
+ mysql_mutex_unlock(&LOCK_wakeup_ready);
}
void
THD::signal_wakeup_ready()
{
- pthread_mutex_lock(&LOCK_wakeup_ready);
+ mysql_mutex_lock(&LOCK_wakeup_ready);
wakeup_ready= true;
- pthread_mutex_unlock(&LOCK_wakeup_ready);
- pthread_cond_signal(&COND_wakeup_ready);
+ mysql_mutex_unlock(&LOCK_wakeup_ready);
+ mysql_cond_signal(&COND_wakeup_ready);
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 24dd495f2f9..9365727393e 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2009, 2014, Monty Program Ab.
+ Copyright (c) 2009, 2013, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,9 +13,11 @@
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
-*/
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+#ifndef SQL_CLASS_INCLUDED
+#define SQL_CLASS_INCLUDED
/* Classes in mysql */
@@ -23,142 +25,27 @@
#pragma interface /* gcc class implementation */
#endif
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#ifdef MYSQL_SERVER
+#include "unireg.h" // REQUIRED: for other includes
+#endif
+#include <waiting_threads.h>
+#include "sql_const.h"
+#include <mysql/plugin_audit.h>
#include "log.h"
#include "rpl_tblmap.h"
-
-
-/**
- Interface for Item iterator
-*/
-
-class Item_iterator
-{
-public:
- /**
- Shall set this iterator to the position before the first item
-
- @note
- This method also may perform some other initialization actions like
- allocation of certain resources.
- */
- virtual void open()= 0;
- /**
- Shall return the next Item (or NULL if there is no next item) and
- move pointer to position after it.
- */
- virtual Item *next()= 0;
- /**
- Shall force iterator to free resources (if it holds them)
-
- @note
- One should not use the iterator without open() call after close()
- */
- virtual void close()= 0;
-
- virtual ~Item_iterator() {}
-};
-
-
-/**
- Item iterator over List_iterator_fast for Item references
-*/
-
-class Item_iterator_ref_list: public Item_iterator
-{
- List_iterator<Item*> list;
-public:
- Item_iterator_ref_list(List_iterator<Item*> &arg_list):
- list(arg_list) {}
- void open() { list.rewind(); }
- Item *next() { return *(list++); }
- void close() {}
-};
-
-
-/**
- Item iterator over List_iterator_fast for Items
-*/
-
-class Item_iterator_list: public Item_iterator
-{
- List_iterator<Item> list;
-public:
- Item_iterator_list(List_iterator<Item> &arg_list):
- list(arg_list) {}
- void open() { list.rewind(); }
- Item *next() { return (list++); }
- void close() {}
-};
-
-
-/**
- Item iterator over Item interface for rows
-*/
-
-class Item_iterator_row: public Item_iterator
-{
- Item *base_item;
- uint current;
-public:
- Item_iterator_row(Item *base) : base_item(base), current(0) {}
- void open() { current= 0; }
- Item *next()
- {
- if (current >= base_item->cols())
- return NULL;
- return base_item->element_index(current++);
- }
- void close() {}
-};
-
-
-/**
- An interface that is used to take an action when
- the locking module notices that a table version has changed
- since the last execution. "Table" here may refer to any kind of
- table -- a base table, a temporary table, a view or an
- information schema table.
-
- When we open and lock tables for execution of a prepared
- statement, we must verify that they did not change
- since statement prepare. If some table did change, the statement
- parse tree *may* be no longer valid, e.g. in case it contains
- optimizations that depend on table metadata.
-
- This class provides an interface (a method) that is
- invoked when such a situation takes place.
- The implementation of the method simply reports an error, but
- the exact details depend on the nature of the SQL statement.
-
- At most 1 instance of this class is active at a time, in which
- case THD::m_reprepare_observer is not NULL.
-
- @sa check_and_update_table_version() for details of the
- version tracking algorithm
-
- @sa Open_tables_state::m_reprepare_observer for the life cycle
- of metadata observers.
-*/
-
-class Reprepare_observer
-{
-public:
- /**
- Check if a change of metadata is OK. In future
- the signature of this method may be extended to accept the old
- and the new versions, but since currently the check is very
- simple, we only need the THD to report an error.
- */
- bool report_error(THD *thd);
- bool is_invalidated() const { return m_invalidated; }
- void reset_reprepare_observer() { m_invalidated= FALSE; }
- Reprepare_observer() {} /* Remove gcc warning */
-private:
- bool m_invalidated;
-};
-
-#include <waiting_threads.h>
-
+#include "mdl.h"
+#include "probes_mysql.h"
+#include "sql_locale.h" /* my_locale_st */
+#include "sql_profile.h" /* PROFILING */
+#include "scheduler.h" /* thd_scheduler */
+#include "protocol.h" /* Protocol_text, Protocol_binary */
+#include "violite.h" /* vio_is_connected */
+#include "thr_lock.h" /* thr_lock_type, THR_LOCK_DATA,
+ THR_LOCK_INFO */
+
+
+class Reprepare_observer;
class Relay_log_info;
class Query_log_event;
@@ -169,18 +56,61 @@ class sp_cache;
class Lex_input_stream;
class Parser_state;
class Rows_log_event;
+class Sroutine_hash_entry;
+class User_level_lock;
+class user_var_entry;
enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME };
enum enum_duplicates { DUP_ERROR, DUP_REPLACE, DUP_UPDATE };
enum enum_delay_key_write { DELAY_KEY_WRITE_NONE, DELAY_KEY_WRITE_ON,
DELAY_KEY_WRITE_ALL };
-
-#define SLAVE_EXEC_MODE_STRICT (1U << 0)
-#define SLAVE_EXEC_MODE_IDEMPOTENT (1U << 1)
-
+enum enum_slave_exec_mode { SLAVE_EXEC_MODE_STRICT,
+ SLAVE_EXEC_MODE_IDEMPOTENT,
+ SLAVE_EXEC_MODE_LAST_BIT};
+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};
+enum enum_filetype { FILETYPE_CSV, FILETYPE_XML };
+
+/* Bits for different SQL modes modes (including ANSI mode) */
+#define MODE_REAL_AS_FLOAT 1
+#define MODE_PIPES_AS_CONCAT 2
+#define MODE_ANSI_QUOTES 4
+#define MODE_IGNORE_SPACE 8
+#define MODE_IGNORE_BAD_TABLE_OPTIONS 16
+#define MODE_ONLY_FULL_GROUP_BY 32
+#define MODE_NO_UNSIGNED_SUBTRACTION 64
+#define MODE_NO_DIR_IN_CREATE 128
+#define MODE_POSTGRESQL 256
+#define MODE_ORACLE 512
+#define MODE_MSSQL 1024
+#define MODE_DB2 2048
+#define MODE_MAXDB 4096
+#define MODE_NO_KEY_OPTIONS 8192
+#define MODE_NO_TABLE_OPTIONS 16384
+#define MODE_NO_FIELD_OPTIONS 32768
+#define MODE_MYSQL323 65536L
+#define MODE_MYSQL40 (MODE_MYSQL323*2)
+#define MODE_ANSI (MODE_MYSQL40*2)
+#define MODE_NO_AUTO_VALUE_ON_ZERO (MODE_ANSI*2)
+#define MODE_NO_BACKSLASH_ESCAPES (MODE_NO_AUTO_VALUE_ON_ZERO*2)
+#define MODE_STRICT_TRANS_TABLES (MODE_NO_BACKSLASH_ESCAPES*2)
+#define MODE_STRICT_ALL_TABLES (MODE_STRICT_TRANS_TABLES*2)
+#define MODE_NO_ZERO_IN_DATE (MODE_STRICT_ALL_TABLES*2)
+#define MODE_NO_ZERO_DATE (MODE_NO_ZERO_IN_DATE*2)
+#define MODE_INVALID_DATES (MODE_NO_ZERO_DATE*2)
+#define MODE_ERROR_FOR_DIVISION_BY_ZERO (MODE_INVALID_DATES*2)
+#define MODE_TRADITIONAL (MODE_ERROR_FOR_DIVISION_BY_ZERO*2)
+#define MODE_NO_AUTO_CREATE_USER (MODE_TRADITIONAL*2)
+#define MODE_HIGH_NOT_PRECEDENCE (MODE_NO_AUTO_CREATE_USER*2)
+#define MODE_NO_ENGINE_SUBSTITUTION (MODE_HIGH_NOT_PRECEDENCE*2)
+#define MODE_PAD_CHAR_TO_FULL_LENGTH (ULL(1) << 31)
+
+/* Bits for different old style modes */
+#define OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE 1
+#define OLD_MODE_NO_PROGRESS_INFO 2
extern char internal_table_name[2];
extern char empty_c_string[1];
@@ -188,12 +118,47 @@ extern MYSQL_PLUGIN_IMPORT const char **errmesg;
extern bool volatile shutdown_in_progress;
+extern "C" LEX_STRING * thd_query_string (MYSQL_THD thd);
+extern "C" char **thd_query(MYSQL_THD thd);
+
+/**
+ @class CSET_STRING
+ @brief Character set armed LEX_STRING
+*/
+class CSET_STRING
+{
+private:
+ LEX_STRING string;
+ CHARSET_INFO *cs;
+public:
+ CSET_STRING() : cs(&my_charset_bin)
+ {
+ string.str= NULL;
+ string.length= 0;
+ }
+ CSET_STRING(char *str_arg, size_t length_arg, CHARSET_INFO *cs_arg) :
+ cs(cs_arg)
+ {
+ DBUG_ASSERT(cs_arg != NULL);
+ string.str= str_arg;
+ string.length= length_arg;
+ }
+
+ inline char *str() const { return string.str; }
+ inline uint32 length() const { return string.length; }
+ CHARSET_INFO *charset() const { return cs; }
+
+ friend LEX_STRING * thd_query_string (MYSQL_THD thd);
+ friend char **thd_query(MYSQL_THD thd);
+};
+
+
#define TC_LOG_PAGE_SIZE 8192
#define TC_LOG_MIN_SIZE (3*TC_LOG_PAGE_SIZE)
#define TC_HEURISTIC_RECOVER_COMMIT 1
#define TC_HEURISTIC_RECOVER_ROLLBACK 2
-extern uint tc_heuristic_recover;
+extern ulong tc_heuristic_recover;
typedef struct st_user_var_events
{
@@ -202,11 +167,9 @@ typedef struct st_user_var_events
ulong length;
Item_result type;
uint charset_number;
+ bool unsigned_flag;
} BINLOG_USER_VAR_EVENT;
-#define RP_LOCK_LOG_IS_ALREADY_LOCKED 1
-#define RP_FORCE_ROTATE 2
-#define RP_BINLOG_CHECKSUM_ALG_CHANGE 4
/*
The COPY_INFO structure is used by INSERT/REPLACE code.
The schema of the row counting by the INSERT/INSERT ... ON DUPLICATE KEY
@@ -239,9 +202,14 @@ typedef struct st_copy_info {
class Key_part_spec :public Sql_alloc {
public:
- const char *field_name;
+ LEX_STRING field_name;
uint length;
- Key_part_spec(const char *name,uint len=0) :field_name(name), length(len) {}
+ Key_part_spec(const LEX_STRING &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
@@ -294,17 +262,27 @@ public:
enum Keytype type;
KEY_CREATE_INFO key_create_info;
List<Key_part_spec> columns;
- const char *name;
+ LEX_STRING name;
engine_option_value *option_list;
bool generated;
- Key(enum Keytype type_par, const char *name_arg,
+ Key(enum Keytype type_par, const LEX_STRING &name_arg,
KEY_CREATE_INFO *key_info_arg,
bool generated_arg, List<Key_part_spec> &cols,
engine_option_value *create_opt)
:type(type_par), key_create_info(*key_info_arg), columns(cols),
name(name_arg), option_list(create_opt), generated(generated_arg)
{}
+ 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)
+ :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) */
@@ -329,7 +307,7 @@ public:
Table_ident *ref_table;
List<Key_part_spec> ref_columns;
uint delete_opt, update_opt, match_opt;
- Foreign_key(const char *name_arg, List<Key_part_spec> &cols,
+ Foreign_key(const LEX_STRING &name_arg, List<Key_part_spec> &cols,
Table_ident *table, List<Key_part_spec> &ref_cols,
uint delete_opt_arg, uint update_opt_arg, uint match_opt_arg)
:Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols, NULL),
@@ -364,8 +342,68 @@ public:
LEX_COLUMN (const String& x,const uint& y ): column (x),rights (y) {}
};
+class MY_LOCALE;
+
+/**
+ Query_cache_tls -- query cache thread local data.
+*/
+
+struct Query_cache_block;
-/* Note: these states are actually bit coded with HARD */
+struct Query_cache_tls
+{
+ /*
+ 'first_query_block' should be accessed only via query cache
+ functions and methods to maintain proper locking.
+ */
+ Query_cache_block *first_query_block;
+ void set_first_query_block(Query_cache_block *first_query_block_arg)
+ {
+ first_query_block= first_query_block_arg;
+ }
+
+ Query_cache_tls() :first_query_block(NULL) {}
+};
+
+/* SIGNAL / RESIGNAL / GET DIAGNOSTICS */
+
+/**
+ This enumeration list all the condition item names of a condition in the
+ SQL condition area.
+*/
+typedef enum enum_diag_condition_item_name
+{
+ /*
+ Conditions that can be set by the user (SIGNAL/RESIGNAL),
+ and by the server implementation.
+ */
+
+ DIAG_CLASS_ORIGIN= 0,
+ FIRST_DIAG_SET_PROPERTY= DIAG_CLASS_ORIGIN,
+ DIAG_SUBCLASS_ORIGIN= 1,
+ DIAG_CONSTRAINT_CATALOG= 2,
+ DIAG_CONSTRAINT_SCHEMA= 3,
+ DIAG_CONSTRAINT_NAME= 4,
+ DIAG_CATALOG_NAME= 5,
+ DIAG_SCHEMA_NAME= 6,
+ DIAG_TABLE_NAME= 7,
+ DIAG_COLUMN_NAME= 8,
+ DIAG_CURSOR_NAME= 9,
+ DIAG_MESSAGE_TEXT= 10,
+ DIAG_MYSQL_ERRNO= 11,
+ LAST_DIAG_SET_PROPERTY= DIAG_MYSQL_ERRNO
+} 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[];
+
+/**
+ These states are bit coded with HARD. For each state there must be a pair
+ <state_even_num>, and <state_odd_num>_HARD.
+*/
enum killed_state
{
NOT_KILLED= 0,
@@ -375,15 +413,23 @@ enum killed_state
KILL_QUERY= 4,
KILL_QUERY_HARD= 5,
/*
+ ABORT_QUERY signals to the query processor to stop execution ASAP without
+ issuing an error. Instead a warning is issued, and when possible a partial
+ query result is returned to the client.
+ */
+ ABORT_QUERY= 6,
+ ABORT_QUERY_HARD= 7,
+ /*
All of the following killed states will kill the connection
- KILL_CONNECTION must be the first of these!
- */
- KILL_CONNECTION= 6,
- KILL_CONNECTION_HARD= 7,
- KILL_SYSTEM_THREAD= 8,
- KILL_SYSTEM_THREAD_HARD= 9,
- KILL_SERVER= 10,
- KILL_SERVER_HARD= 11
+ KILL_CONNECTION must be the first of these and it must start with
+ an even number (becasue of HARD bit)!
+ */
+ KILL_CONNECTION= 8,
+ KILL_CONNECTION_HARD= 9,
+ KILL_SYSTEM_THREAD= 10,
+ KILL_SYSTEM_THREAD_HARD= 11,
+ KILL_SERVER= 12,
+ KILL_SERVER_HARD= 13
};
extern int killed_errno(killed_state killed);
@@ -395,9 +441,9 @@ enum killed_type
KILL_TYPE_USER
};
-
#include "sql_lex.h" /* Must be here */
+extern LEX_STRING sql_statement_names[(uint) SQLCOM_END + 1];
class Delayed_insert;
class select_result;
class Time_zone;
@@ -407,7 +453,7 @@ class Time_zone;
#define THD_CHECK_SENTRY(thd) DBUG_ASSERT(thd->dbug_sentry == THD_SENTRY_MAGIC)
-struct system_variables
+typedef struct system_variables
{
/*
How dynamically allocated system variables are handled:
@@ -423,20 +469,28 @@ struct system_variables
*/
ulong dynamic_variables_version;
char* dynamic_variables_ptr;
- uint dynamic_variables_head; /* largest valid variable offset */
- uint dynamic_variables_size; /* how many bytes are in use */
-
- ulonglong myisam_max_extra_sort_file_size;
- ulonglong myisam_max_sort_file_size;
+ uint dynamic_variables_head; /* largest valid variable offset */
+ uint dynamic_variables_size; /* how many bytes are in use */
+
ulonglong max_heap_table_size;
ulonglong tmp_table_size;
ulonglong long_query_time;
+ ulonglong optimizer_switch;
+ ulonglong sql_mode; ///< which non-standard SQL behaviour should be enabled
+ ulonglong old_behavior; ///< which old SQL behaviour should be enabled
+ ulonglong option_bits; ///< OPTION_xxx constants, e.g. OPTION_PROFILING
ulonglong join_buff_space_limit;
+ ulonglong log_slow_filter;
+ ulonglong log_slow_verbosity;
+ ulonglong bulk_insert_buff_size;
+ ulonglong join_buff_size;
+ ulonglong sortbuff_size;
+ ulonglong group_concat_max_len;
ha_rows select_limit;
ha_rows max_join_size;
+ ha_rows expensive_subquery_limit;
ulong auto_increment_increment, auto_increment_offset;
- ulong bulk_insert_buff_size;
- ulong join_buff_size;
+ ulong lock_wait_timeout;
ulong join_cache_level;
ulong max_allowed_packet;
ulong max_error_count;
@@ -445,9 +499,7 @@ struct system_variables
ulong max_tmp_tables;
ulong max_insert_delayed_threads;
ulong min_examined_row_limit;
- ulong myisam_repair_threads;
- ulong myisam_sort_buff_size;
- ulong myisam_stats_method;
+ ulong multi_range_count;
ulong net_buffer_length;
ulong net_interactive_timeout;
ulong net_read_timeout;
@@ -456,26 +508,15 @@ struct system_variables
ulong net_write_timeout;
ulong optimizer_prune_level;
ulong optimizer_search_depth;
- /* A bitmap for switching optimizations on/off */
- ulong optimizer_switch;
ulong preload_buff_size;
ulong profiling_history_size;
- ulong query_cache_type;
ulong read_buff_size;
ulong read_rnd_buff_size;
ulong mrr_buff_size;
ulong div_precincrement;
- ulong sortbuff_size;
/* Total size of all buffers used by the subselect_rowid_merge_engine. */
ulong rowid_merge_buff_size;
- ulong thread_handling;
- ulong tx_isolation;
- ulong completion_type;
- /* Determines which non-standard SQL behaviour should be enabled */
- ulong sql_mode;
ulong max_sp_recursion_depth;
- /* check of key presence in updatable view */
- ulong updatable_views_with_limit;
ulong default_week_format;
ulong max_seeks_for_key;
ulong range_alloc_block_size;
@@ -484,42 +525,34 @@ struct system_variables
ulong trans_alloc_block_size;
ulong trans_prealloc_size;
ulong log_warnings;
- ulong group_concat_max_len;
/* Flags for slow log filtering */
ulong log_slow_rate_limit;
- ulong log_slow_filter;
- ulong log_slow_verbosity;
- ulong ndb_autoincrement_prefetch_sz;
- ulong ndb_index_stat_cache_entries;
- ulong ndb_index_stat_update_freq;
- ulong binlog_format; // binlog format for this thd (see enum_binlog_format)
+ ulong binlog_format; ///< binlog format for this thd (see enum_binlog_format)
ulong progress_report_time;
my_bool binlog_annotate_row_events;
my_bool binlog_direct_non_trans_update;
- /*
+ my_bool sql_log_bin;
+ ulong completion_type;
+ ulong query_cache_type;
+ ulong tx_isolation;
+ ulong updatable_views_with_limit;
+ int max_user_connections;
+ /**
In slave thread we need to know in behalf of which
thread the query is being run to replicate temp tables properly
*/
my_thread_id pseudo_thread_id;
my_bool low_priority_updates;
- my_bool new_mode;
- /*
- compatibility option:
- - index usage hints (USE INDEX without a FOR clause) behave as in 5.0
- */
- my_bool old_mode;
my_bool query_cache_wlock_invalidate;
my_bool engine_condition_pushdown;
my_bool keep_files_on_create;
- my_bool ndb_force_send;
- my_bool ndb_use_copying_alter_table;
- my_bool ndb_use_exact_count;
- my_bool ndb_use_transactions;
- my_bool ndb_index_stat_enable;
+ my_bool old_mode;
my_bool old_alter_table;
my_bool old_passwords;
+ my_bool big_tables;
+ my_bool query_cache_strip_comments;
plugin_ref table_plugin;
@@ -533,22 +566,24 @@ struct system_variables
CHARSET_INFO *collation_database;
CHARSET_INFO *collation_connection;
+ /* Error messages */
+ MY_LOCALE *lc_messages;
/* Locale Support */
MY_LOCALE *lc_time_names;
Time_zone *time_zone;
- /* DATE, DATETIME and MYSQL_TIME formats */
- DATE_TIME_FORMAT *date_format;
- DATE_TIME_FORMAT *datetime_format;
- DATE_TIME_FORMAT *time_format;
my_bool sysdate_is_now;
/* deadlock detection */
ulong wt_timeout_short, wt_deadlock_search_depth_short;
ulong wt_timeout_long, wt_deadlock_search_depth_long;
-};
+ double long_query_time_double;
+
+ my_bool pseudo_slave_mode;
+
+} SV;
/**
Per thread status variables.
@@ -594,23 +629,17 @@ typedef struct system_status_var
ulong ha_savepoint_count;
ulong ha_savepoint_rollback_count;
- /* KEY_CACHE parts. These are copies of the original */
- ulong key_blocks_changed;
- ulong key_blocks_used;
- ulong key_cache_r_requests;
- ulong key_cache_read;
- ulong key_cache_w_requests;
- ulong key_cache_write;
- /* END OF KEY_CACHE parts */
-
ulong net_big_packet_count;
ulong opened_tables;
ulong opened_shares;
+ ulong opened_views; /* +1 opening a view */
+
ulong select_full_join_count;
ulong select_full_range_join_count;
ulong select_range_count;
ulong select_range_check_count;
ulong select_scan_count;
+ ulong executed_triggers;
ulong long_query_count;
ulong filesort_merge_passes;
ulong filesort_range_count;
@@ -624,6 +653,17 @@ typedef struct system_status_var
ulong com_stmt_fetch;
ulong com_stmt_reset;
ulong com_stmt_close;
+
+ /* Features 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_locale; /* +1 when LOCALE is set */
+ ulong feature_subquery; /* +1 when subqueries are used */
+ ulong feature_timezone; /* +1 when XPATH is used */
+ ulong feature_trigger; /* +1 opening a table with triggers */
+ ulong feature_xml; /* +1 when XPATH is used */
+
ulong empty_queries;
ulong access_denied_errors;
ulong lost_connections;
@@ -655,6 +695,11 @@ typedef struct system_status_var
#define last_system_status_var questions
+void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var);
+
+void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
+ STATUS_VAR *dec_var);
+
void mark_transaction_to_rollback(THD *thd, bool all);
#ifdef MYSQL_SERVER
@@ -685,14 +730,14 @@ public:
/*
The states relfects three diffrent life cycles for three
different types of statements:
- Prepared statement: INITIALIZED -> PREPARED -> EXECUTED.
- Stored procedure: INITIALIZED_FOR_SP -> EXECUTED.
- Other statements: CONVENTIONAL_EXECUTION never changes.
+ Prepared statement: STMT_INITIALIZED -> STMT_PREPARED -> STMT_EXECUTED.
+ Stored procedure: STMT_INITIALIZED_FOR_SP -> STMT_EXECUTED.
+ Other statements: STMT_CONVENTIONAL_EXECUTION never changes.
*/
enum enum_state
{
- INITIALIZED= 0, INITIALIZED_FOR_SP= 1, PREPARED= 2,
- CONVENTIONAL_EXECUTION= 3, EXECUTED= 4, ERROR= -1
+ STMT_INITIALIZED= 0, STMT_INITIALIZED_FOR_SP= 1, STMT_PREPARED= 2,
+ STMT_CONVENTIONAL_EXECUTION= 3, STMT_EXECUTED= 4, STMT_ERROR= -1
};
enum_state state;
@@ -715,18 +760,15 @@ public:
virtual Type type() const;
virtual ~Query_arena() {};
- inline bool is_stmt_prepare() const { return state == INITIALIZED; }
- inline bool is_first_sp_execute() const
- { return state == INITIALIZED_FOR_SP; }
+ inline bool is_stmt_prepare() const { return state == STMT_INITIALIZED; }
inline bool is_stmt_prepare_or_first_sp_execute() const
- { return (int)state < (int)PREPARED; }
+ { return (int)state < (int)STMT_PREPARED; }
inline bool is_stmt_prepare_or_first_stmt_execute() const
- { return (int)state <= (int)PREPARED; }
- inline bool is_first_stmt_execute() const { return state == PREPARED; }
+ { return (int)state <= (int)STMT_PREPARED; }
inline bool is_stmt_execute() const
- { return state == PREPARED || state == EXECUTED; }
+ { return state == STMT_PREPARED || state == STMT_EXECUTED; }
inline bool is_conventional() const
- { return state == CONVENTIONAL_EXECUTION; }
+ { return state == STMT_CONVENTIONAL_EXECUTION; }
inline void* alloc(size_t size) { return alloc_root(mem_root,size); }
inline void* calloc(size_t size)
@@ -818,17 +860,30 @@ public:
This printing is needed at least in SHOW PROCESSLIST and SHOW
ENGINE INNODB STATUS.
*/
- LEX_STRING query_string;
+ CSET_STRING query_string;
/*
If opt_query_cache_strip_comments is set, this contains query without
comments. If not set, it contains pointer to query_string.
*/
String base_query;
- Server_side_cursor *cursor;
- inline char *query() { return query_string.str; }
- inline uint32 query_length() { return (uint32)query_string.length; }
- void set_query_inner(char *query_arg, uint32 query_length_arg);
+
+ inline char *query() const { return query_string.str(); }
+ inline uint32 query_length() const { return query_string.length(); }
+ CHARSET_INFO *query_charset() const { return query_string.charset(); }
+ void set_query_inner(const CSET_STRING &string_arg)
+ {
+ query_string= string_arg;
+ }
+ void set_query_inner(char *query_arg, uint32 query_length_arg,
+ CHARSET_INFO *cs_arg)
+ {
+ set_query_inner(CSET_STRING(query_arg, query_length_arg, cs_arg));
+ }
+ void reset_query_inner()
+ {
+ set_query_inner(CSET_STRING());
+ }
/**
Name of the current (default) database.
@@ -885,8 +940,8 @@ public:
Statement *find_by_name(LEX_STRING *name)
{
Statement *stmt;
- stmt= (Statement*)hash_search(&names_hash, (uchar*)name->str,
- name->length);
+ stmt= (Statement*)my_hash_search(&names_hash, (uchar*)name->str,
+ name->length);
return stmt;
}
@@ -895,7 +950,7 @@ public:
if (last_found_statement == 0 || id != last_found_statement->id)
{
Statement *stmt;
- stmt= (Statement *) hash_search(&st_hash, (uchar *) &id, sizeof(id));
+ stmt= (Statement *) my_hash_search(&st_hash, (uchar *) &id, sizeof(id));
if (stmt && stmt->name.str)
return NULL;
last_found_statement= stmt;
@@ -924,6 +979,8 @@ struct st_savepoint {
char *name;
uint length;
Ha_trx_info *ha_list;
+ /** State of metadata locks before this savepoint was set. */
+ MDL_savepoint mdl_savepoint;
};
enum xa_states {XA_NOTR=0, XA_ACTIVE, XA_IDLE, XA_PREPARED, XA_ROLLBACK_ONLY};
@@ -938,7 +995,7 @@ typedef struct st_xid_state {
uint rm_error;
} XID_STATE;
-extern pthread_mutex_t LOCK_xid_cache;
+extern mysql_mutex_t LOCK_xid_cache;
extern HASH xid_cache;
bool xid_cache_init(void);
void xid_cache_free(void);
@@ -964,8 +1021,11 @@ public:
*/
char *host, *user, *ip;
char priv_user[USERNAME_LENGTH];
+ char proxy_user[USERNAME_LENGTH + MAX_HOSTNAME + 5];
/* The host privilege we are using */
char priv_host[MAX_HOSTNAME];
+ /* The external user (if available) */
+ char *external_user;
/* points to host if host is available, otherwise points to ip */
const char *host_or_ip;
ulong master_access; /* Global privileges from mysql.user */
@@ -1008,12 +1068,17 @@ typedef I_List<Item_change_record> Item_change_list;
/**
- Type of prelocked mode.
- See comment for THD::prelocked_mode for complete description.
+ Type of locked tables mode.
+ See comment for THD::locked_tables_mode for complete description.
*/
-enum prelocked_mode_type {NON_PRELOCKED= 0, PRELOCKED= 1,
- PRELOCKED_UNDER_LOCK_TABLES= 2};
+enum enum_locked_tables_mode
+{
+ LTM_NONE= 0,
+ LTM_LOCK_TABLES,
+ LTM_PRELOCKED,
+ LTM_PRELOCKED_UNDER_LOCK_TABLES
+};
/**
@@ -1052,11 +1117,6 @@ public:
XXX Why are internal temporary tables added to this list?
*/
TABLE *temporary_tables;
- /**
- List of tables that were opened with HANDLER OPEN and are
- still in use by this thread.
- */
- TABLE *handler_tables;
TABLE *derived_tables;
/*
During a MySQL session, one can lock tables in two modes: automatic
@@ -1066,19 +1126,13 @@ public:
statement ends.
Manual mode comes into play when a user issues a 'LOCK TABLES'
statement. In this mode the user can only use the locked tables.
- Trying to use any other tables will give an error. The locked tables are
- stored in 'locked_tables' member. Manual locking is described in
+ Trying to use any other tables will give an error.
+ The locked tables are also stored in this member, however,
+ thd->locked_tables_mode is turned on. Manual locking is described in
the 'LOCK_TABLES' chapter of the MySQL manual.
See also lock_tables() for details.
*/
MYSQL_LOCK *lock;
- /*
- Tables that were locked with explicit or implicit LOCK TABLES.
- (Implicit LOCK TABLES happens when we are prelocking tables for
- execution of statement which uses stored routines. See description
- THD::prelocked_mode for more info.)
- */
- MYSQL_LOCK *locked_tables;
/*
CREATE-SELECT keeps an extra lock for the table being
@@ -1088,30 +1142,34 @@ public:
MYSQL_LOCK *extra_lock;
/*
- prelocked_mode_type enum and prelocked_mode member are used for
- indicating whenever "prelocked mode" is on, and what type of
- "prelocked mode" is it.
-
- Prelocked mode is used for execution of queries which explicitly
- or implicitly (via views or triggers) use functions, thus may need
- some additional tables (mentioned in query table list) for their
- execution.
-
- First open_tables() call for such query will analyse all functions
- used by it and add all additional tables to table its list. It will
- also mark this query as requiring prelocking. After that lock_tables()
- will issue implicit LOCK TABLES for the whole table list and change
- thd::prelocked_mode to non-0. All queries called in functions invoked
- by the main query will use prelocked tables. Non-0 prelocked_mode
- will also surpress mentioned analysys in those queries thus saving
- cycles. Prelocked mode will be turned off once close_thread_tables()
- for the main query will be called.
-
- Note: Since not all "tables" present in table list are really locked
- thd::prelocked_mode does not imply thd::locked_tables.
+ Enum enum_locked_tables_mode and locked_tables_mode member are
+ used to indicate whether the so-called "locked tables mode" is on,
+ and what kind of mode is active.
+
+ Locked tables mode is used when it's necessary to open and
+ lock many tables at once, for usage across multiple
+ (sub-)statements.
+ This may be necessary either for queries that use stored functions
+ and triggers, in which case the statements inside functions and
+ triggers may be executed many times, or for implementation of
+ LOCK TABLES, in which case the opened tables are reused by all
+ subsequent statements until a call to UNLOCK TABLES.
+
+ The kind of locked tables mode employed for stored functions and
+ triggers is also called "prelocked mode".
+ In this mode, first open_tables() call to open the tables used
+ in a statement analyses all functions used by the statement
+ and adds all indirectly used tables to the list of tables to
+ open and lock.
+ It also marks the parse tree of the statement as requiring
+ prelocking. After that, lock_tables() locks the entire list
+ of tables and changes THD::locked_tables_modeto LTM_PRELOCKED.
+ All statements executed inside functions or triggers
+ use the prelocked tables, instead of opening their own ones.
+ Prelocked mode is turned off automatically once close_thread_tables()
+ of the main statement is called.
*/
- prelocked_mode_type prelocked_mode;
- ulong version;
+ enum enum_locked_tables_mode locked_tables_mode;
uint current_tablenr;
enum enum_flags {
@@ -1122,30 +1180,49 @@ public:
Flags with information about the open tables state.
*/
uint state_flags;
-
- /*
- This constructor serves for creation of Open_tables_state instances
- which are used as backup storage.
+ /**
+ This constructor initializes Open_tables_state instance which can only
+ be used as backup storage. To prepare Open_tables_state instance for
+ operations which open/lock/close tables (e.g. open_table()) one has to
+ call init_open_tables_state().
*/
Open_tables_state() : state_flags(0U) { }
- Open_tables_state(ulong version_arg);
-
void set_open_tables_state(Open_tables_state *state)
{
*this= *state;
}
- void reset_open_tables_state()
+ void reset_open_tables_state(THD *thd)
{
- open_tables= temporary_tables= handler_tables= derived_tables= 0;
- extra_lock= lock= locked_tables= 0;
- prelocked_mode= NON_PRELOCKED;
+ open_tables= temporary_tables= derived_tables= 0;
+ extra_lock= lock= 0;
+ locked_tables_mode= LTM_NONE;
state_flags= 0U;
m_reprepare_observer= NULL;
}
};
+
+/**
+ Storage for backup of Open_tables_state. Must
+ be used only to open system tables (TABLE_CATEGORY_SYSTEM
+ and TABLE_CATEGORY_LOG).
+*/
+
+class Open_tables_backup: public Open_tables_state
+{
+public:
+ /**
+ When we backup the open tables state to open a system
+ table or tables, we want to save state of metadata
+ locks which were acquired before the backup. It is used
+ to release metadata locks on system tables after they are
+ no longer used.
+ */
+ MDL_savepoint mdl_system_tables_svp;
+};
+
/**
@class Sub_statement_state
@brief Used to save context when executing a function or trigger
@@ -1160,7 +1237,7 @@ public:
class Sub_statement_state
{
public:
- ulonglong options;
+ 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;
@@ -1225,12 +1302,12 @@ protected:
public:
/**
- Handle an error condition.
+ Handle a sql condition.
This method can be implemented by a subclass to achieve any of the
following:
- - mask an error internally, prevent exposing it to the user,
- - mask an error and throw another one instead.
- When this method returns true, the error condition is considered
+ - mask a warning/error internally, prevent exposing it to the user,
+ - mask a warning/error and throw another one instead.
+ When this method returns true, the sql condition is considered
'handled', and will not be propagated to upper layers.
It is the responsability of the code installing an internal handler
to then check for trapped conditions, and implement logic to recover
@@ -1244,15 +1321,17 @@ public:
before removing it from the exception stack with
<code>THD::pop_internal_handler()</code>.
- @param sql_errno the error number
- @param level the error level
@param thd the calling thread
- @return true if the error is handled
+ @param cond the condition raised.
+ @return true if the condition is handled
*/
- virtual bool handle_error(uint sql_errno,
- const char *message,
- MYSQL_ERROR::enum_warning_level level,
- THD *thd) = 0;
+ virtual bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl) = 0;
+
private:
Internal_error_handler *m_prev_internal_handler;
friend class THD;
@@ -1267,10 +1346,12 @@ private:
class Dummy_error_handler : public Internal_error_handler
{
public:
- bool handle_error(uint sql_errno,
- const char *message,
- MYSQL_ERROR::enum_warning_level level,
- THD *thd)
+ bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl)
{
/* Ignore error */
return TRUE;
@@ -1280,7 +1361,7 @@ public:
/**
- This class is an internal error handler implementation for
+ This class is an internal error handler implementation for
DROP TABLE statements. The thing is that there may be warnings during
execution of these statements, which should not be exposed to the user.
This class is intended to silence such warnings.
@@ -1289,142 +1370,82 @@ public:
class Drop_table_error_handler : public Internal_error_handler
{
public:
- Drop_table_error_handler(Internal_error_handler *err_handler)
- :m_err_handler(err_handler)
- { }
+ Drop_table_error_handler() {}
public:
- bool handle_error(uint sql_errno,
- const char *message,
- MYSQL_ERROR::enum_warning_level level,
- THD *thd);
+ bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl);
private:
- Internal_error_handler *m_err_handler;
};
/**
- Stores status of the currently executed statement.
- Cleared at the beginning of the statement, and then
- can hold either OK, ERROR, or EOF status.
- Can not be assigned twice per statement.
+ Tables that were locked with LOCK TABLES statement.
+
+ Encapsulates a list of TABLE_LIST instances for tables
+ locked by LOCK TABLES statement, memory root for metadata locks,
+ and, generally, the context of LOCK TABLES statement.
+
+ In LOCK TABLES mode, the locked tables are kept open between
+ statements.
+ Therefore, we can't allocate metadata locks on execution memory
+ root -- as well as tables, the locks need to stay around till
+ UNLOCK TABLES is called.
+ The locks are allocated in the memory root encapsulated in this
+ class.
+
+ Some SQL commands, like FLUSH TABLE or ALTER TABLE, demand that
+ the tables they operate on are closed, at least temporarily.
+ This class encapsulates a list of TABLE_LIST instances, one
+ for each base table from LOCK TABLES list,
+ which helps conveniently close the TABLEs when it's necessary
+ and later reopen them.
+
+ Implemented in sql_base.cc
*/
-class Diagnostics_area
+class Locked_tables_list
{
+private:
+ MEM_ROOT m_locked_tables_root;
+ TABLE_LIST *m_locked_tables;
+ TABLE_LIST **m_locked_tables_last;
+ /** An auxiliary array used only in reopen_tables(). */
+ TABLE **m_reopen_array;
+ /**
+ Count the number of tables in m_locked_tables list. We can't
+ rely on thd->lock->table_count because it excludes
+ non-transactional temporary tables. We need to know
+ an exact number of TABLE objects.
+ */
+ size_t m_locked_tables_count;
public:
- enum enum_diagnostics_status
- {
- /** The area is cleared at start of a statement. */
- DA_EMPTY= 0,
- /** Set whenever one calls my_ok(). */
- DA_OK,
- /** Set whenever one calls my_eof(). */
- DA_EOF,
- /** Set whenever one calls my_error() or my_message(). */
- DA_ERROR,
- /** Set in case of a custom response, such as one from COM_STMT_PREPARE. */
- DA_DISABLED
- };
- /** True if status information is sent to the client. */
- bool is_sent;
- /** Set to make set_error_status after set_{ok,eof}_status possible. */
- bool can_overwrite_status;
-
- void set_ok_status(THD *thd, ha_rows affected_rows_arg,
- ulonglong last_insert_id_arg,
- const char *message);
- void set_eof_status(THD *thd);
- void set_error_status(THD *thd, uint sql_errno_arg, const char *message_arg);
-
- void disable_status();
-
- void reset_diagnostics_area();
-
- bool is_set() const { return m_status != DA_EMPTY; }
- bool is_error() const { return m_status == DA_ERROR; }
- bool is_eof() const { return m_status == DA_EOF; }
- bool is_ok() const { return m_status == DA_OK; }
- bool is_disabled() const { return m_status == DA_DISABLED; }
- enum_diagnostics_status status() const { return m_status; }
-
- const char *message() const
- { DBUG_ASSERT(m_status == DA_ERROR || m_status == DA_OK); return m_message; }
-
- uint sql_errno() const
- { DBUG_ASSERT(m_status == DA_ERROR); return m_sql_errno; }
-
- uint server_status() const
- {
- DBUG_ASSERT(m_status == DA_OK || m_status == DA_EOF);
- return m_server_status;
- }
-
- ha_rows affected_rows() const
- { DBUG_ASSERT(m_status == DA_OK); return m_affected_rows; }
-
- ulonglong last_insert_id() const
- { DBUG_ASSERT(m_status == DA_OK); return m_last_insert_id; }
-
- uint total_warn_count() const
+ Locked_tables_list()
+ :m_locked_tables(NULL),
+ m_locked_tables_last(&m_locked_tables),
+ m_reopen_array(NULL),
+ m_locked_tables_count(0)
{
- DBUG_ASSERT(m_status == DA_OK || m_status == DA_EOF);
- return m_total_warn_count;
+ init_sql_alloc(&m_locked_tables_root, MEM_ROOT_BLOCK_SIZE, 0);
}
-
- /* Used to count any warnings pushed after calling set_ok_status(). */
- void increment_warning()
+ void unlock_locked_tables(THD *thd);
+ ~Locked_tables_list()
{
- if (m_status != DA_EMPTY)
- m_total_warn_count++;
+ unlock_locked_tables(0);
}
-
- Diagnostics_area() { reset_diagnostics_area(); }
-
-private:
- /** Message buffer. Can be used by OK or ERROR status. */
- char m_message[MYSQL_ERRMSG_SIZE];
- /**
- SQL error number. One of ER_ codes from share/errmsg.txt.
- Set by set_error_status.
- */
- uint m_sql_errno;
-
- /**
- Copied from thd->server_status when the diagnostics area is assigned.
- We need this member as some places in the code use the following pattern:
- thd->server_status|= ...
- my_eof(thd);
- thd->server_status&= ~...
- Assigned by OK, EOF or ERROR.
- */
- uint m_server_status;
- /**
- The number of rows affected by the last statement. This is
- semantically close to thd->row_count_func, but has a different
- life cycle. thd->row_count_func stores the value returned by
- function ROW_COUNT() and is cleared only by statements that
- update its value, such as INSERT, UPDATE, DELETE and few others.
- This member is cleared at the beginning of the next statement.
-
- We could possibly merge the two, but life cycle of thd->row_count_func
- can not be changed.
- */
- ha_rows m_affected_rows;
- /**
- Similarly to the previous member, this is a replacement of
- thd->first_successful_insert_id_in_prev_stmt, which is used
- to implement LAST_INSERT_ID().
- */
- ulonglong m_last_insert_id;
- /** The total number of warnings. */
- uint m_total_warn_count;
- enum_diagnostics_status m_status;
- /**
- @todo: the following THD members belong here:
- - warn_list, warn_count,
- */
+ bool init_locked_tables(THD *thd);
+ TABLE_LIST *locked_tables() { return m_locked_tables; }
+ void unlink_from_list(THD *thd, TABLE_LIST *table_list,
+ bool remove_from_locked_tables);
+ void unlink_all_closed_tables(THD *thd,
+ MYSQL_LOCK *lock,
+ size_t reopen_count);
+ bool reopen_tables(THD *thd);
};
@@ -1458,6 +1479,63 @@ struct Ha_data
Ha_data() :ha_ptr(NULL) {}
};
+/**
+ An instance of the global read lock in a connection.
+ Implemented in lock.cc.
+*/
+
+class Global_read_lock
+{
+public:
+ enum enum_grl_state
+ {
+ GRL_NONE,
+ GRL_ACQUIRED,
+ GRL_ACQUIRED_AND_BLOCKS_COMMIT
+ };
+
+ Global_read_lock()
+ : m_state(GRL_NONE),
+ m_mdl_global_shared_lock(NULL),
+ m_mdl_blocks_commits_lock(NULL)
+ {}
+
+ bool lock_global_read_lock(THD *thd);
+ void unlock_global_read_lock(THD *thd);
+ /**
+ Check if this connection can acquire protection against GRL and
+ emit error if otherwise.
+ */
+ bool can_acquire_protection() const
+ {
+ if (m_state)
+ {
+ my_error(ER_CANT_UPDATE_WITH_READLOCK, MYF(0));
+ return TRUE;
+ }
+ return FALSE;
+ }
+ bool make_global_read_lock_block_commit(THD *thd);
+ bool is_acquired() const { return m_state != GRL_NONE; }
+ void set_explicit_lock_duration(THD *thd);
+private:
+ enum_grl_state m_state;
+ /**
+ In order to acquire the global read lock, the connection must
+ acquire shared metadata lock in GLOBAL namespace, to prohibit
+ all DDL.
+ */
+ MDL_ticket *m_mdl_global_shared_lock;
+ /**
+ Also in order to acquire the global read lock, the connection
+ must acquire a shared metadata lock in COMMIT namespace, to
+ prohibit commits.
+ */
+ MDL_ticket *m_mdl_blocks_commits_lock;
+};
+
+
+extern "C" void my_message_sql(uint error, const char *str, myf MyFlags);
/**
@class THD
@@ -1468,12 +1546,28 @@ struct Ha_data
class THD :public Statement,
public Open_tables_state
{
+private:
+ inline bool is_stmt_prepare() const
+ { DBUG_ASSERT(0); return Statement::is_stmt_prepare(); }
+
+ inline bool is_stmt_prepare_or_first_sp_execute() const
+ { DBUG_ASSERT(0); return Statement::is_stmt_prepare_or_first_sp_execute(); }
+
+ inline bool is_stmt_prepare_or_first_stmt_execute() const
+ { DBUG_ASSERT(0); return Statement::is_stmt_prepare_or_first_stmt_execute(); }
+
+ inline bool is_conventional() const
+ { DBUG_ASSERT(0); return Statement::is_conventional(); }
+
public:
+ MDL_context mdl_context;
+
/* Used to execute base64 coded binlog events in MySQL server */
Relay_log_info* rli_fake;
/* Slave applier execution context */
Relay_log_info* rli_slave;
+ void reset_for_next_command();
/*
Constant for THD::where initialization in the beginning of every query.
@@ -1501,45 +1595,32 @@ public:
*/
struct st_mysql_stmt *current_stmt;
#endif
+#ifdef HAVE_QUERY_CACHE
+ Query_cache_tls query_cache_tls;
+#endif
NET net; // client connection descriptor
scheduler_functions *scheduler; // Scheduler for this connection
- MEM_ROOT warn_root; // For warnings and errors
Protocol *protocol; // Current protocol
Protocol_text protocol_text; // Normal protocol
Protocol_binary protocol_binary; // Binary protocol
HASH user_vars; // hash for user variables
String packet; // dynamic buffer for network I/O
String convert_buffer; // buffer for charset conversions
- struct sockaddr_in remote; // client socket address
struct my_rnd_struct rand; // used for authentication
struct system_variables variables; // Changeable local variables
struct system_status_var status_var; // Per thread statistic vars
struct system_status_var org_status_var; // For user statistics
struct system_status_var *initial_status_var; /* used by show status */
THR_LOCK_INFO lock_info; // Locking info of this thread
- THR_LOCK_OWNER main_lock_id; // To use for conventional queries
- THR_LOCK_OWNER *lock_id; // If not main_lock_id, points to
- // the lock_id of a cursor.
/**
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).
+ Is locked when THD is deleted.
*/
- pthread_mutex_t LOCK_thd_data;
-
- /**
- - Protects thd->mysys_var (used during KILL statement and shutdown).
- - Is Locked when THD is deleted.
-
- Note: This responsibility was earlier handled by LOCK_thd_data.
- This lock is introduced to solve a deadlock issue waiting for
- LOCK_thd_data. As this lock reduces responsibility of LOCK_thd_data
- the deadlock issues is solved.
- Caution: LOCK_thd_kill should not be taken while holding LOCK_thd_data.
- THD::awake() currently takes LOCK_thd_data after holding
- LOCK_thd_kill.
- */
- pthread_mutex_t LOCK_thd_kill;
+ mysql_mutex_t LOCK_thd_data;
/* all prepared statements and cursors of this connection */
Statement_map stmt_map;
@@ -1619,7 +1700,7 @@ public:
my_hrtime_t user_time;
// track down slow pthread_create
ulonglong prior_thr_create_utime, thr_create_utime;
- ulonglong start_utime, utime_after_lock;
+ ulonglong start_utime, utime_after_lock, utime_after_query;
// Process indicator
struct {
@@ -1638,6 +1719,7 @@ public:
uint stage, max_stage;
ulonglong counter, max_counter;
ulonglong next_report_time;
+ Query_arena *arena;
} progress;
thr_lock_type update_lock_default;
@@ -1645,14 +1727,14 @@ public:
/* <> 0 if we are inside of trigger or stored function. */
uint in_sub_stmt;
-
- /* TRUE when the current top has SQL_LOG_BIN ON */
- bool sql_log_bin_toplevel;
/* True when opt_userstat_running is set at start of query */
bool userstat_running;
/* True if we want to log all errors */
bool log_all_errors;
+ /* Do not set socket timeouts for wait_timeout (used with threadpool) */
+ bool skip_wait_timeout;
+
/* container for handler's private per-connection data */
Ha_data ha_data[MAX_HA];
@@ -1667,7 +1749,7 @@ public:
bool save_prep_leaf_list;
#ifndef MYSQL_CLIENT
- int binlog_setup_trx_data();
+ binlog_cache_mngr * binlog_setup_trx_data();
/*
Public interface to write RBR events to the binlog
@@ -1698,32 +1780,65 @@ public:
size_t needed,
bool is_transactional,
RowsEventT* hint);
- Rows_log_event* binlog_get_pending_rows_event() const;
- void binlog_set_pending_rows_event(Rows_log_event* ev);
- int binlog_flush_pending_rows_event(bool stmt_end);
- int binlog_remove_pending_rows_event(bool clear_maps);
+ Rows_log_event* binlog_get_pending_rows_event(bool is_transactional) const;
+ void binlog_set_pending_rows_event(Rows_log_event* ev, bool is_transactional);
+ inline int binlog_flush_pending_rows_event(bool stmt_end)
+ {
+ return (binlog_flush_pending_rows_event(stmt_end, FALSE) ||
+ binlog_flush_pending_rows_event(stmt_end, TRUE));
+ }
+ int binlog_flush_pending_rows_event(bool stmt_end, bool is_transactional);
+ int binlog_remove_pending_rows_event(bool clear_maps, bool is_transactional);
+
+ /**
+ Determine the binlog format of the current statement.
+
+ @retval 0 if the current statement will be logged in statement
+ format.
+ @retval nonzero if the current statement will be logged in row
+ format.
+ */
+ int is_current_stmt_binlog_format_row() const {
+ DBUG_ASSERT(current_stmt_binlog_format == BINLOG_FORMAT_STMT ||
+ current_stmt_binlog_format == BINLOG_FORMAT_ROW);
+ return current_stmt_binlog_format == BINLOG_FORMAT_ROW;
+ }
private:
+ /**
+ Indicates the format in which the current statement will be
+ logged. This can only be set from @c decide_logging_format().
+ */
+ enum_binlog_format current_stmt_binlog_format;
+
+ /**
+ Bit field for the state of binlog warnings.
+
+ The first Lex::BINLOG_STMT_UNSAFE_COUNT bits list all types of
+ unsafeness that the current statement has.
+
+ This must be a member of THD and not of LEX, because warnings are
+ detected and issued in different places (@c
+ decide_logging_format() and @c binlog_query(), respectively).
+ Between these calls, the THD->lex object may change; e.g., if a
+ stored routine is invoked. Only THD persists between the calls.
+ */
+ uint32 binlog_unsafe_warning_flags;
+
/*
Number of outstanding table maps, i.e., table maps in the
transaction cache.
*/
uint binlog_table_maps;
-
- enum enum_binlog_flag {
- BINLOG_FLAG_UNSAFE_STMT_PRINTED,
- BINLOG_FLAG_COUNT
- };
-
- /**
- Flags with per-thread information regarding the status of the
- binary log.
- */
- uint32 binlog_flags;
public:
+ void issue_unsafe_warnings();
+
uint get_binlog_table_maps() const {
return binlog_table_maps;
}
+ void clear_binlog_table_maps() {
+ binlog_table_maps= 0;
+ }
#endif /* MYSQL_CLIENT */
public:
@@ -1746,6 +1861,7 @@ public:
MEM_ROOT mem_root; // Transaction-life memory allocation pool
void cleanup()
{
+ DBUG_ENTER("thd::cleanup");
changed_tables= 0;
savepoints= 0;
/*
@@ -1756,21 +1872,21 @@ public:
*/
if (!xid_state.rm_error)
xid_state.xid.null();
-#ifdef USING_TRANSACTIONS
free_root(&mem_root,MYF(MY_KEEP_PREALLOC));
-#endif
+ DBUG_VOID_RETURN;
+ }
+ my_bool is_active()
+ {
+ return (all.ha_list != NULL);
}
st_transactions()
{
-#ifdef USING_TRANSACTIONS
bzero((char*)this, sizeof(*this));
xid_state.xid.null();
init_sql_alloc(&mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
-#else
- xid_state.xa_state= XA_NOTR;
-#endif
}
} transaction;
+ Global_read_lock global_read_lock;
Field *dup_field;
#ifndef __WIN__
sigset_t signals;
@@ -1945,8 +2061,50 @@ public:
}
ulonglong limit_found_rows;
- ulonglong options; /* Bitmap of states */
- longlong row_count_func; /* For the ROW_COUNT() function */
+
+private:
+ /**
+ Stores the result of ROW_COUNT() function.
+
+ ROW_COUNT() function is a MySQL extention, but we try to keep it
+ similar to ROW_COUNT member of the GET DIAGNOSTICS stack of the SQL
+ standard (see SQL99, part 2, search for ROW_COUNT). It's value is
+ implementation defined for anything except INSERT, DELETE, UPDATE.
+
+ ROW_COUNT is assigned according to the following rules:
+
+ - In my_ok():
+ - for DML statements: to the number of affected rows;
+ - for DDL statements: to 0.
+
+ - In my_eof(): to -1 to indicate that there was a result set.
+
+ We derive this semantics from the JDBC specification, where int
+ java.sql.Statement.getUpdateCount() is defined to (sic) "return the
+ current result as an update count; if the result is a ResultSet
+ object or there are no more results, -1 is returned".
+
+ - In my_error(): to -1 to be compatible with the MySQL C API and
+ MySQL ODBC driver.
+
+ - For SIGNAL statements: to 0 per WL#2110 specification (see also
+ sql_signal.cc comment). Zero is used since that's the "default"
+ value of ROW_COUNT in the diagnostics area.
+ */
+
+ longlong m_row_count_func; /* For the ROW_COUNT() function */
+
+public:
+ inline longlong get_row_count_func() const
+ {
+ return m_row_count_func;
+ }
+
+ inline void set_row_count_func(longlong row_count_func)
+ {
+ m_row_count_func= row_count_func;
+ }
+
ha_rows cuted_fields;
/*
@@ -1966,20 +2124,26 @@ public:
filesort() before reading it for e.g. update.
*/
ha_rows examined_row_count;
+ /**
+ The number of rows and/or keys examined by the query, both read,
+ changed or written.
+ */
+ ulonglong accessed_rows_and_keys;
+ /**
+ Check if the number of rows accessed by a statement exceeded
+ LIMIT ROWS EXAMINED. If so, signal the query engine to stop execution.
+ */
+ void check_limit_rows_examined()
+ {
+ if (++accessed_rows_and_keys > lex->limit_rows_examined_cnt)
+ killed= ABORT_QUERY;
+ }
USER_CONN *user_connect;
CHARSET_INFO *db_charset;
- /*
- FIXME: this, and some other variables like 'count_cuted_fields'
- maybe should be statement/cursor local, that is, moved to Statement
- class. With current implementation warnings produced in each prepared
- statement/cursor settle here.
- */
- List <MYSQL_ERROR> warn_list;
- uint warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END];
- uint total_warn_count;
- Diagnostics_area main_da;
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ Warning_info *warning_info;
+ Diagnostics_area *stmt_da;
+#if defined(ENABLED_PROFILING)
PROFILING profiling;
#endif
@@ -1991,49 +2155,68 @@ public:
from table are necessary for this select, to check if it's necessary to
update auto-updatable fields (like auto_increment and timestamp).
*/
- query_id_t query_id, warn_id;
+ query_id_t query_id;
ulong col_access;
-#ifdef ERROR_INJECT_SUPPORT
- ulong error_inject_value;
-#endif
/* Statement id is thread-wide. This counter is used to generate ids */
ulong statement_id_counter;
ulong rand_saved_seed1, rand_saved_seed2;
- /*
- Row counter, mainly for errors and warnings. Not increased in
- create_sort_index(); may differ from examined_row_count.
- */
- ulong row_count;
ulong query_plan_flags;
ulong query_plan_fsort_passes;
pthread_t real_id; /* For debugging */
my_thread_id thread_id;
- uint tmp_table, global_read_lock, global_disable_checkpoint;
+ uint tmp_table, global_disable_checkpoint;
uint server_status,open_options;
enum enum_thread_type system_thread;
uint select_number; //number of select (used for EXPLAIN)
- /* variables.transaction_isolation is reset to this after each commit */
- enum_tx_isolation session_tx_isolation;
+ /*
+ Current or next transaction isolation level.
+ When a connection is established, the value is taken from
+ @@session.tx_isolation (default transaction isolation for
+ the session), which is in turn taken from @@global.tx_isolation
+ (the global value).
+ If there is no transaction started, this variable
+ holds the value of the next transaction's isolation level.
+ When a transaction starts, the value stored in this variable
+ becomes "actual".
+ At transaction commit or rollback, we assign this variable
+ again from @@session.tx_isolation.
+ The only statement that can otherwise change the value
+ of this variable is SET TRANSACTION ISOLATION LEVEL.
+ Its purpose is to effect the isolation level of the next
+ transaction in this session. When this statement is executed,
+ the value in this variable is changed. However, since
+ this statement is only allowed when there is no active
+ transaction, this assignment (naturally) only affects the
+ upcoming transaction.
+ At the end of the current active transaction the value is
+ be reset again from @@session.tx_isolation, as described
+ above.
+ */
+ enum_tx_isolation tx_isolation;
enum_check_fields count_cuted_fields;
DYNAMIC_ARRAY user_var_events; /* For user variables replication */
MEM_ROOT *user_var_events_alloc; /* Allocate above array elements here */
+ /*
+ If checking this in conjunction with a wait condition, please
+ include a check after enter_cond() if you want to avoid a race
+ condition. For details see the implementation of awake(),
+ especially the "broadcast" part.
+ */
killed_state volatile killed;
/* scramble - random string sent to client on handshake */
char scramble[SCRAMBLE_LENGTH+1];
bool slave_thread, one_shot_set;
- /* tells if current statement should binlog row-based(1) or stmt-based(0) */
- bool current_stmt_binlog_row_based;
- bool some_tables_deleted;
- bool last_cuted_field;
- bool no_errors, password;
bool extra_port; /* If extra connection */
+ bool no_errors;
+ uint8 password;
uint8 failed_com_change_user;
+
/**
Set to TRUE if execution of the current compound statement
can not continue. In particular, disables activation of
@@ -2076,12 +2259,15 @@ public:
/** is set if some thread specific value(s) used in a statement. */
bool thread_specific_used;
+ /**
+ is set if a statement accesses a temporary table created through
+ CREATE TEMPORARY TABLE.
+ */
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 */
bool abort_on_warning;
bool got_warning; /* Set on call to push_warning() */
- bool no_warnings_for_error; /* no warnings on call to my_error() */
/* set during loop of derived table processing */
bool derived_tables_processing;
bool tablespace_op; /* This is TRUE in DISCARD/IMPORT TABLESPACE */
@@ -2158,15 +2344,30 @@ public:
*/
Parser_state *m_parser_state;
+ Locked_tables_list locked_tables_list;
+
#ifdef WITH_PARTITION_STORAGE_ENGINE
partition_info *work_part_info;
#endif
+#ifndef EMBEDDED_LIBRARY
+ /**
+ Array of active audit plugins which have been used by this THD.
+ This list is later iterated to invoke release_thd() on those
+ plugins.
+ */
+ DYNAMIC_ARRAY audit_class_plugins;
+ /**
+ Array of bits indicating which audit classes have already been
+ added to the list of audit plugins which are currently in use.
+ */
+ unsigned long audit_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
+#endif
+
#if defined(ENABLED_DEBUG_SYNC)
/* Debug Sync facility. See debug_sync.cc. */
struct st_debug_sync_control *debug_sync_control;
#endif /* defined(ENABLED_DEBUG_SYNC) */
-
THD();
~THD();
@@ -2188,47 +2389,41 @@ public:
void cleanup_after_query();
bool store_globals();
void reset_globals();
- bool restore_globals();
#ifdef SIGNAL_WITH_VIO_CLOSE
inline void set_active_vio(Vio* vio)
{
- pthread_mutex_lock(&LOCK_thd_data);
+ mysql_mutex_lock(&LOCK_thd_data);
active_vio = vio;
- pthread_mutex_unlock(&LOCK_thd_data);
+ vio_set_thread_id(vio, pthread_self());
+ mysql_mutex_unlock(&LOCK_thd_data);
}
inline void clear_active_vio()
{
- pthread_mutex_lock(&LOCK_thd_data);
+ mysql_mutex_lock(&LOCK_thd_data);
active_vio = 0;
- pthread_mutex_unlock(&LOCK_thd_data);
+ mysql_mutex_unlock(&LOCK_thd_data);
}
void close_active_vio();
#endif
void awake(killed_state state_to_set);
+ /** Disconnect the associated communication endpoint. */
+ void disconnect();
+
#ifndef MYSQL_CLIENT
enum enum_binlog_query_type {
- /*
- The query can be logged row-based or statement-based
- */
+ /* The query can be logged in row format or in statement format. */
ROW_QUERY_TYPE,
-
- /*
- The query has to be logged statement-based
- */
+
+ /* The query has to be logged in statement format. */
STMT_QUERY_TYPE,
-
- /*
- The query represents a change to a table in the "mysql"
- database and is currently mapped to ROW_QUERY_TYPE.
- */
- MYSQL_QUERY_TYPE,
+
QUERY_TYPE_COUNT
};
int binlog_query(enum_binlog_query_type qtype,
- char const *query, ulong query_len,
- bool is_trans, bool suppress_use,
+ char const *query, ulong query_len, bool is_trans,
+ bool direct, bool suppress_use,
int errcode);
#endif
@@ -2237,11 +2432,11 @@ public:
enter_cond(); this mutex is then released by exit_cond().
Usage must be: lock mutex; enter_cond(); your code; exit_cond().
*/
- inline const char* enter_cond(pthread_cond_t *cond, pthread_mutex_t* mutex,
- const char* msg)
+ inline const char* enter_cond(mysql_cond_t *cond, mysql_mutex_t* mutex,
+ const char* msg)
{
const char* old_msg = proc_info;
- safe_mutex_assert_owner(mutex);
+ mysql_mutex_assert_owner(mutex);
mysys_var->current_mutex = mutex;
mysys_var->current_cond = cond;
proc_info = msg;
@@ -2250,17 +2445,18 @@ public:
inline void exit_cond(const char* old_msg)
{
/*
- Putting the mutex unlock in exit_cond() ensures that
+ Putting the mutex unlock in thd->exit_cond() ensures that
mysys_var->current_mutex is always unlocked _before_ mysys_var->mutex is
locked (if that would not be the case, you'll get a deadlock if someone
does a THD::awake() on you).
*/
- pthread_mutex_unlock(mysys_var->current_mutex);
- pthread_mutex_lock(&mysys_var->mutex);
+ mysql_mutex_unlock(mysys_var->current_mutex);
+ mysql_mutex_lock(&mysys_var->mutex);
mysys_var->current_mutex = 0;
mysys_var->current_cond = 0;
proc_info = old_msg;
- pthread_mutex_unlock(&mysys_var->mutex);
+ mysql_mutex_unlock(&mysys_var->mutex);
+ return;
}
inline my_time_t query_start() { query_start_used=1; return start_time; }
inline ulong query_start_sec_part()
@@ -2271,7 +2467,7 @@ public:
start_time= hrtime_to_my_time(hrtime);
start_time_sec_part= hrtime_sec_part(hrtime);
}
- inline void set_time()
+ inline void set_start_time()
{
if (user_time.val)
{
@@ -2280,14 +2476,16 @@ public:
}
else
set_current_time();
+ }
+ inline void set_time()
+ {
+ set_start_time();
start_utime= utime_after_lock= microsecond_interval_timer();
}
inline void set_time(my_hrtime_t t)
{
user_time= t;
- start_time= hrtime_to_my_time(user_time);
- start_time_sec_part= hrtime_sec_part(user_time);
- start_utime= utime_after_lock= microsecond_interval_timer();
+ set_time();
}
inline void set_time(my_time_t t, ulong sec_part)
{
@@ -2297,17 +2495,86 @@ public:
void set_time_after_lock() { utime_after_lock= microsecond_interval_timer(); }
ulonglong current_utime() { return microsecond_interval_timer(); }
+ /**
+ Update server status after execution of a top level statement.
+ Currently only checks if a query was slow, and assigns
+ the status accordingly.
+ Evaluate the current time, and if it exceeds the long-query-time
+ setting, mark the query as slow.
+ */
+ void update_server_status()
+ {
+ utime_after_query= current_utime();
+ if (utime_after_query > utime_after_lock + variables.long_query_time)
+ server_status|= SERVER_QUERY_WAS_SLOW;
+ }
inline ulonglong found_rows(void)
{
return limit_found_rows;
}
- inline bool active_transaction()
+ /**
+ Returns TRUE if session is in a multi-statement transaction mode.
+
+ OPTION_NOT_AUTOCOMMIT: When autocommit is off, a multi-statement
+ transaction is implicitly started on the first statement after a
+ previous transaction has been ended.
+
+ OPTION_BEGIN: Regardless of the autocommit status, a multi-statement
+ transaction can be explicitly started with the statements "START
+ TRANSACTION", "BEGIN [WORK]", "[COMMIT | ROLLBACK] AND CHAIN", etc.
+
+ Note: this doesn't tell you whether a transaction is active.
+ A session can be in multi-statement transaction mode, and yet
+ have no active transaction, e.g., in case of:
+ set @@autocommit=0;
+ set @a= 3; <-- these statements don't
+ set transaction isolation level serializable; <-- start an active
+ flush tables; <-- transaction
+
+ I.e. for the above scenario this function returns TRUE, even
+ though no active transaction has begun.
+ @sa in_active_multi_stmt_transaction()
+ */
+ inline bool in_multi_stmt_transaction_mode()
+ {
+ return variables.option_bits & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN);
+ }
+ /**
+ TRUE if the session is in a multi-statement transaction mode
+ (@sa in_multi_stmt_transaction_mode()) *and* there is an
+ active transaction, i.e. there is an explicit start of a
+ transaction with BEGIN statement, or implicit with a
+ statement that uses a transactional engine.
+
+ For example, these scenarios don't start an active transaction
+ (even though the server is in multi-statement transaction mode):
+
+ set @@autocommit=0;
+ select * from nontrans_table;
+ set @var=TRUE;
+ flush tables;
+
+ Note, that even for a statement that starts a multi-statement
+ transaction (i.e. select * from trans_table), this
+ flag won't be set until we open the statement's tables
+ and the engines register themselves for the transaction
+ (see trans_register_ha()),
+ hence this method is reliable to use only after
+ open_tables() has completed.
+
+ Why do we need a flag?
+ ----------------------
+ We need to maintain a (at first glance redundant)
+ session flag, rather than looking at thd->transaction.all.ha_list
+ because of explicit start of a transaction with BEGIN.
+
+ I.e. in case of
+ BEGIN;
+ select * from nontrans_t1; <-- in_active_multi_stmt_transaction() is true
+ */
+ inline bool in_active_multi_stmt_transaction()
{
-#ifdef USING_TRANSACTIONS
return server_status & SERVER_STATUS_IN_TRANS;
-#else
- return 0;
-#endif
}
inline bool fill_derived_tables()
{
@@ -2336,25 +2603,40 @@ public:
void add_changed_table(const char *key, long key_length);
CHANGED_TABLE_LIST * changed_table_dup(const char *key, long key_length);
int send_explain_fields(select_result *result);
+
/**
Clear the current error, if any.
We do not clear is_fatal_error or is_fatal_sub_stmt_error since we
assume this is never called if the fatal error is set.
+
@todo: To silence an error, one should use Internal_error_handler
- mechanism. In future this function will be removed.
+ mechanism. Issuing an error that can be possibly later "cleared" is not
+ compatible with other installed error handlers and audit plugins.
+ In future this function will be removed.
*/
inline void clear_error()
{
DBUG_ENTER("clear_error");
- if (main_da.is_error())
- main_da.reset_diagnostics_area();
+ if (stmt_da->is_error())
+ stmt_da->reset_diagnostics_area();
is_slave_error= 0;
DBUG_VOID_RETURN;
}
#ifndef EMBEDDED_LIBRARY
inline bool vio_ok() const { return net.vio != 0; }
+ /** Return FALSE if connection to client is broken. */
+ bool is_connected()
+ {
+ /*
+ All system threads (e.g., the slave IO thread) are connected but
+ not using vio. So this function always returns true for all
+ system threads.
+ */
+ return system_thread || (vio_ok() ? vio_is_connected(net.vio) : FALSE);
+ }
#else
- inline bool vio_ok() const { return true; }
+ inline bool vio_ok() const { return TRUE; }
+ inline bool is_connected() { return TRUE; }
#endif
/**
Mark the current error as fatal. Warning: this does not
@@ -2363,6 +2645,7 @@ public:
*/
inline void fatal_error()
{
+ DBUG_ASSERT(stmt_da->is_error() || killed);
is_fatal_error= 1;
DBUG_PRINT("error",("Fatal error set"));
}
@@ -2379,7 +2662,7 @@ public:
To raise this flag, use my_error().
*/
- inline bool is_error() const { return main_da.is_error(); }
+ inline bool is_error() const { return stmt_da->is_error(); }
inline CHARSET_INFO *charset() { return variables.character_set_client; }
void update_charset();
@@ -2439,6 +2722,19 @@ public:
{
return ::killed_errno(killed);
}
+ 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_data);
+ killed= NOT_KILLED;
+ mysql_mutex_unlock(&LOCK_thd_data);
+ }
+ }
inline void send_kill_message() const
{
int err= killed_errno();
@@ -2453,38 +2749,58 @@ public:
(variables.sql_mode & MODE_STRICT_ALL_TABLES)));
}
void set_status_var_init();
- void reset_n_backup_open_tables_state(Open_tables_state *backup);
- void restore_backup_open_tables_state(Open_tables_state *backup);
+ void reset_n_backup_open_tables_state(Open_tables_backup *backup);
+ 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 set_n_backup_active_arena(Query_arena *set, Query_arena *backup);
void restore_active_arena(Query_arena *set, Query_arena *backup);
- inline void set_current_stmt_binlog_row_based_if_mixed()
+ /*
+ @todo Make these methods private or remove them completely. Only
+ decide_logging_format should call them. /Sven
+ */
+ inline void set_current_stmt_binlog_format_row_if_mixed()
{
+ DBUG_ENTER("set_current_stmt_binlog_format_row_if_mixed");
+ /*
+ This should only be called from decide_logging_format.
+
+ @todo Once we have ensured this, uncomment the following
+ statement, remove the big comment below that, and remove the
+ in_sub_stmt==0 condition from the following 'if'.
+ */
+ /* DBUG_ASSERT(in_sub_stmt == 0); */
/*
If in a stored/function trigger, the caller should already have done the
change. We test in_sub_stmt to prevent introducing bugs where people
wouldn't ensure that, and would switch to row-based mode in the middle
of executing a stored function/trigger (which is too late, see also
- reset_current_stmt_binlog_row_based()); this condition will make their
+ reset_current_stmt_binlog_format_row()); this condition will make their
tests fail and so force them to propagate the
lex->binlog_row_based_if_mixed upwards to the caller.
*/
if ((variables.binlog_format == BINLOG_FORMAT_MIXED) &&
(in_sub_stmt == 0))
- current_stmt_binlog_row_based= TRUE;
+ set_current_stmt_binlog_format_row();
+
+ DBUG_VOID_RETURN;
}
- inline void set_current_stmt_binlog_row_based()
+ inline void set_current_stmt_binlog_format_row()
{
- current_stmt_binlog_row_based= TRUE;
+ DBUG_ENTER("set_current_stmt_binlog_format_row");
+ current_stmt_binlog_format= BINLOG_FORMAT_ROW;
+ DBUG_VOID_RETURN;
}
- inline void clear_current_stmt_binlog_row_based()
+ inline void clear_current_stmt_binlog_format_row()
{
- current_stmt_binlog_row_based= FALSE;
+ DBUG_ENTER("clear_current_stmt_binlog_format_row");
+ current_stmt_binlog_format= BINLOG_FORMAT_STMT;
+ DBUG_VOID_RETURN;
}
- inline void reset_current_stmt_binlog_row_based()
+ inline void reset_current_stmt_binlog_format_row()
{
+ DBUG_ENTER("reset_current_stmt_binlog_format_row");
/*
If there are temporary tables, don't reset back to
statement-based. Indeed it could be that:
@@ -2499,19 +2815,19 @@ public:
or trigger is decided when it starts executing, depending for example on
the caller (for a stored function: if caller is SELECT or
INSERT/UPDATE/DELETE...).
-
- Don't reset binlog format for NDB binlog injector thread.
*/
DBUG_PRINT("debug",
("temporary_tables: %s, in_sub_stmt: %s, system_thread: %s",
YESNO(temporary_tables), YESNO(in_sub_stmt),
show_system_thread(system_thread)));
- if ((temporary_tables == NULL) && (in_sub_stmt == 0) &&
- (system_thread != SYSTEM_THREAD_NDBCLUSTER_BINLOG))
+ if (in_sub_stmt == 0)
{
- current_stmt_binlog_row_based=
- test(variables.binlog_format == BINLOG_FORMAT_ROW);
+ if (variables.binlog_format == BINLOG_FORMAT_ROW)
+ set_current_stmt_binlog_format_row();
+ else if (temporary_tables == NULL)
+ clear_current_stmt_binlog_format_row();
}
+ DBUG_VOID_RETURN;
}
/**
@@ -2539,20 +2855,23 @@ public:
{
/*
Acquiring mutex LOCK_thd_data as we either free the memory allocated
- for the database and reallocate the memory for the new db or memcpy
+ for the database and reallocating the memory for the new db or memcpy
the new_db to the db.
*/
- pthread_mutex_lock(&LOCK_thd_data);
+ 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
{
- x_free(db);
- db= new_db ? my_strndup(new_db, new_db_len, MYF(MY_WME)) : NULL;
+ 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;
- pthread_mutex_unlock(&LOCK_thd_data);
+ mysql_mutex_unlock(&LOCK_thd_data);
return new_db && !db;
}
@@ -2569,8 +2888,13 @@ public:
*/
void reset_db(char *new_db, size_t new_db_len)
{
- db= new_db;
- db_length= 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);
+ }
}
/*
Copy the current database to the argument. Use the current arena to
@@ -2601,27 +2925,140 @@ public:
void push_internal_handler(Internal_error_handler *handler);
/**
- Handle an error condition.
- @param sql_errno the error number
- @param level the error level
- @return true if the error is handled
+ Handle a sql condition.
+ @param sql_errno the condition error number
+ @param sqlstate the condition sqlstate
+ @param level the condition level
+ @param msg the condition message text
+ @param[out] cond_hdl the sql condition raised, if any
+ @return true if the condition is handled
*/
- virtual bool handle_error(uint sql_errno, const char *message,
- MYSQL_ERROR::enum_warning_level level);
+ virtual bool handle_condition(uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl);
/**
Remove the error handler last pushed.
*/
Internal_error_handler *pop_internal_handler();
+ /**
+ Raise an exception condition.
+ @param code the MYSQL_ERRNO error code of the error
+ */
+ void raise_error(uint code);
+
+ /**
+ Raise an exception condition, with a formatted message.
+ @param code the MYSQL_ERRNO error code of the error
+ */
+ void raise_error_printf(uint code, ...);
+
+ /**
+ Raise a completion condition (warning).
+ @param code the MYSQL_ERRNO error code of the warning
+ */
+ void raise_warning(uint code);
+
+ /**
+ Raise a completion condition (warning), with a formatted message.
+ @param code the MYSQL_ERRNO error code of the warning
+ */
+ void raise_warning_printf(uint code, ...);
+
+ /**
+ Raise a completion condition (note), with a fixed message.
+ @param code the MYSQL_ERRNO error code of the note
+ */
+ void raise_note(uint code);
+
+ /**
+ Raise an completion condition (note), with a formatted message.
+ @param code the MYSQL_ERRNO error code of the note
+ */
+ void raise_note_printf(uint code, ...);
+
+private:
+ /*
+ Only the implementation of the SIGNAL and RESIGNAL statements
+ is permitted to raise SQL conditions in a generic way,
+ or to raise them by bypassing handlers (RESIGNAL).
+ To raise a SQL condition, the code should use the public
+ raise_error() or raise_warning() methods provided by class THD.
+ */
+ friend class Signal_common;
+ friend class Signal_statement;
+ friend class Resignal_statement;
+ friend void push_warning(THD*, MYSQL_ERROR::enum_warning_level, uint, const char*);
+ friend void my_message_sql(uint, const char *, myf);
+
+ /**
+ Raise a generic SQL 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
+ */
+ MYSQL_ERROR*
+ raise_condition(uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg);
+
+public:
/** Overloaded to guard query/query_length fields */
virtual void set_statement(Statement *stmt);
/**
- Assign a new value to thd->query.
+ 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, uint32 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*/
+ {
+ set_query(CSET_STRING(query_arg, query_length_arg, charset()));
+ }
+ void set_query(const CSET_STRING &str); /* Mutex protected */
+ void reset_query() /* Mutex protected */
+ { set_query(CSET_STRING()); }
+ void set_query_and_id(char *query_arg, uint32 query_length_arg,
+ CHARSET_INFO *cs, query_id_t new_query_id);
+ void set_query_id(query_id_t new_query_id);
+ void set_open_tables(TABLE *open_tables_arg)
+ {
+ mysql_mutex_lock(&LOCK_thd_data);
+ open_tables= open_tables_arg;
+ mysql_mutex_unlock(&LOCK_thd_data);
+ }
+ void set_mysys_var(struct st_my_thread_var *new_mysys_var);
+ void enter_locked_tables_mode(enum_locked_tables_mode mode_arg)
+ {
+ DBUG_ASSERT(locked_tables_mode == LTM_NONE);
+
+ if (mode_arg == LTM_LOCK_TABLES)
+ {
+ /*
+ When entering LOCK TABLES mode we should set explicit duration
+ for all metadata locks acquired so far in order to avoid releasing
+ them till UNLOCK TABLES statement.
+ We don't do this when entering prelocked mode since sub-statements
+ don't release metadata locks and restoring status-quo after leaving
+ prelocking mode gets complicated.
+ */
+ mdl_context.set_explicit_duration_for_all_locks();
+ }
+
+ locked_tables_mode= mode_arg;
+ }
+ void leave_locked_tables_mode();
+ int decide_logging_format(TABLE_LIST *tables);
void binlog_invoker() { m_binlog_invoker= TRUE; }
bool need_binlog_invoker() { return m_binlog_invoker; }
void get_definer(LEX_USER *definer);
@@ -2634,6 +3071,18 @@ public:
LEX_STRING get_invoker_host() { return invoker_host; }
bool has_invoker() { return invoker_user.length > 0; }
+ void print_aborted_warning(uint threshold, const char *reason)
+ {
+ if (global_system_variables.log_warnings > threshold)
+ {
+ Security_context *sctx= &main_security_ctx;
+ sql_print_warning(ER_THD(this, ER_NEW_ABORTING_CONNECTION),
+ thread_id, (db ? db : "unconnected"),
+ sctx->user ? sctx->user : "unauthenticated",
+ sctx->host_or_ip, reason);
+ }
+ }
+
private:
/*
This reference points to the table arena when the expression
@@ -2662,9 +3111,19 @@ public:
void wait_for_wakeup_ready();
/* Wake this thread up from wait_for_wakeup_ready(). */
void signal_wakeup_ready();
+
+ void add_status_to_global()
+ {
+ mysql_mutex_lock(&LOCK_status);
+ add_to_status(&global_status_var, &status_var);
+ mysql_mutex_unlock(&LOCK_status);
+ }
+
private:
+
/** The current internal error handler for this thread, or NULL. */
Internal_error_handler *m_internal_handler;
+
/**
The lex to hold the parsed tree of conventional (non-prepared) queries.
Whereas for prepared and stored procedure statements we use an own lex
@@ -2681,6 +3140,8 @@ private:
tree itself is reused between executions and thus is stored elsewhere.
*/
MEM_ROOT main_mem_root;
+ Warning_info main_warning_info;
+ Diagnostics_area main_da;
/**
It will be set TURE if CURRENT_USER() is called in account management
@@ -2708,33 +3169,36 @@ private:
other purposes.
*/
bool wakeup_ready;
- pthread_mutex_t LOCK_wakeup_ready;
- pthread_cond_t COND_wakeup_ready;
+ mysql_mutex_t LOCK_wakeup_ready;
+ mysql_cond_t COND_wakeup_ready;
};
-/** A short cut for thd->main_da.set_ok_status(). */
+
+/** A short cut for thd->stmt_da->set_ok_status(). */
inline void
-my_ok(THD *thd, ha_rows affected_rows= 0, ulonglong id= 0,
+my_ok(THD *thd, ulonglong affected_rows= 0, ulonglong id= 0,
const char *message= NULL)
{
- thd->main_da.set_ok_status(thd, affected_rows, id, message);
+ thd->set_row_count_func(affected_rows);
+ thd->stmt_da->set_ok_status(thd, affected_rows, id, message);
}
-/** A short cut for thd->main_da.set_eof_status(). */
+/** A short cut for thd->stmt_da->set_eof_status(). */
inline void
my_eof(THD *thd)
{
- thd->main_da.set_eof_status(thd);
+ thd->set_row_count_func(-1);
+ thd->stmt_da->set_eof_status(thd);
}
#define tmp_disable_binlog(A) \
- {ulonglong tmp_disable_binlog__save_options= (A)->options; \
- (A)->options&= ~OPTION_BIN_LOG
+ {ulonglong tmp_disable_binlog__save_options= (A)->variables.option_bits; \
+ (A)->variables.option_bits&= ~OPTION_BIN_LOG
-#define reenable_binlog(A) (A)->options= tmp_disable_binlog__save_options;}
+#define reenable_binlog(A) (A)->variables.option_bits= tmp_disable_binlog__save_options;}
/*
@@ -2742,11 +3206,11 @@ my_eof(THD *thd)
checking for all date handling.
*/
-extern my_bool strict_date_checking;
+const my_bool strict_date_checking= 0;
inline ulong sql_mode_for_dates(THD *thd)
{
- if (unlikely(strict_date_checking))
+ if (strict_date_checking)
return (thd->variables.sql_mode &
(MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE |
MODE_INVALID_DATES));
@@ -2767,18 +3231,18 @@ inline ulong sql_mode_for_dates()
class sql_exchange :public Sql_alloc
{
public:
+ enum enum_filetype filetype; /* load XML, Added by Arnold & Erik */
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(char *name, bool dumpfile_flag,
+ enum_filetype filetype_arg= FILETYPE_CSV);
bool escaped_given(void);
};
-#include "log_event.h"
-
/*
This is used to get result from a select
*/
@@ -2789,7 +3253,6 @@ class select_result :public Sql_alloc {
protected:
THD *thd;
SELECT_LEX_UNIT *unit;
- int nest_level;
public:
select_result();
virtual ~select_result() {};
@@ -2806,7 +3269,7 @@ public:
*/
virtual uint field_count(List<Item> &fields) const
{ return fields.elements; }
- virtual bool send_fields(List<Item> &list, uint flags)=0;
+ virtual bool send_result_set_metadata(List<Item> &list, uint flags)=0;
/*
send_data returns 0 on ok, 1 on error and -1 if data was ignored, for
example for a duplicate row entry written to a temp table.
@@ -2823,19 +3286,13 @@ public:
@retval TRUE error, an error message is set
*/
virtual bool check_simple_select() const;
- virtual void abort() {}
+ virtual void abort_result_set() {}
/*
Cleanup instance of this class for next execution of a prepared
statement/stored procedure.
*/
virtual void cleanup();
void set_thd(THD *thd_arg) { thd= thd_arg; }
- /**
- The nest level, if supported.
- @return
- -1 if nest level is undefined, otherwise a positive integer.
- */
- int get_nest_level() { return (int) nest_level; }
#ifdef EMBEDDED_LIBRARY
virtual void begin_dataset() {}
#else
@@ -2861,7 +3318,7 @@ public:
DBUG_VOID_RETURN;
} /* Remove gcc warning */
uint field_count(List<Item> &fields) const { return 0; }
- bool send_fields(List<Item> &fields, uint flag) { return FALSE; }
+ bool send_result_set_metadata(List<Item> &fields, uint flag) { return FALSE; }
};
@@ -2874,11 +3331,11 @@ class select_send :public select_result {
bool is_result_set_started;
public:
select_send() :is_result_set_started(FALSE) {}
- bool send_fields(List<Item> &list, uint flags);
+ bool send_result_set_metadata(List<Item> &list, uint flags);
int send_data(List<Item> &items);
bool send_eof();
virtual bool check_simple_select() const { return FALSE; }
- void abort();
+ void abort_result_set();
virtual void cleanup();
};
@@ -2936,14 +3393,6 @@ class select_export :public select_to_file {
CHARSET_INFO *write_cs; // output charset
public:
select_export(sql_exchange *ex) :select_to_file(ex) {}
- /**
- Creates a select_export to represent INTO OUTFILE <filename> with a
- defined level of subquery nesting.
- */
- select_export(sql_exchange *ex, int nest_level_arg) :select_to_file(ex)
- {
- nest_level= nest_level_arg;
- }
~select_export();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
int send_data(List<Item> &items);
@@ -2953,24 +3402,13 @@ public:
class select_dump :public select_to_file {
public:
select_dump(sql_exchange *ex) :select_to_file(ex) {}
- /**
- Creates a select_export to represent INTO DUMPFILE <filename> with a
- defined level of subquery nesting.
- */
- select_dump(sql_exchange *ex, int nest_level_arg) :
- select_to_file(ex)
- {
- nest_level= nest_level_arg;
- }
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
int send_data(List<Item> &items);
};
class select_insert :public select_result_interceptor {
-protected:
- virtual int write_to_binlog(bool is_trans, int errcode);
-public:
+ public:
TABLE_LIST *table_list;
TABLE *table;
List<Item> *fields;
@@ -2989,7 +3427,7 @@ public:
virtual bool can_rollback_data() { return 0; }
void send_error(uint errcode,const char *err);
bool send_eof();
- void abort();
+ virtual void abort_result_set();
/* not implemented: select_insert is never re-used in prepared statements */
void cleanup();
};
@@ -3006,8 +3444,6 @@ class select_create: public select_insert {
MYSQL_LOCK *m_lock;
/* m_lock or thd->extra_lock */
MYSQL_LOCK **m_plock;
-
- virtual int write_to_binlog(bool is_trans, int errcode);
public:
select_create (TABLE_LIST *table_arg,
HA_CREATE_INFO *create_info_par,
@@ -3023,11 +3459,11 @@ public:
{}
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
- int binlog_show_create_table(TABLE **tables, uint count, int errcode);
+ int binlog_show_create_table(TABLE **tables, uint count);
void store_values(List<Item> &values);
void send_error(uint errcode,const char *err);
bool send_eof();
- void abort();
+ virtual void abort_result_set();
virtual bool can_rollback_data() { return 1; }
// Needed for access from local class MY_HOOKS in prepare(), since thd is proteted.
@@ -3036,12 +3472,15 @@ public:
int prepare2(void) { return 0; }
};
+#include <myisam.h>
-#if defined(WITH_ARIA_STORAGE_ENGINE) && defined(USE_MARIA_FOR_TMP_TABLES)
+#ifdef WITH_ARIA_STORAGE_ENGINE
#include <maria.h>
+#endif
+
+#ifdef USE_ARIA_FOR_TMP_TABLES
#define ENGINE_COLUMNDEF MARIA_COLUMNDEF
#else
-#include <myisam.h>
#define ENGINE_COLUMNDEF MI_COLUMNDEF
#endif
@@ -3168,7 +3607,8 @@ public:
bool is_distinct, ulonglong options,
const char *alias,
bool bit_fields_as_long,
- bool create_table);
+ bool create_table,
+ bool keep_row_order= FALSE);
TMP_TABLE_PARAM *get_tmp_table_param() { return &tmp_table_param; }
};
@@ -3238,7 +3678,8 @@ public:
bool is_distinct, ulonglong options,
const char *alias,
bool bit_fields_as_long,
- bool create_table);
+ bool create_table,
+ bool keep_row_order= FALSE);
bool init_result_table(ulonglong select_options);
int send_data(List<Item> &items);
void cleanup();
@@ -3490,8 +3931,6 @@ public:
static double get_use_cost(uint *buffer, size_t nkeys, uint key_size,
ulonglong max_in_memory_size, uint compare_factor,
bool intersect_fl, bool *in_memory);
-
- // Returns the number of bytes needed in imerge_cost_buf.
inline static int get_cost_calc_buff_size(size_t nkeys, uint key_size,
ulonglong max_in_memory_size)
{
@@ -3545,7 +3984,11 @@ public:
int do_deletes();
int do_table_deletes(TABLE *table, bool ignore);
bool send_eof();
- virtual void abort();
+ inline ha_rows num_deleted()
+ {
+ return deleted;
+ }
+ virtual void abort_result_set();
};
@@ -3588,7 +4031,15 @@ public:
void send_error(uint errcode,const char *err);
int do_updates();
bool send_eof();
- virtual void abort();
+ inline ha_rows num_found()
+ {
+ return found;
+ }
+ inline ha_rows num_updated()
+ {
+ return updated;
+ }
+ virtual void abort_result_set();
void update_used_tables();
};
@@ -3616,16 +4067,6 @@ class select_dumpvar :public select_result_interceptor {
public:
List<my_var> var_list;
select_dumpvar() { var_list.empty(); row_count= 0;}
- /**
- Creates a select_dumpvar to represent INTO <variable> with a defined
- level of subquery nesting.
- */
- select_dumpvar(int nest_level_arg)
- {
- var_list.empty();
- row_count= 0;
- nest_level= nest_level_arg;
- }
~select_dumpvar() {}
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
int send_data(List<Item> &items);
@@ -3636,11 +4077,11 @@ public:
/* Bits in sql_command_flags */
-#define CF_CHANGES_DATA 1
-#define CF_HAS_ROW_COUNT 2
-#define CF_STATUS_COMMAND 4
-#define CF_SHOW_TABLE_COMMAND 8
-#define CF_WRITE_LOGS_COMMAND 16
+#define CF_CHANGES_DATA (1U << 0)
+#define CF_REPORT_PROGRESS (1U << 1)
+#define CF_STATUS_COMMAND (1U << 2)
+#define CF_SHOW_TABLE_COMMAND (1U << 3)
+#define CF_WRITE_LOGS_COMMAND (1U << 4)
/**
Must be set for SQL statements that may contain
@@ -3655,23 +4096,105 @@ public:
reprepare. Consequently, complex item expressions and
joins are currently prohibited in these statements.
*/
-#define CF_REEXECUTION_FRAGILE 32
-#define CF_REPORT_PROGRESS 64
+#define CF_REEXECUTION_FRAGILE (1U << 5)
+/**
+ Implicitly commit before the SQL statement is executed.
-/* Functions in sql_class.cc */
+ Statements marked with this flag will cause any active
+ transaction to end (commit) before proceeding with the
+ command execution.
-void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var);
+ This flag should be set for statements that probably can't
+ be rolled back or that do not expect any previously metadata
+ locked tables.
+*/
+#define CF_IMPLICT_COMMIT_BEGIN (1U << 6)
+/**
+ Implicitly commit after the SQL statement.
-void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
- STATUS_VAR *dec_var);
-void mark_transaction_to_rollback(THD *thd, bool all);
+ Statements marked with this flag are automatically committed
+ at the end of the statement.
-/*
- inline handler methods that need to know TABLE and THD structures
+ This flag should be set for statements that will implicitly
+ open and take metadata locks on system tables that should not
+ be carried for the whole duration of a active transaction.
+*/
+#define CF_IMPLICIT_COMMIT_END (1U << 7)
+/**
+ CF_IMPLICT_COMMIT_BEGIN and CF_IMPLICIT_COMMIT_END are used
+ to ensure that the active transaction is implicitly committed
+ before and after every DDL statement and any statement that
+ modifies our currently non-transactional system tables.
+*/
+#define CF_AUTO_COMMIT_TRANS (CF_IMPLICT_COMMIT_BEGIN | CF_IMPLICIT_COMMIT_END)
+
+/**
+ Diagnostic statement.
+ Diagnostic statements:
+ - SHOW WARNING
+ - SHOW ERROR
+ - GET DIAGNOSTICS (WL#2111)
+ do not modify the diagnostics area during execution.
*/
+#define CF_DIAGNOSTIC_STMT (1U << 8)
+
+/**
+ Identifies statements that may generate row events
+ and that may end up in the binary log.
+*/
+#define CF_CAN_GENERATE_ROW_EVENTS (1U << 9)
+
+/* Bits in server_command_flags */
+
+/**
+ 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).
+*/
+#define CF_SKIP_QUERY_ID (1U << 0)
+
+/**
+ Skip the increase of the number of statements that clients have
+ sent to the server. Commonly used for commands that will cause
+ a statement to be executed but the statement might have not been
+ sent by the user (ie: stored procedure).
+*/
+#define CF_SKIP_QUESTIONS (1U << 1)
+
+void mark_transaction_to_rollback(THD *thd, bool all);
+
+/* Inline functions */
+
+inline bool add_item_to_list(THD *thd, Item *item)
+{
+ return thd->lex->current_select->add_item_to_list(thd, item);
+}
+
+inline bool add_value_to_list(THD *thd, Item *value)
+{
+ return thd->lex->value_list.push_back(value);
+}
+
+inline bool add_order_to_list(THD *thd, Item *item, bool asc)
+{
+ return thd->lex->current_select->add_order_to_list(thd, item, asc);
+}
+
+inline bool add_gorder_to_list(THD *thd, Item *item, bool asc)
+{
+ return thd->lex->current_select->add_gorder_to_list(thd, item, asc);
+}
+
+inline bool add_group_to_list(THD *thd, Item *item, bool asc)
+{
+ return thd->lex->current_select->add_group_to_list(thd, item, asc);
+}
+
+/* inline handler methods that need to know TABLE and THD structures */
inline void handler::increment_statistics(ulong SSV::*offset) const
{
status_var_increment(table->in_use->status_var.*offset);
+ table->in_use->check_limit_rows_examined();
}
inline void handler::decrement_statistics(ulong SSV::*offset) const
@@ -3684,11 +4207,13 @@ inline int handler::ha_index_read_map(uchar * buf, const uchar * key,
enum ha_rkey_function find_flag)
{
DBUG_ASSERT(inited==INDEX);
+ MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
increment_statistics(&SSV::ha_read_key_count);
int error= index_read_map(buf, key, keypart_map, find_flag);
if (!error)
update_index_statistics();
table->status=error ? STATUS_NOT_FOUND: 0;
+ MYSQL_INDEX_READ_ROW_DONE(error);
return error;
}
@@ -3705,6 +4230,7 @@ inline int handler::ha_index_read_idx_map(uchar * buf, uint index,
enum ha_rkey_function find_flag)
{
DBUG_ASSERT(inited==NONE);
+ MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
increment_statistics(&SSV::ha_read_key_count);
int error= index_read_idx_map(buf, index, key, keypart_map, find_flag);
if (!error)
@@ -3713,50 +4239,59 @@ inline int handler::ha_index_read_idx_map(uchar * buf, uint index,
index_rows_read[index]++;
}
table->status=error ? STATUS_NOT_FOUND: 0;
+ MYSQL_INDEX_READ_ROW_DONE(error);
return error;
}
inline int handler::ha_index_next(uchar * buf)
{
DBUG_ASSERT(inited==INDEX);
+ MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
increment_statistics(&SSV::ha_read_next_count);
int error= index_next(buf);
if (!error)
update_index_statistics();
table->status=error ? STATUS_NOT_FOUND: 0;
+ MYSQL_INDEX_READ_ROW_DONE(error);
return error;
}
inline int handler::ha_index_prev(uchar * buf)
{
DBUG_ASSERT(inited==INDEX);
+ MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
increment_statistics(&SSV::ha_read_prev_count);
int error= index_prev(buf);
if (!error)
update_index_statistics();
table->status=error ? STATUS_NOT_FOUND: 0;
+ MYSQL_INDEX_READ_ROW_DONE(error);
return error;
}
inline int handler::ha_index_first(uchar * buf)
{
DBUG_ASSERT(inited==INDEX);
+ MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
increment_statistics(&SSV::ha_read_first_count);
int error= index_first(buf);
if (!error)
update_index_statistics();
table->status=error ? STATUS_NOT_FOUND: 0;
+ MYSQL_INDEX_READ_ROW_DONE(error);
return error;
}
inline int handler::ha_index_last(uchar * buf)
{
DBUG_ASSERT(inited==INDEX);
+ MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
increment_statistics(&SSV::ha_read_last_count);
int error= index_last(buf);
if (!error)
update_index_statistics();
table->status=error ? STATUS_NOT_FOUND: 0;
+ MYSQL_INDEX_READ_ROW_DONE(error);
return error;
}
@@ -3764,11 +4299,13 @@ inline int handler::ha_index_next_same(uchar *buf, const uchar *key,
uint keylen)
{
DBUG_ASSERT(inited==INDEX);
+ MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
increment_statistics(&SSV::ha_read_next_count);
int error= index_next_same(buf, key, keylen);
if (!error)
update_index_statistics();
table->status=error ? STATUS_NOT_FOUND: 0;
+ MYSQL_INDEX_READ_ROW_DONE(error);
return error;
}
@@ -3784,6 +4321,7 @@ inline int handler::ha_ft_read(uchar *buf)
inline int handler::ha_rnd_next(uchar *buf)
{
+ MYSQL_READ_ROW_START(table_share->db.str, table_share->table_name.str, TRUE);
int error= rnd_next(buf);
if (!error)
{
@@ -3796,16 +4334,19 @@ inline int handler::ha_rnd_next(uchar *buf)
increment_statistics(&SSV::ha_read_rnd_next_count);
table->status=error ? STATUS_NOT_FOUND: 0;
+ MYSQL_READ_ROW_DONE(error);
return error;
}
inline int handler::ha_rnd_pos(uchar *buf, uchar *pos)
{
+ MYSQL_READ_ROW_START(table_share->db.str, table_share->table_name.str, FALSE);
increment_statistics(&SSV::ha_read_rnd_count);
int error= rnd_pos(buf, pos);
if (!error)
update_rows_read();
table->status=error ? STATUS_NOT_FOUND: 0;
+ MYSQL_READ_ROW_DONE(error);
return error;
}
@@ -3829,14 +4370,24 @@ inline int handler::ha_read_first_row(uchar *buf, uint primary_key)
inline int handler::ha_write_tmp_row(uchar *buf)
{
+ MYSQL_INSERT_ROW_START(table_share->db.str, table_share->table_name.str);
increment_statistics(&SSV::ha_tmp_write_count);
- return write_row(buf);
+ int error= write_row(buf);
+ MYSQL_INSERT_ROW_DONE(error);
+ return error;
}
inline int handler::ha_update_tmp_row(const uchar *old_data, uchar *new_data)
{
+ MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str);
increment_statistics(&SSV::ha_tmp_update_count);
- return update_row(old_data, new_data);
+ int error= update_row(old_data, new_data);
+ MYSQL_UPDATE_ROW_DONE(error);
+ return error;
}
+extern pthread_attr_t *get_connection_attrib(void);
+
#endif /* MYSQL_SERVER */
+
+#endif /* SQL_CLASS_INCLUDED */
diff --git a/sql/sql_client.cc b/sql/sql_client.cc
index 0dd190edf0a..eb6c039c065 100644
--- a/sql/sql_client.cc
+++ b/sql/sql_client.cc
@@ -1,4 +1,5 @@
-/* Copyright (c) 2003, 2006, 2007 MySQL AB
+/* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2012, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,13 +12,14 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
This files defines some MySQL C API functions that are server specific
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "sql_class.h" // system_variables
/*
Function called by my_net_init() to set some check variables
@@ -28,7 +30,7 @@ void my_net_local_init(NET *net)
{
#ifndef EMBEDDED_LIBRARY
net->max_packet= (uint) global_system_variables.net_buffer_length;
-
+ net->read_timeout= net->write_timeout= 0;
my_net_set_read_timeout(net, (uint)global_system_variables.net_read_timeout);
my_net_set_write_timeout(net,
(uint)global_system_variables.net_write_timeout);
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index 89df1835704..041d2a545df 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -1,6 +1,6 @@
/*
- Copyright (c) 2007, 2012, Oracle and/or its affiliates.
- Copyright (c) 2008, 2011, Monty Program Ab
+ Copyright (c) 2007, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,18 +20,30 @@
Functions to autenticate and handle reqests for a connection
*/
-#include "mysql_priv.h"
+#include "my_global.h"
+#include "sql_priv.h"
+#ifndef __WIN__
+#include <netdb.h> // getservbyname, servent
+#endif
+#include "sql_audit.h"
+#include "sql_connect.h"
+#include "probes_mysql.h"
+#include "unireg.h" // REQUIRED: for other includes
+#include "sql_parse.h" // sql_command_flags,
+ // execute_init_command,
+ // do_command
+#include "sql_db.h" // mysql_change_db
+#include "hostname.h" // inc_host_errors, ip_to_hostname,
+ // reset_host_errors
+#include "sql_acl.h" // acl_getroot, NO_ACCESS, SUPER_ACL
+#include "sql_callback.h"
HASH global_user_stats, global_client_stats, global_table_stats;
HASH global_index_stats;
/* Protects the above global stats */
-extern pthread_mutex_t LOCK_global_user_client_stats;
-extern pthread_mutex_t LOCK_global_table_stats;
-extern pthread_mutex_t LOCK_global_index_stats;
-
-#ifdef __WIN__
-extern void win_install_sigabrt_handler();
-#endif
+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;
/*
Get structure for logging connection data for the current user
@@ -42,7 +54,7 @@ static HASH hash_user_connections;
int get_or_create_user_conn(THD *thd, const char *user,
const char *host,
- USER_RESOURCES *mqh)
+ const USER_RESOURCES *mqh)
{
int return_val= 0;
size_t temp_len, user_len;
@@ -55,8 +67,8 @@ int get_or_create_user_conn(THD *thd, const char *user,
user_len= strlen(user);
temp_len= (strmov(strmov(temp_user, user)+1, host) - temp_user)+1;
- (void) pthread_mutex_lock(&LOCK_user_conn);
- if (!(uc = (struct user_conn *) hash_search(&hash_user_connections,
+ mysql_mutex_lock(&LOCK_user_conn);
+ if (!(uc = (struct user_conn *) my_hash_search(&hash_user_connections,
(uchar*) temp_user, temp_len)))
{
/* First connection for user; Create a user connection object */
@@ -78,7 +90,7 @@ int get_or_create_user_conn(THD *thd, const char *user,
if (my_hash_insert(&hash_user_connections, (uchar*) uc))
{
/* The only possible error is out of memory, MY_WME sets an error. */
- my_free((char*) uc,0);
+ my_free(uc);
return_val= 1;
goto end;
}
@@ -86,7 +98,7 @@ int get_or_create_user_conn(THD *thd, const char *user,
thd->user_connect=uc;
uc->connections++;
end:
- (void) pthread_mutex_unlock(&LOCK_user_conn);
+ mysql_mutex_unlock(&LOCK_user_conn);
return return_val;
}
@@ -114,11 +126,12 @@ int check_for_max_user_connections(THD *thd, USER_CONN *uc)
int error= 1;
DBUG_ENTER("check_for_max_user_connections");
- (void) pthread_mutex_lock(&LOCK_user_conn);
+ mysql_mutex_lock(&LOCK_user_conn);
/* Root is not affected by the value of max_user_connections */
- if (max_user_connections && !uc->user_resources.user_conn &&
- max_user_connections < uc->connections &&
+ if (global_system_variables.max_user_connections &&
+ !uc->user_resources.user_conn &&
+ global_system_variables.max_user_connections < uc->connections &&
!(thd->security_ctx->master_access & SUPER_ACL))
{
my_error(ER_TOO_MANY_USER_CONNECTIONS, MYF(0), uc->user);
@@ -146,8 +159,16 @@ int check_for_max_user_connections(THD *thd, USER_CONN *uc)
end:
if (error)
+ {
uc->connections--; // no need for decrease_user_connections() here
- (void) pthread_mutex_unlock(&LOCK_user_conn);
+ /*
+ The thread may returned back to the pool and assigned to a user
+ that doesn't have a limit. Ensure the user is not using resources
+ of someone else.
+ */
+ thd->user_connect= NULL;
+ }
+ mysql_mutex_unlock(&LOCK_user_conn);
DBUG_RETURN(error);
}
@@ -173,14 +194,14 @@ end:
void decrease_user_connections(USER_CONN *uc)
{
DBUG_ENTER("decrease_user_connections");
- (void) pthread_mutex_lock(&LOCK_user_conn);
+ mysql_mutex_lock(&LOCK_user_conn);
DBUG_ASSERT(uc->connections);
if (!--uc->connections && !mqh_used)
{
/* Last connection for user; Delete it */
- (void) hash_delete(&hash_user_connections,(uchar*) uc);
+ (void) my_hash_delete(&hash_user_connections,(uchar*) uc);
}
- (void) pthread_mutex_unlock(&LOCK_user_conn);
+ mysql_mutex_unlock(&LOCK_user_conn);
DBUG_VOID_RETURN;
}
@@ -228,7 +249,7 @@ bool check_mqh(THD *thd, uint check_command)
DBUG_ENTER("check_mqh");
DBUG_ASSERT(uc != 0);
- (void) pthread_mutex_lock(&LOCK_user_conn);
+ mysql_mutex_lock(&LOCK_user_conn);
time_out_user_resource_limits(thd, uc);
@@ -255,13 +276,12 @@ bool check_mqh(THD *thd, uint check_command)
}
}
end:
- (void) pthread_mutex_unlock(&LOCK_user_conn);
+ mysql_mutex_unlock(&LOCK_user_conn);
DBUG_RETURN(error);
}
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
-
/*
Check for maximum allowable user connections, if the mysqld server is
started with corresponding variable that is greater then 0.
@@ -277,17 +297,16 @@ extern "C" uchar *get_key_conn(user_conn *buff, size_t *length,
extern "C" void free_user(struct user_conn *uc)
{
- my_free((char*) uc,MYF(0));
+ my_free(uc);
}
void init_max_user_conn(void)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (hash_init(&hash_user_connections,system_charset_info,max_connections,
- 0,0,
- (hash_get_key) get_key_conn, (hash_free_key) free_user,
- 0))
+ if (my_hash_init(&hash_user_connections,system_charset_info,max_connections,
+ 0,0, (my_hash_get_key) get_key_conn,
+ (my_hash_free_key) free_user, 0))
{
sql_print_error("Initializing hash_user_connections failed.");
exit(1);
@@ -299,7 +318,7 @@ void init_max_user_conn(void)
void free_max_user_conn(void)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- hash_free(&hash_user_connections);
+ my_hash_free(&hash_user_connections);
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
}
@@ -307,7 +326,7 @@ void free_max_user_conn(void)
void reset_mqh(LEX_USER *lu, bool get_them= 0)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- (void) pthread_mutex_lock(&LOCK_user_conn);
+ mysql_mutex_lock(&LOCK_user_conn);
if (lu) // for GRANT
{
USER_CONN *uc;
@@ -317,8 +336,9 @@ void reset_mqh(LEX_USER *lu, bool get_them= 0)
memcpy(temp_user,lu->user.str,lu->user.length);
memcpy(temp_user+lu->user.length+1,lu->host.str,lu->host.length);
temp_user[lu->user.length]='\0'; temp_user[temp_len-1]=0;
- if ((uc = (struct user_conn *) hash_search(&hash_user_connections,
- (uchar*) temp_user, temp_len)))
+ if ((uc = (struct user_conn *) my_hash_search(&hash_user_connections,
+ (uchar*) temp_user,
+ temp_len)))
{
uc->questions=0;
get_mqh(temp_user,&temp_user[lu->user.length+1],uc);
@@ -331,8 +351,8 @@ void reset_mqh(LEX_USER *lu, bool get_them= 0)
/* for FLUSH PRIVILEGES and FLUSH USER_RESOURCES */
for (uint idx=0;idx < hash_user_connections.records; idx++)
{
- USER_CONN *uc=(struct user_conn *) hash_element(&hash_user_connections,
- idx);
+ USER_CONN *uc=(struct user_conn *)
+ my_hash_element(&hash_user_connections, idx);
if (get_them)
get_mqh(uc->user,uc->host,uc);
uc->questions=0;
@@ -340,7 +360,7 @@ void reset_mqh(LEX_USER *lu, bool get_them= 0)
uc->conn_per_hour=0;
}
}
- (void) pthread_mutex_unlock(&LOCK_user_conn);
+ mysql_mutex_unlock(&LOCK_user_conn);
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
}
@@ -378,7 +398,7 @@ extern "C" uchar *get_key_user_stats(USER_STATS *user_stats, size_t *length,
void free_user_stats(USER_STATS* user_stats)
{
- my_free(user_stats, MYF(0));
+ my_free(user_stats);
}
void init_user_stats(USER_STATS *user_stats,
@@ -415,7 +435,7 @@ void init_user_stats(USER_STATS *user_stats,
memcpy(user_stats->user, user, user_length);
user_stats->user[user_length]= 0;
user_stats->user_name_length= user_length;
- strmake(user_stats->priv_user, priv_user, sizeof(user_stats->priv_user)-1);
+ strmake_buf(user_stats->priv_user, priv_user);
user_stats->total_connections= total_connections;
user_stats->concurrent_connections= concurrent_connections;
@@ -441,7 +461,7 @@ void init_user_stats(USER_STATS *user_stats,
}
-#ifdef COMPLEAT_PATCH_NOT_ADDED_YET
+#ifdef COMPLETE_PATCH_NOT_ADDED_YET
void add_user_stats(USER_STATS *user_stats,
uint total_connections,
@@ -495,9 +515,9 @@ void add_user_stats(USER_STATS *user_stats,
void init_global_user_stats(void)
{
- if (hash_init(&global_user_stats, system_charset_info, max_connections,
- 0, 0, (hash_get_key) get_key_user_stats,
- (hash_free_key)free_user_stats, 0))
+ if (my_hash_init(&global_user_stats, system_charset_info, max_connections,
+ 0, 0, (my_hash_get_key) get_key_user_stats,
+ (my_hash_free_key)free_user_stats, 0))
{
sql_print_error("Initializing global_user_stats failed.");
exit(1);
@@ -506,9 +526,9 @@ void init_global_user_stats(void)
void init_global_client_stats(void)
{
- if (hash_init(&global_client_stats, system_charset_info, max_connections,
- 0, 0, (hash_get_key) get_key_user_stats,
- (hash_free_key)free_user_stats, 0))
+ if (my_hash_init(&global_client_stats, system_charset_info, max_connections,
+ 0, 0, (my_hash_get_key) get_key_user_stats,
+ (my_hash_free_key)free_user_stats, 0))
{
sql_print_error("Initializing global_client_stats failed.");
exit(1);
@@ -524,14 +544,14 @@ extern "C" uchar *get_key_table_stats(TABLE_STATS *table_stats, size_t *length,
extern "C" void free_table_stats(TABLE_STATS* table_stats)
{
- my_free(table_stats, MYF(0));
+ my_free(table_stats);
}
void init_global_table_stats(void)
{
- if (hash_init(&global_table_stats, system_charset_info, max_connections,
- 0, 0, (hash_get_key) get_key_table_stats,
- (hash_free_key)free_table_stats, 0)) {
+ if (my_hash_init(&global_table_stats, system_charset_info, max_connections,
+ 0, 0, (my_hash_get_key) get_key_table_stats,
+ (my_hash_free_key)free_table_stats, 0)) {
sql_print_error("Initializing global_table_stats failed.");
exit(1);
}
@@ -546,14 +566,14 @@ extern "C" uchar *get_key_index_stats(INDEX_STATS *index_stats, size_t *length,
extern "C" void free_index_stats(INDEX_STATS* index_stats)
{
- my_free(index_stats, MYF(0));
+ my_free(index_stats);
}
void init_global_index_stats(void)
{
- if (hash_init(&global_index_stats, system_charset_info, max_connections,
- 0, 0, (hash_get_key) get_key_index_stats,
- (hash_free_key)free_index_stats, 0))
+ if (my_hash_init(&global_index_stats, system_charset_info, max_connections,
+ 0, 0, (my_hash_get_key) get_key_index_stats,
+ (my_hash_free_key)free_index_stats, 0))
{
sql_print_error("Initializing global_index_stats failed.");
exit(1);
@@ -563,22 +583,22 @@ void init_global_index_stats(void)
void free_global_user_stats(void)
{
- hash_free(&global_user_stats);
+ my_hash_free(&global_user_stats);
}
void free_global_table_stats(void)
{
- hash_free(&global_table_stats);
+ my_hash_free(&global_table_stats);
}
void free_global_index_stats(void)
{
- hash_free(&global_index_stats);
+ my_hash_free(&global_index_stats);
}
void free_global_client_stats(void)
{
- hash_free(&global_client_stats);
+ my_hash_free(&global_client_stats);
}
/*
@@ -593,7 +613,7 @@ static bool increment_count_by_name(const char *name, size_t name_length,
{
USER_STATS *user_stats;
- if (!(user_stats= (USER_STATS*) hash_search(users_or_clients, (uchar*) name,
+ if (!(user_stats= (USER_STATS*) my_hash_search(users_or_clients, (uchar*) name,
name_length)))
{
/* First connection for this user or client */
@@ -617,7 +637,7 @@ static bool increment_count_by_name(const char *name, size_t name_length,
if (my_hash_insert(users_or_clients, (uchar*)user_stats))
{
- my_free(user_stats, 0);
+ my_free(user_stats);
return TRUE; // Out of memory
}
}
@@ -646,7 +666,7 @@ static bool increment_connection_count(THD* thd, bool use_lock)
return FALSE;
if (use_lock)
- pthread_mutex_lock(&LOCK_global_user_client_stats);
+ mysql_mutex_lock(&LOCK_global_user_client_stats);
if (increment_count_by_name(user_string, strlen(user_string), user_string,
&global_user_stats, thd))
@@ -663,7 +683,7 @@ static bool increment_connection_count(THD* thd, bool use_lock)
end:
if (use_lock)
- pthread_mutex_unlock(&LOCK_global_user_client_stats);
+ mysql_mutex_unlock(&LOCK_global_user_client_stats);
return return_value;
}
#endif
@@ -740,10 +760,10 @@ void update_global_user_stats(THD *thd, bool create_user, time_t now)
client_string= get_client_host(thd);
client_string_length= strlen(client_string);
- pthread_mutex_lock(&LOCK_global_user_client_stats);
+ mysql_mutex_lock(&LOCK_global_user_client_stats);
// Update by user name
- if ((user_stats= (USER_STATS*) hash_search(&global_user_stats,
+ if ((user_stats= (USER_STATS*) my_hash_search(&global_user_stats,
(uchar*) user_string,
user_string_length)))
{
@@ -761,7 +781,7 @@ void update_global_user_stats(THD *thd, bool create_user, time_t now)
}
/* Update by client IP */
- if ((user_stats= (USER_STATS*)hash_search(&global_client_stats,
+ if ((user_stats= (USER_STATS*)my_hash_search(&global_client_stats,
(uchar*) client_string,
client_string_length)))
{
@@ -781,7 +801,7 @@ void update_global_user_stats(THD *thd, bool create_user, time_t now)
thd->select_commands= thd->update_commands= thd->other_commands= 0;
thd->last_global_update_time= now;
- pthread_mutex_unlock(&LOCK_global_user_client_stats);
+ mysql_mutex_unlock(&LOCK_global_user_client_stats);
}
@@ -830,7 +850,7 @@ bool thd_init_client_charset(THD *thd, uint cs_number)
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "character_set_client",
cs->csname);
return true;
- }
+ }
thd->variables.character_set_results=
thd->variables.collation_connection=
thd->variables.character_set_client= cs;
@@ -846,13 +866,8 @@ bool thd_init_client_charset(THD *thd, uint cs_number)
bool init_new_connection_handler_thread()
{
pthread_detach_this_thread();
-#if defined(__WIN__)
- win_install_sigabrt_handler();
-#else
- /* Win32 calls this in pthread_create */
if (my_thread_init())
return 1;
-#endif /* __WIN__ */
return 0;
}
@@ -876,15 +891,16 @@ static int check_connection(THD *thd)
DBUG_PRINT("info",
("New connection received on %s", vio_description(net->vio)));
+
#ifdef SIGNAL_WITH_VIO_CLOSE
thd->set_active_vio(net->vio);
#endif
if (!thd->main_security_ctx.host) // If TCP/IP connection
{
- char ip[30];
+ char ip[NI_MAXHOST];
- if (vio_peer_addr(net->vio, ip, &thd->peer_port))
+ if (vio_peer_addr(net->vio, ip, &thd->peer_port, NI_MAXHOST))
{
my_error(ER_BAD_HOST_ERROR, MYF(0));
return 1;
@@ -905,12 +921,15 @@ static int check_connection(THD *thd)
if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(MY_WME))))
return 1; /* The error is set by my_strdup(). */
thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip;
- vio_in_addr(net->vio,&thd->remote.sin_addr);
if (!(specialflag & SPECIAL_NO_RESOLVE))
{
- vio_in_addr(net->vio,&thd->remote.sin_addr);
- thd->main_security_ctx.host=
- ip_to_hostname(&thd->remote.sin_addr, &connect_errors);
+ if (ip_to_hostname(&net->vio->remote, thd->main_security_ctx.ip,
+ &thd->main_security_ctx.host, &connect_errors))
+ {
+ my_error(ER_BAD_HOST_ERROR, MYF(0));
+ return 1;
+ }
+
/* Cut very long hostnames to avoid possible overflows */
if (thd->main_security_ctx.host)
{
@@ -943,7 +962,7 @@ static int check_connection(THD *thd)
thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host;
thd->main_security_ctx.ip= 0;
/* Reset sin_addr */
- bzero((char*) &thd->remote, sizeof(thd->remote));
+ bzero((char*) &net->vio->remote, sizeof(net->vio->remote));
}
vio_keepalive(net->vio, TRUE);
@@ -971,9 +990,9 @@ bool setup_connection_thread_globals(THD *thd)
{
if (thd->store_globals())
{
- close_connection(thd, ER_OUT_OF_RESOURCES, 1);
+ close_connection(thd, ER_OUT_OF_RESOURCES);
statistic_increment(aborted_connects,&LOCK_status);
- thd->scheduler->end_thread(thd, 0);
+ MYSQL_CALLBACK(thd->scheduler, end_thread, (thd, 0));
return 1; // Error
}
return 0;
@@ -1008,11 +1027,11 @@ bool login_connection(THD *thd)
my_net_set_write_timeout(net, connect_timeout);
error= check_connection(thd);
- net_end_statement(thd);
+ thd->protocol->end_statement();
if (error)
{ // Wrong permissions
-#ifdef __NT__
+#ifdef _WIN32
if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE)
my_sleep(1000); /* must wait after eof() */
#endif
@@ -1026,7 +1045,7 @@ bool login_connection(THD *thd)
/* Updates global user connection stats. */
if (increment_connection_count(thd, TRUE))
{
- net_send_error(thd, ER_OUTOFMEMORY); // Out of memory
+ my_error(ER_OUTOFMEMORY, MYF(0), 2*sizeof(USER_STATS));
DBUG_RETURN(1);
}
@@ -1054,7 +1073,12 @@ void end_connection(THD *thd)
again in thd->cleanup()
*/
decrease_user_connections(thd->user_connect);
- thd->user_connect= 0;
+ /*
+ The thread may returned back to the pool and assigned to a user
+ that doesn't have a limit. Ensure the user is not using resources
+ of someone else.
+ */
+ thd->user_connect= NULL;
}
if (thd->killed || (net->error && net->vio != 0))
@@ -1063,20 +1087,8 @@ void end_connection(THD *thd)
status_var_increment(thd->status_var.lost_connections);
}
- if (net->error && net->vio != 0)
- {
- if (!thd->killed && thd->variables.log_warnings > 1)
- {
- Security_context *sctx= thd->security_ctx;
-
- sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
- thd->thread_id,(thd->db ? thd->db : "unconnected"),
- sctx->user ? sctx->user : "unauthenticated",
- sctx->host_or_ip,
- (thd->main_da.is_error() ? thd->main_da.message() :
- ER(ER_UNKNOWN_ERROR)));
- }
- }
+ if (!thd->killed && (net->error && net->vio != 0))
+ thd->print_aborted_warning(1, ER(ER_UNKNOWN_ERROR));
}
@@ -1088,12 +1100,6 @@ void prepare_new_connection_state(THD* thd)
{
Security_context *sctx= thd->security_ctx;
-#ifdef __NETWARE__
- netware_reg_user(sctx->ip, sctx->user, "MySQL");
-#endif
-
- if (thd->variables.max_join_size == HA_POS_ERROR)
- thd->options |= OPTION_BIG_SELECTS;
if (thd->client_capabilities & CLIENT_COMPRESS)
thd->net.compress=1; // Use compression
@@ -1102,32 +1108,30 @@ void prepare_new_connection_state(THD* thd)
embedded server library.
TODO: refactor this to avoid code duplication there
*/
- thd->version= refresh_version;
thd->proc_info= 0;
thd->command= COM_SLEEP;
thd->set_time();
thd->init_for_queries();
- if (sys_init_connect.value_length && !(sctx->master_access & SUPER_ACL))
+ if (opt_init_connect.length && !(sctx->master_access & SUPER_ACL))
{
- execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect);
+ execute_init_command(thd, &opt_init_connect, &LOCK_sys_init_connect);
if (thd->is_error())
{
- ulong packet_length;
- NET *net= &thd->net;
-
- sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
- thd->thread_id,
- thd->db ? thd->db : "unconnected",
- sctx->user ? sctx->user : "unauthenticated",
- sctx->host_or_ip, "init_connect command failed");
- sql_print_warning("%s", thd->main_da.message());
+ thd->killed= KILL_CONNECTION;
+ thd->print_aborted_warning(0, "init_connect command failed");
+ sql_print_warning("%s", thd->stmt_da->message());
+ /*
+ now let client to send its first command,
+ to be able to send the error back
+ */
+ NET *net= &thd->net;
thd->lex->current_select= 0;
my_net_set_read_timeout(net, thd->variables.net_wait_timeout);
thd->clear_error();
net_new_transaction(net);
- packet_length= my_net_read(net);
+ ulong packet_length= my_net_read(net);
/*
If my_net_read() failed, my_error() has been already called,
and the main Diagnostics Area contains an error condition.
@@ -1138,9 +1142,8 @@ void prepare_new_connection_state(THD* thd)
thd->db ? thd->db : "unconnected",
sctx->user ? sctx->user : "unauthenticated",
sctx->host_or_ip, "init_connect command failed");
-
thd->server_status&= ~SERVER_STATUS_CLEAR_SET;
- net_end_statement(thd);
+ thd->protocol->end_statement();
thd->killed = KILL_CONNECTION;
return;
}
@@ -1173,16 +1176,52 @@ pthread_handler_t handle_one_connection(void *arg)
{
THD *thd= (THD*) arg;
+ mysql_thread_set_psi_id(thd->thread_id);
+
+ do_handle_one_connection(thd);
+ return 0;
+}
+
+bool thd_prepare_connection(THD *thd)
+{
+ bool rc;
+ lex_start(thd);
+ rc= login_connection(thd);
+ MYSQL_AUDIT_NOTIFY_CONNECTION_CONNECT(thd);
+ if (rc)
+ return rc;
+
+ MYSQL_CONNECTION_START(thd->thread_id, &thd->security_ctx->priv_user[0],
+ (char *) thd->security_ctx->host_or_ip);
+
+ prepare_new_connection_state(thd);
+ return FALSE;
+}
+
+bool thd_is_connection_alive(THD *thd)
+{
+ NET *net= &thd->net;
+ if (!net->error &&
+ net->vio != 0 &&
+ thd->killed < KILL_CONNECTION)
+ return TRUE;
+ return FALSE;
+}
+
+void do_handle_one_connection(THD *thd_arg)
+{
+ THD *thd= thd_arg;
+
thd->thr_create_utime= microsecond_interval_timer();
/* We need to set this because of time_out_user_resource_limits */
thd->start_utime= thd->thr_create_utime;
- if (thread_scheduler.init_new_connection_thread())
+ if (MYSQL_CALLBACK_ELSE(thd->scheduler, init_new_connection_thread, (), 0))
{
- close_connection(thd, ER_OUT_OF_RESOURCES, 1);
+ close_connection(thd, ER_OUT_OF_RESOURCES);
statistic_increment(aborted_connects,&LOCK_status);
- thd->scheduler->end_thread(thd,0);
- return 0;
+ MYSQL_CALLBACK(thd->scheduler, end_thread, (thd, 0));
+ return;
}
/*
@@ -1209,37 +1248,34 @@ pthread_handler_t handle_one_connection(void *arg)
*/
thd->thread_stack= (char*) &thd;
if (setup_connection_thread_globals(thd))
- return 0;
+ return;
for (;;)
{
- NET *net= &thd->net;
bool create_user= TRUE;
- lex_start(thd);
- if (login_connection(thd))
+ if (thd_prepare_connection(thd))
{
create_user= FALSE;
goto end_thread;
}
- prepare_new_connection_state(thd);
-
- while (!net->error && net->vio != 0 &&
- thd->killed < KILL_CONNECTION)
+ while (thd_is_connection_alive(thd))
{
+ mysql_audit_release(thd);
if (do_command(thd))
break;
}
end_connection(thd);
end_thread:
- close_connection(thd, 0, 1);
+ close_connection(thd);
+
if (thd->userstat_running)
update_global_user_stats(thd, create_user, time(NULL));
- if (thd->scheduler->end_thread(thd,1))
- return 0; // Probably no-threads
+ if (MYSQL_CALLBACK_ELSE(thd->scheduler, end_thread, (thd, 1), 0))
+ return; // Probably no-threads
/*
If end_thread() returns, this thread has been schedule to
diff --git a/sql/sql_connect.h b/sql/sql_connect.h
new file mode 100644
index 00000000000..bab171606ba
--- /dev/null
+++ b/sql/sql_connect.h
@@ -0,0 +1,73 @@
+/* Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ 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_CONNECT_INCLUDED
+#define SQL_CONNECT_INCLUDED
+
+#include "my_sys.h" /* pthread_handler_t */
+#include "mysql_com.h" /* enum_server_command */
+#include "structs.h"
+#include <hash.h>
+
+class THD;
+typedef struct st_lex_user LEX_USER;
+typedef struct user_conn USER_CONN;
+
+void init_max_user_conn(void);
+void init_global_user_stats(void);
+void init_global_table_stats(void);
+void init_global_index_stats(void);
+void init_global_client_stats(void);
+void free_max_user_conn(void);
+void free_global_user_stats(void);
+void free_global_table_stats(void);
+void free_global_index_stats(void);
+void free_global_client_stats(void);
+
+pthread_handler_t handle_one_connection(void *arg);
+void do_handle_one_connection(THD *thd_arg);
+bool init_new_connection_handler_thread();
+void reset_mqh(LEX_USER *lu, bool get_them);
+bool check_mqh(THD *thd, uint check_command);
+void time_out_user_resource_limits(THD *thd, USER_CONN *uc);
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+void decrease_user_connections(USER_CONN *uc);
+#else
+#define decrease_user_connections(X) do { } while(0) /* nothing */
+#endif
+bool thd_init_client_charset(THD *thd, uint cs_number);
+bool setup_connection_thread_globals(THD *thd);
+bool thd_prepare_connection(THD *thd);
+bool thd_is_connection_alive(THD *thd);
+
+bool login_connection(THD *thd);
+void prepare_new_connection_state(THD* thd);
+void end_connection(THD *thd);
+void update_global_user_stats(THD* thd, bool create_user, time_t now);
+int get_or_create_user_conn(THD *thd, const char *user,
+ const char *host, const USER_RESOURCES *mqh);
+int check_for_max_user_connections(THD *thd, USER_CONN *uc);
+
+extern HASH global_user_stats;
+extern HASH global_client_stats;
+extern HASH global_table_stats;
+extern 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 mysql_mutex_t LOCK_stats;
+
+#endif /* SQL_CONNECT_INCLUDED */
diff --git a/sql/sql_const.h b/sql/sql_const.h
new file mode 100644
index 00000000000..9d227601a20
--- /dev/null
+++ b/sql/sql_const.h
@@ -0,0 +1,257 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/**
+ @file
+ File containing constants that can be used throughout the server.
+
+ @note This file shall not contain any includes of any kinds.
+*/
+
+#ifndef SQL_CONST_INCLUDED
+#define SQL_CONST_INCLUDED
+
+#define LIBLEN FN_REFLEN-FN_LEN /* Max l{ngd p} dev */
+/* extra 4+4 bytes for slave tmp tables */
+#define MAX_DBKEY_LENGTH (NAME_LEN*2+1+1+4+4)
+#define MAX_ALIAS_NAME 256
+#define MAX_FIELD_NAME 34 /* Max colum name length +2 */
+#define MAX_SYS_VAR_LENGTH 32
+#define MAX_KEY MAX_INDEXES /* Max used keys */
+#define MAX_REF_PARTS 32 /* Max parts used as ref */
+#define MAX_KEY_LENGTH 3072 /* max possible key */
+#if SIZEOF_OFF_T > 4
+#define MAX_REFLENGTH 8 /* Max length for record ref */
+#else
+#define MAX_REFLENGTH 4 /* Max length for record ref */
+#endif
+#define MAX_HOSTNAME 61 /* len+1 in mysql.user */
+
+#define MAX_MBWIDTH 3 /* Max multibyte sequence */
+#define MAX_FIELD_CHARLENGTH 255
+#define MAX_FIELD_VARCHARLENGTH 65535
+#define MAX_FIELD_BLOBLENGTH UINT_MAX32 /* cf field_blob::get_length() */
+#define CONVERT_IF_BIGGER_TO_BLOB 512 /* Threshold *in characters* */
+
+/* Max column width +1 */
+#define MAX_FIELD_WIDTH (MAX_FIELD_CHARLENGTH*MAX_MBWIDTH+1)
+
+#define MAX_BIT_FIELD_LENGTH 64 /* Max length in bits for bit fields */
+
+#define MAX_DATE_WIDTH 10 /* YYYY-MM-DD */
+#define MIN_TIME_WIDTH 10 /* -HHH:MM:SS */
+#define MAX_TIME_WIDTH 16 /* -DDDDDD HH:MM:SS */
+#define MAX_TIME_FULL_WIDTH 23 /* -DDDDDD HH:MM:SS.###### */
+#define MAX_DATETIME_FULL_WIDTH 29 /* YYYY-MM-DD HH:MM:SS.###### AM */
+#define MAX_DATETIME_WIDTH 19 /* YYYY-MM-DD HH:MM:SS */
+#define MAX_DATETIME_COMPRESSED_WIDTH 14 /* YYYYMMDDHHMMSS */
+#define MAX_DATETIME_PRECISION 6
+
+#define MAX_TABLES (sizeof(table_map)*8-3) /* Max tables in join */
+#define PARAM_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-3))
+#define OUTER_REF_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-2))
+#define RAND_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-1))
+#define PSEUDO_TABLE_BITS (PARAM_TABLE_BIT | OUTER_REF_TABLE_BIT | \
+ RAND_TABLE_BIT)
+#define MAX_FIELDS 4096 /* Limit in the .frm file */
+#define MAX_PARTITIONS 1024
+
+#define MAX_SELECT_NESTING (sizeof(nesting_map)*8-1)
+
+#define MAX_SORT_MEMORY 2048*1024
+#define MIN_SORT_MEMORY 1024
+
+/* Some portable defines */
+
+#define portable_sizeof_char_ptr 8
+#define STRING_BUFFER_USUAL_SIZE 80
+
+/* Memory allocated when parsing a statement / saving a statement */
+#define MEM_ROOT_BLOCK_SIZE 8192
+#define MEM_ROOT_PREALLOC 8192
+#define TRANS_MEM_ROOT_BLOCK_SIZE 4096
+#define TRANS_MEM_ROOT_PREALLOC 4096
+
+#define DEFAULT_ERROR_COUNT 64
+#define EXTRA_RECORDS 10 /* Extra records in sort */
+#define SCROLL_EXTRA 5 /* Extra scroll-rows. */
+#define FIELD_NAME_USED ((uint) 32768) /* Bit set if fieldname used */
+#define FORM_NAME_USED ((uint) 16384) /* Bit set if formname used */
+#define FIELD_NR_MASK 16383 /* To get fieldnumber */
+#define FERR -1 /* Error from my_functions */
+#define CREATE_MODE 0 /* Default mode on new files */
+#define NAMES_SEP_CHAR 255 /* Char to sep. names */
+
+#define READ_RECORD_BUFFER (uint) (IO_SIZE*8) /* Pointer_buffer_size */
+#define DISK_BUFFER_SIZE (uint) (IO_SIZE*16) /* Size of diskbuffer */
+
+#define FRM_VER_TRUE_VARCHAR (FRM_VER+4) /* 10 */
+
+/***************************************************************************
+ Configuration parameters
+****************************************************************************/
+
+#define ACL_CACHE_SIZE 256
+#define MAX_PASSWORD_LENGTH 32
+#define HOST_CACHE_SIZE 128
+#define MAX_ACCEPT_RETRY 10 // Test accept this many times
+#define MAX_FIELDS_BEFORE_HASH 32
+#define USER_VARS_HASH_SIZE 16
+#define TABLE_OPEN_CACHE_MIN 400
+#define TABLE_OPEN_CACHE_DEFAULT 400
+#define TABLE_DEF_CACHE_DEFAULT 400
+/**
+ We must have room for at least 400 table definitions in the table
+ cache, since otherwise there is no chance prepared
+ statements that use these many tables can work.
+ Prepared statements use table definition cache ids (table_map_id)
+ as table version identifiers. If the table definition
+ cache size is less than the number of tables used in a statement,
+ the contents of the table definition cache is guaranteed to rotate
+ between a prepare and execute. This leads to stable validation
+ errors. In future we shall use more stable version identifiers,
+ for now the only solution is to ensure that the table definition
+ cache can contain at least all tables of a given statement.
+*/
+#define TABLE_DEF_CACHE_MIN 400
+
+/*
+ Stack reservation.
+ Feel free to raise this by the smallest amount you can to get the
+ "execution_constants" test to pass.
+*/
+#define STACK_MIN_SIZE 16000 // Abort if less stack during eval.
+
+#define STACK_MIN_SIZE_FOR_OPEN 1024*80
+#define STACK_BUFF_ALLOC 352 ///< For stack overrun checks
+#ifndef MYSQLD_NET_RETRY_COUNT
+#define MYSQLD_NET_RETRY_COUNT 10 ///< Abort read after this many int.
+#endif
+
+#define QUERY_ALLOC_BLOCK_SIZE 8192
+#define QUERY_ALLOC_PREALLOC_SIZE 8192
+#define TRANS_ALLOC_BLOCK_SIZE 4096
+#define TRANS_ALLOC_PREALLOC_SIZE 4096
+#define RANGE_ALLOC_BLOCK_SIZE 4096
+#define ACL_ALLOC_BLOCK_SIZE 1024
+#define UDF_ALLOC_BLOCK_SIZE 1024
+#define TABLE_ALLOC_BLOCK_SIZE 1024
+#define WARN_ALLOC_BLOCK_SIZE 2048
+#define WARN_ALLOC_PREALLOC_SIZE 1024
+
+/*
+ The following parameters is to decide when to use an extra cache to
+ optimise seeks when reading a big table in sorted order
+*/
+#define MIN_FILE_LENGTH_TO_USE_ROW_CACHE (10L*1024*1024)
+#define MIN_ROWS_TO_USE_TABLE_CACHE 100
+#define MIN_ROWS_TO_USE_BULK_INSERT 100
+
+/**
+ The following is used to decide if MySQL should use table scanning
+ instead of reading with keys. The number says how many evaluation of the
+ WHERE clause is comparable to reading one extra row from a table.
+*/
+#define TIME_FOR_COMPARE 5 // 5 compares == one read
+
+/**
+ Number of comparisons of table rowids equivalent to reading one row from a
+ table.
+*/
+#define TIME_FOR_COMPARE_ROWID (TIME_FOR_COMPARE*100)
+
+/* cost1 is better that cost2 only if cost1 + COST_EPS < cost2 */
+#define COST_EPS 0.001
+
+/*
+ For sequential disk seeks the cost formula is:
+ DISK_SEEK_BASE_COST + DISK_SEEK_PROP_COST * #blocks_to_skip
+
+ The cost of average seek
+ DISK_SEEK_BASE_COST + DISK_SEEK_PROP_COST*BLOCKS_IN_AVG_SEEK =1.0.
+*/
+#define DISK_SEEK_BASE_COST ((double)0.9)
+
+#define BLOCKS_IN_AVG_SEEK 128
+
+#define DISK_SEEK_PROP_COST ((double)0.1/BLOCKS_IN_AVG_SEEK)
+
+
+/**
+ Number of rows in a reference table when refereed through a not unique key.
+ This value is only used when we don't know anything about the key
+ distribution.
+*/
+#define MATCHING_ROWS_IN_OTHER_TABLE 10
+
+/*
+ Subquery materialization-related constants
+*/
+#define HEAP_TEMPTABLE_LOOKUP_COST 0.05
+#define DISK_TEMPTABLE_LOOKUP_COST 1.0
+
+#define MY_CHARSET_BIN_MB_MAXLEN 1
+
+/** Don't pack string keys shorter than this (if PACK_KEYS=1 isn't used). */
+#define KEY_DEFAULT_PACK_LENGTH 8
+
+/** Characters shown for the command in 'show processlist'. */
+#define PROCESS_LIST_WIDTH 100
+/* Characters shown for the command in 'information_schema.processlist' */
+#define PROCESS_LIST_INFO_WIDTH 65535
+
+#define PRECISION_FOR_DOUBLE 53
+#define PRECISION_FOR_FLOAT 24
+
+/* -[digits].E+## */
+#define MAX_FLOAT_STR_LENGTH (FLT_DIG + 6)
+/* -[digits].E+### */
+#define MAX_DOUBLE_STR_LENGTH (DBL_DIG + 7)
+
+/*
+ Default time to wait before aborting a new client connection
+ that does not respond to "initial server greeting" timely
+*/
+#define CONNECT_TIMEOUT 10
+
+/* The following can also be changed from the command line */
+#define DEFAULT_CONCURRENCY 10
+#define DELAYED_LIMIT 100 /**< pause after xxx inserts */
+#define DELAYED_QUEUE_SIZE 1000
+#define DELAYED_WAIT_TIMEOUT 5*60 /**< Wait for delayed insert */
+#define MAX_CONNECT_ERRORS 10 ///< errors before disabling host
+
+#define LONG_TIMEOUT ((ulong) 3600L*24L*365L)
+
+/**
+ Maximum length of time zone name that we support (Time zone name is
+ char(64) in db). mysqlbinlog needs it.
+*/
+#define MAX_TIME_ZONE_NAME_LENGTH (NAME_LEN + 1)
+
+#if defined(__WIN__)
+
+#define INTERRUPT_PRIOR -2
+#define CONNECT_PRIOR -1
+#define WAIT_PRIOR 0
+#define QUERY_PRIOR 2
+#else
+#define INTERRUPT_PRIOR 10
+#define CONNECT_PRIOR 9
+#define WAIT_PRIOR 8
+#define QUERY_PRIOR 6
+#endif /* __WIN92__ */
+
+#endif /* SQL_CONST_INCLUDED */
diff --git a/sql/sql_crypt.cc b/sql/sql_crypt.cc
index 3ebc28ebbd2..bcc8ad0b10f 100644
--- a/sql/sql_crypt.cc
+++ b/sql/sql_crypt.cc
@@ -1,6 +1,4 @@
-/*
- Copyright (c) 2000, 2003, 2005, 2006 MySQL AB, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,8 +11,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
@@ -29,7 +26,9 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "sql_crypt.h"
+#include "password.h"
void SQL_CRYPT::init(ulong *rand_nr)
{
diff --git a/sql/sql_crypt.h b/sql/sql_crypt.h
index 2c06b0e4ac5..3a12d603601 100644
--- a/sql/sql_crypt.h
+++ b/sql/sql_crypt.h
@@ -1,6 +1,7 @@
-/*
- Copyright (c) 2000, 2005, 2006 MySQL AB, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+#ifndef SQL_CRYPT_INCLUDED
+#define SQL_CRYPT_INCLUDED
+
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,14 +14,16 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifdef USE_PRAGMA_INTERFACE
#pragma interface /* gcc class implementation */
#endif
+#include "sql_list.h" /* Sql_alloc */
+#include "mysql_com.h" /* rand_struct */
+
class SQL_CRYPT :public Sql_alloc
{
struct my_rnd_struct rand,org_rand;
@@ -38,3 +41,5 @@ class SQL_CRYPT :public Sql_alloc
void encode(char *str, uint length);
void decode(char *str, uint length);
};
+
+#endif /* SQL_CRYPT_INCLUDED */
diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc
index 82751d64da9..230a8b2c802 100644
--- a/sql/sql_cursor.cc
+++ b/sql/sql_cursor.cc
@@ -12,66 +12,22 @@
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
-*/
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation /* gcc class implementation */
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
#include "sql_cursor.h"
-#include "sql_select.h"
+#include "probes_mysql.h"
+#include "sql_parse.h" // mysql_execute_command
/****************************************************************************
Declarations.
****************************************************************************/
/**
- Sensitive_cursor -- a sensitive non-materialized server side
- cursor. An instance of this class cursor has its own runtime
- state -- list of used items and memory root for runtime memory,
- open and locked tables, change list for the changes of the
- parsed tree. This state is freed when the cursor is closed.
-*/
-
-class Sensitive_cursor: public Server_side_cursor
-{
- MEM_ROOT main_mem_root;
- Query_arena *stmt_arena;
- JOIN *join;
- TABLE *open_tables;
- MYSQL_LOCK *lock;
- TABLE *derived_tables;
- /* List of items created during execution */
- query_id_t query_id;
- struct Engine_info
- {
- handlerton *ht;
- void *read_view;
- };
- Engine_info ht_info[MAX_HA];
- Item_change_list change_list;
- my_bool close_at_commit;
- THR_LOCK_OWNER lock_id;
-private:
- /* bzero cursor state in THD */
- void reset_thd(THD *thd);
-public:
- Sensitive_cursor(THD *thd, select_result *result_arg);
-
- THR_LOCK_OWNER *get_lock_id() { return &lock_id; }
- /* Save THD state into cursor */
- void post_open(THD *thd);
-
- virtual bool is_open() const { return join != 0; }
- virtual int open(JOIN *join);
- virtual void fetch(ulong num_rows);
- virtual void close();
- virtual ~Sensitive_cursor();
-};
-
-
-/**
Materialized_cursor -- an insensitive materialized server-side
cursor. The result set of this cursor is saved in a temporary
table at open. The cursor itself is simply an interface for the
@@ -91,7 +47,7 @@ class Materialized_cursor: public Server_side_cursor
public:
Materialized_cursor(select_result *result, TABLE *table);
- int fill_item_list(THD *thd, List<Item> &send_fields);
+ int send_result_set_metadata(THD *thd, List<Item> &send_result_set_metadata);
virtual bool is_open() const { return table != 0; }
virtual int open(JOIN *join __attribute__((unused)));
virtual void fetch(ulong num_rows);
@@ -116,17 +72,16 @@ public:
Materialized_cursor *materialized_cursor;
Select_materialize(select_result *result_arg)
:result(result_arg), materialized_cursor(0) {}
- virtual bool send_fields(List<Item> &list, uint flags);
+ virtual bool send_result_set_metadata(List<Item> &list, uint flags);
};
/**************************************************************************/
/**
- Attempt to open a materialized or non-materialized cursor.
+ Attempt to open a materialized cursor.
@param thd thread handle
- @param[in] flags create a materialized cursor or not
@param[in] result result class of the caller used as a destination
for the rows fetched from the cursor
@param[out] pcursor a pointer to store a pointer to cursor in
@@ -139,55 +94,41 @@ public:
non-zero an error, 'pcursor' has been left intact.
*/
-int mysql_open_cursor(THD *thd, uint flags, select_result *result,
+int mysql_open_cursor(THD *thd, select_result *result,
Server_side_cursor **pcursor)
{
- Sensitive_cursor *sensitive_cursor;
select_result *save_result;
Select_materialize *result_materialize;
LEX *lex= thd->lex;
int rc;
- /*
- The lifetime of the sensitive cursor is the same or less as the
- lifetime of the runtime memory of the statement it's opened for.
- */
if (! (result_materialize= new (thd->mem_root) Select_materialize(result)))
return 1;
- if (! (sensitive_cursor= new (thd->mem_root) Sensitive_cursor(thd, result)))
- {
- delete result_materialize;
- result_materialize= NULL;
- return 1;
- }
-
save_result= lex->result;
lex->result= result_materialize;
- if (! (flags & (uint) ALWAYS_MATERIALIZED_CURSOR))
- {
- thd->lock_id= sensitive_cursor->get_lock_id();
- thd->cursor= sensitive_cursor;
- }
+ MYSQL_QUERY_EXEC_START(thd->query(),
+ thd->thread_id,
+ (char *) (thd->db ? thd->db : ""),
+ &thd->security_ctx->priv_user[0],
+ (char *) thd->security_ctx->host_or_ip,
+ 2);
+ /* Mark that we can't use query cache with cursors */
+ thd->query_cache_is_applicable= 0;
rc= mysql_execute_command(thd);
+ MYSQL_QUERY_EXEC_DONE(rc);
lex->result= save_result;
- thd->lock_id= &thd->main_lock_id;
- thd->cursor= 0;
-
/*
Possible options here:
- - a sensitive cursor is open. In this case rc is 0 and
- result_materialize->materialized_cursor is NULL, or
- a materialized cursor is open. In this case rc is 0 and
result_materialize->materialized is not NULL
- an error occurred during materialization.
result_materialize->materialized_cursor is not NULL, but rc != 0
- successful completion of mysql_execute_command without
- a cursor: rc is 0, result_materialize->materialized_cursor is NULL,
- sensitive_cursor is not open.
+ a cursor: rc is 0, result_materialize->materialized_cursor is NULL.
This is possible if some command writes directly to the
network, bypassing select_result mechanism. An example of
such command is SHOW VARIABLES or SHOW STATUS.
@@ -195,43 +136,37 @@ int mysql_open_cursor(THD *thd, uint flags, select_result *result,
if (rc)
{
if (result_materialize->materialized_cursor)
+ {
+ /* Rollback metadata in the client-server protocol. */
+ result_materialize->abort_result_set();
+
delete result_materialize->materialized_cursor;
- goto err_open;
- }
+ }
- if (sensitive_cursor->is_open())
- {
- DBUG_ASSERT(!result_materialize->materialized_cursor);
- /*
- It's safer if we grab THD state after mysql_execute_command
- is finished and not in Sensitive_cursor::open(), because
- currently the call to Sensitive_cursor::open is buried deep
- in JOIN::exec of the top level join.
- */
- sensitive_cursor->post_open(thd);
- *pcursor= sensitive_cursor;
goto end;
}
- else if (result_materialize->materialized_cursor)
+
+ if (result_materialize->materialized_cursor)
{
Materialized_cursor *materialized_cursor=
result_materialize->materialized_cursor;
+ /*
+ NOTE: close_thread_tables() has been called in
+ mysql_execute_command(), so all tables except from the cursor
+ temporary table have been closed.
+ */
+
if ((rc= materialized_cursor->open(0)))
{
delete materialized_cursor;
- materialized_cursor= NULL;
- goto err_open;
+ goto end;
}
*pcursor= materialized_cursor;
thd->stmt_arena->cleanup_stmt();
- goto end;
}
-err_open:
- DBUG_ASSERT(! (sensitive_cursor && sensitive_cursor->is_open()));
- delete sensitive_cursor;
end:
delete result_materialize;
return rc;
@@ -263,284 +198,6 @@ void Server_side_cursor::operator delete(void *ptr, size_t size)
DBUG_VOID_RETURN;
}
-/****************************************************************************
- Sensitive_cursor
-****************************************************************************/
-
-Sensitive_cursor::Sensitive_cursor(THD *thd, select_result *result_arg)
- :Server_side_cursor(&main_mem_root, result_arg),
- stmt_arena(0),
- join(0),
- close_at_commit(FALSE)
-{
- /* We will overwrite it at open anyway. */
- init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
- thr_lock_owner_init(&lock_id, &thd->lock_info);
- bzero((void*) ht_info, sizeof(ht_info));
-}
-
-
-/**
- Save THD state into cursor.
-
- @todo
- - XXX: thd->locked_tables is not changed.
- - What problems can we have with it if cursor is open?
- - TODO: must be fixed because of the prelocked mode.
-*/
-void
-Sensitive_cursor::post_open(THD *thd)
-{
- Engine_info *info;
- /*
- We need to save and reset thd->mem_root, otherwise it'll be
- freed later in mysql_parse.
-
- We can't just change thd->mem_root here as we want to keep the
- things that are already allocated in thd->mem_root for
- Sensitive_cursor::fetch()
- */
- *mem_root= *thd->mem_root;
- stmt_arena= thd->stmt_arena;
- state= stmt_arena->state;
- /* Allocate a new memory root for thd */
- init_sql_alloc(thd->mem_root,
- thd->variables.query_alloc_block_size,
- thd->variables.query_prealloc_size);
-
- /*
- Save tables and zero THD pointers to prevent table close in
- close_thread_tables.
- */
- derived_tables= thd->derived_tables;
- open_tables= thd->open_tables;
- lock= thd->lock;
- query_id= thd->query_id;
- free_list= thd->free_list;
- change_list= thd->change_list;
- reset_thd(thd);
- /* Now we have an active cursor and can cause a deadlock */
- thd->lock_info.n_cursors++;
-
- close_at_commit= FALSE; /* reset in case we're reusing the cursor */
- info= &ht_info[0];
- for (Ha_trx_info *ha_trx_info= thd->transaction.stmt.ha_list;
- ha_trx_info; ha_trx_info= ha_trx_info->next())
- {
- handlerton *ht= ha_trx_info->ht();
- close_at_commit|= test(ht->flags & HTON_CLOSE_CURSORS_AT_COMMIT);
- if (ht->create_cursor_read_view)
- {
- info->ht= ht;
- info->read_view= (ht->create_cursor_read_view)(ht, thd);
- ++info;
- }
- }
- /*
- XXX: thd->locked_tables is not changed.
- What problems can we have with it if cursor is open?
- TODO: must be fixed because of the prelocked mode.
- */
-}
-
-
-/**
- bzero cursor state in THD.
-*/
-
-void
-Sensitive_cursor::reset_thd(THD *thd)
-{
- thd->derived_tables= 0;
- thd->open_tables= 0;
- thd->lock= 0;
- thd->free_list= 0;
- thd->change_list.empty();
-}
-
-
-int
-Sensitive_cursor::open(JOIN *join_arg)
-{
- join= join_arg;
- THD *thd= join->thd;
- /* First non-constant table */
- DBUG_ENTER("Sensitive_cursor::open");
-
- join->change_result(result);
- /*
- Send fields description to the client; server_status is sent
- in 'EOF' packet, which follows send_fields().
- We don't simply use SEND_EOF flag of send_fields because we also
- want to flush the network buffer, which is done only in a standalone
- send_eof().
- */
- result->send_fields(*join->fields, Protocol::SEND_NUM_ROWS);
- thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
- result->send_eof();
- thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS;
-
- /* Prepare JOIN for reading rows. */
- join->tmp_table= 0;
- join->join_tab[join->top_join_tab_count - 1].next_select= setup_end_select_func(join);
- join->send_records= 0;
- join->fetch_limit= join->unit->offset_limit_cnt;
-
- /* Disable JOIN CACHE as it is not working with cursors yet */
- for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_CONST_TABLES);
- tab != join->join_tab + join->top_join_tab_count - 1;
- tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS))
- {
- if (tab->next_select == sub_select_cache)
- tab->next_select= sub_select;
- }
-
-#ifndef DBUG_OFF
- JOIN_TAB *first_tab= first_linear_tab(join, WITHOUT_CONST_TABLES);
- DBUG_ASSERT(first_tab->table->reginfo.not_exists_optimize == 0);
- DBUG_ASSERT(first_tab->shortcut_for_distinct == 0);
- /*
- null_row is set only if row not found and it's outer join: should never
- happen for the first table in join_tab list
- */
- DBUG_ASSERT(first_tab->table->null_row == 0);
-#endif
- DBUG_RETURN(0);
-}
-
-
-/**
- Fetch next num_rows rows from the cursor and send them to the client.
-
- Precondition:
- - Sensitive_cursor is open
-
- @param num_rows fetch up to this number of rows (maybe less)
-*/
-
-void
-Sensitive_cursor::fetch(ulong num_rows)
-{
- THD *thd= join->thd;
- JOIN_TAB *join_tab= join->join_tab + join->const_tables;
- enum_nested_loop_state error= NESTED_LOOP_OK;
- Query_arena backup_arena;
- Engine_info *info;
- DBUG_ENTER("Sensitive_cursor::fetch");
- DBUG_PRINT("enter",("rows: %lu", num_rows));
-
- DBUG_ASSERT(thd->derived_tables == 0 && thd->open_tables == 0 &&
- thd->lock == 0);
-
- thd->derived_tables= derived_tables;
- thd->open_tables= open_tables;
- thd->lock= lock;
- thd->query_id= query_id;
- thd->change_list= change_list;
- /* save references to memory allocated during fetch */
- thd->set_n_backup_active_arena(this, &backup_arena);
-
- for (info= ht_info; info->read_view ; info++)
- (info->ht->set_cursor_read_view)(info->ht, thd, info->read_view);
-
- join->fetch_limit+= num_rows;
-
- error= sub_select(join, join_tab, 0);
- if (error == NESTED_LOOP_OK || error == NESTED_LOOP_NO_MORE_ROWS)
- error= sub_select(join,join_tab,1);
- if (error == NESTED_LOOP_QUERY_LIMIT)
- error= NESTED_LOOP_OK; /* select_limit used */
- if (error == NESTED_LOOP_CURSOR_LIMIT)
- join->resume_nested_loop= TRUE;
-
- ha_release_temporary_latches(thd);
-
- /* Grab free_list here to correctly free it in close */
- thd->restore_active_arena(this, &backup_arena);
-
- change_list= thd->change_list;
- reset_thd(thd);
-
- for (info= ht_info; info->read_view; info++)
- (info->ht->set_cursor_read_view)(info->ht, thd, 0);
-
- if (error == NESTED_LOOP_CURSOR_LIMIT)
- {
- /* Fetch limit worked, possibly more rows are there */
- thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
- result->send_eof();
- thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS;
- }
- else
- {
- close();
- if (error == NESTED_LOOP_OK)
- {
- thd->server_status|= SERVER_STATUS_LAST_ROW_SENT;
- result->send_eof();
- thd->server_status&= ~SERVER_STATUS_LAST_ROW_SENT;
- }
- else if (error != NESTED_LOOP_KILLED)
- my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
- }
- DBUG_VOID_RETURN;
-}
-
-
-/**
- @todo
- Another hack: we need to set THD state as if in a fetch to be
- able to call stmt close.
-*/
-void
-Sensitive_cursor::close()
-{
- THD *thd= join->thd;
- DBUG_ENTER("Sensitive_cursor::close");
-
- for (Engine_info *info= ht_info; info->read_view; info++)
- {
- (info->ht->close_cursor_read_view)(info->ht, thd, info->read_view);
- info->read_view= 0;
- info->ht= 0;
- }
-
- thd->change_list= change_list;
- {
- /*
- XXX: Another hack: we need to set THD state as if in a fetch to be
- able to call stmt close.
- */
- DBUG_ASSERT(lock || open_tables || derived_tables);
-
- TABLE *tmp_derived_tables= thd->derived_tables;
- MYSQL_LOCK *tmp_lock= thd->lock;
-
- thd->open_tables= open_tables;
- thd->derived_tables= derived_tables;
- thd->lock= lock;
-
- /* Is expected to at least close tables and empty thd->change_list */
- stmt_arena->cleanup_stmt();
-
- thd->open_tables= tmp_derived_tables;
- thd->derived_tables= tmp_derived_tables;
- thd->lock= tmp_lock;
- }
- thd->lock_info.n_cursors--; /* Decrease the number of active cursors */
- join= 0;
- stmt_arena= 0;
- free_items();
- change_list.empty();
- DBUG_VOID_RETURN;
-}
-
-
-Sensitive_cursor::~Sensitive_cursor()
-{
- if (is_open())
- close();
-}
/***************************************************************************
Materialized_cursor
@@ -560,17 +217,20 @@ Materialized_cursor::Materialized_cursor(select_result *result_arg,
/**
- Preserve the original metadata that would be sent to the client.
+ Preserve the original metadata to be sent to the client.
+ Initiate sending of the original metadata to the client
+ (call Protocol::send_result_set_metadata()).
@param thd Thread identifier.
- @param send_fields List of fields that would be sent.
+ @param send_result_set_metadata List of fields that would be sent.
*/
-int Materialized_cursor::fill_item_list(THD *thd, List<Item> &send_fields)
+int Materialized_cursor::send_result_set_metadata(
+ THD *thd, List<Item> &send_result_set_metadata)
{
Query_arena backup_arena;
int rc;
- List_iterator_fast<Item> it_org(send_fields);
+ List_iterator_fast<Item> it_org(send_result_set_metadata);
List_iterator_fast<Item> it_dst(item_list);
Item *item_org;
Item *item_dst;
@@ -580,7 +240,7 @@ int Materialized_cursor::fill_item_list(THD *thd, List<Item> &send_fields)
if ((rc= table->fill_item_list(&item_list)))
goto end;
- DBUG_ASSERT(send_fields.elements == item_list.elements);
+ DBUG_ASSERT(send_result_set_metadata.elements == item_list.elements);
/*
Unless we preserve the original metadata, it will be lost,
@@ -598,43 +258,49 @@ int Materialized_cursor::fill_item_list(THD *thd, List<Item> &send_fields)
ident->db_name= thd->strdup(send_field.db_name);
ident->table_name= thd->strdup(send_field.table_name);
}
+
+ /*
+ Original metadata result set should be sent here. After
+ mysql_execute_command() is finished, item_list can not be used for
+ sending metadata, because it references closed table.
+ */
+ rc= result->send_result_set_metadata(item_list, Protocol::SEND_NUM_ROWS);
+
end:
thd->restore_active_arena(this, &backup_arena);
/* Check for thd->is_error() in case of OOM */
return rc || thd->is_error();
}
+
int Materialized_cursor::open(JOIN *join __attribute__((unused)))
{
THD *thd= fake_unit.thd;
int rc;
Query_arena backup_arena;
+
thd->set_n_backup_active_arena(this, &backup_arena);
- /* Create a list of fields and start sequential scan */
+
+ /* Create a list of fields and start sequential scan. */
+
rc= result->prepare(item_list, &fake_unit);
- if (!rc && !(rc= table->file->ha_rnd_init_with_error(TRUE)))
- is_rnd_inited= 1;
+ rc= !rc && table->file->ha_rnd_init_with_error(TRUE);
+ is_rnd_inited= !rc;
thd->restore_active_arena(this, &backup_arena);
- if (rc == 0)
+
+ /* Commit or rollback metadata in the client-server protocol. */
+
+ if (!rc)
{
- /*
- Now send the result set metadata to the client. We need to
- do it here, as in Select_materialize::send_fields the items
- for column types are not yet created (send_fields requires
- a list of items). The new types may differ from the original
- ones sent at prepare if some of them were altered by MySQL
- HEAP tables mechanism -- used when create_tmp_field_from_item
- may alter the original column type.
-
- We can't simply supply SEND_EOF flag to send_fields, because
- send_fields doesn't flush the network buffer.
- */
- rc= result->send_fields(item_list, Protocol::SEND_NUM_ROWS);
thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
result->send_eof();
- thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS;
}
+ else
+ {
+ result->abort_result_set();
+ }
+
return rc;
}
@@ -674,12 +340,10 @@ void Materialized_cursor::fetch(ulong num_rows)
case 0:
thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
result->send_eof();
- thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS;
break;
case HA_ERR_END_OF_FILE:
thd->server_status|= SERVER_STATUS_LAST_ROW_SENT;
result->send_eof();
- thd->server_status&= ~SERVER_STATUS_LAST_ROW_SENT;
close();
break;
default:
@@ -719,24 +383,26 @@ Materialized_cursor::~Materialized_cursor()
Select_materialize
****************************************************************************/
-bool Select_materialize::send_fields(List<Item> &list, uint flags)
+bool Select_materialize::send_result_set_metadata(List<Item> &list, uint flags)
{
DBUG_ASSERT(table == 0);
if (create_result_table(unit->thd, unit->get_unit_column_types(),
- FALSE, thd->options | TMP_TABLE_ALL_COLUMNS, "",
- FALSE, TRUE))
- return TRUE;
+ FALSE,
+ thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS,
+ "", FALSE, TRUE, TRUE))
+ return TRUE;
materialized_cursor= new (&table->mem_root)
Materialized_cursor(result, table);
- if (! materialized_cursor)
+ if (!materialized_cursor)
{
free_tmp_table(table->in_use, table);
table= 0;
return TRUE;
}
- if (materialized_cursor->fill_item_list(unit->thd, list))
+
+ if (materialized_cursor->send_result_set_metadata(unit->thd, list))
{
delete materialized_cursor;
table= 0;
diff --git a/sql/sql_cursor.h b/sql/sql_cursor.h
index 2ea7d8539b6..bff47d654b3 100644
--- a/sql/sql_cursor.h
+++ b/sql/sql_cursor.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2005-2007 MySQL AB
+/* Copyright (c) 2005, 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
@@ -11,7 +11,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef _sql_cursor_h_
#define _sql_cursor_h_
@@ -20,6 +20,10 @@
#pragma interface /* gcc class interface */
#endif
+#include "sql_class.h" /* Query_arena */
+
+class JOIN;
+
/**
@file
@@ -28,11 +32,11 @@
*/
/**
- Server_side_cursor -- an interface for materialized and
- sensitive (non-materialized) implementation of cursors. All
- cursors are self-contained (created in their own memory root).
- For that reason they must be deleted only using a pointer to
- Server_side_cursor, not to its base class.
+ Server_side_cursor -- an interface for materialized
+ implementation of cursors. All cursors are self-contained
+ (created in their own memory root). For that reason they must
+ be deleted only using a pointer to Server_side_cursor, not to
+ its base class.
*/
class Server_side_cursor: protected Query_arena, public Sql_alloc
@@ -42,7 +46,7 @@ protected:
select_result *result;
public:
Server_side_cursor(MEM_ROOT *mem_root_arg, select_result *result_arg)
- :Query_arena(mem_root_arg, INITIALIZED), result(result_arg)
+ :Query_arena(mem_root_arg, STMT_INITIALIZED), result(result_arg)
{}
virtual bool is_open() const= 0;
@@ -56,11 +60,7 @@ public:
};
-int mysql_open_cursor(THD *thd, uint flags,
- select_result *result,
+int mysql_open_cursor(THD *thd, select_result *result,
Server_side_cursor **res);
-/** Possible values for flags */
-enum { ANY_CURSOR= 1, ALWAYS_MATERIALIZED_CURSOR= 2 };
-
#endif /* _sql_cusor_h_ */
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 7d96f5a4890..0464128bd97 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2009, 2014, Monty Program Ab.
+ Copyright (c) 2009, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,13 +13,25 @@
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
-*/
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
/* create and drop of databases */
-#include "mysql_priv.h"
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_priv.h"
+#include "unireg.h"
+#include "sql_db.h"
+#include "sql_cache.h" // query_cache_*
+#include "lock.h" // lock_schema_name
+#include "sql_table.h" // build_table_filename,
+ // filename_to_tablename
+#include "sql_rename.h" // mysql_rename_tables
+#include "sql_acl.h" // SELECT_ACL, DB_ACLS,
+ // acl_get, check_grant_db
+#include "log_event.h" // Query_log_event
+#include "sql_base.h" // lock_table_names, tdc_remove_table
+#include "sql_handler.h" // mysql_ha_rm_tables
#include <mysys_err.h>
#include "sp_head.h"
#include "sp.h"
@@ -39,10 +51,12 @@ const char *del_exts[]= {".frm", ".BAK", ".TMD",".opt", NullS};
static TYPELIB deletable_extentions=
{array_elements(del_exts)-1,"del_exts", del_exts, NULL};
-static long mysql_rm_known_files(THD *thd, MY_DIR *dirp,
- const char *db, const char *path, uint level,
- TABLE_LIST **dropped_tables);
-
+static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp,
+ const char *db,
+ const char *path,
+ TABLE_LIST **tables,
+ bool *found_other_files);
+
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,
@@ -51,110 +65,10 @@ static void mysql_change_db_impl(THD *thd,
CHARSET_INFO *new_db_charset);
-/* Database lock hash */
-HASH lock_db_cache;
-pthread_mutex_t LOCK_lock_db;
-int creating_database= 0; // how many database locks are made
-
-
-/* Structure for database lock */
-typedef struct my_dblock_st
-{
- char *name; /* Database name */
- uint name_length; /* Database length name */
-} my_dblock_t;
-
-
-/*
- lock_db key.
-*/
-
-extern "C" uchar* lock_db_get_key(my_dblock_t *, size_t *, my_bool not_used);
-
-uchar* lock_db_get_key(my_dblock_t *ptr, size_t *length,
- my_bool not_used __attribute__((unused)))
-{
- *length= ptr->name_length;
- return (uchar*) ptr->name;
-}
-
-
-/*
- Free lock_db hash element.
-*/
-
-extern "C" void lock_db_free_element(void *ptr);
-
-void lock_db_free_element(void *ptr)
-{
- my_free(ptr, MYF(0));
-}
-
-
-/*
- Put a database lock entry into the hash.
-
- DESCRIPTION
- Insert a database lock entry into hash.
- LOCK_db_lock must be previously locked.
-
- RETURN VALUES
- 0 on success.
- 1 on error.
-*/
-
-static my_bool lock_db_insert(const char *dbname, uint length)
-{
- my_dblock_t *opt;
- my_bool error= 0;
- DBUG_ENTER("lock_db_insert");
-
- safe_mutex_assert_owner(&LOCK_lock_db);
-
- if (!(opt= (my_dblock_t*) hash_search(&lock_db_cache,
- (uchar*) dbname, length)))
- {
- /* Db is not in the hash, insert it */
- char *tmp_name;
- if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
- &opt, (uint) sizeof(*opt), &tmp_name, (uint) length+1,
- NullS))
- {
- error= 1;
- goto end;
- }
-
- opt->name= tmp_name;
- strmov(opt->name, dbname);
- opt->name_length= length;
-
- if ((error= my_hash_insert(&lock_db_cache, (uchar*) opt)))
- my_free(opt, MYF(0));
- }
-
-end:
- DBUG_RETURN(error);
-}
-
-
-/*
- Delete a database lock entry from hash.
-*/
-
-void lock_db_delete(const char *name, uint length)
-{
- my_dblock_t *opt;
- safe_mutex_assert_owner(&LOCK_lock_db);
- if ((opt= (my_dblock_t *)hash_search(&lock_db_cache,
- (const uchar*) name, length)))
- hash_delete(&lock_db_cache, (uchar*) opt);
-}
-
-
/* Database options hash */
static HASH dboptions;
static my_bool dboptions_init= 0;
-static rw_lock_t LOCK_dboptions;
+static mysql_rwlock_t LOCK_dboptions;
/* Structure for database options */
typedef struct my_dbopt_st
@@ -187,7 +101,7 @@ uchar* dboptions_get_key(my_dbopt_t *opt, size_t *length,
static inline int write_to_binlog(THD *thd, char *query, uint q_len,
char *db, uint db_len)
{
- Query_log_event qinfo(thd, query, q_len, 0, 0, 0);
+ Query_log_event qinfo(thd, query, q_len, FALSE, TRUE, FALSE, 0);
qinfo.db= db;
qinfo.db_len= db_len;
return mysql_bin_log.write(&qinfo);
@@ -202,75 +116,88 @@ extern "C" void free_dbopt(void *dbopt);
void free_dbopt(void *dbopt)
{
- my_free((uchar*) dbopt, MYF(0));
+ my_free(dbopt);
}
+#ifdef HAVE_PSI_INTERFACE
+static PSI_rwlock_key key_rwlock_LOCK_dboptions;
-/*
- Initialize database option hash and locked database hash.
+static PSI_rwlock_info all_database_names_rwlocks[]=
+{
+ { &key_rwlock_LOCK_dboptions, "LOCK_dboptions", PSI_FLAG_GLOBAL}
+};
- SYNOPSIS
- my_database_names()
+static void init_database_names_psi_keys(void)
+{
+ const char* category= "sql";
+ int count;
- NOTES
- Must be called before any other database function is called.
+ if (PSI_server == NULL)
+ return;
- RETURN
- 0 ok
- 1 Fatal error
+ count= array_elements(all_database_names_rwlocks);
+ PSI_server->register_rwlock(category, all_database_names_rwlocks, count);
+}
+#endif
+
+/**
+ Initialize database option cache.
+
+ @note Must be called before any other database function is called.
+
+ @retval 0 ok
+ @retval 1 Fatal error
*/
-bool my_database_names_init(void)
+bool my_dboptions_cache_init(void)
{
+#ifdef HAVE_PSI_INTERFACE
+ init_database_names_psi_keys();
+#endif
+
bool error= 0;
- (void) my_rwlock_init(&LOCK_dboptions, NULL);
+ mysql_rwlock_init(key_rwlock_LOCK_dboptions, &LOCK_dboptions);
if (!dboptions_init)
{
dboptions_init= 1;
- error= hash_init(&dboptions, lower_case_table_names ?
- &my_charset_bin : system_charset_info,
- 32, 0, 0, (hash_get_key) dboptions_get_key,
- free_dbopt,0) ||
- hash_init(&lock_db_cache, lower_case_table_names ?
- &my_charset_bin : system_charset_info,
- 32, 0, 0, (hash_get_key) lock_db_get_key,
- lock_db_free_element,0);
-
+ error= my_hash_init(&dboptions, lower_case_table_names ?
+ &my_charset_bin : system_charset_info,
+ 32, 0, 0, (my_hash_get_key) dboptions_get_key,
+ free_dbopt,0);
}
return error;
}
-/*
+/**
Free database option hash and locked databases hash.
*/
-void my_database_names_free(void)
+void my_dboptions_cache_free(void)
{
if (dboptions_init)
{
dboptions_init= 0;
- hash_free(&dboptions);
- (void) rwlock_destroy(&LOCK_dboptions);
- hash_free(&lock_db_cache);
+ my_hash_free(&dboptions);
+ mysql_rwlock_destroy(&LOCK_dboptions);
}
}
-/*
- Cleanup cached options
+/**
+ Cleanup cached options.
*/
void my_dbopt_cleanup(void)
{
- rw_wrlock(&LOCK_dboptions);
- hash_free(&dboptions);
- hash_init(&dboptions, lower_case_table_names ?
- &my_charset_bin : system_charset_info,
- 32, 0, 0, (hash_get_key) dboptions_get_key,
- free_dbopt,0);
- rw_unlock(&LOCK_dboptions);
+ mysql_rwlock_wrlock(&LOCK_dboptions);
+ my_hash_free(&dboptions);
+ my_hash_init(&dboptions, lower_case_table_names ?
+ &my_charset_bin : system_charset_info,
+ 32, 0, 0, (my_hash_get_key) dboptions_get_key,
+ free_dbopt,0);
+ mysql_rwlock_unlock(&LOCK_dboptions);
}
@@ -294,13 +221,13 @@ static my_bool get_dbopt(const char *dbname, HA_CREATE_INFO *create)
length= (uint) strlen(dbname);
- rw_rdlock(&LOCK_dboptions);
- if ((opt= (my_dbopt_t*) hash_search(&dboptions, (uchar*) dbname, length)))
+ mysql_rwlock_rdlock(&LOCK_dboptions);
+ if ((opt= (my_dbopt_t*) my_hash_search(&dboptions, (uchar*) dbname, length)))
{
create->default_table_charset= opt->charset;
error= 0;
}
- rw_unlock(&LOCK_dboptions);
+ mysql_rwlock_unlock(&LOCK_dboptions);
return error;
}
@@ -326,8 +253,9 @@ static my_bool put_dbopt(const char *dbname, HA_CREATE_INFO *create)
length= (uint) strlen(dbname);
- rw_wrlock(&LOCK_dboptions);
- if (!(opt= (my_dbopt_t*) hash_search(&dboptions, (uchar*) dbname, length)))
+ mysql_rwlock_wrlock(&LOCK_dboptions);
+ if (!(opt= (my_dbopt_t*) my_hash_search(&dboptions, (uchar*) dbname,
+ length)))
{
/* Options are not in the hash, insert them */
char *tmp_name;
@@ -345,7 +273,7 @@ static my_bool put_dbopt(const char *dbname, HA_CREATE_INFO *create)
if ((error= my_hash_insert(&dboptions, (uchar*) opt)))
{
- my_free(opt, MYF(0));
+ my_free(opt);
goto end;
}
}
@@ -354,7 +282,7 @@ static my_bool put_dbopt(const char *dbname, HA_CREATE_INFO *create)
opt->charset= create->default_table_charset;
end:
- rw_unlock(&LOCK_dboptions);
+ mysql_rwlock_unlock(&LOCK_dboptions);
DBUG_RETURN(error);
}
@@ -363,14 +291,14 @@ end:
Deletes database options from the hash.
*/
-void del_dbopt(const char *path)
+static void del_dbopt(const char *path)
{
my_dbopt_t *opt;
- rw_wrlock(&LOCK_dboptions);
- if ((opt= (my_dbopt_t *)hash_search(&dboptions, (const uchar*) path,
- strlen(path))))
- hash_delete(&dboptions, (uchar*) opt);
- rw_unlock(&LOCK_dboptions);
+ mysql_rwlock_wrlock(&LOCK_dboptions);
+ if ((opt= (my_dbopt_t *)my_hash_search(&dboptions, (const uchar*) path,
+ strlen(path))))
+ my_hash_delete(&dboptions, (uchar*) opt);
+ mysql_rwlock_unlock(&LOCK_dboptions);
}
@@ -397,7 +325,8 @@ static bool write_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create)
if (put_dbopt(path, create))
return 1;
- if ((file=my_create(path, CREATE_MODE,O_RDWR | O_TRUNC,MYF(MY_WME))) >= 0)
+ if ((file= mysql_file_create(key_file_dbopt, path, CREATE_MODE,
+ O_RDWR | O_TRUNC, MYF(MY_WME))) >= 0)
{
ulong length;
length= (ulong) (strxnmov(buf, sizeof(buf)-1, "default-character-set=",
@@ -406,10 +335,10 @@ static bool write_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create)
create->default_table_charset->name,
"\n", NullS) - buf);
- /* Error is written by my_write */
- if (!my_write(file,(uchar*) buf, length, MYF(MY_NABP+MY_WME)))
+ /* Error is written by mysql_file_write */
+ if (!mysql_file_write(file, (uchar*) buf, length, MYF(MY_NABP+MY_WME)))
error=0;
- my_close(file,MYF(0));
+ mysql_file_close(file, MYF(0));
}
return error;
}
@@ -446,7 +375,8 @@ bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create)
DBUG_RETURN(0);
/* Otherwise, load options from the .opt file */
- if ((file=my_open(path, O_RDONLY | O_SHARE, MYF(0))) < 0)
+ if ((file= mysql_file_open(key_file_dbopt,
+ path, O_RDONLY | O_SHARE, MYF(0))) < 0)
goto err1;
IO_CACHE cache;
@@ -504,7 +434,7 @@ bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create)
end_io_cache(&cache);
err2:
- my_close(file,MYF(0));
+ mysql_file_close(file, MYF(0));
err1:
DBUG_RETURN(error);
}
@@ -623,49 +553,20 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
DBUG_ENTER("mysql_create_db");
/* do not create 'information_schema' db */
- if (is_schema_db(db))
+ if (is_infoschema_db(db))
{
my_error(ER_DB_CREATE_EXISTS, MYF(0), db);
DBUG_RETURN(-1);
}
- /*
- Do not create database if another thread is holding read lock.
- Wait for global read lock before acquiring LOCK_mysql_create_db.
- After wait_if_global_read_lock() we have protection against another
- global read lock. If we would acquire LOCK_mysql_create_db first,
- another thread could step in and get the global read lock before we
- reach wait_if_global_read_lock(). If this thread tries the same as we
- (admin a db), it would then go and wait on LOCK_mysql_create_db...
- Furthermore wait_if_global_read_lock() checks if the current thread
- has the global read lock and refuses the operation with
- ER_CANT_UPDATE_WITH_READLOCK if applicable.
- */
- if (wait_if_global_read_lock(thd, 0, 1))
- {
- error= -1;
- goto exit2;
- }
-
- /*
- Close and mark for re-open all HANDLER tables which are marked for flush
- or which there are pending conflicing locks against. This is needed to
- prevent deadlocks.
- */
- if (thd->handler_tables_hash.records)
- {
- pthread_mutex_lock(&LOCK_open);
- mysql_ha_flush(thd);
- pthread_mutex_unlock(&LOCK_open);
- }
-
- VOID(pthread_mutex_lock(&LOCK_mysql_create_db));
+ if (lock_schema_name(thd, db))
+ DBUG_RETURN(-1);
/* Check directory */
path_len= build_table_filename(path, sizeof(path) - 1, db, "", "", 0);
path[path_len-1]= 0; // Remove last '/' from path
- if (my_stat(path,&stat_info,MYF(0)))
+ if (mysql_file_stat(key_file_misc, path, &stat_info, MYF(0)))
{
if (!(create_options & HA_LEX_CREATE_IF_NOT_EXISTS))
{
@@ -732,7 +633,7 @@ not_silent:
if (mysql_bin_log.is_open())
{
int errcode= query_error_code(thd, TRUE);
- Query_log_event qinfo(thd, query, query_length, 0,
+ Query_log_event qinfo(thd, query, query_length, FALSE, TRUE,
/* suppress_use */ TRUE, errcode);
/*
@@ -755,7 +656,10 @@ not_silent:
qinfo.db = db;
qinfo.db_len = strlen(db);
- /* These DDL methods and logging protected with LOCK_mysql_create_db */
+ /*
+ These DDL methods and logging are protected with the exclusive
+ metadata lock on the schema
+ */
if (mysql_bin_log.write(&qinfo))
{
error= -1;
@@ -766,9 +670,6 @@ not_silent:
}
exit:
- VOID(pthread_mutex_unlock(&LOCK_mysql_create_db));
- start_waiting_global_read_lock(thd);
-exit2:
DBUG_RETURN(error);
}
@@ -782,34 +683,8 @@ bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info)
int error= 0;
DBUG_ENTER("mysql_alter_db");
- /*
- Do not alter database if another thread is holding read lock.
- Wait for global read lock before acquiring LOCK_mysql_create_db.
- After wait_if_global_read_lock() we have protection against another
- global read lock. If we would acquire LOCK_mysql_create_db first,
- another thread could step in and get the global read lock before we
- reach wait_if_global_read_lock(). If this thread tries the same as we
- (admin a db), it would then go and wait on LOCK_mysql_create_db...
- Furthermore wait_if_global_read_lock() checks if the current thread
- has the global read lock and refuses the operation with
- ER_CANT_UPDATE_WITH_READLOCK if applicable.
- */
- if ((error=wait_if_global_read_lock(thd,0,1)))
- goto exit2;
-
- /*
- Close and mark for re-open all HANDLER tables which are marked for flush
- or which there are pending conflicing locks against. This is needed to
- prevent deadlocks.
- */
- if (thd->handler_tables_hash.records)
- {
- pthread_mutex_lock(&LOCK_open);
- mysql_ha_flush(thd);
- pthread_mutex_unlock(&LOCK_open);
- }
-
- VOID(pthread_mutex_lock(&LOCK_mysql_create_db));
+ if (lock_schema_name(thd, db))
+ DBUG_RETURN(TRUE);
/*
Recreate db options file: /dbpath/.db.opt
@@ -836,10 +711,9 @@ bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info)
if (mysql_bin_log.is_open())
{
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query(), thd->query_length(), 0,
- /* suppress_use */ TRUE, 0);
-
+ int errcode= query_error_code(thd, TRUE);
+ Query_log_event qinfo(thd, thd->query(), thd->query_length(), FALSE, TRUE,
+ /* suppress_use */ TRUE, errcode);
/*
Write should use the database being created as the "current
database" and not the threads current database, which is the
@@ -848,78 +722,51 @@ bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info)
qinfo.db = db;
qinfo.db_len = strlen(db);
- /* These DDL methods and logging protected with LOCK_mysql_create_db */
+ /*
+ These DDL methods and logging are protected with the exclusive
+ metadata lock on the schema.
+ */
if ((error= mysql_bin_log.write(&qinfo)))
goto exit;
}
my_ok(thd, result);
exit:
- VOID(pthread_mutex_unlock(&LOCK_mysql_create_db));
- start_waiting_global_read_lock(thd);
-exit2:
DBUG_RETURN(error);
}
-/*
- Drop all tables in a database and the database itself
-
- SYNOPSIS
- mysql_rm_db()
- thd Thread handle
- db Database name in the case given by user
- It's already validated and set to lower case
- (if needed) when we come here
- if_exists Don't give error if database doesn't exists
- silent Don't generate errors
-
- RETURN
- FALSE ok (Database dropped)
- ERROR Error
+/**
+ Drop all tables, routines and events in a database and the database itself.
+
+ @param thd Thread handle
+ @param db Database name in the case given by user
+ It's already validated and set to lower case
+ (if needed) when we come here
+ @param if_exists Don't give error if database doesn't exists
+ @param silent Don't write the statement to the binary log and don't
+ send ok packet to the client
+
+ @retval false OK (Database dropped)
+ @retval true Error
*/
bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
{
- long deleted=0;
- int error= 0;
+ ulong deleted_tables= 0;
+ bool error= true;
char path[FN_REFLEN + 16];
MY_DIR *dirp;
uint length;
- TABLE_LIST* dropped_tables= 0;
+ bool found_other_files= false;
+ TABLE_LIST *tables= NULL;
+ TABLE_LIST *table;
+ Drop_table_error_handler err_handler;
DBUG_ENTER("mysql_rm_db");
- /*
- Do not drop database if another thread is holding read lock.
- Wait for global read lock before acquiring LOCK_mysql_create_db.
- After wait_if_global_read_lock() we have protection against another
- global read lock. If we would acquire LOCK_mysql_create_db first,
- another thread could step in and get the global read lock before we
- reach wait_if_global_read_lock(). If this thread tries the same as we
- (admin a db), it would then go and wait on LOCK_mysql_create_db...
- Furthermore wait_if_global_read_lock() checks if the current thread
- has the global read lock and refuses the operation with
- ER_CANT_UPDATE_WITH_READLOCK if applicable.
- */
- if (wait_if_global_read_lock(thd, 0, 1))
- {
- error= -1;
- goto exit2;
- }
- /*
- Close and mark for re-open all HANDLER tables which are marked for flush
- or which there are pending conflicing locks against. This is needed to
- prevent deadlocks.
- */
- if (thd->handler_tables_hash.records)
- {
- pthread_mutex_lock(&LOCK_open);
- mysql_ha_flush(thd);
- pthread_mutex_unlock(&LOCK_open);
- }
-
- VOID(pthread_mutex_lock(&LOCK_mysql_create_db));
+ if (lock_schema_name(thd, db))
+ DBUG_RETURN(true);
length= build_table_filename(path, sizeof(path) - 1, db, "", "", 0);
strmov(path+length, MY_DB_OPT_FILE); // Append db option file name
@@ -931,21 +778,61 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
{
if (!if_exists)
{
- error= -1;
my_error(ER_DB_DROP_EXISTS, MYF(0), db);
- goto exit;
+ DBUG_RETURN(true);
}
else
+ {
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_DB_DROP_EXISTS, ER(ER_DB_DROP_EXISTS), db);
+ error= false;
+ goto update_binlog;
+ }
}
- else
+
+ if (find_db_tables_and_rm_known_files(thd, dirp, db, path, &tables,
+ &found_other_files))
+ goto exit;
+
+ /*
+ Disable drop of enabled log tables, must be done before name locking.
+ This check is only needed if we are dropping the "mysql" database.
+ */
+ if ((my_strcasecmp(system_charset_info, MYSQL_SCHEMA_NAME.str, db) == 0))
+ {
+ for (table= tables; table; table= table->next_local)
+ {
+ if (check_if_log_table(table->db_length, table->db,
+ table->table_name_length, table->table_name, true))
+ {
+ my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP");
+ goto exit;
+ }
+ }
+ }
+
+ /* Lock all tables and stored routines about to be dropped. */
+ if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout,
+ MYSQL_OPEN_SKIP_TEMPORARY) ||
+ lock_db_routines(thd, db))
+ goto exit;
+
+ /* mysql_ha_rm_tables() requires a non-null TABLE_LIST. */
+ if (tables)
+ mysql_ha_rm_tables(thd, tables);
+
+ for (table= tables; table; table= table->next_local)
{
- pthread_mutex_lock(&LOCK_open);
- remove_db_from_cache(db);
- pthread_mutex_unlock(&LOCK_open);
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name,
+ false);
+ deleted_tables++;
+ }
- error= -1;
+ thd->push_internal_handler(&err_handler);
+ if (!thd->killed &&
+ !(tables &&
+ mysql_rm_table_no_locks(thd, tables, true, false, true, true)))
+ {
/*
We temporarily disable the binary log while dropping the objects
in the database. Since the DROP DATABASE statement is always
@@ -963,23 +850,30 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
ha_drop_database(), since NDB otherwise detects the binary log
as disabled and will not log the drop database statement on any
other connected server.
- */
- if ((deleted= mysql_rm_known_files(thd, dirp, db, path, 0,
- &dropped_tables)) >= 0)
- {
- ha_drop_database(path);
- tmp_disable_binlog(thd);
- query_cache_invalidate1(thd, db);
- (void) sp_drop_db_routines(thd, db); /* @todo Do not ignore errors */
+ */
+
+ ha_drop_database(path);
+ tmp_disable_binlog(thd);
+ query_cache_invalidate1(thd, db);
+ (void) sp_drop_db_routines(thd, db); /* @todo Do not ignore errors */
#ifdef HAVE_EVENT_SCHEDULER
- Events::drop_schema_events(thd, db);
+ Events::drop_schema_events(thd, db);
#endif
- error = 0;
- reenable_binlog(thd);
- }
+ reenable_binlog(thd);
+
+ /*
+ If the directory is a symbolic link, remove the link first, then
+ remove the directory the symbolic link pointed at
+ */
+ if (found_other_files)
+ my_error(ER_DB_DROP_RMDIR, MYF(0), path, EEXIST);
+ else
+ error= rm_dir_w_symlink(path, true);
}
+ thd->pop_internal_handler();
- if (!silent && deleted>=0)
+update_binlog:
+ if (!silent && !error)
{
const char *query;
ulong query_length;
@@ -990,9 +884,9 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
if (mysql_bin_log.is_open())
{
- thd->clear_error();
- Query_log_event qinfo(thd, query, query_length, 0,
- /* suppress_use */ TRUE, 0);
+ int errcode= query_error_code(thd, TRUE);
+ Query_log_event qinfo(thd, query, query_length, FALSE, TRUE,
+ /* suppress_use */ TRUE, errcode);
/*
Write should use the database being created as the "current
database" and not the threads current database, which is the
@@ -1001,19 +895,21 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
qinfo.db = db;
qinfo.db_len = strlen(db);
- /* These DDL methods and logging protected with LOCK_mysql_create_db */
+ /*
+ These DDL methods and logging are protected with the exclusive
+ metadata lock on the schema.
+ */
if (mysql_bin_log.write(&qinfo))
{
- error= -1;
+ error= true;
goto exit;
}
}
thd->clear_error();
thd->server_status|= SERVER_STATUS_DB_DROPPED;
- my_ok(thd, (ulong) deleted);
- thd->server_status&= ~SERVER_STATUS_DB_DROPPED;
+ my_ok(thd, deleted_tables);
}
- else if (mysql_bin_log.is_open())
+ else if (mysql_bin_log.is_open() && !silent)
{
char *query, *query_pos, *query_end, *query_data_start;
TABLE_LIST *tbl;
@@ -1025,19 +921,32 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
query_end= query + MAX_DROP_TABLE_Q_LEN;
db_len= strlen(db);
- for (tbl= dropped_tables; tbl; tbl= tbl->next_local)
+ for (tbl= tables; tbl; tbl= tbl->next_local)
{
uint tbl_name_len;
+ bool exists;
char quoted_name[FN_REFLEN+3];
+ // Only write drop table to the binlog for tables that no longer exist.
+ if (check_if_table_exists(thd, tbl, 0, &exists))
+ {
+ error= true;
+ goto exit;
+ }
+ if (exists)
+ continue;
+
my_snprintf(quoted_name, sizeof(quoted_name), "%`s", tbl->table_name);
tbl_name_len= strlen(quoted_name) + 1; /* +1 for the comma */
if (query_pos + tbl_name_len + 1 >= query_end)
{
- /* These DDL methods and logging protected with LOCK_mysql_create_db */
+ /*
+ These DDL methods and logging are protected with the exclusive
+ metadata lock on the schema.
+ */
if (write_to_binlog(thd, query, query_pos -1 - query, db, db_len))
{
- error= -1;
+ error= true;
goto exit;
}
query_pos= query_data_start;
@@ -1049,10 +958,13 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
if (query_pos != query_data_start)
{
- /* These DDL methods and logging protected with LOCK_mysql_create_db */
+ /*
+ These DDL methods and logging are protected with the exclusive
+ metadata lock on the schema.
+ */
if (write_to_binlog(thd, query, query_pos -1 - query, db, db_len))
{
- error= -1;
+ error= true;
goto exit;
}
}
@@ -1065,33 +977,25 @@ exit:
SELECT DATABASE() in the future). For this we free() thd->db and set
it to 0.
*/
- if (thd->db && !strcmp(thd->db, db))
+ if (thd->db && !strcmp(thd->db, db) && !error)
mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
- VOID(pthread_mutex_unlock(&LOCK_mysql_create_db));
- start_waiting_global_read_lock(thd);
-exit2:
+ my_dirend(dirp);
DBUG_RETURN(error);
}
-/*
- Removes files with known extensions plus all found subdirectories that
- are 2 hex digits (raid directories).
- thd MUST be set when calling this function!
-*/
-static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
- const char *org_path, uint level,
- TABLE_LIST **dropped_tables)
+static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp,
+ const char *db,
+ const char *path,
+ TABLE_LIST **tables,
+ bool *found_other_files)
{
- long deleted=0;
- ulong found_other_files=0;
char filePath[FN_REFLEN];
- TABLE_LIST *tot_list=0, **tot_list_next;
- List<String> raid_dirs;
- DBUG_ENTER("mysql_rm_known_files");
- DBUG_PRINT("enter",("path: %s", org_path));
+ 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));
- tot_list_next= &tot_list;
+ tot_list_next_local= tot_list_next_global= &tot_list;
for (uint idx=0 ;
idx < (uint) dirp->number_off_files && !thd->killed ;
@@ -1106,36 +1010,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
(file->name[1] == '.' && !file->name[2])))
continue;
- /* Check if file is a raid directory */
- if ((my_isdigit(system_charset_info, file->name[0]) ||
- (file->name[0] >= 'a' && file->name[0] <= 'f')) &&
- (my_isdigit(system_charset_info, file->name[1]) ||
- (file->name[1] >= 'a' && file->name[1] <= 'f')) &&
- !file->name[2] && !level)
- {
- char newpath[FN_REFLEN], *copy_of_path;
- MY_DIR *new_dirp;
- String *dir;
- uint length;
-
- strxmov(newpath,org_path,"/",file->name,NullS);
- length= unpack_filename(newpath,newpath);
- if ((new_dirp = my_dir(newpath,MYF(MY_DONT_SORT))))
- {
- DBUG_PRINT("my",("New subdir found: %s", newpath));
- if ((mysql_rm_known_files(thd, new_dirp, NullS, newpath,1,0)) < 0)
- goto err;
- if (!(copy_of_path= (char*) thd->memdup(newpath, length+1)) ||
- !(dir= new (thd->mem_root) String(copy_of_path, length,
- &my_charset_bin)) ||
- raid_dirs.push_back(dir))
- goto err;
- continue;
- }
- found_other_files++;
- continue;
- }
- else if (file->name[0] == 'a' && file->name[1] == 'r' &&
+ if (file->name[0] == 'a' && file->name[1] == 'r' &&
file->name[2] == 'c' && file->name[3] == '\0')
{
/* .frm archive:
@@ -1144,24 +1019,24 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
*/
char newpath[FN_REFLEN];
MY_DIR *new_dirp;
- strxmov(newpath, org_path, "/", "arc", NullS);
+ strxmov(newpath, path, "/", "arc", NullS);
(void) unpack_filename(newpath, newpath);
if ((new_dirp = my_dir(newpath, MYF(MY_DONT_SORT))))
{
DBUG_PRINT("my",("Archive subdir found: %s", newpath));
if ((mysql_rm_arc_files(thd, new_dirp, newpath)) < 0)
- goto err;
+ DBUG_RETURN(true);
continue;
}
- found_other_files++;
+ *found_other_files= true;
continue;
}
if (!(extension= strrchr(file->name, '.')))
extension= strend(file->name);
- if (find_type(extension, &deletable_extentions,1+2) <= 0)
+ if (find_type(extension, &deletable_extentions, FIND_TYPE_NO_PREFIX) <= 0)
{
- if (find_type(extension, ha_known_exts(),1+2) <= 0)
- found_other_files++;
+ if (find_type(extension, ha_known_exts(), FIND_TYPE_NO_PREFIX) <= 0)
+ *found_other_files= true;
continue;
}
/* just for safety we use files_charset_info */
@@ -1177,12 +1052,15 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
strlen(file->name) + 1);
if (!table_list)
- goto err;
+ DBUG_RETURN(true);
table_list->db= (char*) (table_list+1);
- table_list->table_name= strmov(table_list->db, db) + 1;
- VOID(filename_to_tablename(file->name, table_list->table_name,
- MYSQL50_TABLE_NAME_PREFIX_LENGTH +
- strlen(file->name) + 1));
+ table_list->db_length= strmov(table_list->db, db) - table_list->db;
+ table_list->table_name= table_list->db + table_list->db_length + 1;
+ table_list->table_name_length= filename_to_tablename(file->name,
+ table_list->table_name,
+ MYSQL50_TABLE_NAME_PREFIX_LENGTH +
+ strlen(file->name) + 1);
+ table_list->open_type= OT_BASE_ONLY;
/* To be able to correctly look up the table in the table cache. */
if (lower_case_table_names)
@@ -1191,14 +1069,18 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
table_list->alias= table_list->table_name; // If lower_case_table_names=2
table_list->internal_tmp_table= is_prefix(file->name, tmp_file_prefix);
+ table_list->mdl_request.init(MDL_key::TABLE, table_list->db,
+ table_list->table_name, MDL_EXCLUSIVE,
+ MDL_TRANSACTION);
/* Link into list */
- (*tot_list_next)= table_list;
- tot_list_next= &table_list->next_local;
- deleted++;
+ (*tot_list_next_local)= table_list;
+ (*tot_list_next_global)= table_list;
+ tot_list_next_local= &table_list->next_local;
+ tot_list_next_global= &table_list->next_global;
}
else
{
- strxmov(filePath, org_path, "/", file->name, NullS);
+ strxmov(filePath, path, "/", file->name, NullS);
/*
We ignore ENOENT error in order to skip files that was deleted
by concurrently running statement like REAPIR TABLE ...
@@ -1207,60 +1089,12 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
my_errno != ENOENT)
{
my_error(EE_DELETE, MYF(0), filePath, my_errno);
- goto err;
+ DBUG_RETURN(true);
}
}
}
-
- if (thd->killed)
- goto err;
-
- if (tot_list)
- {
- int res= 0;
- Drop_table_error_handler err_handler(thd->get_internal_handler());
-
- thd->push_internal_handler(&err_handler);
- res= mysql_rm_table_part2(thd, tot_list, 1, 0, 1, 1);
- thd->pop_internal_handler();
- if (res)
- goto err;
- }
-
- /* Remove RAID directories */
- {
- List_iterator<String> it(raid_dirs);
- String *dir;
- while ((dir= it++))
- if (rmdir(dir->c_ptr()) < 0)
- found_other_files++;
- }
- my_dirend(dirp);
-
- if (dropped_tables)
- *dropped_tables= tot_list;
-
- /*
- If the directory is a symbolic link, remove the link first, then
- remove the directory the symbolic link pointed at
- */
- if (found_other_files)
- {
- my_error(ER_DB_DROP_RMDIR, MYF(0), org_path, EEXIST);
- DBUG_RETURN(-1);
- }
- else
- {
- /* Don't give errors if we can't delete 'RAID' directory */
- if (rm_dir_w_symlink(org_path, level == 0))
- DBUG_RETURN(-1);
- }
-
- DBUG_RETURN(deleted);
-
-err:
- my_dirend(dirp);
- DBUG_RETURN(-1);
+ *tables= tot_list;
+ DBUG_RETURN(false);
}
@@ -1295,7 +1129,7 @@ static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error)
DBUG_RETURN(1);
if (!error)
{
- if (my_delete(path, MYF(send_error ? MY_WME : 0)))
+ if (mysql_file_delete(key_file_misc, path, MYF(send_error ? MY_WME : 0)))
{
DBUG_RETURN(send_error);
}
@@ -1372,7 +1206,7 @@ long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path)
continue;
}
strxmov(filePath, org_path, "/", file->name, NullS);
- if (my_delete_with_symlink(filePath,MYF(MY_WME)))
+ if (mysql_file_delete_with_symlink(key_file_misc, filePath, MYF(MY_WME)))
{
goto err;
}
@@ -1442,12 +1276,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.
*/
- pthread_mutex_lock(&thd->LOCK_thd_data);
- if (thd->db)
- x_free(thd->db);
- DEBUG_SYNC(thd, "after_freeing_thd_db");
+ thd->set_db(NULL, 0);
thd->reset_db(new_db_name->str, new_db_name->length);
- pthread_mutex_unlock(&thd->LOCK_thd_data);
}
/* 2. Update security context. */
@@ -1615,7 +1445,7 @@ bool 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_schema_db(new_db_name->str, new_db_name->length))
+ if (is_infoschema_db(new_db_name->str, new_db_name->length))
{
/* Switch the current database to INFORMATION_SCHEMA. */
@@ -1651,7 +1481,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
if (check_db_name(&new_db_file_name))
{
my_error(ER_WRONG_DB_NAME, MYF(0), new_db_file_name.str);
- my_free(new_db_file_name.str, MYF(0));
+ my_free(new_db_file_name.str);
if (force_switch)
mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
@@ -1681,7 +1511,7 @@ bool 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(ER_DBACCESS_DENIED_ERROR),
sctx->priv_user, sctx->priv_host, new_db_file_name.str);
- my_free(new_db_file_name.str, MYF(0));
+ my_free(new_db_file_name.str);
DBUG_RETURN(TRUE);
}
#endif
@@ -1698,7 +1528,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
ER_BAD_DB_ERROR, ER(ER_BAD_DB_ERROR),
new_db_file_name.str);
- my_free(new_db_file_name.str, MYF(0));
+ my_free(new_db_file_name.str);
/* Change db to NULL. */
@@ -1713,7 +1543,7 @@ bool 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, MYF(0));
+ my_free(new_db_file_name.str);
/* The operation failed. */
@@ -1771,60 +1601,6 @@ bool mysql_opt_change_db(THD *thd,
}
-static int
-lock_databases(THD *thd, const char *db1, uint length1,
- const char *db2, uint length2)
-{
- pthread_mutex_lock(&LOCK_lock_db);
- while (!thd->killed &&
- (hash_search(&lock_db_cache,(uchar*) db1, length1) ||
- hash_search(&lock_db_cache,(uchar*) db2, length2)))
- {
- wait_for_condition(thd, &LOCK_lock_db, &COND_refresh);
- pthread_mutex_lock(&LOCK_lock_db);
- }
-
- if (thd->killed)
- {
- pthread_mutex_unlock(&LOCK_lock_db);
- return 1;
- }
-
- lock_db_insert(db1, length1);
- lock_db_insert(db2, length2);
- creating_database++;
-
- /*
- Wait if a concurent thread is creating a table at the same time.
- The assumption here is that it will not take too long until
- there is a point in time when a table is not created.
- */
-
- while (!thd->killed && creating_table)
- {
- wait_for_condition(thd, &LOCK_lock_db, &COND_refresh);
- pthread_mutex_lock(&LOCK_lock_db);
- }
-
- if (thd->killed)
- {
- lock_db_delete(db1, length1);
- lock_db_delete(db2, length2);
- creating_database--;
- pthread_mutex_unlock(&LOCK_lock_db);
- pthread_cond_signal(&COND_refresh);
- return(1);
- }
-
- /*
- We can unlock now as the hash will protect against anyone creating a table
- in the databases we are using
- */
- pthread_mutex_unlock(&LOCK_lock_db);
- return 0;
-}
-
-
/**
Upgrade a 5.0 database.
This function is invoked whenever an ALTER DATABASE UPGRADE query is executed:
@@ -1866,8 +1642,8 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db)
new_db.str= old_db->str + MYSQL50_TABLE_NAME_PREFIX_LENGTH;
new_db.length= old_db->length - MYSQL50_TABLE_NAME_PREFIX_LENGTH;
- if (lock_databases(thd, old_db->str, old_db->length,
- new_db.str, new_db.length))
+ /* Lock the old name, the new name will be locked by mysql_create_db().*/
+ if (lock_schema_name(thd, old_db->str))
DBUG_RETURN(1);
/*
@@ -1921,9 +1697,11 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db)
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) ||
+ TL_OPTION_UPDATING, TL_IGNORE,
+ MDL_EXCLUSIVE) ||
!sl->add_table_to_list(thd, new_ident, NULL,
- TL_OPTION_UPDATING, TL_IGNORE))
+ TL_OPTION_UPDATING, TL_IGNORE,
+ MDL_EXCLUSIVE))
{
error= 1;
my_dirend(dirp);
@@ -1949,7 +1727,7 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db)
*/
build_table_filename(path, sizeof(path)-1,
new_db.str,"",MY_DB_OPT_FILE, 0);
- my_delete(path, MYF(MY_WME));
+ mysql_file_delete(key_file_dbopt, path, MYF(MY_WME));
length= build_table_filename(path, sizeof(path)-1, new_db.str, "", "", 0);
if (length && path[length-1] == FN_LIBCHAR)
path[length-1]=0; // remove ending '\'
@@ -2005,15 +1783,15 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db)
old_db->str, "", file->name, 0);
build_table_filename(newname, sizeof(newname)-1,
new_db.str, "", file->name, 0);
- my_rename(oldname, newname, MYF(MY_WME));
+ mysql_file_rename(key_file_misc, oldname, newname, MYF(MY_WME));
}
my_dirend(dirp);
}
/*
Step7: drop the old database.
- remove_db_from_cache(olddb) and query_cache_invalidate(olddb)
- are done inside mysql_rm_db(), no needs to execute them again.
+ query_cache_invalidate(olddb) is done inside mysql_rm_db(), no need
+ to execute them again.
mysql_rm_db() also "unuses" if we drop the current database.
*/
error= mysql_rm_db(thd, old_db->str, 0, 1);
@@ -2023,7 +1801,7 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db)
{
int errcode= query_error_code(thd, TRUE);
Query_log_event qinfo(thd, thd->query(), thd->query_length(),
- 0, TRUE, errcode);
+ FALSE, TRUE, TRUE, errcode);
thd->clear_error();
error|= mysql_bin_log.write(&qinfo);
}
@@ -2033,15 +1811,6 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db)
error|= mysql_change_db(thd, & new_db, FALSE);
exit:
- pthread_mutex_lock(&LOCK_lock_db);
- /* Remove the databases from db lock cache */
- lock_db_delete(old_db->str, old_db->length);
- lock_db_delete(new_db.str, new_db.length);
- creating_database--;
- /* Signal waiting CREATE TABLE's to continue */
- pthread_cond_signal(&COND_refresh);
- pthread_mutex_unlock(&LOCK_lock_db);
-
DBUG_RETURN(error);
}
diff --git a/sql/sql_db.h b/sql/sql_db.h
new file mode 100644
index 00000000000..1f447c11a52
--- /dev/null
+++ b/sql/sql_db.h
@@ -0,0 +1,48 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_DB_INCLUDED
+#define SQL_DB_INCLUDED
+
+#include "hash.h" /* HASH */
+
+class THD;
+typedef struct st_ha_create_information HA_CREATE_INFO;
+
+int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create, bool silent);
+bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create);
+bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent);
+bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db);
+bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name,
+ bool force_switch);
+
+bool mysql_opt_change_db(THD *thd,
+ const LEX_STRING *new_db_name,
+ LEX_STRING *saved_db_name,
+ bool force_switch,
+ bool *cur_db_changed);
+bool my_dboptions_cache_init(void);
+void my_dboptions_cache_free(void);
+bool check_db_dir_existence(const char *db_name);
+bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create);
+bool load_db_opt_by_name(THD *thd, const char *db_name,
+ HA_CREATE_INFO *db_create_info);
+CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name);
+bool my_dbopt_init(void);
+void my_dbopt_cleanup(void);
+
+#define MY_DB_OPT_FILE "db.opt"
+
+#endif /* SQL_DB_INCLUDED */
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 480868241f7..97d3d10c21c 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -12,21 +12,33 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
- Delete of records and truncate of tables.
+ Delete of records tables.
Multi-table deletes were introduced by Monty and Sinisa
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "sql_delete.h"
+#include "sql_cache.h" // query_cache_*
+#include "sql_base.h" // open_temprary_table
+#include "sql_table.h" // build_table_filename
+#include "lock.h" // unlock_table_name
+#include "sql_view.h" // check_key_in_view, mysql_frm_type
+#include "sql_parse.h" // mysql_init_select
+#include "sql_acl.h" // *_ACL
+#include "filesort.h" // filesort
+#include "sql_handler.h" // mysql_ha_rm_tables
#include "sql_select.h"
#include "sp_head.h"
#include "sql_trigger.h"
-#include "sql_handler.h"
-
+#include "transaction.h"
+#include "records.h" // init_read_record,
+#include "sql_derived.h" // mysql_handle_list_of_derived
+ // end_read_record
/**
Implement DELETE SQL word.
@@ -36,8 +48,7 @@
*/
bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
- SQL_I_List<ORDER> *order, ha_rows limit, ulonglong options,
- bool reset_auto_increment)
+ SQL_I_List<ORDER> *order_list, ha_rows limit, ulonglong options)
{
bool will_batch;
int error, loc_error;
@@ -48,19 +59,16 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
bool transactional_table, safe_update, const_cond;
bool const_cond_result;
ha_rows deleted= 0;
- bool triggers_applicable;
+ bool reverse= FALSE;
+ ORDER *order= (ORDER *) ((order_list && order_list->elements) ?
+ order_list->first : NULL);
uint usable_index= MAX_KEY;
SELECT_LEX *select_lex= &thd->lex->select_lex;
killed_state killed_status= NOT_KILLED;
+ THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE;
DBUG_ENTER("mysql_delete");
- bool save_binlog_row_based;
-
- THD::enum_binlog_query_type query_type=
- thd->lex->sql_command == SQLCOM_TRUNCATE ?
- THD::STMT_QUERY_TYPE :
- THD::ROW_QUERY_TYPE;
- if (open_and_lock_tables(thd, table_list))
+ if (open_and_lock_tables(thd, table_list, TRUE, 0))
DBUG_RETURN(TRUE);
if (mysql_handle_list_of_derived(thd->lex, table_list, DT_MERGE_FOR_INSERT))
@@ -91,7 +99,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
thd->lex->current_select->first_cond_optimization= 0;
}
/* check ORDER BY even if it can be ignored */
- if (order && order->elements)
+ if (order)
{
TABLE_LIST tables;
List<Item> fields;
@@ -101,9 +109,9 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
tables.table = table;
tables.alias = table_list->alias;
- if (select_lex->setup_ref_array(thd, order->elements) ||
+ if (select_lex->setup_ref_array(thd, order_list->elements) ||
setup_order(thd, select_lex->ref_pointer_array, &tables,
- fields, all_fields, order->first))
+ fields, all_fields, order))
{
delete select;
free_underlaid_joins(thd, &thd->lex->select_lex);
@@ -112,11 +120,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
}
/* Apply the IN=>EXISTS transformation to all subqueries and optimize them. */
- if (select_lex->optimize_unflattened_subqueries())
+ if (select_lex->optimize_unflattened_subqueries(false))
DBUG_RETURN(TRUE);
const_cond= (!conds || conds->const_item());
- safe_update=test(thd->options & OPTION_SAFE_UPDATES);
+ safe_update=test(thd->variables.option_bits & OPTION_SAFE_UPDATES);
if (safe_update && const_cond)
{
my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
@@ -136,24 +144,19 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
any side-effects (because of triggers), so we can use optimized
handler::delete_all_rows() method.
- We implement fast TRUNCATE for InnoDB even if triggers are
- present. TRUNCATE ignores triggers.
-
We can use delete_all_rows() if and only if:
- We allow new functions (not using option --skip-new), and are
not in safe mode (not using option --safe-mode)
- There is no limit clause
- The condition is constant
- If there is a condition, then it it produces a non-zero value
- - If the current command is DELETE FROM with no where clause
- (i.e., not TRUNCATE) then:
- - We should not be binlogging this statement row-based, and
+ - If the current command is DELETE FROM with no where clause, then:
+ - We should not be binlogging this statement in row-based, and
- there should be no delete triggers associated with the table.
*/
if (!using_limit && const_cond_result &&
- (thd->lex->sql_command == SQLCOM_TRUNCATE ||
- (!thd->current_stmt_binlog_row_based &&
- !(table->triggers && table->triggers->has_delete_triggers()))))
+ (!thd->is_current_stmt_binlog_format_row() &&
+ !(table->triggers && table->triggers->has_delete_triggers())))
{
/* Update the table->file->stats.records number */
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
@@ -166,16 +169,14 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
query in row format, so we have to log it in statement format.
*/
query_type= THD::STMT_QUERY_TYPE;
- error= -1; // ok
+ error= -1;
deleted= maybe_deleted;
- save_binlog_row_based= thd->current_stmt_binlog_row_based;
goto cleanup;
}
if (error != HA_ERR_WRONG_COMMAND)
{
table->file->print_error(error,MYF(0));
error=0;
- save_binlog_row_based= thd->current_stmt_binlog_row_based;
goto cleanup;
}
/* Handler didn't support fast delete; Delete rows one by one */
@@ -192,8 +193,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (prune_partitions(thd, table, conds))
{
free_underlaid_joins(thd, select_lex);
- thd->row_count_func= 0;
- my_ok(thd, (ha_rows) thd->row_count_func); // No matching records
+ // No matching record
+ my_ok(thd, 0);
DBUG_RETURN(0);
}
#endif
@@ -202,6 +203,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
table->covering_keys.clear_all();
table->quick_keys.clear_all(); // Can't use 'only index'
+
select=make_select(table, 0, 0, conds, 0, &error);
if (error)
DBUG_RETURN(TRUE);
@@ -209,7 +211,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
{
delete select;
free_underlaid_joins(thd, select_lex);
- thd->row_count_func= 0;
/*
Error was already created by quick select evaluation (check_quick()).
TODO: Add error code output parameter to Item::val_xxx() methods.
@@ -218,12 +219,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
*/
if (thd->is_error())
DBUG_RETURN(TRUE);
- my_ok(thd, (ha_rows) thd->row_count_func);
- /*
- We don't need to call reset_auto_increment in this case, because
- mysql_truncate always gives a NULL conds argument, hence we never
- get here.
- */
+ my_ok(thd, 0);
DBUG_RETURN(0); // Nothing to delete
}
@@ -243,22 +239,31 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (options & OPTION_QUICK)
(void) table->file->extra(HA_EXTRA_QUICK);
- if (order && order->elements)
+ if (order)
{
uint length= 0;
SORT_FIELD *sortorder;
ha_rows examined_rows;
- if ((!select || table->quick_keys.is_clear_all()) && limit != HA_POS_ERROR)
- usable_index= get_index_for_order(table, order->first, limit);
-
- if (usable_index == MAX_KEY)
+ table->update_const_key_parts(conds);
+ order= simple_remove_const(order, conds);
+
+ bool need_sort;
+ if (select && select->quick && select->quick->unique_key_range())
+ { // Single row select (always "ordered")
+ need_sort= FALSE;
+ usable_index= MAX_KEY;
+ }
+ else
+ usable_index= get_index_for_order(order, table, select, limit,
+ &need_sort, &reverse);
+ if (need_sort)
{
+ DBUG_ASSERT(usable_index == MAX_KEY);
table->sort.io_cache= (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
MYF(MY_FAE | MY_ZEROFILL));
- if (!(sortorder= make_unireg_sortorder(order->first,
- &length, NULL)) ||
+ if (!(sortorder= make_unireg_sortorder(order, &length, NULL)) ||
(table->sort.found_records = filesort(thd, table, sortorder, length,
select, HA_POS_ERROR, 1,
&examined_rows))
@@ -296,17 +301,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
}
}
else
- init_read_record_idx(&info, thd, table, 1, usable_index);
+ init_read_record_idx(&info, thd, table, 1, usable_index, reverse);
init_ftfuncs(thd, select_lex, 1);
thd_proc_info(thd, "updating");
- /* NOTE: TRUNCATE must not invoke triggers. */
-
- triggers_applicable= table->triggers &&
- thd->lex->sql_command != SQLCOM_TRUNCATE;
-
- if (triggers_applicable &&
+ if (table->triggers &&
table->triggers->has_triggers(TRG_EVENT_DELETE,
TRG_ACTION_AFTER))
{
@@ -324,23 +324,18 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
table->mark_columns_needed_for_delete();
- save_binlog_row_based= thd->current_stmt_binlog_row_based;
- if (thd->lex->sql_command == SQLCOM_TRUNCATE &&
- thd->current_stmt_binlog_row_based)
- thd->clear_current_stmt_binlog_row_based();
-
while (!(error=info.read_record(&info)) && !thd->killed &&
! thd->is_error())
{
if (table->vfield)
update_virtual_fields(thd, table,
- triggers_applicable ? VCOL_UPDATE_ALL :
- VCOL_UPDATE_FOR_READ);
+ table->triggers ? VCOL_UPDATE_ALL :
+ VCOL_UPDATE_FOR_READ);
thd->examined_row_count++;
// thd->is_error() is tested to disallow delete row on error
if (!select || select->skip_record(thd) > 0)
{
- if (triggers_applicable &&
+ if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_BEFORE, FALSE))
{
@@ -351,7 +346,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (!(error= table->file->ha_delete_row(table->record[0])))
{
deleted++;
- if (triggers_applicable &&
+ if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_AFTER, FALSE))
{
@@ -375,8 +370,14 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
}
}
}
- else
+ /*
+ 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())
table->file->unlock_row(); // Row failed selection, release lock on it
+ else
+ break;
}
killed_status= thd->killed;
if (killed_status != NOT_KILLED || thd->is_error())
@@ -392,21 +393,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (options & OPTION_QUICK)
(void) table->file->extra(HA_EXTRA_NORMAL);
- if (reset_auto_increment && (error < 0))
- {
- /*
- We're really doing a truncate and need to reset the table's
- auto-increment counter.
- */
- int error2= table->file->ha_reset_auto_increment(0);
-
- if (error2 && (error2 != HA_ERR_WRONG_COMMAND))
- {
- table->file->print_error(error2, MYF(0));
- error= 1;
- }
- }
-
cleanup:
/*
Invalidate the table in the query cache if something changed. This must
@@ -427,21 +413,14 @@ cleanup:
transactional_table= table->file->has_transactions();
if (!transactional_table && deleted > 0)
- thd->transaction.stmt.modified_non_trans_table= TRUE;
+ thd->transaction.stmt.modified_non_trans_table=
+ thd->transaction.all.modified_non_trans_table= TRUE;
/* See similar binlogging code in sql_update.cc, for comments */
if ((error < 0) || thd->transaction.stmt.modified_non_trans_table)
{
- if (mysql_bin_log.is_open() &&
- !(thd->lex->sql_command == SQLCOM_TRUNCATE &&
- thd->current_stmt_binlog_row_based &&
- find_temporary_table(thd, table_list)))
+ if (mysql_bin_log.is_open())
{
- bool const is_trans=
- thd->lex->sql_command == SQLCOM_TRUNCATE ?
- FALSE :
- transactional_table;
-
int errcode= 0;
if (error < 0)
thd->clear_error();
@@ -453,34 +432,24 @@ cleanup:
storage engine does not inject the rows itself, we replicate
statement-based; otherwise, 'ha_delete_row()' was used to
delete specific rows which we might log row-based.
-
- Note that TRUNCATE TABLE is not transactional and should
- therefore be treated as a DDL.
*/
int log_result= thd->binlog_query(query_type,
thd->query(), thd->query_length(),
- is_trans, FALSE, errcode);
+ transactional_table, FALSE, FALSE,
+ errcode);
if (log_result)
{
error=1;
}
}
- if (thd->transaction.stmt.modified_non_trans_table)
- thd->transaction.all.modified_non_trans_table= TRUE;
}
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
DBUG_ASSERT(transactional_table || !deleted || thd->transaction.stmt.modified_non_trans_table);
free_underlaid_joins(thd, select_lex);
if (error < 0 ||
(thd->lex->ignore && !thd->is_error() && !thd->is_fatal_error))
{
- /*
- If a TRUNCATE TABLE was issued, the number of rows should be reported as
- zero since the exact number is unknown.
- */
- thd->row_count_func= reset_auto_increment ? 0 : deleted;
- my_ok(thd, (ha_rows) thd->row_count_func);
+ my_ok(thd, deleted);
DBUG_PRINT("info",("%ld records deleted",(long) deleted));
}
DBUG_RETURN(error >= 0 || thd->is_error());
@@ -507,19 +476,6 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds)
DBUG_ENTER("mysql_prepare_delete");
List<Item> all_fields;
- /*
- Statement-based replication of DELETE ... LIMIT is not safe as order of
- rows is not defined, so in mixed mode we go to row-based.
-
- Note that we may consider a statement as safe if ORDER BY primary_key
- is present. However it may confuse users to see very similiar statements
- replicated differently.
- */
- if (thd->lex->current_select->select_limit)
- {
- thd->lex->set_stmt_unsafe();
- thd->set_current_stmt_binlog_row_based_if_mixed();
- }
thd->lex->allow_sum_func= 0;
if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
&thd->lex->select_lex.top_join_list,
@@ -694,7 +650,7 @@ multi_delete::initialize_tables(JOIN *join)
Unique **tempfiles_ptr;
DBUG_ENTER("initialize_tables");
- if ((thd->options & OPTION_SAFE_UPDATES) && error_if_full_join(join))
+ if ((thd->variables.option_bits & OPTION_SAFE_UPDATES) && error_if_full_join(join))
DBUG_RETURN(1);
table_map tables_to_delete_from=0;
@@ -717,11 +673,12 @@ multi_delete::initialize_tables(JOIN *join)
walk= delete_tables;
- for (JOIN_TAB *tab= first_linear_tab(join, WITH_CONST_TABLES);
+ for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_BUSH_ROOTS,
+ WITH_CONST_TABLES);
tab;
tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS))
{
- if (tab->table->map & tables_to_delete_from)
+ if (!tab->bush_children && tab->table->map & tables_to_delete_from)
{
/* We are going to delete from this table */
TABLE *tbl=walk->table=tab->table;
@@ -873,9 +830,9 @@ void multi_delete::send_error(uint errcode,const char *err)
}
-void multi_delete::abort()
+void multi_delete::abort_result_set()
{
- DBUG_ENTER("multi_delete::abort");
+ DBUG_ENTER("multi_delete::abort_result_set");
/* the error was handled or nothing deleted and no side effects return */
if (error_handled ||
@@ -886,6 +843,9 @@ void multi_delete::abort()
if (deleted)
query_cache_invalidate3(thd, delete_tables, 1);
+ if (thd->transaction.stmt.modified_non_trans_table)
+ thd->transaction.all.modified_non_trans_table= TRUE;
+
/*
If rows from the first table only has been deleted and it is
transactional, just do rollback.
@@ -916,10 +876,9 @@ void multi_delete::abort()
int errcode= query_error_code(thd, thd->killed == NOT_KILLED);
/* possible error of writing binary log is ignored deliberately */
(void) thd->binlog_query(THD::ROW_QUERY_TYPE,
- thd->query(), thd->query_length(),
- transactional_tables, FALSE, errcode);
+ thd->query(), thd->query_length(),
+ transactional_tables, FALSE, FALSE, errcode);
}
- thd->transaction.all.modified_non_trans_table= true;
}
DBUG_VOID_RETURN;
}
@@ -1075,6 +1034,9 @@ bool multi_delete::send_eof()
/* reset used flags */
thd_proc_info(thd, "end");
+ if (thd->transaction.stmt.modified_non_trans_table)
+ thd->transaction.all.modified_non_trans_table= TRUE;
+
/*
We must invalidate the query cache before binlog writing and
ha_autocommit_...
@@ -1094,171 +1056,20 @@ bool multi_delete::send_eof()
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, errcode) &&
+ transactional_tables, FALSE, FALSE, errcode) &&
!normal_tables)
{
local_error=1; // Log write failed: roll back the SQL statement
}
}
- if (thd->transaction.stmt.modified_non_trans_table)
- thd->transaction.all.modified_non_trans_table= TRUE;
}
if (local_error != 0)
error_handled= TRUE; // to force early leave from ::send_error()
if (!local_error)
{
- thd->row_count_func= deleted;
- ::my_ok(thd, (ha_rows) thd->row_count_func);
+ ::my_ok(thd, deleted);
}
return 0;
}
-
-/***************************************************************************
- TRUNCATE TABLE
-****************************************************************************/
-
-/*
- Row-by-row truncation if the engine does not support table recreation.
- Probably a InnoDB table.
-*/
-
-static bool mysql_truncate_by_delete(THD *thd, TABLE_LIST *table_list)
-{
- bool error;
- DBUG_ENTER("mysql_truncate_by_delete");
- table_list->lock_type= TL_WRITE;
- mysql_init_select(thd->lex);
- error= mysql_delete(thd, table_list, NULL, NULL, HA_POS_ERROR, LL(0), TRUE);
- ha_autocommit_or_rollback(thd, error);
- end_trans(thd, error ? ROLLBACK : COMMIT);
- DBUG_RETURN(error);
-}
-
-
-/*
- Optimize delete of all rows by doing a full generate of the table
- This will work even if the .ISM and .ISD tables are destroyed
-
- dont_send_ok should be set if:
- - We should always wants to generate the table (even if the table type
- normally can't safely do this.
- - We don't want an ok to be sent to the end user.
- - We don't want to log the truncate command
- - If we want to have a name lock on the table on exit without errors.
-*/
-
-bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
-{
- HA_CREATE_INFO create_info;
- char path[FN_REFLEN + 1];
- TABLE *table;
- TABLE_LIST *tbl;
- bool error;
- uint path_length;
- bool is_temporary_table= false;
- DBUG_ENTER("mysql_truncate");
-
- bzero((char*) &create_info,sizeof(create_info));
-
- /* Remove tables from the HANDLER's hash. */
- mysql_ha_rm_tables(thd, table_list, FALSE);
-
- /* If it is a temporary table, close and regenerate it */
- if (!dont_send_ok && (table= find_temporary_table(thd, table_list)))
- {
- TABLE_SHARE *share= table->s;
- handlerton *table_type= share->db_type();
- is_temporary_table= true;
-
- if (!ha_check_storage_engine_flag(table_type, HTON_CAN_RECREATE))
- goto trunc_by_del;
-
- for (tbl= table_list; tbl; tbl= tbl->next_local)
- tbl->deleting= TRUE; /* to trigger HA_PREPARE_FOR_DROP */
-
- table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK);
-
- create_info.options|= HA_LEX_CREATE_TMP_TABLE;
- close_temporary_table(thd, table, 0, 0); // Don't free share
- ha_create_table(thd, share->normalized_path.str,
- share->db.str, share->table_name.str, &create_info, 1);
- // We don't need to call invalidate() because this table is not in cache
- if ((error= (int) !(open_temporary_table(thd, share->path.str,
- share->db.str,
- share->table_name.str, 1))))
- (void) rm_temporary_table(table_type, share->path.str);
- else
- thd->thread_specific_used= TRUE;
-
- free_table_share(share);
- my_free((char*) table,MYF(0));
- /*
- If we return here we will not have logged the truncation to the bin log
- and we will not my_ok() to the client.
- */
- goto end;
- }
-
- path_length= build_table_filename(path, sizeof(path) - 1, table_list->db,
- table_list->table_name, reg_ext, 0);
-
- if (!dont_send_ok)
- {
- enum legacy_db_type table_type;
- mysql_frm_type(thd, path, &table_type);
- if (table_type == DB_TYPE_UNKNOWN)
- {
- my_error(ER_NO_SUCH_TABLE, MYF(0),
- table_list->db, table_list->table_name);
- DBUG_RETURN(TRUE);
- }
- if (!ha_check_storage_engine_flag(ha_resolve_by_legacy_type(thd,
- table_type),
- HTON_CAN_RECREATE))
- goto trunc_by_del;
-
- if (lock_and_wait_for_table_name(thd, table_list))
- DBUG_RETURN(TRUE);
- }
-
- /*
- Remove the .frm extension AIX 5.2 64-bit compiler bug (BUG#16155): this
- crashes, replacement works. *(path + path_length - reg_ext_length)=
- '\0';
- */
- path[path_length - reg_ext_length] = 0;
- VOID(pthread_mutex_lock(&LOCK_open));
- error= ha_create_table(thd, path, table_list->db, table_list->table_name,
- &create_info, 1);
- VOID(pthread_mutex_unlock(&LOCK_open));
- query_cache_invalidate3(thd, table_list, 0);
-
-end:
- if (!dont_send_ok)
- {
- if (!error)
- {
- /* In RBR, the statement is not binlogged if the table is temporary. */
- if (!is_temporary_table || !thd->current_stmt_binlog_row_based)
- error= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
- if (!error)
- my_ok(thd); // This should return record count
- }
- VOID(pthread_mutex_lock(&LOCK_open));
- unlock_table_name(thd, table_list);
- VOID(pthread_mutex_unlock(&LOCK_open));
- }
- else if (error)
- {
- VOID(pthread_mutex_lock(&LOCK_open));
- unlock_table_name(thd, table_list);
- VOID(pthread_mutex_unlock(&LOCK_open));
- }
- DBUG_RETURN(error);
-
-trunc_by_del:
- error= mysql_truncate_by_delete(thd, table_list);
- DBUG_RETURN(error);
-}
diff --git a/sql/sql_delete.h b/sql/sql_delete.h
new file mode 100644
index 00000000000..6147e0ea367
--- /dev/null
+++ b/sql/sql_delete.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_DELETE_INCLUDED
+#define SQL_DELETE_INCLUDED
+
+#include "my_base.h" /* ha_rows */
+
+class THD;
+struct TABLE_LIST;
+class Item;
+
+typedef class Item COND;
+template <typename T> class SQL_I_List;
+
+int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds);
+bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
+ SQL_I_List<ORDER> *order, ha_rows rows, ulonglong options);
+
+#endif /* SQL_DELETE_INCLUDED */
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 8e3d182c7b1..004cccb41a9 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -12,8 +12,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
@@ -22,8 +21,14 @@
*/
-#include "mysql_priv.h"
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_priv.h"
+#include "unireg.h"
+#include "sql_derived.h"
#include "sql_select.h"
+#include "sql_base.h"
+#include "sql_view.h" // check_duplicate_names
+#include "sql_acl.h" // SELECT_ACL
typedef bool (*dt_processor)(THD *thd, LEX *lex, TABLE_LIST *derived);
@@ -50,7 +55,6 @@ dt_processor processors[]=
};
/*
- @brief
Run specified phases on all derived tables/views in given LEX.
@param lex LEX for this thread
@@ -129,7 +133,6 @@ mysql_handle_derived(LEX *lex, uint phases)
}
/*
- @brief
Run through phases for the given derived table/view.
@param lex LEX for this thread
@@ -195,7 +198,6 @@ mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived, uint phases)
/**
- @brief
Run specified phases for derived tables/views in the given list
@param lex LEX for this thread
@@ -226,7 +228,6 @@ mysql_handle_list_of_derived(LEX *lex, TABLE_LIST *table_list, uint phases)
/**
- @brief
Merge a derived table/view into the embedding select
@param thd thread handle
@@ -481,7 +482,6 @@ unconditional_materialization:
/**
- @brief
Merge a view for the embedding INSERT/UPDATE/DELETE
@param thd thread handle
@@ -523,7 +523,6 @@ bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived)
/*
- @brief
Initialize a derived table/view
@param thd Thread handle
@@ -555,7 +554,6 @@ bool mysql_derived_init(THD *thd, LEX *lex, TABLE_LIST *derived)
/*
- @brief
Create temporary table structure (but do not fill it)
@param thd Thread handle
@@ -616,6 +614,7 @@ 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", ("unit 0x%lx", (ulong) unit));
// Skip already prepared views/DT
if (!unit || unit->prepared ||
@@ -625,9 +624,6 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
thd->lex->sql_command == SQLCOM_DELETE_MULTI))))
DBUG_RETURN(FALSE);
- Query_arena *arena, backup;
- arena= thd->activate_stmt_arena_if_needed(&backup);
-
SELECT_LEX *first_select= unit->first_select();
/* prevent name resolving out of derived table */
@@ -682,7 +678,7 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
thd->create_tmp_table_for_derived= TRUE;
if (derived->derived_result->create_result_table(thd, &unit->types, FALSE,
(first_select->options |
- thd->options |
+ thd->variables.option_bits |
TMP_TABLE_ALL_COLUMNS),
derived->alias,
FALSE, FALSE))
@@ -701,9 +697,9 @@ exit:
if (derived->view)
{
if (thd->is_error() &&
- (thd->main_da.sql_errno() == ER_BAD_FIELD_ERROR ||
- thd->main_da.sql_errno() == ER_FUNC_INEXISTENT_NAME_COLLISION ||
- thd->main_da.sql_errno() == ER_SP_DOES_NOT_EXIST))
+ (thd->stmt_da->sql_errno() == ER_BAD_FIELD_ERROR ||
+ thd->stmt_da->sql_errno() == ER_FUNC_INEXISTENT_NAME_COLLISION ||
+ thd->stmt_da->sql_errno() == ER_SP_DOES_NOT_EXIST))
{
thd->clear_error();
my_error(ER_VIEW_INVALID, MYF(0), derived->db,
@@ -726,7 +722,7 @@ exit:
{
TABLE *table= derived->table;
table->derived_select_number= first_select->select_number;
- table->s->tmp_table= NON_TRANSACTIONAL_TMP_TABLE;
+ table->s->tmp_table= INTERNAL_TMP_TABLE;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (derived->referencing_view)
table->grant= derived->grant;
@@ -745,14 +741,11 @@ exit:
if (derived->outer_join)
table->maybe_null= 1;
}
- if (arena)
- thd->restore_active_arena(arena, &backup);
DBUG_RETURN(res);
}
/**
- @brief
Runs optimize phase for a derived table/view.
@param thd thread handle
@@ -791,7 +784,7 @@ bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived)
if (!derived->is_merged_derived())
{
JOIN *join= first_select->join;
- unit->set_limit(first_select);
+ unit->set_limit(unit->global_parameters);
unit->optimized= TRUE;
if ((res= join->optimize()))
goto err;
@@ -823,7 +816,6 @@ err:
/**
- @brief
Actually create result table for a materialized derived table/view.
@param thd thread handle
@@ -855,7 +847,7 @@ bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived)
result->tmp_table_param.start_recinfo,
&result->tmp_table_param.recinfo,
(unit->first_select()->options |
- thd->options | TMP_TABLE_ALL_COLUMNS)))
+ thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS)))
DBUG_RETURN(TRUE);
}
if (open_tmp_table(table))
@@ -867,7 +859,6 @@ bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived)
/*
- @brief
Execute subquery of a materialized derived table/view and fill the result
table.
@@ -910,21 +901,21 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
}
else
{
- unit->set_limit(first_select);
+ unit->set_limit(unit->global_parameters);
if (unit->select_limit_cnt == HA_POS_ERROR)
first_select->options&= ~OPTION_FOUND_ROWS;
lex->current_select= first_select;
res= mysql_select(thd, &first_select->ref_pointer_array,
- (TABLE_LIST*) first_select->table_list.first,
+ first_select->table_list.first,
first_select->with_wild,
first_select->item_list, first_select->where,
(first_select->order_list.elements+
first_select->group_list.elements),
- (ORDER *) first_select->order_list.first,
- (ORDER *) first_select->group_list.first,
+ first_select->order_list.first,
+ first_select->group_list.first,
first_select->having, (ORDER*) NULL,
- (first_select->options | thd->options |
+ (first_select->options |thd->variables.option_bits |
SELECT_NO_UNLOCK),
derived_result, unit, first_select);
}
@@ -944,7 +935,6 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
/**
- @brief
Re-initialize given derived table/view for the next execution.
@param thd thread handle
@@ -975,3 +965,4 @@ bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived)
unit->set_thd(thd);
DBUG_RETURN(FALSE);
}
+
diff --git a/sql/sql_derived.h b/sql/sql_derived.h
new file mode 100644
index 00000000000..f232445879e
--- /dev/null
+++ b/sql/sql_derived.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_DERIVED_INCLUDED
+#define SQL_DERIVED_INCLUDED
+
+struct TABLE_LIST;
+class THD;
+struct LEX;
+
+bool mysql_handle_derived(LEX *lex, uint phases);
+bool mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived, uint phases);
+bool mysql_handle_list_of_derived(LEX *lex, TABLE_LIST *dt_list, uint phases);
+
+/**
+ 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);
+
+#endif /* SQL_DERIVED_INCLUDED */
diff --git a/sql/sql_do.cc b/sql/sql_do.cc
index 0378daa567e..4ba887b5ab2 100644
--- a/sql/sql_do.cc
+++ b/sql/sql_do.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000-2006, 2008 MySQL AB
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,12 +11,17 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* Execute DO statement */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "transaction.h"
+#include "unireg.h"
+#include "sql_do.h"
+#include "sql_base.h" // setup_fields
+#include "sql_select.h" // free_underlaid_joins
bool mysql_do(THD *thd, List<Item> &values)
{
@@ -34,9 +39,10 @@ bool mysql_do(THD *thd, List<Item> &values)
/*
Rollback the effect of the statement, since next instruction
will clear the error and the rollback in the end of
- dispatch_command() won't work.
+ mysql_execute_command() won't work.
*/
- ha_autocommit_or_rollback(thd, thd->is_error());
+ if (! thd->in_sub_stmt)
+ trans_rollback_stmt(thd);
thd->clear_error(); // DO always is OK
}
my_ok(thd);
diff --git a/sql/sql_do.h b/sql/sql_do.h
new file mode 100644
index 00000000000..35130cc5836
--- /dev/null
+++ b/sql/sql_do.h
@@ -0,0 +1,26 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_DO_INCLUDED
+#define SQL_DO_INCLUDED
+
+#include "sql_list.h" /* List */
+
+class THD;
+class Item;
+
+bool mysql_do(THD *thd, List<Item> &values);
+
+#endif /* SQL_DO_INCLUDED */
diff --git a/sql/sql_error.cc b/sql/sql_error.cc
index a4c98daac1f..e12723402a8 100644
--- a/sql/sql_error.cc
+++ b/sql/sql_error.cc
@@ -1,6 +1,4 @@
-/*
- Copyright (c) 2002-2008 MySQL AB, 2008, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 1995, 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
@@ -13,8 +11,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
/**********************************************************************
This file contains the implementation of error and warnings related
@@ -44,136 +41,586 @@ This file contains the implementation of error and warnings related
***********************************************************************/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "sql_error.h"
#include "sp_rcontext.h"
/*
- Store a new message in an error object
-
- This is used to in group_concat() to register how many warnings we actually
- got after the query has been executed.
+ Design notes about MYSQL_ERROR::m_message_text.
+
+ The member MYSQL_ERROR::m_message_text contains the text associated with
+ an error, warning or note (which are all SQL 'conditions')
+
+ Producer of MYSQL_ERROR::m_message_text:
+ ----------------------------------------
+
+ (#1) the server implementation itself, when invoking functions like
+ my_error() or push_warning()
+
+ (#2) user code in stored programs, when using the SIGNAL statement.
+
+ (#3) user code in stored programs, when using the RESIGNAL statement.
+
+ When invoking my_error(), the error number and message is typically
+ provided like this:
+ - my_error(ER_WRONG_DB_NAME, MYF(0), ...);
+ - my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
+
+ In both cases, the message is retrieved from ER(ER_XXX), which in turn
+ is read from the resource file errmsg.sys at server startup.
+ The strings stored in the errmsg.sys file are expressed in the character set
+ that corresponds to the server --language start option
+ (see error_message_charset_info).
+
+ When executing:
+ - a SIGNAL statement,
+ - a RESIGNAL statement,
+ the message text is provided by the user logic, and is expressed in UTF8.
+
+ Storage of MYSQL_ERROR::m_message_text:
+ ---------------------------------------
+
+ (#4) The class MYSQL_ERROR is used to hold the message text member.
+ This class represents a single SQL condition.
+
+ (#5) The class Warning_info represents a SQL condition area, and contains
+ a collection of SQL conditions in the Warning_info::m_warn_list
+
+ Consumer of MYSQL_ERROR::m_message_text:
+ ----------------------------------------
+
+ (#6) The statements SHOW WARNINGS and SHOW ERRORS display the content of
+ the warning list.
+
+ (#7) The GET DIAGNOSTICS statement (planned, not implemented yet) will
+ also read the content of:
+ - the top level statement condition area (when executed in a query),
+ - a sub statement (when executed in a stored program)
+ and return the data stored in a MYSQL_ERROR.
+
+ (#8) The RESIGNAL statement reads the MYSQL_ERROR caught by an exception
+ handler, to raise a new or modified condition (in #3).
+
+ The big picture
+ ---------------
+ --------------
+ | ^
+ V |
+ my_error(#1) SIGNAL(#2) RESIGNAL(#3) |
+ |(#A) |(#B) |(#C) |
+ | | | |
+ ----------------------------|---------------------------- |
+ | |
+ V |
+ MYSQL_ERROR(#4) |
+ | |
+ | |
+ V |
+ Warning_info(#5) |
+ | |
+ ----------------------------------------------------- |
+ | | | |
+ | | | |
+ | | | |
+ V V V |
+ SHOW WARNINGS(#6) GET DIAGNOSTICS(#7) RESIGNAL(#8) |
+ | | | | |
+ | -------- | V |
+ | | | --------------
+ V | |
+ Connectors | |
+ | | |
+ -------------------------
+ |
+ V
+ Client application
+
+ Current implementation status
+ -----------------------------
+
+ (#1) (my_error) produces data in the 'error_message_charset_info' CHARSET
+
+ (#2) and (#3) (SIGNAL, RESIGNAL) produces data internally in UTF8
+
+ (#6) (SHOW WARNINGS) produces data in the 'error_message_charset_info' CHARSET
+
+ (#7) (GET DIAGNOSTICS) is not implemented.
+
+ (#8) (RESIGNAL) produces data internally in UTF8 (see #3)
+
+ As a result, the design choice for (#4) and (#5) is to store data in
+ the 'error_message_charset_info' CHARSET, to minimize impact on the code base.
+ This is implemented by using 'String MYSQL_ERROR::m_message_text'.
+
+ The UTF8 -> error_message_charset_info conversion is implemented in
+ Signal_common::eval_signal_informations() (for path #B and #C).
+
+ Future work
+ -----------
+
+ - Change (#1) (my_error) to generate errors in UTF8.
+ See WL#751 (Recoding of error messages)
+
+ - Change (#4 and #5) to store message text in UTF8 natively.
+ In practice, this means changing the type of the message text to
+ '<UTF8 String 128 class> MYSQL_ERROR::m_message_text', and is a direct
+ consequence of WL#751.
+
+ - Implement (#9) (GET DIAGNOSTICS).
+ See WL#2111 (Stored Procedures: Implement GET DIAGNOSTICS)
*/
-void MYSQL_ERROR::set_msg(THD *thd, const char *msg_arg)
+MYSQL_ERROR::MYSQL_ERROR()
+ : 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_handled(0),
+ m_level(MYSQL_ERROR::WARN_LEVEL_ERROR),
+ m_mem_root(NULL)
{
- msg= strdup_root(&thd->warn_root, msg_arg);
+ memset(m_returned_sqlstate, 0, sizeof(m_returned_sqlstate));
}
+void MYSQL_ERROR::init(MEM_ROOT *mem_root)
+{
+ DBUG_ASSERT(mem_root != NULL);
+ DBUG_ASSERT(m_mem_root == NULL);
+ m_mem_root= mem_root;
+}
-/*
- Reset all warnings for the thread
-
- SYNOPSIS
- mysql_reset_errors()
- thd Thread handle
- force Reset warnings even if it has been done before
+void MYSQL_ERROR::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_handled= 0;
+ m_level= MYSQL_ERROR::WARN_LEVEL_ERROR;
+}
- IMPLEMENTATION
- Don't reset warnings if this has already been called for this query.
- This may happen if one gets a warning during the parsing stage,
- in which case push_warnings() has already called this function.
-*/
+MYSQL_ERROR::MYSQL_ERROR(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_handled(0),
+ m_level(MYSQL_ERROR::WARN_LEVEL_ERROR),
+ m_mem_root(mem_root)
+{
+ DBUG_ASSERT(mem_root != NULL);
+ memset(m_returned_sqlstate, 0, sizeof(m_returned_sqlstate));
+}
-void mysql_reset_errors(THD *thd, bool force)
+static void copy_string(MEM_ROOT *mem_root, String* dst, const String* src)
{
- DBUG_ENTER("mysql_reset_errors");
- if (thd->query_id != thd->warn_id || force)
+ size_t len= src->length();
+ if (len)
{
- thd->warn_id= thd->query_id;
- free_root(&thd->warn_root,MYF(0));
- bzero((char*) thd->warn_count, sizeof(thd->warn_count));
- if (force)
- thd->total_warn_count= 0;
- thd->warn_list.empty();
- thd->row_count= 1; // by default point to row 1
+ char* copy= (char*) alloc_root(mem_root, len + 1);
+ if (copy)
+ {
+ memcpy(copy, src->ptr(), len);
+ copy[len]= '\0';
+ dst->set(copy, len, src->charset());
+ }
}
+ else
+ dst->length(0);
+}
+
+void
+MYSQL_ERROR::copy_opt_attributes(const MYSQL_ERROR *cond)
+{
+ DBUG_ASSERT(this != cond);
+ copy_string(m_mem_root, & m_class_origin, & cond->m_class_origin);
+ copy_string(m_mem_root, & m_subclass_origin, & cond->m_subclass_origin);
+ copy_string(m_mem_root, & m_constraint_catalog, & cond->m_constraint_catalog);
+ copy_string(m_mem_root, & m_constraint_schema, & cond->m_constraint_schema);
+ copy_string(m_mem_root, & m_constraint_name, & cond->m_constraint_name);
+ copy_string(m_mem_root, & m_catalog_name, & cond->m_catalog_name);
+ copy_string(m_mem_root, & m_schema_name, & cond->m_schema_name);
+ copy_string(m_mem_root, & m_table_name, & cond->m_table_name);
+ copy_string(m_mem_root, & m_column_name, & cond->m_column_name);
+ copy_string(m_mem_root, & m_cursor_name, & cond->m_cursor_name);
+ m_handled= cond->m_handled;
+}
+
+void
+MYSQL_ERROR::set(uint sql_errno, const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level, const char* msg)
+{
+ 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
+MYSQL_ERROR::set_builtin_message_text(const char* str)
+{
+ /*
+ See the comments
+ "Design notes about MYSQL_ERROR::m_message_text."
+ */
+ const char* copy;
+
+ copy= strdup_root(m_mem_root, str);
+ m_message_text.set(copy, strlen(copy), error_message_charset_info);
+ DBUG_ASSERT(! m_message_text.is_alloced());
+}
+
+const char*
+MYSQL_ERROR::get_message_text() const
+{
+ return m_message_text.ptr();
+}
+
+int
+MYSQL_ERROR::get_message_octet_length() const
+{
+ return m_message_text.length();
+}
+
+void
+MYSQL_ERROR::set_sqlstate(const char* sqlstate)
+{
+ memcpy(m_returned_sqlstate, sqlstate, SQLSTATE_LENGTH);
+ m_returned_sqlstate[SQLSTATE_LENGTH]= '\0';
+}
+
+/**
+ Clear this diagnostics area.
+
+ Normally called at the end of a statement.
+*/
+
+void
+Diagnostics_area::reset_diagnostics_area()
+{
+ DBUG_ENTER("reset_diagnostics_area");
+#ifdef DBUG_OFF
+ can_overwrite_status= FALSE;
+ /** Don't take chances in production */
+ m_message[0]= '\0';
+ m_sql_errno= 0;
+ m_affected_rows= 0;
+ m_last_insert_id= 0;
+ m_statement_warn_count= 0;
+#endif
+ is_sent= FALSE;
+ /** Tiny reset in debug mode to see garbage right away */
+ m_status= DA_EMPTY;
DBUG_VOID_RETURN;
}
-/*
- Push the warning/error to error list if there is still room in the list
+/**
+ Set OK status -- ends commands that do not return a
+ result set, e.g. INSERT/UPDATE/DELETE.
+*/
- SYNOPSIS
- push_warning()
- thd Thread handle
- level Severity of warning (note, warning, error ...)
- code Error number
- msg Clear error message
-
- RETURN
- pointer on MYSQL_ERROR object
+void
+Diagnostics_area::set_ok_status(THD *thd, ulonglong affected_rows_arg,
+ ulonglong last_insert_id_arg,
+ const char *message_arg)
+{
+ DBUG_ENTER("set_ok_status");
+ DBUG_ASSERT(! is_set());
+ /*
+ In production, refuse to overwrite an error or a custom response
+ with an OK packet.
+ */
+ if (is_error() || is_disabled())
+ return;
+
+ m_statement_warn_count= thd->warning_info->statement_warn_count();
+ m_affected_rows= affected_rows_arg;
+ m_last_insert_id= last_insert_id_arg;
+ if (message_arg)
+ strmake_buf(m_message, message_arg);
+ else
+ m_message[0]= '\0';
+ m_status= DA_OK;
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Set EOF status.
*/
-MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
- uint code, const char *msg)
+void
+Diagnostics_area::set_eof_status(THD *thd)
{
- MYSQL_ERROR *err= 0;
- DBUG_ENTER("push_warning");
- DBUG_PRINT("enter", ("code: %d, msg: %s", code, msg));
+ DBUG_ENTER("set_eof_status");
+ /* Only allowed to report eof if has not yet reported an error */
+ DBUG_ASSERT(! is_set());
+ /*
+ In production, refuse to overwrite an error or a custom response
+ with an EOF packet.
+ */
+ if (is_error() || is_disabled())
+ return;
+
+ /*
+ If inside a stored procedure, do not return the total
+ number of warnings, since they are not available to the client
+ anyway.
+ */
+ m_statement_warn_count= (thd->spcont ?
+ 0 : thd->warning_info->statement_warn_count());
+
+ m_status= DA_EOF;
+ DBUG_VOID_RETURN;
+}
- DBUG_ASSERT(code != 0);
- DBUG_ASSERT(msg != NULL);
+/**
+ Set ERROR status.
+*/
- if (level == MYSQL_ERROR::WARN_LEVEL_NOTE &&
- !(thd->options & OPTION_SQL_NOTES))
- DBUG_RETURN(0);
+void
+Diagnostics_area::set_error_status(THD *thd, uint sql_errno_arg,
+ const char *message_arg,
+ const char *sqlstate)
+{
+ DBUG_ENTER("set_error_status");
+ /*
+ Only allowed to report error if has not yet reported a success
+ The only exception is when we flush the message to the client,
+ an error can happen during the flush.
+ */
+ DBUG_ASSERT(! is_set() || can_overwrite_status);
+#ifdef DBUG_OFF
+ /*
+ In production, refuse to overwrite a custom response with an
+ ERROR packet.
+ */
+ if (is_disabled())
+ return;
+#endif
+
+ if (sqlstate == NULL)
+ sqlstate= mysql_errno_to_sqlstate(sql_errno_arg);
+
+ m_sql_errno= sql_errno_arg;
+ memcpy(m_sqlstate, sqlstate, SQLSTATE_LENGTH);
+ m_sqlstate[SQLSTATE_LENGTH]= '\0';
+ strmake_buf(m_message, message_arg);
+
+ m_status= DA_ERROR;
+ DBUG_VOID_RETURN;
+}
- if (thd->query_id != thd->warn_id && !thd->spcont)
- mysql_reset_errors(thd, 0);
- thd->got_warning= 1;
- /* Abort if we are using strict mode and we are not using IGNORE */
- if ((int) level >= (int) MYSQL_ERROR::WARN_LEVEL_WARN &&
- thd->really_abort_on_warning())
- {
- /* Avoid my_message() calling push_warning */
- bool no_warnings_for_error= thd->no_warnings_for_error;
- sp_rcontext *spcont= thd->spcont;
+/**
+ Mark the diagnostics area as 'DISABLED'.
- thd->no_warnings_for_error= 1;
- thd->spcont= NULL;
+ This is used in rare cases when the COM_ command at hand sends a response
+ in a custom format. One example is the query cache, another is
+ COM_STMT_PREPARE.
+*/
- thd->killed= KILL_BAD_DATA;
- my_message(code, msg, MYF(0));
+void
+Diagnostics_area::disable_status()
+{
+ DBUG_ASSERT(! is_set());
+ m_status= DA_DISABLED;
+}
+
+Warning_info::Warning_info(ulonglong warn_id_arg, bool allow_unlimited_warnings)
+ :m_statement_warn_count(0),
+ m_current_row_for_warning(1),
+ m_warn_id(warn_id_arg),
+ m_allow_unlimited_warnings(allow_unlimited_warnings),
+ m_read_only(FALSE)
+{
+ /* Initialize sub structures */
+ init_sql_alloc(&m_warn_root, WARN_ALLOC_BLOCK_SIZE, WARN_ALLOC_PREALLOC_SIZE);
+ m_warn_list.empty();
+ bzero((char*) m_warn_count, sizeof(m_warn_count));
+}
+
+
+Warning_info::~Warning_info()
+{
+ free_root(&m_warn_root,MYF(0));
+}
- thd->spcont= spcont;
- thd->no_warnings_for_error= no_warnings_for_error;
- /* Store error in error list (as my_message() didn't do it) */
- level= MYSQL_ERROR::WARN_LEVEL_ERROR;
- }
- if (thd->handle_error(code, msg, level))
- DBUG_RETURN(NULL);
+/**
+ Reset the warning information of this connection.
+*/
- if (thd->spcont &&
- thd->spcont->handle_error(code, level, thd))
+void Warning_info::clear_warning_info(ulonglong warn_id_arg)
+{
+ m_warn_id= warn_id_arg;
+ free_root(&m_warn_root, MYF(0));
+ bzero((char*) m_warn_count, sizeof(m_warn_count));
+ m_warn_list.empty();
+ m_statement_warn_count= 0;
+ m_current_row_for_warning= 1; /* Start counting from the first row */
+}
+
+/**
+ Append warnings only if the original contents of the routine
+ warning info was replaced.
+*/
+void Warning_info::merge_with_routine_info(THD *thd, Warning_info *source)
+{
+ /*
+ If a routine body is empty or if a routine did not
+ generate any warnings (thus m_warn_id didn't change),
+ do not duplicate our own contents by appending the
+ contents of the called routine. We know that the called
+ routine did not change its warning info.
+
+ On the other hand, if the routine body is not empty and
+ some statement in the routine generates a warning or
+ uses tables, m_warn_id is guaranteed to have changed.
+ In this case we know that the routine warning info
+ contains only new warnings, and thus we perform a copy.
+ */
+ if (m_warn_id != source->m_warn_id)
{
- DBUG_RETURN(NULL);
+ /*
+ If the invocation of the routine was a standalone statement,
+ rather than a sub-statement, in other words, if it's a CALL
+ of a procedure, rather than invocation of a function or a
+ trigger, we need to clear the current contents of the caller's
+ warning info.
+
+ This is per MySQL rules: if a statement generates a warning,
+ warnings from the previous statement are flushed. Normally
+ it's done in push_warning(). However, here we don't use
+ push_warning() to avoid invocation of condition handlers or
+ escalation of warnings to errors.
+ */
+ opt_clear_warning_info(thd->query_id);
+ append_warning_info(thd, source);
}
- query_cache_abort(&thd->net);
+}
+/**
+ Add a warning to the list of warnings. Increment the respective
+ counters.
+*/
+MYSQL_ERROR *Warning_info::push_warning(THD *thd,
+ uint sql_errno, const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char *msg)
+{
+ MYSQL_ERROR *cond= NULL;
- if (thd->warn_list.elements < thd->variables.max_error_count)
+ if (! m_read_only)
{
- /* We have to use warn_root, as mem_root is freed after each query */
- if ((err= new (&thd->warn_root) MYSQL_ERROR(thd, code, level, msg)))
- thd->warn_list.push_back(err, &thd->warn_root);
+ if (m_allow_unlimited_warnings ||
+ m_warn_list.elements < thd->variables.max_error_count)
+ {
+ cond= new (& m_warn_root) MYSQL_ERROR(& m_warn_root);
+ if (cond)
+ {
+ cond->set(sql_errno, sqlstate, level, msg);
+ m_warn_list.push_back(cond, &m_warn_root);
+ }
+ }
+ m_warn_count[(uint) level]++;
}
- thd->warn_count[(uint) level]++;
- thd->total_warn_count++;
+
+ m_statement_warn_count++;
+ return cond;
+}
+
+MYSQL_ERROR *Warning_info::push_warning(THD *thd, const MYSQL_ERROR *sql_condition)
+{
+ MYSQL_ERROR *new_condition= push_warning(thd,
+ sql_condition->get_sql_errno(),
+ sql_condition->get_sqlstate(),
+ sql_condition->get_level(),
+ sql_condition->get_message_text());
+
+ if (new_condition)
+ new_condition->copy_opt_attributes(sql_condition);
+
+ return new_condition;
+}
+
+/*
+ Push the warning to error list if there is still room in the list
+
+ SYNOPSIS
+ push_warning()
+ thd Thread handle
+ level Severity of warning (note, warning)
+ code Error number
+ msg Clear error message
+*/
+
+void push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
+ uint code, const char *msg)
+{
+ DBUG_ENTER("push_warning");
+ DBUG_PRINT("enter", ("code: %d, msg: %s", code, msg));
+
+ /*
+ Calling push_warning/push_warning_printf with a level of
+ WARN_LEVEL_ERROR *is* a bug. Either use my_printf_error(),
+ my_error(), or WARN_LEVEL_WARN.
+ */
+ DBUG_ASSERT(level != MYSQL_ERROR::WARN_LEVEL_ERROR);
+
+ if (level == MYSQL_ERROR::WARN_LEVEL_ERROR)
+ level= MYSQL_ERROR::WARN_LEVEL_WARN;
+
+ (void) thd->raise_condition(code, NULL, level, msg);
+
/* Make sure we also count warnings pushed after calling set_ok_status(). */
- thd->main_da.increment_warning();
- DBUG_RETURN(err);
+ thd->stmt_da->increment_warning();
+
+ DBUG_VOID_RETURN;
}
+
/*
- Push the warning/error to error list if there is still room in the list
+ Push the warning to error list if there is still room in the list
SYNOPSIS
push_warning_printf()
thd Thread handle
- level Severity of warning (note, warning, error ...)
+ level Severity of warning (note, warning)
code Error number
msg Clear error message
*/
@@ -190,7 +637,8 @@ void push_warning_printf(THD *thd, MYSQL_ERROR::enum_warning_level level,
DBUG_ASSERT(format != NULL);
va_start(args,format);
- my_vsnprintf(warning, sizeof(warning), format, args);
+ my_vsnprintf_ex(&my_charset_utf8_general_ci, warning,
+ sizeof(warning), format, args);
va_end(args);
push_warning(thd, level, code, warning);
DBUG_VOID_RETURN;
@@ -222,44 +670,198 @@ const LEX_STRING warning_level_names[]=
};
bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
-{
+{
List<Item> field_list;
DBUG_ENTER("mysqld_show_warnings");
+ DBUG_ASSERT(thd->warning_info->is_read_only());
+
field_list.push_back(new Item_empty_string("Level", 7));
field_list.push_back(new Item_return_int("Code",4, MYSQL_TYPE_LONG));
field_list.push_back(new Item_empty_string("Message",MYSQL_ERRMSG_SIZE));
- if (thd->protocol->send_fields(&field_list,
+ if (thd->protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
MYSQL_ERROR *err;
SELECT_LEX *sel= &thd->lex->select_lex;
SELECT_LEX_UNIT *unit= &thd->lex->unit;
- ha_rows idx= 0;
+ ulonglong idx= 0;
Protocol *protocol=thd->protocol;
unit->set_limit(sel);
- List_iterator_fast<MYSQL_ERROR> it(thd->warn_list);
+ List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
while ((err= it++))
{
/* Skip levels that the user is not interested in */
- if (!(levels_to_show & ((ulong) 1 << err->level)))
+ if (!(levels_to_show & ((ulong) 1 << err->get_level())))
continue;
if (++idx <= unit->offset_limit_cnt)
continue;
if (idx > unit->select_limit_cnt)
break;
protocol->prepare_for_resend();
- protocol->store(warning_level_names[err->level].str,
- warning_level_names[err->level].length, system_charset_info);
- protocol->store((uint32) err->code);
- protocol->store(err->msg, (uint) strlen(err->msg), system_charset_info);
+ protocol->store(warning_level_names[err->get_level()].str,
+ warning_level_names[err->get_level()].length,
+ system_charset_info);
+ protocol->store((uint32) err->get_sql_errno());
+ protocol->store(err->get_message_text(),
+ err->get_message_octet_length(),
+ system_charset_info);
if (protocol->write())
DBUG_RETURN(TRUE);
}
my_eof(thd);
+
+ thd->warning_info->set_read_only(FALSE);
+
DBUG_RETURN(FALSE);
}
+
+
+/**
+ Convert value for dispatch to error message(see WL#751).
+
+ @param to buffer for converted string
+ @param to_length size of the buffer
+ @param from string which should be converted
+ @param from_length string length
+ @param from_cs charset from convert
+
+ @retval
+ result string
+*/
+
+char *err_conv(char *buff, uint to_length, const char *from,
+ uint from_length, CHARSET_INFO *from_cs)
+{
+ char *to= buff;
+ const char *from_start= from;
+ size_t res;
+
+ DBUG_ASSERT(to_length > 0);
+ to_length--;
+ if (from_cs == &my_charset_bin)
+ {
+ uchar char_code;
+ res= 0;
+ while (1)
+ {
+ if ((uint)(from - from_start) >= from_length ||
+ res >= to_length)
+ {
+ *to= 0;
+ break;
+ }
+
+ char_code= ((uchar) *from);
+ if (char_code >= 0x20 && char_code <= 0x7E)
+ {
+ *to++= char_code;
+ from++;
+ res++;
+ }
+ else
+ {
+ if (res + 4 >= to_length)
+ {
+ *to= 0;
+ break;
+ }
+ res+= my_snprintf(to, 5, "\\x%02X", (uint) char_code);
+ to+=4;
+ from++;
+ }
+ }
+ }
+ else
+ {
+ uint errors;
+ res= copy_and_convert(to, to_length, system_charset_info,
+ from, from_length, from_cs, &errors);
+ to[res]= 0;
+ }
+ return buff;
+}
+
+
+/**
+ Convert string for dispatch to client(see WL#751).
+
+ @param to buffer to convert
+ @param to_length buffer length
+ @param to_cs chraset to convert
+ @param from string from convert
+ @param from_length string length
+ @param from_cs charset from convert
+ @param errors count of errors during convertion
+
+ @retval
+ length of converted string
+*/
+
+uint32 convert_error_message(char *to, uint32 to_length, CHARSET_INFO *to_cs,
+ const char *from, uint32 from_length,
+ CHARSET_INFO *from_cs, uint *errors)
+{
+ int cnvres;
+ my_wc_t wc;
+ const uchar *from_end= (const uchar*) from+from_length;
+ char *to_start= to;
+ uchar *to_end;
+ my_charset_conv_mb_wc mb_wc= from_cs->cset->mb_wc;
+ my_charset_conv_wc_mb wc_mb;
+ uint error_count= 0;
+ uint length;
+
+ DBUG_ASSERT(to_length > 0);
+ /* Make room for the null terminator. */
+ to_length--;
+ to_end= (uchar*) (to + to_length);
+
+ if (!to_cs || from_cs == to_cs || to_cs == &my_charset_bin)
+ {
+ length= min(to_length, from_length);
+ memmove(to, from, length);
+ to[length]= 0;
+ return length;
+ }
+
+ wc_mb= to_cs->cset->wc_mb;
+ while (1)
+ {
+ if ((cnvres= (*mb_wc)(from_cs, &wc, (uchar*) from, from_end)) > 0)
+ {
+ if (!wc)
+ break;
+ from+= cnvres;
+ }
+ else if (cnvres == MY_CS_ILSEQ)
+ {
+ wc= (ulong) (uchar) *from;
+ from+=1;
+ }
+ else
+ break;
+
+ if ((cnvres= (*wc_mb)(to_cs, wc, (uchar*) to, to_end)) > 0)
+ to+= cnvres;
+ else if (cnvres == MY_CS_ILUNI)
+ {
+ length= (wc <= 0xFFFF) ? 6/* '\1234' format*/ : 9 /* '\+123456' format*/;
+ if ((uchar*)(to + length) >= to_end)
+ break;
+ cnvres= my_snprintf(to, 9,
+ (wc <= 0xFFFF) ? "\\%04X" : "\\+%06X", (uint) wc);
+ to+= cnvres;
+ }
+ else
+ break;
+ }
+
+ *to= 0;
+ *errors= error_count;
+ return (uint32) (to - to_start);
+}
diff --git a/sql/sql_error.h b/sql/sql_error.h
index 8e8d4e3e075..5f912ad8102 100644
--- a/sql/sql_error.h
+++ b/sql/sql_error.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000-2003, 2005-2007 MySQL AB
+/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,33 +11,597 @@
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 */
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
-class MYSQL_ERROR: public Sql_alloc
+#ifndef SQL_ERROR_H
+#define SQL_ERROR_H
+
+#include "sql_list.h" /* Sql_alloc, MEM_ROOT */
+#include "m_string.h" /* LEX_STRING */
+#include "sql_string.h" /* String */
+#include "mysql_com.h" /* MYSQL_ERRMSG_SIZE */
+
+class THD;
+
+/**
+ Stores status of the currently executed statement.
+ Cleared at the beginning of the statement, and then
+ can hold either OK, ERROR, or EOF status.
+ Can not be assigned twice per statement.
+*/
+
+class Diagnostics_area
{
public:
+ enum enum_diagnostics_status
+ {
+ /** The area is cleared at start of a statement. */
+ DA_EMPTY= 0,
+ /** Set whenever one calls my_ok(). */
+ DA_OK,
+ /** Set whenever one calls my_eof(). */
+ DA_EOF,
+ /** Set whenever one calls my_error() or my_message(). */
+ DA_ERROR,
+ /** Set in case of a custom response, such as one from COM_STMT_PREPARE. */
+ DA_DISABLED
+ };
+ /** True if status information is sent to the client. */
+ bool is_sent;
+ /** Set to make set_error_status after set_{ok,eof}_status possible. */
+ bool can_overwrite_status;
+
+ void set_ok_status(THD *thd, ulonglong affected_rows_arg,
+ ulonglong last_insert_id_arg,
+ const char *message);
+ void set_eof_status(THD *thd);
+ void set_error_status(THD *thd, uint sql_errno_arg, const char *message_arg,
+ const char *sqlstate);
+
+ void disable_status();
+
+ void reset_diagnostics_area();
+
+ bool is_set() const { return m_status != DA_EMPTY; }
+ bool is_error() const { return m_status == DA_ERROR; }
+ bool is_eof() const { return m_status == DA_EOF; }
+ bool is_ok() const { return m_status == DA_OK; }
+ bool is_disabled() const { return m_status == DA_DISABLED; }
+ enum_diagnostics_status status() const { return m_status; }
+
+ const char *message() const
+ { DBUG_ASSERT(m_status == DA_ERROR || m_status == DA_OK); return m_message; }
+
+ uint sql_errno() const
+ { DBUG_ASSERT(m_status == DA_ERROR); return m_sql_errno; }
+
+ const char* get_sqlstate() const
+ { DBUG_ASSERT(m_status == DA_ERROR); return m_sqlstate; }
+
+ ulonglong affected_rows() const
+ { DBUG_ASSERT(m_status == DA_OK); return m_affected_rows; }
+
+ ulonglong last_insert_id() const
+ { DBUG_ASSERT(m_status == DA_OK); return m_last_insert_id; }
+
+ uint statement_warn_count() const
+ {
+ DBUG_ASSERT(m_status == DA_OK || m_status == DA_EOF);
+ return m_statement_warn_count;
+ }
+
+ /* Used to count any warnings pushed after calling set_ok_status(). */
+ void increment_warning()
+ {
+ if (m_status != DA_EMPTY)
+ m_statement_warn_count++;
+ }
+
+ Diagnostics_area() { reset_diagnostics_area(); }
+
+private:
+ /** Message buffer. Can be used by OK or ERROR status. */
+ char m_message[MYSQL_ERRMSG_SIZE];
+ /**
+ SQL error number. One of ER_ codes from share/errmsg.txt.
+ Set by set_error_status.
+ */
+ uint m_sql_errno;
+
+ char m_sqlstate[SQLSTATE_LENGTH+1];
+
+ /**
+ The number of rows affected by the last statement. This is
+ semantically close to thd->row_count_func, but has a different
+ life cycle. thd->row_count_func stores the value returned by
+ function ROW_COUNT() and is cleared only by statements that
+ update its value, such as INSERT, UPDATE, DELETE and few others.
+ This member is cleared at the beginning of the next statement.
+
+ We could possibly merge the two, but life cycle of thd->row_count_func
+ can not be changed.
+ */
+ ulonglong m_affected_rows;
+ /**
+ Similarly to the previous member, this is a replacement of
+ thd->first_successful_insert_id_in_prev_stmt, which is used
+ to implement LAST_INSERT_ID().
+ */
+ ulonglong m_last_insert_id;
+ /**
+ Number of warnings of this last statement. May differ from
+ the number of warnings returned by SHOW WARNINGS e.g. in case
+ the statement doesn't clear the warnings, and doesn't generate
+ them.
+ */
+ uint m_statement_warn_count;
+ enum_diagnostics_status m_status;
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ Representation of a SQL condition.
+ A SQL condition can be a completion condition (note, warning),
+ or an exception condition (error, not found).
+ @note This class is named MYSQL_ERROR instead of SQL_condition for
+ historical reasons, to facilitate merging code with previous releases.
+*/
+class MYSQL_ERROR : public Sql_alloc
+{
+public:
+ /*
+ Enumeration value describing the severity of the error.
+
+ Note that these enumeration values must correspond to the indices
+ of the sql_print_message_handlers array.
+ */
enum enum_warning_level
{ WARN_LEVEL_NOTE, WARN_LEVEL_WARN, WARN_LEVEL_ERROR, WARN_LEVEL_END};
+ /**
+ Get the MESSAGE_TEXT of this condition.
+ @return the message text.
+ */
+ const char* get_message_text() const;
+
+ /**
+ Get the MESSAGE_OCTET_LENGTH of this condition.
+ @return the length in bytes of the message text.
+ */
+ 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.
+ */
+ MYSQL_ERROR::enum_warning_level get_level() const
+ { return m_level; }
+
+ /** check if condition was handled by a condition handler */
+ bool handled() const
+ {
+ return m_handled;
+ }
+ /** mark that condition was handled */
+ void mark_handled()
+ {
+ m_handled= 1;
+ }
+
+private:
+ /*
+ The interface of MYSQL_ERROR is mostly private, by design,
+ so that only the following code:
+ - various raise_error() or raise_warning() methods in class THD,
+ - the implementation of SIGNAL / RESIGNAL
+ - catch / re-throw of SQL conditions in stored procedures (sp_rcontext)
+ is allowed to create / modify a SQL condition.
+ Enforcing this policy prevents confusion, since the only public
+ interface available to the rest of the server implementation
+ is the interface offered by the THD methods (THD::raise_error()),
+ which should be used.
+ */
+ friend class THD;
+ friend class Warning_info;
+ friend class Signal_common;
+ friend class Signal_statement;
+ friend class Resignal_statement;
+ friend class sp_rcontext;
+
+ /**
+ Default constructor.
+ This constructor is usefull when allocating arrays.
+ Note that the init() method should be called to complete the MYSQL_ERROR.
+ */
+ MYSQL_ERROR();
+
+ /**
+ Complete the MYSQL_ERROR initialisation.
+ @param mem_root The memory root to use for the condition items
+ of this condition
+ */
+ void init(MEM_ROOT *mem_root);
+
+ /**
+ Constructor.
+ @param mem_root The memory root to use for the condition items
+ of this condition
+ */
+ MYSQL_ERROR(MEM_ROOT *mem_root);
+
+ /** Destructor. */
+ ~MYSQL_ERROR()
+ {}
+
+ /**
+ Copy optional condition items attributes.
+ @param cond the condition to copy.
+ */
+ void copy_opt_attributes(const MYSQL_ERROR *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,
+ MYSQL_ERROR::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);
+
+ /**
+ Clear this SQL condition.
+ */
+ void clear();
+
+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;
+
+ /** Marker if error/warning was handled by a continue handler */
+ bool m_handled;
+
+ /**
+ 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. */
+ MYSQL_ERROR::enum_warning_level m_level;
+
+ /** Memory root to use to hold condition item values. */
+ MEM_ROOT *m_mem_root;
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ Information about warnings of the current connection.
+*/
+
+class Warning_info
+{
+ /** A memory root to allocate warnings and errors */
+ MEM_ROOT m_warn_root;
+
+ /** List of warnings of all severities (levels). */
+ List <MYSQL_ERROR> m_warn_list;
+
+ /** A break down of the number of warnings per severity (level). */
+ uint m_warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END];
+
+ /**
+ The number of warnings of the current statement. Warning_info
+ life cycle differs from statement life cycle -- it may span
+ multiple statements. In that case we get
+ m_statement_warn_count 0, whereas m_warn_list is not empty.
+ */
+ uint m_statement_warn_count;
+
+ /*
+ Row counter, to print in errors and warnings. Not increased in
+ create_sort_index(); may differ from examined_row_count.
+ */
+ ulong m_current_row_for_warning;
+
+ /** Used to optionally clear warnings only once per statement. */
+ ulonglong m_warn_id;
+
+ /** Indicates if push_warning() allows unlimited number of warnings. */
+ bool m_allow_unlimited_warnings;
+
+private:
+ Warning_info(const Warning_info &rhs); /* Not implemented */
+ Warning_info& operator=(const Warning_info &rhs); /* Not implemented */
+public:
+
+ Warning_info(ulonglong warn_id_arg, bool allow_unlimited_warnings);
+ ~Warning_info();
+
+ /**
+ Reset the warning information. Clear all warnings,
+ the number of warnings, reset current row counter
+ to point to the first row.
+ */
+ void clear_warning_info(ulonglong warn_id_arg);
+ /**
+ Only clear warning info if haven't yet done that already
+ for the current query. Allows to be issued at any time
+ during the query, without risk of clearing some warnings
+ that have been generated by the current statement.
+
+ @todo: This is a sign of sloppy coding. Instead we need to
+ designate one place in a statement life cycle where we call
+ clear_warning_info().
+ */
+ void opt_clear_warning_info(ulonglong query_id)
+ {
+ if (query_id != m_warn_id)
+ clear_warning_info(query_id);
+ }
+
+ void append_warning_info(THD *thd, Warning_info *source)
+ {
+ append_warnings(thd, & source->warn_list());
+ }
+
+ /**
+ Concatenate the list of warnings.
+ It's considered tolerable to lose a warning.
+ */
+ void append_warnings(THD *thd, List<MYSQL_ERROR> *src)
+ {
+ MYSQL_ERROR *err;
+ List_iterator_fast<MYSQL_ERROR> it(*src);
+ /*
+ Don't use ::push_warning() to avoid invocation of condition
+ handlers or escalation of warnings to errors.
+ */
+ while ((err= it++))
+ Warning_info::push_warning(thd, err);
+ }
+
+ /**
+ Conditional merge of related warning information areas.
+ */
+ void merge_with_routine_info(THD *thd, Warning_info *source);
+
+ /**
+ Reset between two COM_ commands. Warnings are preserved
+ between commands, but statement_warn_count indicates
+ the number of warnings of this particular statement only.
+ */
+ void reset_for_next_command() { m_statement_warn_count= 0; }
+
+ /**
+ Used for @@warning_count system variable, which prints
+ the number of rows returned by SHOW WARNINGS.
+ */
+ ulong warn_count() const
+ {
+ /*
+ This may be higher than warn_list.elements if we have
+ had more warnings than thd->variables.max_error_count.
+ */
+ return (m_warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_NOTE] +
+ m_warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_ERROR] +
+ m_warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_WARN]);
+ }
+
+ /**
+ This is for iteration purposes. We return a non-constant reference
+ since List doesn't have constant iterators.
+ */
+ List<MYSQL_ERROR> &warn_list() { return m_warn_list; }
+
+ /**
+ The number of errors, or number of rows returned by SHOW ERRORS,
+ also the value of session variable @@error_count.
+ */
+ ulong error_count() const
+ {
+ return m_warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_ERROR];
+ }
+
+ /** Id of the warning information area. */
+ ulonglong warn_id() const { return m_warn_id; }
+
+ /** Do we have any errors and warnings that we can *show*? */
+ bool is_empty() const { return m_warn_list.elements == 0; }
+
+ /** Increment the current row counter to point at the next row. */
+ void inc_current_row_for_warning() { m_current_row_for_warning++; }
+ /** Reset the current row counter. Start counting from the first row. */
+ void reset_current_row_for_warning() { m_current_row_for_warning= 1; }
+ /** Return the current counter value. */
+ ulong current_row_for_warning() const { return m_current_row_for_warning; }
+
+ ulong statement_warn_count() const { return m_statement_warn_count; }
+
+ /** Add a new condition to the current list. */
+ MYSQL_ERROR *push_warning(THD *thd,
+ uint sql_errno, const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg);
+
+ /** Add a new condition to the current list. */
+ MYSQL_ERROR *push_warning(THD *thd, const MYSQL_ERROR *sql_condition);
- uint code;
- enum_warning_level level;
- char *msg;
-
- MYSQL_ERROR(THD *thd, uint code_arg, enum_warning_level level_arg,
- const char *msg_arg)
- :code(code_arg), level(level_arg)
+ /**
+ Set the read only status for this statement area.
+ This is a privileged operation, reserved for the implementation of
+ diagnostics related statements, to enforce that the statement area is
+ left untouched during execution.
+ The diagnostics statements are:
+ - SHOW WARNINGS
+ - SHOW ERRORS
+ - GET DIAGNOSTICS
+ @param read_only the read only property to set
+ */
+ void set_read_only(bool read_only)
+ { m_read_only= read_only; }
+
+ /**
+ Read only status.
+ @return the read only property
+ */
+ bool is_read_only() const
+ { return m_read_only; }
+
+private:
+ /** Read only status. */
+ bool m_read_only;
+
+ friend class Resignal_statement;
+};
+
+extern char *err_conv(char *buff, uint to_length, const char *from,
+ uint from_length, CHARSET_INFO *from_cs);
+
+class ErrConv
+{
+protected:
+ mutable char err_buffer[MYSQL_ERRMSG_SIZE];
+public:
+ ErrConv() {}
+ virtual ~ErrConv() {}
+ virtual const char *ptr() const = 0;
+};
+
+class ErrConvString : public ErrConv
+{
+ const char *str;
+ size_t len;
+ CHARSET_INFO *cs;
+public:
+ ErrConvString(const char *str_arg, size_t len_arg, CHARSET_INFO *cs_arg)
+ : ErrConv(), str(str_arg), len(len_arg), cs(cs_arg) {}
+ ErrConvString(String *s)
+ : ErrConv(), str(s->ptr()), len(s->length()), cs(s->charset()) {}
+ const char *ptr() const
+ { return err_conv(err_buffer, sizeof(err_buffer), str, len, cs); }
+};
+
+class ErrConvInteger : public ErrConv
+{
+ longlong m_value;
+ bool m_unsigned;
+public:
+ ErrConvInteger(longlong num_arg, bool unsigned_flag= false) :
+ ErrConv(), m_value(num_arg), m_unsigned(unsigned_flag) {}
+ const char *ptr() const
{
- if (msg_arg)
- set_msg(thd, msg_arg);
+ return m_unsigned ? ullstr(m_value, err_buffer) :
+ llstr(m_value, err_buffer);
}
- void set_msg(THD *thd, const char *msg_arg);
};
-MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
- uint code, const char *msg);
+class ErrConvDouble: public ErrConv
+{
+ double num;
+public:
+ ErrConvDouble(double num_arg) : ErrConv(), num(num_arg) {}
+ const char *ptr() const
+ {
+ my_gcvt(num, MY_GCVT_ARG_DOUBLE, sizeof(err_buffer), err_buffer, 0);
+ return err_buffer;
+ }
+};
+
+class ErrConvTime : public ErrConv
+{
+ const MYSQL_TIME *ltime;
+public:
+ ErrConvTime(const MYSQL_TIME *ltime_arg) : ErrConv(), ltime(ltime_arg) {}
+ const char *ptr() const
+ {
+ my_TIME_to_str(ltime, err_buffer, AUTO_SEC_PART_DIGITS);
+ return err_buffer;
+ }
+};
+
+class ErrConvDecimal : public ErrConv
+{
+ const decimal_t *d;
+public:
+ ErrConvDecimal(const decimal_t *d_arg) : ErrConv(), d(d_arg) {}
+ const char *ptr() const
+ {
+ int len= sizeof(err_buffer);
+ decimal2string(d, err_buffer, &len, 0, 0, ' ');
+ return err_buffer;
+ }
+};
+
+void push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
+ uint code, const char *msg);
void push_warning_printf(THD *thd, MYSQL_ERROR::enum_warning_level level,
uint code, const char *format, ...);
-void mysql_reset_errors(THD *thd, bool force);
bool mysqld_show_warnings(THD *thd, ulong levels_to_show);
+uint32 convert_error_message(char *to, uint32 to_length, CHARSET_INFO *to_cs,
+ const char *from, uint32 from_length,
+ CHARSET_INFO *from_cs, uint *errors);
extern const LEX_STRING warning_level_names[];
+
+#endif // SQL_ERROR_H
diff --git a/sql/sql_expression_cache.cc b/sql/sql_expression_cache.cc
index 4174f72f080..f3c96ee2d2f 100644
--- a/sql/sql_expression_cache.cc
+++ b/sql/sql_expression_cache.cc
@@ -13,8 +13,9 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#include "mysql_priv.h"
+#include "sql_base.h"
#include "sql_select.h"
+#include "sql_expression_cache.h"
/**
Minimum hit ration to proceed on disk if in memory table overflowed.
@@ -55,6 +56,8 @@ Expression_cache_tmptable::Expression_cache_tmptable(THD *thd,
void Expression_cache_tmptable::disable_cache()
{
+ if (cache_table->file->inited)
+ cache_table->file->ha_index_end();
free_tmp_table(table_thd, cache_table);
cache_table= NULL;
}
@@ -113,10 +116,9 @@ void Expression_cache_tmptable::init()
if (!(cache_table= create_tmp_table(table_thd, &cache_table_param,
items, (ORDER*) NULL,
FALSE, TRUE,
- ((table_thd->options |
+ ((table_thd->variables.option_bits |
TMP_TABLE_ALL_COLUMNS) &
- ~(OPTION_BIG_TABLES |
- TMP_TABLE_FORCE_MYISAM)),
+ ~TMP_TABLE_FORCE_MYISAM),
HA_POS_ERROR,
(char *)"subquery-cache-table",
TRUE)))
@@ -286,7 +288,7 @@ my_bool Expression_cache_tmptable::put_value(Item *value)
if (create_internal_tmp_table_from_heap(table_thd, cache_table,
cache_table_param.start_recinfo,
&cache_table_param.recinfo,
- error, 1))
+ error, 1, NULL))
goto err;
}
}
diff --git a/sql/sql_expression_cache.h b/sql/sql_expression_cache.h
index 32aecc61dc9..48a8e33a787 100644
--- a/sql/sql_expression_cache.h
+++ b/sql/sql_expression_cache.h
@@ -1,3 +1,19 @@
+/*
+ Copyright (c) 2010, 2011, Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
#ifndef SQL_EXPRESSION_CACHE_INCLUDED
#define SQL_EXPRESSION_CACHE_INCLUDED
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 47d78abdb68..c099a2af496 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -1,5 +1,6 @@
-/*
- Copyright (c) 2001, 2010, Oracle and/or its affiliates.
+/* Copyright (c) 2001, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2011, 2013, Monty Program Ab.
+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
@@ -11,8 +12,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
/* HANDLER ... commands - direct access to ISAM */
@@ -35,37 +35,37 @@
*/
/*
- There are two containers holding information about open handler tables.
- The first is 'thd->handler_tables'. It is a linked list of TABLE objects.
- It is used like 'thd->open_tables' in the table cache. The trick is to
- exchange these two lists during open and lock of tables. Thus the normal
- table cache code can be used.
- The second container is a HASH. It holds objects of the type TABLE_LIST.
- Despite its name, no lists of tables but only single structs are hashed
- (the 'next' pointer is always NULL). The reason for theis second container
- is, that we want handler tables to survive FLUSH TABLE commands. A table
- affected by FLUSH TABLE must be closed so that other threads are not
- blocked by handler tables still in use. Since we use the normal table cache
- functions with 'thd->handler_tables', the closed tables are removed from
- this list. Hence we need the original open information for the handler
- table in the case that it is used again. This information is handed over
- to mysql_ha_open() as a TABLE_LIST. So we store this information in the
- second container, where it is not affected by FLUSH TABLE. The second
- container is implemented as a hash for performance reasons. Consequently,
- we use it not only for re-opening a handler table, but also for the
- HANDLER ... READ commands. For this purpose, we store a pointer to the
- TABLE structure (in the first container) in the TBALE_LIST object in the
- second container. When the table is flushed, the pointer is cleared.
+ The information about open HANDLER objects is stored in a HASH.
+ It holds objects of type TABLE_LIST, which are indexed by table
+ name/alias, and allows us to quickly find a HANDLER table for any
+ operation at hand - be it HANDLER READ or HANDLER CLOSE.
+
+ It also allows us to maintain an "open" HANDLER even in cases
+ when there is no physically open cursor. E.g. a FLUSH TABLE
+ statement in this or some other connection demands that all open
+ HANDLERs against the flushed table are closed. In order to
+ preserve the information about an open HANDLER, we don't perform
+ a complete HANDLER CLOSE, but only close the TABLE object. The
+ corresponding TABLE_LIST is kept in the cache with 'table'
+ pointer set to NULL. The table will be reopened on next access
+ (this, however, leads to loss of cursor position, unless the
+ cursor points at the first record).
*/
+#include "sql_priv.h"
+#include "sql_handler.h"
+#include "unireg.h" // REQUIRED: for other includes
+#include "sql_base.h" // close_thread_tables
+#include "lock.h" // mysql_unlock_tables
+#include "key.h" // key_copy
+#include "sql_base.h" // insert_fields
+#include "sql_select.h"
+#include "transaction.h"
+
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
-#include "sql_select.h"
-#include "sql_handler.h"
-
#define HANDLER_TABLES_HASH_SIZE 120
static enum enum_ha_read_modes rkey_to_rnext[]=
@@ -81,7 +81,7 @@ void SQL_HANDLER::reset()
fields.empty();
arena.free_items();
free_root(&mem_root, MYF(0));
- my_free(lock, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(lock);
init();
}
@@ -90,7 +90,7 @@ void SQL_HANDLER::reset()
SQL_HANDLER::~SQL_HANDLER()
{
reset();
- my_free(base_data, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(base_data);
}
/*
@@ -144,7 +144,6 @@ static void mysql_ha_hash_free(SQL_HANDLER *table)
@param thd Thread identifier.
@param tables A list of tables with the first entry to close.
- @param is_locked If LOCK_open is locked.
@note Though this function takes a list of tables, only the first list entry
will be closed.
@@ -152,45 +151,28 @@ static void mysql_ha_hash_free(SQL_HANDLER *table)
@note Broadcasts refresh if it closed a table with old version.
*/
-static void mysql_ha_close_table(SQL_HANDLER *handler,
- bool is_locked)
+static void mysql_ha_close_table(SQL_HANDLER *handler)
{
THD *thd= handler->thd;
TABLE *table= handler->table;
- TABLE **table_ptr;
/* check if table was already closed */
if (!table)
return;
- /*
- Though we could take the table pointer from hash_tables->table,
- we must follow the thd->handler_tables chain anyway, as we need the
- address of the 'next' pointer referencing this table
- for close_thread_table().
- */
- for (table_ptr= &(thd->handler_tables);
- *table_ptr && (*table_ptr != table);
- table_ptr= &(*table_ptr)->next)
- ;
-
- if (*table_ptr)
+ if (!table->s->tmp_table)
{
+ /* Non temporary table. */
if (handler->lock)
{
// Mark it unlocked, like in reset_lock_data()
reset_lock_data(handler->lock, 1);
}
+
table->file->ha_index_or_rnd_end();
- if (! is_locked)
- VOID(pthread_mutex_lock(&LOCK_open));
- if (close_thread_table(thd, table_ptr))
- {
- /* Tell threads waiting for refresh that something has happened */
- broadcast_refresh();
- }
- if (! is_locked)
- VOID(pthread_mutex_unlock(&LOCK_open));
+ table->open_by_handler= 0;
+ (void) close_thread_table(thd, &table);
+ thd->mdl_context.release_lock(handler->mdl_request.ticket);
}
else
{
@@ -198,8 +180,9 @@ static void mysql_ha_close_table(SQL_HANDLER *handler,
table->file->ha_index_or_rnd_end();
table->query_id= thd->query_id;
table->open_by_handler= 0;
+ mark_tmp_table_for_reuse(table);
}
- my_free(handler->lock, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(handler->lock);
handler->init();
}
@@ -229,14 +212,20 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
{
SQL_HANDLER *sql_handler= 0;
uint counter;
- int error;
- TABLE *table, *backup_open_tables, *write_lock_used;
+ bool error;
+ TABLE *table, *backup_open_tables;
+ MDL_savepoint mdl_savepoint;
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,
reopen != 0));
+ if (thd->locked_tables_mode)
+ {
+ my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
if (tables->schema_table)
{
my_error(ER_WRONG_USAGE, MYF(0), "HANDLER OPEN",
@@ -245,25 +234,29 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
DBUG_RETURN(TRUE);
}
- if (! hash_inited(&thd->handler_tables_hash))
+ if (! my_hash_inited(&thd->handler_tables_hash))
{
/*
HASH entries are of type SQL_HANDLER
*/
- if (hash_init(&thd->handler_tables_hash, &my_charset_latin1,
- HANDLER_TABLES_HASH_SIZE, 0, 0,
- (hash_get_key) mysql_ha_hash_get_key,
- (hash_free_key) mysql_ha_hash_free, 0))
- goto err;
+ if (my_hash_init(&thd->handler_tables_hash, &my_charset_latin1,
+ HANDLER_TABLES_HASH_SIZE, 0, 0,
+ (my_hash_get_key) mysql_ha_hash_get_key,
+ (my_hash_free_key) mysql_ha_hash_free, 0))
+ {
+ DBUG_PRINT("exit",("ERROR"));
+ DBUG_RETURN(TRUE);
+ }
}
else if (! reopen) /* Otherwise we have 'tables' already. */
{
- if (hash_search(&thd->handler_tables_hash, (uchar*) tables->alias,
- strlen(tables->alias) + 1))
+ if (my_hash_search(&thd->handler_tables_hash, (uchar*) tables->alias,
+ strlen(tables->alias) + 1))
{
DBUG_PRINT("info",("duplicate '%s'", tables->alias));
+ DBUG_PRINT("exit",("ERROR"));
my_error(ER_NONUNIQ_TABLE, MYF(0), tables->alias);
- goto err;
+ DBUG_RETURN(TRUE);
}
}
@@ -273,14 +266,10 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
from open_tables(), thd->open_tables will contain only the opened
table.
- The thd->handler_tables list is kept as-is to avoid deadlocks if
- open_table(), called by open_tables(), needs to back-off because
- of a pending name-lock on the table being opened.
-
See open_table() back-off comments for more details.
*/
backup_open_tables= thd->open_tables;
- thd->open_tables= NULL;
+ thd->set_open_tables(NULL);
/*
open_tables() will set 'tables->table' if successful.
@@ -288,40 +277,24 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
*/
DBUG_ASSERT(! tables->table);
+ /*
+ We can't request lock with explicit duration for this table
+ 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,
+ MDL_SHARED, MDL_TRANSACTION);
+ mdl_savepoint= thd->mdl_context.mdl_savepoint();
+
/* for now HANDLER can be used only for real TABLES */
tables->required_type= FRMTYPE_TABLE;
+
/*
We use open_tables() here, rather than, say,
open_ltable() or open_table() because we would like to be able
to open a temporary table.
*/
error= open_tables(thd, &tables, &counter, 0);
- if (thd->open_tables)
- {
- if (thd->open_tables->next)
- {
- /*
- We opened something that is more than a single table.
- This happens with MERGE engine. Don't try to link
- this mess into thd->handler_tables list, close it
- and report an error. We must do it right away
- because mysql_ha_close_table(), called down the road,
- can close a single table only.
- */
- close_thread_tables(thd);
- my_error(ER_ILLEGAL_HA, MYF(0), tables->alias);
- error= 1;
- }
- else
- {
- /* Merge the opened table into handler_tables list. */
- thd->open_tables->next= thd->handler_tables;
- thd->handler_tables= thd->open_tables;
- }
- }
-
- /* Restore the state. */
- thd->open_tables= backup_open_tables;
if (error)
goto err;
@@ -335,6 +308,16 @@ 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))
+ {
+ /* 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)
+ goto err;
+ }
+
if (! reopen)
{
/* copy data to sql_handler */
@@ -342,7 +325,6 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
goto err;
init_alloc_root(&sql_handler->mem_root, 1024, 0);
- sql_handler->table= table;
sql_handler->db.length= strlen(tables->db);
sql_handler->table_name.length= strlen(tables->table_name);
sql_handler->handler_name.length= strlen(tables->alias);
@@ -373,10 +355,11 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
sql_handler->reset();
}
sql_handler->table= table;
+ memcpy(&sql_handler->mdl_request, &tables->mdl_request,
+ sizeof(tables->mdl_request));
if (!(sql_handler->lock= get_lock_data(thd, &sql_handler->table, 1,
- GET_LOCK_STORE_LOCKS,
- &write_lock_used)))
+ GET_LOCK_STORE_LOCKS)))
goto err;
/* Get a list of all fields for send_fields */
@@ -385,34 +368,60 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
thd->restore_active_arena(&sql_handler->arena, &backup_arena);
if (error)
- {
- if (reopen)
- sql_handler= 0;
goto err;
- }
/* Always read all columns */
table->read_set= &table->s->all_set;
table->vcol_set= &table->s->all_set;
+ /* Restore the state. */
+ thd->set_open_tables(backup_open_tables);
+ 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);
+ }
+
+ /*
+ 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
+ (see thd->temporary_tables).
+ */
+ 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. Otherwise, no meaning at all.
+ being used by this handler. For non-temp tables we use this flag
+ in asserts.
*/
table->open_by_handler= 1;
+ /* Safety, cleanup the pointer to satisfy MDL assertions. */
+ tables->mdl_request.ticket= NULL;
+
if (! reopen)
my_ok(thd);
DBUG_PRINT("exit",("OK"));
DBUG_RETURN(FALSE);
err:
- delete sql_handler;
- if (tables->table)
+ /*
+ No need to rollback statement transaction, it's not started.
+ If called with reopen flag, no need to rollback either,
+ it will be done at statement end.
+ */
+ DBUG_ASSERT(thd->transaction.stmt.is_empty());
+ close_thread_tables(thd);
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+ thd->set_open_tables(backup_open_tables);
+ if (sql_handler)
{
- SQL_HANDLER tmp_sql_handler(thd);
- tmp_sql_handler.table= tables->table;
- mysql_ha_close_table(&tmp_sql_handler, FALSE);
+ if (!reopen)
+ my_hash_delete(&thd->handler_tables_hash, (uchar*) sql_handler);
+ else
+ sql_handler->reset(); // or should it be init() ?
}
DBUG_PRINT("exit",("ERROR"));
DBUG_RETURN(TRUE);
@@ -443,12 +452,17 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
tables->db, tables->table_name, tables->alias));
- if ((handler= (SQL_HANDLER*) hash_search(&thd->handler_tables_hash,
- (uchar*) tables->alias,
- strlen(tables->alias) + 1)))
+ if (thd->locked_tables_mode)
{
- mysql_ha_close_table(handler, FALSE);
- hash_delete(&thd->handler_tables_hash, (uchar*) handler);
+ my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ if ((handler= (SQL_HANDLER*) my_hash_search(&thd->handler_tables_hash,
+ (uchar*) tables->alias,
+ strlen(tables->alias) + 1)))
+ {
+ mysql_ha_close_table(handler);
+ my_hash_delete(&thd->handler_tables_hash, (uchar*) handler);
}
else
{
@@ -457,6 +471,13 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
DBUG_RETURN(TRUE);
}
+ /*
+ Mark MDL_context as no longer breaking protocol if we have
+ closed last HANDLER.
+ */
+ if (! thd->handler_tables_hash.records)
+ thd->mdl_context.set_needs_thr_lock_abort(FALSE);
+
my_ok(thd);
DBUG_PRINT("exit", ("OK"));
DBUG_RETURN(FALSE);
@@ -464,6 +485,56 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
/**
+ A helper class to process an error from mysql_lock_tables().
+ HANDLER READ statement's attempt to lock the subject table
+ may get aborted if there is a pending DDL. In that case
+ we close the table, reopen it, and try to read again.
+ This is implicit and obscure, since HANDLER position
+ is lost in the process, but it's the legacy server
+ behaviour we should preserve.
+*/
+
+class Sql_handler_lock_error_handler: public Internal_error_handler
+{
+public:
+ virtual
+ bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char *sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR **cond_hdl);
+
+ bool need_reopen() const { return m_need_reopen; };
+ void init() { m_need_reopen= FALSE; };
+private:
+ bool m_need_reopen;
+};
+
+
+/**
+ Handle an error from mysql_lock_tables().
+ Ignore ER_LOCK_ABORTED errors.
+*/
+
+bool
+Sql_handler_lock_error_handler::
+handle_condition(THD *thd,
+ uint sql_errno,
+ const char *sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR **cond_hdl)
+{
+ *cond_hdl= NULL;
+ if (sql_errno == ER_LOCK_ABORTED)
+ m_need_reopen= TRUE;
+
+ return m_need_reopen;
+}
+
+
+/**
Finds an open HANDLER table.
@params name Name of handler to open
@@ -475,9 +546,8 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
SQL_HANDLER *mysql_ha_find_handler(THD *thd, const char *name)
{
SQL_HANDLER *handler;
- if ((handler= (SQL_HANDLER*) hash_search(&thd->handler_tables_hash,
- (uchar*) name,
- strlen(name) + 1)))
+ if ((handler= (SQL_HANDLER*) my_hash_search(&thd->handler_tables_hash,
+ (uchar*) name, strlen(name) + 1)))
{
DBUG_PRINT("info-in-hash",("'%s'.'%s' as '%s' table: %p",
handler->db.str,
@@ -487,9 +557,9 @@ 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->table_name.str,
- TL_READ);
- tmp.alias= handler->handler_name.str;
+ tmp.init_one_table(handler->db.str, handler->db.length,
+ handler->table_name.str, handler->table_name.length,
+ handler->handler_name.str, TL_READ);
if (mysql_ha_open(thd, &tmp, handler))
{
@@ -549,7 +619,8 @@ mysql_ha_fix_cond_and_key(SQL_HANDLER *handler,
keyname,
table->s->key_info[handler->keyno].name))
{
- if ((handler->keyno= find_type(keyname, &table->s->keynames, 1+2)-1)<0)
+ if ((handler->keyno= find_type(keyname, &table->s->keynames,
+ FIND_TYPE_NO_PREFIX) - 1) < 0)
{
my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), keyname,
handler->handler_name.str);
@@ -652,19 +723,22 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
{
SQL_HANDLER *handler;
TABLE *table;
- List<Item> list;
Protocol *protocol= thd->protocol;
char buff[MAX_FIELD_WIDTH];
String buffer(buff, sizeof(buff), system_charset_info);
int error, keyno;
uint num_rows;
uchar *UNINIT_VAR(key);
- bool need_reopen;
- List_iterator<Item> it;
+ Sql_handler_lock_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));
+ if (thd->locked_tables_mode)
+ {
+ my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
retry:
if (!(handler= mysql_ha_find_handler(thd, tables->alias)))
@@ -672,22 +746,54 @@ retry:
table= handler->table;
tables->table= table; // This is used by fix_fields
+ table->pos_in_table_list= tables;
- /* save open_tables state */
if (handler->lock->lock_count > 0)
{
int lock_error;
handler->lock->locks[0]->type= handler->lock->locks[0]->org_type;
- lock_error= mysql_lock_tables(thd, handler->lock, 0,
- (MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN |
- (handler->table->s->tmp_table ==
- NO_TMP_TABLE ?
- MYSQL_LOCK_NOT_TEMPORARY : 0)),
- &need_reopen);
- if (need_reopen)
+
+ /* save open_tables state */
+ TABLE* backup_open_tables= thd->open_tables;
+ /* Always a one-element list, see mysql_ha_open(). */
+ DBUG_ASSERT(table->next == NULL || table->s->tmp_table);
+ /*
+ mysql_lock_tables() needs thd->open_tables to be set correctly to
+ be able to handle aborts properly.
+ */
+ thd->set_open_tables(table);
+
+ sql_handler_lock_error.init();
+ thd->push_internal_handler(&sql_handler_lock_error);
+
+ lock_error= mysql_lock_tables(thd, handler->lock,
+ (table->s->tmp_table == NO_TMP_TABLE ?
+ MYSQL_LOCK_NOT_TEMPORARY : 0));
+
+ thd->pop_internal_handler();
+
+ /*
+ In 5.1 and earlier, mysql_lock_tables() could replace the TABLE
+ object with another one (reopen it). This is no longer the case
+ with new MDL.
+ */
+ DBUG_ASSERT(table == thd->open_tables);
+ /* Restore previous context. */
+ thd->set_open_tables(backup_open_tables);
+
+ if (sql_handler_lock_error.need_reopen())
{
- mysql_ha_close_table(handler, FALSE);
+ DBUG_ASSERT(lock_error && !thd->is_error());
+ /*
+ Always close statement transaction explicitly,
+ so that the engine doesn't have to count locks.
+ There should be no need to perform transaction
+ rollback due to deadlock.
+ */
+ DBUG_ASSERT(! thd->transaction_rollback_request);
+ trans_rollback_stmt(thd);
+ mysql_ha_close_table(handler);
if (thd->stmt_arena->is_stmt_execute())
{
/*
@@ -698,13 +804,6 @@ retry:
my_error(ER_NEED_REPREPARE, MYF(0));
goto err0;
}
-
- /*
- The lock might have been aborted, we need to manually reset
- thd->some_tables_deleted because handler's tables are closed
- in a non-standard way. Otherwise we might loop indefinitely.
- */
- thd->some_tables_deleted= 0;
goto retry;
}
@@ -717,9 +816,8 @@ retry:
mode= handler->mode;
keyno= handler->keyno;
- it.init(handler->fields);
- protocol->send_fields(&handler->fields,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
+ protocol->send_result_set_metadata(&handler->fields,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
/*
In ::external_lock InnoDB resets the fields which tell it that
@@ -751,14 +849,14 @@ retry:
case RFIRST:
if (keyname)
{
- table->file->ha_index_or_rnd_end();
- table->file->ha_index_init(keyno, 1);
- error= table->file->ha_index_first(table->record[0]);
+ if (!(error= table->file->ha_index_or_rnd_end()) &&
+ !(error= table->file->ha_index_init(keyno, 1)))
+ error= table->file->ha_index_first(table->record[0]);
}
else
{
- table->file->ha_index_or_rnd_end();
- if (!(error= table->file->ha_rnd_init(1)))
+ if (!(error= table->file->ha_index_or_rnd_end()) &&
+ !(error= table->file->ha_rnd_init(1)))
error= table->file->ha_rnd_next(table->record[0]);
}
mode= RNEXT;
@@ -777,10 +875,10 @@ retry:
/* else fall through */
case RLAST:
DBUG_ASSERT(keyname != 0);
- table->file->ha_index_or_rnd_end();
- table->file->ha_index_init(keyno, 1);
- error= table->file->ha_index_last(table->record[0]);
- mode= RPREV;
+ if (!(error= table->file->ha_index_or_rnd_end()) &&
+ !(error= table->file->ha_index_init(keyno, 1)))
+ error= table->file->ha_index_last(table->record[0]);
+ mode=RPREV;
break;
case RNEXT_SAME:
/* Continue scan on "(keypart1,keypart2,...)=(c1, c2, ...) */
@@ -794,13 +892,14 @@ retry:
if (!(key= (uchar*) thd->calloc(ALIGN_SIZE(handler->key_len))))
goto err;
- table->file->ha_index_or_rnd_end();
- table->file->ha_index_init(keyno, 1);
+ if ((error= table->file->ha_index_or_rnd_end()))
+ break;
key_copy(key, table->record[0], table->key_info + keyno,
handler->key_len);
- error= table->file->ha_index_read_map(table->record[0],
- key, handler->keypart_map,
- ha_rkey_mode);
+ if (!(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);
mode= rkey_to_rnext[(int)ha_rkey_mode];
break;
}
@@ -830,32 +929,35 @@ retry:
if (table->vfield)
update_virtual_fields(thd, table);
if (cond && !cond->val_int())
+ {
+ if (thd->is_error())
+ goto err;
continue;
+ }
if (num_rows >= offset_limit_cnt)
{
- Item *item;
protocol->prepare_for_resend();
- it.rewind();
- while ((item=it++))
- {
- if (item->send(thd->protocol, &buffer))
- {
- protocol->free(); // Free used
- my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
- goto err;
- }
- }
+
+ if (protocol->send_result_set_row(&handler->fields))
+ goto err;
+
protocol->write();
}
num_rows++;
}
ok:
+ /*
+ Always close statement transaction explicitly,
+ so that the engine doesn't have to count locks.
+ */
+ trans_commit_stmt(thd);
mysql_unlock_tables(thd, handler->lock, 0);
my_eof(thd);
DBUG_PRINT("exit",("OK"));
DBUG_RETURN(FALSE);
err:
+ trans_rollback_stmt(thd);
mysql_unlock_tables(thd, handler->lock, 0);
err0:
DBUG_PRINT("exit",("ERROR"));
@@ -905,15 +1007,17 @@ static SQL_HANDLER *mysql_ha_find_match(THD *thd, TABLE_LIST *tables)
/* search for all handlers with matching table names */
for (uint i= 0; i < thd->handler_tables_hash.records; i++)
{
- hash_tables= (SQL_HANDLER*) hash_element(&thd->handler_tables_hash, i);
+ hash_tables= (SQL_HANDLER*) my_hash_element(&thd->handler_tables_hash, i);
for (tables= first; tables; tables= tables->next_local)
{
+ if (tables->is_anonymous_derived_table())
+ continue;
if ((! *tables->db ||
! my_strcasecmp(&my_charset_latin1, hash_tables->db.str,
- tables->db)) &&
+ tables->get_db_name())) &&
! my_strcasecmp(&my_charset_latin1, hash_tables->table_name.str,
- tables->table_name))
+ tables->get_table_name()))
{
/* Link into hash_tables list */
hash_tables->next= head;
@@ -931,12 +1035,11 @@ static SQL_HANDLER *mysql_ha_find_match(THD *thd, TABLE_LIST *tables)
@param thd Thread identifier.
@param tables The list of tables to remove.
- @param is_locked If LOCK_open is locked.
@note Broadcasts refresh if it closed a table with old version.
*/
-void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked)
+void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables)
{
SQL_HANDLER *hash_tables, *next;
DBUG_ENTER("mysql_ha_rm_tables");
@@ -949,11 +1052,47 @@ void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked)
{
next= hash_tables->next;
if (hash_tables->table)
- mysql_ha_close_table(hash_tables, is_locked);
- hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
+ mysql_ha_close_table(hash_tables);
+ my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
hash_tables= next;
}
+ /*
+ Mark MDL_context as no longer breaking protocol if we have
+ closed last HANDLER.
+ */
+ if (! thd->handler_tables_hash.records)
+ thd->mdl_context.set_needs_thr_lock_abort(FALSE);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Close cursors of matching tables from the HANDLER's hash table.
+
+ @param thd Thread identifier.
+ @param tables The list of tables to flush.
+*/
+
+void mysql_ha_flush_tables(THD *thd, TABLE_LIST *all_tables)
+{
+ DBUG_ENTER("mysql_ha_flush_tables");
+
+ for (TABLE_LIST *table_list= all_tables; table_list;
+ table_list= table_list->next_global)
+ {
+ SQL_HANDLER *hash_tables= mysql_ha_find_match(thd, table_list);
+ /* Close all aliases of the same table. */
+ while (hash_tables)
+ {
+ SQL_HANDLER *next_local= hash_tables->next;
+ if (hash_tables->table)
+ mysql_ha_close_table(hash_tables);
+ hash_tables= next_local;
+ }
+ }
+
DBUG_VOID_RETURN;
}
@@ -972,13 +1111,28 @@ void mysql_ha_flush(THD *thd)
SQL_HANDLER *hash_tables;
DBUG_ENTER("mysql_ha_flush");
- safe_mutex_assert_owner(&LOCK_open);
+ mysql_mutex_assert_not_owner(&LOCK_open);
+
+ /*
+ Don't try to flush open HANDLERs when we're working with
+ system tables. The main MDL context is backed up and we can't
+ properly release HANDLER locks stored there.
+ */
+ if (thd->state_flags & Open_tables_state::BACKUPS_AVAIL)
+ DBUG_VOID_RETURN;
for (uint i= 0; i < thd->handler_tables_hash.records; i++)
{
- hash_tables= (SQL_HANDLER*) hash_element(&thd->handler_tables_hash, i);
- if (hash_tables->table && hash_tables->table->needs_reopen_or_name_lock())
- mysql_ha_close_table(hash_tables, TRUE);
+ hash_tables= (SQL_HANDLER*) my_hash_element(&thd->handler_tables_hash, i);
+ /*
+ TABLE::mdl_ticket is 0 for temporary tables so we need extra check.
+ */
+ if (hash_tables->table &&
+ ((hash_tables->table->mdl_ticket &&
+ hash_tables->table->mdl_ticket->has_pending_conflicting_lock()) ||
+ (!hash_tables->table->s->tmp_table &&
+ hash_tables->table->s->has_old_version())))
+ mysql_ha_close_table(hash_tables);
}
DBUG_VOID_RETURN;
@@ -1000,13 +1154,36 @@ void mysql_ha_cleanup(THD *thd)
for (uint i= 0; i < thd->handler_tables_hash.records; i++)
{
- hash_tables= (SQL_HANDLER*) hash_element(&thd->handler_tables_hash, i);
+ hash_tables= (SQL_HANDLER*) my_hash_element(&thd->handler_tables_hash, i);
if (hash_tables->table)
- mysql_ha_close_table(hash_tables, FALSE);
- }
+ mysql_ha_close_table(hash_tables);
+ }
+
+ my_hash_free(&thd->handler_tables_hash);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Set explicit duration for metadata locks corresponding to open HANDLERs
+ to protect them from being released at the end of transaction.
- hash_free(&thd->handler_tables_hash);
+ @param thd Thread identifier.
+*/
+void mysql_ha_set_explicit_lock_duration(THD *thd)
+{
+ SQL_HANDLER *hash_tables;
+ DBUG_ENTER("mysql_ha_set_explicit_lock_duration");
+
+ for (uint i= 0; i < thd->handler_tables_hash.records; i++)
+ {
+ hash_tables= (SQL_HANDLER*) my_hash_element(&thd->handler_tables_hash, i);
+ if (hash_tables->table && hash_tables->table->mdl_ticket)
+ thd->mdl_context.set_lock_duration(hash_tables->table->mdl_ticket,
+ MDL_EXPLICIT);
+ }
DBUG_VOID_RETURN;
}
diff --git a/sql/sql_handler.h b/sql/sql_handler.h
index 54e72e9f50e..133f553675e 100644
--- a/sql/sql_handler.h
+++ b/sql/sql_handler.h
@@ -1,3 +1,5 @@
+#ifndef SQL_HANDLER_INCLUDED
+#define SQL_HANDLER_INCLUDED
/* Copyright (C) 2010 Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -10,12 +12,16 @@
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifdef USE_PRAGMA_INTERFACE
#pragma interface /* gcc class implementation */
#endif
+#include "sql_class.h" /* enum_ha_read_mode */
+#include "my_base.h" /* ha_rkey_function, ha_rows */
+#include "sql_list.h" /* List */
+
/* Open handlers are stored here */
class SQL_HANDLER {
@@ -28,6 +34,7 @@ public:
LEX_STRING table_name;
MEM_ROOT mem_root;
MYSQL_LOCK *lock;
+ MDL_request mdl_request;
key_part_map keypart_map;
int keyno; /* Used key */
@@ -40,22 +47,34 @@ public:
Query_arena arena;
char *base_data;
SQL_HANDLER(THD *thd_arg) :
- thd(thd_arg), arena(&mem_root, Query_arena::INITIALIZED)
+ thd(thd_arg), arena(&mem_root, Query_arena::STMT_INITIALIZED)
{ init(); clear_alloc_root(&mem_root); base_data= 0; }
- void init() { keyno= -1; table= 0; lock= 0; }
+ void init()
+ {
+ keyno= -1;
+ table= 0;
+ lock= 0;
+ mdl_request.ticket= 0;
+ }
void reset();
~SQL_HANDLER();
};
+class THD;
+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 *,
List<Item> *,enum ha_rkey_function,Item *,ha_rows,ha_rows);
void mysql_ha_flush(THD *thd);
-void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked);
+void mysql_ha_flush_tables(THD *thd, TABLE_LIST *all_tables);
+void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables);
void mysql_ha_cleanup(THD *thd);
+void mysql_ha_set_explicit_lock_duration(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);
+#endif
diff --git a/sql/sql_help.cc b/sql/sql_help.cc
index 7879c3c74b7..844810af0f4 100644
--- a/sql/sql_help.cc
+++ b/sql/sql_help.cc
@@ -1,5 +1,4 @@
-/*
- Copyright (c) 2002, 2010, Oracle and/or its affiliates.
+/* Copyright (c) 2002, 2012, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,10 +11,15 @@
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
-*/
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "sql_help.h"
+#include "sql_table.h" // primary_key_name
+#include "sql_base.h" // REPORT_ALL_ERRORS, setup_tables
+#include "opt_range.h" // SQL_SELECT
+#include "records.h" // init_read_record, end_read_record
struct st_find_field
{
@@ -285,10 +289,12 @@ int get_topics_for_keyword(THD *thd, TABLE *topics, TABLE *relations,
Field *rtopic_id, *rkey_id;
DBUG_ENTER("get_topics_for_keyword");
- if ((iindex_topic= find_type((char*) primary_key_name,
- &topics->s->keynames, 1+2)-1)<0 ||
- (iindex_relations= find_type((char*) primary_key_name,
- &relations->s->keynames, 1+2)-1)<0)
+ if ((iindex_topic=
+ find_type(primary_key_name, &topics->s->keynames,
+ FIND_TYPE_NO_PREFIX) - 1) < 0 ||
+ (iindex_relations=
+ find_type(primary_key_name, &relations->s->keynames,
+ FIND_TYPE_NO_PREFIX) - 1) < 0)
{
my_message(ER_CORRUPT_HELP_DB, ER(ER_CORRUPT_HELP_DB), MYF(0));
DBUG_RETURN(-1);
@@ -296,8 +302,14 @@ int get_topics_for_keyword(THD *thd, TABLE *topics, TABLE *relations,
rtopic_id= find_fields[help_relation_help_topic_id].field;
rkey_id= find_fields[help_relation_help_keyword_id].field;
- topics->file->ha_index_init(iindex_topic,1);
- relations->file->ha_index_init(iindex_relations,1);
+ if (topics->file->ha_index_init(iindex_topic,1) ||
+ relations->file->ha_index_init(iindex_relations,1))
+ {
+ if (topics->file->inited)
+ topics->file->ha_index_end();
+ my_message(ER_CORRUPT_HELP_DB, ER(ER_CORRUPT_HELP_DB), MYF(0));
+ DBUG_RETURN(-1);
+ }
rkey_id->store((longlong) key_id, TRUE);
rkey_id->get_key_image(buff, rkey_id->pack_length(), Field::itRAW);
@@ -442,7 +454,7 @@ int send_answer_1(Protocol *protocol, String *s1, String *s2, String *s3)
field_list.push_back(new Item_empty_string("description",1000));
field_list.push_back(new Item_empty_string("example",1000));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
@@ -474,7 +486,7 @@ int send_answer_1(Protocol *protocol, String *s1, String *s2, String *s3)
+- -+
RETURN VALUES
- result of protocol->send_fields
+ result of protocol->send_result_set_metadata
*/
int send_header_2(Protocol *protocol, bool for_category)
@@ -485,7 +497,7 @@ int send_header_2(Protocol *protocol, bool for_category)
field_list.push_back(new Item_empty_string("source_category_name",64));
field_list.push_back(new Item_empty_string("name",64));
field_list.push_back(new Item_empty_string("is_it_category",1));
- DBUG_RETURN(protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
+ DBUG_RETURN(protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS |
Protocol::SEND_EOF));
}
@@ -649,24 +661,31 @@ bool mysqld_help(THD *thd, const char *mask)
MEM_ROOT *mem_root= thd->mem_root;
DBUG_ENTER("mysqld_help");
- bzero((uchar*)tables,sizeof(tables));
- tables[0].alias= tables[0].table_name= (char*) "help_topic";
- tables[0].lock_type= TL_READ;
+ 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].next_global= tables[0].next_local=
tables[0].next_name_resolution_table= &tables[1];
- tables[1].alias= tables[1].table_name= (char*) "help_category";
- tables[1].lock_type= TL_READ;
tables[1].next_global= tables[1].next_local=
tables[1].next_name_resolution_table= &tables[2];
- tables[2].alias= tables[2].table_name= (char*) "help_relation";
- tables[2].lock_type= TL_READ;
tables[2].next_global= tables[2].next_local=
tables[2].next_name_resolution_table= &tables[3];
- tables[3].alias= tables[3].table_name= (char*) "help_keyword";
- tables[3].lock_type= TL_READ;
- tables[0].db= tables[1].db= tables[2].db= tables[3].db= (char*) "mysql";
- Open_tables_state open_tables_state_backup;
+ /*
+ HELP must be available under LOCK TABLES.
+ Reset and backup the current open tables state to
+ make it possible.
+ */
+ Open_tables_backup open_tables_state_backup;
if (open_system_tables_for_read(thd, tables, &open_tables_state_backup))
goto error2;
diff --git a/sql/sql_help.h b/sql/sql_help.h
new file mode 100644
index 00000000000..b6ae490e757
--- /dev/null
+++ b/sql/sql_help.h
@@ -0,0 +1,28 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_HELP_INCLUDED
+#define SQL_HELP_INCLUDED
+
+class THD;
+
+
+/*
+ Function prototypes
+*/
+
+bool mysqld_help (THD *thd, const char *text);
+
+#endif /* SQL_HELP_INCLUDED */
diff --git a/sql/sql_hset.h b/sql/sql_hset.h
new file mode 100644
index 00000000000..f3a1467737f
--- /dev/null
+++ b/sql/sql_hset.h
@@ -0,0 +1,97 @@
+#ifndef SQL_HSET_INCLUDED
+#define SQL_HSET_INCLUDED
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+#include "my_global.h"
+#include "hash.h"
+
+
+/**
+ A type-safe wrapper around mysys HASH.
+*/
+
+template <typename T, my_hash_get_key K>
+class Hash_set
+{
+public:
+ typedef T Value_type;
+ enum { START_SIZE= 8 };
+ /**
+ Constructs an empty hash. Does not allocate memory, it is done upon
+ the first insert. Thus does not cause or return errors.
+ */
+ Hash_set()
+ {
+ my_hash_clear(&m_hash);
+ }
+ /**
+ Destroy the hash by freeing the buckets table. Does
+ not call destructors for the elements.
+ */
+ ~Hash_set()
+ {
+ my_hash_free(&m_hash);
+ }
+ /**
+ Insert a single value into a hash. Does not tell whether
+ the value was inserted -- if an identical value existed,
+ it is not replaced.
+
+ @retval TRUE Out of memory.
+ @retval FALSE OK. The value either was inserted or existed
+ in the hash.
+ */
+ bool insert(T *value)
+ {
+ my_hash_init_opt(&m_hash, &my_charset_bin, START_SIZE, 0, 0, K, 0, MYF(0));
+ size_t key_len;
+ const uchar *key= K(reinterpret_cast<uchar*>(value), &key_len, FALSE);
+ if (my_hash_search(&m_hash, key, key_len) == NULL)
+ return my_hash_insert(&m_hash, reinterpret_cast<uchar *>(value));
+ return FALSE;
+ }
+ /** Is this hash set empty? */
+ bool is_empty() const { return m_hash.records == 0; }
+ /** Returns the number of unique elements. */
+ size_t size() const { return static_cast<size_t>(m_hash.records); }
+ /** An iterator over hash elements. Is not insert-stable. */
+ class Iterator
+ {
+ public:
+ Iterator(Hash_set &hash_set)
+ : m_hash(&hash_set.m_hash),
+ m_idx(0)
+ {}
+ /**
+ Return the current element and reposition the iterator to the next
+ element.
+ */
+ inline T *operator++(int)
+ {
+ if (m_idx < m_hash->records)
+ return reinterpret_cast<T*>(my_hash_element(m_hash, m_idx++));
+ return NULL;
+ }
+ void rewind() { m_idx= 0; }
+ private:
+ HASH *m_hash;
+ uint m_idx;
+ };
+private:
+ HASH m_hash;
+};
+
+#endif // SQL_HSET_INCLUDED
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index de38c2f49f2..4500c4492c4 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1,5 +1,6 @@
/*
- Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -55,18 +56,34 @@
*/
-#include "mysql_priv.h"
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_priv.h"
+#include "unireg.h" // REQUIRED: for other includes
+#include "sql_insert.h"
+#include "sql_update.h" // compare_record
+#include "sql_base.h" // close_thread_tables
+#include "sql_cache.h" // query_cache_*
+#include "key.h" // key_copy
+#include "lock.h" // mysql_unlock_tables
#include "sp_head.h"
+#include "sql_view.h" // check_key_in_view, insert_view_fields
+#include "sql_table.h" // mysql_create_table_no_lock
+#include "sql_acl.h" // *_ACL, check_grant_all_columns
#include "sql_trigger.h"
#include "sql_select.h"
#include "sql_show.h"
#include "slave.h"
+#include "sql_parse.h" // end_active_trans
#include "rpl_mi.h"
+#include "transaction.h"
+#include "sql_audit.h"
+#include "sql_derived.h" // mysql_handle_derived
#include "debug_sync.h"
#ifndef EMBEDDED_LIBRARY
-static bool delayed_get_table(THD *thd, TABLE_LIST *table_list);
+static bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
+ TABLE_LIST *table_list);
static int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic,
LEX_STRING query, bool ignore, bool log_on);
static void end_delayed_insert(THD *thd);
@@ -75,19 +92,16 @@ static void unlink_blobs(register TABLE *table);
#endif
static bool check_view_insertability(THD *thd, TABLE_LIST *view);
-
/*
Check that insert/update fields are from the same single table of a view.
- SYNOPSIS
- check_view_single_update()
- fields The insert/update fields to be checked.
- values Values to use for update
- view The view for insert.
- map [in/out] The insert table map.
+ @param fields The insert/update fields to be checked.
+ @param values The insert/update values to be checked, NULL if
+ checking is not wanted.
+ @param view The view for insert.
+ @param map [in/out] The insert table map.
- DESCRIPTION
- This function is called in 2 cases:
+ This function is called in 2 cases:
1. to check insert fields. In this case *map will be set to 0.
Insert fields are checked to be all from the same single underlying
table of the given view. Otherwise the error is thrown. Found table
@@ -97,9 +111,7 @@ static bool check_view_insertability(THD *thd, TABLE_LIST *view);
the function to check insert fields. Update fields are checked to be
from the same table as the insert fields.
- RETURN
- 0 OK
- 1 Error
+ @returns false if success.
*/
bool check_view_single_update(List<Item> &fields, List<Item> *values,
@@ -124,6 +136,7 @@ bool check_view_single_update(List<Item> &fields, List<Item> *values,
/* Convert to real table bits */
tables&= ~PSEUDO_TABLE_BITS;
+
/* Check found map against provided map */
if (*map)
{
@@ -163,26 +176,20 @@ error:
/*
Check if insert fields are correct.
- SYNOPSIS
- check_insert_fields()
- thd The current thread.
- table The table for insert.
- fields The insert fields.
- values The insert values.
- check_unique If duplicate values should be rejected.
- fields_and_values_from_different_maps
- Set to 1 if fields and values are using
- different table maps, like on select ... insert
- map Store here table map for used fields
-
+ @param thd The current thread.
+ @param table_list The table we are inserting into (may be view)
+ @param fields The insert fields.
+ @param values The insert values.
+ @param check_unique If duplicate values should be rejected.
+ @param fields_and_values_from_different_maps If 'values' are allowed to
+ refer to other tables than those of 'fields'
+ @param map See check_view_single_update
NOTE
Clears TIMESTAMP_AUTO_SET_ON_INSERT from table->timestamp_field_type
or leaves it as is, depending on if timestamp should be updated or
not.
-
- RETURN
- 0 OK
- -1 Error
+
+ @returns 0 if success, -1 if error
*/
static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
@@ -266,7 +273,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
if (table_list->is_view() && table_list->is_merged_derived())
{
- if (check_view_single_update(fields,
+ if (check_view_single_update(fields,
fields_and_values_from_different_maps ?
(List<Item>*) 0 : &values,
table_list, map, true))
@@ -312,32 +319,35 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
}
-/*
- Check update fields for the timestamp field.
+/**
+ Check if update fields are correct.
- SYNOPSIS
- check_update_fields()
- thd The current thread.
- insert_table_list The insert table list.
- table The table for update.
- update_fields The update fields.
+ @param thd The current thread.
+ @param insert_table_list The table we are inserting into (may be view)
+ @param update_fields The update fields.
+ @param update_values The update values.
+ @param fields_and_values_from_different_maps If 'update_values' are allowed to
+ refer to other tables than those of 'update_fields'
+ @param map See check_view_single_update
NOTE
If the update fields include the timestamp field,
remove TIMESTAMP_AUTO_SET_ON_UPDATE from table->timestamp_field_type.
- RETURN
- 0 OK
- -1 Error
+ @returns 0 if success, -1 if error
*/
static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
List<Item> &update_fields,
- List<Item> &update_values, table_map *map)
+ List<Item> &update_values,
+ bool fields_and_values_from_different_maps,
+ table_map *map)
{
TABLE *table= insert_table_list->table;
my_bool timestamp_mark;
+ my_bool autoinc_mark;
LINT_INIT(timestamp_mark);
+ LINT_INIT(autoinc_mark);
if (table->timestamp_field)
{
@@ -349,13 +359,28 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
table->timestamp_field->field_index);
}
+ table->next_number_field_updated= FALSE;
+
+ if (table->found_next_number_field)
+ {
+ /*
+ Unmark the auto_increment field so that we can check if this is modified
+ by update_fields
+ */
+ autoinc_mark= bitmap_test_and_clear(table->write_set,
+ table->found_next_number_field->
+ field_index);
+ }
+
/* Check the fields we are going to modify */
if (setup_fields(thd, 0, update_fields, MARK_COLUMNS_WRITE, 0, 0))
return -1;
if (insert_table_list->is_view() &&
insert_table_list->is_merged_derived() &&
- check_view_single_update(update_fields, &update_values,
+ check_view_single_update(update_fields,
+ fields_and_values_from_different_maps ?
+ (List<Item>*) 0 : &update_values,
insert_table_list, map, false))
return -1;
@@ -370,6 +395,18 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
bitmap_set_bit(table->write_set,
table->timestamp_field->field_index);
}
+
+ if (table->found_next_number_field)
+ {
+ if (bitmap_is_set(table->write_set,
+ table->found_next_number_field->field_index))
+ table->next_number_field_updated= TRUE;
+
+ if (autoinc_mark)
+ bitmap_set_bit(table->write_set,
+ table->found_next_number_field->field_index);
+ }
+
return 0;
}
@@ -423,9 +460,9 @@ void prepare_triggers_for_insert_stmt(TABLE *table)
downgrade the lock in handler::store_lock() method.
*/
-void upgrade_lock_type_for_insert(THD *thd, thr_lock_type *lock_type,
- enum_duplicates duplic,
- bool is_multi_insert)
+static
+void upgrade_lock_type(THD *thd, thr_lock_type *lock_type,
+ enum_duplicates duplic)
{
if (duplic == DUP_UPDATE ||
(duplic == DUP_REPLACE && *lock_type == TL_WRITE_CONCURRENT_INSERT))
@@ -460,7 +497,7 @@ void upgrade_lock_type_for_insert(THD *thd, thr_lock_type *lock_type,
*/
if (specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE) ||
thd->variables.max_insert_delayed_threads == 0 ||
- thd->prelocked_mode ||
+ thd->locked_tables_mode > LTM_LOCK_TABLES ||
thd->lex->uses_stored_routines())
{
*lock_type= TL_WRITE;
@@ -474,10 +511,9 @@ void upgrade_lock_type_for_insert(THD *thd, thr_lock_type *lock_type,
return;
}
- bool log_on= (thd->options & OPTION_BIN_LOG ||
- ! (thd->security_ctx->master_access & SUPER_ACL));
+ bool log_on= (thd->variables.option_bits & OPTION_BIN_LOG);
if (global_system_variables.binlog_format == BINLOG_FORMAT_STMT &&
- log_on && mysql_bin_log.is_open() && is_multi_insert)
+ log_on && mysql_bin_log.is_open())
{
/*
Statement-based binary logging does not work in this case, because:
@@ -534,47 +570,78 @@ void upgrade_lock_type_for_insert(THD *thd, thr_lock_type *lock_type,
static
bool open_and_lock_for_insert_delayed(THD *thd, TABLE_LIST *table_list)
{
+ MDL_request protection_request;
DBUG_ENTER("open_and_lock_for_insert_delayed");
#ifndef EMBEDDED_LIBRARY
- if (thd->locked_tables && thd->global_read_lock)
- {
- /*
- If this connection has the global read lock, the handler thread
- will not be able to lock the table. It will wait for the global
- read lock to go away, but this will never happen since the
- connection thread will be stuck waiting for the handler thread
- to open and lock the table.
- If we are not in locked tables mode, INSERT will seek protection
- against the global read lock (and fail), thus we will only get
- to this point in locked tables mode.
- */
- my_error(ER_CANT_UPDATE_WITH_READLOCK, MYF(0));
+ /*
+ In order for the deadlock detector to be able to find any deadlocks
+ caused by the handler thread waiting for GRL or this table, we acquire
+ protection against GRL (global IX metadata lock) and metadata lock on
+ table to being inserted into inside the connection thread.
+ If this goes ok, the tickets are cloned and added to the list of granted
+ locks held by the handler thread.
+ */
+ if (thd->global_read_lock.can_acquire_protection())
DBUG_RETURN(TRUE);
- }
- if (delayed_get_table(thd, table_list))
+ protection_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
+ MDL_STATEMENT);
+
+ if (thd->mdl_context.acquire_lock(&protection_request,
+ thd->variables.lock_wait_timeout))
DBUG_RETURN(TRUE);
- if (table_list->table)
+ if (thd->mdl_context.acquire_lock(&table_list->mdl_request,
+ thd->variables.lock_wait_timeout))
+ /*
+ If a lock can't be acquired, it makes no sense to try normal insert.
+ Therefore we just abort the statement.
+ */
+ DBUG_RETURN(TRUE);
+
+ bool error= FALSE;
+ if (delayed_get_table(thd, &protection_request, table_list))
+ error= TRUE;
+ else if (table_list->table)
{
/*
Open tables used for sub-selects or in stored functions, will also
cache these functions.
*/
- if (open_and_lock_tables(thd, table_list->next_global))
+ if (open_and_lock_tables(thd, table_list->next_global, TRUE, 0))
{
end_delayed_insert(thd);
- DBUG_RETURN(TRUE);
+ error= TRUE;
+ }
+ else
+ {
+ /*
+ First table was not processed by open_and_lock_tables(),
+ we need to set updatability flag "by hand".
+ */
+ if (!table_list->derived && !table_list->view)
+ table_list->updatable= 1; // usual table
}
- /*
- First table was not processed by open_and_lock_tables(),
- we need to set updatability flag "by hand".
- */
- if (!table_list->derived && !table_list->view)
- table_list->updatable= 1; // usual table
- DBUG_RETURN(FALSE);
}
+
+ /*
+ We can't release protection against GRL and metadata lock on the table
+ being inserted into here. These locks might be required, for example,
+ because this INSERT DELAYED calls functions which may try to update
+ this or another tables (updating the same table is of course illegal,
+ but such an attempt can be discovered only later during statement
+ execution).
+ */
+
+ /*
+ Reset the ticket in case we end up having to use normal insert and
+ therefore will reopen the table and reacquire the metadata lock.
+ */
+ table_list->mdl_request.ticket= NULL;
+
+ if (error || table_list->table)
+ DBUG_RETURN(error);
#endif
/*
* This is embedded library and we don't have auxiliary
@@ -586,7 +653,31 @@ bool open_and_lock_for_insert_delayed(THD *thd, TABLE_LIST *table_list)
Use a normal insert.
*/
table_list->lock_type= TL_WRITE;
- DBUG_RETURN(open_and_lock_tables(thd, table_list));
+ DBUG_RETURN(open_and_lock_tables(thd, table_list, TRUE, 0));
+}
+
+
+/**
+ Create a new query string for removing DELAYED keyword for
+ multi INSERT DEALAYED statement.
+
+ @param[in] thd Thread handler
+ @param[in] buf Query string
+
+ @return
+ 0 ok
+ 1 error
+*/
+static int
+create_insert_stmt_from_insert_delayed(THD *thd, String *buf)
+{
+ /* Make a copy of thd->query() and then remove the "DELAYED" keyword */
+ if (buf->append(thd->query()) ||
+ buf->replace(thd->lex->keyword_delayed_begin_offset,
+ thd->lex->keyword_delayed_end_offset -
+ thd->lex->keyword_delayed_begin_offset, 0))
+ return 1;
+ return 0;
}
@@ -625,10 +716,9 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
/*
log_on is about delayed inserts only.
By default, both logs are enabled (this won't cause problems if the server
- runs without --log-update or --log-bin).
+ runs without --log-bin).
*/
- bool log_on= ((thd->options & OPTION_BIN_LOG) ||
- (!(thd->security_ctx->master_access & SUPER_ACL)));
+ bool log_on= (thd->variables.option_bits & OPTION_BIN_LOG);
#endif
thr_lock_type lock_type;
Item *unused_conds= 0;
@@ -638,16 +728,17 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
Upgrade lock type if the requested lock is incompatible with
the current connection mode or table operation.
*/
- upgrade_lock_type_for_insert(thd, &table_list->lock_type, duplic,
- values_list.elements > 1);
+ upgrade_lock_type(thd, &table_list->lock_type, duplic);
/*
We can't write-delayed into a table locked with LOCK TABLES:
this will lead to a deadlock, since the delayed thread will
never be able to get a lock on the table.
*/
- if (table_list->lock_type == TL_WRITE_DELAYED && thd->locked_tables &&
- find_locked_table(thd, table_list->db, table_list->table_name))
+ 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))
{
my_error(ER_DELAYED_INSERT_TABLE_LOCKED, MYF(0),
table_list->table_name);
@@ -667,7 +758,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
}
else
{
- if (open_and_lock_tables(thd, table_list))
+ if (open_and_lock_tables(thd, table_list, TRUE, 0))
DBUG_RETURN(TRUE);
}
@@ -782,7 +873,15 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
{
if (duplic != DUP_ERROR || ignore)
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
- if (!thd->prelocked_mode && values_list.elements > 1)
+ /**
+ This is a simple check for the case when the table has a trigger
+ that reads from it, or when the statement invokes a stored function
+ that reads from the table being inserted to.
+ Engines can't handle a bulk insert in parallel with a read form the
+ same table in the same connection.
+ */
+ if (thd->locked_tables_mode <= LTM_LOCK_TABLES &&
+ values_list.elements > 1)
{
using_bulk_insert= 1;
table->file->ha_start_bulk_insert(values_list.elements);
@@ -887,7 +986,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
error=write_record(thd, table ,&info);
if (error)
break;
- thd->row_count++;
+ thd->warning_info->inc_current_row_for_warning();
}
free_underlaid_joins(thd, &thd->lex->select_lex);
@@ -933,6 +1032,10 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
*/
query_cache_invalidate3(thd, table_list, 1);
}
+
+ if (thd->transaction.stmt.modified_non_trans_table)
+ thd->transaction.all.modified_non_trans_table= TRUE;
+
if (error <= 0 ||
thd->transaction.stmt.modified_non_trans_table ||
was_insert_delayed)
@@ -947,7 +1050,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
thd->net.last_error/errno. For example if there has
been a disk full error when writing the row, and it was
MyISAM, then thd->net.last_error/errno will be set to
- "disk full"... and the my_pwrite() will wait until free
+ "disk full"... and the mysql_file_pwrite() will wait until free
space appears, and so when it finishes then the
write_row() was entirely successful
*/
@@ -970,16 +1073,29 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
such case the flag is ignored for constructing binlog event.
*/
DBUG_ASSERT(thd->killed != KILL_BAD_DATA || error > 0);
- if (thd->binlog_query(THD::ROW_QUERY_TYPE,
- thd->query(), thd->query_length(),
- transactional_table, FALSE,
- errcode))
+ if (was_insert_delayed && table_list->lock_type == TL_WRITE)
{
- error=1;
- }
+ /* Binlog INSERT DELAYED as INSERT without DELAYED. */
+ String log_query;
+ if (create_insert_stmt_from_insert_delayed(thd, &log_query))
+ {
+ sql_print_error("Event Error: An error occurred while creating query string"
+ "for INSERT DELAYED stmt, before writing it into binary log.");
+
+ error= 1;
+ }
+ else if (thd->binlog_query(THD::ROW_QUERY_TYPE,
+ log_query.c_ptr(), log_query.length(),
+ transactional_table, FALSE, FALSE,
+ errcode))
+ error= 1;
+ }
+ else if (thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query(), thd->query_length(),
+ transactional_table, FALSE, FALSE,
+ errcode))
+ error= 1;
}
- if (thd->transaction.stmt.modified_non_trans_table)
- thd->transaction.all.modified_non_trans_table= TRUE;
}
DBUG_ASSERT(transactional_table || !changed ||
thd->transaction.stmt.modified_non_trans_table);
@@ -1010,13 +1126,13 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
if (error)
goto abort;
- if (values_list.elements == 1 && (!(thd->options & OPTION_WARNINGS) ||
+ if (values_list.elements == 1 && (!(thd->variables.option_bits & OPTION_WARNINGS) ||
!thd->cuted_fields))
{
- thd->row_count_func= info.copied + info.deleted +
- ((thd->client_capabilities & CLIENT_FOUND_ROWS) ?
- info.touched : info.updated);
- my_ok(thd, (ulong) thd->row_count_func, id);
+ my_ok(thd, info.copied + info.deleted +
+ ((thd->client_capabilities & CLIENT_FOUND_ROWS) ?
+ info.touched : info.updated),
+ id);
}
else
{
@@ -1026,12 +1142,13 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
if (ignore)
sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records,
(lock_type == TL_WRITE_DELAYED) ? (ulong) 0 :
- (ulong) (info.records - info.copied), (ulong) thd->cuted_fields);
+ (ulong) (info.records - info.copied),
+ (ulong) thd->warning_info->statement_warn_count());
else
sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records,
- (ulong) (info.deleted + updated), (ulong) thd->cuted_fields);
- thd->row_count_func= info.copied + info.deleted + updated;
- ::my_ok(thd, (ulong) thd->row_count_func, id, buff);
+ (ulong) (info.deleted + updated),
+ (ulong) thd->warning_info->statement_warn_count());
+ ::my_ok(thd, info.copied + info.deleted + updated, id, buff);
}
thd->abort_on_warning= 0;
if (thd->lex->current_select->first_cond_optimization)
@@ -1096,7 +1213,7 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view)
DBUG_ASSERT(view->table != 0 && view->field_translation != 0);
- VOID(bitmap_init(&used_fields, used_fields_buff, table->s->fields, 0));
+ (void) bitmap_init(&used_fields, used_fields_buff, table->s->fields, 0);
bitmap_clear_all(&used_fields);
view->contain_auto_increment= 0;
@@ -1350,14 +1467,14 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
thd->abort_on_warning= saved_abort_on_warning;
}
- if (!res)
- res= setup_fields(thd, 0, update_values, MARK_COLUMNS_READ, 0, 0);
+ if (!res)
+ res= setup_fields(thd, 0, update_values, MARK_COLUMNS_READ, 0, 0);
if (!res && duplic == DUP_UPDATE)
{
select_lex->no_wrap_view_item= TRUE;
res= check_update_fields(thd, context->table_list, update_fields,
- update_values, &map);
+ update_values, false, &map);
select_lex->no_wrap_view_item= FALSE;
}
@@ -1409,6 +1526,23 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
static int last_uniq_key(TABLE *table,uint keynr)
{
+ /*
+ When an underlying storage engine informs that the unique key
+ conflicts are not reported in the ascending order by setting
+ the HA_DUPLICATE_KEY_NOT_IN_ORDER flag, we cannot rely on this
+ information to determine the last key conflict.
+
+ The information about the last key conflict will be used to
+ do a replace of the new row on the conflicting row, rather
+ than doing a delete (of old row) + insert (of new row).
+
+ Hence check for this flag and disable replacing the last row
+ by returning 0 always. Returning 0 will result in doing
+ a delete + insert always.
+ */
+ if (table->file->ha_table_flags() & HA_DUPLICATE_KEY_NOT_IN_ORDER)
+ return 0;
+
while (++keynr < table->s->keys)
if (table->key_info[keynr].flags & HA_NOSAME)
return 0;
@@ -1450,6 +1584,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
MY_BITMAP *save_read_set, *save_write_set;
ulonglong prev_insert_id= table->file->next_insert_id;
ulonglong insert_id_for_cur_row= 0;
+ ulonglong prev_insert_id_for_cur_row= 0;
DBUG_ENTER("write_record");
info->records++;
@@ -1485,7 +1620,10 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
report error as usual. We will not do any duplicate key processing.
*/
if (info->ignore)
+ {
+ table->file->print_error(error, MYF(ME_JUST_WARNING));
goto ok_or_after_trg_err; /* Ignoring a not fatal error, return 0 */
+ }
goto err;
}
if ((int) (key_nr = table->file->get_dup_key(error)) < 0)
@@ -1530,9 +1668,10 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
}
}
key_copy((uchar*) key,table->record[0],table->key_info+key_nr,0);
+ key_part_map keypart_map= (1 << table->key_info[key_nr].key_parts) - 1;
if ((error= (table->file->ha_index_read_idx_map(table->record[1],
key_nr, (uchar*) key,
- HA_WHOLE_KEY,
+ keypart_map,
HA_READ_KEY_EXACT))))
goto err;
}
@@ -1565,9 +1704,6 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
goto before_trg_err;
table->file->restore_auto_increment(prev_insert_id);
- if (table->next_number_field)
- table->file->adjust_next_insert_id_after_explicit_value(
- table->next_number_field->val_int());
info->touched++;
if (!records_are_comparable(table) || compare_record(table))
{
@@ -1578,6 +1714,9 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
if (info->ignore &&
!table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
{
+ if (!(thd->variables.old_behavior &
+ OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE))
+ table->file->print_error(error, MYF(ME_JUST_WARNING));
goto ok_or_after_trg_err;
}
goto err;
@@ -1595,6 +1734,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
INSERT query, which is handled separately by
THD::arg_of_last_insert_id_function.
*/
+ prev_insert_id_for_cur_row= table->file->insert_id_for_cur_row;
insert_id_for_cur_row= table->file->insert_id_for_cur_row= 0;
trg_error= (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
@@ -1602,11 +1742,22 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
info->copied++;
}
- if (table->next_number_field)
- table->file->adjust_next_insert_id_after_explicit_value(
- table->next_number_field->val_int());
- info->touched++;
+ /*
+ Only update next_insert_id if the AUTO_INCREMENT value was explicitly
+ updated, so we don't update next_insert_id with the value from the
+ row being updated. Otherwise reset next_insert_id to what it was
+ before the duplicate key error, since that value is unused.
+ */
+ if (table->next_number_field_updated)
+ {
+ DBUG_ASSERT(table->next_number_field != NULL);
+ table->file->adjust_next_insert_id_after_explicit_value(table->next_number_field->val_int());
+ }
+ else if (prev_insert_id_for_cur_row)
+ {
+ table->file->restore_auto_increment(prev_insert_id_for_cur_row);
+ }
goto ok_or_after_trg_err;
}
else /* DUP_REPLACE */
@@ -1695,6 +1846,9 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
if (!info->ignore ||
table->file->is_fatal_error(error, HA_CHECK_DUP))
goto err;
+ 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);
goto ok_or_after_trg_err;
}
@@ -1782,7 +1936,7 @@ public:
enum_duplicates dup;
my_time_t start_time;
ulong start_time_sec_part;
- ulong sql_mode;
+ ulonglong sql_mode;
bool auto_increment_field_not_null;
bool query_start_used, ignore, log_query, query_start_sec_part_used;
bool stmt_depends_on_first_successful_insert_id_in_prev_stmt;
@@ -1801,8 +1955,8 @@ public:
{}
~delayed_row()
{
- x_free(query.str);
- x_free(record);
+ my_free(query.str);
+ my_free(record);
}
};
@@ -1819,50 +1973,65 @@ class Delayed_insert :public ilink {
public:
THD thd;
TABLE *table;
- pthread_mutex_t mutex;
- pthread_cond_t cond,cond_client;
+ mysql_mutex_t mutex;
+ mysql_cond_t cond, cond_client;
volatile uint tables_in_use,stacked_inserts;
- volatile bool status,dead;
+ volatile bool status;
+ /**
+ When the handler thread starts, it clones a metadata lock ticket
+ which protects against GRL and ticket for the table to be inserted.
+ This is done to allow the deadlock detector to detect deadlocks
+ resulting from these locks.
+ Before this is done, the connection thread cannot safely exit
+ without causing problems for clone_ticket().
+ Once handler_thread_initialized has been set, it is safe for the
+ connection thread to exit.
+ Access to handler_thread_initialized is protected by di->mutex.
+ */
+ bool handler_thread_initialized;
COPY_INFO info;
I_List<delayed_row> rows;
ulong group_count;
TABLE_LIST table_list; // Argument
+ /**
+ Request for IX metadata lock protecting against GRL which is
+ passed from connection thread to the handler thread.
+ */
+ MDL_request grl_protection;
Delayed_insert()
- :locks_in_memory(0),
- table(0),tables_in_use(0),stacked_inserts(0), status(0), dead(0),
- group_count(0)
+ :locks_in_memory(0), table(0),tables_in_use(0),stacked_inserts(0),
+ status(0), handler_thread_initialized(FALSE), group_count(0)
{
+ DBUG_ENTER("Delayed_insert constructor");
thd.security_ctx->user=(char*) delayed_user;
thd.security_ctx->host=(char*) my_localhost;
- strmake(thd.security_ctx->priv_user, thd.security_ctx->user,
- USERNAME_LENGTH);
+ strmake_buf(thd.security_ctx->priv_user, thd.security_ctx->user);
thd.current_tablenr=0;
- thd.version=refresh_version;
thd.command=COM_DELAYED_INSERT;
thd.lex->current_select= 0; // for my_message_sql
thd.lex->sql_command= SQLCOM_INSERT; // For innodb::store_lock()
/*
- Statement-based replication of INSERT DELAYED has problems with RAND()
- and user vars, so in mixed mode we go to row-based.
+ Prevent changes to global.lock_wait_timeout from affecting
+ delayed insert threads as any timeouts in delayed inserts
+ are not communicated to the client.
*/
- thd.lex->set_stmt_unsafe();
- thd.set_current_stmt_binlog_row_based_if_mixed();
+ thd.variables.lock_wait_timeout= LONG_TIMEOUT;
bzero((char*) &thd.net, sizeof(thd.net)); // Safety
bzero((char*) &table_list, sizeof(table_list)); // Safety
thd.system_thread= SYSTEM_THREAD_DELAYED_INSERT;
thd.security_ctx->host_or_ip= "";
bzero((char*) &info,sizeof(info));
- my_pthread_mutex_init(&mutex, MY_MUTEX_INIT_FAST, "Delayed_insert::mutex",
- 0);
- pthread_cond_init(&cond,NULL);
- pthread_cond_init(&cond_client,NULL);
- VOID(pthread_mutex_lock(&LOCK_thread_count));
+ mysql_mutex_init(key_delayed_insert_mutex, &mutex, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_delayed_insert_cond, &cond, NULL);
+ mysql_cond_init(key_delayed_insert_cond_client, &cond_client, NULL);
+ mysql_mutex_lock(&LOCK_thread_count);
delayed_insert_threads++;
delayed_lock= global_system_variables.low_priority_updates ?
TL_WRITE_LOW_PRIORITY : TL_WRITE;
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ mysql_mutex_unlock(&LOCK_thread_count);
+ DBUG_VOID_RETURN;
}
~Delayed_insert()
{
@@ -1871,18 +2040,21 @@ public:
while ((row=rows.get()))
delete row;
if (table)
+ {
close_thread_tables(&thd);
- VOID(pthread_mutex_lock(&LOCK_thread_count));
- pthread_mutex_destroy(&mutex);
- pthread_cond_destroy(&cond);
- pthread_cond_destroy(&cond_client);
+ thd.mdl_context.release_transactional_locks();
+ }
+ mysql_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_destroy(&mutex);
+ mysql_cond_destroy(&cond);
+ mysql_cond_destroy(&cond_client);
thd.unlink(); // Must be unlinked under lock
- x_free(thd.query());
+ my_free(thd.query());
thd.security_ctx->user= thd.security_ctx->host=0;
thread_count--;
delayed_insert_threads--;
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
- VOID(pthread_cond_broadcast(&COND_thread_count)); /* Tell main we are ready */
+ mysql_mutex_unlock(&LOCK_thread_count);
+ mysql_cond_broadcast(&COND_thread_count); /* Tell main we are ready */
}
/* The following is for checking when we can delete ourselves */
@@ -1892,22 +2064,23 @@ public:
}
void unlock()
{
- pthread_mutex_lock(&LOCK_delayed_insert);
+ mysql_mutex_lock(&LOCK_delayed_insert);
if (!--locks_in_memory)
{
- pthread_mutex_lock(&mutex);
+ mysql_mutex_lock(&mutex);
if (thd.killed && ! stacked_inserts && ! tables_in_use)
{
- pthread_cond_signal(&cond);
+ mysql_cond_signal(&cond);
status=1;
}
- pthread_mutex_unlock(&mutex);
+ mysql_mutex_unlock(&mutex);
}
- pthread_mutex_unlock(&LOCK_delayed_insert);
+ mysql_mutex_unlock(&LOCK_delayed_insert);
}
inline uint lock_count() { return locks_in_memory; }
TABLE* get_local_table(THD* client_thd);
+ bool open_and_lock_table();
bool handle_inserts(void);
};
@@ -1924,7 +2097,7 @@ static
Delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list)
{
thd_proc_info(thd, "waiting for delay_list");
- pthread_mutex_lock(&LOCK_delayed_insert); // Protect master list
+ mysql_mutex_lock(&LOCK_delayed_insert); // Protect master list
I_List_iterator<Delayed_insert> it(delayed_threads);
Delayed_insert *di;
while ((di= it++))
@@ -1936,7 +2109,7 @@ Delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list)
break;
}
}
- pthread_mutex_unlock(&LOCK_delayed_insert); // For unlink from list
+ mysql_mutex_unlock(&LOCK_delayed_insert); // For unlink from list
return di;
}
@@ -1981,13 +2154,13 @@ Delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list)
a given consumer (delayed insert thread), only at different
stages of producer-consumer relationship.
- 'dead' and 'status' variables in Delayed_insert are redundant
- too, since there is already 'di->thd.killed' and
- di->stacked_inserts.
+ The 'status' variable in Delayed_insert is redundant
+ too, since there is already di->stacked_inserts.
*/
static
-bool delayed_get_table(THD *thd, TABLE_LIST *table_list)
+bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
+ TABLE_LIST *table_list)
{
int error;
Delayed_insert *di;
@@ -2006,7 +2179,7 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list)
if (delayed_insert_threads >= thd->variables.max_insert_delayed_threads)
DBUG_RETURN(0);
thd_proc_info(thd, "Creating delayed handler");
- pthread_mutex_lock(&LOCK_delayed_create);
+ mysql_mutex_lock(&LOCK_delayed_create);
/*
The first search above was done without LOCK_delayed_create.
Another thread might have created the handler in between. Search again.
@@ -2014,55 +2187,71 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list)
if (! (di= find_handler(thd, table_list)))
{
if (!(di= new Delayed_insert()))
- {
- thd->fatal_error();
goto end_create;
- }
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
thread_count++;
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
/*
Annotating delayed inserts is not supported.
*/
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)), 0);
+ 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)
{
/* The error is reported */
delete di;
- thd->fatal_error();
goto end_create;
}
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.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, "", "",
+ MDL_INTENTION_EXCLUSIVE, MDL_STATEMENT);
+ di->grl_protection.ticket= grl_protection_request->ticket;
+ init_mdl_requests(&di->table_list);
+ di->table_list.mdl_request.ticket= table_list->mdl_request.ticket;
+
di->lock();
- pthread_mutex_lock(&di->mutex);
- if ((error= pthread_create(&di->thd.real_id, &connection_attrib,
- handle_delayed_insert, (void*) di)))
+ mysql_mutex_lock(&di->mutex);
+ if ((error= mysql_thread_create(key_thread_delayed_insert,
+ &di->thd.real_id, &connection_attrib,
+ handle_delayed_insert, (void*) di)))
{
DBUG_PRINT("error",
("Can't create thread to handle delayed insert (error %d)",
error));
- pthread_mutex_unlock(&di->mutex);
+ mysql_mutex_unlock(&di->mutex);
di->unlock();
delete di;
- my_error(ER_CANT_CREATE_THREAD, MYF(0), error);
- thd->fatal_error();
+ my_error(ER_CANT_CREATE_THREAD, MYF(ME_FATALERROR), error);
goto end_create;
}
- /* Wait until table is open */
+ /*
+ Wait until table is open unless the handler thread or the connection
+ thread has been killed. Note that we in all cases must wait until the
+ handler thread has been properly initialized before exiting. Otherwise
+ we risk doing clone_ticket() on a ticket that is no longer valid.
+ */
thd_proc_info(thd, "waiting for handler open");
- while (!di->thd.killed && !di->table && !thd->killed)
+ while (!di->handler_thread_initialized ||
+ (!di->thd.killed && !di->table && !thd->killed))
{
- pthread_cond_wait(&di->cond_client, &di->mutex);
+ mysql_cond_wait(&di->cond_client, &di->mutex);
}
- pthread_mutex_unlock(&di->mutex);
+ mysql_mutex_unlock(&di->mutex);
thd_proc_info(thd, "got old table");
+ if (thd->killed)
+ {
+ di->unlock();
+ goto end_create;
+ }
if (di->thd.killed)
{
if (di->thd.is_error())
@@ -2070,30 +2259,26 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list)
/*
Copy the error message. Note that we don't treat fatal
errors in the delayed thread as fatal errors in the
- main thread. Use of my_message will enable stored
- procedures continue handlers.
+ main thread. If delayed thread was killed, we don't
+ want to send "Server shutdown in progress" in the
+ INSERT THREAD.
*/
- my_message(di->thd.main_da.sql_errno(), di->thd.main_da.message(),
+ my_message(di->thd.stmt_da->sql_errno(), di->thd.stmt_da->message(),
MYF(0));
- }
- di->unlock();
+ }
+ di->unlock();
goto end_create;
}
- if (thd->killed)
- {
- di->unlock();
- goto end_create;
- }
- pthread_mutex_lock(&LOCK_delayed_insert);
+ mysql_mutex_lock(&LOCK_delayed_insert);
delayed_threads.append(di);
- pthread_mutex_unlock(&LOCK_delayed_insert);
+ mysql_mutex_unlock(&LOCK_delayed_insert);
}
- pthread_mutex_unlock(&LOCK_delayed_create);
+ mysql_mutex_unlock(&LOCK_delayed_create);
}
- pthread_mutex_lock(&di->mutex);
+ mysql_mutex_lock(&di->mutex);
table_list->table= di->get_local_table(thd);
- pthread_mutex_unlock(&di->mutex);
+ mysql_mutex_unlock(&di->mutex);
if (table_list->table)
{
DBUG_ASSERT(! thd->is_error());
@@ -2104,7 +2289,7 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list)
DBUG_RETURN((table_list->table == NULL));
end_create:
- pthread_mutex_unlock(&LOCK_delayed_create);
+ mysql_mutex_unlock(&LOCK_delayed_create);
DBUG_RETURN(thd->is_error());
}
@@ -2142,17 +2327,33 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
if (!thd.lock) // Table is not locked
{
thd_proc_info(client_thd, "waiting for handler lock");
- pthread_cond_signal(&cond); // Tell handler to lock table
- while (!dead && !thd.lock && ! client_thd->killed)
+ mysql_cond_signal(&cond); // Tell handler to lock table
+ while (!thd.killed && !thd.lock && ! client_thd->killed)
{
- pthread_cond_wait(&cond_client,&mutex);
+ mysql_cond_wait(&cond_client, &mutex);
}
thd_proc_info(client_thd, "got handler lock");
if (client_thd->killed)
goto error;
- if (dead)
+ if (thd.killed)
{
- my_message(thd.main_da.sql_errno(), thd.main_da.message(), MYF(0));
+ /*
+ Copy the error message. Note that we don't treat fatal
+ errors in the delayed thread as fatal errors in the
+ main thread. If delayed thread was killed, we don't
+ want to send "Server shutdown in progress" in the
+ INSERT THREAD.
+
+ The thread could be killed with an error message if
+ di->handle_inserts() or di->open_and_lock_table() fails.
+ The thread could be killed without an error message if
+ killed using mysql_notify_thread_having_shared_lock() or
+ kill_delayed_threads_for_table().
+ */
+ if (!thd.is_error())
+ my_message(ER_QUERY_INTERRUPTED, ER(ER_QUERY_INTERRUPTED), MYF(0));
+ else
+ my_message(thd.stmt_da->sql_errno(), thd.stmt_da->message(), MYF(0));
goto error;
}
}
@@ -2189,6 +2390,9 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
bitmap= (uchar*) (field + share->fields + 1);
copy->record[0]= (bitmap + share->column_bitmap_size*3);
memcpy((char*) copy->record[0], (char*) table->record[0], share->reclength);
+ /* Ensure we don't use the table list of the original table */
+ copy->pos_in_table_list= 0;
+
/*
Make a copy of all fields.
The copied fields need to point into the copied record. This is done
@@ -2264,7 +2468,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
error:
tables_in_use--;
status=1;
- pthread_cond_signal(&cond); // Inform thread about abort
+ mysql_cond_signal(&cond); // Inform thread about abort
DBUG_RETURN(0);
}
@@ -2283,9 +2487,9 @@ int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic,
(ulong) query.length));
thd_proc_info(thd, "waiting for handler insert");
- pthread_mutex_lock(&di->mutex);
+ mysql_mutex_lock(&di->mutex);
while (di->stacked_inserts >= delayed_queue_size && !thd->killed)
- pthread_cond_wait(&di->cond_client,&di->mutex);
+ mysql_cond_wait(&di->cond_client, &di->mutex);
thd_proc_info(thd, "storing row into queue");
if (thd->killed)
@@ -2307,7 +2511,7 @@ int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic,
row= new delayed_row(query, duplic, ignore, log_on);
if (row == NULL)
{
- my_free(query.str, MYF(MY_WME));
+ my_free(query.str);
goto err;
}
@@ -2362,15 +2566,15 @@ int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic,
di->status=1;
if (table->s->blob_fields)
unlink_blobs(table);
- pthread_cond_signal(&di->cond);
+ mysql_cond_signal(&di->cond);
thread_safe_increment(delayed_rows_in_use,&LOCK_delayed_status);
- pthread_mutex_unlock(&di->mutex);
+ mysql_mutex_unlock(&di->mutex);
DBUG_RETURN(0);
err:
delete row;
- pthread_mutex_unlock(&di->mutex);
+ mysql_mutex_unlock(&di->mutex);
DBUG_RETURN(1);
}
@@ -2383,14 +2587,14 @@ static void end_delayed_insert(THD *thd)
{
DBUG_ENTER("end_delayed_insert");
Delayed_insert *di=thd->di;
- pthread_mutex_lock(&di->mutex);
+ mysql_mutex_lock(&di->mutex);
DBUG_PRINT("info",("tables in use: %d",di->tables_in_use));
if (!--di->tables_in_use || di->thd.killed)
{ // Unlock table
di->status=1;
- pthread_cond_signal(&di->cond);
+ mysql_cond_signal(&di->cond);
}
- pthread_mutex_unlock(&di->mutex);
+ mysql_mutex_unlock(&di->mutex);
DBUG_VOID_RETURN;
}
@@ -2399,17 +2603,17 @@ static void end_delayed_insert(THD *thd)
void kill_delayed_threads(void)
{
- VOID(pthread_mutex_lock(&LOCK_delayed_insert)); // For unlink from list
+ mysql_mutex_lock(&LOCK_delayed_insert); // For unlink from list
I_List_iterator<Delayed_insert> it(delayed_threads);
Delayed_insert *di;
while ((di= it++))
{
- di->thd.killed= KILL_SYSTEM_THREAD;
- pthread_mutex_lock(&di->thd.LOCK_thd_data);
+ di->thd.killed= KILL_CONNECTION;
+ mysql_mutex_lock(&di->thd.LOCK_thd_data);
if (di->thd.mysys_var)
{
- pthread_mutex_lock(&di->thd.mysys_var->mutex);
+ mysql_mutex_lock(&di->thd.mysys_var->mutex);
if (di->thd.mysys_var->current_cond)
{
/*
@@ -2417,212 +2621,114 @@ void kill_delayed_threads(void)
in handle_delayed_insert()
*/
if (&di->mutex != di->thd.mysys_var->current_mutex)
- my_pthread_mutex_lock(di->thd.mysys_var->current_mutex,
- MYF_NO_DEADLOCK_DETECTION);
- pthread_cond_broadcast(di->thd.mysys_var->current_cond);
+ mysql_mutex_lock(di->thd.mysys_var->current_mutex);
+ mysql_cond_broadcast(di->thd.mysys_var->current_cond);
if (&di->mutex != di->thd.mysys_var->current_mutex)
- pthread_mutex_unlock(di->thd.mysys_var->current_mutex);
+ mysql_mutex_unlock(di->thd.mysys_var->current_mutex);
}
- pthread_mutex_unlock(&di->thd.mysys_var->mutex);
+ mysql_mutex_unlock(&di->thd.mysys_var->mutex);
}
- pthread_mutex_unlock(&di->thd.LOCK_thd_data);
+ mysql_mutex_unlock(&di->thd.LOCK_thd_data);
}
- VOID(pthread_mutex_unlock(&LOCK_delayed_insert)); // For unlink from list
+ mysql_mutex_unlock(&LOCK_delayed_insert); // For unlink from list
}
-static void handle_delayed_insert_impl(THD *thd, Delayed_insert *di)
+/**
+ A strategy for the prelocking algorithm which prevents the
+ delayed insert thread from opening tables with engines which
+ do not support delayed inserts.
+
+ Particularly it allows to abort open_tables() as soon as we
+ discover that we have opened a MERGE table, without acquiring
+ metadata locks on underlying tables.
+*/
+
+class Delayed_prelocking_strategy : public Prelocking_strategy
{
- DBUG_ENTER("handle_delayed_insert_impl");
- thd->thread_stack= (char*) &thd;
- if (init_thr_lock() || thd->store_globals())
- {
- /* Can't use my_error since store_globals has perhaps failed */
- thd->main_da.set_error_status(thd, ER_OUT_OF_RESOURCES,
- ER(ER_OUT_OF_RESOURCES));
- thd->fatal_error();
- goto err;
- }
+public:
+ virtual bool handle_routine(THD *thd, Query_tables_list *prelocking_ctx,
+ Sroutine_hash_entry *rt, sp_head *sp,
+ bool *need_prelocking);
+ virtual bool handle_table(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list, bool *need_prelocking);
+ virtual bool handle_view(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list, bool *need_prelocking);
+};
- /*
- Open table requires an initialized lex in case the table is
- partitioned. The .frm file contains a partial SQL string which is
- parsed using a lex, that depends on initialized thd->lex.
- */
- lex_start(thd);
- thd->lex->sql_command= SQLCOM_INSERT; // For innodb::store_lock()
- /*
- Statement-based replication of INSERT DELAYED has problems with RAND()
- and user vars, so in mixed mode we go to row-based.
- */
- thd->lex->set_stmt_unsafe();
- thd->set_current_stmt_binlog_row_based_if_mixed();
- /* Open table */
- if (!(di->table= open_n_lock_single_table(thd, &di->table_list,
- TL_WRITE_DELAYED)))
- {
- thd->fatal_error(); // Abort waiting inserts
- goto err;
- }
- if (!(di->table->file->ha_table_flags() & HA_CAN_INSERT_DELAYED))
- {
- thd->fatal_error();
- my_error(ER_DELAYED_NOT_SUPPORTED, MYF(0), di->table_list.table_name);
- goto err;
- }
- if (di->table->triggers)
+bool Delayed_prelocking_strategy::
+handle_table(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list, bool *need_prelocking)
+{
+ DBUG_ASSERT(table_list->lock_type == TL_WRITE_DELAYED);
+
+ if (!(table_list->table->file->ha_table_flags() & HA_CAN_INSERT_DELAYED))
{
- /*
- Table has triggers. This is not an error, but we do
- not support triggers with delayed insert. Terminate the delayed
- thread without an error and thus request lock upgrade.
- */
- goto err;
+ my_error(ER_DELAYED_NOT_SUPPORTED, MYF(0), table_list->table_name);
+ return TRUE;
}
- di->table->copy_blobs=1;
+ return FALSE;
+}
- /* Tell client that the thread is initialized */
- pthread_cond_signal(&di->cond_client);
- /* Now wait until we get an insert or lock to handle */
- /* We will not abort as long as a client thread uses this thread */
+bool Delayed_prelocking_strategy::
+handle_routine(THD *thd, Query_tables_list *prelocking_ctx,
+ Sroutine_hash_entry *rt, sp_head *sp,
+ bool *need_prelocking)
+{
+ /* LEX used by the delayed insert thread has no routines. */
+ DBUG_ASSERT(0);
+ return FALSE;
+}
- for (;;)
- {
- if (thd->killed >= KILL_CONNECTION)
- {
- uint lock_count;
- /*
- Remove this from delay insert list so that no one can request a
- table from this
- */
- pthread_mutex_unlock(&di->mutex);
- pthread_mutex_lock(&LOCK_delayed_insert);
- di->unlink();
- lock_count=di->lock_count();
- pthread_mutex_unlock(&LOCK_delayed_insert);
- pthread_mutex_lock(&di->mutex);
- if (!lock_count && !di->tables_in_use && !di->stacked_inserts)
- break; // Time to die
- }
- if (!di->status && !di->stacked_inserts)
- {
- struct timespec abstime;
- set_timespec(abstime, delayed_insert_timeout);
+bool Delayed_prelocking_strategy::
+handle_view(THD *thd, Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list, bool *need_prelocking)
+{
+ /* We don't open views in the delayed insert thread. */
+ DBUG_ASSERT(0);
+ return FALSE;
+}
- /* Information for pthread_kill */
- di->thd.mysys_var->current_mutex= &di->mutex;
- di->thd.mysys_var->current_cond= &di->cond;
- thd_proc_info(&(di->thd), "Waiting for INSERT");
- DBUG_PRINT("info",("Waiting for someone to insert rows"));
- while (!thd->killed)
- {
- int error;
-#if defined(HAVE_BROKEN_COND_TIMEDWAIT)
- error=pthread_cond_wait(&di->cond,&di->mutex);
-#else
- error=pthread_cond_timedwait(&di->cond,&di->mutex,&abstime);
-#ifdef EXTRA_DEBUG
- if (error && error != EINTR && error != ETIMEDOUT)
- {
- fprintf(stderr, "Got error %d from pthread_cond_timedwait\n",error);
- DBUG_PRINT("error",("Got error %d from pthread_cond_timedwait",
- error));
- }
-#endif
-#endif
- if (thd->killed || di->status)
- break;
- if (error == ETIMEDOUT || error == ETIME)
- {
- thd->killed= KILL_SYSTEM_THREAD;
- break;
- }
- }
- /* We can't lock di->mutex and mysys_var->mutex at the same time */
- pthread_mutex_unlock(&di->mutex);
- pthread_mutex_lock(&di->thd.mysys_var->mutex);
- di->thd.mysys_var->current_mutex= 0;
- di->thd.mysys_var->current_cond= 0;
- pthread_mutex_unlock(&di->thd.mysys_var->mutex);
- pthread_mutex_lock(&di->mutex);
- }
- thd_proc_info(&(di->thd), 0);
+/**
+ Open and lock table for use by delayed thread and check that
+ this table is suitable for delayed inserts.
- if (di->tables_in_use && ! thd->lock)
- {
- bool not_used;
- /*
- Request for new delayed insert.
- Lock the table, but avoid to be blocked by a global read lock.
- If we got here while a global read lock exists, then one or more
- inserts started before the lock was requested. These are allowed
- to complete their work before the server returns control to the
- client which requested the global read lock. The delayed insert
- handler will close the table and finish when the outstanding
- inserts are done.
- */
- if (! (thd->lock= mysql_lock_tables(thd, &di->table, 1,
- MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK,
- &not_used)))
- {
- /* Fatal error */
- di->dead= 1;
- thd->killed= KILL_SYSTEM_THREAD;
- }
- pthread_cond_broadcast(&di->cond_client);
- }
- if (di->stacked_inserts)
- {
- if (di->handle_inserts())
- {
- /* Some fatal error */
- di->dead= 1;
- thd->killed= KILL_SYSTEM_THREAD;
- }
- }
- di->status=0;
- if (!di->stacked_inserts && !di->tables_in_use && thd->lock)
- {
- /*
- No one is doing a insert delayed
- Unlock table so that other threads can use it
- */
- MYSQL_LOCK *lock=thd->lock;
- thd->lock=0;
- pthread_mutex_unlock(&di->mutex);
- /*
- We need to release next_insert_id before unlocking. This is
- enforced by handler::ha_external_lock().
- */
- di->table->file->ha_release_auto_increment();
- mysql_unlock_tables(thd, lock);
- ha_autocommit_or_rollback(thd, 0);
- di->group_count=0;
- pthread_mutex_lock(&di->mutex);
- }
- if (di->tables_in_use)
- pthread_cond_broadcast(&di->cond_client); // If waiting clients
- }
+ @retval FALSE - Success.
+ @retval TRUE - Failure.
+*/
+
+bool Delayed_insert::open_and_lock_table()
+{
+ Delayed_prelocking_strategy prelocking_strategy;
-err:
/*
- mysql_lock_tables() can potentially start a transaction and write
- a table map. In the event of an error, that transaction has to be
- rolled back. We only need to roll back a potential statement
- transaction, since real transactions are rolled back in
- close_thread_tables().
-
- TODO: This is not true any more, table maps are generated on the
- first call to ha_*_row() instead. Remove code that are used to
- cover for the case outlined above.
- */
- ha_autocommit_or_rollback(thd, 1);
+ Use special prelocking strategy to get ER_DELAYED_NOT_SUPPORTED
+ error for tables with engines which don't support delayed inserts.
+ */
+ if (!(table= open_n_lock_single_table(&thd, &table_list,
+ TL_WRITE_DELAYED,
+ MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK,
+ &prelocking_strategy)))
+ {
+ thd.fatal_error(); // Abort waiting inserts
+ return TRUE;
+ }
- DBUG_VOID_RETURN;
+ if (table->triggers)
+ {
+ /*
+ Table has triggers. This is not an error, but we do
+ not support triggers with delayed insert. Terminate the delayed
+ thread without an error and thus request lock upgrade.
+ */
+ return TRUE;
+ }
+ table->copy_blobs= 1;
+ return FALSE;
}
@@ -2637,55 +2743,220 @@ 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' */
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
thd->set_current_time();
threads.append(thd);
- thd->killed=abort_loop ? KILL_SYSTEM_THREAD : NOT_KILLED;
- pthread_mutex_unlock(&LOCK_thread_count);
+ if (abort_loop)
+ thd->killed= KILL_CONNECTION;
+ else
+ thd->reset_killed();
+ mysql_mutex_unlock(&LOCK_thread_count);
+
+ mysql_thread_set_psi_id(thd->thread_id);
/*
- Wait until the client runs into pthread_cond_wait(),
+ Wait until the client runs into mysql_cond_wait(),
where we free it after the table is opened and di linked in the list.
If we did not wait here, the client might detect the opened table
before it is linked to the list. It would release LOCK_delayed_create
and allow another thread to create another handler for the same table,
since it does not find one in the list.
*/
- pthread_mutex_lock(&di->mutex);
-#if !defined( __WIN__) /* Win32 calls this in pthread_create */
+ mysql_mutex_lock(&di->mutex);
if (my_thread_init())
{
/* Can't use my_error since store_globals has not yet been called */
- thd->main_da.set_error_status(thd, ER_OUT_OF_RESOURCES,
- ER(ER_OUT_OF_RESOURCES));
- goto end;
+ thd->stmt_da->set_error_status(thd, ER_OUT_OF_RESOURCES,
+ ER(ER_OUT_OF_RESOURCES), NULL);
+ di->handler_thread_initialized= TRUE;
}
-#endif
+ else
+ {
+ DBUG_ENTER("handle_delayed_insert");
+ thd->thread_stack= (char*) &thd;
+ if (init_thr_lock() || thd->store_globals())
+ {
+ /* Can't use my_error since store_globals has perhaps failed */
+ thd->stmt_da->set_error_status(thd, ER_OUT_OF_RESOURCES,
+ ER(ER_OUT_OF_RESOURCES), NULL);
+ di->handler_thread_initialized= TRUE;
+ thd->fatal_error();
+ goto err;
+ }
+
+ thd->lex->sql_command= SQLCOM_INSERT; // For innodb::store_lock()
- handle_delayed_insert_impl(thd, di);
+ /*
+ INSERT DELAYED has to go to row-based format because the time
+ at which rows are inserted cannot be determined in mixed mode.
+ */
+ thd->set_current_stmt_binlog_format_row_if_mixed();
-#ifndef __WIN__
-end:
+ /*
+ Clone tickets representing protection against GRL and the lock on
+ the target table for the insert and add them to the list of granted
+ metadata locks held by the handler thread. This is safe since the
+ handler thread is not holding nor waiting on any metadata locks.
+ */
+ if (thd->mdl_context.clone_ticket(&di->grl_protection) ||
+ thd->mdl_context.clone_ticket(&di->table_list.mdl_request))
+ {
+ thd->mdl_context.release_transactional_locks();
+ di->handler_thread_initialized= TRUE;
+ goto err;
+ }
+
+ /*
+ Now that the ticket has been cloned, it is safe for the connection
+ thread to exit.
+ */
+ di->handler_thread_initialized= TRUE;
+ di->table_list.mdl_request.ticket= NULL;
+
+ if (di->open_and_lock_table())
+ goto err;
+
+ /* Tell client that the thread is initialized */
+ mysql_cond_signal(&di->cond_client);
+
+ /* Now wait until we get an insert or lock to handle */
+ /* We will not abort as long as a client thread uses this thread */
+
+ for (;;)
+ {
+ if (thd->killed)
+ {
+ uint lock_count;
+ /*
+ Remove this from delay insert list so that no one can request a
+ table from this
+ */
+ mysql_mutex_unlock(&di->mutex);
+ mysql_mutex_lock(&LOCK_delayed_insert);
+ di->unlink();
+ lock_count=di->lock_count();
+ mysql_mutex_unlock(&LOCK_delayed_insert);
+ mysql_mutex_lock(&di->mutex);
+ if (!lock_count && !di->tables_in_use && !di->stacked_inserts)
+ break; // Time to die
+ }
+
+ /* Shouldn't wait if killed or an insert is waiting. */
+ if (!thd->killed && !di->status && !di->stacked_inserts)
+ {
+ struct timespec abstime;
+ set_timespec(abstime, delayed_insert_timeout);
+
+ /* Information for pthread_kill */
+ mysql_mutex_unlock(&di->mutex);
+ mysql_mutex_lock(&di->thd.mysys_var->mutex);
+ di->thd.mysys_var->current_mutex= &di->mutex;
+ di->thd.mysys_var->current_cond= &di->cond;
+ mysql_mutex_unlock(&di->thd.mysys_var->mutex);
+ mysql_mutex_lock(&di->mutex);
+ thd_proc_info(&(di->thd), "Waiting for INSERT");
+
+ DBUG_PRINT("info",("Waiting for someone to insert rows"));
+ while (!thd->killed && !di->status)
+ {
+ int error;
+ mysql_audit_release(thd);
+ error= mysql_cond_timedwait(&di->cond, &di->mutex, &abstime);
+#ifdef EXTRA_DEBUG
+ if (error && error != EINTR && error != ETIMEDOUT)
+ {
+ fprintf(stderr, "Got error %d from mysql_cond_timedwait\n", error);
+ DBUG_PRINT("error", ("Got error %d from mysql_cond_timedwait",
+ error));
+ }
#endif
- /*
- di should be unlinked from the thread handler list and have no active
- clients
- */
+ if (error == ETIMEDOUT || error == ETIME)
+ thd->killed= KILL_CONNECTION;
+ }
+ /* We can't lock di->mutex and mysys_var->mutex at the same time */
+ mysql_mutex_unlock(&di->mutex);
+ mysql_mutex_lock(&di->thd.mysys_var->mutex);
+ di->thd.mysys_var->current_mutex= 0;
+ di->thd.mysys_var->current_cond= 0;
+ mysql_mutex_unlock(&di->thd.mysys_var->mutex);
+ mysql_mutex_lock(&di->mutex);
+ }
+ thd_proc_info(&(di->thd), 0);
+
+ if (di->tables_in_use && ! thd->lock && !thd->killed)
+ {
+ /*
+ Request for new delayed insert.
+ Lock the table, but avoid to be blocked by a global read lock.
+ If we got here while a global read lock exists, then one or more
+ inserts started before the lock was requested. These are allowed
+ to complete their work before the server returns control to the
+ client which requested the global read lock. The delayed insert
+ handler will close the table and finish when the outstanding
+ inserts are done.
+ */
+ if (! (thd->lock= mysql_lock_tables(thd, &di->table, 1, 0)))
+ {
+ /* Fatal error */
+ thd->killed= KILL_CONNECTION;
+ }
+ mysql_cond_broadcast(&di->cond_client);
+ }
+ if (di->stacked_inserts)
+ {
+ if (di->handle_inserts())
+ {
+ /* Some fatal error */
+ thd->killed= KILL_CONNECTION;
+ }
+ }
+ di->status=0;
+ if (!di->stacked_inserts && !di->tables_in_use && thd->lock)
+ {
+ /*
+ No one is doing a insert delayed
+ Unlock table so that other threads can use it
+ */
+ MYSQL_LOCK *lock=thd->lock;
+ thd->lock=0;
+ mysql_mutex_unlock(&di->mutex);
+ /*
+ We need to release next_insert_id before unlocking. This is
+ enforced by handler::ha_external_lock().
+ */
+ di->table->file->ha_release_auto_increment();
+ mysql_unlock_tables(thd, lock);
+ trans_commit_stmt(thd);
+ di->group_count=0;
+ mysql_audit_release(thd);
+ mysql_mutex_lock(&di->mutex);
+ }
+ if (di->tables_in_use)
+ mysql_cond_broadcast(&di->cond_client); // If waiting clients
+ }
+
+ err:
+ DBUG_LEAVE;
+ }
di->table=0;
- di->dead= 1; // If error
- thd->killed= KILL_SYSTEM_THREAD; // If error
- pthread_mutex_unlock(&di->mutex);
+ thd->killed= KILL_CONNECTION; // If error
+ mysql_mutex_unlock(&di->mutex);
close_thread_tables(thd); // Free the table
- pthread_cond_broadcast(&di->cond_client); // Safety
+ thd->mdl_context.release_transactional_locks();
+ mysql_cond_broadcast(&di->cond_client); // Safety
- pthread_mutex_lock(&LOCK_delayed_create); // Because of delayed_get_table
- pthread_mutex_lock(&LOCK_delayed_insert);
+ mysql_mutex_lock(&LOCK_delayed_create); // Because of delayed_get_table
+ mysql_mutex_lock(&LOCK_delayed_insert);
+ /*
+ di should be unlinked from the thread handler list and have no active
+ clients
+ */
delete di;
- pthread_mutex_unlock(&LOCK_delayed_insert);
- pthread_mutex_unlock(&LOCK_delayed_create);
+ mysql_mutex_unlock(&LOCK_delayed_insert);
+ mysql_mutex_unlock(&LOCK_delayed_create);
my_thread_end();
pthread_exit(0);
@@ -2715,7 +2986,7 @@ static void free_delayed_insert_blobs(register TABLE *table)
{
uchar *str;
((Field_blob *) (*ptr))->get_ptr(&str);
- my_free(str,MYF(MY_ALLOW_ZERO_PTR));
+ my_free(str);
((Field_blob *) (*ptr))->reset();
}
}
@@ -2726,19 +2997,21 @@ bool Delayed_insert::handle_inserts(void)
{
int error;
ulong max_rows;
+ bool has_trans = TRUE;
bool using_ignore= 0, using_opt_replace= 0,
using_bin_log= mysql_bin_log.is_open();
delayed_row *row;
DBUG_ENTER("handle_inserts");
/* Allow client to insert new rows */
- pthread_mutex_unlock(&mutex);
+ mysql_mutex_unlock(&mutex);
table->next_number_field=table->found_next_number_field;
table->use_all_columns();
thd_proc_info(&thd, "upgrading lock");
- if (thr_upgrade_write_delay_lock(*thd.lock->locks, delayed_lock))
+ if (thr_upgrade_write_delay_lock(*thd.lock->locks, delayed_lock,
+ thd.variables.lock_wait_timeout))
{
/*
This can happen if thread is killed either by a shutdown
@@ -2752,7 +3025,7 @@ bool Delayed_insert::handle_inserts(void)
thd_proc_info(&thd, "insert");
max_rows= delayed_insert_limit;
- if (thd.killed || table->needs_reopen_or_name_lock())
+ if (thd.killed || table->s->has_old_version())
{
thd.killed= KILL_SYSTEM_THREAD;
max_rows= ULONG_MAX; // Do as much as possible
@@ -2765,12 +3038,12 @@ bool Delayed_insert::handle_inserts(void)
*/
if (!using_bin_log)
table->file->extra(HA_EXTRA_WRITE_CACHE);
- pthread_mutex_lock(&mutex);
+ mysql_mutex_lock(&mutex);
while ((row=rows.get()))
{
stacked_inserts--;
- pthread_mutex_unlock(&mutex);
+ mysql_mutex_unlock(&mutex);
memcpy(table->record[0],row->record,table->s->reclength);
thd.start_time=row->start_time;
@@ -2788,6 +3061,13 @@ bool Delayed_insert::handle_inserts(void)
if (log_query)
{
/*
+ Guaranteed that the INSERT DELAYED STMT will not be here
+ in SBR when mysql binlog is enabled.
+ */
+ DBUG_ASSERT(!(mysql_bin_log.is_open() &&
+ !thd.is_current_stmt_binlog_format_row()));
+
+ /*
This is the first value of an INSERT statement.
It is the right place to clear a forced insert_id.
This is usually done after the last value of an INSERT statement,
@@ -2854,44 +3134,17 @@ bool Delayed_insert::handle_inserts(void)
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
}
- if (log_query && mysql_bin_log.is_open())
- {
- bool backup_time_zone_used = thd.time_zone_used;
- Time_zone *backup_time_zone = thd.variables.time_zone;
- if (row->time_zone != NULL)
- {
- thd.time_zone_used = true;
- thd.variables.time_zone = row->time_zone;
- }
-
- /* if the delayed insert was killed, the killed status is
- ignored while binlogging */
- int errcode= 0;
- if (thd.killed == NOT_KILLED)
- errcode= query_error_code(&thd, TRUE);
-
- /*
- If the query has several rows to insert, only the first row will come
- here. In row-based binlogging, this means that the first row will be
- written to binlog as one Table_map event and one Rows event (due to an
- event flush done in binlog_query()), then all other rows of this query
- will be binlogged together as one single Table_map event and one
- single Rows event.
- */
- if (thd.binlog_query(THD::ROW_QUERY_TYPE,
- row->query.str, row->query.length,
- FALSE, FALSE, errcode))
- goto err;
-
- thd.time_zone_used = backup_time_zone_used;
- thd.variables.time_zone = backup_time_zone;
- }
-
if (table->s->blob_fields)
free_delayed_insert_blobs(table);
thread_safe_decrement(delayed_rows_in_use,&LOCK_delayed_status);
thread_safe_increment(delayed_insert_writes,&LOCK_delayed_status);
- pthread_mutex_lock(&mutex);
+ mysql_mutex_lock(&mutex);
+
+ /*
+ Reset the table->auto_increment_field_not_null as it is valid for
+ only one row.
+ */
+ table->auto_increment_field_not_null= FALSE;
delete row;
/*
@@ -2907,19 +3160,20 @@ bool Delayed_insert::handle_inserts(void)
if (stacked_inserts || tables_in_use) // Let these wait a while
{
if (tables_in_use)
- pthread_cond_broadcast(&cond_client); // If waiting clients
+ mysql_cond_broadcast(&cond_client); // If waiting clients
thd_proc_info(&thd, "reschedule");
- pthread_mutex_unlock(&mutex);
+ mysql_mutex_unlock(&mutex);
if ((error=table->file->extra(HA_EXTRA_NO_CACHE)))
{
/* This should never happen */
table->file->print_error(error,MYF(0));
- sql_print_error("%s", thd.main_da.message());
+ sql_print_error("%s", thd.stmt_da->message());
DBUG_PRINT("error", ("HA_EXTRA_NO_CACHE failed in loop"));
goto err;
}
query_cache_invalidate3(&thd, table, 1);
- if (thr_reschedule_write_lock(*thd.lock->locks))
+ if (thr_reschedule_write_lock(*thd.lock->locks,
+ thd.variables.lock_wait_timeout))
{
/* This is not known to happen. */
my_error(ER_DELAYED_CANT_CHANGE_LOCK,
@@ -2929,15 +3183,15 @@ bool Delayed_insert::handle_inserts(void)
}
if (!using_bin_log)
table->file->extra(HA_EXTRA_WRITE_CACHE);
- pthread_mutex_lock(&mutex);
+ mysql_mutex_lock(&mutex);
thd_proc_info(&thd, "insert");
}
if (tables_in_use)
- pthread_cond_broadcast(&cond_client); // If waiting clients
+ mysql_cond_broadcast(&cond_client); // If waiting clients
}
}
thd_proc_info(&thd, 0);
- pthread_mutex_unlock(&mutex);
+ mysql_mutex_unlock(&mutex);
/*
We need to flush the pending event when using row-based
@@ -2950,20 +3204,22 @@ bool Delayed_insert::handle_inserts(void)
or trigger.
TODO: Move the logging to last in the sequence of rows.
- */
- if (thd.current_stmt_binlog_row_based &&
- thd.binlog_flush_pending_rows_event(TRUE))
+ */
+ has_trans= thd.lex->sql_command == SQLCOM_CREATE_TABLE ||
+ table->file->has_transactions();
+ if (thd.is_current_stmt_binlog_format_row() &&
+ thd.binlog_flush_pending_rows_event(TRUE, has_trans))
goto err;
if ((error=table->file->extra(HA_EXTRA_NO_CACHE)))
{ // This shouldn't happen
table->file->print_error(error,MYF(0));
- sql_print_error("%s", thd.main_da.message());
+ sql_print_error("%s", thd.stmt_da->message());
DBUG_PRINT("error", ("HA_EXTRA_NO_CACHE failed after loop"));
goto err;
}
query_cache_invalidate3(&thd, table, 1);
- pthread_mutex_lock(&mutex);
+ mysql_mutex_lock(&mutex);
DBUG_RETURN(0);
err:
@@ -2987,7 +3243,7 @@ bool Delayed_insert::handle_inserts(void)
}
DBUG_PRINT("error", ("dropped %lu rows after an error", max_rows));
thread_safe_increment(delayed_insert_errors, &LOCK_delayed_status);
- pthread_mutex_lock(&mutex);
+ mysql_mutex_lock(&mutex);
DBUG_RETURN(1);
}
#endif /* EMBEDDED_LIBRARY */
@@ -3017,19 +3273,6 @@ bool mysql_insert_select_prepare(THD *thd)
/*
- Statement-based replication of INSERT ... SELECT ... LIMIT is not safe
- as order of rows is not defined, so in mixed mode we go to row-based.
-
- Note that we may consider a statement as safe if ORDER BY primary_key
- is present or we SELECT a constant. However it may confuse users to
- see very similiar statements replicated differently.
- */
- if (lex->current_select->select_limit)
- {
- lex->set_stmt_unsafe();
- thd->set_current_stmt_binlog_row_based_if_mixed();
- }
- /*
SELECT_LEX do not belong to INSERT statement, so we can't add WHERE
clause if table is VIEW
*/
@@ -3118,6 +3361,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
res= (setup_fields(thd, 0, values, MARK_COLUMNS_READ, 0, 0) ||
check_insert_fields(thd, table_list, *fields, values,
!insert_into_view, 1, &map));
+
if (!res && fields->elements)
{
bool saved_abort_on_warning= thd->abort_on_warning;
@@ -3142,9 +3386,16 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
context->resolve_in_table_list_only(table_list);
lex->select_lex.no_wrap_view_item= TRUE;
- res= res || check_update_fields(thd, context->table_list,
- *info.update_fields, *info.update_values,
- &map);
+ res= res ||
+ check_update_fields(thd, context->table_list,
+ *info.update_fields, *info.update_values,
+ /*
+ In INSERT SELECT ON DUPLICATE KEY UPDATE col=x
+ 'x' can legally refer to a non-inserted table.
+ 'x' is not even resolved yet.
+ */
+ true,
+ &map);
lex->select_lex.no_wrap_view_item= FALSE;
/*
When we are not using GROUP BY and there are no ungrouped aggregate functions
@@ -3206,7 +3457,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
lex->current_select->join->select_options|= OPTION_BUFFER_RESULT;
}
else if (!(lex->current_select->options & OPTION_BUFFER_RESULT) &&
- !thd->prelocked_mode)
+ thd->locked_tables_mode <= LTM_LOCK_TABLES)
{
/*
We must not yet prepare the result table if it is the same as one of the
@@ -3272,7 +3523,7 @@ int select_insert::prepare2(void)
{
DBUG_ENTER("select_insert::prepare2");
if (thd->lex->current_select->options & OPTION_BUFFER_RESULT &&
- !thd->prelocked_mode)
+ thd->locked_tables_mode <= LTM_LOCK_TABLES)
table->file->ha_start_bulk_insert((ha_rows) 0);
DBUG_RETURN(0);
}
@@ -3309,6 +3560,8 @@ int select_insert::send_data(List<Item> &values)
unit->offset_limit_cnt--;
DBUG_RETURN(0);
}
+ if (thd->killed == ABORT_QUERY)
+ DBUG_RETURN(0);
thd->count_cuted_fields= CHECK_FIELD_WARN; // Calculate cuted fields
store_values(values);
@@ -3327,7 +3580,7 @@ int select_insert::send_data(List<Item> &values)
DBUG_RETURN(1);
}
}
-
+
// Release latches in case bulk insert takes a long time
ha_release_temporary_latches(thd);
@@ -3392,14 +3645,18 @@ bool select_insert::send_eof()
{
int error;
bool const trans_table= table->file->has_transactions();
- ulonglong id;
+ ulonglong id, row_count;
bool changed;
killed_state killed_status= thd->killed;
DBUG_ENTER("select_insert::send_eof");
DBUG_PRINT("enter", ("trans_table=%d, table_type='%s'",
trans_table, table->file->table_type()));
- error= (!thd->prelocked_mode) ? table->file->ha_end_bulk_insert() : 0;
+ error= (thd->locked_tables_mode <= LTM_LOCK_TABLES ?
+ table->file->ha_end_bulk_insert() : 0);
+ if (!error && thd->is_error())
+ error= thd->stmt_da->sql_errno();
+
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
@@ -3410,15 +3667,17 @@ bool select_insert::send_eof()
and ha_autocommit_or_rollback.
*/
query_cache_invalidate3(thd, table, 1);
- if (thd->transaction.stmt.modified_non_trans_table)
- thd->transaction.all.modified_non_trans_table= TRUE;
}
+
+ if (thd->transaction.stmt.modified_non_trans_table)
+ thd->transaction.all.modified_non_trans_table= TRUE;
+
DBUG_ASSERT(trans_table || !changed ||
thd->transaction.stmt.modified_non_trans_table);
/*
Write to binlog before commiting transaction. No statement will
- be written by the write_to_binlog() below in RBR mode. All the
+ be written by the binlog_query() below in RBR mode. All the
events are in the transaction cache and will be written when
ha_autocommit_or_rollback() is issued below.
*/
@@ -3430,8 +3689,9 @@ bool select_insert::send_eof()
thd->clear_error();
else
errcode= query_error_code(thd, killed_status == NOT_KILLED);
-
- if (write_to_binlog(trans_table, errcode))
+ if (thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query(), thd->query_length(),
+ trans_table, FALSE, FALSE, errcode))
{
table->file->ha_release_auto_increment();
DBUG_RETURN(1);
@@ -3447,26 +3707,27 @@ bool select_insert::send_eof()
char buff[160];
if (info.ignore)
sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records,
- (ulong) (info.records - info.copied), (ulong) thd->cuted_fields);
+ (ulong) (info.records - info.copied),
+ (ulong) thd->warning_info->statement_warn_count());
else
sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records,
- (ulong) (info.deleted+info.updated), (ulong) thd->cuted_fields);
- thd->row_count_func= info.copied + info.deleted +
- ((thd->client_capabilities & CLIENT_FOUND_ROWS) ?
- info.touched : info.updated);
-
+ (ulong) (info.deleted+info.updated),
+ (ulong) thd->warning_info->statement_warn_count());
+ row_count= info.copied + info.deleted +
+ ((thd->client_capabilities & CLIENT_FOUND_ROWS) ?
+ info.touched : info.updated);
id= (thd->first_successful_insert_id_in_cur_stmt > 0) ?
thd->first_successful_insert_id_in_cur_stmt :
(thd->arg_of_last_insert_id_function ?
thd->first_successful_insert_id_in_prev_stmt :
(info.copied ? autoinc_value_of_last_inserted_row : 0));
- ::my_ok(thd, (ulong) thd->row_count_func, id, buff);
+ ::my_ok(thd, row_count, id, buff);
DBUG_RETURN(0);
}
-void select_insert::abort() {
+void select_insert::abort_result_set() {
- DBUG_ENTER("select_insert::abort");
+ DBUG_ENTER("select_insert::abort_result_set");
/*
If the creation of the table failed (due to a syntax error, for
example), no table will have been opened and therefore 'table'
@@ -3480,7 +3741,7 @@ void select_insert::abort() {
If we are not in prelocked mode, we end the bulk insert started
before.
*/
- if (!thd->prelocked_mode)
+ if (thd->locked_tables_mode <= LTM_LOCK_TABLES)
table->file->ha_end_bulk_insert();
/*
@@ -3501,14 +3762,17 @@ void select_insert::abort() {
transactional_table= table->file->has_transactions();
if (thd->transaction.stmt.modified_non_trans_table)
{
+ if (!can_rollback_data())
+ thd->transaction.all.modified_non_trans_table= TRUE;
+
if (mysql_bin_log.is_open())
{
int errcode= query_error_code(thd, thd->killed == NOT_KILLED);
/* error of writing binary log is ignored */
- write_to_binlog(transactional_table, errcode);
+ (void) thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(),
+ thd->query_length(),
+ transactional_table, FALSE, FALSE, errcode);
}
- if (!thd->current_stmt_binlog_row_based && !can_rollback_data())
- thd->transaction.all.modified_non_trans_table= TRUE;
if (changed)
query_cache_invalidate3(thd, table, 1);
}
@@ -3520,147 +3784,48 @@ void select_insert::abort() {
DBUG_VOID_RETURN;
}
-int select_insert::write_to_binlog(bool is_trans, int errcode)
-{
- /* It is only for statement mode */
- if (thd->current_stmt_binlog_row_based)
- return 0;
-
- return thd->binlog_query(THD::ROW_QUERY_TYPE,
- thd->query(), thd->query_length(),
- is_trans, FALSE, errcode);
-}
-
-/* Override the select_insert::write_to_binlog */
-int select_create::write_to_binlog(bool is_trans, int errcode)
-{
- /* It is only for statement mode */
- if (thd->current_stmt_binlog_row_based)
- return 0;
-
- /*
- WL#5370 Keep the compatibility between 5.1 master and 5.5 slave.
- Binlog a 'INSERT ... SELECT' statement only when it has the option
- 'IF NOT EXISTS' and the table already exists as a base table.
- */
- if ((create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) &&
- create_info->table_existed)
- {
- String query;
- int result;
-
- thd->binlog_start_trans_and_stmt();
- /* Binlog the CREATE TABLE IF NOT EXISTS statement */
- result= binlog_show_create_table(&table, 1, 0);
- if (result)
- return result;
-
- uint db_len= strlen(create_table->db);
- uint table_len= strlen(create_info->alias);
- uint select_len= thd->query_length() - thd->lex->create_select_pos;
- uint field_len= (table->s->fields - (field - table->field)) *
- (MAX_FIELD_NAME + 3);
-
- /*
- pre-allocating memory reduces the times of reallocating memory,
- when calling query.appen().
- 40bytes is enough for other words("INSERT IGNORE INTO", etc.).
- */
- if (query.real_alloc(40 + db_len + table_len + field_len + select_len))
- return 1;
-
- if (thd->lex->create_select_in_comment)
- query.append(STRING_WITH_LEN("/*! "));
- if (thd->lex->ignore)
- query.append(STRING_WITH_LEN("INSERT IGNORE INTO "));
- else if (thd->lex->duplicates == DUP_REPLACE)
- query.append(STRING_WITH_LEN("REPLACE INTO "));
- else
- query.append(STRING_WITH_LEN("INSERT INTO "));
-
- append_identifier(thd, &query, create_table->db, db_len);
- query.append(STRING_WITH_LEN("."));
- append_identifier(thd, &query, create_info->alias, table_len);
- query.append(STRING_WITH_LEN(" "));
-
- /*
- The insert items.
- Field is the the rightmost columns that the rows are inster in.
- */
- query.append(STRING_WITH_LEN("("));
- for (Field **f= field ; *f ; f++)
- {
- if (f != field)
- query.append(STRING_WITH_LEN(","));
-
- append_identifier(thd, &query, (*f)->field_name,
- strlen((*f)->field_name));
- }
- query.append(STRING_WITH_LEN(") "));
-
- /* The SELECT clause*/
- DBUG_ASSERT(thd->lex->create_select_pos);
- if (thd->lex->create_select_start_with_brace)
- query.append(STRING_WITH_LEN("("));
- if (query.append(thd->query() + thd->lex->create_select_pos, select_len))
- return 1;
-
- /*
- Avoid to use thd->binlog_query() twice, otherwise it will print the unsafe
- warning twice.
- */
- Query_log_event ev(thd, query.ptr(), query.length(), is_trans,
- FALSE, errcode);
- return mysql_bin_log.write(&ev);
- }
- else
- return select_insert::write_to_binlog(is_trans, errcode);
-}
/***************************************************************************
CREATE TABLE (SELECT) ...
***************************************************************************/
-/*
+/**
Create table from lists of fields and items (or just return TABLE
object for pre-opened existing table).
- SYNOPSIS
- create_table_from_items()
- thd in Thread object
- create_info in Create information (like MAX_ROWS, ENGINE or
- temporary table flag)
- create_table in Pointer to TABLE_LIST object providing database
- and name for table to be created or to be open
- alter_info in/out Initial list of columns and indexes for the table
- to be created
- items in List of items which should be used to produce rest
- of fields for the table (corresponding fields will
- be added to the end of alter_info->create_list)
- lock out Pointer to the MYSQL_LOCK object for table created
- (or open temporary table) will be returned in this
- parameter. Since this table is not included in
- THD::lock caller is responsible for explicitly
- unlocking this table.
- hooks
-
- NOTES
- This function behaves differently for base and temporary tables:
- - For base table we assume that either table exists and was pre-opened
- and locked at open_and_lock_tables() stage (and in this case we just
- emit error or warning and return pre-opened TABLE object) or special
- placeholder was put in table cache that guarantees that this table
- won't be created or opened until the placeholder will be removed
- (so there is an exclusive lock on this table).
- - We don't pre-open existing temporary table, instead we either open
- or create and then open table in this function.
-
+ @param thd [in] Thread object
+ @param create_info [in] Create information (like MAX_ROWS, ENGINE or
+ temporary table flag)
+ @param create_table [in] Pointer to TABLE_LIST object providing database
+ and name for table to be created or to be open
+ @param alter_info [in/out] Initial list of columns and indexes for the
+ table to be created
+ @param items [in] List of items which should be used to produce
+ rest of fields for the table (corresponding
+ fields will be added to the end of
+ alter_info->create_list)
+ @param lock [out] Pointer to the MYSQL_LOCK object for table
+ created will be returned in this parameter.
+ Since this table is not included in THD::lock
+ caller is responsible for explicitly unlocking
+ this table.
+ @param hooks [in] Hooks to be invoked before and after obtaining
+ table lock on the table being created.
+
+ @note
+ This function assumes that either table exists and was pre-opened and
+ locked at open_and_lock_tables() stage (and in this case we just emit
+ error or warning and return pre-opened TABLE object) or an exclusive
+ metadata lock was acquired on table so we can safely create, open and
+ lock table in it (we don't acquire metadata lock if this create is
+ for temporary table).
+
+ @note
Since this function contains some logic specific to CREATE TABLE ...
SELECT it should be changed before it can be used in other contexts.
- RETURN VALUES
- non-zero Pointer to TABLE object for table created or opened
- 0 Error
+ @retval non-zero Pointer to TABLE object for table created or opened
+ @retval 0 Error
*/
static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
@@ -3678,7 +3843,6 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
List_iterator_fast<Item> it(*items);
Item *item;
Field *tmp_field;
- bool not_used;
DBUG_ENTER("create_table_from_items");
tmp_table.alias= 0;
@@ -3716,7 +3880,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
alter_info->create_list.push_back(cr_field);
}
- DBUG_EXECUTE_IF("sleep_create_select_before_create", my_sleep(6000000););
+ DEBUG_SYNC(thd,"create_table_select_before_create");
/*
Create and lock table.
@@ -3735,30 +3899,21 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
open_table().
*/
{
- tmp_disable_binlog(thd);
if (!mysql_create_table_no_lock(thd, create_table->db,
create_table->table_name,
create_info, alter_info, 0,
- select_field_count))
+ select_field_count, NULL))
{
- if (create_info->table_existed &&
- !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
- {
- /*
- This means that someone created table underneath server
- or it was created via different mysqld front-end to the
- cluster. We don't have much options but throw an error.
- */
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), create_table->table_name);
- DBUG_RETURN(0);
- }
-
- DBUG_EXECUTE_IF("sleep_create_select_before_open", my_sleep(6000000););
+ DEBUG_SYNC(thd,"create_table_select_before_open");
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
{
- VOID(pthread_mutex_lock(&LOCK_open));
- if (reopen_name_locked_table(thd, create_table, FALSE))
+ Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN);
+ /*
+ Here we open the destination table, on which we already have
+ an exclusive metadata lock.
+ */
+ if (open_table(thd, create_table, thd->mem_root, &ot_ctx))
{
quick_rm_table(create_info->db_type, create_table->db,
table_case_name(create_info, create_table->table_name),
@@ -3766,34 +3921,41 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
}
else
table= create_table->table;
- VOID(pthread_mutex_unlock(&LOCK_open));
}
else
{
- if (!(table= open_table(thd, create_table, thd->mem_root, (bool*) 0,
- MYSQL_OPEN_TEMPORARY_ONLY)) &&
- !create_info->table_existed)
+ Open_table_context ot_ctx(thd, MYSQL_OPEN_TEMPORARY_ONLY);
+ if (open_table(thd, create_table, thd->mem_root, &ot_ctx))
{
/*
This shouldn't happen as creation of temporary table should make
it preparable for open. But let us do close_temporary_table() here
just in case.
*/
- drop_temporary_table(thd, create_table);
+ drop_temporary_table(thd, create_table, NULL);
}
+ else
+ table= create_table->table;
}
}
- reenable_binlog(thd);
if (!table) // open failed
+ {
+ if (!thd->is_error()) // CREATE ... IF NOT EXISTS
+ my_ok(thd); // succeed, but did nothing
DBUG_RETURN(0);
+ }
}
- DBUG_EXECUTE_IF("sleep_create_select_before_lock", my_sleep(6000000););
+ DEBUG_SYNC(thd,"create_table_select_before_lock");
table->reginfo.lock_type=TL_WRITE;
hooks->prelock(&table, 1); // Call prelock hooks
- if (! ((*lock)= mysql_lock_tables(thd, &table, 1,
- MYSQL_LOCK_IGNORE_FLUSH, &not_used)) ||
+ /*
+ 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))
{
/* purecov: begin tested */
@@ -3807,9 +3969,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
mysql_unlock_tables(thd, *lock);
*lock= 0;
}
-
- if (!create_info->table_existed)
- 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 */
}
@@ -3844,34 +4004,42 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
*/
class MY_HOOKS : public TABLEOP_HOOKS {
public:
- MY_HOOKS(select_create *x, TABLE_LIST *create_table,
- TABLE_LIST *select_tables)
- : ptr(x), all_tables(*create_table)
+ MY_HOOKS(select_create *x, TABLE_LIST *create_table_arg,
+ TABLE_LIST *select_tables_arg)
+ : ptr(x),
+ create_table(create_table_arg),
+ select_tables(select_tables_arg)
{
- all_tables.next_global= select_tables;
}
private:
virtual int do_postlock(TABLE **tables, uint count)
{
+ int error;
THD *thd= const_cast<THD*>(ptr->get_thd());
- if (int error= decide_logging_format(thd, &all_tables))
+ TABLE_LIST *save_next_global= create_table->next_global;
+
+ create_table->next_global= select_tables;
+
+ error= thd->decide_logging_format(create_table);
+
+ create_table->next_global= save_next_global;
+
+ if (error)
return error;
TABLE const *const table = *tables;
- if (thd->current_stmt_binlog_row_based &&
- !table->s->tmp_table &&
- !ptr->get_create_info()->table_existed)
+ if (thd->is_current_stmt_binlog_format_row() &&
+ !table->s->tmp_table)
{
- int errcode= query_error_code(thd, thd->killed == NOT_KILLED);
- if (int error= ptr->binlog_show_create_table(tables, count, errcode))
+ if (int error= ptr->binlog_show_create_table(tables, count))
return error;
}
return 0;
}
-
select_create *ptr;
- TABLE_LIST all_tables;
+ TABLE_LIST *create_table;
+ TABLE_LIST *select_tables;
};
MY_HOOKS hooks(this, create_table, select_tables);
@@ -3885,44 +4053,21 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
temporary table, we need to start a statement transaction.
*/
if ((thd->lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) == 0 &&
- thd->current_stmt_binlog_row_based &&
+ thd->is_current_stmt_binlog_format_row() &&
mysql_bin_log.is_open())
{
thd->binlog_start_trans_and_stmt();
}
- DBUG_EXECUTE_IF("sleep_create_select_before_check_if_exists", my_sleep(6000000););
+ DBUG_ASSERT(create_table->table == NULL);
- if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
- (create_table->table && create_table->table->db_stat))
- {
- /* Table already exists and was open at open_and_lock_tables() stage. */
- if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
- {
- /* Mark that table existed */
- create_info->table_existed= 1;
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
- create_table->table_name);
- if (thd->current_stmt_binlog_row_based)
- {
- int errcode= query_error_code(thd, thd->killed == NOT_KILLED);
- binlog_show_create_table(&(create_table->table), 1, errcode);
- }
- table= create_table->table;
- }
- else
- {
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), create_table->table_name);
- DBUG_RETURN(-1);
- }
- }
- else
- if (!(table= create_table_from_items(thd, create_info, create_table,
- alter_info, &values,
- &extra_lock, hook_ptr)))
- /* abort() deletes table */
- DBUG_RETURN(-1);
+ 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)))
+ /* abort() deletes table */
+ DBUG_RETURN(-1);
if (extra_lock)
{
@@ -3962,7 +4107,7 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
if (info.handle_duplicates == DUP_UPDATE)
table->file->extra(HA_EXTRA_INSERT_WITH_UPDATE);
- if (!thd->prelocked_mode)
+ if (thd->locked_tables_mode <= LTM_LOCK_TABLES)
table->file->ha_start_bulk_insert((ha_rows) 0);
thd->abort_on_warning= (!info.ignore &&
(thd->variables.sql_mode &
@@ -3976,10 +4121,10 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
}
int
-select_create::binlog_show_create_table(TABLE **tables, uint count, int errcode)
+select_create::binlog_show_create_table(TABLE **tables, uint count)
{
/*
- Note 1: We generate a CREATE TABLE statement for the
+ Note 1: In RBR mode, we generate a CREATE TABLE statement for the
created table by calling store_create_info() (behaves as SHOW
CREATE TABLE). In the event of an error, nothing should be
written to the binary log, even if the table is non-transactional;
@@ -3995,6 +4140,7 @@ select_create::binlog_show_create_table(TABLE **tables, uint count, int errcode)
schema that will do a close_thread_tables(), destroying the
statement transaction cache.
*/
+ DBUG_ASSERT(thd->is_current_stmt_binlog_format_row());
DBUG_ASSERT(tables && *tables && count > 0);
char buf[2048];
@@ -4012,9 +4158,11 @@ select_create::binlog_show_create_table(TABLE **tables, uint count, int errcode)
if (mysql_bin_log.is_open())
{
+ int errcode= query_error_code(thd, thd->killed == NOT_KILLED);
result= thd->binlog_query(THD::STMT_QUERY_TYPE,
query.ptr(), query.length(),
/* is_trans */ TRUE,
+ /* direct */ FALSE,
/* suppress_use */ FALSE,
errcode);
}
@@ -4034,15 +4182,11 @@ void select_create::send_error(uint errcode,const char *err)
DBUG_PRINT("info",
("Current statement %s row-based",
- thd->current_stmt_binlog_row_based ? "is" : "is NOT"));
+ thd->is_current_stmt_binlog_format_row() ? "is" : "is NOT"));
DBUG_PRINT("info",
("Current table (at 0x%lu) %s a temporary (or non-existant) table",
(ulong) table,
table && !table->s->tmp_table ? "is NOT" : "is"));
- DBUG_PRINT("info",
- ("Table %s prior to executing this statement",
- get_create_info()->table_existed ? "existed" : "did not exist"));
-
/*
This will execute any rollbacks that are necessary before writing
the transcation cache.
@@ -4066,7 +4210,7 @@ bool select_create::send_eof()
{
bool tmp=select_insert::send_eof();
if (tmp)
- abort();
+ abort_result_set();
else
{
/*
@@ -4076,8 +4220,8 @@ bool select_create::send_eof()
*/
if (!table->s->tmp_table)
{
- ha_autocommit_or_rollback(thd, 0);
- end_active_trans(thd);
+ trans_commit_stmt(thd);
+ trans_commit_implicit(thd);
}
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
@@ -4093,12 +4237,12 @@ bool select_create::send_eof()
}
-void select_create::abort()
+void select_create::abort_result_set()
{
- DBUG_ENTER("select_create::abort");
+ DBUG_ENTER("select_create::abort_result_set");
/*
- In select_insert::abort() we roll back the statement, including
+ In select_insert::abort_result_set() we roll back the statement, including
truncating the transaction cache of the binary log. To do this, we
pretend that the statement is transactional, even though it might
be the case that it was not.
@@ -4113,11 +4257,11 @@ void select_create::abort()
log state.
*/
tmp_disable_binlog(thd);
- select_insert::abort();
+ select_insert::abort_result_set();
thd->transaction.stmt.modified_non_trans_table= FALSE;
reenable_binlog(thd);
/* possible error of writing binary log is ignored deliberately */
- (void)thd->binlog_flush_pending_rows_event(TRUE);
+ (void) thd->binlog_flush_pending_rows_event(TRUE, TRUE);
if (m_plock)
{
@@ -4128,21 +4272,10 @@ void select_create::abort()
if (table)
{
- if (thd->lex->sql_command == SQLCOM_CREATE_TABLE &&
- thd->current_stmt_binlog_row_based &&
- !(thd->lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&
- mysql_bin_log.is_open())
- {
- /*
- This should be removed after BUG#47899.
- */
- mysql_bin_log.reset_gathered_updates(thd);
- }
-
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
- if (!create_info->table_existed)
- drop_open_table(thd, table, create_table->db, create_table->table_name);
+ table->auto_increment_field_not_null= FALSE;
+ drop_open_table(thd, table, create_table->db, create_table->table_name);
table=0; // Safety
}
DBUG_VOID_RETURN;
diff --git a/sql/sql_insert.h b/sql/sql_insert.h
new file mode 100644
index 00000000000..8564f4d103e
--- /dev/null
+++ b/sql/sql_insert.h
@@ -0,0 +1,49 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_INSERT_INCLUDED
+#define SQL_INSERT_INCLUDED
+
+#include "sql_class.h" /* enum_duplicates */
+#include "sql_list.h"
+
+/* Instead of including sql_lex.h we add this typedef here */
+typedef List<Item> List_item;
+typedef struct st_copy_info COPY_INFO;
+
+bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table,
+ List<Item> &fields, List_item *values,
+ List<Item> &update_fields,
+ List<Item> &update_values, enum_duplicates duplic,
+ COND **where, bool select_insert,
+ bool check_fields, bool abort_on_warning);
+bool mysql_insert(THD *thd,TABLE_LIST *table,List<Item> &fields,
+ List<List_item> &values, List<Item> &update_fields,
+ List<Item> &update_values, enum_duplicates flag,
+ bool ignore);
+void upgrade_lock_type_for_insert(THD *thd, thr_lock_type *lock_type,
+ enum_duplicates duplic,
+ bool is_multi_insert);
+int check_that_all_fields_are_given_values(THD *thd, TABLE *entry,
+ TABLE_LIST *table_list);
+void prepare_triggers_for_insert_stmt(TABLE *table);
+int write_record(THD *thd, TABLE *table, COPY_INFO *info);
+void kill_delayed_threads(void);
+
+#ifdef EMBEDDED_LIBRARY
+inline void kill_delayed_threads(void) {}
+#endif
+
+#endif /* SQL_INSERT_INCLUDED */
diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc
index 49b523b61be..9411b3a92c8 100644
--- a/sql/sql_join_cache.cc
+++ b/sql/sql_join_cache.cc
@@ -27,7 +27,8 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "key.h"
+#include "sql_base.h"
#include "sql_select.h"
#include "opt_subselect.h"
diff --git a/sql/sql_join_cache.h b/sql/sql_join_cache.h
index ba8e4ba8e4a..6953f6881ee 100644
--- a/sql/sql_join_cache.h
+++ b/sql/sql_join_cache.h
@@ -1,4 +1,20 @@
/*
+ Copyright (c) 2011, 2012, Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
This file contains declarations for implementations
of block based join algorithms
*/
@@ -550,7 +566,7 @@ public:
BNL_JOIN_ALG, /* Block Nested Loop Join algorithm */
BNLH_JOIN_ALG, /* Block Nested Loop Hash Join algorithm */
BKA_JOIN_ALG, /* Batched Key Access Join algorithm */
- BKAH_JOIN_ALG, /* Batched Key Access with Hash Table Join Algorithm */
+ BKAH_JOIN_ALG /* Batched Key Access with Hash Table Join Algorithm */
};
/*
@@ -649,7 +665,7 @@ public:
void reset_join(JOIN *j) { join= j; }
void free()
{
- x_free(buff);
+ my_free(buff);
buff= 0;
}
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 659f3659e38..8aeb2e6c516 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1,5 +1,4 @@
-/*
- Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
Copyright (c) 2009, 2014, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
@@ -13,14 +12,17 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* A lexical scanner on a temporary buffer with a yacc interface */
#define MYSQL_LEX 1
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h" // REQUIRED: for other includes
+#include "sql_class.h" // sql_lex.h: SQLCOM_END
+#include "sql_lex.h"
+#include "sql_parse.h" // add_to_list
#include "item_create.h"
#include <m_ctype.h>
#include <hash.h>
@@ -28,6 +30,8 @@
#include "sp.h"
#include "sql_select.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.
@@ -35,7 +39,43 @@
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};
+/**
+ @note The order of the elements of this array must correspond to
+ the order of elements in enum_binlog_stmt_unsafe.
+*/
+const int
+Query_tables_list::binlog_stmt_unsafe_errcode[BINLOG_STMT_UNSAFE_COUNT] =
+{
+ ER_BINLOG_UNSAFE_LIMIT,
+ ER_BINLOG_UNSAFE_INSERT_DELAYED,
+ ER_BINLOG_UNSAFE_SYSTEM_TABLE,
+ ER_BINLOG_UNSAFE_AUTOINC_COLUMNS,
+ ER_BINLOG_UNSAFE_UDF,
+ ER_BINLOG_UNSAFE_SYSTEM_VARIABLE,
+ ER_BINLOG_UNSAFE_SYSTEM_FUNCTION,
+ ER_BINLOG_UNSAFE_NONTRANS_AFTER_TRANS,
+ ER_BINLOG_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE,
+ ER_BINLOG_UNSAFE_MIXED_STATEMENT,
+ ER_BINLOG_UNSAFE_INSERT_IGNORE_SELECT,
+ ER_BINLOG_UNSAFE_INSERT_SELECT_UPDATE,
+ ER_BINLOG_UNSAFE_WRITE_AUTOINC_SELECT,
+ ER_BINLOG_UNSAFE_REPLACE_SELECT,
+ ER_BINLOG_UNSAFE_CREATE_IGNORE_SELECT,
+ ER_BINLOG_UNSAFE_CREATE_REPLACE_SELECT,
+ ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC,
+ ER_BINLOG_UNSAFE_UPDATE_IGNORE,
+ ER_BINLOG_UNSAFE_INSERT_TWO_KEYS,
+ ER_BINLOG_UNSAFE_AUTOINC_NOT_FIRST
+};
+
+
/* Longest standard keyword name */
+
#define TOCK_NAME_LENGTH 24
/*
@@ -104,6 +144,88 @@ void lex_free(void)
DBUG_VOID_RETURN;
}
+/**
+ Initialize lex object for use in fix_fields and parsing.
+
+ SYNOPSIS
+ init_lex_with_single_table()
+ @param thd The thread object
+ @param table The table object
+ @return Operation status
+ @retval TRUE An error occurred, memory allocation error
+ @retval FALSE Ok
+
+ DESCRIPTION
+ This function is used to initialize a lex object on the
+ stack for use by fix_fields and for parsing. In order to
+ work properly it also needs to initialize the
+ Name_resolution_context object of the lexer.
+ Finally it needs to set a couple of variables to ensure
+ proper functioning of fix_fields.
+*/
+
+int
+init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex)
+{
+ TABLE_LIST *table_list;
+ Table_ident *table_ident;
+ SELECT_LEX *select_lex= &lex->select_lex;
+ Name_resolution_context *context= &select_lex->context;
+ /*
+ We will call the parser to create a part_info struct based on the
+ partition string stored in the frm file.
+ We will use a local lex object for this purpose. However we also
+ need to set the Name_resolution_object for this lex object. We
+ do this by using add_table_to_list where we add the table that
+ we're working with to the Name_resolution_context.
+ */
+ 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))))
+ return TRUE;
+ context->resolve_in_table_list_only(table_list);
+ lex->use_only_table_context= TRUE;
+ lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VCOL_EXPR;
+ select_lex->cur_pos_in_select_list= UNDEF_POS;
+ table->map= 1; //To ensure correct calculation of const item
+ table->get_fields_in_item_tree= TRUE;
+ table_list->table= table;
+ table_list->cacheable_table= false;
+ return FALSE;
+}
+
+/**
+ End use of local lex with single table
+
+ SYNOPSIS
+ end_lex_with_single_table()
+ @param thd The thread object
+ @param table The table object
+ @param old_lex The real lex object connected to THD
+
+ DESCRIPTION
+ This function restores the real lex object after calling
+ init_lex_with_single_table and also restores some table
+ variables temporarily set.
+*/
+
+void
+end_lex_with_single_table(THD *thd, TABLE *table, LEX *old_lex)
+{
+ LEX *lex= thd->lex;
+ table->map= 0;
+ table->get_fields_in_item_tree= FALSE;
+ lex_end(lex);
+ thd->lex= old_lex;
+}
+
void
st_parsing_options::reset()
@@ -115,7 +237,18 @@ st_parsing_options::reset()
}
-bool Lex_input_stream::init(THD *thd, char *buff, unsigned int length)
+/**
+ Perform initialization of Lex_input_stream instance.
+
+ Basically, a buffer for pre-processed query. This buffer should be large
+ enough to keep multi-statement query. The allocation is done once in
+ Lex_input_stream::init() in order to prevent memory pollution when
+ the server is processing large multi-statement queries.
+*/
+
+bool Lex_input_stream::init(THD *thd,
+ char* buff,
+ unsigned int length)
{
DBUG_EXECUTE_IF("bug42064_simulate_oom",
DBUG_SET("+d,simulate_out_of_memory"););
@@ -128,19 +261,54 @@ bool Lex_input_stream::init(THD *thd, char *buff, unsigned int length)
if (m_cpp_buf == NULL)
return TRUE;
- m_cpp_ptr= m_cpp_buf;
m_thd= thd;
- m_ptr= buff;
- m_end_of_query= buff + length;
- m_buf= buff;
- m_buf_length= length;
- ignore_space= test(thd->variables.sql_mode & MODE_IGNORE_SPACE);
+ reset(buff, length);
return FALSE;
}
/**
+ Prepare Lex_input_stream instance state for use for handling next SQL statement.
+
+ It should be called between two statements in a multi-statement query.
+ The operation resets the input stream to the beginning-of-parse state,
+ but does not reallocate m_cpp_buf.
+*/
+
+void
+Lex_input_stream::reset(char *buffer, unsigned int length)
+{
+ yylineno= 1;
+ yytoklen= 0;
+ yylval= NULL;
+ lookahead_token= -1;
+ lookahead_yylval= NULL;
+ m_ptr= buffer;
+ m_tok_start= NULL;
+ m_tok_end= NULL;
+ m_end_of_query= buffer + length;
+ m_tok_start_prev= NULL;
+ m_buf= buffer;
+ m_buf_length= length;
+ m_echo= TRUE;
+ m_cpp_tok_start= NULL;
+ m_cpp_tok_start_prev= NULL;
+ m_cpp_tok_end= NULL;
+ m_body_utf8= NULL;
+ m_cpp_utf8_processed_ptr= NULL;
+ next_state= MY_LEX_START;
+ found_semicolon= NULL;
+ ignore_space= test(m_thd->variables.sql_mode & MODE_IGNORE_SPACE);
+ stmt_prepare_mode= FALSE;
+ multi_statements= TRUE;
+ in_comment=NO_COMMENT;
+ m_underscore_cs= NULL;
+ m_cpp_ptr= m_cpp_buf;
+}
+
+
+/**
The operation is called from the parser in order to
1) designate the intention to have utf8 body;
1) Indicate to the lexer that we will need a utf8 representation of this
@@ -313,7 +481,6 @@ void lex_start(THD *thd)
lex->subqueries= FALSE;
lex->context_analysis_only= 0;
lex->derived_tables= 0;
- lex->lock_option= TL_READ;
lex->safe_to_cache_query= 1;
lex->parsing_options.reset();
lex->empty_field_list_on_rset= 0;
@@ -325,12 +492,13 @@ void lex_start(THD *thd)
lex->select_lex.ftfunc_list= &lex->select_lex.ftfunc_list_alloc;
lex->select_lex.group_list.empty();
lex->select_lex.order_list.empty();
- lex->sql_command= SQLCOM_END;
+ lex->select_lex.gorder_list.empty();
lex->duplicates= DUP_ERROR;
lex->ignore= 0;
lex->spname= NULL;
lex->sphead= NULL;
lex->spcont= NULL;
+ lex->m_stmt= NULL;
lex->proc_list.first= 0;
lex->escape_used= FALSE;
lex->query_tables= 0;
@@ -347,7 +515,6 @@ void lex_start(THD *thd)
lex->select_lex.nest_level_base= &lex->unit;
lex->allow_sum_func= 0;
lex->in_sum_func= NULL;
- lex->protect_against_global_read_lock= FALSE;
/*
ok, there must be a better solution for this, long-term
I tried "bzero" in the sql_yacc.yy code, but that for
@@ -366,6 +533,9 @@ void lex_start(THD *thd)
lex->is_lex_started= TRUE;
lex->used_tables= 0;
+ lex->reset_slave_info.all= false;
+ lex->limit_rows_examined= 0;
+ lex->limit_rows_examined_cnt= ULONGLONG_MAX;
DBUG_VOID_RETURN;
}
@@ -375,10 +545,18 @@ void lex_end(LEX *lex)
DBUG_PRINT("enter", ("lex: 0x%lx", (long) lex));
/* release used plugins */
- plugin_unlock_list(0, (plugin_ref*)lex->plugins.buffer,
- lex->plugins.elements);
+ if (lex->plugins.elements) /* No function call and no mutex if no plugins. */
+ {
+ plugin_unlock_list(0, (plugin_ref*)lex->plugins.buffer,
+ lex->plugins.elements);
+ }
reset_dynamic(&lex->plugins);
+ delete lex->sphead;
+ lex->sphead= NULL;
+
+ lex->mi.reset();
+
DBUG_VOID_RETURN;
}
@@ -386,8 +564,8 @@ Yacc_state::~Yacc_state()
{
if (yacc_yyss)
{
- my_free(yacc_yyss, MYF(0));
- my_free(yacc_yyvs, MYF(0));
+ my_free(yacc_yyss);
+ my_free(yacc_yyvs);
}
}
@@ -773,22 +951,75 @@ bool consume_comment(Lex_input_stream *lip, int remaining_recursions_permitted)
/*
MYSQLlex remember the following states from the following MYSQLlex()
+ @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)
*/
-int MYSQLlex(void *arg, void *yythd)
+int MYSQLlex(YYSTYPE *yylval, THD *thd)
+{
+ Lex_input_stream *lip= & thd->m_parser_state->m_lip;
+ int token;
+
+ if (lip->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;
+ return token;
+ }
+
+ token= lex_one_token(yylval, thd);
+
+ switch(token) {
+ case WITH:
+ /*
+ Parsing 'WITH' 'ROLLUP' or 'WITH' 'CUBE' requires 2 look ups,
+ which makes the grammar LALR(2).
+ Replace by a single 'WITH_ROLLUP' or 'WITH_CUBE' token,
+ to transform the grammar into a LALR(1) grammar,
+ which sql_yacc.yy can process.
+ */
+ token= lex_one_token(yylval, thd);
+ switch(token) {
+ case CUBE_SYM:
+ return WITH_CUBE_SYM;
+ case ROLLUP_SYM:
+ return WITH_ROLLUP_SYM;
+ default:
+ /*
+ Save the token following 'WITH'
+ */
+ lip->lookahead_yylval= lip->yylval;
+ lip->yylval= NULL;
+ lip->lookahead_token= token;
+ return WITH;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return token;
+}
+
+static int lex_one_token(YYSTYPE *yylval, THD *thd)
{
reg1 uchar c;
bool comment_closed;
int tokval, result_state;
uint length;
enum my_lex_states state;
- THD *thd= (THD *)yythd;
Lex_input_stream *lip= & thd->m_parser_state->m_lip;
LEX *lex= thd->lex;
- YYSTYPE *yylval=(YYSTYPE*) arg;
CHARSET_INFO *const cs= thd->charset();
const uchar *const state_map= cs->state_map;
const uchar *const ident_map= cs->ident_map;
@@ -1181,7 +1412,7 @@ int MYSQLlex(void *arg, void *yythd)
yylval->lex_str=get_token(lip,
2, // skip x'
length-3); // don't count x' and last '
- return (HEX_NUM);
+ return HEX_STRING;
case MY_LEX_BIN_NUMBER: // Found b'bin-string'
lip->yySkip(); // Accept opening '
@@ -1279,27 +1510,23 @@ int MYSQLlex(void *arg, void *yythd)
lip->save_in_comment_state();
- if (lip->yyPeekn(2) == 'M' && lip->yyPeekn(3) == '!')
- {
- /* Skip MariaDB unique marker */
- lip->set_echo(FALSE);
- lip->yySkip();
- /* The following if will be true */
- }
- if (lip->yyPeekn(2) == '!')
+ if (lip->yyPeekn(2) == '!' ||
+ (lip->yyPeekn(2) == 'M' && lip->yyPeekn(3) == '!'))
{
+ bool maria_comment_syntax= lip->yyPeekn(2) == 'M';
lip->in_comment= DISCARD_COMMENT;
/* Accept '/' '*' '!', but do not keep this marker. */
lip->set_echo(FALSE);
- lip->yySkipn(3);
+ lip->yySkipn(maria_comment_syntax ? 4 : 3);
/*
The special comment format is very strict:
'/' '*' '!', followed by an optional 'M' and exactly
- 1 digit (major), 2 digits (minor), then 2 digits (dot).
- 32302 -> 3.23.02
- 50032 -> 5.0.32
- 50114 -> 5.1.14
+ 1-2 digits (major), 2 digits (minor), then 2 digits (dot).
+ 32302 -> 3.23.02
+ 50032 -> 5.0.32
+ 50114 -> 5.1.14
+ 100000 -> 10.0.0
*/
if ( my_isdigit(cs, lip->yyPeekn(0))
&& my_isdigit(cs, lip->yyPeekn(1))
@@ -1309,14 +1536,21 @@ int MYSQLlex(void *arg, void *yythd)
)
{
ulong version;
- char *end_ptr= (char*) lip->get_ptr()+5;
+ uint length= 5;
+ char *end_ptr= (char*) lip->get_ptr()+length;
int error;
+ if (my_isdigit(cs, lip->yyPeekn(5)))
+ {
+ end_ptr++; // 6 digit number
+ length++;
+ }
+
version= (ulong) my_strtoll10(lip->get_ptr(), &end_ptr, &error);
if (version <= MYSQL_VERSION_ID)
{
/* Accept 'M' 'm' 'm' 'd' 'd' */
- lip->yySkipn(5);
+ lip->yySkipn(length);
/* Expand the content of the special comment as real code */
lip->set_echo(TRUE);
state=MY_LEX_START;
@@ -1519,7 +1753,7 @@ Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root)
keys_onoff(rhs.keys_onoff),
tablespace_op(rhs.tablespace_op),
partition_names(rhs.partition_names, mem_root),
- no_parts(rhs.no_parts),
+ num_parts(rhs.num_parts),
change_level(rhs.change_level),
datetime_field(rhs.datetime_field),
error_if_not_empty(rhs.error_if_not_empty)
@@ -1632,10 +1866,11 @@ void st_select_lex::init_query()
parent_lex->push_context(&context);
cond_count= between_count= with_wild= 0;
max_equal_elems= 0;
- conds_processed_with_permanent_arena= 0;
ref_pointer_array= 0;
+ ref_pointer_array_size= 0;
select_n_where_fields= 0;
select_n_having_items= 0;
+ n_sum_items= 0;
n_child_sum_items= 0;
subquery_in_having= explicit_limit= 0;
is_item_list_lookup= 0;
@@ -1646,9 +1881,7 @@ void st_select_lex::init_query()
exclude_from_table_unique_test= no_wrap_view_item= FALSE;
nest_level= 0;
link_next= 0;
- lock_option= TL_READ_DEFAULT;
is_prep_leaf_list_saved= FALSE;
-
bzero((char*) expr_cache_may_be_used, sizeof(expr_cache_may_be_used));
m_non_agg_field_used= false;
m_agg_func_used= false;
@@ -1898,14 +2131,15 @@ void st_select_lex_unit::exclude_tree()
this to 'last' as dependent
SYNOPSIS
- last - pointer to last st_select_lex struct, before wich all
+ last - pointer to last st_select_lex struct, before which all
st_select_lex have to be marked as dependent
NOTE
'last' should be reachable from this st_select_lex_node
*/
-bool st_select_lex::mark_as_dependent(THD *thd, st_select_lex *last, Item *dependency)
+bool st_select_lex::mark_as_dependent(THD *thd, st_select_lex *last,
+ Item *dependency)
{
DBUG_ASSERT(this != last);
@@ -1953,6 +2187,7 @@ 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,
LEX_STRING *option)
{
@@ -2071,11 +2306,6 @@ ulong st_select_lex::get_table_join_options()
bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num)
{
- DBUG_ENTER("st_select_lex::setup_ref_array");
-
- if (ref_pointer_array)
- DBUG_RETURN(0);
-
// find_order_in_list() may need some extra space, so multiply by two.
order_group_num*= 2;
@@ -2083,13 +2313,33 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num)
We have to create array in prepared statement memory if it is a
prepared statement
*/
- ref_pointer_array=
- (Item **)thd->stmt_arena->alloc(sizeof(Item*) * (n_child_sum_items +
- item_list.elements +
- select_n_having_items +
- select_n_where_fields +
- order_group_num)*5);
- DBUG_RETURN(ref_pointer_array == 0);
+ Query_arena *arena= thd->stmt_arena;
+ const uint n_elems= (n_sum_items +
+ n_child_sum_items +
+ item_list.elements +
+ select_n_having_items +
+ select_n_where_fields +
+ order_group_num) * 5;
+ if (ref_pointer_array != NULL)
+ {
+ /*
+ We need to take 'n_sum_items' into account when allocating the array,
+ and this may actually increase during the optimization phase due to
+ MIN/MAX rewrite in Item_in_subselect::single_value_transformer.
+ In the usual case we can reuse the array from the prepare phase.
+ If we need a bigger array, we must allocate a new one.
+ */
+ if (ref_pointer_array_size >= n_elems)
+ {
+ DBUG_PRINT("info", ("reusing old ref_array"));
+ return false;
+ }
+ }
+ ref_pointer_array= static_cast<Item**>(arena->alloc(sizeof(Item*) * n_elems));
+ if (ref_pointer_array != NULL)
+ ref_pointer_array_size= n_elems;
+
+ return ref_pointer_array == NULL;
}
@@ -2208,7 +2458,7 @@ void st_select_lex::print_limit(THD *thd,
to implement the clean up.
*/
-void st_lex::cleanup_lex_after_parse_error(THD *thd)
+void LEX::cleanup_lex_after_parse_error(THD *thd)
{
/*
Delete sphead for the side effect of restoring of the original
@@ -2248,6 +2498,7 @@ void st_lex::cleanup_lex_after_parse_error(THD *thd)
void Query_tables_list::reset_query_tables_list(bool init)
{
+ sql_command= SQLCOM_END;
if (!init && query_tables)
{
TABLE_LIST *table= query_tables;
@@ -2268,7 +2519,7 @@ void Query_tables_list::reset_query_tables_list(bool init)
We delay real initialization of hash (and therefore related
memory allocation) until first insertion into this hash.
*/
- hash_clear(&sroutines);
+ my_hash_clear(&sroutines);
}
else if (sroutines.records)
{
@@ -2279,6 +2530,7 @@ void Query_tables_list::reset_query_tables_list(bool init)
sroutines_list_own_last= sroutines_list.next;
sroutines_list_own_elements= 0;
binlog_stmt_flags= 0;
+ stmt_accessed_table_flag= 0;
}
@@ -2291,7 +2543,7 @@ void Query_tables_list::reset_query_tables_list(bool init)
void Query_tables_list::destroy_query_tables_list()
{
- hash_free(&sroutines);
+ my_hash_free(&sroutines);
}
@@ -2299,7 +2551,7 @@ void Query_tables_list::destroy_query_tables_list()
Initialize LEX object.
SYNOPSIS
- st_lex::st_lex()
+ LEX::LEX()
NOTE
LEX object initialized with this constructor can be used as part of
@@ -2309,9 +2561,9 @@ void Query_tables_list::destroy_query_tables_list()
for this.
*/
-st_lex::st_lex()
- :result(0),
- sql_command(SQLCOM_END), option_type(OPT_DEFAULT), is_lex_started(0)
+LEX::LEX()
+ :result(0), option_type(OPT_DEFAULT), is_lex_started(0),
+ limit_rows_examined_cnt(ULONGLONG_MAX)
{
my_init_dynamic_array2(&plugins, sizeof(plugin_ref),
@@ -2319,6 +2571,7 @@ st_lex::st_lex()
INITIAL_LEX_PLUGIN_LIST_SIZE,
INITIAL_LEX_PLUGIN_LIST_SIZE);
reset_query_tables_list(TRUE);
+ mi.init();
}
@@ -2326,7 +2579,7 @@ st_lex::st_lex()
Check whether the merging algorithm can be used on this VIEW
SYNOPSIS
- st_lex::can_be_merged()
+ LEX::can_be_merged()
DESCRIPTION
We can apply merge algorithm if it is single SELECT view with
@@ -2340,7 +2593,7 @@ st_lex::st_lex()
TRUE - merge algorithm can be used
*/
-bool st_lex::can_be_merged()
+bool LEX::can_be_merged()
{
// TODO: do not forget implement case when select_lex.table_list.elements==0
@@ -2379,19 +2632,19 @@ bool st_lex::can_be_merged()
check if command can use VIEW with MERGE algorithm (for top VIEWs)
SYNOPSIS
- st_lex::can_use_merged()
+ LEX::can_use_merged()
DESCRIPTION
Only listed here commands can use merge algorithm in top level
SELECT_LEX (for subqueries will be used merge algorithm if
- st_lex::can_not_use_merged() is not TRUE).
+ LEX::can_not_use_merged() is not TRUE).
RETURN
FALSE - command can't use merged VIEWs
TRUE - VIEWs with MERGE algorithms can be used
*/
-bool st_lex::can_use_merged()
+bool LEX::can_use_merged()
{
switch (sql_command)
{
@@ -2416,18 +2669,18 @@ bool st_lex::can_use_merged()
Check if command can't use merged views in any part of command
SYNOPSIS
- st_lex::can_not_use_merged()
+ LEX::can_not_use_merged()
DESCRIPTION
Temporary table algorithm will be used on all SELECT levels for queries
- listed here (see also st_lex::can_use_merged()).
+ listed here (see also LEX::can_use_merged()).
RETURN
FALSE - command can't use merged VIEWs
TRUE - VIEWs with MERGE algorithms can be used
*/
-bool st_lex::can_not_use_merged()
+bool LEX::can_not_use_merged()
{
switch (sql_command)
{
@@ -2456,7 +2709,7 @@ bool st_lex::can_not_use_merged()
FALSE no, we need data
*/
-bool st_lex::only_view_structure()
+bool LEX::only_view_structure()
{
switch (sql_command) {
case SQLCOM_SHOW_CREATE:
@@ -2485,7 +2738,7 @@ bool st_lex::only_view_structure()
*/
-bool st_lex::need_correct_ident()
+bool LEX::need_correct_ident()
{
switch(sql_command)
{
@@ -2515,7 +2768,7 @@ bool st_lex::need_correct_ident()
VIEW_CHECK_CASCADED CHECK OPTION CASCADED
*/
-uint8 st_lex::get_effective_with_check(TABLE_LIST *view)
+uint8 LEX::get_effective_with_check(TABLE_LIST *view)
{
if (view->select_lex->master_unit() == &unit &&
which_check_option_applicable())
@@ -2544,7 +2797,7 @@ uint8 st_lex::get_effective_with_check(TABLE_LIST *view)
*/
bool
-st_lex::copy_db_to(char **p_db, size_t *p_db_length) const
+LEX::copy_db_to(char **p_db, size_t *p_db_length) const
{
if (sphead)
{
@@ -2575,7 +2828,47 @@ void st_select_lex_unit::set_limit(st_select_lex *sl)
ulonglong val;
DBUG_ASSERT(! thd->stmt_arena->is_stmt_prepare());
- val= sl->select_limit ? sl->select_limit->val_uint() : HA_POS_ERROR;
+ if (sl->select_limit)
+ {
+ Item *item = sl->select_limit;
+ /*
+ fix_fields() has not been called for sl->select_limit. That's due to the
+ historical reasons -- this item could be only of type Item_int, and
+ Item_int does not require fix_fields(). Thus, fix_fields() was never
+ called for sl->select_limit.
+
+ Some time ago, Item_splocal was also allowed for LIMIT / OFFSET clauses.
+ However, the fix_fields() behavior was not updated, which led to a crash
+ in some cases.
+
+ There is no single place where to call fix_fields() for LIMIT / OFFSET
+ items during the fix-fields-phase. Thus, for the sake of readability,
+ it was decided to do it here, on the evaluation phase (which is a
+ violation of design, but we chose the lesser of two evils).
+
+ We can call fix_fields() here, because sl->select_limit can be of two
+ types only: Item_int and Item_splocal. Item_int::fix_fields() is trivial,
+ and Item_splocal::fix_fields() (or rather Item_sp_variable::fix_fields())
+ has the following specific:
+ 1) it does not affect other items;
+ 2) it does not fail.
+
+ Nevertheless DBUG_ASSERT was added to catch future changes in
+ 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 (!item->fixed)
+ {
+ fix_fields_successful= !item->fix_fields(thd, NULL);
+
+ DBUG_ASSERT(fix_fields_successful);
+ }
+ val= fix_fields_successful ? item->val_uint() : HA_POS_ERROR;
+ }
+ else
+ val= HA_POS_ERROR;
+
select_limit_val= (ha_rows)val;
#ifndef BIG_TABLES
/*
@@ -2585,7 +2878,22 @@ void st_select_lex_unit::set_limit(st_select_lex *sl)
if (val != (ulonglong)select_limit_val)
select_limit_val= HA_POS_ERROR;
#endif
- val= sl->offset_limit ? sl->offset_limit->val_uint() : ULL(0);
+ if (sl->offset_limit)
+ {
+ Item *item = sl->offset_limit;
+ // see comment for sl->select_limit branch.
+ bool fix_fields_successful= true;
+ if (!item->fixed)
+ {
+ fix_fields_successful= !item->fix_fields(thd, NULL);
+
+ DBUG_ASSERT(fix_fields_successful);
+ }
+ val= fix_fields_successful ? item->val_uint() : 0;
+ }
+ else
+ val= ULL(0);
+
offset_limit_cnt= (ha_rows)val;
#ifndef BIG_TABLES
/* Check for truncation. */
@@ -2621,9 +2929,10 @@ void st_select_lex_unit::set_limit(st_select_lex *sl)
clause.
*/
-void st_lex::set_trg_event_type_for_tables()
+void LEX::set_trg_event_type_for_tables()
{
uint8 new_trg_event_map= 0;
+ DBUG_ENTER("LEX::set_trg_event_type_for_tables");
/*
Some auxiliary operations
@@ -2743,6 +3052,7 @@ void st_lex::set_trg_event_type_for_tables()
tables->trg_event_map= new_trg_event_map;
tables= tables->next_local;
}
+ DBUG_VOID_RETURN;
}
@@ -2764,7 +3074,7 @@ void st_lex::set_trg_event_type_for_tables()
In this case link_to_local is set.
*/
-TABLE_LIST *st_lex::unlink_first_table(bool *link_to_local)
+TABLE_LIST *LEX::unlink_first_table(bool *link_to_local)
{
TABLE_LIST *first;
if ((first= query_tables))
@@ -2804,7 +3114,7 @@ TABLE_LIST *st_lex::unlink_first_table(bool *link_to_local)
table list
SYNOPSYS
- st_lex::first_lists_tables_same()
+ LEX::first_lists_tables_same()
NOTES
In many cases (for example, usual INSERT/DELETE/...) the first table of
@@ -2815,7 +3125,7 @@ TABLE_LIST *st_lex::unlink_first_table(bool *link_to_local)
the global list first.
*/
-void st_lex::first_lists_tables_same()
+void LEX::first_lists_tables_same()
{
TABLE_LIST *first_table= select_lex.table_list.first;
if (query_tables != first_table && first_table != 0)
@@ -2851,7 +3161,7 @@ void st_lex::first_lists_tables_same()
global list
*/
-void st_lex::link_first_table_back(TABLE_LIST *first,
+void LEX::link_first_table_back(TABLE_LIST *first,
bool link_to_local)
{
if (first)
@@ -2878,7 +3188,7 @@ void st_lex::link_first_table_back(TABLE_LIST *first,
cleanup lex for case when we open table by table for processing
SYNOPSIS
- st_lex::cleanup_after_one_table_open()
+ LEX::cleanup_after_one_table_open()
NOTE
This method is mostly responsible for cleaning up of selects lists and
@@ -2886,7 +3196,7 @@ void st_lex::link_first_table_back(TABLE_LIST *first,
to call Query_tables_list::reset_query_tables_list(FALSE).
*/
-void st_lex::cleanup_after_one_table_open()
+void LEX::cleanup_after_one_table_open()
{
/*
thd->lex->derived_tables & additional units may be set if we open
@@ -2922,7 +3232,7 @@ void st_lex::cleanup_after_one_table_open()
backup Pointer to Query_tables_list instance to be used for backup
*/
-void st_lex::reset_n_backup_query_tables_list(Query_tables_list *backup)
+void LEX::reset_n_backup_query_tables_list(Query_tables_list *backup)
{
backup->set_query_tables_list(this);
/*
@@ -2941,7 +3251,7 @@ void st_lex::reset_n_backup_query_tables_list(Query_tables_list *backup)
backup Pointer to Query_tables_list instance used for backup
*/
-void st_lex::restore_backup_query_tables_list(Query_tables_list *backup)
+void LEX::restore_backup_query_tables_list(Query_tables_list *backup)
{
this->destroy_query_tables_list();
this->set_query_tables_list(backup);
@@ -2952,14 +3262,14 @@ void st_lex::restore_backup_query_tables_list(Query_tables_list *backup)
Checks for usage of routines and/or tables in a parsed statement
SYNOPSIS
- st_lex:table_or_sp_used()
+ LEX:table_or_sp_used()
RETURN
FALSE No routines and tables used
TRUE Either or both routines and tables are used.
*/
-bool st_lex::table_or_sp_used()
+bool LEX::table_or_sp_used()
{
DBUG_ENTER("table_or_sp_used");
@@ -2989,7 +3299,7 @@ static void fix_prepare_info_in_table_list(THD *thd, TABLE_LIST *tbl)
{
for (; tbl; tbl= tbl->next_local)
{
- if (tbl->on_expr)
+ if (tbl->on_expr && !tbl->prep_on_expr)
{
thd->check_and_register_item_tree(&tbl->prep_on_expr, &tbl->on_expr);
tbl->on_expr= tbl->on_expr->copy_andor_structure(thd);
@@ -3026,6 +3336,7 @@ static void fix_prepare_info_in_table_list(THD *thd, TABLE_LIST *tbl)
void st_select_lex::fix_prepare_information(THD *thd, Item **conds,
Item **having_conds)
{
+ DBUG_ENTER("st_select_lex::fix_prepare_information");
if (!thd->stmt_arena->is_conventional() && first_execution)
{
first_execution= 0;
@@ -3054,6 +3365,7 @@ void st_select_lex::fix_prepare_information(THD *thd, Item **conds,
}
fix_prepare_info_in_table_list(thd, table_list.first);
}
+ DBUG_VOID_RETURN;
}
@@ -3129,7 +3441,23 @@ bool st_select_lex::add_index_hint (THD *thd, char *str, uint length)
}
-bool st_select_lex::optimize_unflattened_subqueries()
+/**
+ Optimize all subqueries that have not been flattened into semi-joins.
+
+ @details
+ This functionality is a method of SELECT_LEX instead of JOIN because
+ SQL statements as DELETE/UPDATE do not have a corresponding JOIN object.
+
+ @see JOIN::optimize_unflattened_subqueries
+
+ @param const_only Restrict subquery optimization to constant subqueries
+
+ @return Operation status
+ @retval FALSE success.
+ @retval TRUE error occurred.
+*/
+
+bool st_select_lex::optimize_unflattened_subqueries(bool const_only)
{
for (SELECT_LEX_UNIT *un= first_inner_unit(); un; un= un->next_unit())
{
@@ -3139,12 +3467,19 @@ bool st_select_lex::optimize_unflattened_subqueries()
{
if (subquery_predicate->substype() == Item_subselect::IN_SUBS)
{
- Item_in_subselect *in_subs=(Item_in_subselect*)subquery_predicate;
+ Item_in_subselect *in_subs= (Item_in_subselect*) subquery_predicate;
if (in_subs->is_jtbm_merged)
continue;
}
+ if (const_only && !subquery_predicate->const_item())
+ {
+ /* Skip non-constant subqueries if the caller asked so. */
+ continue;
+ }
+
bool empty_union_result= true;
+ bool is_correlated_unit= false;
/*
If the subquery is a UNION, optimize all the subqueries in the UNION. If
there is no UNION, then the loop will execute once for the subquery.
@@ -3169,6 +3504,8 @@ bool st_select_lex::optimize_unflattened_subqueries()
inner_join->select_options|= SELECT_DESCRIBE;
}
res= inner_join->optimize();
+ sl->update_correlated_cache();
+ is_correlated_unit|= sl->is_correlated;
inner_join->select_options= save_options;
un->thd->lex->current_select= save_select;
if (empty_union_result)
@@ -3184,6 +3521,9 @@ bool st_select_lex::optimize_unflattened_subqueries()
}
if (empty_union_result)
subquery_predicate->no_rows_in_result();
+ if (!is_correlated_unit)
+ un->uncacheable&= ~UNCACHEABLE_DEPENDENT;
+ subquery_predicate->is_correlated= is_correlated_unit;
}
}
return FALSE;
@@ -3205,7 +3545,7 @@ bool st_select_lex::optimize_unflattened_subqueries()
@return TRUE an error occur.
*/
-bool st_select_lex::handle_derived(struct st_lex *lex, uint phases)
+bool st_select_lex::handle_derived(LEX *lex, uint phases)
{
for (TABLE_LIST *cursor= (TABLE_LIST*) table_list.first;
cursor;
@@ -3551,6 +3891,61 @@ void SELECT_LEX::update_used_tables()
/**
+ @brief
+ Update is_correlated cache for this select
+
+ @details
+*/
+
+void st_select_lex::update_correlated_cache()
+{
+ TABLE_LIST *tl;
+ List_iterator<TABLE_LIST> ti(leaf_tables);
+
+ is_correlated= false;
+
+ while ((tl= ti++))
+ {
+ if (tl->on_expr)
+ is_correlated|= test(tl->on_expr->used_tables() & OUTER_REF_TABLE_BIT);
+ for (TABLE_LIST *embedding= tl->embedding ; embedding ;
+ embedding= embedding->embedding)
+ {
+ if (embedding->on_expr)
+ is_correlated|= test(embedding->on_expr->used_tables() &
+ OUTER_REF_TABLE_BIT);
+ }
+ }
+
+ if (join->conds)
+ is_correlated|= test(join->conds->used_tables() & OUTER_REF_TABLE_BIT);
+
+ if (join->having)
+ is_correlated|= test(join->having->used_tables() & OUTER_REF_TABLE_BIT);
+
+ if (join->tmp_having)
+ is_correlated|= test(join->tmp_having->used_tables() & OUTER_REF_TABLE_BIT);
+
+ Item *item;
+ List_iterator_fast<Item> it(join->fields_list);
+ while ((item= it++))
+ is_correlated|= test(item->used_tables() & OUTER_REF_TABLE_BIT);
+
+ for (ORDER *order= group_list.first; order; order= order->next)
+ is_correlated|= test((*order->item)->used_tables() & OUTER_REF_TABLE_BIT);
+
+ if (!master_unit()->is_union())
+ {
+ for (ORDER *order= order_list.first; order; order= order->next)
+ is_correlated|= test((*order->item)->used_tables() & OUTER_REF_TABLE_BIT);
+ }
+
+ if (!is_correlated)
+ uncacheable&= ~UNCACHEABLE_DEPENDENT;
+}
+
+
+/**
Set the EXPLAIN type for this subquery.
*/
@@ -3783,13 +4178,160 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor)
@retval FALSE No, not a management partition command
*/
-bool st_lex::is_partition_management() const
+bool LEX::is_partition_management() const
{
return (sql_command == SQLCOM_ALTER_TABLE &&
(alter_info.flags == ALTER_ADD_PARTITION ||
alter_info.flags == ALTER_REORGANIZE_PARTITION));
}
+#ifdef MYSQL_SERVER
+uint binlog_unsafe_map[256];
+
+#define UNSAFE(a, b, c) \
+ { \
+ DBUG_PRINT("unsafe_mixed_statement", ("SETTING BASE VALUES: %s, %s, %02X\n", \
+ LEX::stmt_accessed_table_string(a), \
+ LEX::stmt_accessed_table_string(b), \
+ c)); \
+ unsafe_mixed_statement(a, b, c); \
+ }
+
+/*
+ Sets the combination given by "a" and "b" and automatically combinations
+ given by other types of access, i.e. 2^(8 - 2), as unsafe.
+
+ It may happen a colision when automatically defining a combination as unsafe.
+ For that reason, a combination has its unsafe condition redefined only when
+ the new_condition is greater then the old. For instance,
+
+ . (BINLOG_DIRECT_ON & TRX_CACHE_NOT_EMPTY) is never overwritten by
+ . (BINLOG_DIRECT_ON | BINLOG_DIRECT_OFF).
+*/
+void unsafe_mixed_statement(LEX::enum_stmt_accessed_table a,
+ LEX::enum_stmt_accessed_table b, uint condition)
+{
+ int type= 0;
+ int index= (1U << a) | (1U << b);
+
+
+ for (type= 0; type < 256; type++)
+ {
+ if ((type & index) == index)
+ {
+ binlog_unsafe_map[type] |= condition;
+ }
+ }
+}
+/*
+ The BINLOG_* AND TRX_CACHE_* values can be combined by using '&' or '|',
+ which means that both conditions need to be satisfied or any of them is
+ enough. For example,
+
+ . BINLOG_DIRECT_ON & TRX_CACHE_NOT_EMPTY means that the statment is
+ unsafe when the option is on and trx-cache is not empty;
+
+ . BINLOG_DIRECT_ON | BINLOG_DIRECT_OFF means the statement is unsafe
+ in all cases.
+
+ . TRX_CACHE_EMPTY | TRX_CACHE_NOT_EMPTY means the statement is unsafe
+ in all cases. Similar as above.
+*/
+void binlog_unsafe_map_init()
+{
+ memset((void*) binlog_unsafe_map, 0, sizeof(uint) * 256);
+
+ /*
+ Classify a statement as unsafe when there is a mixed statement and an
+ on-going transaction at any point of the execution if:
+
+ 1. The mixed statement is about to update a transactional table and
+ a non-transactional table.
+
+ 2. The mixed statement is about to update a transactional table and
+ read from a non-transactional table.
+
+ 3. The mixed statement is about to update a non-transactional table
+ and temporary transactional table.
+
+ 4. The mixed statement is about to update a temporary transactional
+ table and read from a non-transactional table.
+
+ 5. The mixed statement is about to update a transactional table and
+ a temporary non-transactional table.
+
+ 6. The mixed statement is about to update a transactional table and
+ read from a temporary non-transactional table.
+
+ 7. The mixed statement is about to update a temporary transactional
+ table and temporary non-transactional table.
+
+ 8. The mixed statement is about to update a temporary transactional
+ table and read from a temporary non-transactional table.
+
+ After updating a transactional table if:
+
+ 9. The mixed statement is about to update a non-transactional table
+ and read from a transactional table.
+
+ 10. The mixed statement is about to update a non-transactional table
+ and read from a temporary transactional table.
+
+ 11. The mixed statement is about to update a temporary non-transactional
+ table and read from a transactional table.
+
+ 12. The mixed statement is about to update a temporary non-transactional
+ table and read from a temporary transactional table.
+
+ 13. The mixed statement is about to update a temporary non-transactional
+ table and read from a non-transactional table.
+
+ The reason for this is that locks acquired may not protected a concurrent
+ transaction of interfering in the current execution and by consequence in
+ the result.
+ */
+ /* Case 1. */
+ UNSAFE(LEX::STMT_WRITES_TRANS_TABLE, LEX::STMT_WRITES_NON_TRANS_TABLE,
+ BINLOG_DIRECT_ON | BINLOG_DIRECT_OFF);
+ /* Case 2. */
+ UNSAFE(LEX::STMT_WRITES_TRANS_TABLE, LEX::STMT_READS_NON_TRANS_TABLE,
+ BINLOG_DIRECT_ON | BINLOG_DIRECT_OFF);
+ /* Case 3. */
+ UNSAFE(LEX::STMT_WRITES_NON_TRANS_TABLE, LEX::STMT_WRITES_TEMP_TRANS_TABLE,
+ BINLOG_DIRECT_ON | BINLOG_DIRECT_OFF);
+ /* Case 4. */
+ UNSAFE(LEX::STMT_WRITES_TEMP_TRANS_TABLE, LEX::STMT_READS_NON_TRANS_TABLE,
+ BINLOG_DIRECT_ON | BINLOG_DIRECT_OFF);
+ /* Case 5. */
+ UNSAFE(LEX::STMT_WRITES_TRANS_TABLE, LEX::STMT_WRITES_TEMP_NON_TRANS_TABLE,
+ BINLOG_DIRECT_ON);
+ /* Case 6. */
+ UNSAFE(LEX::STMT_WRITES_TRANS_TABLE, LEX::STMT_READS_TEMP_NON_TRANS_TABLE,
+ BINLOG_DIRECT_ON);
+ /* Case 7. */
+ UNSAFE(LEX::STMT_WRITES_TEMP_TRANS_TABLE, LEX::STMT_WRITES_TEMP_NON_TRANS_TABLE,
+ BINLOG_DIRECT_ON);
+ /* Case 8. */
+ UNSAFE(LEX::STMT_WRITES_TEMP_TRANS_TABLE, LEX::STMT_READS_TEMP_NON_TRANS_TABLE,
+ BINLOG_DIRECT_ON);
+ /* Case 9. */
+ UNSAFE(LEX::STMT_WRITES_NON_TRANS_TABLE, LEX::STMT_READS_TRANS_TABLE,
+ (BINLOG_DIRECT_ON | BINLOG_DIRECT_OFF) & TRX_CACHE_NOT_EMPTY);
+ /* Case 10 */
+ UNSAFE(LEX::STMT_WRITES_NON_TRANS_TABLE, LEX::STMT_READS_TEMP_TRANS_TABLE,
+ (BINLOG_DIRECT_ON | BINLOG_DIRECT_OFF) & TRX_CACHE_NOT_EMPTY);
+ /* Case 11. */
+ UNSAFE(LEX::STMT_WRITES_TEMP_NON_TRANS_TABLE, LEX::STMT_READS_TRANS_TABLE,
+ BINLOG_DIRECT_ON & TRX_CACHE_NOT_EMPTY);
+ /* Case 12. */
+ UNSAFE(LEX::STMT_WRITES_TEMP_NON_TRANS_TABLE, LEX::STMT_READS_TEMP_TRANS_TABLE,
+ BINLOG_DIRECT_ON & TRX_CACHE_NOT_EMPTY);
+ /* Case 13. */
+ UNSAFE(LEX::STMT_WRITES_TEMP_NON_TRANS_TABLE, LEX::STMT_READS_NON_TRANS_TABLE,
+ BINLOG_DIRECT_OFF & TRX_CACHE_NOT_EMPTY);
+}
+#endif
+
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
template class Mem_root_array<ORDER*, true>;
#endif
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 7d539ea8316..4f424000180 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1,5 +1,5 @@
-/*
- Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2014, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,13 +12,21 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@defgroup Semantic_Analysis Semantic Analysis
*/
+#ifndef SQL_LEX_INCLUDED
+#define SQL_LEX_INCLUDED
+
+#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"
+
/* YACC and LEX Definitions */
/* These may not be declared yet */
@@ -32,26 +40,92 @@ class sp_pcontext;
class st_alter_tablespace;
class partition_info;
class Event_parse_data;
+class set_var_base;
+class sys_var;
+class Item_func_match;
+class Alter_drop;
+class Alter_column;
+class Key;
+class File_parser;
+class Key_part_spec;
#ifdef MYSQL_SERVER
/*
- The following hack is needed because mysql_yacc.cc does not define
- YYSTYPE before including this file
+ There are 8 different type of table access so there is no more than
+ combinations 2^8 = 256:
+
+ . STMT_READS_TRANS_TABLE
+
+ . STMT_READS_NON_TRANS_TABLE
+
+ . STMT_READS_TEMP_TRANS_TABLE
+
+ . STMT_READS_TEMP_NON_TRANS_TABLE
+
+ . STMT_WRITES_TRANS_TABLE
+
+ . STMT_WRITES_NON_TRANS_TABLE
+
+ . STMT_WRITES_TEMP_TRANS_TABLE
+
+ . STMT_WRITES_TEMP_NON_TRANS_TABLE
+
+ The unsafe conditions for each combination is represented within a byte
+ and stores the status of the option --binlog-direct-non-trans-updates,
+ whether the trx-cache is empty or not, and whether the isolation level
+ is lower than ISO_REPEATABLE_READ:
+
+ . option (OFF/ON)
+ . trx-cache (empty/not empty)
+ . isolation (>= ISO_REPEATABLE_READ / < ISO_REPEATABLE_READ)
+
+ bits 0 : . OFF, . empty, . >= ISO_REPEATABLE_READ
+ bits 1 : . OFF, . empty, . < ISO_REPEATABLE_READ
+ bits 2 : . OFF, . not empty, . >= ISO_REPEATABLE_READ
+ bits 3 : . OFF, . not empty, . < ISO_REPEATABLE_READ
+ bits 4 : . ON, . empty, . >= ISO_REPEATABLE_READ
+ bits 5 : . ON, . empty, . < ISO_REPEATABLE_READ
+ bits 6 : . ON, . not empty, . >= ISO_REPEATABLE_READ
+ bits 7 : . ON, . not empty, . < ISO_REPEATABLE_READ
+*/
+extern uint binlog_unsafe_map[256];
+/*
+ Initializes the array with unsafe combinations and its respective
+ conditions.
*/
+void binlog_unsafe_map_init();
+#endif
-#include "set_var.h"
-#include "mem_root_array.h"
+/**
+ 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;
+ char *length, *dec;
+ CHARSET_INFO *charset;
+ void set(int t, char *l, char *d, CHARSET_INFO *cs)
+ { type= (enum_field_types)t; length= l; dec= d; charset= cs; }
+};
+
+#ifdef MYSQL_SERVER
+/*
+ The following hack is needed because mysql_yacc.cc does not define
+ YYSTYPE before including this file
+*/
#ifdef MYSQL_YACC
#define LEX_YYSTYPE void *
#else
#include "lex_symbol.h"
#if MYSQL_LEX
-# if YACC_HEXT_HH
-# include "sql_yacc.hh"
-# else
-# include "sql_yacc.h"
-# endif
+#include "item_func.h" /* Cast_target used in sql_yacc.h */
+#include "sql_yacc.h"
#define LEX_YYSTYPE YYSTYPE *
#else
#define LEX_YYSTYPE void *
@@ -93,15 +167,15 @@ enum enum_sql_command {
SQLCOM_ROLLBACK, SQLCOM_ROLLBACK_TO_SAVEPOINT,
SQLCOM_COMMIT, SQLCOM_SAVEPOINT, SQLCOM_RELEASE_SAVEPOINT,
SQLCOM_SLAVE_START, SQLCOM_SLAVE_STOP,
- SQLCOM_BEGIN, SQLCOM_LOAD_MASTER_TABLE, SQLCOM_CHANGE_MASTER,
- SQLCOM_RENAME_TABLE, SQLCOM_BACKUP_TABLE, SQLCOM_RESTORE_TABLE,
+ SQLCOM_BEGIN, SQLCOM_CHANGE_MASTER,
+ SQLCOM_RENAME_TABLE,
SQLCOM_RESET, SQLCOM_PURGE, SQLCOM_PURGE_BEFORE, SQLCOM_SHOW_BINLOGS,
- SQLCOM_SHOW_OPEN_TABLES, SQLCOM_LOAD_MASTER_DATA,
+ SQLCOM_SHOW_OPEN_TABLES,
SQLCOM_HA_OPEN, SQLCOM_HA_CLOSE, SQLCOM_HA_READ,
SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_DELETE_MULTI, SQLCOM_UPDATE_MULTI,
- SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_SHOW_NEW_MASTER, SQLCOM_DO,
+ SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_DO,
SQLCOM_SHOW_WARNS, SQLCOM_EMPTY_QUERY, SQLCOM_SHOW_ERRORS,
- SQLCOM_SHOW_COLUMN_TYPES, SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES,
+ SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES,
SQLCOM_HELP, SQLCOM_CREATE_USER, SQLCOM_DROP_USER, SQLCOM_RENAME_USER,
SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM,
SQLCOM_CREATE_PROCEDURE, SQLCOM_CREATE_SPFUNCTION, SQLCOM_CALL,
@@ -125,6 +199,8 @@ enum enum_sql_command {
SQLCOM_SHOW_CREATE_TRIGGER,
SQLCOM_ALTER_DB_UPGRADE,
SQLCOM_SHOW_PROFILE, SQLCOM_SHOW_PROFILES,
+ SQLCOM_SIGNAL, SQLCOM_RESIGNAL,
+ SQLCOM_SHOW_RELAYLOG_EVENTS,
SQLCOM_SHOW_USER_STATS, SQLCOM_SHOW_TABLE_STATS, SQLCOM_SHOW_INDEX_STATS,
SQLCOM_SHOW_CLIENT_STATS,
@@ -189,6 +265,12 @@ enum enum_drop_mode
DROP_RESTRICT // RESTRICT option
};
+/* Options to add_table_to_list() */
+#define TL_OPTION_UPDATING 1
+#define TL_OPTION_FORCE_INDEX 2
+#define TL_OPTION_IGNORE_LEAVES 4
+#define TL_OPTION_ALIAS 8
+
typedef List<Item> List_item;
typedef Mem_root_array<ORDER*, true> Group_list_ptrs;
@@ -202,30 +284,52 @@ typedef struct st_lex_server_options
/**
- Structure to hold parameters for CHANGE MASTER or START/STOP SLAVE
- or SHOW NEW MASTER.
+ Structure to hold parameters for CHANGE MASTER, START SLAVE, and STOP SLAVE.
Remark: this should not be confused with Master_info (and perhaps
would better be renamed to st_lex_replication_info). Some fields,
e.g., delay, are saved in Relay_log_info, not in Master_info.
*/
-typedef struct st_lex_master_info
+struct LEX_MASTER_INFO
{
+ DYNAMIC_ARRAY repl_ignore_server_ids;
char *host, *user, *password, *log_file_name;
- uint port, connect_retry;
+ char *ssl_key, *ssl_cert, *ssl_ca, *ssl_capath, *ssl_cipher;
+ char *relay_log_name;
ulonglong pos;
+ ulong relay_log_pos;
ulong server_id;
+ uint port, connect_retry;
+ float heartbeat_period;
/*
Enum is used for making it possible to detect if the user
changed variable or if it should be left at old value
*/
- enum {SSL_UNCHANGED, SSL_DISABLE, SSL_ENABLE}
- ssl, ssl_verify_server_cert;
- char *ssl_key, *ssl_cert, *ssl_ca, *ssl_capath, *ssl_cipher;
- char *relay_log_name;
- ulong relay_log_pos;
-} LEX_MASTER_INFO;
+ enum {LEX_MI_UNCHANGED, LEX_MI_DISABLE, LEX_MI_ENABLE}
+ ssl, ssl_verify_server_cert, heartbeat_opt, repl_ignore_server_ids_opt;
+ void init()
+ {
+ bzero(this, sizeof(*this));
+ my_init_dynamic_array(&repl_ignore_server_ids,
+ sizeof(::server_id), 0, 16);
+ }
+ void reset()
+ {
+ delete_dynamic(&repl_ignore_server_ids);
+ host= user= password= log_file_name= ssl_key= ssl_cert= ssl_ca=
+ ssl_capath= ssl_cipher= relay_log_name= 0;
+ pos= relay_log_pos= server_id= port= connect_retry= 0;
+ heartbeat_period= 0;
+ ssl= ssl_verify_server_cert= heartbeat_opt=
+ repl_ignore_server_ids_opt= LEX_MI_UNCHANGED;
+ }
+};
+
+typedef struct st_lex_reset_slave
+{
+ bool all;
+} LEX_RESET_SLAVE;
enum sub_select_type
{
@@ -404,9 +508,11 @@ public:
Base class for st_select_lex (SELECT_LEX) &
st_select_lex_unit (SELECT_LEX_UNIT)
*/
-struct st_lex;
+struct LEX;
class st_select_lex;
class st_select_lex_unit;
+
+
class st_select_lex_node {
protected:
st_select_lex_node *next, **prev, /* neighbor list */
@@ -444,8 +550,17 @@ public:
{ return (void*) alloc_root(mem_root, (uint) size); }
static void operator delete(void *ptr,size_t size) { TRASH(ptr, size); }
static void operator delete(void *ptr, MEM_ROOT *mem_root) {}
- st_select_lex_node(): linkage(UNSPECIFIED_TYPE) {}
+
+ // Ensures that at least all members used during cleanup() are initialized.
+ st_select_lex_node()
+ : next(NULL), prev(NULL),
+ master(NULL), slave(NULL),
+ link_next(NULL), link_prev(NULL),
+ 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();
@@ -471,17 +586,18 @@ public:
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,
LEX_STRING *option= 0);
virtual void set_lock_for_tables(thr_lock_type lock_type) {}
friend class st_select_lex_unit;
- friend bool mysql_new_select(struct st_lex *lex, bool move_down);
+ friend bool mysql_new_select(LEX *lex, bool move_down);
friend bool mysql_make_view(THD *thd, File_parser *parser,
TABLE_LIST *table, uint flags);
- friend bool mysql_derived_prepare(THD *thd, st_lex *lex,
+ friend bool mysql_derived_prepare(THD *thd, LEX *lex,
TABLE_LIST *orig_table_list);
- friend bool mysql_derived_merge(THD *thd, st_lex *lex,
+ 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:
@@ -498,6 +614,8 @@ class select_result;
class JOIN;
class select_union;
class Procedure;
+
+
class st_select_lex_unit: public st_select_lex_node {
protected:
TABLE_LIST result_table_list;
@@ -506,6 +624,15 @@ protected:
bool saved_error;
public:
+ // Ensures that at least all members used during cleanup() are initialized.
+ st_select_lex_unit()
+ : union_result(NULL), table(NULL), result(NULL),
+ cleaned(false),
+ fake_select_lex(NULL)
+ {
+ }
+
+
TABLE *table; /* temporary table using for appending UNION results */
select_result *result;
bool prepared, // prepare phase already performed for UNION (unit)
@@ -563,11 +690,11 @@ public:
st_select_lex* outer_select();
st_select_lex* first_select()
{
- return my_reinterpret_cast(st_select_lex*)(slave);
+ return reinterpret_cast<st_select_lex*>(slave);
}
st_select_lex_unit* next_unit()
{
- return my_reinterpret_cast(st_select_lex_unit*)(next);
+ return reinterpret_cast<st_select_lex_unit*>(next);
}
st_select_lex* return_after_parsing() { return return_to; }
void exclude_level();
@@ -616,7 +743,7 @@ public:
/* Saved values of the WHERE and HAVING clauses*/
Item::cond_result cond_value, having_value;
/* point on lex in which it was created, used in view subquery detection */
- st_lex *parent_lex;
+ LEX *parent_lex;
enum olap_type olap;
/* FROM clause - points to the beginning of the TABLE_LIST::next_local list. */
SQL_I_List<TABLE_LIST> table_list;
@@ -674,6 +801,7 @@ public:
Item *select_limit, *offset_limit; /* LIMIT clause parameters */
// Arrays of pointers to top elements of all_fields list
Item **ref_pointer_array;
+ size_t ref_pointer_array_size; // Number of elements in array.
/*
number of items in select_list and HAVING clause used to get number
@@ -691,11 +819,6 @@ public:
uint select_n_where_fields;
enum_parsing_place parsing_place; /* where we are parsing expression */
bool with_sum_func; /* sum function indicator */
- /*
- PS or SP cond natural joins was alredy processed with permanent
- arena and all additional items which we need alredy stored in it
- */
- bool conds_processed_with_permanent_arena;
ulong table_join_options;
uint in_sum_expr;
@@ -760,14 +883,6 @@ public:
List<udf_func> udf_list; /* udf function calls stack */
- /**
- Per sub-query locking strategy.
- Note: This variable might interfer with the corresponding statement-level
- variable Lex::lock_option because on how different parser rules depend
- on eachother.
- */
- thr_lock_type lock_option;
-
/*
This is a copy of the original JOIN USING list that comes from
the parser. The parser :
@@ -824,6 +939,7 @@ public:
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,
LEX_STRING *option= 0);
TABLE_LIST* get_table_list();
@@ -896,15 +1012,10 @@ public:
void clear_index_hints(void) { index_hints= NULL; }
bool is_part_of_union() { return master_unit()->is_union(); }
- /*
- Optimize all subqueries that have not been flattened into semi-joins.
- This functionality is a method of SELECT_LEX instead of JOIN because
- some SQL statements as DELETE do not have a corresponding JOIN object.
- */
- bool optimize_unflattened_subqueries();
+ bool optimize_unflattened_subqueries(bool const_only);
/* Set the EXPLAIN type for this subquery. */
void set_explain_type();
- bool handle_derived(struct st_lex *lex, uint phases);
+ bool handle_derived(LEX *lex, uint phases);
void append_table_to_list(TABLE_LIST *TABLE_LIST::*link, TABLE_LIST *table);
bool get_free_table_map(table_map *map, uint *tablenr);
void replace_leaf_table(TABLE_LIST *table, List<TABLE_LIST> &tbl_list);
@@ -922,6 +1033,7 @@ public:
void mark_as_belong_to_derived(TABLE_LIST *derived);
void increase_derived_records(ha_rows records);
void update_used_tables();
+ void update_correlated_cache();
void mark_const_derived(bool empty);
bool save_leaf_tables(THD *thd);
@@ -969,19 +1081,19 @@ inline bool st_select_lex_unit::is_union ()
#define ALTER_CHANGE_COLUMN_DEFAULT (1L << 8)
#define ALTER_KEYS_ONOFF (1L << 9)
#define ALTER_CONVERT (1L << 10)
-#define ALTER_FORCE (1L << 11)
-#define ALTER_RECREATE (1L << 12)
-#define ALTER_ADD_PARTITION (1L << 13)
-#define ALTER_DROP_PARTITION (1L << 14)
-#define ALTER_COALESCE_PARTITION (1L << 15)
-#define ALTER_REORGANIZE_PARTITION (1L << 16)
-#define ALTER_PARTITION (1L << 17)
-#define ALTER_ADMIN_PARTITION (1L << 18)
-#define ALTER_TABLE_REORG (1L << 19)
-#define ALTER_REBUILD_PARTITION (1L << 20)
-#define ALTER_ALL_PARTITION (1L << 21)
-#define ALTER_REMOVE_PARTITIONING (1L << 22)
-#define ALTER_FOREIGN_KEY (1L << 23)
+#define ALTER_RECREATE (1L << 11)
+#define ALTER_ADD_PARTITION (1L << 12)
+#define ALTER_DROP_PARTITION (1L << 13)
+#define ALTER_COALESCE_PARTITION (1L << 14)
+#define ALTER_REORGANIZE_PARTITION (1L << 15)
+#define ALTER_PARTITION (1L << 16)
+#define ALTER_ADMIN_PARTITION (1L << 17)
+#define ALTER_TABLE_REORG (1L << 18)
+#define ALTER_REBUILD_PARTITION (1L << 19)
+#define ALTER_ALL_PARTITION (1L << 20)
+#define ALTER_REMOVE_PARTITIONING (1L << 21)
+#define ALTER_FOREIGN_KEY (1L << 22)
+#define ALTER_TRUNCATE_PARTITION (1L << 23)
enum enum_alter_table_change_level
{
@@ -990,6 +1102,24 @@ enum enum_alter_table_change_level
ALTER_TABLE_INDEX_CHANGED= 2
};
+
+/**
+ Temporary hack to enable a class bound forward declaration
+ of the enum_alter_table_change_level enumeration. To be
+ removed once Alter_info is moved to the sql_alter.h
+ header.
+*/
+class Alter_table_change_level
+{
+private:
+ typedef enum enum_alter_table_change_level enum_type;
+ enum_type value;
+public:
+ void operator = (enum_type v) { value = v; }
+ operator enum_type () { return value; }
+};
+
+
/**
@brief Parsing data for CREATE or ALTER TABLE.
@@ -1008,7 +1138,7 @@ public:
enum enum_enable_or_disable keys_onoff;
enum tablespace_op_type tablespace_op;
List<char> partition_names;
- uint no_parts;
+ uint num_parts;
enum_alter_table_change_level change_level;
Create_field *datetime_field;
bool error_if_not_empty;
@@ -1018,7 +1148,7 @@ public:
flags(0),
keys_onoff(LEAVE_AS_IS),
tablespace_op(NO_TABLESPACE_OP),
- no_parts(0),
+ num_parts(0),
change_level(ALTER_TABLE_METADATA_ONLY),
datetime_field(NULL),
error_if_not_empty(FALSE)
@@ -1033,7 +1163,7 @@ public:
flags= 0;
keys_onoff= LEAVE_AS_IS;
tablespace_op= NO_TABLESPACE_OP;
- no_parts= 0;
+ num_parts= 0;
partition_names.empty();
change_level= ALTER_TABLE_METADATA_ONLY;
datetime_field= 0;
@@ -1053,6 +1183,8 @@ struct st_sp_chistics
enum enum_sp_data_access daccess;
};
+extern const LEX_STRING null_lex_str;
+extern const LEX_STRING empty_lex_str;
struct st_trg_chistics
{
@@ -1068,22 +1200,32 @@ enum xa_option_words {XA_NONE, XA_JOIN, XA_RESUME, XA_ONE_PHASE,
extern const LEX_STRING null_lex_str;
extern const LEX_STRING empty_lex_str;
-struct Sroutine_hash_entry;
+class Sroutine_hash_entry;
/*
- Class representing list of all tables used by statement.
- It also contains information about stored functions used by statement
+ Class representing list of all tables used by statement and other
+ information which is necessary for opening and locking its tables,
+ like SQL command for this statement.
+
+ Also contains information about stored functions used by statement
since during its execution we may have to add all tables used by its
stored functions/triggers to this list in order to pre-open and lock
them.
- Also used by st_lex::reset_n_backup/restore_backup_query_tables_list()
+ Also used by LEX::reset_n_backup/restore_backup_query_tables_list()
methods to save and restore this information.
*/
class Query_tables_list
{
public:
+ /**
+ SQL command for this statement. Part of this class since the
+ process of opening and locking tables for the statement needs
+ this information to determine correct type of lock for some of
+ the tables.
+ */
+ enum_sql_command sql_command;
/* Global list of all tables used by this statement */
TABLE_LIST *query_tables;
/* Pointer to next_global member of last element in the previous list. */
@@ -1162,25 +1304,462 @@ public:
}
}
+ /** Return a pointer to the last element in query table list. */
+ TABLE_LIST *last_table()
+ {
+ /* Don't use offsetof() macro in order to avoid warnings. */
+ return query_tables ?
+ (TABLE_LIST*) ((char*) query_tables_last -
+ ((char*) &(query_tables->next_global) -
+ (char*) query_tables)) :
+ 0;
+ }
+
/**
- Has the parser/scanner detected that this statement is unsafe?
- */
+ Enumeration listing of all types of unsafe statement.
+
+ @note The order of elements of this enumeration type must
+ correspond to the order of the elements of the @c explanations
+ array defined in the body of @c THD::issue_unsafe_warnings.
+ */
+ enum enum_binlog_stmt_unsafe {
+ /**
+ SELECT..LIMIT is unsafe because the set of rows returned cannot
+ be predicted.
+ */
+ BINLOG_STMT_UNSAFE_LIMIT= 0,
+ /**
+ INSERT DELAYED is unsafe because the time when rows are inserted
+ cannot be predicted.
+ */
+ BINLOG_STMT_UNSAFE_INSERT_DELAYED,
+ /**
+ Access to log tables is unsafe because slave and master probably
+ log different things.
+ */
+ BINLOG_STMT_UNSAFE_SYSTEM_TABLE,
+ /**
+ Inserting into an autoincrement column in a stored routine is unsafe.
+ Even with just one autoincrement column, if the routine is invoked more than
+ once slave is not guaranteed to execute the statement graph same way as
+ the master.
+ And since it's impossible to estimate how many times a routine can be invoked at
+ the query pre-execution phase (see lock_tables), the statement is marked
+ pessimistically unsafe.
+ */
+ BINLOG_STMT_UNSAFE_AUTOINC_COLUMNS,
+ /**
+ Using a UDF (user-defined function) is unsafe.
+ */
+ BINLOG_STMT_UNSAFE_UDF,
+ /**
+ Using most system variables is unsafe, because slave may run
+ with different options than master.
+ */
+ BINLOG_STMT_UNSAFE_SYSTEM_VARIABLE,
+ /**
+ Using some functions is unsafe (e.g., UUID).
+ */
+ BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION,
+
+ /**
+ Mixing transactional and non-transactional statements are unsafe if
+ non-transactional reads or writes are occur after transactional
+ reads or writes inside a transaction.
+ */
+ BINLOG_STMT_UNSAFE_NONTRANS_AFTER_TRANS,
+
+ /**
+ Mixing self-logging and non-self-logging engines in a statement
+ is unsafe.
+ */
+ BINLOG_STMT_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE,
+
+ /**
+ Statements that read from both transactional and non-transactional
+ tables and write to any of them are unsafe.
+ */
+ BINLOG_STMT_UNSAFE_MIXED_STATEMENT,
+
+ /**
+ INSERT...IGNORE SELECT is unsafe because which rows are ignored depends
+ on the order that rows are retrieved by SELECT. This order cannot be
+ predicted and may differ on master and the slave.
+ */
+ BINLOG_STMT_UNSAFE_INSERT_IGNORE_SELECT,
+
+ /**
+ INSERT...SELECT...UPDATE is unsafe because which rows are updated depends
+ on the order that rows are retrieved by SELECT. This order cannot be
+ predicted and may differ on master and the slave.
+ */
+ BINLOG_STMT_UNSAFE_INSERT_SELECT_UPDATE,
+
+ /**
+ Query that writes to a table with auto_inc column after selecting from
+ other tables are unsafe as the order in which the rows are retrieved by
+ select may differ on master and slave.
+ */
+ BINLOG_STMT_UNSAFE_WRITE_AUTOINC_SELECT,
+
+ /**
+ INSERT...REPLACE SELECT is unsafe because which rows are replaced depends
+ on the order that rows are retrieved by SELECT. This order cannot be
+ predicted and may differ on master and the slave.
+ */
+ BINLOG_STMT_UNSAFE_REPLACE_SELECT,
+
+ /**
+ CREATE TABLE... IGNORE... SELECT is unsafe because which rows are ignored
+ depends on the order that rows are retrieved by SELECT. This order cannot
+ be predicted and may differ on master and the slave.
+ */
+ BINLOG_STMT_UNSAFE_CREATE_IGNORE_SELECT,
+
+ /**
+ CREATE TABLE...REPLACE... SELECT is unsafe because which rows are replaced
+ depends on the order that rows are retrieved from SELECT. This order
+ cannot be predicted and may differ on master and the slave
+ */
+ BINLOG_STMT_UNSAFE_CREATE_REPLACE_SELECT,
+
+ /**
+ CREATE TABLE...SELECT on a table with auto-increment column is unsafe
+ because which rows are replaced depends on the order that rows are
+ retrieved from SELECT. This order cannot be predicted and may differ on
+ master and the slave
+ */
+ BINLOG_STMT_UNSAFE_CREATE_SELECT_AUTOINC,
+
+ /**
+ UPDATE...IGNORE is unsafe because which rows are ignored depends on the
+ order that rows are updated. This order cannot be predicted and may differ
+ on master and the slave.
+ */
+ BINLOG_STMT_UNSAFE_UPDATE_IGNORE,
+
+ /**
+ INSERT... ON DUPLICATE KEY UPDATE on a table with more than one
+ UNIQUE KEYS is unsafe.
+ */
+ BINLOG_STMT_UNSAFE_INSERT_TWO_KEYS,
+
+ /**
+ INSERT into auto-inc field which is not the first part of composed
+ primary key.
+ */
+ BINLOG_STMT_UNSAFE_AUTOINC_NOT_FIRST,
+
+ /* The last element of this enumeration type. */
+ BINLOG_STMT_UNSAFE_COUNT
+ };
+ /**
+ This has all flags from 0 (inclusive) to BINLOG_STMT_FLAG_COUNT
+ (exclusive) set.
+ */
+ static const int BINLOG_STMT_UNSAFE_ALL_FLAGS=
+ ((1 << BINLOG_STMT_UNSAFE_COUNT) - 1);
+
+ /**
+ Maps elements of enum_binlog_stmt_unsafe to error codes.
+ */
+ static const int binlog_stmt_unsafe_errcode[BINLOG_STMT_UNSAFE_COUNT];
+
+ /**
+ Determine if this statement is marked as unsafe.
+
+ @retval 0 if the statement is not marked as unsafe.
+ @retval nonzero if the statement is marked as unsafe.
+ */
inline bool is_stmt_unsafe() const {
- return binlog_stmt_flags & (1U << BINLOG_STMT_FLAG_UNSAFE);
+ return get_stmt_unsafe_flags() != 0;
}
/**
- Flag the current (top-level) statement as unsafe.
+ Flag the current (top-level) statement as unsafe.
+ The flag will be reset after the statement has finished.
- The flag will be reset after the statement has finished.
+ @param unsafe_type The type of unsafety: one of the @c
+ BINLOG_STMT_FLAG_UNSAFE_* flags in @c enum_binlog_stmt_flag.
+ */
+ inline void set_stmt_unsafe(enum_binlog_stmt_unsafe unsafe_type) {
+ DBUG_ENTER("set_stmt_unsafe");
+ DBUG_ASSERT(unsafe_type >= 0 && unsafe_type < BINLOG_STMT_UNSAFE_COUNT);
+ binlog_stmt_flags|= (1U << unsafe_type);
+ DBUG_VOID_RETURN;
+ }
- */
- inline void set_stmt_unsafe() {
- binlog_stmt_flags|= (1U << BINLOG_STMT_FLAG_UNSAFE);
+ /**
+ Set the bits of binlog_stmt_flags determining the type of
+ unsafeness of the current statement. No existing bits will be
+ cleared, but new bits may be set.
+
+ @param flags A binary combination of zero or more bits, (1<<flag)
+ where flag is a member of enum_binlog_stmt_unsafe.
+ */
+ inline void set_stmt_unsafe_flags(uint32 flags) {
+ DBUG_ENTER("set_stmt_unsafe_flags");
+ DBUG_ASSERT((flags & ~BINLOG_STMT_UNSAFE_ALL_FLAGS) == 0);
+ binlog_stmt_flags|= flags;
+ DBUG_VOID_RETURN;
+ }
+
+ /**
+ Return a binary combination of all unsafe warnings for the
+ statement. If the statement has been marked as unsafe by the
+ 'flag' member of enum_binlog_stmt_unsafe, then the return value
+ from this function has bit (1<<flag) set to 1.
+ */
+ inline uint32 get_stmt_unsafe_flags() const {
+ DBUG_ENTER("get_stmt_unsafe_flags");
+ DBUG_RETURN(binlog_stmt_flags & BINLOG_STMT_UNSAFE_ALL_FLAGS);
}
+ /**
+ Mark the current statement as safe; i.e., clear all bits in
+ binlog_stmt_flags that correspond to elements of
+ enum_binlog_stmt_unsafe.
+ */
inline void clear_stmt_unsafe() {
- binlog_stmt_flags&= ~(1U << BINLOG_STMT_FLAG_UNSAFE);
+ DBUG_ENTER("clear_stmt_unsafe");
+ binlog_stmt_flags&= ~BINLOG_STMT_UNSAFE_ALL_FLAGS;
+ DBUG_VOID_RETURN;
+ }
+
+ /**
+ Determine if this statement is a row injection.
+
+ @retval 0 if the statement is not a row injection
+ @retval nonzero if the statement is a row injection
+ */
+ inline bool is_stmt_row_injection() const {
+ return binlog_stmt_flags &
+ (1U << (BINLOG_STMT_UNSAFE_COUNT + BINLOG_STMT_TYPE_ROW_INJECTION));
+ }
+
+ /**
+ Flag the statement as a row injection. A row injection is either
+ a BINLOG statement, or a row event in the relay log executed by
+ the slave SQL thread.
+ */
+ inline void set_stmt_row_injection() {
+ DBUG_ENTER("set_stmt_row_injection");
+ binlog_stmt_flags|=
+ (1U << (BINLOG_STMT_UNSAFE_COUNT + BINLOG_STMT_TYPE_ROW_INJECTION));
+ DBUG_VOID_RETURN;
+ }
+
+ enum enum_stmt_accessed_table
+ {
+ /*
+ If a transactional table is about to be read. Note that
+ a write implies a read.
+ */
+ STMT_READS_TRANS_TABLE= 0,
+ /*
+ If a non-transactional table is about to be read. Note that
+ a write implies a read.
+ */
+ STMT_READS_NON_TRANS_TABLE,
+ /*
+ If a temporary transactional table is about to be read. Note
+ that a write implies a read.
+ */
+ STMT_READS_TEMP_TRANS_TABLE,
+ /*
+ If a temporary non-transactional table is about to be read. Note
+ that a write implies a read.
+ */
+ STMT_READS_TEMP_NON_TRANS_TABLE,
+ /*
+ If a transactional table is about to be updated.
+ */
+ STMT_WRITES_TRANS_TABLE,
+ /*
+ If a non-transactional table is about to be updated.
+ */
+ STMT_WRITES_NON_TRANS_TABLE,
+ /*
+ If a temporary transactional table is about to be updated.
+ */
+ STMT_WRITES_TEMP_TRANS_TABLE,
+ /*
+ If a temporary non-transactional table is about to be updated.
+ */
+ STMT_WRITES_TEMP_NON_TRANS_TABLE,
+ /*
+ The last element of the enumeration. Please, if necessary add
+ anything before this.
+ */
+ STMT_ACCESS_TABLE_COUNT
+ };
+
+#ifndef DBUG_OFF
+ static inline const char *stmt_accessed_table_string(enum_stmt_accessed_table accessed_table)
+ {
+ switch (accessed_table)
+ {
+ case STMT_READS_TRANS_TABLE:
+ return "STMT_READS_TRANS_TABLE";
+ break;
+ case STMT_READS_NON_TRANS_TABLE:
+ return "STMT_READS_NON_TRANS_TABLE";
+ break;
+ case STMT_READS_TEMP_TRANS_TABLE:
+ return "STMT_READS_TEMP_TRANS_TABLE";
+ break;
+ case STMT_READS_TEMP_NON_TRANS_TABLE:
+ return "STMT_READS_TEMP_NON_TRANS_TABLE";
+ break;
+ case STMT_WRITES_TRANS_TABLE:
+ return "STMT_WRITES_TRANS_TABLE";
+ break;
+ case STMT_WRITES_NON_TRANS_TABLE:
+ return "STMT_WRITES_NON_TRANS_TABLE";
+ break;
+ case STMT_WRITES_TEMP_TRANS_TABLE:
+ return "STMT_WRITES_TEMP_TRANS_TABLE";
+ break;
+ case STMT_WRITES_TEMP_NON_TRANS_TABLE:
+ return "STMT_WRITES_TEMP_NON_TRANS_TABLE";
+ break;
+ case STMT_ACCESS_TABLE_COUNT:
+ default:
+ DBUG_ASSERT(0);
+ break;
+ }
+ MY_ASSERT_UNREACHABLE();
+ return "";
+ }
+#endif /* DBUG */
+
+ #define BINLOG_DIRECT_ON 0xF0 /* unsafe when
+ --binlog-direct-non-trans-updates
+ is ON */
+
+ #define BINLOG_DIRECT_OFF 0xF /* unsafe when
+ --binlog-direct-non-trans-updates
+ is OFF */
+
+ #define TRX_CACHE_EMPTY 0x33 /* unsafe when trx-cache is empty */
+
+ #define TRX_CACHE_NOT_EMPTY 0xCC /* unsafe when trx-cache is not empty */
+
+ #define IL_LT_REPEATABLE 0xAA /* unsafe when < ISO_REPEATABLE_READ */
+
+ #define IL_GTE_REPEATABLE 0x55 /* unsafe when >= ISO_REPEATABLE_READ */
+
+ /**
+ Sets the type of table that is about to be accessed while executing a
+ statement.
+
+ @param accessed_table Enumeration type that defines the type of table,
+ e.g. temporary, transactional, non-transactional.
+ */
+ inline void set_stmt_accessed_table(enum_stmt_accessed_table accessed_table)
+ {
+ DBUG_ENTER("LEX::set_stmt_accessed_table");
+
+ DBUG_ASSERT(accessed_table >= 0 && accessed_table < STMT_ACCESS_TABLE_COUNT);
+ stmt_accessed_table_flag |= (1U << accessed_table);
+
+ DBUG_VOID_RETURN;
+ }
+
+ /**
+ Checks if a type of table is about to be accessed while executing a
+ statement.
+
+ @param accessed_table Enumeration type that defines the type of table,
+ e.g. temporary, transactional, non-transactional.
+
+ @return
+ @retval TRUE if the type of the table is about to be accessed
+ @retval FALSE otherwise
+ */
+ inline bool stmt_accessed_table(enum_stmt_accessed_table accessed_table)
+ {
+ DBUG_ENTER("LEX::stmt_accessed_table");
+
+ DBUG_ASSERT(accessed_table >= 0 && accessed_table < STMT_ACCESS_TABLE_COUNT);
+
+ DBUG_RETURN((stmt_accessed_table_flag & (1U << accessed_table)) != 0);
+ }
+
+ /**
+ Checks if a temporary non-transactional table is about to be accessed
+ while executing a statement.
+
+ @return
+ @retval TRUE if a temporary non-transactional table is about to be
+ accessed
+ @retval FALSE otherwise
+ */
+ inline bool stmt_accessed_non_trans_temp_table()
+ {
+ DBUG_ENTER("THD::stmt_accessed_non_trans_temp_table");
+
+ DBUG_RETURN((stmt_accessed_table_flag &
+ ((1U << STMT_READS_TEMP_NON_TRANS_TABLE) |
+ (1U << STMT_WRITES_TEMP_NON_TRANS_TABLE))) != 0);
+ }
+
+ /*
+ Checks if a mixed statement is unsafe.
+
+
+ @param in_multi_stmt_transaction_mode defines if there is an on-going
+ multi-transactional statement.
+ @param binlog_direct defines if --binlog-direct-non-trans-updates is
+ active.
+ @param trx_cache_is_not_empty defines if the trx-cache is empty or not.
+ @param trx_isolation defines the isolation level.
+
+ @return
+ @retval TRUE if the mixed statement is unsafe
+ @retval FALSE otherwise
+ */
+ inline bool is_mixed_stmt_unsafe(bool in_multi_stmt_transaction_mode,
+ bool binlog_direct,
+ bool trx_cache_is_not_empty,
+ uint tx_isolation)
+ {
+ bool unsafe= FALSE;
+
+ if (in_multi_stmt_transaction_mode)
+ {
+ uint condition=
+ (binlog_direct ? BINLOG_DIRECT_ON : BINLOG_DIRECT_OFF) &
+ (trx_cache_is_not_empty ? TRX_CACHE_NOT_EMPTY : TRX_CACHE_EMPTY) &
+ (tx_isolation >= ISO_REPEATABLE_READ ? IL_GTE_REPEATABLE : IL_LT_REPEATABLE);
+
+ 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,
+ binlog_unsafe_map[stmt_accessed_table_flag],
+ (binlog_unsafe_map[stmt_accessed_table_flag] & condition)));
+
+ int type_in= 0;
+ for (; type_in < STMT_ACCESS_TABLE_COUNT; type_in++)
+ {
+ if (stmt_accessed_table((enum_stmt_accessed_table) type_in))
+ DBUG_PRINT("LEX::is_mixed_stmt_unsafe", ("ACCESSED %s ",
+ stmt_accessed_table_string((enum_stmt_accessed_table) type_in)));
+ }
+#endif
+ }
+
+ if (stmt_accessed_table(STMT_WRITES_NON_TRANS_TABLE) &&
+ stmt_accessed_table(STMT_READS_TRANS_TABLE) &&
+ tx_isolation < ISO_REPEATABLE_READ)
+ unsafe= TRUE;
+ else if (stmt_accessed_table(STMT_WRITES_TEMP_NON_TRANS_TABLE) &&
+ stmt_accessed_table(STMT_READS_TRANS_TABLE) &&
+ tx_isolation < ISO_REPEATABLE_READ)
+ unsafe= TRUE;
+
+ return(unsafe);
}
/**
@@ -1191,18 +1770,45 @@ public:
{ return sroutines_list.elements != 0; }
private:
- enum enum_binlog_stmt_flag {
- BINLOG_STMT_FLAG_UNSAFE,
- BINLOG_STMT_FLAG_COUNT
+
+ /**
+ Enumeration listing special types of statements.
+
+ Currently, the only possible type is ROW_INJECTION.
+ */
+ enum enum_binlog_stmt_type {
+ /**
+ The statement is a row injection (i.e., either a BINLOG
+ statement or a row event executed by the slave SQL thread).
+ */
+ BINLOG_STMT_TYPE_ROW_INJECTION = 0,
+
+ /** The last element of this enumeration type. */
+ BINLOG_STMT_TYPE_COUNT
};
- /*
- Tells if the parsing stage detected properties of the statement,
- for example: that some items require row-based binlogging to give
- a reliable binlog/replication, or if we will use stored functions
- or triggers which themselves need require row-based binlogging.
+ /**
+ Bit field indicating the type of statement.
+
+ There are two groups of bits:
+
+ - The low BINLOG_STMT_UNSAFE_COUNT bits indicate the types of
+ unsafeness that the current statement has.
+
+ - The next BINLOG_STMT_TYPE_COUNT bits indicate if the statement
+ is of some special type.
+
+ This must be a member of LEX, not of THD: each stored procedure
+ needs to remember its unsafeness state between calls and each
+ stored procedure has its own LEX object (but no own THD object).
*/
uint32 binlog_stmt_flags;
+
+ /**
+ Bit field that determines the type of tables that are about to be
+ be accessed while executing a statement.
+ */
+ uint32 stmt_accessed_table_flag;
};
@@ -1264,24 +1870,7 @@ enum enum_comment_state
class Lex_input_stream
{
public:
- Lex_input_stream() :
- yylineno(1),
- yytoklen(0),
- yylval(NULL),
- m_tok_start(NULL),
- m_tok_end(NULL),
- m_tok_start_prev(NULL),
- m_echo(TRUE),
- m_cpp_tok_start(NULL),
- m_cpp_tok_start_prev(NULL),
- m_cpp_tok_end(NULL),
- m_body_utf8(NULL),
- m_cpp_utf8_processed_ptr(NULL),
- next_state(MY_LEX_START),
- found_semicolon(NULL),
- stmt_prepare_mode(FALSE),
- in_comment(NO_COMMENT),
- m_underscore_cs(NULL)
+ Lex_input_stream()
{
}
@@ -1296,6 +1885,9 @@ public:
@retval TRUE Error
*/
bool init(THD *thd, char *buff, unsigned int length);
+
+ void reset(char *buff, unsigned int length);
+
/**
Set the echo mode.
@@ -1567,6 +2159,17 @@ public:
/** Interface with bison, value of the last token parsed. */
LEX_YYSTYPE yylval;
+ /**
+ LALR(2) resolution, look ahead token.
+ Value of the next token to return, if any,
+ or -1, if no token was parsed in advance.
+ Note: 0 is a legal token, and represents YYEOF.
+ */
+ int lookahead_token;
+
+ /** LALR(2) resolution, value of the look ahead token.*/
+ LEX_YYSTYPE lookahead_yylval;
+
private:
/** Pointer to the current position in the raw input stream. */
char *m_ptr;
@@ -1648,9 +2251,13 @@ public:
/**
TRUE if we're parsing a prepared statement: in this mode
- we should allow placeholders and disallow multi-statements.
+ we should allow placeholders.
*/
bool stmt_prepare_mode;
+ /**
+ TRUE if we should allow multi-statements.
+ */
+ bool multi_statements;
/** State of the lexical analyser for comments. */
enum_comment_state in_comment;
@@ -1680,10 +2287,66 @@ public:
CHARSET_INFO *m_underscore_cs;
};
+/**
+ Abstract representation of a statement.
+ This class is an interface between the parser and the runtime.
+ The parser builds the appropriate sub classes of Sql_statement
+ to represent a SQL statement in the parsed tree.
+ The execute() method in the sub classes contain the runtime implementation.
+ Note that this interface is used for SQL statement recently implemented,
+ the code for older statements tend to load the LEX structure with more
+ attributes instead.
+ The recommended way to implement new statements is to sub-class
+ Sql_statement, as this improves code modularity (see the 'big switch' in
+ dispatch_command()), and decrease the total size of the LEX structure
+ (therefore saving memory in stored programs).
+*/
+class Sql_statement : public Sql_alloc
+{
+public:
+ /**
+ Execute this SQL statement.
+ @param thd the current thread.
+ @return 0 on success.
+ */
+ virtual bool execute(THD *thd) = 0;
+
+protected:
+ /**
+ Constructor.
+ @param lex the LEX structure that represents parts of this statement.
+ */
+ Sql_statement(LEX *lex)
+ : m_lex(lex)
+ {}
+
+ /** Destructor. */
+ virtual ~Sql_statement()
+ {
+ /*
+ Sql_statement objects are allocated in thd->mem_root.
+ In MySQL, the C++ destructor is never called, the underlying MEM_ROOT is
+ simply destroyed instead.
+ Do not rely on the destructor for any cleanup.
+ */
+ DBUG_ASSERT(FALSE);
+ }
+
+protected:
+ /**
+ The legacy LEX structure for this statement.
+ The LEX structure contains the existing properties of the parsed tree.
+ TODO: with time, attributes from LEX should move to sub classes of
+ Sql_statement, so that the parser only builds Sql_statement objects
+ with the minimum set of attributes, instead of a LEX structure that
+ contains the collection of every possible attribute.
+ */
+ LEX *m_lex;
+};
/* The state of the lex parsing. This is saved in the THD struct */
-typedef struct st_lex : public Query_tables_list
+struct LEX: public Query_tables_list
{
SELECT_LEX_UNIT unit; /* most upper unit */
SELECT_LEX select_lex; /* first SELECT_LEX */
@@ -1769,6 +2432,7 @@ typedef struct st_lex : public Query_tables_list
LEX_MASTER_INFO mi; // used by CHANGE MASTER
LEX_SERVER_OPTIONS server_options;
USER_RESOURCES mqh;
+ LEX_RESET_SLAVE reset_slave_info;
ulong type;
/* The following is used by KILL */
killed_state kill_signal;
@@ -1783,7 +2447,9 @@ typedef struct st_lex : public Query_tables_list
the variable can contain 0 or 1 for each nest level.
*/
nesting_map allow_sum_func;
- enum_sql_command sql_command;
+
+ Sql_statement *m_stmt;
+
/*
Usually `expr` rule of yacc is quite reused but some commands better
not support subqueries which comes standard with this rule, like
@@ -1800,7 +2466,6 @@ typedef struct st_lex : public Query_tables_list
*/
bool parse_vcol_expr;
- thr_lock_type lock_option;
enum SSL_type ssl_type; /* defined in violite.h */
enum enum_duplicates duplicates;
enum enum_tx_isolation tx_isolation;
@@ -1817,7 +2482,9 @@ typedef struct st_lex : public Query_tables_list
uint profile_options;
uint uint_geom_type;
uint grant, grant_tot_col, which_columns;
- uint fk_delete_opt, fk_update_opt, fk_match_option;
+ enum Foreign_key::fk_match_opt fk_match_option;
+ enum Foreign_key::fk_option fk_update_opt;
+ enum Foreign_key::fk_option fk_delete_opt;
uint slave_thd_opt, start_transaction_opt;
int nest_level;
/*
@@ -1835,17 +2502,22 @@ typedef struct st_lex : public Query_tables_list
uint8 derived_tables;
uint16 create_view_algorithm;
uint8 create_view_check;
+ uint8 context_analysis_only;
bool drop_if_exists, drop_temporary, local_file, one_shot_set;
bool autocommit;
bool verbose, no_write_to_binlog;
- bool tx_chain, tx_release;
-
- uint8 context_analysis_only;
+ enum enum_yes_no_unknown tx_chain, tx_release;
bool safe_to_cache_query;
bool subqueries, ignore, online;
st_parsing_options parsing_options;
Alter_info alter_info;
+ /*
+ For CREATE TABLE statement last element of table list which is not
+ part of SELECT or LIKE part (i.e. either element for table we are
+ creating or last of tables referenced by foreign keys).
+ */
+ TABLE_LIST *create_last_non_select_table;
/* Prepared statements SQL syntax:*/
LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */
/*
@@ -1861,6 +2533,7 @@ typedef struct st_lex : public Query_tables_list
sp_name *spname;
bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */
bool all_privileges;
+ bool proxy_priv;
sp_pcontext *spcont;
st_sp_chistics sp_chistics;
@@ -1898,10 +2571,20 @@ typedef struct st_lex : public Query_tables_list
This pointer is required to add possibly omitted DEFINER-clause to the
DDL-statement before dumping it to the binlog.
+
+ keyword_delayed_begin_offset is the offset to the beginning of the DELAYED
+ keyword in INSERT DELAYED statement. keyword_delayed_end_offset is the
+ offset to the character right after the DELAYED keyword.
*/
- const char *stmt_definition_begin;
+ union {
+ const char *stmt_definition_begin;
+ uint keyword_delayed_begin_offset;
+ };
- const char *stmt_definition_end;
+ union {
+ const char *stmt_definition_end;
+ uint keyword_delayed_end_offset;
+ };
/**
Collects create options for Field and KEY
@@ -1926,39 +2609,6 @@ typedef struct st_lex : public Query_tables_list
bool is_lex_started; /* If lex_start() did run. For debugging. */
/*
- Special case for SELECT .. FOR UPDATE and LOCK TABLES .. WRITE.
-
- Protect from a impending GRL as otherwise the thread might deadlock
- if it starts waiting for the GRL in mysql_lock_tables.
-
- The protection is needed because there is a race between setting
- the global read lock and waiting for all open tables to be closed.
- The problem is a circular wait where a thread holding "old" open
- tables will wait for the global read lock to be released while the
- thread holding the global read lock will wait for all "old" open
- tables to be closed -- the flush part of flush tables with read
- lock.
- */
- bool protect_against_global_read_lock;
-
- /*
- The following three variables are used in 'CREATE TABLE IF NOT EXISTS ...
- SELECT' statement. They are used to binlog the statement.
-
- create_select_start_with_brace will be set if there is a '(' before
- the first SELECT clause
-
- create_select_pos records the relative position of the SELECT clause
- in the whole statement.
-
- create_select_in_comment will be set if SELECT keyword is in conditional
- comment.
- */
- bool create_select_start_with_brace;
- uint create_select_pos;
- bool create_select_in_comment;
-
- /*
The set of those tables whose fields are referenced in all subqueries
of the query.
TODO: possibly this it is incorrect to have used tables in LEX because
@@ -1967,10 +2617,26 @@ typedef struct st_lex : public Query_tables_list
into the select_lex.
*/
table_map used_tables;
+ /**
+ Maximum number of rows and/or keys examined by the query, both read,
+ changed or written. This is the argument of LIMIT ROWS EXAMINED.
+ The limit is represented by two variables - the Item is needed because
+ in case of parameters we have to delay its evaluation until execution.
+ Once evaluated, its value is stored in examined_rows_limit_cnt.
+ */
+ Item *limit_rows_examined;
+ ulonglong limit_rows_examined_cnt;
+ inline void set_limit_rows_examined()
+ {
+ if (limit_rows_examined)
+ limit_rows_examined_cnt= limit_rows_examined->val_uint();
+ else
+ limit_rows_examined_cnt= ULONGLONG_MAX;
+ }
- st_lex();
+ LEX();
- virtual ~st_lex()
+ virtual ~LEX()
{
destroy_query_tables_list();
plugin_unlock_list(NULL, (plugin_ref *)plugins.buffer, plugins.elements);
@@ -1985,6 +2651,11 @@ typedef struct st_lex : public Query_tables_list
CONTEXT_ANALYSIS_ONLY_VIEW));
}
+ inline bool is_view_context_analysis()
+ {
+ return (context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW);
+ }
+
inline void uncacheable(uint8 cause)
{
safe_to_cache_query= 0;
@@ -2021,7 +2692,7 @@ typedef struct st_lex : public Query_tables_list
Is this update command where 'WHITH CHECK OPTION' clause is important
SYNOPSIS
- st_lex::which_check_option_applicable()
+ LEX::which_check_option_applicable()
RETURN
TRUE have to take 'WHITH CHECK OPTION' clause into account
@@ -2095,7 +2766,37 @@ typedef struct st_lex : public Query_tables_list
}
return FALSE;
}
-} LEX;
+};
+
+
+/**
+ Set_signal_information is a container used in the parsed tree to represent
+ the collection of assignments to condition items in the SIGNAL and RESIGNAL
+ statements.
+*/
+class Set_signal_information
+{
+public:
+ /** Empty default constructor, use clear() */
+ Set_signal_information() {}
+
+ /** Copy constructor. */
+ Set_signal_information(const Set_signal_information& set);
+
+ /** Destructor. */
+ ~Set_signal_information()
+ {}
+
+ /** Clear all items. */
+ void clear();
+
+ /**
+ For each condition item assignment, m_item[] contains the parsed tree
+ that represents the expression assigned, if any.
+ m_item[] is an array indexed by Diag_condition_item_name.
+ */
+ Item *m_item[LAST_DIAG_SET_PROPERTY+1];
+};
/**
@@ -2107,12 +2808,32 @@ class Yacc_state
{
public:
Yacc_state()
- : yacc_yyss(NULL), yacc_yyvs(NULL)
- {}
+ {
+ reset();
+ }
+
+ void reset()
+ {
+ yacc_yyss= NULL;
+ yacc_yyvs= NULL;
+ m_set_signal_info.clear();
+ m_lock_type= TL_READ_DEFAULT;
+ m_mdl_type= MDL_SHARED_READ;
+ }
~Yacc_state();
/**
+ Reset part of the state which needs resetting before parsing
+ substatement.
+ */
+ void reset_before_substatement()
+ {
+ m_lock_type= TL_READ_DEFAULT;
+ m_mdl_type= MDL_SHARED_READ;
+ }
+
+ /**
Bison internal state stack, yyss, when dynamically allocated using
my_yyoverflow().
*/
@@ -2124,6 +2845,37 @@ public:
*/
uchar *yacc_yyvs;
+ /**
+ Fragments of parsed tree,
+ used during the parsing of SIGNAL and RESIGNAL.
+ */
+ Set_signal_information m_set_signal_info;
+
+ /**
+ Type of lock to be used for tables being added to the statement's
+ table list in table_factor, table_alias_ref, single_multi and
+ table_wild_one rules.
+ Statements which use these rules but require lock type different
+ from one specified by this member have to override it by using
+ st_select_lex::set_lock_for_tables() method.
+
+ The default value of this member is TL_READ_DEFAULT. The only two
+ cases in which we change it are:
+ - When parsing SELECT HIGH_PRIORITY.
+ - Rule for DELETE. In which we use this member to pass information
+ about type of lock from delete to single_multi part of rule.
+
+ We should try to avoid introducing new use cases as we would like
+ to get rid of this member eventually.
+ */
+ thr_lock_type m_lock_type;
+
+ /**
+ The type of requested metadata lock for tables added to
+ the statement table list.
+ */
+ enum_mdl_type m_mdl_type;
+
/*
TODO: move more attributes from the LEX structure here.
*/
@@ -2158,10 +2910,16 @@ public:
Lex_input_stream m_lip;
Yacc_state m_yacc;
+
+ void reset(char *found_semicolon, unsigned int length)
+ {
+ m_lip.reset(found_semicolon, length);
+ m_yacc.reset();
+ }
};
-struct st_lex_local: public st_lex
+struct st_lex_local: public LEX
{
static void *operator new(size_t size) throw()
{
@@ -2181,7 +2939,9 @@ extern void lex_init(void);
extern void lex_free(void);
extern void lex_start(THD *thd);
extern void lex_end(LEX *lex);
-extern int MYSQLlex(void *arg, void *yythd);
+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 void trim_whitespace(CHARSET_INFO *cs, LEX_STRING *str);
@@ -2191,6 +2951,8 @@ extern bool is_lex_native_function(const LEX_STRING *name);
@} (End of group Semantic_Analysis)
*/
-int my_missing_function_error(const LEX_STRING &token, const char *name);
+void my_missing_function_error(const LEX_STRING &token, const char *name);
+bool is_keyword(const char *name, uint len);
#endif /* MYSQL_SERVER */
+#endif /* SQL_LEX_INCLUDED */
diff --git a/sql/sql_lifo_buffer.h b/sql/sql_lifo_buffer.h
index 95fe02ecda7..feec4aeb4c2 100644
--- a/sql/sql_lifo_buffer.h
+++ b/sql/sql_lifo_buffer.h
@@ -1,3 +1,19 @@
+/*
+ Copyright (c) 2010, 2011, Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
/**
@defgroup Bi-directional LIFO buffers used by DS-MRR implementation
@{
diff --git a/sql/sql_list.cc b/sql/sql_list.cc
index 9b8ec5c5742..2c1b3c47d55 100644
--- a/sql/sql_list.cc
+++ b/sql/sql_list.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2001, 2003, 2005-2007 MySQL AB
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,14 +11,14 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_list.h"
list_node end_of_list;
diff --git a/sql/sql_list.h b/sql/sql_list.h
index 6ffed441f8d..7538f69766d 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -13,14 +13,17 @@
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
-*/
-
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifdef USE_PRAGMA_INTERFACE
#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 */
+#include "thr_malloc.h" /* sql_alloc */
+
/* mysql standard class memory allocator */
class Sql_alloc
@@ -327,11 +330,12 @@ 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
less than N elements.
*/
- inline void *nth_element(int n)
+ void *elem(int n)
{
list_node *node= first;
void *data= NULL;
@@ -347,6 +351,8 @@ public:
}
return data;
}
+#endif
+
#ifdef LIST_EXTRA_DEBUG
/*
Check list invariants and print results into trace. Invariants are:
@@ -525,7 +531,9 @@ public:
}
empty();
}
- inline T *nth_element(int n) { return (T*)base_list::nth_element(n); }
+#ifndef DBUG_OFF
+ T *elem(int n) { return (T*)base_list::elem(n); }
+#endif
};
@@ -618,11 +626,11 @@ struct ilink
struct ilink **prev,*next;
static void *operator new(size_t size) throw ()
{
- return (void*)my_malloc((uint)size, MYF(MY_WME | MY_FAE));
+ return (void*)my_malloc((uint)size, MYF(MY_WME | MY_FAE | ME_FATALERROR));
}
static void operator delete(void* ptr_arg, size_t size)
{
- my_free((uchar*)ptr_arg, MYF(MY_WME|MY_ALLOW_ZERO_PTR));
+ my_free(ptr_arg);
}
inline ilink()
@@ -664,18 +672,18 @@ public:
template <class T> class I_List_iterator;
-/*
- WARNING: copy constructor of this class does not create a usable
- copy, as its members may point at each other.
-*/
class base_ilist
{
+ struct ilink *first;
+ struct ilink last;
public:
- struct ilink *first,last;
inline void empty() { first= &last; last.prev= &first; }
base_ilist() { empty(); }
inline bool is_empty() { return first == &last; }
+ // Returns true if p is the last "real" object in the list,
+ // i.e. p->next points to the sentinel.
+ inline bool is_last(ilink *p) { return p->next == NULL || p->next == &last; }
inline void append(ilink *a)
{
first->prev= &a->next;
@@ -700,7 +708,31 @@ public:
{
return (first != &last) ? first : 0;
}
- friend class base_list_iterator;
+
+ /**
+ Moves list elements to new owner, and empties current owner (i.e. this).
+
+ @param[in,out] new_owner The new owner of the list elements.
+ Should be empty in input.
+ */
+
+ void move_elements_to(base_ilist *new_owner)
+ {
+ DBUG_ASSERT(new_owner->is_empty());
+ new_owner->first= first;
+ new_owner->last= last;
+ empty();
+ }
+
+ friend class base_ilist_iterator;
+ private:
+ /*
+ We don't want to allow copying of this class, as that would give us
+ two list heads containing the same elements.
+ So we declare, but don't define copy CTOR and assignment operator.
+ */
+ base_ilist(const base_ilist&);
+ void operator=(const base_ilist&);
};
@@ -727,12 +759,16 @@ class I_List :private base_ilist
{
public:
I_List() :base_ilist() {}
+ inline bool is_last(T *p) { return base_ilist::is_last(p); }
inline void empty() { base_ilist::empty(); }
inline bool is_empty() { return base_ilist::is_empty(); }
inline void append(T* a) { base_ilist::append(a); }
inline void push_back(T* a) { base_ilist::push_back(a); }
inline T* get() { return (T*) base_ilist::get(); }
inline T* head() { return (T*) base_ilist::head(); }
+ inline void move_elements_to(I_List<T>* new_owner) {
+ base_ilist::move_elements_to(new_owner);
+ }
#ifndef _lint
friend class I_List_iterator<T>;
#endif
@@ -773,4 +809,7 @@ list_copy_and_replace_each_value(List<T> &list, MEM_ROOT *mem_root)
it.replace(el->clone(mem_root));
}
+void free_list(I_List <i_string_pair> *list);
+void free_list(I_List <i_string> *list);
+
#endif // INCLUDES_MYSQL_SQL_LIST_H
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 01961cec04a..b4f8b107f9b 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -1,5 +1,6 @@
/*
- Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2000, 2014, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2014, Monty Progrm 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
@@ -12,27 +13,63 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* Copy data from a textfile to table */
-#include "mysql_priv.h"
+/* 2006-12 Erik Wetterberg : LOAD XML added */
+
+#include "sql_priv.h"
+#include "unireg.h"
+#include "sql_load.h"
+#include "sql_load.h"
+#include "sql_cache.h" // query_cache_*
+#include "sql_base.h" // fill_record_n_invoke_before_triggers
#include <my_dir.h>
+#include "sql_view.h" // check_key_in_view
+#include "sql_insert.h" // check_that_all_fields_are_given_values,
+ // prepare_triggers_for_insert_stmt,
+ // write_record
+#include "sql_acl.h" // INSERT_ACL, UPDATE_ACL
+#include "log_event.h" // Delete_file_log_event,
+ // Execute_load_query_log_event,
+ // LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F
#include <m_ctype.h>
#include "rpl_mi.h"
#include "sql_repl.h"
#include "sp_head.h"
#include "sql_trigger.h"
+#include "sql_derived.h"
#include "sql_show.h"
+class XML_TAG {
+public:
+ int level;
+ String field;
+ String value;
+ XML_TAG(int l, String f, String v);
+};
+
+
+XML_TAG::XML_TAG(int l, String f, String v)
+{
+ level= l;
+ field.append(f);
+ value.append(v);
+}
+
+
+#define GET (stack_pos != stack ? *--stack_pos : my_b_get(&cache))
+#define PUSH(A) *(stack_pos++)=(A)
+
class READ_INFO {
File file;
uchar *buffer, /* Buffer for read text */
*end_of_buff; /* Data in bufferts ends here */
uint buff_length, /* Length of buffert */
max_length; /* Max length of row */
- char *field_term_ptr,*line_term_ptr,*line_start_ptr,*line_start_end;
+ const uchar *field_term_ptr,*line_term_ptr;
+ const char *line_start_ptr,*line_start_end;
uint field_term_length,line_term_length,enclosed_length;
int field_term_char,line_term_char,enclosed_char,escape_char;
int *stack,*stack_pos;
@@ -40,6 +77,7 @@ class READ_INFO {
bool need_end_io_cache;
IO_CACHE cache;
NET *io_net;
+ int level; /* for load xml */
public:
bool error,line_cuted,found_null,enclosed;
@@ -55,8 +93,14 @@ public:
int read_fixed_length(void);
int next_line(void);
char unescape(char chr);
- int terminator(char *ptr,uint length);
+ int terminator(const uchar *ptr, uint length);
bool find_start_of_fields();
+ /* load xml */
+ List<XML_TAG> taglist;
+ int read_value(int delim, String *val);
+ int read_xml();
+ int clear_level(int level);
+
/*
We need to force cache close before destructor is invoked to log
the last read block
@@ -75,6 +119,15 @@ public:
either the table or THD value
*/
void set_io_cache_arg(void* arg) { cache.arg = arg; }
+
+ /**
+ skip all data till the eof.
+ */
+ void skip_data_till_eof()
+ {
+ while (GET != my_b_EOF)
+ ;
+ }
};
static int read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
@@ -87,14 +140,16 @@ static int read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
List<Item> &set_values, READ_INFO &read_info,
String &enclosed, ulong skip_lines,
bool ignore_check_option_errors);
+
+static int read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
+ List<Item> &fields_vars, List<Item> &set_fields,
+ List<Item> &set_values, READ_INFO &read_info,
+ String &enclosed, ulong skip_lines,
+ bool ignore_check_option_errors);
+
#ifndef EMBEDDED_LIBRARY
-static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
- const char* db_arg, /* table's database */
- const char* table_name_arg,
- enum enum_duplicates duplicates,
- bool ignore,
- bool transactional_table,
- int errocode);
+static bool write_execute_load_query_log_event(THD *, sql_exchange*, const
+ char*, const char*, bool, enum enum_duplicates, bool, bool, int);
#endif /* EMBEDDED_LIBRARY */
/*
@@ -134,6 +189,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
#ifndef EMBEDDED_LIBRARY
LOAD_FILE_INFO lf_info;
killed_state killed_status;
+ bool is_concurrent;
#endif
char *db = table_list->db; // This is never null
/*
@@ -152,7 +208,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
load data infile, so in mixed mode we go to row-based for
avoiding the problem.
*/
- thd->set_current_stmt_binlog_row_based_if_mixed();
+ thd->set_current_stmt_binlog_format_row_if_mixed();
#ifdef EMBEDDED_LIBRARY
read_file_from_client = 0; //server is always in the same process
@@ -175,7 +231,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
ER(WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED));
}
- if (open_and_lock_tables(thd, table_list))
+ if (open_and_lock_tables(thd, table_list, TRUE, 0))
DBUG_RETURN(TRUE);
if (mysql_handle_single_derived(thd->lex, table_list, DT_MERGE_FOR_INSERT) ||
mysql_handle_single_derived(thd->lex, table_list, DT_PREPARE))
@@ -215,12 +271,21 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
table= table_list->table;
transactional_table= table->file->has_transactions();
+#ifndef EMBEDDED_LIBRARY
+ is_concurrent= (table_list->lock_type == TL_WRITE_CONCURRENT_INSERT);
+#endif
if (!fields_vars.elements)
{
- Field **field;
- for (field=table->field; *field ; field++)
- fields_vars.push_back(new Item_field(*field));
+ Field_iterator_table_ref field_iterator;
+ field_iterator.set(table_list);
+ for (; !field_iterator.end_of_fields(); field_iterator.next())
+ {
+ Item *item;
+ if (!(item= field_iterator.create_item(thd)))
+ DBUG_RETURN(TRUE);
+ fields_vars.push_back(item->real_item());
+ }
bitmap_set_all(table->write_set);
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
/*
@@ -361,10 +426,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
// if we are not in slave thread, the file must be:
if (!thd->slave_thread &&
- !((stat_info.st_mode & S_IROTH) == S_IROTH && // readable by others
- (stat_info.st_mode & S_IFLNK) != S_IFLNK && // and not a symlink
- ((stat_info.st_mode & S_IFREG) == S_IFREG || // and a regular file
- (stat_info.st_mode & S_IFIFO) == S_IFIFO))) // or FIFO
+ !((stat_info.st_mode & S_IFLNK) != S_IFLNK && // symlink
+ ((stat_info.st_mode & S_IFREG) == S_IFREG || // regular file
+ (stat_info.st_mode & S_IFIFO) == S_IFIFO))) // named pipe
{
my_error(ER_TEXTFILE_NOT_READABLE, MYF(0), name);
DBUG_RETURN(TRUE);
@@ -372,8 +436,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
if ((stat_info.st_mode & S_IFIFO) == S_IFIFO)
is_fifo= 1;
#endif
+ if ((file= mysql_file_open(key_file_load,
+ name, O_RDONLY, MYF(MY_WME))) < 0)
- if ((file=my_open(name,O_RDONLY,MYF(MY_WME))) < 0)
DBUG_RETURN(TRUE);
}
@@ -391,8 +456,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
info.escape_char, read_file_from_client, is_fifo);
if (read_info.error)
{
- if (file >= 0)
- my_close(file,MYF(0)); // no files in net reading
+ if (file >= 0)
+ mysql_file_close(file, MYF(0)); // no files in net reading
DBUG_RETURN(TRUE); // Can't allocate buffers
}
@@ -410,7 +475,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
thd->count_cuted_fields= CHECK_FIELD_WARN; /* calc cuted fields */
thd->cuted_fields=0L;
/* Skip lines if there is a line terminator */
- if (ex->line_term->length())
+ if (ex->line_term->length() && ex->filetype != FILETYPE_XML)
{
/* ex->skip_lines needs to be preserved for logging */
while (skip_lines > 0)
@@ -432,7 +497,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
(!table->triggers ||
!table->triggers->has_delete_triggers()))
table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
- if (!thd->prelocked_mode)
+ if (thd->locked_tables_mode <= LTM_LOCK_TABLES)
table->file->ha_start_bulk_insert((ha_rows) 0);
table->copy_blobs=1;
@@ -442,7 +507,11 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
MODE_STRICT_ALL_TABLES)));
thd_progress_init(thd, 2);
- if (!field_term->length() && !enclosed->length())
+ if (ex->filetype == FILETYPE_XML) /* load xml */
+ error= read_xml_field(thd, info, table_list, fields_vars,
+ set_fields, set_values, read_info,
+ *(ex->line_term), skip_lines, ignore);
+ else if (!field_term->length() && !enclosed->length())
error= read_fixed_length(thd, info, table_list, fields_vars,
set_fields, set_values, read_info,
skip_lines, ignore);
@@ -450,10 +519,11 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
error= read_sep_field(thd, info, table_list, fields_vars,
set_fields, set_values, read_info,
*enclosed, skip_lines, ignore);
-
+
thd_proc_info(thd, "End bulk insert");
thd_progress_next_stage(thd);
- if (!thd->prelocked_mode && table->file->ha_end_bulk_insert() && !error)
+ if (thd->locked_tables_mode <= LTM_LOCK_TABLES &&
+ table->file->ha_end_bulk_insert() && !error)
{
table->file->print_error(my_errno, MYF(0));
error= 1;
@@ -463,7 +533,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
table->next_number_field=0;
}
if (file >= 0)
- my_close(file,MYF(0));
+ mysql_file_close(file, MYF(0));
free_blobs(table); /* if pack_blob was used */
table->copy_blobs=0;
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
@@ -489,8 +559,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
if (error)
{
if (read_file_from_client)
- while (!read_info.next_line())
- ;
+ read_info.skip_data_till_eof();
#ifndef EMBEDDED_LIBRARY
if (mysql_bin_log.is_open())
@@ -530,6 +599,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
(void) write_execute_load_query_log_event(thd, ex,
table_list->db,
table_list->table_name,
+ is_concurrent,
handle_duplicates, ignore,
transactional_table,
errcode);
@@ -546,7 +616,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
goto err;
}
sprintf(name, ER(ER_LOAD_INFO), (ulong) info.records, (ulong) info.deleted,
- (ulong) (info.records - info.copied), (ulong) thd->cuted_fields);
+ (ulong) (info.records - info.copied),
+ (ulong) thd->warning_info->statement_warn_count());
if (thd->transaction.stmt.modified_non_trans_table)
thd->transaction.all.modified_non_trans_table= TRUE;
@@ -561,8 +632,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
version for the binary log to mark that table maps are invalid
after this point.
*/
- if (thd->current_stmt_binlog_row_based)
- error= thd->binlog_flush_pending_rows_event(true);
+ if (thd->is_current_stmt_binlog_format_row())
+ error= thd->binlog_flush_pending_rows_event(TRUE, transactional_table);
else
{
/*
@@ -576,6 +647,7 @@ 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,
+ is_concurrent,
handle_duplicates, ignore,
transactional_table,
errcode);
@@ -611,6 +683,7 @@ err:
static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
const char* db_arg, /* table's database */
const char* table_name_arg,
+ bool is_concurrent,
enum enum_duplicates duplicates,
bool ignore,
bool transactional_table,
@@ -624,15 +697,12 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
int n;
const char *tdb= (thd->db != NULL ? thd->db : db_arg);
const char *qualify_db= NULL;
- char name_buffer[SAFE_NAME_LEN*2];
char command_buffer[1024];
- String string_buf(name_buffer, sizeof(name_buffer),
- system_charset_info);
String query_str(command_buffer, sizeof(command_buffer),
system_charset_info);
- Load_log_event lle(thd, ex, tdb, table_name_arg, fv, duplicates,
- ignore, transactional_table);
+ Load_log_event lle(thd, ex, tdb, table_name_arg, fv, is_concurrent,
+ duplicates, ignore, transactional_table);
/*
force in a LOCAL if there was one in the original.
@@ -667,10 +737,14 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
{
if (n++)
query_str.append(", ");
- if (item->name)
+ if (item->real_type() == Item::FIELD_ITEM)
append_identifier(thd, &query_str, item->name, strlen(item->name));
else
+ {
+ /* Actually Item_user_var_as_out_param despite claiming STRING_ITEM. */
+ DBUG_ASSERT(item->type() == Item::STRING_ITEM);
((Item_user_var_as_out_param *)item)->print_for_load(thd, &query_str);
+ }
}
query_str.append(")");
}
@@ -680,17 +754,16 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
List_iterator<Item> lu(thd->lex->update_list);
List_iterator<Item> lv(thd->lex->value_list);
- query_str.append(" SET ");
+ query_str.append(STRING_WITH_LEN(" SET "));
n= 0;
while ((item= lu++))
{
val= lv++;
if (n++)
- query_str.append(", ");
+ query_str.append(STRING_WITH_LEN(", "));
append_identifier(thd, &query_str, item->name, strlen(item->name));
- query_str.append("=");
- val->print(&query_str, QT_ORDINARY);
+ query_str.append(val->name);
}
}
@@ -702,7 +775,7 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
(uint) (fname_start - 1), (uint) fname_end,
(duplicates == DUP_REPLACE) ? LOAD_DUP_REPLACE :
(ignore ? LOAD_DUP_IGNORE : LOAD_DUP_ERROR),
- transactional_table, FALSE, errcode);
+ transactional_table, FALSE, FALSE, errcode);
return mysql_bin_log.write(&e);
}
@@ -785,9 +858,10 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (pos == read_info.row_end)
{
thd->cuted_fields++; /* Not enough fields */
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_WARN_TOO_FEW_RECORDS,
- ER(ER_WARN_TOO_FEW_RECORDS), thd->row_count);
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_TOO_FEW_RECORDS,
+ ER(ER_WARN_TOO_FEW_RECORDS),
+ thd->warning_info->current_row_for_warning());
if (!field->maybe_null() && field->type() == FIELD_TYPE_TIMESTAMP)
((Field_timestamp*) field)->set_time();
}
@@ -808,9 +882,10 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (pos != read_info.row_end)
{
thd->cuted_fields++; /* To long row */
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_WARN_TOO_MANY_RECORDS,
- ER(ER_WARN_TOO_MANY_RECORDS), thd->row_count);
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_TOO_MANY_RECORDS,
+ ER(ER_WARN_TOO_MANY_RECORDS),
+ thd->warning_info->current_row_for_warning());
}
if (thd->killed ||
@@ -843,11 +918,12 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (read_info.line_cuted)
{
thd->cuted_fields++; /* To long row */
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_WARN_TOO_MANY_RECORDS,
- ER(ER_WARN_TOO_MANY_RECORDS), thd->row_count);
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_TOO_MANY_RECORDS,
+ ER(ER_WARN_TOO_MANY_RECORDS),
+ thd->warning_info->current_row_for_warning());
}
- thd->row_count++;
+ thd->warning_info->inc_current_row_for_warning();
continue_loop:;
}
DBUG_RETURN(test(read_info.error));
@@ -927,7 +1003,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (field->reset())
{
my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0), field->field_name,
- thd->row_count);
+ thd->warning_info->current_row_for_warning());
DBUG_RETURN(1);
}
field->set_null();
@@ -999,7 +1075,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (field->reset())
{
my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0),field->field_name,
- thd->row_count);
+ thd->warning_info->current_row_for_warning());
DBUG_RETURN(1);
}
if (!field->maybe_null() && field->type() == FIELD_TYPE_TIMESTAMP)
@@ -1013,7 +1089,8 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
thd->cuted_fields++;
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_TOO_FEW_RECORDS,
- ER(ER_WARN_TOO_FEW_RECORDS), thd->row_count);
+ ER(ER_WARN_TOO_FEW_RECORDS),
+ thd->warning_info->current_row_for_warning());
}
else if (item->type() == Item::STRING_ITEM)
{
@@ -1057,19 +1134,184 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (read_info.line_cuted)
{
thd->cuted_fields++; /* To long row */
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_WARN_TOO_MANY_RECORDS, ER(ER_WARN_TOO_MANY_RECORDS),
- thd->row_count);
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_TOO_MANY_RECORDS, ER(ER_WARN_TOO_MANY_RECORDS),
+ thd->warning_info->current_row_for_warning());
if (thd->killed)
DBUG_RETURN(1);
}
- thd->row_count++;
+ thd->warning_info->inc_current_row_for_warning();
continue_loop:;
}
DBUG_RETURN(test(read_info.error));
}
+/****************************************************************************
+** Read rows in xml format
+****************************************************************************/
+static int
+read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
+ List<Item> &fields_vars, List<Item> &set_fields,
+ List<Item> &set_values, READ_INFO &read_info,
+ String &row_tag, ulong skip_lines,
+ bool ignore_check_option_errors)
+{
+ List_iterator_fast<Item> it(fields_vars);
+ Item *item;
+ TABLE *table= table_list->table;
+ bool no_trans_update_stmt;
+ CHARSET_INFO *cs= read_info.read_charset;
+ DBUG_ENTER("read_xml_field");
+
+ no_trans_update_stmt= !table->file->has_transactions();
+
+ for ( ; ; it.rewind())
+ {
+ if (thd->killed)
+ {
+ thd->send_kill_message();
+ DBUG_RETURN(1);
+ }
+
+ // read row tag and save values into tag list
+ if (read_info.read_xml())
+ break;
+
+ List_iterator_fast<XML_TAG> xmlit(read_info.taglist);
+ xmlit.rewind();
+ XML_TAG *tag= NULL;
+
+#ifndef DBUG_OFF
+ DBUG_PRINT("read_xml_field", ("skip_lines=%d", (int) skip_lines));
+ while ((tag= xmlit++))
+ {
+ DBUG_PRINT("read_xml_field", ("got tag:%i '%s' '%s'",
+ tag->level, tag->field.c_ptr(),
+ tag->value.c_ptr()));
+ }
+#endif
+
+ restore_record(table, s->default_values);
+
+ while ((item= it++))
+ {
+ /* If this line is to be skipped we don't want to fill field or var */
+ if (skip_lines)
+ continue;
+
+ /* find field in tag list */
+ xmlit.rewind();
+ tag= xmlit++;
+
+ while(tag && strcmp(tag->field.c_ptr(), item->name) != 0)
+ tag= xmlit++;
+
+ if (!tag) // found null
+ {
+ if (item->type() == Item::FIELD_ITEM)
+ {
+ Field *field= ((Item_field *) item)->field;
+ field->reset();
+ field->set_null();
+ if (field == table->next_number_field)
+ table->auto_increment_field_not_null= TRUE;
+ if (!field->maybe_null())
+ {
+ if (field->type() == FIELD_TYPE_TIMESTAMP)
+ ((Field_timestamp *) field)->set_time();
+ else if (field != table->next_number_field)
+ field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_NULL_TO_NOTNULL, 1);
+ }
+ }
+ else
+ ((Item_user_var_as_out_param *) item)->set_null_value(cs);
+ continue;
+ }
+
+ if (item->type() == Item::FIELD_ITEM)
+ {
+
+ Field *field= ((Item_field *)item)->field;
+ field->set_notnull();
+ if (field == table->next_number_field)
+ table->auto_increment_field_not_null= TRUE;
+ field->store((char *) tag->value.ptr(), tag->value.length(), cs);
+ }
+ else
+ ((Item_user_var_as_out_param *) item)->set_value(
+ (char *) tag->value.ptr(),
+ tag->value.length(), cs);
+ }
+
+ if (read_info.error)
+ break;
+
+ if (skip_lines)
+ {
+ skip_lines--;
+ continue;
+ }
+
+ if (item)
+ {
+ /* Have not read any field, thus input file is simply ended */
+ if (item == fields_vars.head())
+ break;
+
+ for ( ; item; item= it++)
+ {
+ if (item->type() == Item::FIELD_ITEM)
+ {
+ /*
+ QQ: We probably should not throw warning for each field.
+ But how about intention to always have the same number
+ of warnings in THD::cuted_fields (and get rid of cuted_fields
+ in the end ?)
+ */
+ thd->cuted_fields++;
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_TOO_FEW_RECORDS,
+ ER(ER_WARN_TOO_FEW_RECORDS),
+ thd->warning_info->current_row_for_warning());
+ }
+ else
+ ((Item_user_var_as_out_param *)item)->set_null_value(cs);
+ }
+ }
+
+ if (thd->killed ||
+ fill_record_n_invoke_before_triggers(thd, set_fields, set_values,
+ ignore_check_option_errors,
+ table->triggers,
+ TRG_EVENT_INSERT))
+ DBUG_RETURN(1);
+
+ switch (table_list->view_check_option(thd,
+ ignore_check_option_errors)) {
+ case VIEW_CHECK_SKIP:
+ read_info.next_line();
+ goto continue_loop;
+ case VIEW_CHECK_ERROR:
+ DBUG_RETURN(-1);
+ }
+
+ if (write_record(thd, table, &info))
+ DBUG_RETURN(1);
+
+ /*
+ We don't need to reset auto-increment field since we are restoring
+ its default value at the beginning of each loop iteration.
+ */
+ thd->transaction.stmt.modified_non_trans_table= no_trans_update_stmt;
+ thd->warning_info->inc_current_row_for_warning();
+ continue_loop:;
+ }
+ DBUG_RETURN(test(read_info.error) || thd->is_error());
+} /* load xml end */
+
+
/* Unescape all escape characters, mark \N as null */
char
@@ -1101,14 +1343,23 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs,
String &field_term, String &line_start, String &line_term,
String &enclosed_par, int escape, bool get_it_from_net,
bool is_fifo)
- :file(file_par), buff_length(tot_length), escape_char(escape),
+ :file(file_par), buffer(NULL), buff_length(tot_length), escape_char(escape),
found_end_of_line(false), eof(false), need_end_io_cache(false),
error(false), line_cuted(false), found_null(false), read_charset(cs)
{
- field_term_ptr=(char*) field_term.ptr();
+ /*
+ Field and line terminators must be interpreted as sequence of unsigned char.
+ Otherwise, non-ascii terminators will be negative on some platforms,
+ and positive on others (depending on the implementation of char).
+ */
+ field_term_ptr=
+ static_cast<const uchar*>(static_cast<const void*>(field_term.ptr()));
field_term_length= field_term.length();
- line_term_ptr=(char*) line_term.ptr();
+ line_term_ptr=
+ static_cast<const uchar*>(static_cast<const void*>(line_term.ptr()));
line_term_length= line_term.length();
+
+ level= 0; /* for load xml */
if (line_start.length() == 0)
{
line_start_ptr=0;
@@ -1116,7 +1367,7 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs,
}
else
{
- line_start_ptr=(char*) line_start.ptr();
+ line_start_ptr= line_start.ptr();
line_start_end=line_start_ptr+line_start.length();
start_of_line= 1;
}
@@ -1125,12 +1376,12 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs,
!memcmp(field_term_ptr,line_term_ptr,field_term_length))
{
line_term_length=0;
- line_term_ptr=(char*) "";
+ line_term_ptr= NULL;
}
enclosed_char= (enclosed_length=enclosed_par.length()) ?
(uchar) enclosed_par[0] : INT_MAX;
- field_term_char= field_term_length ? (uchar) field_term_ptr[0] : INT_MAX;
- line_term_char= line_term_length ? (uchar) line_term_ptr[0] : INT_MAX;
+ field_term_char= field_term_length ? field_term_ptr[0] : INT_MAX;
+ line_term_char= line_term_length ? line_term_ptr[0] : INT_MAX;
/* Set of a stack for unget if long terminators */
uint length= max(cs->mbmaxlen, max(field_term_length, line_term_length)) + 1;
@@ -1147,8 +1398,8 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs,
(is_fifo ? READ_FIFO : READ_CACHE),0L,1,
MYF(MY_WME)))
{
- my_free((uchar*) buffer,MYF(0)); /* purecov: inspected */
- buffer= 0;
+ my_free(buffer); /* purecov: inspected */
+ buffer= NULL;
error=1;
}
else
@@ -1177,21 +1428,21 @@ READ_INFO::~READ_INFO()
{
if (need_end_io_cache)
::end_io_cache(&cache);
- my_free(buffer, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(buffer);
+ List_iterator<XML_TAG> xmlit(taglist);
+ XML_TAG *t;
+ while ((t= xmlit++))
+ delete(t);
}
-#define GET (stack_pos != stack ? *--stack_pos : my_b_get(&cache))
-#define PUSH(A) *(stack_pos++)=(A)
-
-
-inline int READ_INFO::terminator(char *ptr,uint length)
+inline int READ_INFO::terminator(const uchar *ptr,uint length)
{
int chr=0; // Keep gcc happy
uint i;
for (i=1 ; i < length ; i++)
{
- if ((chr=GET) != *++ptr)
+ if ((chr=GET) != *(uchar*)++ptr)
{
break;
}
@@ -1200,7 +1451,7 @@ inline int READ_INFO::terminator(char *ptr,uint length)
return 1;
PUSH(chr);
while (i-- > 1)
- PUSH((uchar) *--ptr);
+ PUSH(*--ptr);
return 0;
}
@@ -1332,7 +1583,7 @@ int READ_INFO::read_field()
if (my_mbcharlen(read_charset, chr) > 1 &&
to + my_mbcharlen(read_charset, chr) <= end_of_buff)
{
- uchar* p= (uchar*) to;
+ uchar* p= to;
int ml, i;
*to++ = chr;
@@ -1357,7 +1608,7 @@ int READ_INFO::read_field()
(const char *)to))
continue;
for (i= 0; i < ml; i++)
- PUSH((uchar) *--to);
+ PUSH(*--to);
chr= GET;
}
#endif
@@ -1506,7 +1757,7 @@ bool READ_INFO::find_start_of_fields()
return 1;
}
} while ((char) chr != line_start_ptr[0]);
- for (char *ptr=line_start_ptr+1 ; ptr != line_start_end ; ptr++)
+ for (const char *ptr=line_start_ptr+1 ; ptr != line_start_end ; ptr++)
{
chr=GET; // Eof will be checked later
if ((char) chr != *ptr)
@@ -1514,10 +1765,326 @@ bool READ_INFO::find_start_of_fields()
PUSH(chr);
while (--ptr != line_start_ptr)
{ // Restart with next char
- PUSH((uchar) *ptr);
+ PUSH( *ptr);
}
goto try_again;
}
}
return 0;
}
+
+
+/*
+ Clear taglist from tags with a specified level
+*/
+int READ_INFO::clear_level(int level_arg)
+{
+ DBUG_ENTER("READ_INFO::read_xml clear_level");
+ List_iterator<XML_TAG> xmlit(taglist);
+ xmlit.rewind();
+ XML_TAG *tag;
+
+ while ((tag= xmlit++))
+ {
+ if(tag->level >= level_arg)
+ {
+ xmlit.remove();
+ delete tag;
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Convert an XML entity to Unicode value.
+ Return -1 on error;
+*/
+static int
+my_xml_entity_to_char(const char *name, uint length)
+{
+ if (length == 2)
+ {
+ if (!memcmp(name, "gt", length))
+ return '>';
+ if (!memcmp(name, "lt", length))
+ return '<';
+ }
+ else if (length == 3)
+ {
+ if (!memcmp(name, "amp", length))
+ return '&';
+ }
+ else if (length == 4)
+ {
+ if (!memcmp(name, "quot", length))
+ return '"';
+ if (!memcmp(name, "apos", length))
+ return '\'';
+ }
+ return -1;
+}
+
+
+/**
+ @brief Convert newline, linefeed, tab to space
+
+ @param chr character
+
+ @details According to the "XML 1.0" standard,
+ only space (#x20) characters, carriage returns,
+ line feeds or tabs are considered as spaces.
+ Convert all of them to space (#x20) for parsing simplicity.
+*/
+static int
+my_tospace(int chr)
+{
+ return (chr == '\t' || chr == '\r' || chr == '\n') ? ' ' : chr;
+}
+
+
+/*
+ Read an xml value: handle multibyte and xml escape
+*/
+int READ_INFO::read_value(int delim, String *val)
+{
+ int chr;
+ String tmp;
+
+ for (chr= GET; my_tospace(chr) != delim && chr != my_b_EOF;)
+ {
+#ifdef USE_MB
+ if (my_mbcharlen(read_charset, chr) > 1)
+ {
+ DBUG_PRINT("read_xml",("multi byte"));
+ int i, ml= my_mbcharlen(read_charset, chr);
+ for (i= 1; i < ml; i++)
+ {
+ val->append(chr);
+ /*
+ Don't use my_tospace() in the middle of a multi-byte character
+ TODO: check that the multi-byte sequence is valid.
+ */
+ chr= GET;
+ if (chr == my_b_EOF)
+ return chr;
+ }
+ }
+#endif
+ if(chr == '&')
+ {
+ tmp.length(0);
+ for (chr= my_tospace(GET) ; chr != ';' ; chr= my_tospace(GET))
+ {
+ if (chr == my_b_EOF)
+ return chr;
+ tmp.append(chr);
+ }
+ if ((chr= my_xml_entity_to_char(tmp.ptr(), tmp.length())) >= 0)
+ val->append(chr);
+ else
+ {
+ val->append('&');
+ val->append(tmp);
+ val->append(';');
+ }
+ }
+ else
+ val->append(chr);
+ chr= GET;
+ }
+ return my_tospace(chr);
+}
+
+
+/*
+ Read a record in xml format
+ tags and attributes are stored in taglist
+ when tag set in ROWS IDENTIFIED BY is closed, we are ready and return
+*/
+int READ_INFO::read_xml()
+{
+ DBUG_ENTER("READ_INFO::read_xml");
+ int chr, chr2, chr3;
+ int delim= 0;
+ String tag, attribute, value;
+ bool in_tag= false;
+
+ tag.length(0);
+ attribute.length(0);
+ value.length(0);
+
+ for (chr= my_tospace(GET); chr != my_b_EOF ; )
+ {
+ switch(chr){
+ case '<': /* read tag */
+ /* TODO: check if this is a comment <!-- comment --> */
+ chr= my_tospace(GET);
+ if(chr == '!')
+ {
+ chr2= GET;
+ chr3= GET;
+
+ if(chr2 == '-' && chr3 == '-')
+ {
+ chr2= 0;
+ chr3= 0;
+ chr= my_tospace(GET);
+
+ while(chr != '>' || chr2 != '-' || chr3 != '-')
+ {
+ if(chr == '-')
+ {
+ chr3= chr2;
+ chr2= chr;
+ }
+ else if (chr2 == '-')
+ {
+ chr2= 0;
+ chr3= 0;
+ }
+ chr= my_tospace(GET);
+ if (chr == my_b_EOF)
+ goto found_eof;
+ }
+ break;
+ }
+ }
+
+ tag.length(0);
+ while(chr != '>' && chr != ' ' && chr != '/' && chr != my_b_EOF)
+ {
+ if(chr != delim) /* fix for the '<field name =' format */
+ tag.append(chr);
+ chr= my_tospace(GET);
+ }
+
+ // row tag should be in ROWS IDENTIFIED BY '<row>' - stored in line_term
+ if((tag.length() == line_term_length -2) &&
+ (memcmp(tag.ptr(), line_term_ptr + 1, tag.length()) == 0))
+ {
+ DBUG_PRINT("read_xml", ("start-of-row: %i %s %s",
+ level,tag.c_ptr_safe(), line_term_ptr));
+ }
+
+ if(chr == ' ' || chr == '>')
+ {
+ level++;
+ clear_level(level + 1);
+ }
+
+ if (chr == ' ')
+ in_tag= true;
+ else
+ in_tag= false;
+ break;
+
+ case ' ': /* read attribute */
+ while(chr == ' ') /* skip blanks */
+ chr= my_tospace(GET);
+
+ if(!in_tag)
+ break;
+
+ while(chr != '=' && chr != '/' && chr != '>' && chr != my_b_EOF)
+ {
+ attribute.append(chr);
+ chr= my_tospace(GET);
+ }
+ break;
+
+ case '>': /* end tag - read tag value */
+ in_tag= false;
+ chr= read_value('<', &value);
+ if(chr == my_b_EOF)
+ goto found_eof;
+
+ /* save value to list */
+ if(tag.length() > 0 && value.length() > 0)
+ {
+ DBUG_PRINT("read_xml", ("lev:%i tag:%s val:%s",
+ level,tag.c_ptr_safe(), value.c_ptr_safe()));
+ taglist.push_front( new XML_TAG(level, tag, value));
+ }
+ tag.length(0);
+ value.length(0);
+ attribute.length(0);
+ break;
+
+ case '/': /* close tag */
+ level--;
+ chr= my_tospace(GET);
+ if(chr != '>') /* if this is an empty tag <tag /> */
+ tag.length(0); /* we should keep tag value */
+ while(chr != '>' && chr != my_b_EOF)
+ {
+ tag.append(chr);
+ chr= my_tospace(GET);
+ }
+
+ if((tag.length() == line_term_length -2) &&
+ (memcmp(tag.ptr(), line_term_ptr + 1, tag.length()) == 0))
+ {
+ DBUG_PRINT("read_xml", ("found end-of-row %i %s",
+ level, tag.c_ptr_safe()));
+ DBUG_RETURN(0); //normal return
+ }
+ chr= my_tospace(GET);
+ break;
+
+ 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")))
+ {
+ /*
+ this is format <field name="xx">xx</field>
+ where actual fieldname is in attribute
+ */
+ delim= my_tospace(GET);
+ tag.length(0);
+ attribute.length(0);
+ chr= '<'; /* we pretend that it is a tag */
+ level--;
+ break;
+ }
+
+ //check for " or '
+ chr= GET;
+ if (chr == my_b_EOF)
+ goto found_eof;
+ if(chr == '"' || chr == '\'')
+ {
+ delim= chr;
+ }
+ else
+ {
+ delim= ' '; /* no delimiter, use space */
+ PUSH(chr);
+ }
+
+ chr= read_value(delim, &value);
+ if(attribute.length() > 0 && value.length() > 0)
+ {
+ DBUG_PRINT("read_xml", ("lev:%i att:%s val:%s\n",
+ level + 1,
+ attribute.c_ptr_safe(),
+ value.c_ptr_safe()));
+ taglist.push_front(new XML_TAG(level + 1, attribute, value));
+ }
+ attribute.length(0);
+ value.length(0);
+ if (chr != ' ')
+ chr= my_tospace(GET);
+ break;
+
+ default:
+ chr= my_tospace(GET);
+ } /* end switch */
+ } /* end while */
+
+found_eof:
+ DBUG_PRINT("read_xml",("Found eof"));
+ eof= 1;
+ DBUG_RETURN(1);
+}
diff --git a/sql/sql_load.h b/sql/sql_load.h
new file mode 100644
index 00000000000..f767e39387b
--- /dev/null
+++ b/sql/sql_load.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_LOAD_INCLUDED
+#define SQL_LOAD_INCLUDED
+
+#include "sql_list.h" /* List */
+
+class Item;
+
+#include "sql_class.h" /* enum_duplicates */
+
+class sql_exchange;
+
+int mysql_load(THD *thd, sql_exchange *ex, TABLE_LIST *table_list,
+ List<Item> &fields_vars, List<Item> &set_fields,
+ List<Item> &set_values_list,
+ enum enum_duplicates handle_duplicates, bool ignore,
+ bool local_file);
+
+
+#endif /* SQL_LOAD_INCLUDED */
diff --git a/sql/sql_locale.cc b/sql/sql_locale.cc
index a90a47ce0c1..13e00c99f19 100644
--- a/sql/sql_locale.cc
+++ b/sql/sql_locale.cc
@@ -1,6 +1,4 @@
-/*
- Copyright (c) 2006 MySQL AB, 2008, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2005, 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
@@ -13,8 +11,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
The beginnings of locale(7) support.
@@ -23,7 +20,48 @@
!! This file is built from my_locale.pl !!
*/
-#include "mysql_priv.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
+{
+ en_US= 0, cs_CZ, da_DK, nl_NL, et_EE, fr_FR, de_DE, el_GR, hu_HU, it_IT,
+ ja_JP, ko_KR, no_NO, nn_NO, pl_PL, pt_PT, ro_RO, ru_RU, sr_RS, sk_SK,
+ es_ES, sv_SE, uk_UA
+} ERR_MSGS_INDEX;
+
+
+MY_LOCALE_ERRMSGS global_errmsgs[]=
+{
+ {"english", NULL},
+ {"czech", NULL},
+ {"danish", NULL},
+ {"dutch", NULL},
+ {"estonian", NULL},
+ {"french", NULL},
+ {"german", NULL},
+ {"greek", NULL},
+ {"hungarian", NULL},
+ {"italian", NULL},
+ {"japanese", NULL},
+ {"korean", NULL},
+ {"norwegian", NULL},
+ {"norwegian-ny", NULL},
+ {"polish", NULL},
+ {"portuguese", NULL},
+ {"romanian", NULL},
+ {"russian", NULL},
+ {"serbian", NULL},
+ {"slovak", NULL},
+ {"spanish", NULL},
+ {"swedish", NULL},
+ {"ukrainian", NULL},
+ {NULL, NULL}
+};
/***** LOCALE BEGIN ar_AE: Arabic - United Arab Emirates *****/
@@ -54,7 +92,11 @@ MY_LOCALE my_locale_ar_AE
&my_locale_typelib_day_names_ar_AE,
&my_locale_typelib_ab_day_names_ar_AE,
6,
- 8
+ 8,
+ '.', /* decimal point ar_AE */
+ ',', /* thousands_sep ar_AE */
+ "\x03", /* grouping ar_AE */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ar_AE *****/
@@ -86,7 +128,11 @@ MY_LOCALE my_locale_ar_BH
&my_locale_typelib_day_names_ar_BH,
&my_locale_typelib_ab_day_names_ar_BH,
6,
- 8
+ 8,
+ '.', /* decimal point ar_BH */
+ ',', /* thousands_sep ar_BH */
+ "\x03", /* grouping ar_BH */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ar_BH *****/
@@ -118,7 +164,11 @@ MY_LOCALE my_locale_ar_JO
&my_locale_typelib_day_names_ar_JO,
&my_locale_typelib_ab_day_names_ar_JO,
12,
- 8
+ 8,
+ '.', /* decimal point ar_JO */
+ ',', /* thousands_sep ar_JO */
+ "\x03", /* grouping ar_JO */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ar_JO *****/
@@ -150,7 +200,11 @@ MY_LOCALE my_locale_ar_SA
&my_locale_typelib_day_names_ar_SA,
&my_locale_typelib_ab_day_names_ar_SA,
12,
- 8
+ 8,
+ '.', /* decimal point ar_SA */
+ '\0', /* thousands_sep ar_SA */
+ "\x80", /* grouping ar_SA */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ar_SA *****/
@@ -182,7 +236,11 @@ MY_LOCALE my_locale_ar_SY
&my_locale_typelib_day_names_ar_SY,
&my_locale_typelib_ab_day_names_ar_SY,
12,
- 8
+ 8,
+ '.', /* decimal point ar_SY */
+ ',', /* thousands_sep ar_SY */
+ "\x03", /* grouping ar_SY */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ar_SY *****/
@@ -214,7 +272,11 @@ MY_LOCALE my_locale_be_BY
&my_locale_typelib_day_names_be_BY,
&my_locale_typelib_ab_day_names_be_BY,
10,
- 10
+ 10,
+ ',', /* decimal point be_BY */
+ '.', /* thousands_sep be_BY */
+ "\x03\x03", /* grouping be_BY */
+ &global_errmsgs[en_US]
);
/***** LOCALE END be_BY *****/
@@ -246,7 +308,11 @@ MY_LOCALE my_locale_bg_BG
&my_locale_typelib_day_names_bg_BG,
&my_locale_typelib_ab_day_names_bg_BG,
9,
- 10
+ 10,
+ ',', /* decimal point bg_BG */
+ '\0', /* thousands_sep bg_BG */
+ "\x03\x03", /* grouping bg_BG */
+ &global_errmsgs[en_US]
);
/***** LOCALE END bg_BG *****/
@@ -278,7 +344,11 @@ MY_LOCALE my_locale_ca_ES
&my_locale_typelib_day_names_ca_ES,
&my_locale_typelib_ab_day_names_ca_ES,
8,
- 9
+ 9,
+ ',', /* decimal point ca_ES */
+ '\0', /* thousands_sep ca_ES */
+ "\x80\x80", /* grouping ca_ES */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ca_ES *****/
@@ -310,7 +380,11 @@ MY_LOCALE my_locale_cs_CZ
&my_locale_typelib_day_names_cs_CZ,
&my_locale_typelib_ab_day_names_cs_CZ,
8,
- 7
+ 7,
+ ',', /* decimal point cs_CZ */
+ ' ', /* thousands_sep cs_CZ */
+ "\x03\x03", /* grouping cs_CZ */
+ &global_errmsgs[cs_CZ]
);
/***** LOCALE END cs_CZ *****/
@@ -342,7 +416,11 @@ MY_LOCALE my_locale_da_DK
&my_locale_typelib_day_names_da_DK,
&my_locale_typelib_ab_day_names_da_DK,
9,
- 7
+ 7,
+ ',', /* decimal point da_DK */
+ '.', /* thousands_sep da_DK */
+ "\x03\x03", /* grouping da_DK */
+ &global_errmsgs[da_DK]
);
/***** LOCALE END da_DK *****/
@@ -374,7 +452,11 @@ MY_LOCALE my_locale_de_AT
&my_locale_typelib_day_names_de_AT,
&my_locale_typelib_ab_day_names_de_AT,
9,
- 10
+ 10,
+ ',', /* decimal point de_AT */
+ '\0', /* thousands_sep de_AT */
+ "\x80\x80", /* grouping de_AT */
+ &global_errmsgs[de_DE]
);
/***** LOCALE END de_AT *****/
@@ -406,7 +488,11 @@ MY_LOCALE my_locale_de_DE
&my_locale_typelib_day_names_de_DE,
&my_locale_typelib_ab_day_names_de_DE,
9,
- 10
+ 10,
+ ',', /* decimal point de_DE */
+ '.', /* thousands_sep de_DE */
+ "\x03\x03", /* grouping de_DE */
+ &global_errmsgs[de_DE]
);
/***** LOCALE END de_DE *****/
@@ -438,7 +524,11 @@ MY_LOCALE my_locale_en_US
&my_locale_typelib_day_names_en_US,
&my_locale_typelib_ab_day_names_en_US,
9,
- 9
+ 9,
+ '.', /* decimal point en_US */
+ ',', /* thousands_sep en_US */
+ "\x03\x03", /* grouping en_US */
+ &global_errmsgs[en_US]
);
/***** LOCALE END en_US *****/
@@ -470,7 +560,11 @@ MY_LOCALE my_locale_es_ES
&my_locale_typelib_day_names_es_ES,
&my_locale_typelib_ab_day_names_es_ES,
10,
- 9
+ 9,
+ ',', /* decimal point es_ES */
+ '\0', /* thousands_sep es_ES */
+ "\x80\x80", /* grouping es_ES */
+ &global_errmsgs[es_ES]
);
/***** LOCALE END es_ES *****/
@@ -502,7 +596,11 @@ MY_LOCALE my_locale_et_EE
&my_locale_typelib_day_names_et_EE,
&my_locale_typelib_ab_day_names_et_EE,
9,
- 9
+ 9,
+ ',', /* decimal point et_EE */
+ ' ', /* thousands_sep et_EE */
+ "\x03\x03", /* grouping et_EE */
+ &global_errmsgs[et_EE]
);
/***** LOCALE END et_EE *****/
@@ -534,7 +632,11 @@ MY_LOCALE my_locale_eu_ES
&my_locale_typelib_day_names_eu_ES,
&my_locale_typelib_ab_day_names_eu_ES,
9,
- 10
+ 10,
+ ',', /* decimal point eu_ES */
+ '\0', /* thousands_sep eu_ES */
+ "\x80\x80", /* grouping eu_ES */
+ &global_errmsgs[en_US]
);
/***** LOCALE END eu_ES *****/
@@ -566,7 +668,11 @@ MY_LOCALE my_locale_fi_FI
&my_locale_typelib_day_names_fi_FI,
&my_locale_typelib_ab_day_names_fi_FI,
9,
- 11
+ 11,
+ ',', /* decimal point fi_FI */
+ ' ', /* thousands_sep fi_FI */
+ "\x03\x03", /* grouping fi_FI */
+ &global_errmsgs[en_US]
);
/***** LOCALE END fi_FI *****/
@@ -598,7 +704,11 @@ MY_LOCALE my_locale_fo_FO
&my_locale_typelib_day_names_fo_FO,
&my_locale_typelib_ab_day_names_fo_FO,
9,
- 12
+ 12,
+ ',', /* decimal point fo_FO */
+ '.', /* thousands_sep fo_FO */
+ "\x03\x03", /* grouping fo_FO */
+ &global_errmsgs[en_US]
);
/***** LOCALE END fo_FO *****/
@@ -630,7 +740,11 @@ MY_LOCALE my_locale_fr_FR
&my_locale_typelib_day_names_fr_FR,
&my_locale_typelib_ab_day_names_fr_FR,
9,
- 8
+ 8,
+ ',', /* decimal point fr_FR */
+ '\0', /* thousands_sep fr_FR */
+ "\x80\x80", /* grouping fr_FR */
+ &global_errmsgs[fr_FR]
);
/***** LOCALE END fr_FR *****/
@@ -662,7 +776,11 @@ MY_LOCALE my_locale_gl_ES
&my_locale_typelib_day_names_gl_ES,
&my_locale_typelib_ab_day_names_gl_ES,
8,
- 8
+ 8,
+ ',', /* decimal point gl_ES */
+ '\0', /* thousands_sep gl_ES */
+ "\x80\x80", /* grouping gl_ES */
+ &global_errmsgs[en_US]
);
/***** LOCALE END gl_ES *****/
@@ -694,7 +812,11 @@ MY_LOCALE my_locale_gu_IN
&my_locale_typelib_day_names_gu_IN,
&my_locale_typelib_ab_day_names_gu_IN,
10,
- 8
+ 8,
+ '.', /* decimal point gu_IN */
+ ',', /* thousands_sep gu_IN */
+ "\x03", /* grouping gu_IN */
+ &global_errmsgs[en_US]
);
/***** LOCALE END gu_IN *****/
@@ -726,7 +848,11 @@ MY_LOCALE my_locale_he_IL
&my_locale_typelib_day_names_he_IL,
&my_locale_typelib_ab_day_names_he_IL,
7,
- 5
+ 5,
+ '.', /* decimal point he_IL */
+ ',', /* thousands_sep he_IL */
+ "\x03\x03", /* grouping he_IL */
+ &global_errmsgs[en_US]
);
/***** LOCALE END he_IL *****/
@@ -758,7 +884,11 @@ MY_LOCALE my_locale_hi_IN
&my_locale_typelib_day_names_hi_IN,
&my_locale_typelib_ab_day_names_hi_IN,
7,
- 9
+ 9,
+ '.', /* decimal point hi_IN */
+ ',', /* thousands_sep hi_IN */
+ "\x03", /* grouping hi_IN */
+ &global_errmsgs[en_US]
);
/***** LOCALE END hi_IN *****/
@@ -790,7 +920,11 @@ MY_LOCALE my_locale_hr_HR
&my_locale_typelib_day_names_hr_HR,
&my_locale_typelib_ab_day_names_hr_HR,
8,
- 11
+ 11,
+ ',', /* decimal point hr_HR */
+ '\0', /* thousands_sep hr_HR */
+ "\x80\x80", /* grouping hr_HR */
+ &global_errmsgs[en_US]
);
/***** LOCALE END hr_HR *****/
@@ -822,7 +956,11 @@ MY_LOCALE my_locale_hu_HU
&my_locale_typelib_day_names_hu_HU,
&my_locale_typelib_ab_day_names_hu_HU,
10,
- 9
+ 9,
+ ',', /* decimal point hu_HU */
+ '.', /* thousands_sep hu_HU */
+ "\x03\x03", /* grouping hu_HU */
+ &global_errmsgs[hu_HU]
);
/***** LOCALE END hu_HU *****/
@@ -854,7 +992,11 @@ MY_LOCALE my_locale_id_ID
&my_locale_typelib_day_names_id_ID,
&my_locale_typelib_ab_day_names_id_ID,
9,
- 6
+ 6,
+ ',', /* decimal point id_ID */
+ '.', /* thousands_sep id_ID */
+ "\x03\x03", /* grouping id_ID */
+ &global_errmsgs[en_US]
);
/***** LOCALE END id_ID *****/
@@ -886,7 +1028,11 @@ MY_LOCALE my_locale_is_IS
&my_locale_typelib_day_names_is_IS,
&my_locale_typelib_ab_day_names_is_IS,
9,
- 12
+ 12,
+ ',', /* decimal point is_IS */
+ '.', /* thousands_sep is_IS */
+ "\x03\x03", /* grouping is_IS */
+ &global_errmsgs[en_US]
);
/***** LOCALE END is_IS *****/
@@ -918,7 +1064,11 @@ MY_LOCALE my_locale_it_CH
&my_locale_typelib_day_names_it_CH,
&my_locale_typelib_ab_day_names_it_CH,
9,
- 9
+ 9,
+ ',', /* decimal point it_CH */
+ '\'', /* thousands_sep it_CH */
+ "\x03\x03", /* grouping it_CH */
+ &global_errmsgs[it_IT]
);
/***** LOCALE END it_CH *****/
@@ -950,7 +1100,11 @@ MY_LOCALE my_locale_ja_JP
&my_locale_typelib_day_names_ja_JP,
&my_locale_typelib_ab_day_names_ja_JP,
3,
- 3
+ 3,
+ '.', /* decimal point ja_JP */
+ ',', /* thousands_sep ja_JP */
+ "\x03", /* grouping ja_JP */
+ &global_errmsgs[ja_JP]
);
/***** LOCALE END ja_JP *****/
@@ -982,7 +1136,11 @@ MY_LOCALE my_locale_ko_KR
&my_locale_typelib_day_names_ko_KR,
&my_locale_typelib_ab_day_names_ko_KR,
3,
- 3
+ 3,
+ '.', /* decimal point ko_KR */
+ ',', /* thousands_sep ko_KR */
+ "\x03\x03", /* grouping ko_KR */
+ &global_errmsgs[ko_KR]
);
/***** LOCALE END ko_KR *****/
@@ -1014,7 +1172,11 @@ MY_LOCALE my_locale_lt_LT
&my_locale_typelib_day_names_lt_LT,
&my_locale_typelib_ab_day_names_lt_LT,
9,
- 14
+ 14,
+ ',', /* decimal point lt_LT */
+ '.', /* thousands_sep lt_LT */
+ "\x03\x03", /* grouping lt_LT */
+ &global_errmsgs[en_US]
);
/***** LOCALE END lt_LT *****/
@@ -1046,7 +1208,11 @@ MY_LOCALE my_locale_lv_LV
&my_locale_typelib_day_names_lv_LV,
&my_locale_typelib_ab_day_names_lv_LV,
10,
- 11
+ 11,
+ ',', /* decimal point lv_LV */
+ ' ', /* thousands_sep lv_LV */
+ "\x03\x03", /* grouping lv_LV */
+ &global_errmsgs[en_US]
);
/***** LOCALE END lv_LV *****/
@@ -1078,7 +1244,11 @@ MY_LOCALE my_locale_mk_MK
&my_locale_typelib_day_names_mk_MK,
&my_locale_typelib_ab_day_names_mk_MK,
9,
- 10
+ 10,
+ ',', /* decimal point mk_MK */
+ ' ', /* thousands_sep mk_MK */
+ "\x03\x03", /* grouping mk_MK */
+ &global_errmsgs[en_US]
);
/***** LOCALE END mk_MK *****/
@@ -1110,7 +1280,11 @@ MY_LOCALE my_locale_mn_MN
&my_locale_typelib_day_names_mn_MN,
&my_locale_typelib_ab_day_names_mn_MN,
18,
- 6
+ 6,
+ ',', /* decimal point mn_MN */
+ '.', /* thousands_sep mn_MN */
+ "\x03\x03", /* grouping mn_MN */
+ &global_errmsgs[en_US]
);
/***** LOCALE END mn_MN *****/
@@ -1142,7 +1316,11 @@ MY_LOCALE my_locale_ms_MY
&my_locale_typelib_day_names_ms_MY,
&my_locale_typelib_ab_day_names_ms_MY,
9,
- 6
+ 6,
+ '.', /* decimal point ms_MY */
+ ',', /* thousands_sep ms_MY */
+ "\x03", /* grouping ms_MY */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ms_MY *****/
@@ -1174,7 +1352,11 @@ MY_LOCALE my_locale_nb_NO
&my_locale_typelib_day_names_nb_NO,
&my_locale_typelib_ab_day_names_nb_NO,
9,
- 7
+ 7,
+ ',', /* decimal point nb_NO */
+ '.', /* thousands_sep nb_NO */
+ "\x03\x03", /* grouping nb_NO */
+ &global_errmsgs[no_NO]
);
/***** LOCALE END nb_NO *****/
@@ -1206,7 +1388,11 @@ MY_LOCALE my_locale_nl_NL
&my_locale_typelib_day_names_nl_NL,
&my_locale_typelib_ab_day_names_nl_NL,
9,
- 9
+ 9,
+ ',', /* decimal point nl_NL */
+ '\0', /* thousands_sep nl_NL */
+ "\x80\x80", /* grouping nl_NL */
+ &global_errmsgs[nl_NL]
);
/***** LOCALE END nl_NL *****/
@@ -1238,7 +1424,11 @@ MY_LOCALE my_locale_pl_PL
&my_locale_typelib_day_names_pl_PL,
&my_locale_typelib_ab_day_names_pl_PL,
11,
- 12
+ 12,
+ ',', /* decimal point pl_PL */
+ '\0', /* thousands_sep pl_PL */
+ "\x80\x80", /* grouping pl_PL */
+ &global_errmsgs[pl_PL]
);
/***** LOCALE END pl_PL *****/
@@ -1270,7 +1460,11 @@ MY_LOCALE my_locale_pt_BR
&my_locale_typelib_day_names_pt_BR,
&my_locale_typelib_ab_day_names_pt_BR,
9,
- 7
+ 7,
+ ',', /* decimal point pt_BR */
+ '\0', /* thousands_sep pt_BR */
+ "\x80\x80", /* grouping pt_BR */
+ &global_errmsgs[pt_PT]
);
/***** LOCALE END pt_BR *****/
@@ -1302,7 +1496,11 @@ MY_LOCALE my_locale_pt_PT
&my_locale_typelib_day_names_pt_PT,
&my_locale_typelib_ab_day_names_pt_PT,
9,
- 7
+ 7,
+ ',', /* decimal point pt_PT */
+ '\0', /* thousands_sep pt_PT */
+ "\x80\x80", /* grouping pt_PT */
+ &global_errmsgs[pt_PT]
);
/***** LOCALE END pt_PT *****/
@@ -1334,7 +1532,11 @@ MY_LOCALE my_locale_ro_RO
&my_locale_typelib_day_names_ro_RO,
&my_locale_typelib_ab_day_names_ro_RO,
10,
- 8
+ 8,
+ ',', /* decimal point ro_RO */
+ '.', /* thousands_sep ro_RO */
+ "\x03\x03", /* grouping ro_RO */
+ &global_errmsgs[ro_RO]
);
/***** LOCALE END ro_RO *****/
@@ -1366,7 +1568,11 @@ MY_LOCALE my_locale_ru_RU
&my_locale_typelib_day_names_ru_RU,
&my_locale_typelib_ab_day_names_ru_RU,
8,
- 11
+ 11,
+ ',', /* decimal point ru_RU */
+ ' ', /* thousands_sep ru_RU */
+ "\x03\x03", /* grouping ru_RU */
+ &global_errmsgs[ru_RU]
);
/***** LOCALE END ru_RU *****/
@@ -1398,7 +1604,11 @@ MY_LOCALE my_locale_ru_UA
&my_locale_typelib_day_names_ru_UA,
&my_locale_typelib_ab_day_names_ru_UA,
8,
- 11
+ 11,
+ ',', /* decimal point ru_UA */
+ '.', /* thousands_sep ru_UA */
+ "\x03\x03", /* grouping ru_UA */
+ &global_errmsgs[ru_RU]
);
/***** LOCALE END ru_UA *****/
@@ -1430,7 +1640,11 @@ MY_LOCALE my_locale_sk_SK
&my_locale_typelib_day_names_sk_SK,
&my_locale_typelib_ab_day_names_sk_SK,
9,
- 8
+ 8,
+ ',', /* decimal point sk_SK */
+ ' ', /* thousands_sep sk_SK */
+ "\x03\x03", /* grouping sk_SK */
+ &global_errmsgs[sk_SK]
);
/***** LOCALE END sk_SK *****/
@@ -1462,7 +1676,11 @@ MY_LOCALE my_locale_sl_SI
&my_locale_typelib_day_names_sl_SI,
&my_locale_typelib_ab_day_names_sl_SI,
9,
- 10
+ 10,
+ ',', /* decimal point sl_SI */
+ ' ', /* thousands_sep sl_SI */
+ "\x80\x80", /* grouping sl_SI */
+ &global_errmsgs[en_US]
);
/***** LOCALE END sl_SI *****/
@@ -1494,41 +1712,67 @@ MY_LOCALE my_locale_sq_AL
&my_locale_typelib_day_names_sq_AL,
&my_locale_typelib_ab_day_names_sq_AL,
7,
- 10
+ 10,
+ ',', /* decimal point sq_AL */
+ '.', /* thousands_sep sq_AL */
+ "\x03", /* grouping sq_AL */
+ &global_errmsgs[en_US]
);
/***** LOCALE END sq_AL *****/
-/***** LOCALE BEGIN sr_YU: Servian - Yugoslavia *****/
-static const char *my_locale_month_names_sr_YU[13] =
+/***** LOCALE BEGIN sr_RS: Serbian - Serbia *****/
+static const char *my_locale_month_names_sr_RS[13] =
{"januar","februar","mart","april","maj","juni","juli","avgust","septembar","oktobar","novembar","decembar", NullS };
-static const char *my_locale_ab_month_names_sr_YU[13] =
+static const char *my_locale_ab_month_names_sr_RS[13] =
{"jan","feb","mar","apr","maj","jun","jul","avg","sep","okt","nov","dec", NullS };
-static const char *my_locale_day_names_sr_YU[8] =
+static const char *my_locale_day_names_sr_RS[8] =
{"ponedeljak","utorak","sreda","Äetvrtak","petak","subota","nedelja", NullS };
-static const char *my_locale_ab_day_names_sr_YU[8] =
+static const char *my_locale_ab_day_names_sr_RS[8] =
{"pon","uto","sre","Äet","pet","sub","ned", NullS };
-static TYPELIB my_locale_typelib_month_names_sr_YU =
- { array_elements(my_locale_month_names_sr_YU)-1, "", my_locale_month_names_sr_YU, NULL };
-static TYPELIB my_locale_typelib_ab_month_names_sr_YU =
- { array_elements(my_locale_ab_month_names_sr_YU)-1, "", my_locale_ab_month_names_sr_YU, NULL };
-static TYPELIB my_locale_typelib_day_names_sr_YU =
- { array_elements(my_locale_day_names_sr_YU)-1, "", my_locale_day_names_sr_YU, NULL };
-static TYPELIB my_locale_typelib_ab_day_names_sr_YU =
- { array_elements(my_locale_ab_day_names_sr_YU)-1, "", my_locale_ab_day_names_sr_YU, NULL };
-MY_LOCALE my_locale_sr_YU
+static TYPELIB my_locale_typelib_month_names_sr_RS =
+ { array_elements(my_locale_month_names_sr_RS)-1, "", my_locale_month_names_sr_RS, NULL };
+static TYPELIB my_locale_typelib_ab_month_names_sr_RS =
+ { array_elements(my_locale_ab_month_names_sr_RS)-1, "", my_locale_ab_month_names_sr_RS, NULL };
+static TYPELIB my_locale_typelib_day_names_sr_RS =
+ { array_elements(my_locale_day_names_sr_RS)-1, "", my_locale_day_names_sr_RS, NULL };
+static TYPELIB my_locale_typelib_ab_day_names_sr_RS =
+ { array_elements(my_locale_ab_day_names_sr_RS)-1, "", my_locale_ab_day_names_sr_RS, NULL };
+MY_LOCALE my_locale_sr_YU /* Deprecated, use sr_RS instead */
(
48,
"sr_YU",
- "Servian - Yugoslavia",
+ "Serbian - Yugoslavia",
FALSE,
- &my_locale_typelib_month_names_sr_YU,
- &my_locale_typelib_ab_month_names_sr_YU,
- &my_locale_typelib_day_names_sr_YU,
- &my_locale_typelib_ab_day_names_sr_YU,
+ &my_locale_typelib_month_names_sr_RS,
+ &my_locale_typelib_ab_month_names_sr_RS,
+ &my_locale_typelib_day_names_sr_RS,
+ &my_locale_typelib_ab_day_names_sr_RS,
9,
- 10
+ 10,
+ '.', /* decimal point sr_RS */
+ '\0', /* thousands_sep sr_RS */
+ "\x80", /* grouping sr_RS */
+ &global_errmsgs[sr_RS]
);
-/***** LOCALE END sr_YU *****/
+
+MY_LOCALE my_locale_sr_RS
+(
+ 48,
+ "sr_RS",
+ "Serbian - Serbia",
+ FALSE,
+ &my_locale_typelib_month_names_sr_RS,
+ &my_locale_typelib_ab_month_names_sr_RS,
+ &my_locale_typelib_day_names_sr_RS,
+ &my_locale_typelib_ab_day_names_sr_RS,
+ 9,
+ 10,
+ '.', /* decimal point sr_RS */
+ '\0', /* thousands_sep sr_RS */
+ "\x80", /* grouping sr_RS */
+ &global_errmsgs[sr_RS]
+);
+/***** LOCALE END sr_RS *****/
/***** LOCALE BEGIN sv_SE: Swedish - Sweden *****/
static const char *my_locale_month_names_sv_SE[13] =
@@ -1558,7 +1802,11 @@ MY_LOCALE my_locale_sv_SE
&my_locale_typelib_day_names_sv_SE,
&my_locale_typelib_ab_day_names_sv_SE,
9,
- 7
+ 7,
+ ',', /* decimal point sv_SE */
+ ' ', /* thousands_sep sv_SE */
+ "\x03\x03", /* grouping sv_SE */
+ &global_errmsgs[sv_SE]
);
/***** LOCALE END sv_SE *****/
@@ -1590,7 +1838,11 @@ MY_LOCALE my_locale_ta_IN
&my_locale_typelib_day_names_ta_IN,
&my_locale_typelib_ab_day_names_ta_IN,
10,
- 8
+ 8,
+ '.', /* decimal point ta_IN */
+ ',', /* thousands_sep ta_IN */
+ "\x03\x02", /* grouping ta_IN */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ta_IN *****/
@@ -1622,7 +1874,11 @@ MY_LOCALE my_locale_te_IN
&my_locale_typelib_day_names_te_IN,
&my_locale_typelib_ab_day_names_te_IN,
10,
- 9
+ 9,
+ '.', /* decimal point te_IN */
+ ',', /* thousands_sep te_IN */
+ "\x03\x02", /* grouping te_IN */
+ &global_errmsgs[en_US]
);
/***** LOCALE END te_IN *****/
@@ -1654,7 +1910,11 @@ MY_LOCALE my_locale_th_TH
&my_locale_typelib_day_names_th_TH,
&my_locale_typelib_ab_day_names_th_TH,
10,
- 8
+ 8,
+ '.', /* decimal point th_TH */
+ ',', /* thousands_sep th_TH */
+ "\x03", /* grouping th_TH */
+ &global_errmsgs[en_US]
);
/***** LOCALE END th_TH *****/
@@ -1686,7 +1946,11 @@ MY_LOCALE my_locale_tr_TR
&my_locale_typelib_day_names_tr_TR,
&my_locale_typelib_ab_day_names_tr_TR,
7,
- 9
+ 9,
+ ',', /* decimal point tr_TR */
+ '.', /* thousands_sep tr_TR */
+ "\x03\x03", /* grouping tr_TR */
+ &global_errmsgs[en_US]
);
/***** LOCALE END tr_TR *****/
@@ -1718,7 +1982,11 @@ MY_LOCALE my_locale_uk_UA
&my_locale_typelib_day_names_uk_UA,
&my_locale_typelib_ab_day_names_uk_UA,
8,
- 9
+ 9,
+ ',', /* decimal point uk_UA */
+ '.', /* thousands_sep uk_UA */
+ "\x03\x03", /* grouping uk_UA */
+ &global_errmsgs[uk_UA]
);
/***** LOCALE END uk_UA *****/
@@ -1750,7 +2018,11 @@ MY_LOCALE my_locale_ur_PK
&my_locale_typelib_day_names_ur_PK,
&my_locale_typelib_ab_day_names_ur_PK,
6,
- 6
+ 6,
+ '.', /* decimal point ur_PK */
+ ',', /* thousands_sep ur_PK */
+ "\x03\x03", /* grouping ur_PK */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ur_PK *****/
@@ -1782,7 +2054,11 @@ MY_LOCALE my_locale_vi_VN
&my_locale_typelib_day_names_vi_VN,
&my_locale_typelib_ab_day_names_vi_VN,
16,
- 11
+ 11,
+ ',', /* decimal point vi_VN */
+ '.', /* thousands_sep vi_VN */
+ "\x03\x03", /* grouping vi_VN */
+ &global_errmsgs[en_US]
);
/***** LOCALE END vi_VN *****/
@@ -1814,7 +2090,11 @@ MY_LOCALE my_locale_zh_CN
&my_locale_typelib_day_names_zh_CN,
&my_locale_typelib_ab_day_names_zh_CN,
3,
- 3
+ 3,
+ '.', /* decimal point zh_CN */
+ ',', /* thousands_sep zh_CN */
+ "\x03", /* grouping zh_CN */
+ &global_errmsgs[en_US]
);
/***** LOCALE END zh_CN *****/
@@ -1846,7 +2126,11 @@ MY_LOCALE my_locale_zh_TW
&my_locale_typelib_day_names_zh_TW,
&my_locale_typelib_ab_day_names_zh_TW,
3,
- 2
+ 2,
+ '.', /* decimal point zh_TW */
+ ',', /* thousands_sep zh_TW */
+ "\x03", /* grouping zh_TW */
+ &global_errmsgs[en_US]
);
/***** LOCALE END zh_TW *****/
@@ -1862,7 +2146,11 @@ MY_LOCALE my_locale_ar_DZ
&my_locale_typelib_day_names_ar_BH,
&my_locale_typelib_ab_day_names_ar_BH,
6,
- 8
+ 8,
+ '.', /* decimal point ar_DZ */
+ ',', /* thousands_sep ar_DZ */
+ "\x03", /* grouping ar_DZ */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ar_DZ *****/
@@ -1878,7 +2166,11 @@ MY_LOCALE my_locale_ar_EG
&my_locale_typelib_day_names_ar_BH,
&my_locale_typelib_ab_day_names_ar_BH,
6,
- 8
+ 8,
+ '.', /* decimal point ar_EG */
+ ',', /* thousands_sep ar_EG */
+ "\x03", /* grouping ar_EG */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ar_EG *****/
@@ -1894,7 +2186,11 @@ MY_LOCALE my_locale_ar_IN
&my_locale_typelib_day_names_ar_BH,
&my_locale_typelib_ab_day_names_ar_BH,
6,
- 8
+ 8,
+ '.', /* decimal point ar_IN */
+ ',', /* thousands_sep ar_IN */
+ "\x03", /* grouping ar_IN */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ar_IN *****/
@@ -1910,7 +2206,11 @@ MY_LOCALE my_locale_ar_IQ
&my_locale_typelib_day_names_ar_BH,
&my_locale_typelib_ab_day_names_ar_BH,
6,
- 8
+ 8,
+ '.', /* decimal point ar_IQ */
+ ',', /* thousands_sep ar_IQ */
+ "\x03", /* grouping ar_IQ */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ar_IQ *****/
@@ -1926,7 +2226,11 @@ MY_LOCALE my_locale_ar_KW
&my_locale_typelib_day_names_ar_BH,
&my_locale_typelib_ab_day_names_ar_BH,
6,
- 8
+ 8,
+ '.', /* decimal point ar_KW */
+ ',', /* thousands_sep ar_KW */
+ "\x03", /* grouping ar_KW */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ar_KW *****/
@@ -1942,7 +2246,11 @@ MY_LOCALE my_locale_ar_LB
&my_locale_typelib_day_names_ar_JO,
&my_locale_typelib_ab_day_names_ar_JO,
12,
- 8
+ 8,
+ '.', /* decimal point ar_LB */
+ ',', /* thousands_sep ar_LB */
+ "\x03", /* grouping ar_LB */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ar_LB *****/
@@ -1958,7 +2266,11 @@ MY_LOCALE my_locale_ar_LY
&my_locale_typelib_day_names_ar_BH,
&my_locale_typelib_ab_day_names_ar_BH,
6,
- 8
+ 8,
+ '.', /* decimal point ar_LY */
+ ',', /* thousands_sep ar_LY */
+ "\x03", /* grouping ar_LY */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ar_LY *****/
@@ -1974,7 +2286,11 @@ MY_LOCALE my_locale_ar_MA
&my_locale_typelib_day_names_ar_BH,
&my_locale_typelib_ab_day_names_ar_BH,
6,
- 8
+ 8,
+ '.', /* decimal point ar_MA */
+ ',', /* thousands_sep ar_MA */
+ "\x03", /* grouping ar_MA */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ar_MA *****/
@@ -1990,7 +2306,11 @@ MY_LOCALE my_locale_ar_OM
&my_locale_typelib_day_names_ar_BH,
&my_locale_typelib_ab_day_names_ar_BH,
6,
- 8
+ 8,
+ '.', /* decimal point ar_OM */
+ ',', /* thousands_sep ar_OM */
+ "\x03", /* grouping ar_OM */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ar_OM *****/
@@ -2006,7 +2326,11 @@ MY_LOCALE my_locale_ar_QA
&my_locale_typelib_day_names_ar_BH,
&my_locale_typelib_ab_day_names_ar_BH,
6,
- 8
+ 8,
+ '.', /* decimal point ar_QA */
+ ',', /* thousands_sep ar_QA */
+ "\x03", /* grouping ar_QA */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ar_QA *****/
@@ -2022,7 +2346,11 @@ MY_LOCALE my_locale_ar_SD
&my_locale_typelib_day_names_ar_BH,
&my_locale_typelib_ab_day_names_ar_BH,
6,
- 8
+ 8,
+ '.', /* decimal point ar_SD */
+ ',', /* thousands_sep ar_SD */
+ "\x03", /* grouping ar_SD */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ar_SD *****/
@@ -2038,7 +2366,11 @@ MY_LOCALE my_locale_ar_TN
&my_locale_typelib_day_names_ar_BH,
&my_locale_typelib_ab_day_names_ar_BH,
6,
- 8
+ 8,
+ '.', /* decimal point ar_TN */
+ ',', /* thousands_sep ar_TN */
+ "\x03", /* grouping ar_TN */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ar_TN *****/
@@ -2054,7 +2386,11 @@ MY_LOCALE my_locale_ar_YE
&my_locale_typelib_day_names_ar_BH,
&my_locale_typelib_ab_day_names_ar_BH,
6,
- 8
+ 8,
+ '.', /* decimal point ar_YE */
+ ',', /* thousands_sep ar_YE */
+ "\x03", /* grouping ar_YE */
+ &global_errmsgs[en_US]
);
/***** LOCALE END ar_YE *****/
@@ -2070,7 +2406,11 @@ MY_LOCALE my_locale_de_BE
&my_locale_typelib_day_names_de_DE,
&my_locale_typelib_ab_day_names_de_DE,
9,
- 10
+ 10,
+ ',', /* decimal point de_BE */
+ '.', /* thousands_sep de_BE */
+ "\x03\x03", /* grouping de_BE */
+ &global_errmsgs[de_DE]
);
/***** LOCALE END de_BE *****/
@@ -2086,7 +2426,11 @@ MY_LOCALE my_locale_de_CH
&my_locale_typelib_day_names_de_DE,
&my_locale_typelib_ab_day_names_de_DE,
9,
- 10
+ 10,
+ '.', /* decimal point de_CH */
+ '\'', /* thousands_sep de_CH */
+ "\x03\x03", /* grouping de_CH */
+ &global_errmsgs[de_DE]
);
/***** LOCALE END de_CH *****/
@@ -2102,7 +2446,11 @@ MY_LOCALE my_locale_de_LU
&my_locale_typelib_day_names_de_DE,
&my_locale_typelib_ab_day_names_de_DE,
9,
- 10
+ 10,
+ ',', /* decimal point de_LU */
+ '.', /* thousands_sep de_LU */
+ "\x03\x03", /* grouping de_LU */
+ &global_errmsgs[de_DE]
);
/***** LOCALE END de_LU *****/
@@ -2118,7 +2466,11 @@ MY_LOCALE my_locale_en_AU
&my_locale_typelib_day_names_en_US,
&my_locale_typelib_ab_day_names_en_US,
9,
- 9
+ 9,
+ '.', /* decimal point en_AU */
+ ',', /* thousands_sep en_AU */
+ "\x03\x03", /* grouping en_AU */
+ &global_errmsgs[en_US]
);
/***** LOCALE END en_AU *****/
@@ -2134,7 +2486,11 @@ MY_LOCALE my_locale_en_CA
&my_locale_typelib_day_names_en_US,
&my_locale_typelib_ab_day_names_en_US,
9,
- 9
+ 9,
+ '.', /* decimal point en_CA */
+ ',', /* thousands_sep en_CA */
+ "\x03\x03", /* grouping en_CA */
+ &global_errmsgs[en_US]
);
/***** LOCALE END en_CA *****/
@@ -2150,7 +2506,11 @@ MY_LOCALE my_locale_en_GB
&my_locale_typelib_day_names_en_US,
&my_locale_typelib_ab_day_names_en_US,
9,
- 9
+ 9,
+ '.', /* decimal point en_GB */
+ ',', /* thousands_sep en_GB */
+ "\x03\x03", /* grouping en_GB */
+ &global_errmsgs[en_US]
);
/***** LOCALE END en_GB *****/
@@ -2166,7 +2526,11 @@ MY_LOCALE my_locale_en_IN
&my_locale_typelib_day_names_en_US,
&my_locale_typelib_ab_day_names_en_US,
9,
- 9
+ 9,
+ '.', /* decimal point en_IN */
+ ',', /* thousands_sep en_IN */
+ "\x03\x02", /* grouping en_IN */
+ &global_errmsgs[en_US]
);
/***** LOCALE END en_IN *****/
@@ -2182,7 +2546,11 @@ MY_LOCALE my_locale_en_NZ
&my_locale_typelib_day_names_en_US,
&my_locale_typelib_ab_day_names_en_US,
9,
- 9
+ 9,
+ '.', /* decimal point en_NZ */
+ ',', /* thousands_sep en_NZ */
+ "\x03\x03", /* grouping en_NZ */
+ &global_errmsgs[en_US]
);
/***** LOCALE END en_NZ *****/
@@ -2198,7 +2566,11 @@ MY_LOCALE my_locale_en_PH
&my_locale_typelib_day_names_en_US,
&my_locale_typelib_ab_day_names_en_US,
9,
- 9
+ 9,
+ '.', /* decimal point en_PH */
+ ',', /* thousands_sep en_PH */
+ "\x03", /* grouping en_PH */
+ &global_errmsgs[en_US]
);
/***** LOCALE END en_PH *****/
@@ -2214,7 +2586,11 @@ MY_LOCALE my_locale_en_ZA
&my_locale_typelib_day_names_en_US,
&my_locale_typelib_ab_day_names_en_US,
9,
- 9
+ 9,
+ '.', /* decimal point en_ZA */
+ ',', /* thousands_sep en_ZA */
+ "\x03\x03", /* grouping en_ZA */
+ &global_errmsgs[en_US]
);
/***** LOCALE END en_ZA *****/
@@ -2230,7 +2606,11 @@ MY_LOCALE my_locale_en_ZW
&my_locale_typelib_day_names_en_US,
&my_locale_typelib_ab_day_names_en_US,
9,
- 9
+ 9,
+ '.', /* decimal point en_ZW */
+ ',', /* thousands_sep en_ZW */
+ "\x03\x03", /* grouping en_ZW */
+ &global_errmsgs[en_US]
);
/***** LOCALE END en_ZW *****/
@@ -2246,7 +2626,11 @@ MY_LOCALE my_locale_es_AR
&my_locale_typelib_day_names_es_ES,
&my_locale_typelib_ab_day_names_es_ES,
10,
- 9
+ 9,
+ ',', /* decimal point es_AR */
+ '.', /* thousands_sep es_AR */
+ "\x03\x03", /* grouping es_AR */
+ &global_errmsgs[es_ES]
);
/***** LOCALE END es_AR *****/
@@ -2262,7 +2646,11 @@ MY_LOCALE my_locale_es_BO
&my_locale_typelib_day_names_es_ES,
&my_locale_typelib_ab_day_names_es_ES,
10,
- 9
+ 9,
+ ',', /* decimal point es_BO */
+ '\0', /* thousands_sep es_BO */
+ "\x80\x80", /* grouping es_BO */
+ &global_errmsgs[es_ES]
);
/***** LOCALE END es_BO *****/
@@ -2278,7 +2666,11 @@ MY_LOCALE my_locale_es_CL
&my_locale_typelib_day_names_es_ES,
&my_locale_typelib_ab_day_names_es_ES,
10,
- 9
+ 9,
+ ',', /* decimal point es_CL */
+ '\0', /* thousands_sep es_CL */
+ "\x80\x80", /* grouping es_CL */
+ &global_errmsgs[es_ES]
);
/***** LOCALE END es_CL *****/
@@ -2294,7 +2686,11 @@ MY_LOCALE my_locale_es_CO
&my_locale_typelib_day_names_es_ES,
&my_locale_typelib_ab_day_names_es_ES,
10,
- 9
+ 9,
+ ',', /* decimal point es_CO */
+ '\0', /* thousands_sep es_CO */
+ "\x80\x80", /* grouping es_CO */
+ &global_errmsgs[es_ES]
);
/***** LOCALE END es_CO *****/
@@ -2310,7 +2706,11 @@ MY_LOCALE my_locale_es_CR
&my_locale_typelib_day_names_es_ES,
&my_locale_typelib_ab_day_names_es_ES,
10,
- 9
+ 9,
+ '.', /* decimal point es_CR */
+ '\0', /* thousands_sep es_CR */
+ "\x80\x80", /* grouping es_CR */
+ &global_errmsgs[es_ES]
);
/***** LOCALE END es_CR *****/
@@ -2326,7 +2726,11 @@ MY_LOCALE my_locale_es_DO
&my_locale_typelib_day_names_es_ES,
&my_locale_typelib_ab_day_names_es_ES,
10,
- 9
+ 9,
+ '.', /* decimal point es_DO */
+ '\0', /* thousands_sep es_DO */
+ "\x80\x80", /* grouping es_DO */
+ &global_errmsgs[es_ES]
);
/***** LOCALE END es_DO *****/
@@ -2342,7 +2746,11 @@ MY_LOCALE my_locale_es_EC
&my_locale_typelib_day_names_es_ES,
&my_locale_typelib_ab_day_names_es_ES,
10,
- 9
+ 9,
+ ',', /* decimal point es_EC */
+ '\0', /* thousands_sep es_EC */
+ "\x80\x80", /* grouping es_EC */
+ &global_errmsgs[es_ES]
);
/***** LOCALE END es_EC *****/
@@ -2358,7 +2766,11 @@ MY_LOCALE my_locale_es_GT
&my_locale_typelib_day_names_es_ES,
&my_locale_typelib_ab_day_names_es_ES,
10,
- 9
+ 9,
+ '.', /* decimal point es_GT */
+ '\0', /* thousands_sep es_GT */
+ "\x80\x80", /* grouping es_GT */
+ &global_errmsgs[es_ES]
);
/***** LOCALE END es_GT *****/
@@ -2374,7 +2786,11 @@ MY_LOCALE my_locale_es_HN
&my_locale_typelib_day_names_es_ES,
&my_locale_typelib_ab_day_names_es_ES,
10,
- 9
+ 9,
+ '.', /* decimal point es_HN */
+ '\0', /* thousands_sep es_HN */
+ "\x80\x80", /* grouping es_HN */
+ &global_errmsgs[es_ES]
);
/***** LOCALE END es_HN *****/
@@ -2390,7 +2806,11 @@ MY_LOCALE my_locale_es_MX
&my_locale_typelib_day_names_es_ES,
&my_locale_typelib_ab_day_names_es_ES,
10,
- 9
+ 9,
+ '.', /* decimal point es_MX */
+ '\0', /* thousands_sep es_MX */
+ "\x80\x80", /* grouping es_MX */
+ &global_errmsgs[es_ES]
);
/***** LOCALE END es_MX *****/
@@ -2406,7 +2826,11 @@ MY_LOCALE my_locale_es_NI
&my_locale_typelib_day_names_es_ES,
&my_locale_typelib_ab_day_names_es_ES,
10,
- 9
+ 9,
+ '.', /* decimal point es_NI */
+ '\0', /* thousands_sep es_NI */
+ "\x80\x80", /* grouping es_NI */
+ &global_errmsgs[es_ES]
);
/***** LOCALE END es_NI *****/
@@ -2422,7 +2846,11 @@ MY_LOCALE my_locale_es_PA
&my_locale_typelib_day_names_es_ES,
&my_locale_typelib_ab_day_names_es_ES,
10,
- 9
+ 9,
+ '.', /* decimal point es_PA */
+ '\0', /* thousands_sep es_PA */
+ "\x80\x80", /* grouping es_PA */
+ &global_errmsgs[es_ES]
);
/***** LOCALE END es_PA *****/
@@ -2438,7 +2866,11 @@ MY_LOCALE my_locale_es_PE
&my_locale_typelib_day_names_es_ES,
&my_locale_typelib_ab_day_names_es_ES,
10,
- 9
+ 9,
+ '.', /* decimal point es_PE */
+ '\0', /* thousands_sep es_PE */
+ "\x80\x80", /* grouping es_PE */
+ &global_errmsgs[es_ES]
);
/***** LOCALE END es_PE *****/
@@ -2454,7 +2886,11 @@ MY_LOCALE my_locale_es_PR
&my_locale_typelib_day_names_es_ES,
&my_locale_typelib_ab_day_names_es_ES,
10,
- 9
+ 9,
+ '.', /* decimal point es_PR */
+ '\0', /* thousands_sep es_PR */
+ "\x80\x80", /* grouping es_PR */
+ &global_errmsgs[es_ES]
);
/***** LOCALE END es_PR *****/
@@ -2470,7 +2906,11 @@ MY_LOCALE my_locale_es_PY
&my_locale_typelib_day_names_es_ES,
&my_locale_typelib_ab_day_names_es_ES,
10,
- 9
+ 9,
+ ',', /* decimal point es_PY */
+ '\0', /* thousands_sep es_PY */
+ "\x80\x80", /* grouping es_PY */
+ &global_errmsgs[es_ES]
);
/***** LOCALE END es_PY *****/
@@ -2486,7 +2926,11 @@ MY_LOCALE my_locale_es_SV
&my_locale_typelib_day_names_es_ES,
&my_locale_typelib_ab_day_names_es_ES,
10,
- 9
+ 9,
+ '.', /* decimal point es_SV */
+ '\0', /* thousands_sep es_SV */
+ "\x80\x80", /* grouping es_SV */
+ &global_errmsgs[es_ES]
);
/***** LOCALE END es_SV *****/
@@ -2502,7 +2946,11 @@ MY_LOCALE my_locale_es_US
&my_locale_typelib_day_names_es_ES,
&my_locale_typelib_ab_day_names_es_ES,
10,
- 9
+ 9,
+ '.', /* decimal point es_US */
+ ',', /* thousands_sep es_US */
+ "\x03\x03", /* grouping es_US */
+ &global_errmsgs[es_ES]
);
/***** LOCALE END es_US *****/
@@ -2518,7 +2966,11 @@ MY_LOCALE my_locale_es_UY
&my_locale_typelib_day_names_es_ES,
&my_locale_typelib_ab_day_names_es_ES,
10,
- 9
+ 9,
+ ',', /* decimal point es_UY */
+ '\0', /* thousands_sep es_UY */
+ "\x80\x80", /* grouping es_UY */
+ &global_errmsgs[es_ES]
);
/***** LOCALE END es_UY *****/
@@ -2534,7 +2986,11 @@ MY_LOCALE my_locale_es_VE
&my_locale_typelib_day_names_es_ES,
&my_locale_typelib_ab_day_names_es_ES,
10,
- 9
+ 9,
+ ',', /* decimal point es_VE */
+ '\0', /* thousands_sep es_VE */
+ "\x80\x80", /* grouping es_VE */
+ &global_errmsgs[es_ES]
);
/***** LOCALE END es_VE *****/
@@ -2550,7 +3006,11 @@ MY_LOCALE my_locale_fr_BE
&my_locale_typelib_day_names_fr_FR,
&my_locale_typelib_ab_day_names_fr_FR,
9,
- 8
+ 8,
+ ',', /* decimal point fr_BE */
+ '.', /* thousands_sep fr_BE */
+ "\x80\x80", /* grouping fr_BE */
+ &global_errmsgs[fr_FR]
);
/***** LOCALE END fr_BE *****/
@@ -2566,7 +3026,11 @@ MY_LOCALE my_locale_fr_CA
&my_locale_typelib_day_names_fr_FR,
&my_locale_typelib_ab_day_names_fr_FR,
9,
- 8
+ 8,
+ ',', /* decimal point fr_CA */
+ ' ', /* thousands_sep fr_CA */
+ "\x80\x80", /* grouping fr_CA */
+ &global_errmsgs[fr_FR]
);
/***** LOCALE END fr_CA *****/
@@ -2582,7 +3046,11 @@ MY_LOCALE my_locale_fr_CH
&my_locale_typelib_day_names_fr_FR,
&my_locale_typelib_ab_day_names_fr_FR,
9,
- 8
+ 8,
+ ',', /* decimal point fr_CH */
+ '\0', /* thousands_sep fr_CH */
+ "\x80\x80", /* grouping fr_CH */
+ &global_errmsgs[fr_FR]
);
/***** LOCALE END fr_CH *****/
@@ -2598,7 +3066,11 @@ MY_LOCALE my_locale_fr_LU
&my_locale_typelib_day_names_fr_FR,
&my_locale_typelib_ab_day_names_fr_FR,
9,
- 8
+ 8,
+ ',', /* decimal point fr_LU */
+ '\0', /* thousands_sep fr_LU */
+ "\x80\x80", /* grouping fr_LU */
+ &global_errmsgs[fr_FR]
);
/***** LOCALE END fr_LU *****/
@@ -2614,7 +3086,11 @@ MY_LOCALE my_locale_it_IT
&my_locale_typelib_day_names_it_CH,
&my_locale_typelib_ab_day_names_it_CH,
9,
- 9
+ 9,
+ ',', /* decimal point it_IT */
+ '\0', /* thousands_sep it_IT */
+ "\x80\x80", /* grouping it_IT */
+ &global_errmsgs[it_IT]
);
/***** LOCALE END it_IT *****/
@@ -2630,7 +3106,11 @@ MY_LOCALE my_locale_nl_BE
&my_locale_typelib_day_names_nl_NL,
&my_locale_typelib_ab_day_names_nl_NL,
9,
- 9
+ 9,
+ ',', /* decimal point nl_BE */
+ '.', /* thousands_sep nl_BE */
+ "\x80\x80", /* grouping nl_BE */
+ &global_errmsgs[nl_NL]
);
/***** LOCALE END nl_BE *****/
@@ -2646,7 +3126,11 @@ MY_LOCALE my_locale_no_NO
&my_locale_typelib_day_names_nb_NO,
&my_locale_typelib_ab_day_names_nb_NO,
9,
- 7
+ 7,
+ ',', /* decimal point no_NO */
+ '.', /* thousands_sep no_NO */
+ "\x03\x03", /* grouping no_NO */
+ &global_errmsgs[no_NO]
);
/***** LOCALE END no_NO *****/
@@ -2662,7 +3146,11 @@ MY_LOCALE my_locale_sv_FI
&my_locale_typelib_day_names_sv_SE,
&my_locale_typelib_ab_day_names_sv_SE,
9,
- 7
+ 7,
+ ',', /* decimal point sv_FI */
+ ' ', /* thousands_sep sv_FI */
+ "\x03\x03", /* grouping sv_FI */
+ &global_errmsgs[sv_SE]
);
/***** LOCALE END sv_FI *****/
@@ -2678,11 +3166,87 @@ MY_LOCALE my_locale_zh_HK
&my_locale_typelib_day_names_zh_CN,
&my_locale_typelib_ab_day_names_zh_CN,
3,
- 3
+ 3,
+ '.', /* decimal point zh_HK */
+ ',', /* thousands_sep zh_HK */
+ "\x03", /* grouping zh_HK */
+ &global_errmsgs[en_US]
);
/***** LOCALE END zh_HK *****/
+/***** LOCALE BEGIN el_GR: Greek - Greece *****/
+static const char *my_locale_month_names_el_GR[13]=
+{
+ "ΙανουάÏιος", "ΦεβÏουάÏιος", "ΜάÏτιος",
+ "ΑπÏίλιος", "Μάιος", "ΙοÏνιος",
+ "ΙοÏλιος", "ΑÏγουστος", "ΣεπτέμβÏιος",
+ "ΟκτώβÏιος", "ÎοέμβÏιος", "ΔεκέμβÏιος", NullS
+};
+
+static const char *my_locale_ab_month_names_el_GR[13]=
+{
+ "Ιαν", "Φεβ", "ΜάÏ",
+ "ΑπÏ", "Μάι", "ΙοÏν",
+ "ΙοÏλ","ΑÏγ", "Σεπ",
+ "Οκτ", "Îοέ", "Δεκ", NullS
+};
+
+static const char *my_locale_day_names_el_GR[8] =
+{
+ "ΔευτέÏα", "ΤÏίτη", "ΤετάÏτη", "Πέμπτη",
+ "ΠαÏασκευή", "Σάββατο", "ΚυÏιακή", NullS
+};
+
+static const char *my_locale_ab_day_names_el_GR[8]=
+{
+ "Δευ", "ΤÏί", "Τετ", "Πέμ",
+ "ΠαÏ", "Σάβ", "ΚυÏ", NullS
+};
+
+static TYPELIB my_locale_typelib_month_names_el_GR=
+{
+ array_elements(my_locale_month_names_el_GR) - 1,
+ "", my_locale_month_names_el_GR, NULL
+};
+
+static TYPELIB my_locale_typelib_ab_month_names_el_GR=
+{
+ array_elements(my_locale_ab_month_names_el_GR)-1,
+ "", my_locale_ab_month_names_el_GR, NULL
+};
+
+static TYPELIB my_locale_typelib_day_names_el_GR=
+{
+ array_elements(my_locale_day_names_el_GR)-1,
+ "", my_locale_day_names_el_GR, NULL
+};
+
+static TYPELIB my_locale_typelib_ab_day_names_el_GR=
+{
+ array_elements(my_locale_ab_day_names_el_GR) - 1,
+ "", my_locale_ab_day_names_el_GR, NULL
+};
+
+MY_LOCALE my_locale_el_GR
+(
+ 109,
+ "el_GR",
+ "Greek - Greece",
+ FALSE,
+ &my_locale_typelib_month_names_el_GR,
+ &my_locale_typelib_ab_month_names_el_GR,
+ &my_locale_typelib_day_names_el_GR,
+ &my_locale_typelib_ab_day_names_el_GR,
+ 11, /* max mon name length */
+ 9, /* max day name length */
+ ',', /* decimal point el_GR */
+ '.', /* thousands_sep el_GR */
+ "\x80", /* grouping el_GR */
+ &global_errmsgs[el_GR]
+);
+/***** LOCALE END el_GR *****/
+
/*
The list of all locales.
Note, locales must be ordered according to their
@@ -2739,7 +3303,7 @@ MY_LOCALE *my_locales[]=
&my_locale_sk_SK,
&my_locale_sl_SI,
&my_locale_sq_AL,
- &my_locale_sr_YU,
+ &my_locale_sr_RS,
&my_locale_ta_IN,
&my_locale_te_IN,
&my_locale_th_TH,
@@ -2800,10 +3364,18 @@ MY_LOCALE *my_locales[]=
&my_locale_no_NO,
&my_locale_sv_FI,
&my_locale_zh_HK,
+ &my_locale_el_GR,
NULL
};
+MY_LOCALE *my_locales_deprecated[]=
+{
+ &my_locale_sr_YU,
+ NULL
+};
+
+
MY_LOCALE *my_locale_by_number(uint number)
{
MY_LOCALE *locale;
@@ -2816,17 +3388,60 @@ MY_LOCALE *my_locale_by_number(uint number)
}
-MY_LOCALE *my_locale_by_name(const char *name)
+static MY_LOCALE*
+my_locale_by_name(MY_LOCALE** locales, const char *name)
{
MY_LOCALE **locale;
- for (locale= my_locales; *locale != NULL; locale++)
+ for (locale= locales; *locale != NULL; locale++)
{
if (!my_strcasecmp(&my_charset_latin1, (*locale)->name, name))
- {
- // Check that locale is on its correct position in the array
- DBUG_ASSERT((*locale) == my_locales[(*locale)->number]);
return *locale;
- }
}
return NULL;
}
+
+
+MY_LOCALE *my_locale_by_name(const char *name)
+{
+ MY_LOCALE *locale;
+
+ if ((locale= my_locale_by_name(my_locales, name)))
+ {
+ // Check that locale is on its correct position in the array
+ DBUG_ASSERT(locale == my_locales[locale->number]);
+ return locale;
+ }
+ else if ((locale= my_locale_by_name(my_locales_deprecated, name)))
+ {
+ THD *thd= current_thd;
+ /*
+ Replace the deprecated locale to the corresponding
+ 'fresh' locale with the same ID.
+ */
+ locale= my_locales[locale->number];
+ if (thd)
+ {
+ // Send a warning to the client
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX),
+ name, locale->name);
+ }
+ else
+ {
+ // Send a warning to mysqld error log
+ sql_print_warning("The syntax '%s' is deprecated and will be removed. "
+ "Please use %s instead.",
+ name, locale->name);
+ }
+ }
+ return locale;
+}
+
+
+void cleanup_errmsgs()
+{
+ for (MY_LOCALE_ERRMSGS *msgs= global_errmsgs; msgs->language; msgs++)
+ {
+ my_free(msgs->errmsgs);
+ }
+}
diff --git a/sql/sql_locale.h b/sql/sql_locale.h
new file mode 100644
index 00000000000..8357a9ecba4
--- /dev/null
+++ b/sql/sql_locale.h
@@ -0,0 +1,78 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_LOCALE_INCLUDED
+#define SQL_LOCALE_INCLUDED
+
+typedef struct my_locale_errmsgs
+{
+ const char *language;
+ const char **errmsgs;
+} MY_LOCALE_ERRMSGS;
+
+#include "my_global.h" /* uint */
+
+typedef struct st_typelib TYPELIB;
+
+class MY_LOCALE
+{
+public:
+ uint number;
+ const char *name;
+ const char *description;
+ const bool is_ascii;
+ TYPELIB *month_names;
+ TYPELIB *ab_month_names;
+ TYPELIB *day_names;
+ TYPELIB *ab_day_names;
+ uint max_month_name_length;
+ uint max_day_name_length;
+ uint decimal_point;
+ uint thousand_sep;
+ const char *grouping;
+ MY_LOCALE_ERRMSGS *errmsgs;
+ MY_LOCALE(uint number_par,
+ const char *name_par, const char *descr_par, bool is_ascii_par,
+ TYPELIB *month_names_par, TYPELIB *ab_month_names_par,
+ TYPELIB *day_names_par, TYPELIB *ab_day_names_par,
+ uint max_month_name_length_par, uint max_day_name_length_par,
+ uint decimal_point_par, uint thousand_sep_par,
+ const char *grouping_par, MY_LOCALE_ERRMSGS *errmsgs_par) :
+ number(number_par),
+ name(name_par), description(descr_par), is_ascii(is_ascii_par),
+ month_names(month_names_par), ab_month_names(ab_month_names_par),
+ day_names(day_names_par), ab_day_names(ab_day_names_par),
+ max_month_name_length(max_month_name_length_par),
+ max_day_name_length(max_day_name_length_par),
+ decimal_point(decimal_point_par),
+ thousand_sep(thousand_sep_par),
+ grouping(grouping_par),
+ errmsgs(errmsgs_par)
+ {}
+};
+/* Exported variables */
+
+extern MY_LOCALE my_locale_en_US;
+extern MY_LOCALE *my_locales[];
+extern MY_LOCALE *my_default_lc_messages;
+extern MY_LOCALE *my_default_lc_time_names;
+
+/* Exported functions */
+
+MY_LOCALE *my_locale_by_name(const char *name);
+MY_LOCALE *my_locale_by_number(uint number);
+void cleanup_errmsgs(void);
+
+#endif /* SQL_LOCALE_INCLUDED */
diff --git a/sql/sql_manager.cc b/sql/sql_manager.cc
index 57fe5072dcd..778c732d443 100644
--- a/sql/sql_manager.cc
+++ b/sql/sql_manager.cc
@@ -1,6 +1,4 @@
-/*
- Copyright (c) 2000, 2002, 2005-2007 MySQL AB, 2008 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2000, 2013, 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
@@ -13,8 +11,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
* sql_manager.cc
@@ -24,15 +21,17 @@
* o Berkeley DB: removing unneeded log files.
*/
-#include "mysql_priv.h"
-
+#include "sql_priv.h"
+#include "sql_manager.h"
+#include "unireg.h" // REQUIRED: for other includes
+#include "sql_base.h" // flush_tables
static bool volatile manager_thread_in_use;
static bool abort_manager;
pthread_t manager_thread;
-pthread_mutex_t LOCK_manager;
-pthread_cond_t COND_manager;
+mysql_mutex_t LOCK_manager;
+mysql_cond_t COND_manager;
struct handler_cb {
struct handler_cb *next;
@@ -46,7 +45,7 @@ bool mysql_manager_submit(void (*action)())
bool result= FALSE;
DBUG_ASSERT(manager_thread_in_use);
struct handler_cb * volatile *cb;
- pthread_mutex_lock(&LOCK_manager);
+ mysql_mutex_lock(&LOCK_manager);
cb= &cb_list;
while (*cb && (*cb)->action != action)
cb= &(*cb)->next;
@@ -61,7 +60,7 @@ bool mysql_manager_submit(void (*action)())
(*cb)->action= action;
}
}
- pthread_mutex_unlock(&LOCK_manager);
+ mysql_mutex_unlock(&LOCK_manager);
return result;
}
@@ -76,12 +75,12 @@ pthread_handler_t handle_manager(void *arg __attribute__((unused)))
pthread_detach_this_thread();
manager_thread = pthread_self();
- (void) pthread_cond_init(&COND_manager,NULL);
- (void) pthread_mutex_init(&LOCK_manager,NULL);
+ mysql_cond_init(key_COND_manager, &COND_manager,NULL);
+ mysql_mutex_init(key_LOCK_manager, &LOCK_manager, NULL);
manager_thread_in_use = 1;
for (;;)
{
- pthread_mutex_lock(&LOCK_manager);
+ mysql_mutex_lock(&LOCK_manager);
/* XXX: This will need to be made more general to handle different
* polling needs. */
if (flush_time)
@@ -92,26 +91,26 @@ pthread_handler_t handle_manager(void *arg __attribute__((unused)))
reset_flush_time = FALSE;
}
while ((!error || error == EINTR) && !abort_manager)
- error= pthread_cond_timedwait(&COND_manager, &LOCK_manager, &abstime);
+ error= mysql_cond_timedwait(&COND_manager, &LOCK_manager, &abstime);
}
else
{
while ((!error || error == EINTR) && !abort_manager)
- error= pthread_cond_wait(&COND_manager, &LOCK_manager);
+ error= mysql_cond_wait(&COND_manager, &LOCK_manager);
}
if (cb == NULL)
{
cb= cb_list;
cb_list= NULL;
}
- pthread_mutex_unlock(&LOCK_manager);
+ mysql_mutex_unlock(&LOCK_manager);
if (abort_manager)
break;
if (error == ETIMEDOUT || error == ETIME)
{
- flush_tables();
+ tdc_flush_unused_tables();
error = 0;
reset_flush_time = TRUE;
}
@@ -120,13 +119,13 @@ pthread_handler_t handle_manager(void *arg __attribute__((unused)))
{
struct handler_cb *next= cb->next;
cb->action();
- my_free((uchar*)cb, MYF(0));
+ my_free(cb);
cb= next;
}
}
manager_thread_in_use = 0;
- (void) pthread_mutex_destroy(&LOCK_manager);
- (void) pthread_cond_destroy(&COND_manager);
+ mysql_mutex_destroy(&LOCK_manager);
+ mysql_cond_destroy(&COND_manager);
DBUG_LEAVE; // Can't use DBUG_RETURN after my_thread_end
my_thread_end();
return (NULL);
@@ -141,8 +140,12 @@ void start_handle_manager()
if (flush_time && flush_time != ~(ulong) 0L)
{
pthread_t hThread;
- if (pthread_create(&hThread,&connection_attrib,handle_manager,0))
- sql_print_warning("Can't create handle_manager thread");
+ int error;
+ if ((error= mysql_thread_create(key_thread_handle_manager,
+ &hThread, &connection_attrib,
+ handle_manager, 0)))
+ sql_print_warning("Can't create handle_manager thread (errno= %d)",
+ error);
}
DBUG_VOID_RETURN;
}
@@ -155,11 +158,11 @@ void stop_handle_manager()
abort_manager = true;
if (manager_thread_in_use)
{
- pthread_mutex_lock(&LOCK_manager);
+ mysql_mutex_lock(&LOCK_manager);
DBUG_PRINT("quit", ("initiate shutdown of handle manager thread: 0x%lx",
(ulong)manager_thread));
- pthread_cond_signal(&COND_manager);
- pthread_mutex_unlock(&LOCK_manager);
+ mysql_cond_signal(&COND_manager);
+ mysql_mutex_unlock(&LOCK_manager);
}
DBUG_VOID_RETURN;
}
diff --git a/sql/sql_manager.h b/sql/sql_manager.h
new file mode 100644
index 00000000000..8debbe6ead9
--- /dev/null
+++ b/sql/sql_manager.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_MANAGER_INCLUDED
+#define SQL_MANAGER_INCLUDED
+
+void start_handle_manager();
+void stop_handle_manager();
+bool mysql_manager_submit(void (*action)());
+
+#endif /* SQL_MANAGER_INCLUDED */
diff --git a/sql/sql_map.cc b/sql/sql_map.cc
deleted file mode 100644
index 46aa33535fd..00000000000
--- a/sql/sql_map.cc
+++ /dev/null
@@ -1,140 +0,0 @@
-/* Copyright (c) 2000, 2001, 2004-2007 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 Street, Fifth Floor, Boston, MA 02110-1301, USA */
-
-
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
-#include "mysql_priv.h"
-#include <sys/stat.h>
-#ifdef HAVE_SYS_MMAN_H
-#include <sys/mman.h>
-#endif
-
-mapped_files::mapped_files(const char * filename,uchar *magic,uint magic_length)
-{
-#ifdef HAVE_MMAP
- name=my_strdup(filename,MYF(0));
- use_count=1;
- error=0;
- map=0;
- size=0;
- if ((file=my_open(name,O_RDONLY,MYF(MY_WME))) >= 0)
- {
- struct stat stat_buf;
- if (!fstat(file,&stat_buf))
- {
- if (!(map=(uchar*) my_mmap(0,(size_t)(size= stat_buf.st_size),PROT_READ,
- MAP_SHARED | MAP_NORESERVE,file,
- 0L)))
- {
- error=errno;
- my_error(ER_NO_FILE_MAPPING, MYF(0), (char *) name, error);
- }
- }
- if (map && memcmp(map,magic,magic_length))
- {
- my_error(ER_WRONG_MAGIC, MYF(0), name);
- VOID(my_munmap((char*) map,(size_t)size));
- map=0;
- }
- if (!map)
- {
- VOID(my_close(file,MYF(0)));
- file= -1;
- }
- }
-#endif
-}
-
-
-mapped_files::~mapped_files()
-{
-#ifdef HAVE_MMAP
- if (file >= 0)
- {
- VOID(my_munmap((char*) map,(size_t)size));
- VOID(my_close(file,MYF(0)));
- file= -1; map=0;
- }
- my_free(name,MYF(0));
-#endif
-}
-
-
-static I_List<mapped_files> maps_in_use;
-
-/*
-** Check if a file is mapped. If it is, then return pointer to old map,
-** else alloc new object
-*/
-
-mapped_files *map_file(const char * name,uchar *magic,uint magic_length)
-{
-#ifdef HAVE_MMAP
- VOID(pthread_mutex_lock(&LOCK_mapped_file));
- I_List_iterator<mapped_files> list(maps_in_use);
- mapped_files *map;
- char path[FN_REFLEN];
- sprintf(path,"%s/%s/%s.uniq",mysql_data_home,current_thd->db,name);
- (void) unpack_filename(path,path);
-
- while ((map=list++))
- {
- if (!strcmp(path,map->name))
- break;
- }
- if (!map)
- {
- map=new mapped_files(path,magic,magic_length);
- maps_in_use.append(map);
- }
- else
- {
- map->use_count++;
- if (!map->map)
- my_error(ER_NO_FILE_MAPPING, MYF(0), path, map->error);
- }
- VOID(pthread_mutex_unlock(&LOCK_mapped_file));
- return map;
-#else
- return NULL;
-#endif
-}
-
-/*
-** free the map if there are no more users for it
-*/
-
-void unmap_file(mapped_files *map)
-{
-#ifdef HAVE_MMAP
- VOID(pthread_mutex_lock(&LOCK_mapped_file));
- if (!map->use_count--)
- delete map;
- VOID(pthread_mutex_unlock(&LOCK_mapped_file));
-#endif
-}
-
-/*****************************************************************************
-** Instansiate templates
-*****************************************************************************/
-
-#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-/* Used templates */
-template class I_List<mapped_files>;
-template class I_List_iterator<mapped_files>;
-#endif
diff --git a/sql/sql_map.h b/sql/sql_map.h
deleted file mode 100644
index 5f3308aad13..00000000000
--- a/sql/sql_map.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* Copyright (c) 2000, 2001, 2005-2007 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 Street, Fifth Floor, Boston, MA 02110-1301, USA */
-
-
-/* interface for memory mapped files */
-
-#ifdef USE_PRAGMA_INTERFACE
-#pragma interface /* gcc class implementation */
-#endif
-
-class mapped_files;
-mapped_files *map_file(const char * name,uchar *magic,uint magic_length);
-void unmap_file(mapped_files *map);
-
-class mapped_files :public ilink {
- uchar *map;
- ha_rows size;
- char *name; // name of mapped file
- File file; // >= 0 if open
- int error; // If not mapped
- uint use_count;
-
-public:
- mapped_files(const char * name,uchar *magic,uint magic_length);
- ~mapped_files();
-
- friend class mapped_file;
- friend mapped_files *map_file(const char * name,uchar *magic,
- uint magic_length);
- friend void unmap_file(mapped_files *map);
-};
-
-
-class mapped_file
-{
- mapped_files *file;
-public:
- mapped_file(const char * name,uchar *magic,uint magic_length)
- {
- file=map_file(name,magic,magic_length); /* old or new map */
- }
- ~mapped_file()
- {
- unmap_file(file); /* free map */
- }
- uchar *map()
- {
- return file->map;
- }
-};
diff --git a/sql/sql_olap.cc b/sql/sql_olap.cc
deleted file mode 100644
index a7a847cf286..00000000000
--- a/sql/sql_olap.cc
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- Copyright (c) 2002, 2010, Oracle and/or its affiliates.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-*/
-
-
-/*
- OLAP implementation by Sinisa Milivojevic <sinisa@mysql.com>
- Inspired by code submitted by Srilakshmi <lakshmi@gdit.iiit.net>
-
- The ROLLUP code in this file has to be complitely rewritten as it's
- not good enough to satisfy the goals of MySQL.
-
- In 4.1 we will replace this with a working, superior implementation
- of ROLLUP.
-*/
-
-#ifdef DISABLED_UNTIL_REWRITTEN_IN_4_1
-
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
-#include "mysql_priv.h"
-#include "sql_select.h"
-
-
-/****************************************************************************
- Functions that recursively actually creates new SELECT's
- Returns 0 if OK, 1 if error, -1 if error already printed to client
-****************************************************************************/
-
-
-static int make_new_olap_select(LEX *lex, SELECT_LEX *select_lex, List<Item> new_fields)
-{
- THD *thd=current_thd;
- Item *item, *new_item;
- Item_null *constant= new Item_null("ALL");
-
- SELECT_LEX *new_select = (SELECT_LEX *) thd->memdup((char*) select_lex, sizeof(*select_lex));
- if (!new_select)
- return 1;
- lex->last_selects->next=new_select;
- new_select->linkage=OLAP_TYPE;
- new_select->olap=NON_EXISTING_ONE;
- new_select->group_list.elements=0;
- new_select->group_list.first=(uchar *)0;
- new_select->group_list.next=(uchar **)&new_select->group_list.first;
- List<Item> privlist;
-
- List_iterator<Item> list_it(select_lex->item_list);
- List_iterator<Item> new_it(new_fields);
-
- while ((item=list_it++))
- {
- bool not_found= TRUE;
- if (item->type()==Item::FIELD_ITEM)
- {
- Item_field *iif = (Item_field *)item;
- new_it.rewind();
- while ((new_item=new_it++))
- {
- if (new_item->type()==Item::FIELD_ITEM &&
- !strcmp(((Item_field*)new_item)->table_name,iif->table_name) &&
- !strcmp(((Item_field*)new_item)->field_name,iif->field_name))
- {
- not_found= 0;
- ((Item_field*)new_item)->db_name=iif->db_name;
- Item_field *new_one=new Item_field(&select_lex->context,
- iif->db_name, iif->table_name, iif->field_name);
- privlist.push_back(new_one);
- if (add_to_list(new_select->group_list,new_one,1))
- return 1;
- break;
- }
- }
- }
- if (not_found)
- {
- if (item->type() == Item::FIELD_ITEM)
- privlist.push_back(constant);
- else
- privlist.push_back((Item*)thd->memdup((char *)item,item->size_of()));
- }
- }
- new_select->item_list=privlist;
-
- lex->last_selects = new_select;
- return 0;
-}
-
-/****************************************************************************
- Functions that recursively creates combinations of queries for OLAP
- Returns 0 if OK, 1 if error, -1 if error already printed to client
-****************************************************************************/
-
-static int olap_combos(List<Item> old_fields, List<Item> new_fields, Item *item, LEX *lex,
- SELECT_LEX *select_lex, int position, int selection, int num_fields,
- int num_new_fields)
-{
- int sl_return = 0;
- if (position == num_new_fields)
- {
- if (item)
- new_fields.push_front(item);
- sl_return = make_new_olap_select(lex, select_lex, new_fields);
- }
- else
- {
- if (item)
- new_fields.push_front(item);
- while ((num_fields - num_new_fields >= selection - position) && !sl_return)
- {
- item = old_fields.pop();
- sl_return = olap_combos(old_fields, new_fields, item, lex, select_lex, position+1, ++selection, num_fields, num_new_fields);
- }
- }
- return sl_return;
-}
-
-
-/****************************************************************************
- Top level function for converting OLAP clauses to multiple selects
- This is also a place where clauses treatment depends on OLAP type
- Returns 0 if OK, 1 if error, -1 if error already printed to client
-****************************************************************************/
-
-int handle_olaps(LEX *lex, SELECT_LEX *select_lex)
-{
- List<Item> item_list_copy, new_item_list;
- item_list_copy.empty();
- new_item_list.empty();
- int count=select_lex->group_list.elements;
- int sl_return=0;
-
-
- lex->last_selects=select_lex;
-
- for (ORDER *order= select_lex->group_list.first ; order ; order=order->next)
- item_list_copy.push_back(*(order->item));
-
- List<Item> all_fields(select_lex->item_list);
-
-
- if (setup_tables(lex->thd, &select_lex->context, &select_lex->top_join_list,
- FALSE, FALSE) ||
- setup_fields(lex->thd, 0, select_lex->item_list, MARK_COLUMNS_READ,
- &all_fields,1) ||
- setup_fields(lex->thd, 0, item_list_copy, MARK_COLUMNS_READ,
- &all_fields, 1))
- return -1;
-
- if (select_lex->olap == CUBE_TYPE)
- {
- for ( int i=count-1; i>=0 && !sl_return; i--)
- sl_return=olap_combos(item_list_copy, new_item_list, (Item *)0, lex, select_lex, 0, 0, count, i);
- }
- else if (select_lex->olap == ROLLUP_TYPE)
- {
- for ( int i=count-1; i>=0 && !sl_return; i--)
- {
- Item *item;
- item_list_copy.pop();
- List_iterator<Item> it(item_list_copy);
- new_item_list.empty();
- while ((item = it++))
- new_item_list.push_front(item);
- sl_return=make_new_olap_select(lex, select_lex, new_item_list);
- }
- }
- else
- sl_return=1; // impossible
- return sl_return;
-}
-
-#endif /* DISABLED_UNTIL_REWRITTEN_IN_4_1 */
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 43b10a6fb9e..c21e7fa3e4b 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1,6 +1,5 @@
-/*
- Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2008, 2014, Monty Program Ab.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,26 +12,91 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#define MYSQL_LEX 1
-#include "mysql_priv.h"
+#include "my_global.h"
+#include "sql_priv.h"
+#include "unireg.h" // REQUIRED: for other includes
+#include "sql_parse.h" // sql_kill, *_precheck, *_prepare
+#include "lock.h" // try_transactional_lock,
+ // check_transactional_lock,
+ // set_handler_table_locks,
+ // lock_global_read_lock,
+ // make_global_read_lock_block_commit
+#include "sql_base.h" // find_temporary_tablesx
+#include "sql_cache.h" // QUERY_CACHE_FLAGS_SIZE, query_cache_*
+#include "sql_show.h" // mysqld_list_*, mysqld_show_*,
+ // calc_sum_of_all_status
+#include "mysqld.h"
+#include "sql_locale.h" // my_locale_en_US
+#include "log.h" // flush_error_log
+#include "sql_view.h" // mysql_create_view, mysql_drop_view
+#include "sql_delete.h" // mysql_delete
+#include "sql_insert.h" // mysql_insert
+#include "sql_update.h" // mysql_update, mysql_multi_update
+#include "sql_partition.h" // struct partition_info
+#include "sql_db.h" // mysql_change_db, mysql_create_db,
+ // mysql_rm_db, mysql_upgrade_db,
+ // mysql_alter_db,
+ // check_db_dir_existence,
+ // my_dbopt_cleanup
+#include "sql_table.h" // mysql_create_like_table,
+ // mysql_create_table,
+ // mysql_alter_table,
+ // mysql_recreate_table,
+ // mysql_backup_table,
+ // mysql_restore_table
+#include "sql_reload.h" // reload_acl_and_cache
+#include "sql_admin.h" // mysql_assign_to_keycache
+#include "sql_connect.h" // decrease_user_connections,
+ // check_mqh,
+ // reset_mqh
+#include "sql_rename.h" // mysql_rename_table
+#include "sql_tablespace.h" // mysql_alter_tablespace
+#include "hostname.h" // hostname_cache_refresh
+#include "sql_acl.h" // *_ACL, check_grant, is_acl_user,
+ // has_any_table_level_privileges,
+ // mysql_drop_user, mysql_rename_user,
+ // check_grant_routine,
+ // mysql_routine_grant,
+ // mysql_show_grants,
+ // sp_grant_privileges, ...
+#include "sql_test.h" // mysql_print_status
+#include "sql_select.h" // handle_select, mysql_select,
+ // mysql_explain_union
+#include "sql_load.h" // mysql_load
+#include "sql_servers.h" // create_servers, alter_servers,
+ // drop_servers, servers_reload
+#include "sql_handler.h" // mysql_ha_open, mysql_ha_close,
+ // mysql_ha_read
+#include "sql_binlog.h" // mysql_client_binlog_statement
+#include "sql_do.h" // mysql_do
+#include "sql_help.h" // mysqld_help
+#include "rpl_constants.h" // Incident, INCIDENT_LOST_EVENTS
+#include "log_event.h"
#include "sql_repl.h"
#include "rpl_filter.h"
#include "repl_failsafe.h"
#include <m_ctype.h>
#include <myisam.h>
#include <my_dir.h>
+#include "rpl_handler.h"
#include "sp_head.h"
#include "sp.h"
#include "sp_cache.h"
#include "events.h"
#include "sql_trigger.h"
+#include "transaction.h"
+#include "sql_audit.h"
+#include "sql_prepare.h"
#include "debug_sync.h"
-#include "sql_handler.h"
-#include <rpl_mi.h>
+#include "probes_mysql.h"
+#include "set_var.h"
+#include "log_slow.h"
+
+#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")
#ifdef WITH_ARIA_STORAGE_ENGINE
#include "../storage/maria/ha_maria.h"
@@ -54,10 +118,10 @@
"FUNCTION" : "PROCEDURE")
static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
-static bool execute_show_status(THD *thd, TABLE_LIST *all_tables);
-static bool execute_rename_table(THD *thd, TABLE_LIST *first_table,
- TABLE_LIST *all_tables);
-static bool check_show_create_table_access(THD *thd, TABLE_LIST *table);
+static void sql_kill(THD *thd, ulong id, killed_state state);
+static void sql_kill_user(THD *thd, LEX_USER *user, killed_state state);
+static bool execute_show_status(THD *, TABLE_LIST *);
+static bool execute_rename_table(THD *, TABLE_LIST *, TABLE_LIST *);
const char *any_db="*any*"; // Special symbol for check_access
@@ -99,132 +163,6 @@ const char *xa_state_names[]={
"NON-EXISTING", "ACTIVE", "IDLE", "PREPARED", "ROLLBACK ONLY"
};
-/**
- Mark a XA transaction as rollback-only if the RM unilaterally
- rolled back the transaction branch.
-
- @note If a rollback was requested by the RM, this function sets
- the appropriate rollback error code and transits the state
- to XA_ROLLBACK_ONLY.
-
- @return TRUE if transaction was rolled back or if the transaction
- state is XA_ROLLBACK_ONLY. FALSE otherwise.
-*/
-static bool xa_trans_rolled_back(XID_STATE *xid_state)
-{
- if (xid_state->rm_error)
- {
- switch (xid_state->rm_error) {
- case ER_LOCK_WAIT_TIMEOUT:
- my_error(ER_XA_RBTIMEOUT, MYF(0));
- break;
- case ER_LOCK_DEADLOCK:
- my_error(ER_XA_RBDEADLOCK, MYF(0));
- break;
- default:
- my_error(ER_XA_RBROLLBACK, MYF(0));
- }
- xid_state->xa_state= XA_ROLLBACK_ONLY;
- }
-
- return (xid_state->xa_state == XA_ROLLBACK_ONLY);
-}
-
-/**
- Rollback work done on behalf of at ransaction branch.
-*/
-static bool xa_trans_rollback(THD *thd)
-{
- /*
- Resource Manager error is meaningless at this point, as we perform
- explicit rollback request by user. We must reset rm_error before
- calling ha_rollback(), so thd->transaction.xid structure gets reset
- by ha_rollback()/THD::transaction::cleanup().
- */
- thd->transaction.xid_state.rm_error= 0;
-
- bool status= test(ha_rollback(thd));
-
- thd->options&= ~(ulong) OPTION_BEGIN;
- thd->transaction.all.modified_non_trans_table= FALSE;
- thd->server_status&= ~SERVER_STATUS_IN_TRANS;
- xid_cache_delete(&thd->transaction.xid_state);
- thd->transaction.xid_state.xa_state= XA_NOTR;
-
- return status;
-}
-
-static void unlock_locked_tables(THD *thd)
-{
- if (thd->locked_tables)
- {
- thd->lock=thd->locked_tables;
- thd->locked_tables=0; // Will be automatically closed
- close_thread_tables(thd); // Free tables
- }
-}
-
-
-bool end_active_trans(THD *thd)
-{
- int error=0;
- DBUG_ENTER("end_active_trans");
- if (unlikely(thd->in_sub_stmt))
- {
- my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
- DBUG_RETURN(1);
- }
- if (thd->transaction.xid_state.xa_state != XA_NOTR)
- {
- my_error(ER_XAER_RMFAIL, MYF(0),
- xa_state_names[thd->transaction.xid_state.xa_state]);
- DBUG_RETURN(1);
- }
- if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN |
- OPTION_TABLE_LOCK))
- {
- DBUG_PRINT("info",("options: 0x%llx", thd->options));
- /* Safety if one did "drop table" on locked tables */
- if (!thd->locked_tables)
- thd->options&= ~OPTION_TABLE_LOCK;
- thd->server_status&= ~SERVER_STATUS_IN_TRANS;
- if (ha_commit(thd))
- error=1;
-#ifdef WITH_ARIA_STORAGE_ENGINE
- if (ha_storage_engine_is_enabled(maria_hton))
- ha_maria::implicit_commit(thd, TRUE);
-#endif
- }
- thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
- thd->transaction.all.modified_non_trans_table= FALSE;
- DBUG_RETURN(error);
-}
-
-
-bool begin_trans(THD *thd)
-{
- int error=0;
- if (unlikely(thd->in_sub_stmt))
- {
- my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
- return 1;
- }
- if (thd->locked_tables)
- {
- thd->lock=thd->locked_tables;
- thd->locked_tables=0; // Will be automatically closed
- close_thread_tables(thd); // Free tables
- }
- if (end_active_trans(thd))
- error= -1;
- else
- {
- thd->options|= OPTION_BEGIN;
- thd->server_status|= SERVER_STATUS_IN_TRANS;
- }
- return error;
-}
-
#ifdef HAVE_REPLICATION
/**
Returns true if all tables should be ignored.
@@ -242,14 +180,49 @@ 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);
- if (table->updating &&
- !find_temporary_table(thd, table->db, table->table_name))
+ if (table->updating && !find_temporary_table(thd, table))
return 1;
}
return 0;
}
+/*
+ Implicitly commit a active transaction if statement requires so.
+
+ @param thd Thread handle.
+ @param mask Bitmask used for the SQL command match.
+
+*/
+static bool stmt_causes_implicit_commit(THD *thd, uint mask)
+{
+ LEX *lex= thd->lex;
+ bool skip= FALSE;
+ DBUG_ENTER("stmt_causes_implicit_commit");
+
+ if (!(sql_command_flags[lex->sql_command] & mask))
+ DBUG_RETURN(FALSE);
+
+ switch (lex->sql_command) {
+ case SQLCOM_DROP_TABLE:
+ skip= lex->drop_temporary;
+ break;
+ case SQLCOM_ALTER_TABLE:
+ case SQLCOM_CREATE_TABLE:
+ /* If CREATE TABLE of non-temporary table, do implicit commit */
+ skip= (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE);
+ break;
+ case SQLCOM_SET_OPTION:
+ skip= lex->autocommit ? FALSE : TRUE;
+ break;
+ default:
+ break;
+ }
+
+ DBUG_RETURN(!skip);
+}
+
+
/**
Mark all commands that somehow changes a table.
@@ -264,48 +237,79 @@ static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables)
*/
uint sql_command_flags[SQLCOM_END+1];
+uint server_command_flags[COM_END+1];
void init_update_queries(void)
{
- bzero((uchar*) &sql_command_flags, sizeof(sql_command_flags));
-
- sql_command_flags[SQLCOM_CREATE_TABLE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE;
- sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA | CF_REPORT_PROGRESS;
- sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND | CF_REPORT_PROGRESS;
- sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND;
- sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA;
- sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_REPORT_PROGRESS;
- sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA;
- sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA;
- sql_command_flags[SQLCOM_RENAME_TABLE]= CF_CHANGES_DATA;
- sql_command_flags[SQLCOM_BACKUP_TABLE]= CF_CHANGES_DATA;
- sql_command_flags[SQLCOM_RESTORE_TABLE]= CF_CHANGES_DATA;
- sql_command_flags[SQLCOM_DROP_INDEX]= CF_CHANGES_DATA | CF_REPORT_PROGRESS;
- sql_command_flags[SQLCOM_CREATE_VIEW]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE;
- sql_command_flags[SQLCOM_DROP_VIEW]= CF_CHANGES_DATA;
- sql_command_flags[SQLCOM_CREATE_EVENT]= CF_CHANGES_DATA;
- sql_command_flags[SQLCOM_ALTER_EVENT]= CF_CHANGES_DATA;
- sql_command_flags[SQLCOM_DROP_EVENT]= CF_CHANGES_DATA;
-
- sql_command_flags[SQLCOM_UPDATE]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
- CF_REEXECUTION_FRAGILE;
- sql_command_flags[SQLCOM_UPDATE_MULTI]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
- CF_REEXECUTION_FRAGILE;
- sql_command_flags[SQLCOM_INSERT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
- CF_REEXECUTION_FRAGILE;
- sql_command_flags[SQLCOM_INSERT_SELECT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
- CF_REEXECUTION_FRAGILE;
- sql_command_flags[SQLCOM_DELETE]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
- CF_REEXECUTION_FRAGILE;
- sql_command_flags[SQLCOM_DELETE_MULTI]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
- CF_REEXECUTION_FRAGILE;
- sql_command_flags[SQLCOM_REPLACE]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
- CF_REEXECUTION_FRAGILE;
- sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
- CF_REEXECUTION_FRAGILE;
- sql_command_flags[SQLCOM_SELECT]= CF_REEXECUTION_FRAGILE;
- sql_command_flags[SQLCOM_SET_OPTION]= CF_REEXECUTION_FRAGILE;
- sql_command_flags[SQLCOM_DO]= CF_REEXECUTION_FRAGILE;
+ /* Initialize the server command flags array. */
+ memset(server_command_flags, 0, sizeof(server_command_flags));
+
+ server_command_flags[COM_STATISTICS]= CF_SKIP_QUERY_ID | CF_SKIP_QUESTIONS;
+ server_command_flags[COM_PING]= CF_SKIP_QUERY_ID | CF_SKIP_QUESTIONS;
+ server_command_flags[COM_STMT_PREPARE]= CF_SKIP_QUESTIONS;
+ server_command_flags[COM_STMT_CLOSE]= CF_SKIP_QUESTIONS;
+ server_command_flags[COM_STMT_RESET]= CF_SKIP_QUESTIONS;
+
+ /* Initialize the sql command flags array. */
+ memset(sql_command_flags, 0, sizeof(sql_command_flags));
+
+ /*
+ In general, DDL statements do not generate row events and do not go
+ through a cache before being written to the binary log. However, the
+ CREATE TABLE...SELECT is an exception because it may generate row
+ events. For that reason, the SQLCOM_CREATE_TABLE which represents
+ a CREATE TABLE, including the CREATE TABLE...SELECT, has the
+ CF_CAN_GENERATE_ROW_EVENTS flag. The distinction between a regular
+ CREATE TABLE and the CREATE TABLE...SELECT is made in other parts of
+ the code, in particular in the Query_log_event's constructor.
+ */
+ sql_command_flags[SQLCOM_CREATE_TABLE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
+ CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS |
+ CF_CAN_GENERATE_ROW_EVENTS;
+ sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
+ CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS ;
+ sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
+ CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
+ CF_CAN_GENERATE_ROW_EVENTS | CF_REPORT_PROGRESS;
+ sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS;
+ 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;
+ sql_command_flags[SQLCOM_DROP_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS;
+ sql_command_flags[SQLCOM_CREATE_VIEW]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
+ CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_VIEW]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_CREATE_TRIGGER]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_TRIGGER]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_CREATE_EVENT]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_ALTER_EVENT]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_EVENT]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+
+ sql_command_flags[SQLCOM_UPDATE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
+ CF_CAN_GENERATE_ROW_EVENTS;
+ sql_command_flags[SQLCOM_UPDATE_MULTI]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
+ CF_CAN_GENERATE_ROW_EVENTS;
+ sql_command_flags[SQLCOM_INSERT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
+ CF_CAN_GENERATE_ROW_EVENTS;
+ sql_command_flags[SQLCOM_INSERT_SELECT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
+ CF_CAN_GENERATE_ROW_EVENTS;
+ sql_command_flags[SQLCOM_DELETE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
+ CF_CAN_GENERATE_ROW_EVENTS;
+ sql_command_flags[SQLCOM_DELETE_MULTI]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
+ CF_CAN_GENERATE_ROW_EVENTS;
+ sql_command_flags[SQLCOM_REPLACE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
+ CF_CAN_GENERATE_ROW_EVENTS;
+ sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
+ CF_CAN_GENERATE_ROW_EVENTS;
+ sql_command_flags[SQLCOM_SELECT]= CF_REEXECUTION_FRAGILE |
+ CF_CAN_GENERATE_ROW_EVENTS;
+ sql_command_flags[SQLCOM_SET_OPTION]= CF_REEXECUTION_FRAGILE | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DO]= CF_REEXECUTION_FRAGILE |
+ CF_CAN_GENERATE_ROW_EVENTS;
sql_command_flags[SQLCOM_SHOW_STATUS_PROC]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
sql_command_flags[SQLCOM_SHOW_STATUS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
@@ -319,46 +323,57 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_SHOW_VARIABLES]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
sql_command_flags[SQLCOM_SHOW_CHARSETS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
sql_command_flags[SQLCOM_SHOW_COLLATIONS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
- sql_command_flags[SQLCOM_SHOW_NEW_MASTER]= CF_STATUS_COMMAND;
- sql_command_flags[SQLCOM_SHOW_BINLOGS]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_BINLOGS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_SLAVE_HOSTS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_BINLOG_EVENTS]= CF_STATUS_COMMAND;
- sql_command_flags[SQLCOM_SHOW_COLUMN_TYPES]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_STORAGE_ENGINES]= CF_STATUS_COMMAND;
- sql_command_flags[SQLCOM_SHOW_AUTHORS]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_AUTHORS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CONTRIBUTORS]= CF_STATUS_COMMAND;
- sql_command_flags[SQLCOM_SHOW_PRIVILEGES]= CF_STATUS_COMMAND;
- sql_command_flags[SQLCOM_SHOW_WARNS]= CF_STATUS_COMMAND;
- sql_command_flags[SQLCOM_SHOW_ERRORS]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_PRIVILEGES]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_WARNS]= CF_STATUS_COMMAND | CF_DIAGNOSTIC_STMT;
+ sql_command_flags[SQLCOM_SHOW_ERRORS]= CF_STATUS_COMMAND | CF_DIAGNOSTIC_STMT;
sql_command_flags[SQLCOM_SHOW_ENGINE_STATUS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_ENGINE_MUTEX]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_ENGINE_LOGS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_PROCESSLIST]= CF_STATUS_COMMAND;
- sql_command_flags[SQLCOM_SHOW_GRANTS]= CF_STATUS_COMMAND;
- sql_command_flags[SQLCOM_SHOW_CREATE_DB]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_GRANTS]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_CREATE_DB]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE]= CF_STATUS_COMMAND;
- sql_command_flags[SQLCOM_SHOW_MASTER_STAT]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_MASTER_STAT]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_SLAVE_STAT]= CF_STATUS_COMMAND;
- sql_command_flags[SQLCOM_SHOW_CREATE_PROC]= CF_STATUS_COMMAND;
- sql_command_flags[SQLCOM_SHOW_CREATE_FUNC]= CF_STATUS_COMMAND;
+ 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_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_CREATE_EVENT]= CF_STATUS_COMMAND;
- sql_command_flags[SQLCOM_SHOW_PROFILES]= CF_STATUS_COMMAND;
- sql_command_flags[SQLCOM_SHOW_PROFILE]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_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_CREATE_EVENT]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_PROFILES]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_PROFILE]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_BINLOG_BASE64_EVENT]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CLIENT_STATS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_USER_STATS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_TABLE_STATS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_INDEX_STATS]= CF_STATUS_COMMAND;
-
- sql_command_flags[SQLCOM_SHOW_TABLES]= (CF_STATUS_COMMAND |
- CF_SHOW_TABLE_COMMAND |
- CF_REEXECUTION_FRAGILE);
- sql_command_flags[SQLCOM_SHOW_TABLE_STATUS]= (CF_STATUS_COMMAND |
- CF_SHOW_TABLE_COMMAND |
- CF_REEXECUTION_FRAGILE);
+ sql_command_flags[SQLCOM_SHOW_TABLES]= (CF_STATUS_COMMAND | CF_SHOW_TABLE_COMMAND | CF_REEXECUTION_FRAGILE);
+ sql_command_flags[SQLCOM_SHOW_TABLE_STATUS]= (CF_STATUS_COMMAND | CF_SHOW_TABLE_COMMAND | CF_REEXECUTION_FRAGILE);
+
+
+ sql_command_flags[SQLCOM_CREATE_USER]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_RENAME_USER]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_DROP_USER]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_GRANT]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_REVOKE]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_OPTIMIZE]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_CREATE_FUNCTION]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_CREATE_PROCEDURE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_CREATE_SPFUNCTION]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_PROCEDURE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_FUNCTION]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_ALTER_PROCEDURE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_ALTER_FUNCTION]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_INSTALL_PLUGIN]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_UNINSTALL_PLUGIN]= CF_CHANGES_DATA;
/*
The following is used to preserver CF_ROW_COUNT during the
@@ -366,21 +381,43 @@ void init_update_queries(void)
last called (or executed) statement is preserved.
See mysql_execute_command() for how CF_ROW_COUNT is used.
*/
- sql_command_flags[SQLCOM_CALL]= CF_HAS_ROW_COUNT | CF_REEXECUTION_FRAGILE;
- sql_command_flags[SQLCOM_EXECUTE]= CF_HAS_ROW_COUNT;
+ sql_command_flags[SQLCOM_CALL]= CF_REEXECUTION_FRAGILE |
+ CF_CAN_GENERATE_ROW_EVENTS;
+ sql_command_flags[SQLCOM_EXECUTE]= CF_CAN_GENERATE_ROW_EVENTS;
/*
The following admin table operations are allowed
on log tables.
*/
- sql_command_flags[SQLCOM_REPAIR]= CF_WRITE_LOGS_COMMAND | CF_REPORT_PROGRESS;
- sql_command_flags[SQLCOM_OPTIMIZE]= CF_WRITE_LOGS_COMMAND | CF_REPORT_PROGRESS;
- sql_command_flags[SQLCOM_ANALYZE]= CF_WRITE_LOGS_COMMAND | CF_REPORT_PROGRESS;
- sql_command_flags[SQLCOM_CHECK]= CF_REPORT_PROGRESS;
- sql_command_flags[SQLCOM_CHECKSUM]= CF_REPORT_PROGRESS;
+ sql_command_flags[SQLCOM_REPAIR]= CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS;
+ sql_command_flags[SQLCOM_OPTIMIZE]|= CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS;
+ sql_command_flags[SQLCOM_ANALYZE]= CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS;
+ sql_command_flags[SQLCOM_CHECK]= CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS;
+ sql_command_flags[SQLCOM_CHECKSUM]= CF_REPORT_PROGRESS;
+
+ sql_command_flags[SQLCOM_CREATE_USER]|= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_USER]|= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_RENAME_USER]|= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_REVOKE_ALL]= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_REVOKE]|= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_GRANT]|= CF_AUTO_COMMIT_TRANS;
+
+ sql_command_flags[SQLCOM_ASSIGN_TO_KEYCACHE]= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_PRELOAD_KEYS]= CF_AUTO_COMMIT_TRANS;
+
+ sql_command_flags[SQLCOM_FLUSH]= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_RESET]= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_CREATE_SERVER]= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_ALTER_SERVER]= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_SERVER]= CF_AUTO_COMMIT_TRANS;
}
-
+bool sqlcom_can_generate_row_events(const THD *thd)
+{
+ return (sql_command_flags[thd->lex->sql_command] &
+ CF_CAN_GENERATE_ROW_EVENTS);
+}
+
bool is_update_query(enum enum_sql_command command)
{
DBUG_ASSERT(command <= SQLCOM_END);
@@ -398,25 +435,34 @@ bool is_log_table_write_query(enum enum_sql_command command)
return (sql_command_flags[command] & CF_WRITE_LOGS_COMMAND) != 0;
}
-void execute_init_command(THD *thd, sys_var_str *init_command_var,
- rw_lock_t *var_mutex)
+void execute_init_command(THD *thd, LEX_STRING *init_command,
+ mysql_rwlock_t *var_lock)
{
Vio* save_vio;
ulong save_client_capabilities;
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ mysql_rwlock_rdlock(var_lock);
+ if (!init_command->length)
+ {
+ mysql_rwlock_unlock(var_lock);
+ return;
+ }
+
+ /*
+ copy the value under a lock, and release the lock.
+ init_command has to be executed without a lock held,
+ as it may try to change itself
+ */
+ size_t len= init_command->length;
+ char *buf= thd->strmake(init_command->str, len);
+ mysql_rwlock_unlock(var_lock);
+
+#if defined(ENABLED_PROFILING)
thd->profiling.start_new_query();
- thd->profiling.set_query_source(init_command_var->value,
- init_command_var->value_length);
+ thd->profiling.set_query_source(buf, len);
#endif
thd_proc_info(thd, "Execution of init_command");
- /*
- We need to lock init_command_var because
- during execution of init_command_var query
- values of init_command_var can't be changed
- */
- rw_rdlock(var_mutex);
save_client_capabilities= thd->client_capabilities;
thd->client_capabilities|= CLIENT_MULTI_QUERIES;
/*
@@ -425,14 +471,11 @@ void execute_init_command(THD *thd, sys_var_str *init_command_var,
*/
save_vio= thd->net.vio;
thd->net.vio= 0;
- dispatch_command(COM_QUERY, thd,
- init_command_var->value,
- init_command_var->value_length);
- rw_unlock(var_mutex);
+ dispatch_command(COM_QUERY, thd, buf, len);
thd->client_capabilities= save_client_capabilities;
thd->net.vio= save_vio;
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+#if defined(ENABLED_PROFILING)
thd->profiling.finish_current_query();
#endif
}
@@ -440,9 +483,8 @@ void execute_init_command(THD *thd, sys_var_str *init_command_var,
static void handle_bootstrap_impl(THD *thd)
{
- FILE *file=bootstrap_file;
- char *buff;
- const char* found_semicolon= NULL;
+ MYSQL_FILE *file= bootstrap_file;
+ char *buff, *res;
DBUG_ENTER("handle_bootstrap");
@@ -451,11 +493,7 @@ static void handle_bootstrap_impl(THD *thd)
thd->thread_stack= (char*) &thd;
#endif /* EMBEDDED_LIBRARY */
- if (thd->variables.max_join_size == HA_POS_ERROR)
- thd->options |= OPTION_BIG_SELECTS;
-
thd_proc_info(thd, 0);
- thd->version=refresh_version;
thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
thd->security_ctx->priv_user[0]= thd->security_ctx->priv_host[0]=0;
/*
@@ -467,12 +505,12 @@ static void handle_bootstrap_impl(THD *thd)
buff= (char*) thd->net.buff;
thd->init_for_queries();
- while (fgets(buff, thd->net.max_packet, file))
+ while (mysql_file_fgets(buff, thd->net.max_packet, file))
{
char *query;
- /* strlen() can't be deleted because fgets() doesn't return length */
+ /* strlen() can't be deleted because mysql_file_fgets() doesn't return length */
ulong length= (ulong) strlen(buff);
- while (buff[length-1] != '\n' && !feof(file))
+ while (buff[length-1] != '\n' && !mysql_file_feof(file))
{
/*
We got only a part of the current string. Will try to increase
@@ -481,14 +519,15 @@ static void handle_bootstrap_impl(THD *thd)
/* purecov: begin tested */
if (net_realloc(&(thd->net), 2 * thd->net.max_packet))
{
- net_end_statement(thd);
+ thd->protocol->end_statement();
bootstrap_error= 1;
break;
}
buff= (char*) thd->net.buff;
- if (!fgets(buff + length, thd->net.max_packet - length, file))
+ res= mysql_file_fgets(buff + length, thd->net.max_packet - length, file);
+ if (!res && !mysql_file_feof(file))
{
- net_end_statement(thd);
+ thd->protocol->end_statement();
bootstrap_error= 1;
break;
}
@@ -511,10 +550,12 @@ static void handle_bootstrap_impl(THD *thd)
thd->db_length + 1 +
QUERY_CACHE_DB_LENGTH_SIZE +
QUERY_CACHE_FLAGS_SIZE);
- thd->set_query(query, length);
+ size_t db_len= 0;
+ memcpy(query + length + 1, (char *) &db_len, sizeof(size_t));
+ thd->set_query_and_id(query, length, thd->charset(), next_query_id());
int2store(query + length + 1, 0); // No db in bootstrap
- DBUG_PRINT("query",("%-.4096s", thd->query()));
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ DBUG_PRINT("query",("%-.4096s",thd->query()));
+#if defined(ENABLED_PROFILING)
thd->profiling.start_new_query();
thd->profiling.set_query_source(thd->query(), length);
#endif
@@ -523,15 +564,21 @@ static void handle_bootstrap_impl(THD *thd)
We don't need to obtain LOCK_thread_count here because in bootstrap
mode we have only one thread.
*/
- thd->query_id=next_query_id();
thd->set_time();
- mysql_parse(thd, thd->query(), length, & found_semicolon);
- close_thread_tables(thd); // Free tables
+ Parser_state parser_state;
+ if (parser_state.init(thd, thd->query(), length))
+ {
+ thd->protocol->end_statement();
+ bootstrap_error= 1;
+ break;
+ }
+
+ mysql_parse(thd, thd->query(), length, &parser_state);
bootstrap_error= thd->is_error();
- net_end_statement(thd);
+ thd->protocol->end_statement();
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+#if defined(ENABLED_PROFILING)
thd->profiling.finish_current_query();
#endif
@@ -539,9 +586,7 @@ static void handle_bootstrap_impl(THD *thd)
break;
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
-#ifdef USING_TRANSACTIONS
free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC));
-#endif
}
DBUG_VOID_RETURN;
@@ -558,12 +603,20 @@ pthread_handler_t handle_bootstrap(void *arg)
{
THD *thd=(THD*) arg;
+ mysql_thread_set_psi_id(thd->thread_id);
+
+ do_handle_bootstrap(thd);
+ return 0;
+}
+
+void do_handle_bootstrap(THD *thd)
+{
/* The following must be called before DBUG_ENTER */
thd->thread_stack= (char*) &thd;
if (my_thread_init() || thd->store_globals())
{
#ifndef EMBEDDED_LIBRARY
- close_connection(thd, ER_OUT_OF_RESOURCES, 1);
+ close_connection(thd, ER_OUT_OF_RESOURCES);
#endif
thd->fatal_error();
goto end;
@@ -577,57 +630,18 @@ end:
delete thd;
#ifndef EMBEDDED_LIBRARY
- (void) pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
thread_count--;
in_bootstrap= FALSE;
- (void) pthread_cond_broadcast(&COND_thread_count);
- (void) pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_cond_broadcast(&COND_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
my_thread_end();
pthread_exit(0);
#endif
- return 0;
+ return;
}
-/**
- @brief Check access privs for a MERGE table and fix children lock types.
-
- @param[in] thd thread handle
- @param[in] db database name
- @param[in,out] table_list list of child tables (merge_list)
- lock_type and optionally db set per table
-
- @return status
- @retval 0 OK
- @retval != 0 Error
-
- @detail
- This function is used for write access to MERGE tables only
- (CREATE TABLE, ALTER TABLE ... UNION=(...)). Set TL_WRITE for
- every child. Set 'db' for every child if not present.
-*/
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
-static bool check_merge_table_access(THD *thd, char *db,
- TABLE_LIST *table_list)
-{
- int error= 0;
-
- if (table_list)
- {
- /* Check that all tables use the current database */
- TABLE_LIST *tlist;
-
- for (tlist= table_list; tlist; tlist= tlist->next_local)
- {
- if (!tlist->db || !tlist->db[0])
- tlist->db= db; /* purecov: inspected */
- }
- error= check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
- table_list, UINT_MAX, FALSE);
- }
- return error;
-}
-#endif
/* This works because items are allocated with sql_alloc() */
@@ -655,165 +669,6 @@ void cleanup_items(Item *item)
DBUG_VOID_RETURN;
}
-/**
- Handle COM_TABLE_DUMP command.
-
- @param thd thread handle
- @param db database name or an empty string. If empty,
- the current database of the connection is used
- @param tbl_name name of the table to dump
-
- @note
- This function is written to handle one specific command only.
-
- @retval
- 0 success
- @retval
- 1 error, the error message is set in THD
-*/
-
-static
-int mysql_table_dump(THD *thd, LEX_STRING *db, LEX_STRING *table_name)
-{
- TABLE* table;
- TABLE_LIST* table_list;
- int error = 0;
- DBUG_ENTER("mysql_table_dump");
- if (db->length == 0)
- {
- db->str= thd->db; /* purecov: inspected */
- db->length= thd->db_length; /* purecov: inspected */
- }
- if (!(table_list = (TABLE_LIST*) thd->calloc(sizeof(TABLE_LIST))))
- DBUG_RETURN(1); // out of memory
- table_list->db= db->str;
- table_list->table_name= table_list->alias= table_name->str;
- table_list->lock_type= TL_READ_NO_INSERT;
- table_list->prev_global= &table_list; // can be removed after merge with 4.1
-
- if (check_db_name(db))
- {
- /* purecov: begin inspected */
- my_error(ER_WRONG_DB_NAME ,MYF(0), db->str ? db->str : "NULL");
- goto err;
- /* purecov: end */
- }
- if (!table_name->length ||
- check_table_name(table_name->str, table_name->length, TRUE))
- {
- my_error(ER_WRONG_TABLE_NAME, MYF(0),
- table_name->str ? table_name->str : "NULL");
- error= 1;
- goto err;
- }
- if (lower_case_table_names)
- my_casedn_str(files_charset_info, table_name->str);
-
- if (!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT, 0)))
- DBUG_RETURN(1);
-
- if (check_one_table_access(thd, SELECT_ACL, table_list))
- goto err;
- thd->free_list = 0;
- thd->set_query(table_name->str, table_name->length);
- if ((error = mysqld_dump_create_info(thd, table_list, -1)))
- {
- my_error(ER_GET_ERRNO, MYF(0), my_errno);
- goto err;
- }
- net_flush(&thd->net);
- if ((error= table->file->dump(thd,-1)))
- my_error(ER_GET_ERRNO, MYF(0), error);
-
-err:
- DBUG_RETURN(error);
-}
-
-/**
- Ends the current transaction and (maybe) begin the next.
-
- @param thd Current thread
- @param completion Completion type
-
- @retval
- 0 OK
-*/
-
-int end_trans(THD *thd, enum enum_mysql_completiontype completion)
-{
- bool do_release= 0;
- int res= 0;
- DBUG_ENTER("end_trans");
-
- if (unlikely(thd->in_sub_stmt))
- {
- my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
- DBUG_RETURN(1);
- }
- if (thd->transaction.xid_state.xa_state != XA_NOTR)
- {
- my_error(ER_XAER_RMFAIL, MYF(0),
- xa_state_names[thd->transaction.xid_state.xa_state]);
- DBUG_RETURN(1);
- }
- thd->lex->start_transaction_opt= 0; /* for begin_trans() */
- switch (completion) {
- case COMMIT:
- /*
- We don't use end_active_trans() here to ensure that this works
- even if there is a problem with the OPTION_AUTO_COMMIT flag
- (Which of course should never happen...)
- */
- thd->server_status&= ~SERVER_STATUS_IN_TRANS;
- res= ha_commit(thd);
- thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
- thd->transaction.all.modified_non_trans_table= FALSE;
- break;
- case COMMIT_RELEASE:
- do_release= 1; /* fall through */
- case COMMIT_AND_CHAIN:
- res= end_active_trans(thd);
- if (!res && completion == COMMIT_AND_CHAIN)
- res= begin_trans(thd);
- break;
- case ROLLBACK_RELEASE:
- do_release= 1; /* fall through */
- case ROLLBACK:
- case ROLLBACK_AND_CHAIN:
- {
- thd->server_status&= ~SERVER_STATUS_IN_TRANS;
- if (ha_rollback(thd))
- res= -1;
- thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
- thd->transaction.all.modified_non_trans_table= FALSE;
- if (!res && (completion == ROLLBACK_AND_CHAIN))
- res= begin_trans(thd);
- break;
- }
- default:
- res= -1;
- my_error(ER_UNKNOWN_COM_ERROR, MYF(0));
- DBUG_RETURN(-1);
- }
-
- if (res < 0)
- my_error(thd->killed_errno(), MYF(0));
- else if ((res == 0) && do_release)
- {
- thd->killed= KILL_CONNECTION;
- if (global_system_variables.log_warnings > 3)
- {
- Security_context *sctx= &thd->main_security_ctx;
- sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
- thd->thread_id,(thd->db ? thd->db : "unconnected"),
- sctx->user ? sctx->user : "unauthenticated",
- sctx->host_or_ip, "RELEASE");
- }
- }
-
- DBUG_RETURN(res);
-}
-
#ifndef EMBEDDED_LIBRARY
/**
@@ -847,26 +702,41 @@ bool do_command(THD *thd)
This thread will do a blocking read from the client which
will be interrupted when the next command is received from
the client, the connection is closed or "net_wait_timeout"
- number of seconds has passed
+ number of seconds has passed.
*/
- my_net_set_read_timeout(net, thd->variables.net_wait_timeout);
+ if(!thd->skip_wait_timeout)
+ my_net_set_read_timeout(net, thd->variables.net_wait_timeout);
+
/*
XXX: this code is here only to clear possible errors of init_connect.
Consider moving to init_connect() instead.
*/
thd->clear_error(); // Clear error message
- thd->main_da.reset_diagnostics_area();
+ thd->stmt_da->reset_diagnostics_area();
net_new_transaction(net);
/* Save for user statistics */
thd->start_bytes_received= thd->status_var.bytes_received;
- packet_length= my_net_read(net);
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
- thd->profiling.start_new_query();
-#endif
- if (packet_length == packet_error)
+
+ /*
+ Synchronization point for testing of KILL_CONNECTION.
+ This sync point can wait here, to simulate slow code execution
+ between the last test of thd->killed and blocking in read().
+
+ The goal of this test is to verify that a connection does not
+ hang, if it is killed at this point of execution.
+ (Bug#37780 - main.kill fails randomly)
+
+ Note that the sync point wait itself will be terminated by a
+ kill. In this case it consumes a condition broadcast, but does
+ not change anything else. The consumed broadcast should not
+ matter here, because the read/recv() below doesn't use it.
+ */
+ DEBUG_SYNC(thd, "before_do_command_net_read");
+
+ if ((packet_length= my_net_read(net)) == packet_error)
{
DBUG_PRINT("info",("Got error %d reading command from socket %s",
net->error,
@@ -876,7 +746,7 @@ bool do_command(THD *thd)
/* The error must be set. */
DBUG_ASSERT(thd->is_error());
- net_end_statement(thd);
+ thd->protocol->end_statement();
if (net->error != 3)
{
@@ -923,9 +793,6 @@ bool do_command(THD *thd)
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
out:
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
- thd->profiling.finish_current_query();
-#endif
DBUG_RETURN(return_value);
}
#endif /* EMBEDDED_LIBRARY */
@@ -1027,6 +894,13 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
DBUG_ENTER("dispatch_command");
DBUG_PRINT("info", ("command: %d", command));
+#if defined(ENABLED_PROFILING)
+ thd->profiling.start_new_query();
+#endif
+ MYSQL_COMMAND_START(thd->thread_id, command,
+ &thd->security_ctx->priv_user[0],
+ (char *) thd->security_ctx->host_or_ip);
+
DBUG_EXECUTE_IF("crash_dispatch_command_before",
{ DBUG_PRINT("crash_dispatch_command_before", ("now"));
DBUG_ABORT(); });
@@ -1039,30 +913,26 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->enable_slow_log= TRUE;
thd->query_plan_flags= QPLAN_INIT;
thd->lex->sql_command= SQLCOM_END; /* to avoid confusing VIEW detectors */
- thd->set_time();
- VOID(pthread_mutex_lock(&LOCK_thread_count));
- thd->query_id= global_query_id;
+ DEBUG_SYNC(thd,"dispatch_command_before_set_time");
- switch( command ) {
- /* Ignore these statements. */
- case COM_STATISTICS:
- case COM_PING:
- break;
- /* Only increase id on these statements but don't count them. */
- case COM_STMT_PREPARE:
- case COM_STMT_CLOSE:
- case COM_STMT_RESET:
- next_query_id();
- break;
- /* Increase id and count all other statements. */
- default:
+ thd->set_time();
+ if (server_command_flags[command] & CF_SKIP_QUERY_ID)
+ thd->set_query_id(get_query_id());
+ else
+ thd->set_query_id(next_query_id());
+ inc_thread_running();
+
+ if (!(server_command_flags[command] & CF_SKIP_QUESTIONS))
statistic_increment(thd->status_var.questions, &LOCK_status);
- next_query_id();
- }
- thread_running++;
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ /* Copy data for user stats */
+ if ((thd->userstat_running= opt_userstat_running))
+ {
+ thd->start_cpu_time= my_getcputime();
+ memcpy(&thd->org_status_var, &thd->status_var, sizeof(thd->status_var));
+ thd->select_commands= thd->update_commands= thd->other_commands= 0;
+ }
/**
Clear the set of flags that are expected to be cleared at the
@@ -1091,43 +961,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break;
}
#endif
- case COM_TABLE_DUMP:
- {
- LEX_STRING db, table;
- /* Safe because there is always a trailing \0 at the end of the packet */
- uint db_len= *(uchar*) packet;
- if (db_len + 1 > packet_length || db_len > NAME_LEN)
- {
- my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
- break;
- }
- /* Safe because there is always a trailing \0 at the end of the packet */
- uint tbl_len= *(uchar*) (packet + db_len + 1);
- if (db_len + tbl_len + 2 > packet_length || tbl_len > NAME_LEN)
- {
- my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
- break;
- }
-
- status_var_increment(thd->status_var.com_other);
- thd->enable_slow_log= opt_log_slow_admin_statements;
- thd->query_plan_flags|= QPLAN_ADMIN;
- db.str= (char*) thd->alloc(db_len + tbl_len + 2);
- if (!db.str)
- {
- my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
- break;
- }
- db.length= db_len;
- table.length= tbl_len;
- table.str= strmake(db.str, packet + 1, db_len) + 1;
- strmake(table.str, packet + db_len + 2, tbl_len);
- if (mysql_table_dump(thd, &db, &table) == 0)
- thd->main_da.disable_status();
- break;
- }
case COM_CHANGE_USER:
{
+ bool rc;
status_var_increment(thd->status_var.com_other);
thd->change_user();
@@ -1138,7 +974,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
uint save_db_length= thd->db_length;
char *save_db= thd->db;
- int rc;
USER_CONN *save_user_connect= thd->user_connect;
Security_context save_security_ctx= *thd->security_ctx;
CHARSET_INFO *save_character_set_client=
@@ -1164,10 +999,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
else
rc= acl_authenticate(thd, 0, packet_length);
+ MYSQL_AUDIT_NOTIFY_CONNECTION_CHANGE_USER(thd);
if (rc)
{
/* Free user if allocated by acl_authenticate */
- x_free(thd->security_ctx->user);
+ my_free(thd->security_ctx->user);
*thd->security_ctx= save_security_ctx;
if (thd->user_connect)
decrease_user_connections(thd->user_connect);
@@ -1187,10 +1023,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 */
- pthread_mutex_lock(&thd->LOCK_thd_data);
- x_free(save_db);
- pthread_mutex_unlock(&thd->LOCK_thd_data);
- x_free(save_security_ctx.user);
+ my_free(save_db);
+ my_free(save_security_ctx.user);
}
break;
}
@@ -1228,39 +1062,43 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
if (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->security_ctx->priv_user[0],
+ (char *) thd->security_ctx->host_or_ip);
char *packet_end= thd->query() + thd->query_length();
- /* 'b' stands for 'buffer' parameter', special for 'my_snprintf' */
- const char* end_of_stmt= NULL;
-
general_log_write(thd, command, thd->query(), thd->query_length());
DBUG_PRINT("query",("%-.4096s",thd->query()));
-
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+#if defined(ENABLED_PROFILING)
thd->profiling.set_query_source(thd->query(), thd->query_length());
#endif
+ Parser_state parser_state;
+ if (parser_state.init(thd, thd->query(), thd->query_length()))
+ break;
- if (!(specialflag & SPECIAL_NO_PRIOR))
- my_pthread_setprio(pthread_self(),QUERY_PRIOR);
-
- mysql_parse(thd, thd->query(), thd->query_length(), &end_of_stmt);
+ mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);
- while (!thd->killed && (end_of_stmt != NULL) && ! thd->is_error())
+ while (!thd->killed && (parser_state.m_lip.found_semicolon != NULL) &&
+ ! thd->is_error())
{
- char *beginning_of_next_stmt= (char*) end_of_stmt;
+ /*
+ Multiple queries exist, execute them individually
+ */
+ char *beginning_of_next_stmt= (char*) parser_state.m_lip.found_semicolon;
#ifdef WITH_ARIA_STORAGE_ENGINE
- if (ha_storage_engine_is_enabled(maria_hton))
- ha_maria::implicit_commit(thd, FALSE);
+ ha_maria::implicit_commit(thd, FALSE);
#endif
- /*
- Multiple queries exits, execute them individually
- */
- close_thread_tables(thd);
-
- net_end_statement(thd);
+ /* Finalize server status flags after executing a statement. */
+ thd->update_server_status();
+ thd->protocol->end_statement();
query_cache_end_of_result(thd);
+ mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_STATUS,
+ thd->stmt_da->is_error() ? thd->stmt_da->sql_errno()
+ : 0, command_name[command].str);
+
ulong length= (ulong)(packet_end - beginning_of_next_stmt);
log_slow_statement(thd);
@@ -1272,27 +1110,34 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
length--;
}
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+ if (MYSQL_QUERY_DONE_ENABLED())
+ {
+ MYSQL_QUERY_DONE(thd->is_error());
+ }
+
+#if defined(ENABLED_PROFILING)
thd->profiling.finish_current_query();
thd->profiling.start_new_query("continuing");
thd->profiling.set_query_source(beginning_of_next_stmt, length);
#endif
- thd->set_query(beginning_of_next_stmt, length);
+ MYSQL_QUERY_START(beginning_of_next_stmt, thd->thread_id,
+ (char *) (thd->db ? thd->db : ""),
+ &thd->security_ctx->priv_user[0],
+ (char *) thd->security_ctx->host_or_ip);
+
+ thd->set_query_and_id(beginning_of_next_stmt, length,
+ thd->charset(), next_query_id());
/*
Count each statement from the client.
*/
statistic_increment(thd->status_var.questions, &LOCK_status);
- thd->set_time(); /* Reset the query start time for next query. */
- VOID(pthread_mutex_lock(&LOCK_thread_count));
- thd->query_id= next_query_id();
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ 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 */
- mysql_parse(thd, beginning_of_next_stmt, length, &end_of_stmt);
+ mysql_parse(thd, beginning_of_next_stmt, length, &parser_state);
}
- if (!(specialflag & SPECIAL_NO_PRIOR))
- my_pthread_setprio(pthread_self(),WAIT_PRIOR);
DBUG_PRINT("info",("query ready"));
break;
}
@@ -1303,78 +1148,100 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break;
#else
{
- char *fields, *packet_end= packet + packet_length, *wildcard;
+ char *fields, *packet_end= packet + packet_length, *arg_end;
/* Locked closure of all tables */
TABLE_LIST table_list;
- char db_buff[SAFE_NAME_LEN+1];
- uint32 db_length;
- uint dummy_errors, query_length;
-
- /* used as fields initializator */
- lex_start(thd);
+ LEX_STRING table_name;
+ LEX_STRING db;
+ /*
+ SHOW statements should not add the used tables to the list of tables
+ used in a transaction.
+ */
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS]);
- bzero((char*) &table_list,sizeof(table_list));
- if (thd->copy_db_to(&table_list.db, &table_list.db_length))
+ if (thd->copy_db_to(&db.str, &db.length))
break;
/*
We have name + wildcard in packet, separated by endzero
(The packet is guaranteed to end with an end zero)
*/
- wildcard= strend(packet);
- db_length= wildcard - packet;
- wildcard++;
- query_length= (uint) (packet_end - wildcard); // Don't count end \0
- if (db_length > SAFE_NAME_LEN || query_length > NAME_LEN)
+ arg_end= strend(packet);
+ uint arg_length= arg_end - packet;
+
+ /* Check given table name length. */
+ if (packet_length - arg_length > NAME_LEN + 1 || arg_length > SAFE_NAME_LEN)
{
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
break;
}
- db_length= copy_and_convert(db_buff, sizeof(db_buff)-1,
- system_charset_info, packet, db_length,
- thd->charset(), &dummy_errors);
- db_buff[db_length]= '\0';
- if (check_table_name(db_buff, db_length, FALSE))
+ thd->convert_string(&table_name, system_charset_info,
+ packet, arg_length, thd->charset());
+ if (check_table_name(table_name.str, table_name.length, FALSE))
{
- my_error(ER_WRONG_TABLE_NAME, MYF(0), db_buff);
+ /* this is OK due to convert_string() null-terminating the string */
+ my_error(ER_WRONG_TABLE_NAME, MYF(0), table_name.str);
break;
}
- table_list.alias= table_list.table_name= db_buff;
- if (!(fields= (char *) thd->memdup(wildcard, query_length + 1)))
- break;
+ packet= arg_end + 1;
+ mysql_reset_thd_for_next_command(thd);
+ lex_start(thd);
+ /* Must be before we init the table list. */
+ if (lower_case_table_names)
+ table_name.length= my_casedn_str(files_charset_info, table_name.str);
+ table_list.init_one_table(db.str, db.length, table_name.str,
+ table_name.length, table_name.str, TL_READ);
+ /*
+ Init TABLE_LIST members necessary when the undelrying
+ table is view.
+ */
+ table_list.select_lex= &(thd->lex->select_lex);
+ thd->lex->
+ select_lex.table_list.link_in_list(&table_list,
+ &table_list.next_local);
+ thd->lex->add_to_query_tables(&table_list);
- if (is_schema_db(table_list.db, table_list.db_length))
+ if (is_infoschema_db(table_list.db, table_list.db_length))
{
ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, table_list.alias);
if (schema_table)
table_list.schema_table= schema_table;
}
+ uint query_length= (uint) (packet_end - packet); // Don't count end \0
+ 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);
- if (lower_case_table_names)
- my_casedn_str(files_charset_info, table_list.table_name);
- if (check_access(thd,SELECT_ACL,table_list.db,&table_list.grant.privilege,
- 0, 0, test(table_list.schema_table)))
- break;
- if (check_grant(thd, SELECT_ACL, &table_list, 2, UINT_MAX, 0))
+ if (check_table_access(thd, SELECT_ACL, &table_list,
+ TRUE, UINT_MAX, FALSE))
break;
- /* init structures for VIEW processing */
- table_list.select_lex= &(thd->lex->select_lex);
-
- lex_start(thd);
- mysql_reset_thd_for_next_command(thd, opt_userstat_running);
-
- thd->lex->
- select_lex.table_list.link_in_list(&table_list,
- &table_list.next_local);
- thd->lex->add_to_query_tables(&table_list);
-
- /* switch on VIEW optimisation: do not fill temporary tables */
+ /*
+ Turn on an optimization relevant if the underlying table
+ is a view: do not fill derived tables.
+ */
thd->lex->sql_command= SQLCOM_SHOW_FIELDS;
+
mysqld_list_fields(thd,&table_list,fields);
thd->lex->unit.cleanup();
+ /* No need to rollback statement transaction, it's not started. */
+ DBUG_ASSERT(thd->transaction.stmt.is_empty());
+ close_thread_tables(thd);
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+
+ if (thd->transaction_rollback_request)
+ {
+ /*
+ Transaction rollback was requested since MDL deadlock was
+ discovered while trying to open tables. Rollback transaction
+ in all storage engines including binary log and release all
+ locks.
+ */
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+
thd->cleanup_after_query();
break;
}
@@ -1383,10 +1250,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
/* We don't calculate statistics for this command */
general_log_print(thd, command, NullS);
net->error=0; // Don't give 'abort' message
- thd->main_da.disable_status(); // Don't send anything back
+ thd->stmt_da->disable_status(); // Don't send anything back
error=TRUE; // End server
break;
-
#ifndef EMBEDDED_LIBRARY
case COM_BINLOG_DUMP:
{
@@ -1395,6 +1261,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
uint32 slave_server_id;
status_var_increment(thd->status_var.com_other);
+
thd->enable_slow_log= opt_log_slow_admin_statements;
thd->query_plan_flags|= QPLAN_ADMIN;
if (check_global_access(thd, REPL_SLAVE_ACL))
@@ -1430,6 +1297,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
status_var_increment(thd->status_var.com_stat[SQLCOM_FLUSH]);
ulong options= (ulong) (uchar) packet[0];
+ if (trans_commit_implicit(thd))
+ break;
+ thd->mdl_context.release_transactional_locks();
if (check_global_access(thd,RELOAD_ACL))
break;
general_log_print(thd, command, NullS);
@@ -1449,13 +1319,18 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
res= reload_acl_and_cache(NULL, options | REFRESH_FAST,
NULL, &not_used);
my_pthread_setspecific_ptr(THR_THD, thd);
- if (!res)
- my_ok(thd);
- break;
+ if (res)
+ break;
}
+ else
#endif
- if (!reload_acl_and_cache(thd, options, NULL, &not_used))
- my_ok(thd);
+ if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, &not_used))
+ break;
+ if (trans_commit_implicit(thd))
+ break;
+ close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
+ my_ok(thd);
break;
}
#ifndef EMBEDDED_LIBRARY
@@ -1482,7 +1357,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
DBUG_PRINT("quit",("Got shutdown command for level %u", level));
general_log_print(thd, command, NullS);
my_eof(thd);
- close_thread_tables(thd); // Free before kill
kill_mysql();
error=TRUE;
break;
@@ -1492,9 +1366,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
STATUS_VAR *current_global_status_var; // Big; Don't allocate on stack
ulong uptime;
-#if defined(SAFEMALLOC) || !defined(EMBEDDED_LIBRARY)
- uint length;
-#endif
+ uint length __attribute__((unused));
ulonglong queries_per_second1000;
char buff[250];
uint buff_len= sizeof(buff);
@@ -1510,38 +1382,25 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
else
queries_per_second1000= thd->query_id * LL(1000) / uptime;
-#if defined(SAFEMALLOC) || !defined(EMBEDDED_LIBRARY)
- length=
-#endif
- my_snprintf((char*) buff, buff_len - 1,
- "Uptime: %lu Threads: %d Questions: %lu "
- "Slow queries: %lu Opens: %lu Flush tables: %lu "
- "Open tables: %u Queries per second avg: %u.%u",
- uptime,
- (int) thread_count, (ulong) thd->query_id,
- current_global_status_var->long_query_count,
- current_global_status_var->opened_tables,
- refresh_version,
- cached_open_tables(),
- (uint) (queries_per_second1000 / 1000),
- (uint) (queries_per_second1000 % 1000));
-#ifdef SAFEMALLOC
- if (sf_malloc_cur_memory) // Using SAFEMALLOC
- {
- char *end= buff + length;
- length+= my_snprintf(end, buff_len - length - 1,
- " Memory in use: %ldK Max memory used: %ldK",
- (sf_malloc_cur_memory+1023L)/1024L,
- (sf_malloc_max_memory+1023L)/1024L);
- }
-#endif
-#ifndef EMBEDDED_LIBRARY
- VOID(my_net_write(net, (uchar*) buff, length));
- VOID(net_flush(net));
- thd->main_da.disable_status();
-#else
+ length= my_snprintf(buff, buff_len - 1,
+ "Uptime: %lu Threads: %d Questions: %lu "
+ "Slow queries: %lu Opens: %lu Flush tables: %lu "
+ "Open tables: %u Queries per second avg: %u.%03u",
+ uptime,
+ (int) thread_count, (ulong) thd->query_id,
+ current_global_status_var->long_query_count,
+ current_global_status_var->opened_tables,
+ refresh_version,
+ cached_open_tables(),
+ (uint) (queries_per_second1000 / 1000),
+ (uint) (queries_per_second1000 % 1000));
+#ifdef EMBEDDED_LIBRARY
/* Store the buffer in permanent memory */
my_ok(thd, 0, 0, buff);
+#else
+ (void) my_net_write(net, (uchar*) buff, length);
+ (void) net_flush(net);
+ thd->stmt_da->disable_status();
#endif
break;
}
@@ -1603,65 +1462,51 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
break;
}
+ DBUG_ASSERT(thd->derived_tables == NULL &&
+ (thd->open_tables == NULL ||
+ (thd->locked_tables_mode == LTM_LOCK_TABLES)));
- /* report error issued during command execution */
- if (thd->killed)
- {
- if (thd->killed_errno())
- {
- if (! thd->main_da.is_set())
- thd->send_kill_message();
- }
- if (thd->killed < KILL_CONNECTION)
- {
- thd->killed= NOT_KILLED;
- thd->mysys_var->abort= 0;
- }
- }
-
- /* If commit fails, we should be able to reset the OK status. */
- thd->main_da.can_overwrite_status= TRUE;
- ha_autocommit_or_rollback(thd, thd->is_error());
- thd->main_da.can_overwrite_status= FALSE;
-
- thd->transaction.stmt.reset();
+ thd_proc_info(thd, "updating status");
+ /* Finalize server status flags after executing a command. */
+ thd->update_server_status();
+ thd->protocol->end_statement();
+ query_cache_end_of_result(thd);
-#ifdef WITH_ARIA_STORAGE_ENGINE
- if (ha_storage_engine_is_enabled(maria_hton))
- ha_maria::implicit_commit(thd, FALSE);
-#endif
+ if (!thd->is_error() && !thd->killed_errno())
+ mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_RESULT, 0, 0);
- if (!(sql_command_flags[thd->lex->sql_command] & CF_CHANGES_DATA))
- {
- /* No changes in data; We can send ok at once to the client */
- net_end_statement(thd);
- query_cache_end_of_result(thd);
- }
- thd->proc_info= "closing tables";
- /* Free tables */
- close_thread_tables(thd);
+ mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_STATUS,
+ thd->stmt_da->is_error() ? thd->stmt_da->sql_errno() : 0,
+ command_name[command].str);
- /* Update status; Must be done after close_thread_tables */
thd->update_all_stats();
- if (sql_command_flags[thd->lex->sql_command] & CF_CHANGES_DATA)
- {
- net_end_statement(thd);
- query_cache_end_of_result(thd);
- }
-
log_slow_statement(thd);
thd_proc_info(thd, "cleaning up");
- thd->set_query(NULL, 0);
+ thd->reset_query();
thd->command=COM_SLEEP;
- VOID(pthread_mutex_lock(&LOCK_thread_count)); // For process list
- thread_running--;
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ thd->set_time();
+ dec_thread_running();
thd_proc_info(thd, 0);
thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
+#if defined(ENABLED_PROFILING)
+ thd->profiling.finish_current_query();
+#endif
+ if (MYSQL_QUERY_DONE_ENABLED() || MYSQL_COMMAND_DONE_ENABLED())
+ {
+ int res __attribute__((unused));
+ res= (int) thd->is_error();
+ if (command == COM_QUERY)
+ {
+ MYSQL_QUERY_DONE(res);
+ }
+ MYSQL_COMMAND_DONE(res);
+ }
+ DEBUG_SYNC(thd,"dispatch_command_end");
+
/* Check that some variables are reset properly */
DBUG_ASSERT(thd->abort_on_warning == 0);
DBUG_RETURN(error);
@@ -1681,41 +1526,31 @@ void log_slow_statement(THD *thd)
DBUG_VOID_RETURN; // Don't set time for sub stmt
/* Follow the slow log filter configuration. */
- DBUG_ASSERT(thd->variables.log_slow_filter != 0);
- if (!(thd->variables.log_slow_filter & thd->query_plan_flags))
+ if (!thd->enable_slow_log ||
+ (thd->variables.log_slow_filter
+ && !(thd->variables.log_slow_filter & thd->query_plan_flags)))
DBUG_VOID_RETURN;
- /*
- If rate limiting of slow log writes is enabled, decide whether to log
- this query to the log or not.
- */
- if (thd->variables.log_slow_rate_limit > 1 &&
- (global_query_id % thd->variables.log_slow_rate_limit) != 0)
- DBUG_VOID_RETURN;
+ if (((thd->server_status & SERVER_QUERY_WAS_SLOW) ||
+ ((thd->server_status &
+ (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) &&
+ opt_log_queries_not_using_indexes &&
+ !(sql_command_flags[thd->lex->sql_command] & CF_STATUS_COMMAND))) &&
+ thd->examined_row_count >= thd->variables.min_examined_row_limit)
+ {
+ thd->status_var.long_query_count++;
+ /*
+ If rate limiting of slow log writes is enabled, decide whether to log
+ this query to the log or not.
+ */
+ if (thd->variables.log_slow_rate_limit > 1 &&
+ (global_query_id % thd->variables.log_slow_rate_limit) != 0)
+ DBUG_VOID_RETURN;
- /*
- Do not log administrative statements unless the appropriate option is
- set.
- */
- if (thd->enable_slow_log)
- {
- ulonglong end_utime_of_query= thd->current_utime();
thd_proc_info(thd, "logging slow query");
-
- if ((((end_utime_of_query > thd->utime_after_lock) &&
- ((end_utime_of_query - thd->utime_after_lock) >
- thd->variables.long_query_time)) ||
- ((thd->server_status &
- (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) &&
- opt_log_queries_not_using_indexes &&
- !(sql_command_flags[thd->lex->sql_command] & CF_STATUS_COMMAND))) &&
- thd->examined_row_count >= thd->variables.min_examined_row_limit)
- {
- thd_proc_info(thd, "logging slow query");
- thd->status_var.long_query_count++;
- slow_log_print(thd, thd->query(), thd->query_length(),
- end_utime_of_query);
- }
+ slow_log_print(thd, thd->query(), thd->query_length(),
+ thd->utime_after_query);
+ thd_proc_info(thd, 0);
}
DBUG_VOID_RETURN;
}
@@ -1808,7 +1643,8 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
/* 'parent_lex' is used in init_query() so it must be before it. */
schema_select_lex->parent_lex= lex;
schema_select_lex->init_query();
- if (!schema_select_lex->add_table_to_list(thd, table_ident, 0, 0, TL_READ))
+ if (!schema_select_lex->add_table_to_list(thd, table_ident, 0, 0, TL_READ,
+ MDL_SHARED_READ))
DBUG_RETURN(1);
lex->query_tables_last= query_tables_last;
break;
@@ -1819,13 +1655,13 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
Mark this current profiling record to be discarded. We don't
wish to have SHOW commands show up in profiling.
*/
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+#if defined(ENABLED_PROFILING)
thd->profiling.discard_current_query();
#endif
break;
case SCH_USER_STATS:
case SCH_CLIENT_STATS:
- if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
+ if (check_global_access(thd, SUPER_ACL | PROCESS_ACL, true))
DBUG_RETURN(1);
case SCH_TABLE_STATS:
case SCH_INDEX_STATS:
@@ -2000,7 +1836,7 @@ bool sp_process_definer(THD *thd)
if ((strcmp(lex->definer->user.str, thd->security_ctx->priv_user) ||
my_strcasecmp(system_charset_info, lex->definer->host.str,
thd->security_ctx->priv_host)) &&
- check_global_access(thd, SUPER_ACL))
+ check_global_access(thd, SUPER_ACL, true))
{
my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
DBUG_RETURN(TRUE);
@@ -2026,17 +1862,68 @@ bool sp_process_definer(THD *thd)
/**
- Execute command saved in thd and lex->sql_command.
+ Auxiliary call that opens and locks tables for LOCK TABLES statement
+ and initializes the list of locked tables.
+
+ @param thd Thread context.
+ @param tables List of tables to be locked.
+
+ @return FALSE in case of success, TRUE in case of error.
+*/
+
+static bool lock_tables_open_and_lock_tables(THD *thd, TABLE_LIST *tables)
+{
+ Lock_tables_prelocking_strategy lock_tables_prelocking_strategy;
+ uint counter;
+ TABLE_LIST *table;
+
+ thd->in_lock_tables= 1;
+
+ if (open_tables(thd, &tables, &counter, 0, &lock_tables_prelocking_strategy))
+ goto err;
- Before every operation that can request a write lock for a table
- wait if a global read lock exists. However do not wait if this
- thread has locked tables already. No new locks can be requested
- until the other locks are released. The thread that requests the
- global read lock waits for write locked tables to become unlocked.
+ /*
+ We allow to change temporary tables even if they were locked for read
+ by LOCK TABLES. To avoid a discrepancy between lock acquired at LOCK
+ TABLES time and by the statement which is later executed under LOCK TABLES
+ we ensure that for temporary tables we always request a write lock (such
+ discrepancy can cause problems for the storage engine).
+ We don't set TABLE_LIST::lock_type in this case as this might result in
+ extra warnings from THD::decide_logging_format() even though binary logging
+ is totally irrelevant for LOCK TABLES.
+ */
+ for (table= tables; table; table= table->next_global)
+ if (!table->placeholder() && table->table->s->tmp_table)
+ table->table->reginfo.lock_type= TL_WRITE;
+
+ if (lock_tables(thd, tables, counter, 0) ||
+ thd->locked_tables_list.init_locked_tables(thd))
+ goto err;
+
+ thd->in_lock_tables= 0;
+
+ return FALSE;
- Note that wait_if_global_read_lock() sets a protection against a new
- global read lock when it succeeds. This needs to be released by
- start_waiting_global_read_lock() after the operation.
+err:
+ thd->in_lock_tables= 0;
+
+ trans_rollback_stmt(thd);
+ /*
+ Need to end the current transaction, so the storage engine (InnoDB)
+ can free its locks if LOCK TABLES locked some tables before finding
+ that it can't lock a table in its list
+ */
+ trans_rollback(thd);
+ /* Close tables and release metadata locks. */
+ close_thread_tables(thd);
+ DBUG_ASSERT(!thd->locked_tables_mode);
+ thd->mdl_context.release_transactional_locks();
+ return TRUE;
+}
+
+
+/**
+ Execute command saved in thd and lex->sql_command.
@param thd Thread handle
@@ -2046,8 +1933,6 @@ bool sp_process_definer(THD *thd)
TODO: this is workaround. right way will be move invalidating in
the unlock procedure.
- TODO: use check_change_password()
- - JOIN is not supported yet. TODO
- - SUSPEND and FOR MIGRATE are not supported yet. TODO
@retval
FALSE OK
@@ -2059,7 +1944,6 @@ int
mysql_execute_command(THD *thd)
{
int res= FALSE;
- bool need_start_waiting= FALSE; // have protection against global read lock
int up_result= 0;
LEX *lex= thd->lex;
/* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
@@ -2074,12 +1958,20 @@ mysql_execute_command(THD *thd)
/* have table map for update for multi-update statement (BUG#37051) */
bool have_table_map_for_update= FALSE;
#endif
- /* Saved variable value */
DBUG_ENTER("mysql_execute_command");
+
#ifdef WITH_PARTITION_STORAGE_ENGINE
thd->work_part_info= 0;
#endif
+ DBUG_ASSERT(thd->transaction.stmt.is_empty() || thd->in_sub_stmt);
+ /*
+ Each statement or replication event which might produce deadlock
+ should handle transaction rollback on its own. So by the start of
+ the next statement transaction rollback request should be fulfilled
+ already.
+ */
+ DBUG_ASSERT(! thd->transaction_rollback_request || thd->in_sub_stmt);
/*
In many cases first table of main SELECT_LEX have special meaning =>
check that it is first table in global list and relink it first in
@@ -2108,10 +2000,15 @@ mysql_execute_command(THD *thd)
A better approach would be to reset this for any commands
that is not a SHOW command or a select that only access local
variables, but for now this is probably good enough.
- Don't reset warnings when executing a stored routine.
*/
- if ((all_tables || !lex->is_single_level_stmt()) && !thd->spcont)
- mysql_reset_errors(thd, 0);
+ if ((sql_command_flags[lex->sql_command] & CF_DIAGNOSTIC_STMT) != 0)
+ thd->warning_info->set_read_only(TRUE);
+ else
+ {
+ thd->warning_info->set_read_only(FALSE);
+ if (all_tables)
+ thd->warning_info->opt_clear_warning_info(thd->query_id);
+ }
#ifdef HAVE_REPLICATION
if (unlikely(thd->slave_thread))
@@ -2243,12 +2140,40 @@ mysql_execute_command(THD *thd)
#ifdef HAVE_REPLICATION
} /* endif unlikely slave */
#endif
+
status_var_increment(thd->status_var.com_stat[lex->sql_command]);
thd->progress.report_to_client= test(sql_command_flags[lex->sql_command] &
CF_REPORT_PROGRESS);
DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table == FALSE);
-
+
+ /*
+ End a active transaction so that this command will have it's
+ own transaction and will also sync the binary log. If a DDL is
+ not run in it's own transaction it may simply never appear on
+ the slave in case the outside transaction rolls back.
+ */
+ if (stmt_causes_implicit_commit(thd, CF_IMPLICT_COMMIT_BEGIN))
+ {
+ /*
+ Note that this should never happen inside of stored functions
+ or triggers as all such statements prohibited there.
+ */
+ DBUG_ASSERT(! thd->in_sub_stmt);
+ /* Statement transaction still should not be started. */
+ DBUG_ASSERT(thd->transaction.stmt.is_empty());
+ /* Commit the normal transaction if one is active. */
+ if (trans_commit_implicit(thd))
+ goto error;
+ /* Release metadata locks acquired in this transaction. */
+ thd->mdl_context.release_transactional_locks();
+ }
+
+#ifndef DBUG_OFF
+ if (lex->sql_command != SQLCOM_SET_OPTION)
+ DEBUG_SYNC(thd,"before_execute_sql_command");
+#endif
+
switch (lex->sql_command) {
case SQLCOM_SHOW_EVENTS:
@@ -2258,8 +2183,10 @@ mysql_execute_command(THD *thd)
#endif
case SQLCOM_SHOW_STATUS_PROC:
case SQLCOM_SHOW_STATUS_FUNC:
- if (!(res= check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE)))
- res= execute_sqlcom_select(thd, all_tables);
+ if ((res= check_table_access(thd, SELECT_ACL, all_tables, FALSE,
+ UINT_MAX, FALSE)))
+ goto error;
+ res= execute_sqlcom_select(thd, all_tables);
break;
case SQLCOM_SHOW_STATUS:
{
@@ -2284,29 +2211,30 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_TABLE_STATS:
case SQLCOM_SHOW_INDEX_STATS:
case SQLCOM_SELECT:
+ {
thd->status_var.last_query_cost= 0.0;
+
+ /*
+ lex->exchange != NULL implies SELECT .. INTO OUTFILE and this
+ requires FILE_ACL access.
+ */
+ ulong privileges_requested= lex->exchange ? SELECT_ACL | FILE_ACL :
+ SELECT_ACL;
+
if (all_tables)
- {
res= check_table_access(thd,
- lex->exchange ? SELECT_ACL | FILE_ACL :
- SELECT_ACL,
- all_tables, UINT_MAX, FALSE);
- }
+ privileges_requested,
+ all_tables, FALSE, UINT_MAX, FALSE);
else
- res= check_access(thd,
- lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
- any_db, 0, 0, 0, 0);
+ res= check_access(thd, privileges_requested, any_db, NULL, NULL, 0, 0);
if (res)
break;
- if (!thd->locked_tables && lex->protect_against_global_read_lock &&
- !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
- break;
-
res= execute_sqlcom_select(thd, all_tables);
break;
- case SQLCOM_PREPARE:
+ }
+case SQLCOM_PREPARE:
{
mysql_sql_stmt_prepare(thd);
break;
@@ -2322,8 +2250,8 @@ mysql_execute_command(THD *thd)
break;
}
case SQLCOM_DO:
- if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) ||
- open_and_lock_tables(thd, all_tables))
+ if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)
+ || open_and_lock_tables(thd, all_tables, TRUE, 0))
goto error;
res= mysql_do(thd, *lex->insert_list);
@@ -2383,7 +2311,7 @@ mysql_execute_command(THD *thd)
}
case SQLCOM_SHOW_PROFILES:
{
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+#if defined(ENABLED_PROFILING)
thd->profiling.discard_current_query();
res= thd->profiling.show_profiles();
if (res)
@@ -2392,20 +2320,7 @@ mysql_execute_command(THD *thd)
my_error(ER_FEATURE_DISABLED, MYF(0), "SHOW PROFILES", "enable-profiling");
goto error;
#endif
- }
- break;
- case SQLCOM_SHOW_NEW_MASTER:
- {
- if (check_global_access(thd, REPL_SLAVE_ACL))
- goto error;
- /* This query don't work now. See comment in repl_failsafe.cc */
-#ifndef WORKING_NEW_MASTER
- my_error(ER_NOT_SUPPORTED_YET, MYF(0), "SHOW NEW MASTER");
- goto error;
-#else
- res = show_new_master(thd);
break;
-#endif
}
#ifdef HAVE_REPLICATION
@@ -2416,6 +2331,7 @@ mysql_execute_command(THD *thd)
res = show_slave_hosts(thd);
break;
}
+ case SQLCOM_SHOW_RELAYLOG_EVENTS: /* fall through */
case SQLCOM_SHOW_BINLOG_EVENTS:
{
if (check_global_access(thd, REPL_SLAVE_ACL))
@@ -2425,38 +2341,13 @@ mysql_execute_command(THD *thd)
}
#endif
- case SQLCOM_BACKUP_TABLE:
- {
- DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) ||
- check_global_access(thd, FILE_ACL))
- goto error; /* purecov: inspected */
- thd->enable_slow_log= opt_log_slow_admin_statements;
- thd->query_plan_flags|= QPLAN_ADMIN;
- res = mysql_backup_table(thd, first_table);
- select_lex->table_list.first= first_table;
- lex->query_tables=all_tables;
- break;
- }
- case SQLCOM_RESTORE_TABLE:
- {
- DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_table_access(thd, INSERT_ACL, all_tables, UINT_MAX, FALSE) ||
- check_global_access(thd, FILE_ACL))
- goto error; /* purecov: inspected */
- thd->enable_slow_log= opt_log_slow_admin_statements;
- thd->query_plan_flags|= QPLAN_ADMIN;
- res = mysql_restore_table(thd, first_table);
- select_lex->table_list.first= first_table;
- lex->query_tables=all_tables;
- break;
- }
case SQLCOM_ASSIGN_TO_KEYCACHE:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
if (check_access(thd, INDEX_ACL, first_table->db,
- &first_table->grant.privilege, 0, 0,
- test(first_table->schema_table)))
+ &first_table->grant.privilege,
+ &first_table->grant.m_internal,
+ 0, 0))
goto error;
res= mysql_assign_to_keycache(thd, first_table, &lex->ident);
break;
@@ -2465,8 +2356,9 @@ mysql_execute_command(THD *thd)
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
if (check_access(thd, INDEX_ACL, first_table->db,
- &first_table->grant.privilege, 0, 0,
- test(first_table->schema_table)))
+ &first_table->grant.privilege,
+ &first_table->grant.m_internal,
+ 0, 0))
goto error;
res = mysql_preload_keys(thd, first_table);
break;
@@ -2476,9 +2368,9 @@ mysql_execute_command(THD *thd)
{
if (check_global_access(thd, SUPER_ACL))
goto error;
- pthread_mutex_lock(&LOCK_active_mi);
+ mysql_mutex_lock(&LOCK_active_mi);
res = change_master(thd,active_mi);
- pthread_mutex_unlock(&LOCK_active_mi);
+ mysql_mutex_unlock(&LOCK_active_mi);
break;
}
case SQLCOM_SHOW_SLAVE_STAT:
@@ -2486,7 +2378,7 @@ mysql_execute_command(THD *thd)
/* Accept one of two privileges */
if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
goto error;
- pthread_mutex_lock(&LOCK_active_mi);
+ mysql_mutex_lock(&LOCK_active_mi);
if (active_mi != NULL)
{
res = show_master_info(thd, active_mi);
@@ -2497,7 +2389,7 @@ mysql_execute_command(THD *thd)
WARN_NO_MASTER_INFO, ER(WARN_NO_MASTER_INFO));
my_ok(thd);
}
- pthread_mutex_unlock(&LOCK_active_mi);
+ mysql_mutex_unlock(&LOCK_active_mi);
break;
}
case SQLCOM_SHOW_MASTER_STAT:
@@ -2509,13 +2401,6 @@ mysql_execute_command(THD *thd)
break;
}
- case SQLCOM_LOAD_MASTER_DATA: // sync with master
- if (check_global_access(thd, SUPER_ACL))
- goto error;
- if (end_active_trans(thd))
- goto error;
- res = load_master_data(thd);
- break;
#endif /* HAVE_REPLICATION */
case SQLCOM_SHOW_ENGINE_STATUS:
{
@@ -2531,51 +2416,13 @@ mysql_execute_command(THD *thd)
res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_MUTEX);
break;
}
-#ifdef HAVE_REPLICATION
- case SQLCOM_LOAD_MASTER_TABLE:
- {
- DBUG_ASSERT(first_table == all_tables && first_table != 0);
- DBUG_ASSERT(first_table->db); /* Must be set in the parser */
-
- if (check_access(thd, CREATE_ACL, first_table->db,
- &first_table->grant.privilege, 0, 0,
- test(first_table->schema_table)))
- goto error; /* purecov: inspected */
- /* Check that the first table has CREATE privilege */
- if (check_grant(thd, CREATE_ACL, all_tables, 0, 1, 0))
- goto error;
-
- pthread_mutex_lock(&LOCK_active_mi);
- /*
- fetch_master_table will send the error to the client on failure.
- Give error if the table already exists.
- */
- if (!fetch_master_table(thd, first_table->db, first_table->table_name,
- active_mi, 0, 0))
- {
- my_ok(thd);
- }
- pthread_mutex_unlock(&LOCK_active_mi);
- break;
- }
-#endif /* HAVE_REPLICATION */
-
case SQLCOM_CREATE_TABLE:
{
- /* If CREATE TABLE of non-temporary table, do implicit commit */
- if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
- {
- if (end_active_trans(thd))
- {
- res= -1;
- break;
- }
- }
DBUG_ASSERT(first_table == all_tables && first_table != 0);
bool link_to_local;
- // Skip first table, which is the table we are creating
- TABLE_LIST *create_table= lex->unlink_first_table(&link_to_local);
- TABLE_LIST *select_tables= lex->query_tables;
+ TABLE_LIST *create_table= first_table;
+ TABLE_LIST *select_tables= lex->create_last_non_select_table->next_global;
+
/*
Code below (especially in mysql_create_table() and select_create
methods) may modify HA_CREATE_INFO structure in LEX, so we have to
@@ -2631,25 +2478,7 @@ mysql_execute_command(THD *thd)
create_info.default_table_charset= create_info.table_charset;
create_info.table_charset= 0;
}
- /*
- The create-select command will open and read-lock the select table
- and then create, open and write-lock the new table. If a global
- read lock steps in, we get a deadlock. The write lock waits for
- the global read lock, while the global read lock waits for the
- select table to be closed. So we wait until the global readlock is
- gone before starting both steps. Note that
- wait_if_global_read_lock() sets a protection against a new global
- read lock when it succeeds. This needs to be released by
- start_waiting_global_read_lock(). We protect the normal CREATE
- TABLE in the same way. That way we avoid that a new table is
- created during a gobal read lock.
- */
- if (!thd->locked_tables &&
- !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
- {
- res= 1;
- goto end_with_restore_list;
- }
+
#ifdef WITH_PARTITION_STORAGE_ENGINE
{
partition_info *part_info= thd->lex->part_info;
@@ -2661,11 +2490,28 @@ mysql_execute_command(THD *thd)
thd->work_part_info= part_info;
}
#endif
+
+ /* Close any open handlers for the table. */
+ mysql_ha_rm_tables(thd, create_table);
+
if (select_lex->item_list.elements) // With select
{
select_result *result;
/*
+ CREATE TABLE...IGNORE/REPLACE SELECT... can be unsafe, unless
+ ORDER BY PRIMARY KEY clause is used in SELECT statement. We therefore
+ use row based logging if mixed or row based logging is available.
+ TODO: Check if the order of the output of the select statement is
+ deterministic. Waiting for BUG#42415
+ */
+ if(lex->ignore)
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_CREATE_IGNORE_SELECT);
+
+ if(lex->duplicates == DUP_REPLACE)
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_CREATE_REPLACE_SELECT);
+
+ /*
If:
a) we inside an SP and there was NAME_CONST substitution,
b) binlogging is on (STMT mode),
@@ -2718,71 +2564,44 @@ mysql_execute_command(THD *thd)
goto end_with_restore_list;
}
- if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
+ res= open_and_lock_tables(thd, lex->query_tables, TRUE, 0);
+ if (res)
{
- lex->link_first_table_back(create_table, link_to_local);
- create_table->create= TRUE;
- /* Base table and temporary table are not in the same name space. */
- create_table->skip_temporary= 1;
+ /* Got error or warning. Set res to 1 if error */
+ if (!(res= thd->is_error()))
+ my_ok(thd); // CREATE ... IF NOT EXISTS
}
-
- if (!(res= open_and_lock_tables(thd, lex->query_tables)))
+ else
{
- /*
- Is table which we are changing used somewhere in other parts
- of query
- */
- if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
+ /* The table already exists */
+ if (create_table->table)
{
- TABLE_LIST *duplicate;
- create_table= lex->unlink_first_table(&link_to_local);
-
- if (create_table->view)
+ if (create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS)
{
- if (create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS)
- {
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_TABLE_EXISTS_ERROR,
- ER(ER_TABLE_EXISTS_ERROR),
- create_info.alias);
- my_ok(thd);
- }
- else
- {
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), create_info.alias);
- res= 1;
- }
- goto end_with_restore_list;
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_TABLE_EXISTS_ERROR,
+ ER(ER_TABLE_EXISTS_ERROR),
+ create_info.alias);
+ my_ok(thd);
}
-
- if ((duplicate= unique_table(thd, create_table, select_tables, 0)))
+ else
{
- update_non_unique_table_error(create_table, "CREATE", duplicate);
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), create_info.alias);
res= 1;
- goto end_with_restore_list;
- }
- }
- /* If we create merge table, we have to test tables in merge, too */
- if (create_info.used_fields & HA_CREATE_USED_UNION)
- {
- TABLE_LIST *tab;
- for (tab= create_info.merge_list.first;
- tab;
- tab= tab->next_local)
- {
- TABLE_LIST *duplicate;
- if ((duplicate= unique_table(thd, tab, select_tables, 0)))
- {
- update_non_unique_table_error(tab, "CREATE", duplicate);
- res= 1;
- goto end_with_restore_list;
- }
}
+ goto end_with_restore_list;
}
+ /*
+ Remove target table from main select and name resolution
+ context. This can't be done earlier as it will break view merging in
+ statements like "CREATE TABLE IF NOT EXISTS existing_view SELECT".
+ */
+ lex->unlink_first_table(&link_to_local);
+
/* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
if (create_info.options & HA_LEX_CREATE_TMP_TABLE)
- thd->options|= OPTION_KEEP_LOG;
+ thd->variables.option_bits|= OPTION_KEEP_LOG;
/*
select_create is currently not re-execution friendly and
@@ -2803,33 +2622,33 @@ mysql_execute_command(THD *thd)
res= handle_select(thd, lex, result, 0);
delete result;
}
- }
- else if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
- create_table= lex->unlink_first_table(&link_to_local);
+ lex->link_first_table_back(create_table, link_to_local);
+ }
}
else
{
/* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
if (create_info.options & HA_LEX_CREATE_TMP_TABLE)
- thd->options|= OPTION_KEEP_LOG;
+ thd->variables.option_bits|= OPTION_KEEP_LOG;
/* regular create */
if (create_info.options & HA_LEX_CREATE_TABLE_LIKE)
+ {
+ /* CREATE TABLE ... LIKE ... */
res= mysql_create_like_table(thd, create_table, select_tables,
&create_info);
+ }
else
{
- res= mysql_create_table(thd, create_table->db,
- create_table->table_name, &create_info,
- &alter_info, 0, 0);
+ /* Regular CREATE TABLE */
+ res= mysql_create_table(thd, create_table,
+ &create_info, &alter_info);
}
if (!res)
- my_ok(thd);
+ my_ok(thd);
}
- /* put tables back for PS rexecuting */
end_with_restore_list:
- lex->link_first_table_back(create_table, link_to_local);
break;
}
case SQLCOM_CREATE_INDEX:
@@ -2854,8 +2673,6 @@ end_with_restore_list:
DBUG_ASSERT(first_table == all_tables && first_table != 0);
if (check_one_table_access(thd, INDEX_ACL, all_tables))
goto error; /* purecov: inspected */
- if (end_active_trans(thd))
- goto error;
/*
Currently CREATE INDEX or DROP INDEX cause a full table rebuild
and thus classify as slow administrative statements just like
@@ -2877,9 +2694,9 @@ end_with_restore_list:
#ifdef HAVE_REPLICATION
case SQLCOM_SLAVE_START:
{
- pthread_mutex_lock(&LOCK_active_mi);
+ mysql_mutex_lock(&LOCK_active_mi);
start_slave(thd,active_mi,1 /* net report*/);
- pthread_mutex_unlock(&LOCK_active_mi);
+ mysql_mutex_unlock(&LOCK_active_mi);
break;
}
case SQLCOM_SLAVE_STOP:
@@ -2896,101 +2713,21 @@ end_with_restore_list:
To prevent that, refuse SLAVE STOP if the
client thread has locked tables
*/
- if (thd->locked_tables || thd->active_transaction() || thd->global_read_lock)
+ if (thd->locked_tables_mode ||
+ thd->in_active_multi_stmt_transaction() || thd->global_read_lock.is_acquired())
{
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
goto error;
}
{
- pthread_mutex_lock(&LOCK_active_mi);
+ mysql_mutex_lock(&LOCK_active_mi);
stop_slave(thd,active_mi,1/* net report*/);
- pthread_mutex_unlock(&LOCK_active_mi);
+ mysql_mutex_unlock(&LOCK_active_mi);
break;
}
#endif /* HAVE_REPLICATION */
- case SQLCOM_ALTER_TABLE:
- {
- ulong priv=0;
- ulong priv_needed= ALTER_ACL;
-
- DBUG_ASSERT(first_table == all_tables && first_table != 0);
-
- /*
- Code in mysql_alter_table() may modify its HA_CREATE_INFO argument,
- so we have to use a copy of this structure to make execution
- prepared statement- safe. A shallow copy is enough as no memory
- referenced from this structure will be modified.
- */
- HA_CREATE_INFO create_info(lex->create_info);
- Alter_info alter_info(lex->alter_info, thd->mem_root);
-
- if (thd->is_fatal_error) /* OOM creating a copy of alter_info */
- goto error;
- /*
- We also require DROP priv for ALTER TABLE ... DROP PARTITION, as well
- as for RENAME TO, as being done by SQLCOM_RENAME_TABLE
- */
- if (alter_info.flags & (ALTER_DROP_PARTITION | ALTER_RENAME))
- priv_needed|= DROP_ACL;
-
- /* Must be set in the parser */
- DBUG_ASSERT(select_lex->db);
- if (check_access(thd, priv_needed, first_table->db,
- &first_table->grant.privilege, 0, 0,
- test(first_table->schema_table)) ||
- check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv,0,0,
- is_schema_db(select_lex->db))||
- check_merge_table_access(thd, first_table->db,
- create_info.merge_list.first))
- goto error; /* purecov: inspected */
- if (check_grant(thd, priv_needed, all_tables, 0, UINT_MAX, 0))
- goto error;
- if (lex->name.str && !test_all_bits(priv,INSERT_ACL | CREATE_ACL))
- { // Rename of table
- TABLE_LIST tmp_table;
- bzero((char*) &tmp_table,sizeof(tmp_table));
- tmp_table.table_name= lex->name.str;
- tmp_table.db=select_lex->db;
- tmp_table.grant.privilege=priv;
- if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, 0,
- UINT_MAX, 0))
- goto error;
- }
-
- /* Don't yet allow changing of symlinks with ALTER TABLE */
- if (create_info.data_file_name)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
- "DATA DIRECTORY");
- if (create_info.index_file_name)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
- "INDEX DIRECTORY");
- create_info.data_file_name= create_info.index_file_name= NULL;
- /* ALTER TABLE ends previous transaction */
- if (end_active_trans(thd))
- goto error;
-
- if (!thd->locked_tables &&
- !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
- {
- res= 1;
- break;
- }
-
- thd->enable_slow_log= opt_log_slow_admin_statements;
- thd->query_plan_flags|= QPLAN_ADMIN;
- res= mysql_alter_table(thd, select_lex->db, lex->name.str,
- &create_info,
- first_table,
- &alter_info,
- select_lex->order_list.elements,
- (ORDER *) select_lex->order_list.first,
- lex->ignore, lex->online);
- break;
- }
case SQLCOM_RENAME_TABLE:
{
if (execute_rename_table(thd, first_table, all_tables))
@@ -3020,11 +2757,54 @@ end_with_restore_list:
goto error;
#else
{
- /* Ignore temporary tables if this is "SHOW CREATE VIEW" */
+ /*
+ Access check:
+ SHOW CREATE TABLE require any privileges on the table level (ie
+ effecting all columns in the table).
+ SHOW CREATE VIEW require the SHOW_VIEW and SELECT ACLs on the table
+ level.
+ NOTE: SHOW_VIEW ACL is checked when the view is created.
+ */
+
+ DBUG_PRINT("debug", ("lex->only_view: %d, table: %s.%s",
+ lex->only_view,
+ first_table->db, first_table->table_name));
if (lex->only_view)
- first_table->skip_temporary= 1;
- if (check_show_create_table_access(thd, first_table))
- goto error;
+ {
+ if (check_table_access(thd, SELECT_ACL, first_table, 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, first_table->alias);
+ goto error;
+ }
+ DBUG_PRINT("debug", ("check_table_access succeeded"));
+
+ /* Ignore temporary tables if this is "SHOW CREATE VIEW" */
+ first_table->open_type= OT_BASE_ONLY;
+
+ }
+ else
+ {
+ /*
+ The fact that check_some_access() returned FALSE does not mean that
+ access is granted. We need to check if first_table->grant.privilege
+ contains any table-specific privilege.
+ */
+ DBUG_PRINT("debug", ("first_table->grant.privilege: %lx",
+ first_table->grant.privilege));
+ if (check_some_access(thd, SHOW_CREATE_TABLE_ACLS, first_table) ||
+ (first_table->grant.privilege & SHOW_CREATE_TABLE_ACLS) == 0)
+ {
+ my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
+ "SHOW", thd->security_ctx->priv_user,
+ thd->security_ctx->host_or_ip, first_table->alias);
+ goto error;
+ }
+ }
+
+ /* Access is granted. Execute the command. */
res= mysqld_show_create(thd, first_table);
break;
}
@@ -3032,99 +2812,32 @@ end_with_restore_list:
case SQLCOM_CHECKSUM:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_table_access(thd, SELECT_ACL | EXTRA_ACL, all_tables,
- UINT_MAX, FALSE))
- goto error; /* purecov: inspected */
- res = mysql_checksum_table(thd, first_table, &lex->check_opt);
- break;
- }
- case SQLCOM_REPAIR:
- {
- DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables,
- UINT_MAX, FALSE))
- goto error; /* purecov: inspected */
- thd->enable_slow_log= opt_log_slow_admin_statements;
- thd->query_plan_flags|= QPLAN_ADMIN;
- res= mysql_repair_table(thd, first_table, &lex->check_opt);
- /* ! we write after unlocking the table */
- if (!res && !lex->no_write_to_binlog)
- {
- /*
- Presumably, REPAIR and binlog writing doesn't require synchronization
- */
- res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
- }
- select_lex->table_list.first= first_table;
- lex->query_tables=all_tables;
- break;
- }
- case SQLCOM_CHECK:
- {
- DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_table_access(thd, SELECT_ACL | EXTRA_ACL , all_tables,
- UINT_MAX, FALSE))
- goto error; /* purecov: inspected */
- thd->enable_slow_log= opt_log_slow_admin_statements;
- thd->query_plan_flags|= QPLAN_ADMIN;
- res = mysql_check_table(thd, first_table, &lex->check_opt);
- select_lex->table_list.first= first_table;
- lex->query_tables=all_tables;
- break;
- }
- case SQLCOM_ANALYZE:
- {
- DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables,
- UINT_MAX, FALSE))
+ if (check_table_access(thd, SELECT_ACL, all_tables,
+ FALSE, UINT_MAX, FALSE))
goto error; /* purecov: inspected */
- thd->enable_slow_log= opt_log_slow_admin_statements;
- thd->query_plan_flags|= QPLAN_ADMIN;
- res= mysql_analyze_table(thd, first_table, &lex->check_opt);
- /* ! we write after unlocking the table */
- if (!res && !lex->no_write_to_binlog)
- {
- /*
- Presumably, ANALYZE and binlog writing doesn't require synchronization
- */
- res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
- }
- select_lex->table_list.first= first_table;
- lex->query_tables=all_tables;
- break;
- }
-
- case SQLCOM_OPTIMIZE:
- {
- DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables,
- UINT_MAX, FALSE))
- goto error; /* purecov: inspected */
- thd->enable_slow_log= opt_log_slow_admin_statements;
- thd->query_plan_flags|= QPLAN_ADMIN;
- res= mysql_optimize_table(thd, first_table, &lex->check_opt);
- /* ! we write after unlocking the table */
- if (!res && !lex->no_write_to_binlog)
- {
- /*
- Presumably, OPTIMIZE and binlog writing doesn't require synchronization
- */
- res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
- }
- select_lex->table_list.first= first_table;
- lex->query_tables=all_tables;
+ res = mysql_checksum_table(thd, first_table, &lex->check_opt);
break;
}
case SQLCOM_UPDATE:
+ {
+ ha_rows found= 0, updated= 0;
DBUG_ASSERT(first_table == all_tables && first_table != 0);
if (update_precheck(thd, all_tables))
break;
- if (!thd->locked_tables &&
- !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
- goto error;
+
+ /*
+ UPDATE IGNORE can be unsafe. We therefore use row based
+ logging if mixed or row based logging is available.
+ TODO: Check if the order of the output of the select statement is
+ deterministic. Waiting for BUG#42415
+ */
+ if (lex->ignore)
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_UPDATE_IGNORE);
+
DBUG_ASSERT(select_lex->offset_limit == 0);
unit->set_limit(select_lex);
+ MYSQL_UPDATE_START(thd->query());
res= (up_result= mysql_update(thd, all_tables,
select_lex->item_list,
lex->value_list,
@@ -3132,11 +2845,14 @@ end_with_restore_list:
select_lex->order_list.elements,
select_lex->order_list.first,
unit->select_limit_cnt,
- lex->duplicates, lex->ignore));
+ lex->duplicates, 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)
break;
/* Fall through */
+ }
case SQLCOM_UPDATE_MULTI:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
@@ -3149,15 +2865,6 @@ end_with_restore_list:
else
res= 0;
- /*
- Protection might have already been risen if its a fall through
- from the SQLCOM_UPDATE case above.
- */
- if (!thd->locked_tables &&
- lex->sql_command == SQLCOM_UPDATE_MULTI &&
- !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
- goto error;
-
res= mysql_multi_update_prepare(thd);
#ifdef HAVE_REPLICATION
@@ -3193,13 +2900,31 @@ end_with_restore_list:
#ifdef HAVE_REPLICATION
} /* unlikely */
#endif
-
- res= mysql_multi_update(thd, all_tables,
- &select_lex->item_list,
- &lex->value_list,
- select_lex->where,
- select_lex->options,
- lex->duplicates, lex->ignore, unit, select_lex);
+ {
+ multi_update *result_obj;
+ MYSQL_MULTI_UPDATE_START(thd->query());
+ res= mysql_multi_update(thd, all_tables,
+ &select_lex->item_list,
+ &lex->value_list,
+ select_lex->where,
+ select_lex->options,
+ lex->duplicates,
+ lex->ignore,
+ unit,
+ select_lex,
+ &result_obj);
+ if (result_obj)
+ {
+ MYSQL_MULTI_UPDATE_DONE(res, result_obj->num_found(),
+ result_obj->num_updated());
+ res= FALSE; /* Ignore errors here */
+ delete result_obj;
+ }
+ else
+ {
+ MYSQL_MULTI_UPDATE_DONE(1, 0, 0);
+ }
+ }
break;
}
case SQLCOM_REPLACE:
@@ -3228,7 +2953,7 @@ end_with_restore_list:
{
Incident_log_event ev(thd, incident);
(void) mysql_bin_log.write(&ev); /* error is ignored */
- if (mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE))
+ if (mysql_bin_log.rotate_and_purge(true))
{
res= 1;
break;
@@ -3243,17 +2968,11 @@ end_with_restore_list:
if ((res= insert_precheck(thd, all_tables)))
break;
- if (!thd->locked_tables &&
- !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
- {
- res= 1;
- break;
- }
-
+ MYSQL_INSERT_START(thd->query());
res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
lex->update_list, lex->value_list,
lex->duplicates, lex->ignore);
-
+ MYSQL_INSERT_DONE(res, (ulong) thd->get_row_count_func());
/*
If we have inserted into a VIEW, and the base table has
AUTO_INCREMENT column, but this column is not accessible through
@@ -3264,6 +2983,7 @@ end_with_restore_list:
thd->first_successful_insert_id_in_cur_stmt=
thd->first_successful_insert_id_in_prev_stmt;
+#ifdef ENABLED_DEBUG_SYNC
DBUG_EXECUTE_IF("after_mysql_insert",
{
const char act1[]=
@@ -3272,12 +2992,14 @@ end_with_restore_list:
const char act2[]=
"now "
"signal signal.continued";
- DBUG_ASSERT(opt_debug_sync_timeout > 0);
+ DBUG_ASSERT(debug_sync_service);
DBUG_ASSERT(!debug_sync_set_action(thd,
STRING_WITH_LEN(act1)));
DBUG_ASSERT(!debug_sync_set_action(thd,
STRING_WITH_LEN(act2)));
};);
+ DEBUG_SYNC(thd, "after_mysql_insert");
+#endif
break;
}
case SQLCOM_REPLACE_SELECT:
@@ -3287,6 +3009,23 @@ end_with_restore_list:
DBUG_ASSERT(first_table == all_tables && first_table != 0);
if ((res= insert_precheck(thd, all_tables)))
break;
+ /*
+ INSERT...SELECT...ON DUPLICATE KEY UPDATE/REPLACE SELECT/
+ INSERT...IGNORE...SELECT can be unsafe, unless ORDER BY PRIMARY KEY
+ clause is used in SELECT statement. We therefore use row based
+ logging if mixed or row based logging is available.
+ TODO: Check if the order of the output of the select statement is
+ deterministic. Waiting for BUG#42415
+ */
+ if (lex->sql_command == SQLCOM_INSERT_SELECT &&
+ lex->duplicates == DUP_UPDATE)
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_SELECT_UPDATE);
+
+ if (lex->sql_command == SQLCOM_INSERT_SELECT && lex->ignore)
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_IGNORE_SELECT);
+
+ if (lex->sql_command == SQLCOM_REPLACE_SELECT)
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_REPLACE_SELECT);
/* Fix lock for first table */
if (first_table->lock_type == TL_WRITE_DELAYED)
@@ -3297,15 +3036,9 @@ end_with_restore_list:
unit->set_limit(select_lex);
- if (! thd->locked_tables &&
- ! (need_start_waiting= ! wait_if_global_read_lock(thd, 0, 1)))
- {
- res= 1;
- break;
- }
-
- if (!(res= open_and_lock_tables(thd, all_tables)))
+ if (!(res= open_and_lock_tables(thd, all_tables, TRUE, 0)))
{
+ MYSQL_INSERT_SELECT_START(thd->query());
/*
Only the INSERT table should be merged. Other will be handled by
select.
@@ -3343,9 +3076,9 @@ end_with_restore_list:
delete sel_result;
}
/* revert changes for SP */
+ MYSQL_INSERT_SELECT_DONE(res, (ulong) thd->get_row_count_func());
select_lex->table_list.first= first_table;
}
-
/*
If we have inserted into a VIEW, and the base table has
AUTO_INCREMENT column, but this column is not accessible through
@@ -3358,29 +3091,6 @@ end_with_restore_list:
break;
}
- case SQLCOM_TRUNCATE:
- if (end_active_trans(thd))
- {
- res= -1;
- break;
- }
- DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_one_table_access(thd, DROP_ACL, all_tables))
- goto error;
- /*
- Don't allow this within a transaction because we want to use
- re-generate table
- */
- if (thd->locked_tables || thd->active_transaction())
- {
- my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
- ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
- goto error;
- }
- if (!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
- goto error;
- res= mysql_truncate(thd, first_table, 0);
- break;
case SQLCOM_DELETE:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
@@ -3389,17 +3099,11 @@ end_with_restore_list:
DBUG_ASSERT(select_lex->offset_limit == 0);
unit->set_limit(select_lex);
- if (!thd->locked_tables &&
- !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
- {
- res= 1;
- break;
- }
-
+ MYSQL_DELETE_START(thd->query());
res = mysql_delete(thd, all_tables, select_lex->where,
&select_lex->order_list,
- unit->select_limit_cnt, select_lex->options,
- FALSE);
+ unit->select_limit_cnt, select_lex->options);
+ MYSQL_DELETE_DONE(res, (ulong) thd->get_row_count_func());
break;
}
case SQLCOM_DELETE_MULTI:
@@ -3408,13 +3112,6 @@ end_with_restore_list:
TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first;
multi_delete *del_result;
- if (!thd->locked_tables &&
- !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
- {
- res= 1;
- break;
- }
-
if ((res= multi_delete_precheck(thd, all_tables)))
break;
@@ -3425,11 +3122,15 @@ end_with_restore_list:
goto error;
thd_proc_info(thd, "init");
- if ((res= open_and_lock_tables(thd, all_tables)))
+ if ((res= open_and_lock_tables(thd, all_tables, TRUE, 0)))
break;
+ MYSQL_MULTI_DELETE_START(thd->query());
if ((res= mysql_multi_delete_prepare(thd)))
+ {
+ MYSQL_MULTI_DELETE_DONE(1, 0);
goto error;
+ }
if (!thd->is_fatal_error &&
(del_result= new multi_delete(aux_tables, lex->table_count)))
@@ -3441,17 +3142,21 @@ end_with_restore_list:
select_lex->where,
0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
(ORDER *)NULL,
- (select_lex->options | thd->options |
+ (select_lex->options | thd->variables.option_bits |
SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
OPTION_SETUP_TABLES_DONE) & ~OPTION_BUFFER_RESULT,
del_result, unit, select_lex);
res|= thd->is_error();
+ MYSQL_MULTI_DELETE_DONE(res, del_result->num_deleted());
if (res)
- del_result->abort();
+ del_result->abort_result_set();
delete del_result;
}
else
+ {
res= TRUE; // Error
+ MYSQL_MULTI_DELETE_DONE(1, 0);
+ }
break;
}
case SQLCOM_DROP_TABLE:
@@ -3459,17 +3164,15 @@ end_with_restore_list:
DBUG_ASSERT(first_table == all_tables && first_table != 0);
if (!lex->drop_temporary)
{
- if (check_table_access(thd, DROP_ACL, all_tables, UINT_MAX, FALSE))
+ if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE))
goto error; /* purecov: inspected */
- if (end_active_trans(thd))
- goto error;
}
else
{
/* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */
- thd->options|= OPTION_KEEP_LOG;
+ thd->variables.option_bits|= OPTION_KEEP_LOG;
}
- /* DDL and binlog write order protected by LOCK_open */
+ /* DDL and binlog write order are protected by metadata locks. */
res= mysql_rm_table(thd, first_table, lex->drop_if_exists,
lex->drop_temporary);
}
@@ -3493,9 +3196,6 @@ end_with_restore_list:
case SQLCOM_SHOW_PRIVILEGES:
res= mysqld_show_privileges(thd);
break;
- case SQLCOM_SHOW_COLUMN_TYPES:
- res= mysqld_show_column_types(thd);
- break;
case SQLCOM_SHOW_ENGINE_LOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
@@ -3503,7 +3203,7 @@ end_with_restore_list:
goto error;
#else
{
- if (check_access(thd, FILE_ACL, any_db,0,0,0,0))
+ if (check_access(thd, FILE_ACL, any_db, NULL, NULL, 0, 0))
goto error;
res= ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_LOGS);
break;
@@ -3539,10 +3239,6 @@ end_with_restore_list:
if (check_one_table_access(thd, privilege, all_tables))
goto error;
- if (!thd->locked_tables &&
- !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
- goto error;
-
res= mysql_load(thd, lex->exchange, first_table, lex->field_list,
lex->update_list, lex->value_list, lex->duplicates,
lex->ignore, (bool) lex->local_file);
@@ -3553,17 +3249,9 @@ end_with_restore_list:
{
List<set_var_base> *lex_var_list= &lex->var_list;
- if (lex->autocommit && end_active_trans(thd))
- goto error;
-
- if ((check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) ||
- open_and_lock_tables(thd, all_tables)))
- goto error;
- if (lex->one_shot_set && not_all_support_one_shot(lex_var_list))
- {
- my_error(ER_RESERVED_SYNTAX, MYF(0), "SET ONE_SHOT");
+ 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)))
{
/*
@@ -3595,52 +3283,47 @@ end_with_restore_list:
done FLUSH TABLES WITH READ LOCK + BEGIN. If this assumption becomes
false, mysqldump will not work.
*/
- unlock_locked_tables(thd);
- if (thd->options & OPTION_TABLE_LOCK)
+ if (thd->variables.option_bits & OPTION_TABLE_LOCK)
{
- end_active_trans(thd);
- thd->options&= ~(OPTION_TABLE_LOCK);
+ res= trans_commit_implicit(thd);
+ thd->locked_tables_list.unlock_locked_tables(thd);
+ thd->mdl_context.release_transactional_locks();
+ thd->variables.option_bits&= ~(OPTION_TABLE_LOCK);
}
- if (thd->global_read_lock)
- unlock_global_read_lock(thd);
+ if (thd->global_read_lock.is_acquired())
+ thd->global_read_lock.unlock_global_read_lock(thd);
+ if (res)
+ goto error;
my_ok(thd);
break;
case SQLCOM_LOCK_TABLES:
- unlock_locked_tables(thd);
- /* we must end the trasaction first, regardless of anything */
- if (end_active_trans(thd))
+ /* We must end the transaction first, regardless of anything */
+ res= trans_commit_implicit(thd);
+ thd->locked_tables_list.unlock_locked_tables(thd);
+ /* Release transactional metadata locks. */
+ thd->mdl_context.release_transactional_locks();
+ if (res)
goto error;
if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
- UINT_MAX, FALSE))
- goto error;
- if (lex->protect_against_global_read_lock &&
- !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
+ FALSE, UINT_MAX, FALSE))
goto error;
- thd->in_lock_tables=1;
- thd->options|= OPTION_TABLE_LOCK;
- if (!(res= simple_open_n_lock_tables(thd, all_tables)))
+ thd->variables.option_bits|= OPTION_TABLE_LOCK;
+
+ res= lock_tables_open_and_lock_tables(thd, all_tables);
+
+ if (res)
+ {
+ thd->variables.option_bits&= ~(OPTION_TABLE_LOCK);
+ }
+ else
{
#ifdef HAVE_QUERY_CACHE
if (thd->variables.query_cache_wlock_invalidate)
query_cache.invalidate_locked_for_write(thd, first_table);
#endif /*HAVE_QUERY_CACHE*/
- thd->locked_tables=thd->lock;
- thd->lock=0;
my_ok(thd);
}
- else
- {
- /*
- Need to end the current transaction, so the storage engine (InnoDB)
- can free its locks if LOCK TABLES locked some tables before finding
- that it can't lock a table in its list
- */
- ha_autocommit_or_rollback(thd, 1);
- end_active_trans(thd);
- thd->options&= ~(OPTION_TABLE_LOCK);
- }
- thd->in_lock_tables=0;
break;
case SQLCOM_CREATE_DB:
{
@@ -3650,11 +3333,6 @@ end_with_restore_list:
prepared statement- safe.
*/
HA_CREATE_INFO create_info(lex->create_info);
- if (end_active_trans(thd))
- {
- res= -1;
- break;
- }
char *alias;
if (!(alias=thd->strmake(lex->name.str, lex->name.length)) ||
check_db_name(&lex->name))
@@ -3678,8 +3356,7 @@ end_with_restore_list:
break;
}
#endif
- if (check_access(thd,CREATE_ACL,lex->name.str, 0, 1, 0,
- is_schema_db(lex->name.str, lex->name.length)))
+ if (check_access(thd, CREATE_ACL, lex->name.str, NULL, NULL, 1, 0))
break;
res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias :
lex->name.str), &create_info, 0);
@@ -3687,11 +3364,6 @@ end_with_restore_list:
}
case SQLCOM_DROP_DB:
{
- if (end_active_trans(thd))
- {
- res= -1;
- break;
- }
if (check_db_name(&lex->name))
{
my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
@@ -3713,26 +3385,14 @@ end_with_restore_list:
break;
}
#endif
- if (check_access(thd,DROP_ACL,lex->name.str,0,1,0,
- is_schema_db(lex->name.str, lex->name.length)))
+ if (check_access(thd, DROP_ACL, lex->name.str, NULL, NULL, 1, 0))
break;
- if (thd->locked_tables || thd->active_transaction())
- {
- my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
- ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
- goto error;
- }
res= mysql_rm_db(thd, lex->name.str, lex->drop_if_exists, 0);
break;
}
case SQLCOM_ALTER_DB_UPGRADE:
{
LEX_STRING *db= & lex->name;
- if (end_active_trans(thd))
- {
- res= 1;
- break;
- }
#ifdef HAVE_REPLICATION
if (thd->slave_thread &&
(!rpl_filter->db_ok(db->str) ||
@@ -3748,24 +3408,13 @@ end_with_restore_list:
my_error(ER_WRONG_DB_NAME, MYF(0), db->str);
break;
}
- if (check_access(thd, ALTER_ACL, db->str, 0, 1, 0,
- is_schema_db(db->str, db->length)) ||
- check_access(thd, DROP_ACL, db->str, 0, 1, 0,
- is_schema_db(db->str, db->length)) ||
- check_access(thd, CREATE_ACL, db->str, 0, 1, 0,
- is_schema_db(db->str, db->length)))
+ if (check_access(thd, ALTER_ACL, db->str, NULL, NULL, 1, 0) ||
+ check_access(thd, DROP_ACL, db->str, NULL, NULL, 1, 0) ||
+ check_access(thd, CREATE_ACL, db->str, NULL, NULL, 1, 0))
{
res= 1;
break;
}
- if (thd->locked_tables || thd->active_transaction())
- {
- res= 1;
- my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
- ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
- goto error;
- }
-
res= mysql_upgrade_db(thd, db);
if (!res)
my_ok(thd);
@@ -3796,15 +3445,8 @@ end_with_restore_list:
break;
}
#endif
- if (check_access(thd, ALTER_ACL, db->str, 0, 1, 0,
- is_schema_db(db->str, db->length)))
+ if (check_access(thd, ALTER_ACL, db->str, NULL, NULL, 1, 0))
break;
- if (thd->locked_tables || thd->active_transaction())
- {
- my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
- ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
- goto error;
- }
res= mysql_alter_db(thd, db->str, &create_info);
break;
}
@@ -3882,7 +3524,7 @@ end_with_restore_list:
#endif
case SQLCOM_CREATE_FUNCTION: // UDF function
{
- if (check_access(thd,INSERT_ACL,"mysql",0,1,0,0))
+ if (check_access(thd, INSERT_ACL, "mysql", NULL, NULL, 1, 0))
break;
#ifdef HAVE_DLOPEN
if (!(res = mysql_create_function(thd, &lex->udf)))
@@ -3896,11 +3538,9 @@ end_with_restore_list:
#ifndef NO_EMBEDDED_ACCESS_CHECKS
case SQLCOM_CREATE_USER:
{
- if (check_access(thd, INSERT_ACL, "mysql", 0, 1, 1, 0) &&
+ if (check_access(thd, INSERT_ACL, "mysql", NULL, NULL, 1, 1) &&
check_global_access(thd,CREATE_USER_ACL))
break;
- if (end_active_trans(thd))
- goto error;
/* Conditionally writes to binlog */
if (!(res= mysql_create_user(thd, lex->users_list)))
my_ok(thd);
@@ -3908,11 +3548,9 @@ end_with_restore_list:
}
case SQLCOM_DROP_USER:
{
- if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 1, 0) &&
+ if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 1) &&
check_global_access(thd,CREATE_USER_ACL))
break;
- if (end_active_trans(thd))
- goto error;
/* Conditionally writes to binlog */
if (!(res= mysql_drop_user(thd, lex->users_list)))
my_ok(thd);
@@ -3920,11 +3558,9 @@ end_with_restore_list:
}
case SQLCOM_RENAME_USER:
{
- if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1, 0) &&
+ if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 1) &&
check_global_access(thd,CREATE_USER_ACL))
break;
- if (end_active_trans(thd))
- goto error;
/* Conditionally writes to binlog */
if (!(res= mysql_rename_user(thd, lex->users_list)))
my_ok(thd);
@@ -3932,9 +3568,7 @@ end_with_restore_list:
}
case SQLCOM_REVOKE_ALL:
{
- if (end_active_trans(thd))
- goto error;
- if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1, 0) &&
+ if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 1) &&
check_global_access(thd,CREATE_USER_ACL))
break;
@@ -3949,16 +3583,12 @@ end_with_restore_list:
case SQLCOM_REVOKE:
case SQLCOM_GRANT:
{
- if (end_active_trans(thd))
- goto error;
-
- if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
- first_table ? first_table->db : select_lex->db,
- first_table ? &first_table->grant.privilege : 0,
- first_table ? 0 : 1, 0,
- first_table ? (bool) first_table->schema_table :
- select_lex->db ?
- is_schema_db(select_lex->db) : 0))
+ 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->grant.privilege : NULL,
+ first_table ? &first_table->grant.m_internal : NULL,
+ first_table ? 0 : 1, 0))
goto error;
/* Replicate current user as grantor */
@@ -3967,6 +3597,7 @@ end_with_restore_list:
if (thd->security_ctx->user) // If not replication
{
LEX_USER *user, *tmp_user;
+ bool first_user= TRUE;
List_iterator <LEX_USER> user_list(lex->users_list);
while ((tmp_user= user_list++))
@@ -3980,20 +3611,23 @@ end_with_restore_list:
ER(ER_WARN_HOSTNAME_WONT_WORK));
// Are we trying to change a password of another user
DBUG_ASSERT(user->host.str != 0);
- if (strcmp(thd->security_ctx->user, user->user.str) ||
- my_strcasecmp(system_charset_info,
- user->host.str, thd->security_ctx->host_or_ip))
+
+ /*
+ GRANT/REVOKE PROXY has the target user as a first entry in the list.
+ */
+ if (lex->type == TYPE_ENUM_PROXY && first_user)
{
- // TODO: use check_change_password()
- if (is_acl_user(user->host.str, user->user.str) &&
- user->password.str &&
- check_access(thd, UPDATE_ACL,"mysql",0,1,1,0))
- {
- my_message(ER_PASSWORD_NOT_ALLOWED,
- ER(ER_PASSWORD_NOT_ALLOWED), MYF(0));
+ first_user= FALSE;
+ if (acl_check_proxy_grant_access (thd, user->host.str, user->user.str,
+ lex->grant & GRANT_ACL))
goto error;
- }
- }
+ }
+ else if (is_acl_user(user->host.str, user->user.str) &&
+ user->password.str &&
+ check_change_password (thd, user->host.str, user->user.str,
+ user->password.str,
+ user->password.length))
+ goto error;
}
}
if (first_table)
@@ -4018,7 +3652,7 @@ end_with_restore_list:
else
{
if (check_grant(thd,(lex->grant | lex->grant_tot_col | GRANT_ACL),
- all_tables, 0, UINT_MAX, 0))
+ all_tables, FALSE, UINT_MAX, FALSE))
goto error;
/* Conditionally writes to binlog */
res= mysql_table_grant(thd, all_tables, lex->users_list,
@@ -4028,16 +3662,19 @@ end_with_restore_list:
}
else
{
- if (lex->columns.elements || lex->type)
+ if (lex->columns.elements || (lex->type && lex->type != TYPE_ENUM_PROXY))
{
my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
MYF(0));
goto error;
}
else
- /* Conditionally writes to binlog */
- res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
- lex->sql_command == SQLCOM_REVOKE);
+ {
+ /* Conditionally writes to binlog */
+ res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
+ lex->sql_command == SQLCOM_REVOKE,
+ lex->type == TYPE_ENUM_PROXY);
+ }
if (!res)
{
if (lex->sql_command == SQLCOM_GRANT)
@@ -4068,6 +3705,18 @@ end_with_restore_list:
if (check_global_access(thd,RELOAD_ACL))
goto error;
+ if (first_table && lex->type & REFRESH_READ_LOCK)
+ {
+ /* Check table-level privileges. */
+ if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
+ FALSE, UINT_MAX, FALSE))
+ goto error;
+ if (flush_tables_with_read_lock(thd, all_tables))
+ goto error;
+ my_ok(thd);
+ break;
+ }
+
/*
reload_acl_and_cache() will tell us if we are allowed to write to the
binlog or not.
@@ -4134,7 +3783,7 @@ end_with_restore_list:
goto error;
if ((thd->security_ctx->priv_user &&
!strcmp(thd->security_ctx->priv_user, grant_user->user.str)) ||
- !check_access(thd, SELECT_ACL, "mysql",0,1,0,0))
+ !check_access(thd, SELECT_ACL, "mysql", NULL, NULL, 1, 0))
{
res = mysql_show_grants(thd, grant_user);
}
@@ -4143,7 +3792,7 @@ end_with_restore_list:
#endif
case SQLCOM_HA_OPEN:
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE))
+ if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE))
goto error;
res= mysql_ha_open(thd, first_table, 0);
break;
@@ -4165,128 +3814,87 @@ end_with_restore_list:
break;
case SQLCOM_BEGIN:
- if (thd->transaction.xid_state.xa_state != XA_NOTR)
- {
- my_error(ER_XAER_RMFAIL, MYF(0),
- xa_state_names[thd->transaction.xid_state.xa_state]);
- break;
- }
- if (begin_trans(thd))
+ if (trans_begin(thd, lex->start_transaction_opt))
goto error;
- if (lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT)
- {
- if (ha_start_consistent_snapshot(thd))
- goto error;
- }
my_ok(thd);
break;
case SQLCOM_COMMIT:
- if (end_trans(thd, lex->tx_release ? COMMIT_RELEASE :
- lex->tx_chain ? COMMIT_AND_CHAIN : COMMIT))
+ {
+ DBUG_ASSERT(thd->lock == NULL ||
+ thd->locked_tables_mode == LTM_LOCK_TABLES);
+ bool tx_chain= (lex->tx_chain == TVL_YES ||
+ (thd->variables.completion_type == 1 &&
+ lex->tx_chain != TVL_NO));
+ bool tx_release= (lex->tx_release == TVL_YES ||
+ (thd->variables.completion_type == 2 &&
+ lex->tx_release != TVL_NO));
+ if (trans_commit(thd))
goto error;
- my_ok(thd);
- break;
- case SQLCOM_ROLLBACK:
- if (end_trans(thd, lex->tx_release ? ROLLBACK_RELEASE :
- lex->tx_chain ? ROLLBACK_AND_CHAIN : ROLLBACK))
+ thd->mdl_context.release_transactional_locks();
+ /* Begin transaction with the same isolation level. */
+ if (tx_chain)
+ {
+ if (trans_begin(thd))
goto error;
- my_ok(thd);
- break;
- case SQLCOM_RELEASE_SAVEPOINT:
- {
- SAVEPOINT *sv;
- for (sv=thd->transaction.savepoints; sv; sv=sv->prev)
+ }
+ else
{
- if (my_strnncoll(system_charset_info,
- (uchar *)lex->ident.str, lex->ident.length,
- (uchar *)sv->name, sv->length) == 0)
- break;
+ /* Reset the isolation level if no chaining transaction. */
+ thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
}
- if (sv)
+ /* Disconnect the current client connection. */
+ if (tx_release)
{
- if (ha_release_savepoint(thd, sv))
- res= TRUE; // cannot happen
- else
- my_ok(thd);
- thd->transaction.savepoints=sv->prev;
+ thd->killed= KILL_CONNECTION;
+ thd->print_aborted_warning(3, "RELEASE");
}
- else
- my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", lex->ident.str);
+ my_ok(thd);
break;
}
- case SQLCOM_ROLLBACK_TO_SAVEPOINT:
+ case SQLCOM_ROLLBACK:
{
- SAVEPOINT *sv;
- for (sv=thd->transaction.savepoints; sv; sv=sv->prev)
+ DBUG_ASSERT(thd->lock == NULL ||
+ thd->locked_tables_mode == LTM_LOCK_TABLES);
+ bool tx_chain= (lex->tx_chain == TVL_YES ||
+ (thd->variables.completion_type == 1 &&
+ lex->tx_chain != TVL_NO));
+ bool tx_release= (lex->tx_release == TVL_YES ||
+ (thd->variables.completion_type == 2 &&
+ lex->tx_release != TVL_NO));
+ if (trans_rollback(thd))
+ goto error;
+ thd->mdl_context.release_transactional_locks();
+ /* Begin transaction with the same isolation level. */
+ if (tx_chain)
{
- if (my_strnncoll(system_charset_info,
- (uchar *)lex->ident.str, lex->ident.length,
- (uchar *)sv->name, sv->length) == 0)
- break;
+ if (trans_begin(thd))
+ goto error;
}
- if (sv)
+ else
{
- if (ha_rollback_to_savepoint(thd, sv))
- res= TRUE; // cannot happen
- else
- {
- if (((thd->options & OPTION_KEEP_LOG) ||
- thd->transaction.all.modified_non_trans_table) &&
- !thd->slave_thread)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_WARNING_NOT_COMPLETE_ROLLBACK,
- ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
- my_ok(thd);
- }
- thd->transaction.savepoints=sv;
+ /* Reset the isolation level if no chaining transaction. */
+ thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
}
- else
- my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", lex->ident.str);
+ /* Disconnect the current client connection. */
+ if (tx_release)
+ thd->killed= KILL_CONNECTION;
+ my_ok(thd);
break;
}
+ case SQLCOM_RELEASE_SAVEPOINT:
+ if (trans_release_savepoint(thd, lex->ident))
+ goto error;
+ my_ok(thd);
+ break;
+ case SQLCOM_ROLLBACK_TO_SAVEPOINT:
+ if (trans_rollback_to_savepoint(thd, lex->ident))
+ goto error;
+ my_ok(thd);
+ break;
case SQLCOM_SAVEPOINT:
- if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) ||
- thd->in_sub_stmt) || !opt_using_transactions)
- my_ok(thd);
- else
- {
- SAVEPOINT **sv, *newsv;
- for (sv=&thd->transaction.savepoints; *sv; sv=&(*sv)->prev)
- {
- if (my_strnncoll(system_charset_info,
- (uchar *)lex->ident.str, lex->ident.length,
- (uchar *)(*sv)->name, (*sv)->length) == 0)
- break;
- }
- if (*sv) /* old savepoint of the same name exists */
- {
- newsv=*sv;
- ha_release_savepoint(thd, *sv); // it cannot fail
- *sv=(*sv)->prev;
- }
- else if ((newsv=(SAVEPOINT *) alloc_root(&thd->transaction.mem_root,
- savepoint_alloc_size)) == 0)
- {
- my_error(ER_OUT_OF_RESOURCES, MYF(0));
- break;
- }
- newsv->name=strmake_root(&thd->transaction.mem_root,
- lex->ident.str, lex->ident.length);
- newsv->length=lex->ident.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))
- res= TRUE;
- else
- {
- newsv->prev=thd->transaction.savepoints;
- thd->transaction.savepoints=newsv;
- my_ok(thd);
- }
- }
+ if (trans_savepoint(thd, lex->ident))
+ goto error;
+ my_ok(thd);
break;
case SQLCOM_CREATE_PROCEDURE:
case SQLCOM_CREATE_SPFUNCTION:
@@ -4318,12 +3926,8 @@ end_with_restore_list:
goto create_sp_error;
}
- if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str, 0, 0, 0,
- is_schema_db(lex->sphead->m_db.str,
- lex->sphead->m_db.length)))
- goto create_sp_error;
-
- if (end_active_trans(thd))
+ if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str,
+ NULL, NULL, 0, 0))
goto create_sp_error;
name= lex->sphead->name(&namelen);
@@ -4343,7 +3947,7 @@ end_with_restore_list:
if (sp_process_definer(thd))
goto create_sp_error;
- res= (sp_result= lex->sphead->create(thd));
+ res= (sp_result= sp_create_routine(thd, lex->sphead->m_type, lex->sphead));
switch (sp_result) {
case SP_OK: {
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -4354,6 +3958,23 @@ end_with_restore_list:
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.
@@ -4425,13 +4046,13 @@ create_sp_error:
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, UINT_MAX, FALSE) ||
- open_and_lock_tables(thd, all_tables))
+ if (check_table_access(thd, SELECT_ACL, all_tables, FALSE,
+ UINT_MAX, FALSE) ||
+ open_and_lock_tables(thd, all_tables, TRUE, 0))
goto error;
/*
@@ -4500,20 +4121,15 @@ create_sp_error:
So just execute the statement.
*/
res= sp->execute_procedure(thd, &lex->value_list);
- /*
- If warnings have been cleared, we have to clear total_warn_count
- too, otherwise the clients get confused.
- */
- if (thd->warn_list.is_empty())
- thd->total_warn_count= 0;
thd->variables.select_limit= select_limit;
thd->server_status&= ~bits_to_be_cleared;
if (!res)
- my_ok(thd, (ulong) (thd->row_count_func < 0 ? 0 :
- thd->row_count_func));
+ {
+ my_ok(thd, (thd->get_row_count_func() < 0) ? 0 : thd->get_row_count_func());
+ }
else
{
DBUG_ASSERT(thd->is_error() || thd->killed);
@@ -4526,68 +4142,23 @@ create_sp_error:
case SQLCOM_ALTER_FUNCTION:
{
int sp_result;
- sp_head *sp;
- st_sp_chistics chistics;
+ enum stored_procedure_type type;
+ type= (lex->sql_command == SQLCOM_ALTER_PROCEDURE ?
+ TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
- memcpy(&chistics, &lex->sp_chistics, sizeof(chistics));
- if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
- sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
- &thd->sp_proc_cache, FALSE);
- else
- sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
- &thd->sp_func_cache, FALSE);
- mysql_reset_errors(thd, 0);
- if (! sp)
- {
- if (lex->spname->m_db.str)
- sp_result= SP_KEY_NOT_FOUND;
- else
- {
- my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
- goto error;
- }
- }
- else
- {
- if (check_routine_access(thd, ALTER_PROC_ACL, sp->m_db.str,
- sp->m_name.str,
- lex->sql_command == SQLCOM_ALTER_PROCEDURE, 0))
- goto error;
-
- if (end_active_trans(thd))
- goto error;
- memcpy(&lex->sp_chistics, &chistics, sizeof(lex->sp_chistics));
- if ((sp->m_type == TYPE_ENUM_FUNCTION) &&
- !trust_function_creators && mysql_bin_log.is_open() &&
- !sp->m_chistics->detistic &&
- (chistics.daccess == SP_CONTAINS_SQL ||
- chistics.daccess == SP_MODIFIES_SQL_DATA))
- {
- my_message(ER_BINLOG_UNSAFE_ROUTINE,
- ER(ER_BINLOG_UNSAFE_ROUTINE), MYF(0));
- sp_result= SP_INTERNAL_ERROR;
- }
- else
- {
- /*
- Note that if you implement the capability of ALTER FUNCTION to
- alter the body of the function, this command should be made to
- follow the restrictions that log-bin-trust-function-creators=0
- already puts on CREATE FUNCTION.
- */
- /* Conditionally writes to binlog */
-
- 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))
+ goto error;
- sp_result= sp_update_routine(thd,
- type,
- lex->spname,
- &lex->sp_chistics);
- }
- }
+ /*
+ Note that if you implement the capability of ALTER FUNCTION to
+ alter the body of the function, this command should be made to
+ follow the restrictions that log-bin-trust-function-creators=0
+ already puts on CREATE FUNCTION.
+ */
+ /* Conditionally writes to binlog */
+ sp_result= sp_update_routine(thd, type, lex->spname, &lex->sp_chistics);
switch (sp_result)
{
case SP_OK:
@@ -4607,70 +4178,93 @@ create_sp_error:
case SQLCOM_DROP_PROCEDURE:
case SQLCOM_DROP_FUNCTION:
{
- int sp_result;
- stored_procedure_type type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ?
- TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
-
- sp_result= sp_routine_exists_in_table(thd, type, lex->spname);
- mysql_reset_errors(thd, 0);
- if (sp_result == SP_OK)
+#ifdef HAVE_DLOPEN
+ if (lex->sql_command == SQLCOM_DROP_FUNCTION &&
+ ! lex->spname->m_explicit_name)
{
- char *db= lex->spname->m_db.str;
- char *name= lex->spname->m_name.str;
+ /* DROP FUNCTION <non qualified name> */
+ udf_func *udf = find_udf(lex->spname->m_name.str,
+ lex->spname->m_name.length);
+ if (udf)
+ {
+ if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 0))
+ goto error;
- if (check_routine_access(thd, ALTER_PROC_ACL, db, name,
- lex->sql_command == SQLCOM_DROP_PROCEDURE, 0))
+ if (!(res = mysql_drop_function(thd, &lex->spname->m_name)))
+ {
+ my_ok(thd);
+ break;
+ }
+ my_error(ER_SP_DROP_FAILED, MYF(0),
+ "FUNCTION (UDF)", lex->spname->m_name.str);
goto error;
+ }
- if (end_active_trans(thd))
+ if (lex->spname->m_db.str == NULL)
+ {
+ if (lex->drop_if_exists)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
+ "FUNCTION (UDF)", lex->spname->m_name.str);
+ res= FALSE;
+ my_ok(thd);
+ break;
+ }
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
+ "FUNCTION (UDF)", lex->spname->m_name.str);
goto error;
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (sp_automatic_privileges && !opt_noacl &&
- sp_revoke_privileges(thd, db, name,
- lex->sql_command == SQLCOM_DROP_PROCEDURE))
- {
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_PROC_AUTO_REVOKE_FAIL,
- ER(ER_PROC_AUTO_REVOKE_FAIL));
- }
+ }
+ /* Fall thought to test for a stored function */
+ }
#endif
- /* Conditionally writes to binlog */
- stored_procedure_type type;
- type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ?
- TYPE_ENUM_PROCEDURE :
- TYPE_ENUM_FUNCTION);
+ 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))
+ goto error;
- sp_result= sp_drop_routine(thd, type, lex->spname);
- }
- else
+ /* Conditionally writes to binlog */
+ sp_result= sp_drop_routine(thd, type, lex->spname);
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ /*
+ We're going to issue an implicit REVOKE statement so we close all
+ open tables. We have to keep metadata locks as this ensures that
+ this statement is atomic against concurent FLUSH TABLES WITH READ
+ LOCK. Deadlocks which can arise due to fact that this implicit
+ statement takes metadata locks should be detected by a deadlock
+ detector in MDL subsystem and reported as errors.
+
+ No need to commit/rollback statement transaction, it's not started.
+
+ TODO: Long-term we should either ensure that implicit REVOKE statement
+ is written into binary log as a separate statement or make both
+ dropping of routine and implicit REVOKE parts of one fully atomic
+ statement.
+ */
+ DBUG_ASSERT(thd->transaction.stmt.is_empty());
+ close_thread_tables(thd);
+
+ if (sp_result != SP_KEY_NOT_FOUND &&
+ sp_automatic_privileges && !opt_noacl &&
+ sp_revoke_privileges(thd, db, name,
+ lex->sql_command == SQLCOM_DROP_PROCEDURE))
{
-#ifdef HAVE_DLOPEN
- if (lex->sql_command == SQLCOM_DROP_FUNCTION)
- {
- udf_func *udf = find_udf(lex->spname->m_name.str,
- lex->spname->m_name.length);
- if (udf)
- {
- if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 0, 0))
- goto error;
-
- if (!(res = mysql_drop_function(thd, &lex->spname->m_name)))
- {
- my_ok(thd);
- break;
- }
- }
- }
-#endif
- if (lex->spname->m_db.str)
- sp_result= SP_KEY_NOT_FOUND;
- else
- {
- my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
- goto error;
- }
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_PROC_AUTO_REVOKE_FAIL,
+ ER(ER_PROC_AUTO_REVOKE_FAIL));
+ /* If this happens, an error should have been reported. */
+ goto error;
}
+#endif
+
res= sp_result;
switch (sp_result) {
case SP_OK:
@@ -4682,7 +4276,7 @@ create_sp_error:
res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
- SP_COM_STRING(lex), lex->spname->m_name.str);
+ SP_COM_STRING(lex), lex->spname->m_qname.str);
if (!res)
my_ok(thd);
break;
@@ -4700,35 +4294,25 @@ create_sp_error:
case SQLCOM_SHOW_CREATE_PROC:
{
if (sp_show_create_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname))
- {
- my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
- SP_COM_STRING(lex), lex->spname->m_name.str);
- goto error;
- }
+ goto error;
break;
}
case SQLCOM_SHOW_CREATE_FUNC:
{
if (sp_show_create_routine(thd, TYPE_ENUM_FUNCTION, lex->spname))
- {
- my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
- SP_COM_STRING(lex), lex->spname->m_name.str);
goto error;
- }
break;
}
-#ifndef DBUG_OFF
case SQLCOM_SHOW_PROC_CODE:
case SQLCOM_SHOW_FUNC_CODE:
{
+#ifndef DBUG_OFF
sp_head *sp;
+ stored_procedure_type type= (lex->sql_command == SQLCOM_SHOW_PROC_CODE ?
+ TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
- if (lex->sql_command == SQLCOM_SHOW_PROC_CODE)
- sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
- &thd->sp_proc_cache, FALSE);
- else
- sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
- &thd->sp_func_cache, FALSE);
+ if (sp_cache_routine(thd, type, lex->spname, FALSE, &sp))
+ goto error;
if (!sp || sp->show_routine_code(thd))
{
/* We don't distinguish between errors for now */
@@ -4737,8 +4321,12 @@ create_sp_error:
goto error;
}
break;
- }
+#else
+ my_error(ER_FEATURE_DISABLED, MYF(0),
+ "SHOW PROCEDURE|FUNCTION CODE", "--with-debug");
+ goto error;
#endif // ifndef DBUG_OFF
+ }
case SQLCOM_SHOW_CREATE_TRIGGER:
{
if (lex->spname->m_name.length > NAME_LEN)
@@ -4758,16 +4346,12 @@ create_sp_error:
Note: SQLCOM_CREATE_VIEW also handles 'ALTER VIEW' commands
as specified through the thd->lex->create_view_mode flag.
*/
- if (end_active_trans(thd))
- goto error;
-
res= mysql_create_view(thd, first_table, thd->lex->create_view_mode);
break;
}
case SQLCOM_DROP_VIEW:
{
- if (check_table_access(thd, DROP_ACL, all_tables, UINT_MAX, FALSE) ||
- end_active_trans(thd))
+ if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE))
goto error;
/* Conditionally writes to binlog. */
res= mysql_drop_view(thd, first_table, thd->lex->drop_mode);
@@ -4775,9 +4359,6 @@ create_sp_error:
}
case SQLCOM_CREATE_TRIGGER:
{
- if (end_active_trans(thd))
- goto error;
-
/* Conditionally writes to binlog. */
res= mysql_create_or_drop_trigger(thd, all_tables, 1);
@@ -4785,210 +4366,52 @@ create_sp_error:
}
case SQLCOM_DROP_TRIGGER:
{
- if (end_active_trans(thd))
- goto error;
-
/* Conditionally writes to binlog. */
res= mysql_create_or_drop_trigger(thd, all_tables, 0);
break;
}
case SQLCOM_XA_START:
- if (thd->transaction.xid_state.xa_state == XA_IDLE &&
- thd->lex->xa_opt == XA_RESUME)
- {
- if (! thd->transaction.xid_state.xid.eq(thd->lex->xid))
- {
- my_error(ER_XAER_NOTA, MYF(0));
- break;
- }
- thd->transaction.xid_state.xa_state= XA_ACTIVE;
- my_ok(thd);
- break;
- }
- if (thd->lex->xa_opt != XA_NONE)
- { // JOIN is not supported yet. TODO
- my_error(ER_XAER_INVAL, MYF(0));
- break;
- }
- if (thd->transaction.xid_state.xa_state != XA_NOTR)
- {
- my_error(ER_XAER_RMFAIL, MYF(0),
- xa_state_names[thd->transaction.xid_state.xa_state]);
- break;
- }
- if (thd->active_transaction() || thd->locked_tables)
- {
- my_error(ER_XAER_OUTSIDE, MYF(0));
- break;
- }
- DBUG_ASSERT(thd->transaction.xid_state.xid.is_null());
- thd->transaction.xid_state.xa_state= XA_ACTIVE;
- thd->transaction.xid_state.rm_error= 0;
- thd->transaction.xid_state.xid.set(thd->lex->xid);
- if (xid_cache_insert(&thd->transaction.xid_state))
- {
- thd->transaction.xid_state.xa_state= XA_NOTR;
- thd->transaction.xid_state.xid.null();
- break;
- }
- thd->transaction.all.modified_non_trans_table= FALSE;
- thd->options= ((thd->options & ~(OPTION_KEEP_LOG)) | OPTION_BEGIN);
- thd->server_status|= SERVER_STATUS_IN_TRANS;
+ if (trans_xa_start(thd))
+ goto error;
my_ok(thd);
break;
case SQLCOM_XA_END:
- /* fake it */
- if (thd->lex->xa_opt != XA_NONE)
- { // SUSPEND and FOR MIGRATE are not supported yet. TODO
- my_error(ER_XAER_INVAL, MYF(0));
- break;
- }
- if (thd->transaction.xid_state.xa_state != XA_ACTIVE)
- {
- my_error(ER_XAER_RMFAIL, MYF(0),
- xa_state_names[thd->transaction.xid_state.xa_state]);
- break;
- }
- if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
- {
- my_error(ER_XAER_NOTA, MYF(0));
- break;
- }
- if (xa_trans_rolled_back(&thd->transaction.xid_state))
- break;
- thd->transaction.xid_state.xa_state=XA_IDLE;
+ if (trans_xa_end(thd))
+ goto error;
my_ok(thd);
break;
case SQLCOM_XA_PREPARE:
- if (thd->transaction.xid_state.xa_state != XA_IDLE)
- {
- my_error(ER_XAER_RMFAIL, MYF(0),
- xa_state_names[thd->transaction.xid_state.xa_state]);
- break;
- }
- if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
- {
- my_error(ER_XAER_NOTA, MYF(0));
- break;
- }
- if (ha_prepare(thd))
- {
- my_error(ER_XA_RBROLLBACK, MYF(0));
- xid_cache_delete(&thd->transaction.xid_state);
- thd->transaction.xid_state.xa_state=XA_NOTR;
- break;
- }
- thd->transaction.xid_state.xa_state=XA_PREPARED;
+ if (trans_xa_prepare(thd))
+ goto error;
my_ok(thd);
break;
case SQLCOM_XA_COMMIT:
- if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
- {
- /*
- xid_state.in_thd is always true beside of xa recovery
- procedure. Note, that there is no race condition here
- between xid_cache_search and xid_cache_delete, since we're always
- deleting our own XID (thd->lex->xid == thd->transaction.xid_state.xid).
- The only case when thd->lex->xid != thd->transaction.xid_state.xid
- and xid_state->in_thd == 0 is in ha_recover() functionality,
- which is called before starting client connections, and thus is
- always single-threaded.
- */
- XID_STATE *xs=xid_cache_search(thd->lex->xid);
- if (!xs || xs->in_thd)
- my_error(ER_XAER_NOTA, MYF(0));
- else if (xa_trans_rolled_back(xs))
- {
- ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
- xid_cache_delete(xs);
- break;
- }
- else
- {
- ha_commit_or_rollback_by_xid(thd->lex->xid, 1);
- xid_cache_delete(xs);
- my_ok(thd);
- }
- break;
- }
- if (xa_trans_rolled_back(&thd->transaction.xid_state))
- {
- xa_trans_rollback(thd);
- break;
- }
- if (thd->transaction.xid_state.xa_state == XA_IDLE &&
- thd->lex->xa_opt == XA_ONE_PHASE)
- {
- int r;
- if ((r= ha_commit(thd)))
- my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0));
- else
- my_ok(thd);
- }
- else if (thd->transaction.xid_state.xa_state == XA_PREPARED &&
- thd->lex->xa_opt == XA_NONE)
- {
- if (wait_if_global_read_lock(thd, 0, 0))
- {
- ha_rollback(thd);
- my_error(ER_XAER_RMERR, MYF(0));
- }
- else
- {
- if (ha_commit_one_phase(thd, 1))
- my_error(ER_XAER_RMERR, MYF(0));
- else
- my_ok(thd);
- start_waiting_global_read_lock(thd);
- }
- }
- else
- {
- my_error(ER_XAER_RMFAIL, MYF(0),
- xa_state_names[thd->transaction.xid_state.xa_state]);
- break;
- }
- thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
- thd->transaction.all.modified_non_trans_table= FALSE;
- thd->server_status&= ~SERVER_STATUS_IN_TRANS;
- xid_cache_delete(&thd->transaction.xid_state);
- thd->transaction.xid_state.xa_state=XA_NOTR;
+ if (trans_xa_commit(thd))
+ goto error;
+ thd->mdl_context.release_transactional_locks();
+ /*
+ We've just done a commit, reset transaction
+ isolation level to the session default.
+ */
+ thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
+ my_ok(thd);
break;
case SQLCOM_XA_ROLLBACK:
- if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
- {
- XID_STATE *xs=xid_cache_search(thd->lex->xid);
- if (!xs || xs->in_thd)
- my_error(ER_XAER_NOTA, MYF(0));
- else
- {
- bool ok= !xa_trans_rolled_back(xs);
- ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
- xid_cache_delete(xs);
- if (ok)
- my_ok(thd);
- }
- break;
- }
- if (thd->transaction.xid_state.xa_state != XA_IDLE &&
- thd->transaction.xid_state.xa_state != XA_PREPARED &&
- thd->transaction.xid_state.xa_state != XA_ROLLBACK_ONLY)
- {
- my_error(ER_XAER_RMFAIL, MYF(0),
- xa_state_names[thd->transaction.xid_state.xa_state]);
- break;
- }
- if (xa_trans_rollback(thd))
- my_error(ER_XAER_RMERR, MYF(0));
- else
- my_ok(thd);
+ if (trans_xa_rollback(thd))
+ goto error;
+ thd->mdl_context.release_transactional_locks();
+ /*
+ We've just done a rollback, reset transaction
+ isolation level to the session default.
+ */
+ thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
+ my_ok(thd);
break;
case SQLCOM_XA_RECOVER:
res= mysql_xa_recover(thd);
break;
case SQLCOM_ALTER_TABLESPACE:
- if (check_access(thd, ALTER_ACL, thd->db, 0, 1, 0,
- thd->db ? is_schema_db(thd->db, thd->db_length) : 0))
+ if (check_global_access(thd, CREATE_TABLESPACE_ACL))
break;
if (!(res= mysql_alter_tablespace(thd, lex->alter_tablespace_info)))
my_ok(thd);
@@ -4999,7 +4422,8 @@ create_sp_error:
my_ok(thd);
break;
case SQLCOM_UNINSTALL_PLUGIN:
- if (! (res= mysql_uninstall_plugin(thd, &thd->lex->comment)))
+ if (! (res= mysql_uninstall_plugin(thd, &thd->lex->comment,
+ &thd->lex->ident)))
my_ok(thd);
break;
case SQLCOM_BINLOG_BASE64_EVENT:
@@ -5075,6 +4499,20 @@ create_sp_error:
my_ok(thd, 1);
break;
}
+ case SQLCOM_ANALYZE:
+ case SQLCOM_CHECK:
+ case SQLCOM_OPTIMIZE:
+ case SQLCOM_REPAIR:
+ case SQLCOM_TRUNCATE:
+ case SQLCOM_ALTER_TABLE:
+ thd->query_plan_flags|= QPLAN_ADMIN;
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ /* fall through */
+ case SQLCOM_SIGNAL:
+ case SQLCOM_RESIGNAL:
+ DBUG_ASSERT(lex->m_stmt != NULL);
+ res= lex->m_stmt->execute(thd);
+ break;
default:
#ifndef EMBEDDED_LIBRARY
DBUG_ASSERT(0); /* Impossible */
@@ -5098,30 +4536,99 @@ create_sp_error:
if (thd->one_shot_set && lex->sql_command != SQLCOM_SET_OPTION)
reset_one_shot_variables(thd);
- /*
- The return value for ROW_COUNT() is "implementation dependent" if the
- statement is not DELETE, INSERT or UPDATE, but -1 is what JDBC and ODBC
- wants. We also keep the last value in case of SQLCOM_CALL or
- SQLCOM_EXECUTE.
- */
- if (!(sql_command_flags[lex->sql_command] & CF_HAS_ROW_COUNT))
- thd->row_count_func= -1;
+ goto finish;
+
+error:
+ res= TRUE;
finish:
- if (need_start_waiting)
+
+ DBUG_ASSERT(!thd->in_active_multi_stmt_transaction() ||
+ thd->in_multi_stmt_transaction_mode());
+
+ lex->unit.cleanup();
+
+ if (! thd->in_sub_stmt)
+ {
+ if (thd->killed != NOT_KILLED)
+ {
+ /* report error issued during command execution */
+ if (thd->killed_errno())
+ {
+ /* If we already sent 'ok', we can ignore any kill query statements */
+ if (! thd->stmt_da->is_set())
+ thd->send_kill_message();
+ }
+ if (thd->killed < KILL_CONNECTION)
+ {
+ thd->reset_killed();
+ thd->mysys_var->abort= 0;
+ }
+ }
+ if (thd->is_error() || (thd->variables.option_bits & OPTION_MASTER_SQL_ERROR))
+ trans_rollback_stmt(thd);
+ else
+ {
+ /* If commit fails, we should be able to reset the OK status. */
+ thd->stmt_da->can_overwrite_status= TRUE;
+ trans_commit_stmt(thd);
+ thd->stmt_da->can_overwrite_status= FALSE;
+ }
+#ifdef WITH_ARIA_STORAGE_ENGINE
+ ha_maria::implicit_commit(thd, FALSE);
+#endif
+ }
+
+ /* Free tables */
+ close_thread_tables(thd);
+ thd_proc_info(thd, 0);
+
+#ifndef DBUG_OFF
+ if (lex->sql_command != SQLCOM_SET_OPTION && ! thd->in_sub_stmt)
+ DEBUG_SYNC(thd, "execute_command_after_close_tables");
+#endif
+
+ if (! thd->in_sub_stmt && thd->transaction_rollback_request)
{
/*
- Release the protection against the global read lock and wake
- everyone, who might want to set a global read lock.
+ We are not in sub-statement and transaction rollback was requested by
+ one of storage engines (e.g. due to deadlock). Rollback transaction in
+ all storage engines including binary log.
*/
- start_waiting_global_read_lock(thd);
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+ else if (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END))
+ {
+ /* No transaction control allowed in sub-statements. */
+ DBUG_ASSERT(! thd->in_sub_stmt);
+ /* If commit fails, we should be able to reset the OK status. */
+ thd->stmt_da->can_overwrite_status= TRUE;
+ /* Commit the normal transaction if one is active. */
+ trans_commit_implicit(thd);
+ thd->stmt_da->can_overwrite_status= FALSE;
+ thd->mdl_context.release_transactional_locks();
+ }
+ else if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode())
+ {
+ /*
+ - If inside a multi-statement transaction,
+ defer the release of metadata locks until the current
+ transaction is either committed or rolled back. This prevents
+ other statements from modifying the table for the entire
+ duration of this transaction. This provides commit ordering
+ and guarantees serializability across multiple transactions.
+ - If in autocommit mode, or outside a transactional context,
+ automatically release metadata locks of the current statement.
+ */
+ thd->mdl_context.release_transactional_locks();
+ }
+ else if (! thd->in_sub_stmt)
+ {
+ thd->mdl_context.release_statement_locks();
}
- DBUG_RETURN(res || thd->is_error());
-error:
- thd_proc_info(thd, "query end");
- res= TRUE;
- goto finish;
+ DBUG_RETURN(res || thd->is_error());
}
@@ -5137,7 +4644,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
param->select_limit=
new Item_int((ulonglong) thd->variables.select_limit);
}
- if (!(res= open_and_lock_tables(thd, all_tables)))
+ if (!(res= open_and_lock_tables(thd, all_tables, TRUE, 0)))
{
if (lex->describe)
{
@@ -5151,17 +4658,25 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
return 1; /* purecov: inspected */
thd->send_explain_fields(result);
res= mysql_explain_union(thd, &thd->lex->unit, result);
- if (lex->describe & DESCRIBE_EXTENDED)
+ /*
+ The code which prints the extended description is not robust
+ against malformed queries, so skip it if we have an error.
+ */
+ if (!res && (lex->describe & DESCRIBE_EXTENDED))
{
char buff[1024];
String str(buff,(uint32) sizeof(buff), system_charset_info);
str.length(0);
- thd->lex->unit.print(&str, QT_ORDINARY);
+ /*
+ The warnings system requires input in utf8, @see
+ mysqld_show_warnings().
+ */
+ thd->lex->unit.print(&str, QT_TO_SYSTEM_CHARSET);
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_YES, str.c_ptr_safe());
}
if (res)
- result->abort();
+ result->abort_result_set();
else
result->send_eof();
delete result;
@@ -5179,7 +4694,8 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
/* Count number of empty select queries */
if (!thd->sent_row_count)
status_var_increment(thd->status_var.empty_queries);
- status_var_add(thd->status_var.rows_sent, thd->sent_row_count);
+ else
+ status_var_add(thd->status_var.rows_sent, thd->sent_row_count);
return res;
}
@@ -5189,7 +4705,8 @@ static bool execute_show_status(THD *thd, TABLE_LIST *all_tables)
bool res;
system_status_var old_status_var= thd->status_var;
thd->initial_status_var= &old_status_var;
- if (!(res= check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE)))
+ if (!(res= check_table_access(thd, SELECT_ACL, all_tables, FALSE,
+ UINT_MAX, FALSE)))
res= execute_sqlcom_select(thd, all_tables);
/* Don't log SHOW STATUS commands to slow query log */
thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED |
@@ -5198,11 +4715,11 @@ static bool execute_show_status(THD *thd, TABLE_LIST *all_tables)
restore status variables, as we don't want 'show status' to cause
changes
*/
- pthread_mutex_lock(&LOCK_status);
+ mysql_mutex_lock(&LOCK_status);
add_diff_to_status(&global_status_var, &thd->status_var,
&old_status_var);
thd->status_var= old_status_var;
- pthread_mutex_unlock(&LOCK_status);
+ mysql_mutex_unlock(&LOCK_status);
return res;
}
@@ -5215,10 +4732,13 @@ static bool execute_rename_table(THD *thd, TABLE_LIST *first_table,
for (table= first_table; table; table= table->next_local->next_local)
{
if (check_access(thd, ALTER_ACL | DROP_ACL, table->db,
- &table->grant.privilege,0,0, test(table->schema_table)) ||
+ &table->grant.privilege,
+ &table->grant.m_internal,
+ 0, 0) ||
check_access(thd, INSERT_ACL | CREATE_ACL, table->next_local->db,
- &table->next_local->grant.privilege, 0, 0,
- test(table->next_local->schema_table)))
+ &table->next_local->grant.privilege,
+ &table->next_local->grant.m_internal,
+ 0, 0))
return 1;
TABLE_LIST old_list, new_list;
/*
@@ -5227,16 +4747,15 @@ static bool execute_rename_table(THD *thd, TABLE_LIST *first_table,
*/
old_list= table[0];
new_list= table->next_local[0];
- if (check_grant(thd, ALTER_ACL | DROP_ACL, &old_list, 0, 1, 0) ||
- (!test_all_bits(table->next_local->grant.privilege,
- INSERT_ACL | CREATE_ACL) &&
- check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0, 1, 0)))
+ if (check_grant(thd, ALTER_ACL | DROP_ACL, &old_list, FALSE, 1, FALSE) ||
+ (!test_all_bits(table->next_local->grant.privilege,
+ INSERT_ACL | CREATE_ACL) &&
+ check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, FALSE, 1,
+ FALSE)))
return 1;
}
- if (end_active_trans(thd) || mysql_rename_tables(thd, first_table, 0))
- return 1;
- return 0;
+ return mysql_rename_tables(thd, first_table, 0);
}
@@ -5273,14 +4792,15 @@ bool check_single_table_access(THD *thd, ulong privilege,
db_name= all_tables->db;
if (check_access(thd, privilege, db_name,
- &all_tables->grant.privilege, 0, no_errors,
- test(all_tables->schema_table)))
+ &all_tables->grant.privilege,
+ &all_tables->grant.m_internal,
+ 0, no_errors))
goto deny;
/* Show only 1 table for check_grant */
if (!(all_tables->belong_to_view &&
(thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) &&
- check_grant(thd, privilege, all_tables, 0, 1, no_errors))
+ check_grant(thd, privilege, all_tables, FALSE, 1, no_errors))
goto deny;
thd->security_ctx= backup_ctx;
@@ -5325,7 +4845,8 @@ bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables)
subselects_tables= subselects_tables->next_global;
}
if (subselects_tables &&
- (check_table_access(thd, SELECT_ACL, subselects_tables, UINT_MAX, FALSE)))
+ (check_table_access(thd, SELECT_ACL, subselects_tables, FALSE,
+ UINT_MAX, FALSE)))
return 1;
}
return 0;
@@ -5333,37 +4854,46 @@ bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables)
/**
- Get the user (global) and database privileges for all used tables.
-
- @param save_priv In this we store global and db level grants for the
- table. Note that we don't store db level grants if the
- global grants is enough to satisfy the request and the
- global grants contains a SELECT grant.
-
- @note
- The idea of EXTRA_ACL is that one will be granted access to the table if
- one has the asked privilege on any column combination of the table; For
- example to be able to check a table one needs to have SELECT privilege on
- any column of the table.
-
- @retval
- 0 ok
- @retval
- 1 If we can't get the privileges and we don't use table/column
- grants.
+ @brief Compare requested privileges with the privileges acquired from the
+ User- and Db-tables.
+ @param thd Thread handler
+ @param want_access The requested access privileges.
+ @param db A pointer to the Db name.
+ @param[out] save_priv A pointer to the granted privileges will be stored.
+ @param grant_internal_info A pointer to the internal grant cache.
+ @param dont_check_global_grants True if no global grants are checked.
+ @param no_error True if no errors should be sent to the client.
+
+ 'save_priv' is used to save the User-table (global) and Db-table grants for
+ the supplied db name. Note that we don't store db level grants if the global
+ grants is enough to satisfy the request AND the global grants contains a
+ SELECT grant.
+
+ For internal databases (INFORMATION_SCHEMA, PERFORMANCE_SCHEMA),
+ additional rules apply, see ACL_internal_schema_access.
+
+ @see check_grant
+
+ @return Status of denial of access by exclusive ACLs.
+ @retval FALSE Access can't exclusively be denied by Db- and User-table
+ access unless Column- and Table-grants are checked too.
+ @retval TRUE Access denied.
*/
+
bool
check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
- bool dont_check_global_grants, bool no_errors, bool schema_db)
+ GRANT_INTERNAL_INFO *grant_internal_info,
+ bool dont_check_global_grants, bool no_errors)
{
Security_context *sctx= thd->security_ctx;
ulong db_access;
+
/*
GRANT command:
In case of database level grant the database name may be a pattern,
in case of table|column level grant the database name can not be a pattern.
We use 'dont_check_global_grants' as a flag to determine
- if it's database level grant command
+ if it's database level grant command
(see SQLCOM_GRANT case, mysql_execute_command() function) and
set db_is_pattern according to 'dont_check_global_grants' value.
*/
@@ -5372,10 +4902,14 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
DBUG_ENTER("check_access");
DBUG_PRINT("enter",("db: %s want_access: %lu master_access: %lu",
db ? db : "", want_access, sctx->master_access));
+
if (save_priv)
*save_priv=0;
else
+ {
save_priv= &dummy;
+ dummy= 0;
+ }
thd_proc_info(thd, "checking permissions");
if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
@@ -5387,50 +4921,76 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
DBUG_RETURN(TRUE); /* purecov: tested */
}
- if (schema_db)
+ if ((db != NULL) && (db != any_db))
{
- if ((!(sctx->master_access & FILE_ACL) && (want_access & FILE_ACL)) ||
- (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL)))
+ const ACL_internal_schema_access *access;
+ access= get_cached_schema_access(grant_internal_info, db);
+ if (access)
{
- if (!no_errors)
+ switch (access->check(want_access, save_priv))
{
- const char *db_name= db ? db : thd->db;
- status_var_increment(thd->status_var.access_denied_errors);
- my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
- sctx->priv_user, sctx->priv_host, db_name);
+ case ACL_INTERNAL_ACCESS_GRANTED:
+ /*
+ All the privileges requested have been granted internally.
+ [out] *save_privileges= Internal privileges.
+ */
+ DBUG_RETURN(FALSE);
+ case ACL_INTERNAL_ACCESS_DENIED:
+ if (! no_errors)
+ {
+ status_var_increment(thd->status_var.access_denied_errors);
+ my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
+ sctx->priv_user, sctx->priv_host, db);
+ }
+ DBUG_RETURN(TRUE);
+ case ACL_INTERNAL_ACCESS_CHECK_GRANT:
+ /*
+ Only some of the privilege requested have been granted internally,
+ proceed with the remaining bits of the request (want_access).
+ */
+ want_access&= ~(*save_priv);
+ break;
}
- DBUG_RETURN(TRUE);
- }
- else
- {
- *save_priv= SELECT_ACL;
- DBUG_RETURN(FALSE);
}
}
if ((sctx->master_access & want_access) == want_access)
{
/*
- If we don't have a global SELECT privilege, we have to get the database
- specific access rights to be able to handle queries of type
+ 1. If we don't have a global SELECT privilege, we have to get the
+ database specific access rights to be able to handle queries of type
UPDATE t1 SET a=1 WHERE b > 0
+ 2. Change db access if it isn't current db which is being addressed
*/
- db_access= sctx->db_access;
- if (!(sctx->master_access & SELECT_ACL) &&
- (db && (!thd->db || db_is_pattern || strcmp(db,thd->db))))
- db_access=acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
- db_is_pattern);
- *save_priv=sctx->master_access | db_access;
+ if (!(sctx->master_access & SELECT_ACL))
+ {
+ if (db && (!thd->db || db_is_pattern || strcmp(db, thd->db)))
+ db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
+ db_is_pattern);
+ else
+ {
+ /* get access for current db */
+ db_access= sctx->db_access;
+ }
+ /*
+ The effective privileges are the union of the global privileges
+ and the intersection of db- and host-privileges,
+ plus the internal privileges.
+ */
+ *save_priv|= sctx->master_access | db_access;
+ }
+ else
+ *save_priv|= sctx->master_access;
DBUG_RETURN(FALSE);
}
- if (((want_access & ~sctx->master_access) & ~(DB_ACLS | EXTRA_ACL)) ||
+ if (((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)
{
status_var_increment(thd->status_var.access_denied_errors);
- my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
+ my_error(access_denied_error_code(thd->password), MYF(0),
sctx->priv_user,
sctx->priv_host,
(thd->password ?
@@ -5441,25 +5001,57 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
}
if (db == any_db)
- DBUG_RETURN(FALSE); // Allow select on anything
+ {
+ /*
+ Access granted; Allow select on *any* db.
+ [out] *save_privileges= 0
+ */
+ DBUG_RETURN(FALSE);
+ }
if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))
db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
db_is_pattern);
else
db_access= sctx->db_access;
- DBUG_PRINT("info",("db_access: %lu", db_access));
- /* Remove SHOW attribute and access rights we already have */
- want_access &= ~(sctx->master_access | EXTRA_ACL);
DBUG_PRINT("info",("db_access: %lu want_access: %lu",
db_access, want_access));
- db_access= ((*save_priv=(db_access | sctx->master_access)) & want_access);
- if (db_access == want_access ||
+ /*
+ Save the union of User-table and the intersection between Db-table and
+ Host-table privileges, with the already saved internal privileges.
+ */
+ db_access= (db_access | sctx->master_access);
+ *save_priv|= db_access;
+
+ /*
+ We need to investigate column- and table access if all requested privileges
+ belongs to the bit set of .
+ */
+ bool need_table_or_column_check=
+ (want_access & (TABLE_ACLS | PROC_ACLS | db_access)) == want_access;
+
+ /*
+ Grant access if the requested access is in the intersection of
+ host- and db-privileges (as retrieved from the acl cache),
+ also grant access if all the requested privileges are in the union of
+ TABLES_ACLS and PROC_ACLS; see check_grant.
+ */
+ if ( (db_access & want_access) == want_access ||
(!dont_check_global_grants &&
- !(want_access & ~(db_access | TABLE_ACLS | PROC_ACLS))))
- DBUG_RETURN(FALSE); /* Ok */
+ need_table_or_column_check))
+ {
+ /*
+ Ok; but need to check table- and column privileges.
+ [out] *save_privileges is (User-priv | (Db-priv & Host-priv) | Internal-priv)
+ */
+ DBUG_RETURN(FALSE);
+ }
+ /*
+ Access is denied;
+ [out] *save_privileges is (User-priv | (Db-priv & Host-priv) | Internal-priv)
+ */
DBUG_PRINT("error",("Access denied"));
if (!no_errors)
{
@@ -5468,14 +5060,26 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
sctx->priv_user, sctx->priv_host,
(db ? db : (thd->db ?
thd->db :
- "unknown"))); /* purecov: tested */
+ "unknown")));
}
- DBUG_RETURN(TRUE); /* purecov: tested */
+ DBUG_RETURN(TRUE);
}
static bool check_show_access(THD *thd, TABLE_LIST *table)
{
+ /*
+ This is a SHOW command using an INFORMATION_SCHEMA table.
+ check_access() has not been called for 'table',
+ and SELECT is currently always granted on the I_S, so we automatically
+ grant SELECT on table here, to bypass a call to check_access().
+ Note that not calling check_access(table) is an optimization,
+ which needs to be revisited if the INFORMATION_SCHEMA does
+ not always automatically grant SELECT but use the grant tables.
+ See Bug#38837 need a way to disable information_schema for security
+ */
+ table->grant.privilege= SELECT_ACL;
+
switch (get_schema_table_idx(table->schema_table)) {
case SCH_SCHEMATA:
return (specialflag & SPECIAL_SKIP_SHOW_DB) &&
@@ -5492,8 +5096,7 @@ static bool check_show_access(THD *thd, TABLE_LIST *table)
DBUG_ASSERT(dst_db_name);
if (check_access(thd, SELECT_ACL, dst_db_name,
- &thd->col_access, FALSE, FALSE,
- is_schema_db(dst_db_name)))
+ &thd->col_access, NULL, FALSE, FALSE))
return TRUE;
if (!thd->col_access && check_grant_db(thd, dst_db_name))
@@ -5517,14 +5120,21 @@ static bool check_show_access(THD *thd, TABLE_LIST *table)
DBUG_ASSERT(dst_table);
- if (check_access(thd, SELECT_ACL | EXTRA_ACL,
- dst_table->db,
+ if (check_access(thd, SELECT_ACL, dst_table->db,
&dst_table->grant.privilege,
- FALSE, FALSE,
- test(dst_table->schema_table)))
- return FALSE;
+ &dst_table->grant.m_internal,
+ FALSE, FALSE))
+ return TRUE; /* Access denied */
+
+ /*
+ Check_grant will grant access if there is any column privileges on
+ all of the tables thanks to the fourth parameter (bool show_table).
+ */
+ if (check_grant(thd, SELECT_ACL, dst_table, TRUE, UINT_MAX, FALSE))
+ return TRUE; /* Access denied */
- return (check_grant(thd, SELECT_ACL, dst_table, 2, UINT_MAX, FALSE));
+ /* Access granted */
+ return FALSE;
}
default:
break;
@@ -5534,60 +5144,65 @@ static bool check_show_access(THD *thd, TABLE_LIST *table)
}
-/**
- Check the privilege for all used tables.
- @param thd Thread context
- @param want_access Privileges requested
- @param tables List of tables to be checked
- @param number Check at most this number of tables.
- @param no_errors FALSE/TRUE - report/don't report error to
- the client (using my_error() call).
+/**
+ @brief Check if the requested privileges exists in either User-, Host- or
+ Db-tables.
+ @param thd Thread context
+ @param want_access Privileges requested
+ @param tables List of tables to be compared against
+ @param no_errors Don't report error to the client (using my_error() call).
+ @param any_combination_of_privileges_will_do TRUE if any privileges on any
+ column combination is enough.
+ @param number Only the first 'number' tables in the linked list are
+ relevant.
+
+ The suppled table list contains cached privileges. This functions calls the
+ help functions check_access and check_grant to verify the first three steps
+ in the privileges check queue:
+ 1. Global privileges
+ 2. OR (db privileges AND host privileges)
+ 3. OR table privileges
+ 4. OR column privileges (not checked by this function!)
+ 5. OR routine privileges (not checked by this function!)
+
+ @see check_access
+ @see check_grant
+
+ @note This functions assumes that table list used and
+ thd->lex->query_tables_own_last value correspond to each other
+ (the latter should be either 0 or point to next_global member
+ of one of elements of this table list).
- @note
- Table privileges are cached in the table list for GRANT checking.
- This functions assumes that table list used and
- thd->lex->query_tables_own_last value correspond to each other
- (the latter should be either 0 or point to next_global member
- of one of elements of this table list).
-
- @retval FALSE OK
- @retval TRUE Access denied
+ @return
+ @retval FALSE OK
+ @retval TRUE Access denied; But column or routine privileges might need to
+ be checked also.
*/
bool
-check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
- uint number, bool no_errors)
+check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
+ bool any_combination_of_privileges_will_do,
+ uint number, bool no_errors)
{
TABLE_LIST *org_tables= tables;
TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
Security_context *sctx= thd->security_ctx, *backup_ctx= thd->security_ctx;
- uint i;
+ uint i= 0;
/*
The check that first_not_own_table is not reached is for the case when
the given table list refers to the list for prelocking (contains tables
of other queries). For simple queries first_not_own_table is 0.
*/
- for (i=0; i < number && tables != first_not_own_table;
+ for (; i < number && tables != first_not_own_table && tables;
tables= tables->next_global, i++)
{
+ ulong want_access= requirements;
if (tables->security_ctx)
sctx= tables->security_ctx;
else
sctx= backup_ctx;
- if (tables->schema_table &&
- (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL)))
- {
- if (!no_errors)
- {
- status_var_increment(thd->status_var.access_denied_errors);
- my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
- sctx->priv_user, sctx->priv_host,
- INFORMATION_SCHEMA_NAME.str);
- }
- return TRUE;
- }
/*
Register access for view underlying table.
Remove SHOW_VIEW_ACL, because it will be checked during making view
@@ -5598,33 +5213,27 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
{
if (check_show_access(thd, tables))
goto deny;
-
continue;
}
+ DBUG_PRINT("info", ("derived: %d view: %d", tables->derived != 0,
+ tables->view != 0));
if (tables->is_anonymous_derived_table() ||
- (tables->table && (int)tables->table->s->tmp_table))
+ (tables->table && tables->table->s &&
+ (int)tables->table->s->tmp_table))
continue;
thd->security_ctx= sctx;
- if ((sctx->master_access & want_access) ==
- (want_access & ~EXTRA_ACL) &&
- thd->db)
- tables->grant.privilege= want_access;
- else if (tables->db && thd->db && strcmp(tables->db, thd->db) == 0)
- {
- if (check_access(thd, want_access, tables->get_db_name(),
- &tables->grant.privilege, 0, no_errors,
- test(tables->schema_table)))
- goto deny; // Access denied
- }
- else if (check_access(thd, want_access, tables->get_db_name(),
- &tables->grant.privilege, 0, no_errors,
- test(tables->schema_table)))
+
+ if (check_access(thd, want_access, tables->get_db_name(),
+ &tables->grant.privilege,
+ &tables->grant.m_internal,
+ 0, no_errors))
goto deny;
}
thd->security_ctx= backup_ctx;
- return check_grant(thd,want_access & ~EXTRA_ACL,org_tables,
- test(want_access & EXTRA_ACL), number, no_errors);
+ return check_grant(thd,requirements,org_tables,
+ any_combination_of_privileges_will_do,
+ number, no_errors);
deny:
thd->security_ctx= backup_ctx;
return TRUE;
@@ -5646,14 +5255,23 @@ check_routine_access(THD *thd, ulong want_access,char *db, char *name,
calculating db_access) under the assumption that it's common to
give persons global right to execute all stored SP (but not
necessary to create them).
+ Note that this effectively bypasses the ACL_internal_schema_access checks
+ that are implemented for the INFORMATION_SCHEMA and PERFORMANCE_SCHEMA,
+ which are located in check_access().
+ Since the I_S and P_S do not contain routines, this bypass is ok,
+ as long as this code path is not abused to create routines.
+ The assert enforce that.
*/
+ 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,&tables->grant.privilege,
- 0, no_errors, 0))
+ else if (check_access(thd, want_access, db,
+ &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, is_proc, no_errors);
}
@@ -5674,13 +5292,18 @@ bool check_some_routine_access(THD *thd, const char *db, const char *name,
bool is_proc)
{
ulong save_priv;
- if (thd->security_ctx->master_access & SHOW_PROC_ACLS)
- return FALSE;
/*
- There are no routines in information_schema db. So we can safely
- pass zero to last paramter of check_access function
+ The following test is just a shortcut for check_access() (to avoid
+ calculating db_access)
+ Note that this effectively bypasses the ACL_internal_schema_access checks
+ that are implemented for the INFORMATION_SCHEMA and PERFORMANCE_SCHEMA,
+ which are located in check_access().
+ Since the I_S and P_S do not contain routines, this bypass is ok,
+ as it only opens SHOW_PROC_ACLS.
*/
- if (!check_access(thd, SHOW_PROC_ACLS, db, &save_priv, 0, 1, 0) ||
+ if (thd->security_ctx->master_access & SHOW_PROC_ACLS)
+ return FALSE;
+ 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);
@@ -5710,9 +5333,10 @@ bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
if (access & want_access)
{
if (!check_access(thd, access, table->db,
- &table->grant.privilege, 0, 1,
- test(table->schema_table)) &&
- !check_grant(thd, access, table, 0, 1, 1))
+ &table->grant.privilege,
+ &table->grant.m_internal,
+ 0, 1) &&
+ !check_grant(thd, access, table, FALSE, 1, TRUE))
DBUG_RETURN(0);
}
}
@@ -5741,14 +5365,17 @@ bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
1 Access denied. In this case an error is sent to the client
*/
-bool check_global_access(THD *thd, ulong want_access)
+bool check_global_access(THD *thd, ulong want_access, bool no_errors)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
char command[128];
if ((thd->security_ctx->master_access & want_access))
return 0;
- get_privilege_desc(command, sizeof(command), want_access);
- my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
+ if (!no_errors)
+ {
+ get_privilege_desc(command, sizeof(command), want_access);
+ my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
+ }
status_var_increment(thd->status_var.access_denied_errors);
return 1;
#else
@@ -5760,7 +5387,6 @@ bool check_global_access(THD *thd, ulong want_access)
Check stack size; Send error if there isn't enough stack to continue
****************************************************************************/
-#ifndef EMBEDDED_LIBRARY
#if STACK_DIRECTION < 0
#define used_stack(A,B) (long) (A - B)
@@ -5787,11 +5413,17 @@ bool check_stack_overrun(THD *thd, long margin,
if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >=
(long) (my_thread_stack_size - margin))
{
- char ebuff[MYSQL_ERRMSG_SIZE];
- my_snprintf(ebuff, sizeof(ebuff), ER(ER_STACK_OVERRUN_NEED_MORE),
- stack_used, my_thread_stack_size, margin);
- my_message(ER_STACK_OVERRUN_NEED_MORE, ebuff, MYF(ME_FATALERROR));
- thd->fatal_error();
+ /*
+ Do not use stack for the message buffer to ensure correct
+ behaviour in cases we have close to no stack left.
+ */
+ char* ebuff= new char[MYSQL_ERRMSG_SIZE];
+ if (ebuff) {
+ my_snprintf(ebuff, MYSQL_ERRMSG_SIZE, ER(ER_STACK_OVERRUN_NEED_MORE),
+ stack_used, my_thread_stack_size, margin);
+ my_message(ER_STACK_OVERRUN_NEED_MORE, ebuff, MYF(ME_FATALERROR));
+ delete [] ebuff;
+ }
return 1;
}
#ifndef DBUG_OFF
@@ -5799,7 +5431,7 @@ bool check_stack_overrun(THD *thd, long margin,
#endif
return 0;
}
-#endif /* EMBEDDED_LIBRARY */
+
#define MY_YACC_INIT 1000 // Start with big alloc
#define MY_YACC_MAX 32000 // Because of 'short'
@@ -5840,22 +5472,27 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
/**
- Reset THD part responsible for command processing state.
+ Reset the part of THD responsible for the state of command
+ processing.
- This needs to be called before execution of every statement
- (prepared or conventional).
- It is not called by substatements of routines.
+ This needs to be called before execution of every statement
+ (prepared or conventional). It is not called by substatements of
+ routines.
- @todo
- Make it a method of THD and align its name with the rest of
- reset/end/start/init methods.
- @todo
- Call it after we use THD for queries, not before.
+ @todo Remove mysql_reset_thd_for_next_command and only use the
+ member function.
+
+ @todo Call it after we use THD for queries, not before.
*/
+void mysql_reset_thd_for_next_command(THD *thd)
+{
+ thd->reset_for_next_command();
+}
-void mysql_reset_thd_for_next_command(THD *thd, my_bool calculate_userstat)
+void THD::reset_for_next_command()
{
- DBUG_ENTER("mysql_reset_thd_for_next_command");
+ 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(thd->transaction.on);
@@ -5881,9 +5518,9 @@ void mysql_reset_thd_for_next_command(THD *thd, my_bool calculate_userstat)
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->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
+ if (!thd->in_multi_stmt_transaction_mode())
{
- thd->options&= ~OPTION_KEEP_LOG;
+ thd->variables.option_bits&= ~OPTION_KEEP_LOG;
thd->transaction.all.modified_non_trans_table= FALSE;
}
DBUG_ASSERT(thd->security_ctx== &thd->main_security_ctx);
@@ -5895,31 +5532,21 @@ void mysql_reset_thd_for_next_command(THD *thd, my_bool calculate_userstat)
thd->user_var_events_alloc= thd->mem_root;
}
thd->clear_error();
- thd->main_da.reset_diagnostics_area();
- thd->total_warn_count=0; // Warnings for this query
+ thd->stmt_da->reset_diagnostics_area();
+ thd->warning_info->reset_for_next_command();
thd->rand_used= 0;
thd->sent_row_count= thd->examined_row_count= 0;
-
- /* Copy data for user stats */
- if ((thd->userstat_running= calculate_userstat))
- {
- thd->start_cpu_time= my_getcputime();
- memcpy(&thd->org_status_var, &thd->status_var, sizeof(thd->status_var));
- thd->select_commands= thd->update_commands= thd->other_commands= 0;
- }
+ thd->accessed_rows_and_keys= 0;
thd->query_plan_flags= QPLAN_INIT;
thd->query_plan_fsort_passes= 0;
- /*
- Because we come here only for start of top-statements, binlog format is
- constant inside a complex statement (using stored functions) etc.
- */
- thd->reset_current_stmt_binlog_row_based();
+ thd->reset_current_stmt_binlog_format_row();
+ thd->binlog_unsafe_warning_flags= 0;
DBUG_PRINT("debug",
- ("current_stmt_binlog_row_based: %d",
- thd->current_stmt_binlog_row_based));
+ ("is_current_stmt_binlog_format_row(): %d",
+ thd->is_current_stmt_binlog_format_row()));
DBUG_VOID_RETURN;
}
@@ -6075,7 +5702,6 @@ void mysql_init_multi_delete(LEX *lex)
lex->select_lex.select_limit= 0;
lex->unit.select_limit_cnt= HA_POS_ERROR;
lex->select_lex.table_list.save_and_clear(&lex->auxiliary_table_list);
- lex->lock_option= TL_READ_DEFAULT;
lex->query_tables= 0;
lex->query_tables_last= &lex->query_tables;
}
@@ -6097,8 +5723,9 @@ void mysql_init_multi_delete(LEX *lex)
*/
void mysql_parse(THD *thd, char *rawbuf, uint length,
- const char ** found_semicolon)
+ Parser_state *parser_state)
{
+ int error __attribute__((unused));
DBUG_ENTER("mysql_parse");
DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on(););
@@ -6119,24 +5746,13 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
FIXME: cleanup the dependencies in the code to simplify this.
*/
lex_start(thd);
- mysql_reset_thd_for_next_command(thd, opt_userstat_running);
+ mysql_reset_thd_for_next_command(thd);
if (query_cache_send_result_to_client(thd, rawbuf, length) <= 0)
{
LEX *lex= thd->lex;
- sp_cache_flush_obsolete(&thd->sp_proc_cache);
- sp_cache_flush_obsolete(&thd->sp_func_cache);
-
- Parser_state parser_state;
- bool err;
- if (!(err= parser_state.init(thd, rawbuf, length)))
- {
- err= parse_sql(thd, & parser_state, NULL);
- *found_semicolon= parser_state.m_lip.found_semicolon;
- }
- else
- *found_semicolon= NULL;
+ bool err= parse_sql(thd, parser_state, NULL);
if (!err)
{
@@ -6151,6 +5767,7 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
{
if (! thd->is_error())
{
+ const char *found_semicolon= parser_state->m_lip.found_semicolon;
/*
Binlog logs a string starting from thd->query and having length
thd->query_length; so we set thd->query_length correctly (to not
@@ -6161,18 +5778,27 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
PROCESSLIST.
Note that we don't need LOCK_thread_count to modify query_length.
*/
- if (*found_semicolon && (ulong) (*found_semicolon - thd->query()))
+ if (found_semicolon && (ulong) (found_semicolon - thd->query()))
thd->set_query_inner(thd->query(),
- (uint32) (*found_semicolon -
- thd->query() - 1));
+ (uint32) (found_semicolon -
+ thd->query() - 1),
+ thd->charset());
/* Actually execute the query */
- if (*found_semicolon)
+ if (found_semicolon)
{
lex->safe_to_cache_query= 0;
thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
}
lex->set_trg_event_type_for_tables();
- mysql_execute_command(thd);
+ MYSQL_QUERY_EXEC_START(thd->query(),
+ thd->thread_id,
+ (char *) (thd->db ? thd->db : ""),
+ &thd->security_ctx->priv_user[0],
+ (char *) thd->security_ctx->host_or_ip,
+ 0);
+
+ error= mysql_execute_command(thd);
+ MYSQL_QUERY_EXEC_DONE(error);
}
}
}
@@ -6182,15 +5808,11 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
thd->is_fatal_error));
- query_cache_abort(&thd->net);
- }
- if (thd->lex->sphead)
- {
- delete thd->lex->sphead;
- thd->lex->sphead= 0;
+ query_cache_abort(&thd->query_cache_tls);
}
- lex->unit.cleanup();
thd_proc_info(thd, "freeing items");
+ sp_cache_enforce_limit(thd->sp_proc_cache, stored_program_cache_size);
+ sp_cache_enforce_limit(thd->sp_func_cache, stored_program_cache_size);
thd->end_statement();
thd->cleanup_after_query();
DBUG_ASSERT(thd->change_list.is_empty());
@@ -6199,8 +5821,8 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
{
/* Update statistics for getting the query from the cache */
thd->lex->sql_command= SQLCOM_SELECT;
- /* There are no multi queries in the cache. */
- *found_semicolon= NULL;
+ status_var_increment(thd->status_var.com_stat[SQLCOM_SELECT]);
+ thd->update_stats();
}
DBUG_VOID_RETURN;
}
@@ -6227,7 +5849,7 @@ bool mysql_test_parse_for_slave(THD *thd, char *rawbuf, uint length)
if (!(error= parser_state.init(thd, rawbuf, length)))
{
lex_start(thd);
- mysql_reset_thd_for_next_command(thd, opt_userstat_running);
+ mysql_reset_thd_for_next_command(thd);
if (!parse_sql(thd, & parser_state, NULL) &&
all_tables_not_ok(thd, lex->select_lex.table_list.first))
@@ -6272,8 +5894,8 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
if (type_modifier & PRI_KEY_FLAG)
{
Key *key;
- lex->col_list.push_back(new Key_part_spec(field_name->str, 0));
- key= new Key(Key::PRIMARY, NullS,
+ lex->col_list.push_back(new Key_part_spec(*field_name, 0));
+ key= new Key(Key::PRIMARY, null_lex_str,
&default_key_create_info,
0, lex->col_list, NULL);
lex->alter_info.key_list.push_back(key);
@@ -6282,8 +5904,8 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
{
Key *key;
- lex->col_list.push_back(new Key_part_spec(field_name->str, 0));
- key= new Key(Key::UNIQUE, NullS,
+ lex->col_list.push_back(new Key_part_spec(*field_name, 0));
+ key= new Key(Key::UNIQUE, null_lex_str,
&default_key_create_info, 0,
lex->col_list, NULL);
lex->alter_info.key_list.push_back(key);
@@ -6346,7 +5968,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
void store_position_for_column(const char *name)
{
- current_thd->lex->last_field->after=my_const_cast(char*) (name);
+ current_thd->lex->last_field->after=(char*) (name);
}
bool
@@ -6397,6 +6019,7 @@ bool add_to_list(THD *thd, SQL_I_List<ORDER> &list, Item *item,bool asc)
- TL_OPTION_FORCE_INDEX : Force usage of index
- TL_OPTION_ALIAS : an alias in multi table DELETE
@param lock_type How table should be locked
+ @param mdl_type Type of metadata lock to acquire on the table.
@param use_index List of indexed used in USE INDEX
@param ignore_index List of indexed used in IGNORE INDEX
@@ -6411,6 +6034,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
LEX_STRING *alias,
ulong table_options,
thr_lock_type lock_type,
+ enum_mdl_type mdl_type,
List<Index_hint> *index_hints_arg,
LEX_STRING *option)
{
@@ -6453,13 +6077,17 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
DBUG_RETURN(0); /* purecov: inspected */
if (table->db.str)
{
+ ptr->is_fqtn= TRUE;
ptr->db= table->db.str;
ptr->db_length= table->db.length;
}
else if (lex->copy_db_to(&ptr->db, &ptr->db_length))
DBUG_RETURN(0);
+ else
+ ptr->is_fqtn= FALSE;
ptr->alias= alias_str;
+ ptr->is_alias= alias ? TRUE : FALSE;
if (lower_case_table_names && table->table.length)
table->table.length= my_casedn_str(files_charset_info, table->table.str);
ptr->table_name=table->table.str;
@@ -6470,9 +6098,21 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX);
ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES);
ptr->derived= table->sel;
- if (!ptr->derived && is_schema_db(ptr->db, ptr->db_length))
+ if (!ptr->derived && is_infoschema_db(ptr->db, ptr->db_length))
{
- ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, ptr->table_name);
+ ST_SCHEMA_TABLE *schema_table;
+ if (ptr->updating &&
+ /* Special cases which are processed by commands itself */
+ lex->sql_command != SQLCOM_CHECK &&
+ lex->sql_command != SQLCOM_CHECKSUM)
+ {
+ my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
+ thd->security_ctx->priv_user,
+ thd->security_ctx->priv_host,
+ INFORMATION_SCHEMA_NAME.str);
+ DBUG_RETURN(0);
+ }
+ schema_table= find_schema_table(thd, ptr->table_name);
if (!schema_table ||
(schema_table->hidden &&
((sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) == 0 ||
@@ -6490,7 +6130,11 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
ptr->schema_table= schema_table;
}
ptr->select_lex= lex->current_select;
- ptr->cacheable_table= 1;
+ /*
+ We can't cache internal temporary tables between prepares as the
+ table may be deleted before next exection.
+ */
+ 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 */
@@ -6542,6 +6186,13 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
ptr->next_name_resolution_table= NULL;
/* Link table in global list (all used tables) */
lex->add_to_query_tables(ptr);
+
+ // Pure table aliases do not need to be locked:
+ if (!test(table_options & TL_OPTION_ALIAS))
+ {
+ ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type,
+ MDL_TRANSACTION);
+ }
DBUG_RETURN(ptr);
}
@@ -6621,6 +6272,7 @@ TABLE_LIST *st_select_lex::end_nested_join(THD *thd)
embedded->embedding= embedding;
join_list->push_front(embedded);
ptr= embedded;
+ embedded->lifted= 1;
}
else if (nested_join->join_list.elements == 0)
{
@@ -6666,6 +6318,8 @@ 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)
+ DBUG_RETURN(NULL);
table->join_list= embedded_list;
table->embedding= ptr;
embedded_list->push_back(table);
@@ -6777,6 +6431,8 @@ void st_select_lex::set_lock_for_tables(thr_lock_type lock_type)
{
tables->lock_type= lock_type;
tables->updating= for_update;
+ tables->mdl_request.set_type((lock_type >= TL_WRITE_ALLOW_WRITE) ?
+ MDL_SHARED_WRITE : MDL_SHARED_READ);
}
DBUG_VOID_RETURN;
}
@@ -6980,289 +6636,6 @@ void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields,
/**
- Reload/resets privileges and the different caches.
-
- @param thd Thread handler (can be NULL!)
- @param options What should be reset/reloaded (tables, privileges, slave...)
- @param tables Tables to flush (if any)
- @param write_to_binlog < 0 if there was an error while interacting with the binary log inside
- reload_acl_and_cache,
- 0 if we should not write to the binary log,
- > 0 if we can write to the binlog.
-
- @note Depending on 'options', it may be very bad to write the
- query to the binlog (e.g. FLUSH SLAVE); this is a
- pointer where reload_acl_and_cache() will put 0 if
- it thinks we really should not write to the binlog.
- Otherwise it will put 1.
-
- @return Error status code
- @retval 0 Ok
- @retval !=0 Error; thd->killed is set or thd->is_error() is true
-*/
-
-bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
- int *write_to_binlog)
-{
- bool result=0;
- select_errors=0; /* Write if more errors */
- int tmp_write_to_binlog= *write_to_binlog= 1;
-
- DBUG_ASSERT(!thd || !thd->in_sub_stmt);
-
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (options & REFRESH_GRANT)
- {
- THD *tmp_thd= 0;
- /*
- 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)))
- {
- thd->thread_stack= (char*) &tmp_thd;
- thd->store_globals();
- lex_start(thd);
- }
-
- if (thd)
- {
- bool reload_acl_failed= acl_reload(thd);
- bool reload_grants_failed= grant_reload(thd);
- bool reload_servers_failed= servers_reload(thd);
-
- if (reload_acl_failed || reload_grants_failed || reload_servers_failed)
- {
- result= 1;
- /*
- When an error is returned, my_message may have not been called and
- the client will hang waiting for a response.
- */
- my_error(ER_UNKNOWN_ERROR, MYF(0), "FLUSH PRIVILEGES failed");
- }
- }
-
- if (tmp_thd)
- {
- delete tmp_thd;
- /* Remember that we don't have a THD */
- my_pthread_setspecific_ptr(THR_THD, 0);
- thd= 0;
- }
- reset_mqh((LEX_USER *)NULL, TRUE);
- }
-#endif
- if (options & REFRESH_LOG)
- {
- /*
- Flush the normal query log, the update log, the binary log,
- the slow query log, the relay log (if it exists) and the log
- tables.
- */
-
- /*
- Writing this command to the binlog may result in infinite loops
- when doing mysqlbinlog|mysql, and anyway it does not really make
- sense to log it automatically (would cause more trouble to users
- than it would help them)
- */
- tmp_write_to_binlog= 0;
- if( mysql_bin_log.is_open() )
- {
- if (mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE))
- *write_to_binlog= -1;
- }
-#ifdef HAVE_REPLICATION
- int rotate_error= 0;
- pthread_mutex_lock(&active_mi->data_lock);
- rotate_error= rotate_relay_log(active_mi);
- pthread_mutex_unlock(&active_mi->data_lock);
- if (rotate_error)
- *write_to_binlog= -1;
-#endif
-
- /* flush slow and general logs */
- logger.flush_logs(thd);
-
- if (ha_flush_logs(NULL))
- result=1;
- 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));
- result= 1;
- }
- }
- if (((options & (REFRESH_SLOW_QUERY_LOG | REFRESH_LOG)) ==
- REFRESH_SLOW_QUERY_LOG))
- {
- /* We are only flushing slow query log */
- logger.flush_slow_log(thd);
- }
-
-#ifdef HAVE_QUERY_CACHE
- if (options & REFRESH_QUERY_CACHE_FREE)
- {
- query_cache.pack(thd); // FLUSH QUERY CACHE
- options &= ~REFRESH_QUERY_CACHE; // Don't flush cache, just free memory
- }
- if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE))
- {
- query_cache.flush(); // RESET QUERY CACHE
- }
-#endif /*HAVE_QUERY_CACHE*/
- /*
- Note that if REFRESH_READ_LOCK bit is set then REFRESH_TABLES is set too
- (see sql_yacc.yy)
- */
- if (options & (REFRESH_TABLES | REFRESH_READ_LOCK))
- {
- if ((options & REFRESH_READ_LOCK) && thd)
- {
- /*
- We must not try to aspire a global read lock if we have a write
- locked table. This would lead to a deadlock when trying to
- reopen (and re-lock) the table after the flush.
- */
- if (thd->locked_tables)
- {
- THR_LOCK_DATA **lock_p= thd->locked_tables->locks;
- THR_LOCK_DATA **end_p= lock_p + thd->locked_tables->lock_count;
-
- for (; lock_p < end_p; lock_p++)
- {
- if ((*lock_p)->type >= TL_WRITE_ALLOW_WRITE)
- {
- my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
- return 1;
- }
- }
- }
- /*
- Writing to the binlog could cause deadlocks, as we don't log
- UNLOCK TABLES
- */
- tmp_write_to_binlog= 0;
- if (lock_global_read_lock(thd))
- return 1; // Killed
- if (close_cached_tables(thd, tables, FALSE, (options & REFRESH_FAST) ?
- FALSE : TRUE, TRUE))
- {
- /*
- NOTE: my_error() has been already called by reopen_tables() within
- close_cached_tables().
- */
- result= 1;
- }
-
- if (make_global_read_lock_block_commit(thd)) // Killed
- {
- /* Don't leave things in a half-locked state */
- unlock_global_read_lock(thd);
- return 1;
- }
- if (options & REFRESH_CHECKPOINT)
- disable_checkpoints(thd);
- }
- else
- {
- if (close_cached_tables(thd, tables, FALSE, (options & REFRESH_FAST) ?
- FALSE : TRUE, FALSE))
- {
- /*
- NOTE: my_error() has been already called by reopen_tables() within
- close_cached_tables().
- */
- result= 1;
- }
- }
- my_dbopt_cleanup();
- }
- if (options & REFRESH_HOSTS)
- hostname_cache_refresh();
- if (thd && (options & REFRESH_STATUS))
- refresh_status(thd);
- if (options & REFRESH_THREADS)
- flush_thread_cache();
-#ifdef HAVE_REPLICATION
- if (options & REFRESH_MASTER)
- {
- DBUG_ASSERT(thd);
- tmp_write_to_binlog= 0;
- if (reset_master(thd))
- {
- /* NOTE: my_error() has been already called by reset_master(). */
- result= 1;
- }
- }
-#endif
-#ifdef OPENSSL
- if (options & REFRESH_DES_KEY_FILE)
- {
- if (des_key_file && load_des_key_file(des_key_file))
- {
- /* NOTE: my_error() has been already called by load_des_key_file(). */
- result= 1;
- }
- }
-#endif
-#ifdef HAVE_REPLICATION
- if (options & REFRESH_SLAVE)
- {
- tmp_write_to_binlog= 0;
- pthread_mutex_lock(&LOCK_active_mi);
- if (reset_slave(thd, active_mi))
- {
- /* NOTE: my_error() has been already called by reset_slave(). */
- result= 1;
- }
- pthread_mutex_unlock(&LOCK_active_mi);
- }
-#endif
- if (options & REFRESH_USER_RESOURCES)
- reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */
- if (options & REFRESH_TABLE_STATS)
- {
- pthread_mutex_lock(&LOCK_global_table_stats);
- free_global_table_stats();
- init_global_table_stats();
- pthread_mutex_unlock(&LOCK_global_table_stats);
- }
- if (options & REFRESH_INDEX_STATS)
- {
- pthread_mutex_lock(&LOCK_global_index_stats);
- free_global_index_stats();
- init_global_index_stats();
- pthread_mutex_unlock(&LOCK_global_index_stats);
- }
- if (options & (REFRESH_USER_STATS | REFRESH_CLIENT_STATS))
- {
- pthread_mutex_lock(&LOCK_global_user_client_stats);
- if (options & REFRESH_USER_STATS)
- {
- free_global_user_stats();
- init_global_user_stats();
- }
- if (options & REFRESH_CLIENT_STATS)
- {
- free_global_client_stats();
- init_global_client_stats();
- }
- pthread_mutex_unlock(&LOCK_global_user_client_stats);
- }
- if (*write_to_binlog != -1)
- *write_to_binlog= tmp_write_to_binlog;
- /*
- If the query was killed then this function must fail.
- */
- return result || (thd ? thd->killed : 0);
-}
-
-
-/**
kill on thread.
@param thd Thread class
@@ -7280,7 +6653,7 @@ uint kill_one_thread(THD *thd, ulong id, killed_state kill_signal)
DBUG_ENTER("kill_one_thread");
DBUG_PRINT("enter", ("id: %lu signal: %u", id, (uint) kill_signal));
- VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list
+ mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
I_List_iterator<THD> it(threads);
while ((tmp=it++))
{
@@ -7288,11 +6661,11 @@ uint kill_one_thread(THD *thd, ulong id, killed_state kill_signal)
continue;
if (tmp->thread_id == id)
{
- pthread_mutex_lock(&tmp->LOCK_thd_kill); // Lock from delete
+ mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
break;
}
}
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ mysql_mutex_unlock(&LOCK_thread_count);
if (tmp)
{
/*
@@ -7319,13 +6692,12 @@ uint kill_one_thread(THD *thd, ulong id, killed_state kill_signal)
if ((thd->security_ctx->master_access & SUPER_ACL) ||
thd->security_ctx->user_matches(tmp->security_ctx))
{
- DEBUG_SYNC(thd, "kill_one_thread_before_kill");
tmp->awake(kill_signal);
error=0;
}
else
error=ER_KILL_DENIED_ERROR;
- pthread_mutex_unlock(&tmp->LOCK_thd_kill);
+ mysql_mutex_unlock(&tmp->LOCK_thd_data);
}
DBUG_PRINT("exit", ("%d", error));
DBUG_RETURN(error);
@@ -7361,7 +6733,7 @@ static uint kill_threads_for_user(THD *thd, LEX_USER *user,
DBUG_PRINT("enter", ("user: %s signal: %u", user->user.str,
(uint) kill_signal));
- VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list
+ mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
I_List_iterator<THD> it(threads);
while ((tmp=it++))
{
@@ -7373,20 +6745,20 @@ static uint kill_threads_for_user(THD *thd, LEX_USER *user,
host.str[0] == '%' means that host name was not given. See sql_yacc.yy
*/
if (((user->host.str[0] == '%' && !user->host.str[1]) ||
- !strcmp(tmp->security_ctx->host, user->host.str)) &&
+ !strcmp(tmp->security_ctx->host_or_ip, user->host.str)) &&
!strcmp(tmp->security_ctx->user, user->user.str))
{
if (!(thd->security_ctx->master_access & SUPER_ACL) &&
!thd->security_ctx->user_matches(tmp->security_ctx))
{
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ mysql_mutex_unlock(&LOCK_thread_count);
DBUG_RETURN(ER_KILL_DENIED_ERROR);
}
if (!threads_to_kill.push_back(tmp, tmp->mem_root))
- pthread_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
+ mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
}
}
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ mysql_mutex_unlock(&LOCK_thread_count);
if (!threads_to_kill.is_empty())
{
List_iterator_fast<THD> it(threads_to_kill);
@@ -7404,7 +6776,7 @@ static uint kill_threads_for_user(THD *thd, LEX_USER *user,
previous list node, we need to do this while holding LOCK_thd_data.
*/
next_ptr= it++;
- pthread_mutex_unlock(&ptr->LOCK_thd_data);
+ mysql_mutex_unlock(&ptr->LOCK_thd_data);
(*rows)++;
} while ((ptr= next_ptr));
}
@@ -7422,16 +6794,23 @@ static uint kill_threads_for_user(THD *thd, LEX_USER *user,
only_kill_query Should it kill the query or the connection
*/
+static
void sql_kill(THD *thd, ulong id, killed_state state)
{
uint error;
if (!(error= kill_one_thread(thd, id, state)))
- my_ok(thd);
+ {
+ if ((!thd->killed))
+ my_ok(thd);
+ else
+ my_error(killed_errno(thd->killed), MYF(0), id);
+ }
else
my_error(error, MYF(0), id);
}
+static
void sql_kill_user(THD *thd, LEX_USER *user, killed_state state)
{
uint error;
@@ -7603,13 +6982,15 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
if (table->derived)
table->grant.privilege= SELECT_ACL;
else if ((check_access(thd, UPDATE_ACL, table->db,
- &table->grant.privilege, 0, 1,
- test(table->schema_table)) ||
- check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) &&
+ &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,
- &table->grant.privilege, 0, 0,
- test(table->schema_table)) ||
- check_grant(thd, SELECT_ACL, table, 0, 1, 0)))
+ &table->grant.privilege,
+ &table->grant.m_internal,
+ 0, 0) ||
+ check_grant(thd, SELECT_ACL, table, FALSE, 1, FALSE)))
DBUG_RETURN(TRUE);
table->table_in_first_from_clause= 1;
@@ -7625,9 +7006,10 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
if (!table->table_in_first_from_clause)
{
if (check_access(thd, SELECT_ACL, table->db,
- &table->grant.privilege, 0, 0,
- test(table->schema_table)) ||
- check_grant(thd, SELECT_ACL, table, 0, 1, 0))
+ &table->grant.privilege,
+ &table->grant.m_internal,
+ 0, 0) ||
+ check_grant(thd, SELECT_ACL, table, FALSE, 1, FALSE))
DBUG_RETURN(TRUE);
}
}
@@ -7666,7 +7048,7 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables)
/* sql_yacc guarantees that tables and aux_tables are not zero */
DBUG_ASSERT(aux_tables != 0);
- if (check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE))
+ if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE))
DBUG_RETURN(TRUE);
/*
@@ -7675,14 +7057,14 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables)
call check_table_access() safely.
*/
thd->lex->query_tables_own_last= 0;
- if (check_table_access(thd, DELETE_ACL, aux_tables, UINT_MAX, FALSE))
+ if (check_table_access(thd, DELETE_ACL, aux_tables, FALSE, UINT_MAX, FALSE))
{
thd->lex->query_tables_own_last= save_query_tables_own_last;
DBUG_RETURN(TRUE);
}
thd->lex->query_tables_own_last= save_query_tables_own_last;
- if ((thd->options & OPTION_SAFE_UPDATES) && !select_lex->where)
+ if ((thd->variables.option_bits & OPTION_SAFE_UPDATES) && !select_lex->where)
{
my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0));
@@ -7692,6 +7074,63 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables)
}
+/*
+ Given a table in the source list, find a correspondent table in the
+ table references list.
+
+ @param lex Pointer to LEX representing multi-delete.
+ @param src Source table to match.
+ @param ref Table references list.
+
+ @remark The source table list (tables listed before the FROM clause
+ or tables listed in the FROM clause before the USING clause) may
+ contain table names or aliases that must match unambiguously one,
+ and only one, table in the target table list (table references list,
+ after FROM/USING clause).
+
+ @return Matching table, NULL otherwise.
+*/
+
+static TABLE_LIST *multi_delete_table_match(LEX *lex, TABLE_LIST *tbl,
+ TABLE_LIST *tables)
+{
+ TABLE_LIST *match= NULL;
+ DBUG_ENTER("multi_delete_table_match");
+
+ for (TABLE_LIST *elem= tables; elem; elem= elem->next_local)
+ {
+ int cmp;
+
+ 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);
+ else if (elem->is_alias)
+ cmp= my_strcasecmp(table_alias_charset, tbl->alias, elem->alias);
+ else
+ cmp= my_strcasecmp(table_alias_charset, tbl->table_name, elem->table_name) ||
+ strcmp(tbl->db, elem->db);
+
+ if (cmp)
+ continue;
+
+ if (match)
+ {
+ my_error(ER_NONUNIQ_TABLE, MYF(0), elem->alias);
+ DBUG_RETURN(NULL);
+ }
+
+ match= elem;
+ }
+
+ if (!match)
+ my_error(ER_UNKNOWN_TABLE, MYF(0), tbl->table_name, "MULTI DELETE");
+
+ DBUG_RETURN(match);
+}
+
+
/**
Link tables in auxilary table list of multi-delete with corresponding
elements in main table list, and set proper locks for them.
@@ -7717,20 +7156,9 @@ bool multi_delete_set_locks_and_link_aux_tables(LEX *lex)
{
lex->table_count++;
/* All tables in aux_tables must be found in FROM PART */
- TABLE_LIST *walk;
- for (walk= tables; walk; walk= walk->next_local)
- {
- if (!my_strcasecmp(table_alias_charset,
- target_tbl->alias, walk->alias) &&
- !strcmp(walk->db, target_tbl->db))
- break;
- }
+ TABLE_LIST *walk= multi_delete_table_match(lex, target_tbl, tables);
if (!walk)
- {
- my_error(ER_UNKNOWN_TABLE, MYF(0),
- target_tbl->table_name, "MULTI DELETE");
DBUG_RETURN(TRUE);
- }
if (!walk->derived)
{
target_tbl->table_name= walk->table_name;
@@ -7738,6 +7166,9 @@ bool multi_delete_set_locks_and_link_aux_tables(LEX *lex)
}
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. */
+ DBUG_ASSERT(walk->lock_type >= TL_WRITE_ALLOW_WRITE);
+ walk->mdl_request.set_type(MDL_SHARED_WRITE);
target_tbl->correspondent_table= walk; // Remember corresponding table
}
DBUG_RETURN(FALSE);
@@ -7829,21 +7260,30 @@ bool insert_precheck(THD *thd, TABLE_LIST *tables)
/**
- @brief Check privileges for SHOW CREATE TABLE statement.
-
- @param thd Thread context
- @param table Target table
-
- @retval TRUE Failure
- @retval FALSE Success
+ Set proper open mode and table type for element representing target table
+ of CREATE TABLE statement, also adjust statement table list if necessary.
*/
-static bool check_show_create_table_access(THD *thd, TABLE_LIST *table)
+void create_table_set_open_action_and_adjust_tables(LEX *lex)
{
- return check_access(thd, SELECT_ACL | EXTRA_ACL, table->db,
- &table->grant.privilege, 0, 0,
- test(table->schema_table)) ||
- check_grant(thd, SELECT_ACL, table, 2, UINT_MAX, 0);
+ TABLE_LIST *create_table= lex->query_tables;
+
+ if (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)
+ create_table->open_type= OT_TEMPORARY_ONLY;
+ else
+ create_table->open_type= OT_BASE_ONLY;
+
+ if (!lex->select_lex.item_list.elements)
+ {
+ /*
+ Avoid opening and locking target table for ordinary CREATE TABLE
+ or CREATE TABLE LIKE for write (unlike in CREATE ... SELECT we
+ won't do any insertions in it anyway). Not doing this causes
+ problems when running CREATE TABLE IF NOT EXISTS for already
+ existing log table.
+ */
+ create_table->lock_type= TL_READ;
+ }
}
@@ -7879,46 +7319,32 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
(select_lex->item_list.elements ? INSERT_ACL : 0);
if (check_access(thd, want_priv, create_table->db,
- &create_table->grant.privilege, 0, 0,
- test(create_table->schema_table)) ||
- check_merge_table_access(thd, create_table->db,
- lex->create_info.merge_list.first))
+ &create_table->grant.privilege,
+ &create_table->grant.m_internal,
+ 0, 0))
+ goto err;
+
+ /* If it is a merge table, check privileges for merge children. */
+ if (lex->create_info.merge_list.first &&
+ check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
+ lex->create_info.merge_list.first,
+ FALSE, UINT_MAX, FALSE))
goto err;
+
if (want_priv != CREATE_TMP_ACL &&
- check_grant(thd, want_priv, create_table, 0, 1, 0))
+ check_grant(thd, want_priv, create_table, FALSE, 1, FALSE))
goto err;
if (select_lex->item_list.elements)
{
/* Check permissions for used tables in CREATE TABLE ... SELECT */
-
-#ifdef NOT_NECESSARY_TO_CHECK_CREATE_TABLE_EXIST_WHEN_PREPARING_STATEMENT
- /* This code throws an ill error for CREATE TABLE t1 SELECT * FROM t1 */
- /*
- Only do the check for PS, because we on execute we have to check that
- against the opened tables to ensure we don't use a table that is part
- of the view (which can only be done after the table has been opened).
- */
- if (thd->stmt_arena->is_stmt_prepare_or_first_sp_execute())
- {
- /*
- For temporary tables we don't have to check if the created table exists
- */
- if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&
- find_table_in_global_list(tables, create_table->db,
- create_table->table_name))
- {
- error= FALSE;
- goto err;
- }
- }
-#endif
- if (tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE))
+ if (tables && check_table_access(thd, SELECT_ACL, tables, FALSE,
+ UINT_MAX, FALSE))
goto err;
}
else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
{
- if (check_show_create_table_access(thd, tables))
+ if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE))
goto err;
}
error= FALSE;
@@ -8117,7 +7543,10 @@ bool check_string_char_length(LEX_STRING *str, const char *err_msg,
return FALSE;
if (!no_error)
- my_error(ER_WRONG_STRING_LENGTH, MYF(0), str->str, err_msg, max_char_length);
+ {
+ ErrConvString err(str->str, str->length, cs);
+ my_error(ER_WRONG_STRING_LENGTH, MYF(0), err.ptr(), err_msg, max_char_length);
+ }
return TRUE;
}
@@ -8210,7 +7639,7 @@ bool check_host_name(LEX_STRING *str)
}
-extern int MYSQLparse(void *thd); // from sql_yacc.cc
+extern int MYSQLparse(THD *thd); // from sql_yacc.cc
/**
@@ -8230,11 +7659,12 @@ bool parse_sql(THD *thd,
Parser_state *parser_state,
Object_creation_ctx *creation_ctx)
{
- bool mysql_parse_status;
+ bool ret_value;
DBUG_ENTER("parse_sql");
-
DBUG_ASSERT(thd->m_parser_state == NULL);
+ DBUG_ASSERT(thd->lex->m_stmt == NULL);
+ MYSQL_QUERY_PARSE_START(thd->query());
/* Backup creation context. */
Object_creation_ctx *backup_ctx= NULL;
@@ -8248,11 +7678,17 @@ bool parse_sql(THD *thd,
/* Parse the query. */
- mysql_parse_status= MYSQLparse(thd) != 0;
+ bool mysql_parse_status= MYSQLparse(thd) != 0;
/*
- Check that if MYSQLparse() failed, thd->is_error() is set (unless
- we have an error handler installed, which might have silenced error).
+ Check that if MYSQLparse() failed either thd->is_error() is set, or an
+ internal error handler is set.
+
+ The assert will not catch a situation where parsing fails without an
+ error reported if an error handler exists. The problem is that the
+ error handler might have intercepted the error, so thd->is_error() is
+ not set. However, there is no way to be 100% sure here (the error
+ handler might be for other errors than parsing one).
*/
DBUG_ASSERT(!mysql_parse_status ||
@@ -8270,9 +7706,45 @@ bool parse_sql(THD *thd,
/* That's it. */
- DBUG_RETURN(mysql_parse_status || thd->is_fatal_error);
+ ret_value= mysql_parse_status || thd->is_fatal_error;
+ MYSQL_QUERY_PARSE_DONE(ret_value);
+ DBUG_RETURN(ret_value);
}
/**
@} (end of group Runtime_Environment)
*/
+
+
+
+/**
+ Check and merge "CHARACTER SET cs [ COLLATE cl ]" clause
+
+ @param cs character set pointer.
+ @param cl collation pointer.
+
+ Check if collation "cl" is applicable to character set "cs".
+
+ If "cl" is NULL (e.g. when COLLATE clause is not specified),
+ then simply "cs" is returned.
+
+ @return Error status.
+ @retval NULL, if "cl" is not applicable to "cs".
+ @retval pointer to merged CHARSET_INFO on success.
+*/
+
+
+CHARSET_INFO*
+merge_charset_and_collation(CHARSET_INFO *cs, CHARSET_INFO *cl)
+{
+ if (cl)
+ {
+ if (!my_charset_same(cs, cl))
+ {
+ my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), cl->name, cs->csname);
+ return NULL;
+ }
+ return cl;
+ }
+ return cs;
+}
diff --git a/sql/sql_parse.h b/sql/sql_parse.h
new file mode 100644
index 00000000000..d1d6458d22c
--- /dev/null
+++ b/sql/sql_parse.h
@@ -0,0 +1,207 @@
+/* Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ 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_PARSE_INCLUDED
+#define SQL_PARSE_INCLUDED
+
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_acl.h" /* GLOBAL_ACLS */
+
+class Comp_creator;
+class Item;
+class Object_creation_ctx;
+class Parser_state;
+struct TABLE_LIST;
+class THD;
+class Table_ident;
+struct LEX;
+
+enum enum_mysql_completiontype {
+ ROLLBACK_RELEASE=-2, ROLLBACK=1, ROLLBACK_AND_CHAIN=7,
+ COMMIT_RELEASE=-1, COMMIT=0, COMMIT_AND_CHAIN=6
+};
+
+extern "C" int test_if_data_home_dir(const char *dir);
+
+bool multi_update_precheck(THD *thd, TABLE_LIST *tables);
+bool multi_delete_precheck(THD *thd, TABLE_LIST *tables);
+int mysql_multi_update_prepare(THD *thd);
+int mysql_multi_delete_prepare(THD *thd);
+bool mysql_insert_select_prepare(THD *thd);
+bool update_precheck(THD *thd, TABLE_LIST *tables);
+bool delete_precheck(THD *thd, TABLE_LIST *tables);
+bool insert_precheck(THD *thd, TABLE_LIST *tables);
+bool create_table_precheck(THD *thd, TABLE_LIST *tables,
+ TABLE_LIST *create_table);
+
+bool parse_sql(THD *thd,
+ Parser_state *parser_state,
+ Object_creation_ctx *creation_ctx);
+
+void free_items(Item *item);
+void cleanup_items(Item *item);
+
+Comp_creator *comp_eq_creator(bool invert);
+Comp_creator *comp_ge_creator(bool invert);
+Comp_creator *comp_gt_creator(bool invert);
+Comp_creator *comp_le_creator(bool invert);
+Comp_creator *comp_lt_creator(bool invert);
+Comp_creator *comp_ne_creator(bool invert);
+
+int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
+ enum enum_schema_tables schema_table_idx);
+void get_default_definer(THD *thd, LEX_USER *definer);
+LEX_USER *create_default_definer(THD *thd);
+LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name);
+LEX_USER *get_current_user(THD *thd, LEX_USER *user);
+bool check_string_byte_length(LEX_STRING *str, const char *err_msg,
+ uint max_byte_length);
+bool check_string_char_length(LEX_STRING *str, const char *err_msg,
+ uint max_char_length, CHARSET_INFO *cs,
+ bool no_error);
+CHARSET_INFO* merge_charset_and_collation(CHARSET_INFO *cs, CHARSET_INFO *cl);
+bool check_host_name(LEX_STRING *str);
+bool check_identifier_name(LEX_STRING *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 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);
+void mysql_init_select(LEX *lex);
+void mysql_parse(THD *thd, char *rawbuf, uint length,
+ Parser_state *parser_state);
+void mysql_reset_thd_for_next_command(THD *thd);
+bool mysql_new_select(LEX *lex, bool move_down);
+void create_select_for_variable(const char *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);
+void create_table_set_open_action_and_adjust_tables(LEX *lex);
+pthread_handler_t handle_bootstrap(void *arg);
+int mysql_execute_command(THD *thd);
+bool do_command(THD *thd);
+void do_handle_bootstrap(THD *thd);
+bool dispatch_command(enum enum_server_command command, THD *thd,
+ char* packet, uint packet_length);
+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);
+void execute_init_command(THD *thd, LEX_STRING *init_command,
+ mysql_rwlock_t *var_lock);
+bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum enum_field_types type,
+ char *length, char *decimal,
+ uint type_modifier,
+ Item *default_value, Item *on_update_value,
+ LEX_STRING *comment,
+ char *change, List<String> *interval_list,
+ CHARSET_INFO *cs,
+ uint uint_geom_type,
+ Virtual_column_info *vcol_info,
+ engine_option_value *create_options);
+bool add_to_list(THD *thd, SQL_I_List<ORDER> &list, Item *group, bool asc);
+void add_join_on(TABLE_LIST *b,Item *expr);
+void add_join_natural(TABLE_LIST *a,TABLE_LIST *b,List<String> *using_fields,
+ SELECT_LEX *lex);
+bool add_proc_to_list(THD *thd, Item *item);
+bool push_new_name_resolution_context(THD *thd,
+ TABLE_LIST *left_op,
+ TABLE_LIST *right_op);
+void store_position_for_column(const char *name);
+void init_update_queries(void);
+bool check_simple_select();
+Item *normalize_cond(Item *cond);
+Item *negate_expression(THD *thd, Item *expr);
+bool check_stack_overrun(THD *thd, long margin, uchar *dummy);
+
+/* Variables */
+
+extern const char* any_db;
+extern uint sql_command_flags[];
+extern uint server_command_flags[];
+extern const LEX_STRING command_name[];
+extern uint server_command_flags[];
+
+/* Inline functions */
+inline bool check_identifier_name(LEX_STRING *str, uint err_code)
+{
+ return check_identifier_name(str, NAME_CHAR_LEN, err_code, "");
+}
+
+inline bool check_identifier_name(LEX_STRING *str)
+{
+ return check_identifier_name(str, NAME_CHAR_LEN, 0, "");
+}
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables);
+bool check_single_table_access(THD *thd, ulong privilege,
+ TABLE_LIST *tables, bool no_errors);
+bool check_routine_access(THD *thd,ulong want_access,char *db,char *name,
+ bool is_proc, bool no_errors);
+bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table);
+bool check_some_routine_access(THD *thd, const char *db, const char *name, bool is_proc);
+bool check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
+ GRANT_INTERNAL_INFO *grant_internal_info,
+ bool dont_check_global_grants, bool no_errors);
+bool check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
+ bool any_combination_of_privileges_will_do,
+ uint number,
+ bool no_errors);
+#else
+inline bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables)
+{ return false; }
+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)
+{ return false; }
+inline bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
+{
+ table->grant.privilege= want_access;
+ return false;
+}
+inline bool check_some_routine_access(THD *thd, const char *db,
+ const char *name, bool is_proc)
+{ return false; }
+inline bool check_access(THD *, ulong, const char *, ulong *save_priv,
+ GRANT_INTERNAL_INFO *, bool, bool)
+{
+ if (save_priv)
+ *save_priv= GLOBAL_ACLS;
+ return false;
+}
+inline bool
+check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
+ bool any_combination_of_privileges_will_do,
+ uint number,
+ bool no_errors)
+{ return false; }
+#endif /*NO_EMBEDDED_ACCESS_CHECKS*/
+
+/* These were under the INNODB_COMPATIBILITY_HOOKS */
+
+bool check_global_access(THD *thd, ulong want_access, bool no_errors= false);
+
+inline bool is_supported_parser_charset(CHARSET_INFO *cs)
+{
+ return test(cs->mbminlen == 1);
+}
+
+
+#endif /* SQL_PARSE_INCLUDED */
diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc
index 80b52e7e17b..b0fb38bf748 100644
--- a/sql/sql_partition.cc
+++ b/sql/sql_partition.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2005, 2013, Oracle and/or its affiliates.
- Copyright (c) 2009, 2014, Monty Program Ab.
+/* Copyright (c) 2005, 2014, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,28 +19,63 @@
to partitioning introduced in MySQL version 5.1. It contains functionality
used by all handlers that support partitioning, such as
the partitioning handler itself and the NDB handler.
+ (Much of the code in this file has been split into partition_info.cc and
+ the header files partition_info.h + partition_element.h + sql_partition.h)
- The first version was written by Mikael Ronstrom.
+ The first version was written by Mikael Ronstrom 2004-2006.
+ Various parts of the optimizer code was written by Sergey Petrunia.
+ Code have been maintained by Mattias Jonsson.
+ The second version was written by Mikael Ronstrom 2006-2007 with some
+ final fixes for partition pruning in 2008-2009 with assistance from Sergey
+ Petrunia and Mattias Jonsson.
- This version supports RANGE partitioning, LIST partitioning, HASH
+ The first version supports RANGE partitioning, LIST partitioning, HASH
partitioning and composite partitioning (hereafter called subpartitioning)
where each RANGE/LIST partitioning is HASH partitioned. The hash function
can either be supplied by the user or by only a list of fields (also
called KEY partitioning), where the MySQL server will use an internal
hash function.
There are quite a few defaults that can be used as well.
+
+ The second version introduces a new variant of RANGE and LIST partitioning
+ which is often referred to as column lists in the code variables. This
+ enables a user to specify a set of columns and their concatenated value
+ as the partition value. By comparing the concatenation of these values
+ the proper partition can be choosen.
*/
/* Some general useful functions */
#define MYSQL_LEX 1
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h" // REQUIRED: for other includes
+#include "sql_partition.h"
+#include "key.h" // key_restore
+#include "sql_parse.h" // parse_sql
+#include "sql_cache.h" // query_cache_invalidate3
+#include "lock.h" // mysql_lock_remove
+#include "sql_show.h" // append_identifier
#include <errno.h>
#include <m_ctype.h>
#include "my_md5.h"
+#include "transaction.h"
+
+#include "sql_base.h" // close_all_tables_for_name
+#include "sql_table.h" // build_table_filename,
+ // build_table_shadow_filename,
+ // table_to_filename
+ // mysql_*_alter_copy_data
+#include "opt_range.h" // store_key_image_to_rec
+#include "sql_analyse.h" // append_escaped
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
+
+#define ERROR_INJECT_CRASH(code) \
+ DBUG_EVALUATE_IF(code, (DBUG_SUICIDE(), 0), 0)
+#define ERROR_INJECT_ERROR(code) \
+ DBUG_EVALUATE_IF(code, (my_error(ER_UNKNOWN_ERROR, MYF(0)), TRUE), 0)
+
/*
Partition related functions declarations and some static constants;
*/
@@ -51,7 +86,10 @@ const LEX_STRING partition_keywords[]=
{ C_STRING_WITH_LEN("LIST") },
{ C_STRING_WITH_LEN("KEY") },
{ C_STRING_WITH_LEN("MAXVALUE") },
- { C_STRING_WITH_LEN("LINEAR ") }
+ { C_STRING_WITH_LEN("LINEAR ") },
+ { C_STRING_WITH_LEN(" COLUMNS") },
+ { C_STRING_WITH_LEN("ALGORITHM") }
+
};
static const char *part_str= "PARTITION";
static const char *sub_str= "SUB";
@@ -62,26 +100,23 @@ static const char *end_paren_str= ")";
static const char *begin_paren_str= "(";
static const char *comma_str= ",";
-static int get_part_id_charset_func_all(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value);
-static int get_part_id_charset_func_part(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value);
-static int get_part_id_charset_func_subpart(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value);
-static int get_part_part_id_charset_func(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value);
-static int get_subpart_id_charset_func(partition_info *part_info,
- uint32 *part_id);
+int get_partition_id_list_col(partition_info *part_info,
+ uint32 *part_id,
+ longlong *func_value);
int get_partition_id_list(partition_info *part_info,
uint32 *part_id,
longlong *func_value);
+int get_partition_id_range_col(partition_info *part_info,
+ uint32 *part_id,
+ longlong *func_value);
int get_partition_id_range(partition_info *part_info,
uint32 *part_id,
longlong *func_value);
+static int get_part_id_charset_func_part(partition_info *part_info,
+ uint32 *part_id,
+ longlong *func_value);
+static int get_part_id_charset_func_subpart(partition_info *part_info,
+ uint32 *part_id);
int get_partition_id_hash_nosub(partition_info *part_info,
uint32 *part_id,
longlong *func_value);
@@ -94,30 +129,9 @@ int get_partition_id_linear_hash_nosub(partition_info *part_info,
int get_partition_id_linear_key_nosub(partition_info *part_info,
uint32 *part_id,
longlong *func_value);
-int get_partition_id_range_sub_hash(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value);
-int get_partition_id_range_sub_key(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value);
-int get_partition_id_range_sub_linear_hash(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value);
-int get_partition_id_range_sub_linear_key(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value);
-int get_partition_id_list_sub_hash(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value);
-int get_partition_id_list_sub_key(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value);
-int get_partition_id_list_sub_linear_hash(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value);
-int get_partition_id_list_sub_linear_key(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value);
+int get_partition_id_with_sub(partition_info *part_info,
+ uint32 *part_id,
+ longlong *func_value);
int get_partition_id_hash_sub(partition_info *part_info,
uint32 *part_id);
int get_partition_id_key_sub(partition_info *part_info,
@@ -135,16 +149,65 @@ 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 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,
+ bool is_left_endpoint,
+ bool include_endpoint);
+
+/*
+ Convert constants in VALUES definition to the character set the
+ corresponding field uses.
+
+ SYNOPSIS
+ convert_charset_partition_constant()
+ item Item to convert
+ cs Character set to convert to
+
+ RETURN VALUE
+ NULL Error
+ item New converted item
+*/
+
+Item* convert_charset_partition_constant(Item *item, CHARSET_INFO *cs)
+{
+ THD *thd= current_thd;
+ Name_resolution_context *context= &thd->lex->current_select->context;
+ TABLE_LIST *save_list= context->table_list;
+ const char *save_where= thd->where;
+
+ item= item->safe_charset_converter(cs);
+ context->table_list= NULL;
+ thd->where= "convert character set partition constant";
+ if (!item || item->fix_fields(thd, (Item**)NULL))
+ item= NULL;
+ thd->where= save_where;
+ context->table_list= save_list;
+ return item;
+}
+
+
/*
A support function to check if a name is in a list of strings
@@ -162,7 +225,7 @@ bool is_name_in_list(char *name,
List<char> list_names)
{
List_iterator<char> names_it(list_names);
- uint no_names= list_names.elements;
+ uint num_names= list_names.elements;
uint i= 0;
do
@@ -170,7 +233,7 @@ bool is_name_in_list(char *name,
char *list_name= names_it++;
if (!(my_strcasecmp(system_charset_info, name, list_name)))
return TRUE;
- } while (++i < no_names);
+ } while (++i < num_names);
return FALSE;
}
@@ -199,24 +262,24 @@ bool partition_default_handling(TABLE *table, partition_info *part_info,
if (!is_create_table_ind)
{
- if (part_info->use_default_no_partitions)
+ if (part_info->use_default_num_partitions)
{
- if (table->file->get_no_parts(normalized_path, &part_info->no_parts))
+ if (table->file->get_no_parts(normalized_path, &part_info->num_parts))
{
DBUG_RETURN(TRUE);
}
}
else if (part_info->is_sub_partitioned() &&
- part_info->use_default_no_subpartitions)
+ part_info->use_default_num_subpartitions)
{
- uint no_parts;
- if (table->file->get_no_parts(normalized_path, &no_parts))
+ uint num_parts;
+ if (table->file->get_no_parts(normalized_path, &num_parts))
{
DBUG_RETURN(TRUE);
}
- DBUG_ASSERT(part_info->no_parts > 0);
- DBUG_ASSERT((no_parts % part_info->no_parts) == 0);
- part_info->no_subparts= no_parts / part_info->no_parts;
+ DBUG_ASSERT(part_info->num_parts > 0);
+ DBUG_ASSERT((num_parts % part_info->num_parts) == 0);
+ part_info->num_subparts= num_parts / part_info->num_parts;
}
}
part_info->set_up_defaults_for_partitioning(table->file,
@@ -250,8 +313,8 @@ bool check_reorganise_list(partition_info *new_part_info,
List<char> list_part_names)
{
uint new_count, old_count;
- uint no_new_parts= new_part_info->partitions.elements;
- uint no_old_parts= old_part_info->partitions.elements;
+ uint num_new_parts= new_part_info->partitions.elements;
+ uint num_old_parts= old_part_info->partitions.elements;
List_iterator<partition_element> new_parts_it(new_part_info->partitions);
bool same_part_info= (new_part_info == old_part_info);
DBUG_ENTER("check_reorganise_list");
@@ -274,8 +337,8 @@ bool check_reorganise_list(partition_info *new_part_info,
if (!is_name_in_list(old_name, list_part_names))
DBUG_RETURN(TRUE);
}
- } while (old_count < no_old_parts);
- } while (new_count < no_new_parts);
+ } while (old_count < num_old_parts);
+ } while (new_count < num_new_parts);
DBUG_RETURN(FALSE);
}
@@ -308,7 +371,7 @@ int get_parts_for_update(const uchar *old_data, uchar *new_data,
longlong old_func_value;
DBUG_ENTER("get_parts_for_update");
- DBUG_ASSERT(new_data == rec0);
+ DBUG_ASSERT(new_data == rec0); // table->record[0]
set_field_ptr(part_field_array, old_data, rec0);
error= part_info->get_partition_id(part_info, old_part_id,
&old_func_value);
@@ -450,9 +513,10 @@ static bool set_up_field_array(TABLE *table,
bool is_sub_part)
{
Field **ptr, *field, **field_array;
- uint no_fields= 0;
+ uint num_fields= 0;
uint size_field_array;
uint i= 0;
+ uint inx;
partition_info *part_info= table->part_info;
int result= FALSE;
DBUG_ENTER("set_up_field_array");
@@ -461,9 +525,19 @@ static bool set_up_field_array(TABLE *table,
while ((field= *(ptr++)))
{
if (field->flags & GET_FIXED_FIELDS_FLAG)
- no_fields++;
+ num_fields++;
}
- if (no_fields == 0)
+ if (num_fields > MAX_REF_PARTS)
+ {
+ char *err_str;
+ if (is_sub_part)
+ err_str= (char*)"subpartition function";
+ else
+ err_str= (char*)"partition function";
+ my_error(ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR, MYF(0), err_str);
+ DBUG_RETURN(TRUE);
+ }
+ if (num_fields == 0)
{
/*
We are using hidden key as partitioning field
@@ -471,8 +545,8 @@ static bool set_up_field_array(TABLE *table,
DBUG_ASSERT(!is_sub_part);
DBUG_RETURN(result);
}
- size_field_array= (no_fields+1)*sizeof(Field*);
- field_array= (Field**)sql_alloc(size_field_array);
+ size_field_array= (num_fields+1)*sizeof(Field*);
+ field_array= (Field**)sql_calloc(size_field_array);
if (unlikely(!field_array))
{
mem_alloc_error(size_field_array);
@@ -487,7 +561,32 @@ static bool set_up_field_array(TABLE *table,
field->flags|= FIELD_IN_PART_FUNC_FLAG;
if (likely(!result))
{
- field_array[i++]= field;
+ if (!is_sub_part && part_info->column_list)
+ {
+ List_iterator<char> it(part_info->part_field_list);
+ char *field_name;
+
+ DBUG_ASSERT(num_fields == part_info->part_field_list.elements);
+ inx= 0;
+ do
+ {
+ field_name= it++;
+ if (!my_strcasecmp(system_charset_info,
+ field_name,
+ field->field_name))
+ break;
+ } while (++inx < num_fields);
+ if (inx == num_fields)
+ {
+ mem_alloc_error(1);
+ result= TRUE;
+ continue;
+ }
+ }
+ else
+ inx= i;
+ field_array[inx]= field;
+ i++;
/*
We check that the fields are proper. It is required for each
@@ -505,16 +604,16 @@ static bool set_up_field_array(TABLE *table,
}
}
}
- field_array[no_fields]= 0;
+ field_array[num_fields]= 0;
if (!is_sub_part)
{
part_info->part_field_array= field_array;
- part_info->no_part_fields= no_fields;
+ part_info->num_part_fields= num_fields;
}
else
{
part_info->subpart_field_array= field_array;
- part_info->no_subpart_fields= no_fields;
+ part_info->num_subpart_fields= num_fields;
}
DBUG_RETURN(result);
}
@@ -553,36 +652,36 @@ static bool create_full_part_field_array(THD *thd, TABLE *table,
if (!part_info->is_sub_partitioned())
{
part_info->full_part_field_array= part_info->part_field_array;
- part_info->no_full_part_fields= part_info->no_part_fields;
+ part_info->num_full_part_fields= part_info->num_part_fields;
}
else
{
Field *field, **field_array;
- uint no_part_fields=0, size_field_array;
+ uint num_part_fields=0, size_field_array;
ptr= table->field;
while ((field= *(ptr++)))
{
if (field->flags & FIELD_IN_PART_FUNC_FLAG)
- no_part_fields++;
+ num_part_fields++;
}
- size_field_array= (no_part_fields+1)*sizeof(Field*);
- field_array= (Field**)sql_alloc(size_field_array);
+ size_field_array= (num_part_fields+1)*sizeof(Field*);
+ field_array= (Field**)sql_calloc(size_field_array);
if (unlikely(!field_array))
{
mem_alloc_error(size_field_array);
result= TRUE;
goto end;
}
- no_part_fields= 0;
+ num_part_fields= 0;
ptr= table->field;
while ((field= *(ptr++)))
{
if (field->flags & FIELD_IN_PART_FUNC_FLAG)
- field_array[no_part_fields++]= field;
+ field_array[num_part_fields++]= field;
}
- field_array[no_part_fields]=0;
+ field_array[num_part_fields]=0;
part_info->full_part_field_array= field_array;
- part_info->no_full_part_fields= no_part_fields;
+ part_info->num_full_part_fields= num_part_fields;
}
/*
@@ -762,9 +861,6 @@ static bool handle_list_of_fields(List_iterator<char> it,
bool result;
char *field_name;
bool is_list_empty= TRUE;
- int fields_handled = 0;
- char* field_name_array[MAX_KEY];
-
DBUG_ENTER("handle_list_of_fields");
while ((field_name= it++))
@@ -780,36 +876,17 @@ static bool handle_list_of_fields(List_iterator<char> it,
result= TRUE;
goto end;
}
-
- /*
- Check for duplicate fields in the list.
- Assuming that there are not many fields in the partition key list.
- If there were, it would be better to replace the for-loop
- with a more efficient algorithm.
- */
-
- field_name_array[fields_handled] = field_name;
- for (int i = 0; i < fields_handled; ++i)
- {
- if (my_strcasecmp(system_charset_info,
- field_name_array[i], field_name) == 0)
- {
- my_error(ER_FIELD_NOT_FOUND_PART_ERROR, MYF(0));
- DBUG_RETURN(TRUE);
- }
- }
- fields_handled++;
}
- if (is_list_empty)
+ if (is_list_empty && part_info->part_type == HASH_PARTITION)
{
uint primary_key= table->s->primary_key;
if (primary_key != MAX_KEY)
{
- uint no_key_parts= table->key_info[primary_key].key_parts, i;
+ uint num_key_parts= table->key_info[primary_key].key_parts, i;
/*
In the case of an empty list we use primary key as partition key.
*/
- for (i= 0; i < no_key_parts; i++)
+ for (i= 0; i < num_key_parts; i++)
{
Field *field= table->key_info[primary_key].key_part[i].field;
field->flags|= GET_FIXED_FIELDS_FLAG;
@@ -873,11 +950,15 @@ int check_signed_flag(partition_info *part_info)
error= ER_PARTITION_CONST_DOMAIN_ERROR;
break;
}
- } while (++i < part_info->no_parts);
+ } while (++i < part_info->num_parts);
}
return error;
}
+/*
+ init_lex_with_single_table and end_lex_with_single_table
+ are now in sql_lex.cc
+*/
/*
The function uses a new feature in fix_fields where the flag
@@ -892,7 +973,6 @@ int check_signed_flag(partition_info *part_info)
table The table object
part_info Reference to partitioning data structure
is_sub_part Is the table subpartitioned as well
- is_field_to_be_setup Flag if we are to set-up field arrays
is_create_table_ind Indicator of whether openfrm was called as part of
CREATE or ALTER TABLE
@@ -918,66 +998,20 @@ 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_field_to_be_setup,
- bool is_create_table_ind)
+ bool is_sub_part, bool is_create_table_ind)
{
partition_info *part_info= table->part_info;
- uint dir_length, home_dir_length;
bool result= TRUE;
- TABLE_LIST tables;
- TABLE_LIST *save_table_list, *save_first_table, *save_last_table;
int error;
- Name_resolution_context *context;
- const char *save_where;
- char* db_name;
- char db_name_string[FN_REFLEN];
+ LEX *old_lex= thd->lex;
+ LEX lex;
DBUG_ENTER("fix_fields_part_func");
- if (part_info->fixed)
- {
- if (!(is_sub_part || (error= check_signed_flag(part_info))))
- result= FALSE;
+ if (init_lex_with_single_table(thd, table, &lex))
goto end;
- }
-
- /*
- Set-up the TABLE_LIST object to be a list with a single table
- Set the object to zero to create NULL pointers and set alias
- and real name to table name and get database name from file name.
- TODO: Consider generalizing or refactoring Lex::add_table_to_list() so
- it can be used in all places where we create TABLE_LIST objects.
- Also consider creating appropriate constructors for TABLE_LIST.
- */
- bzero((void*)&tables, sizeof(TABLE_LIST));
- tables.alias= tables.table_name= (char*) table->s->table_name.str;
- tables.table= table;
- tables.next_local= 0;
- tables.next_name_resolution_table= 0;
- /*
- Cache the table in Item_fields. All the tables can be cached except
- the trigger pseudo table.
- */
- tables.cacheable_table= TRUE;
- context= thd->lex->current_context();
- tables.select_lex= context->select_lex;
- strmov(db_name_string, table->s->normalized_path.str);
- dir_length= dirname_length(db_name_string);
- db_name_string[dir_length - 1]= 0;
- home_dir_length= dirname_length(db_name_string);
- db_name= &db_name_string[home_dir_length];
- tables.db= db_name;
-
- table->map= 1; //To ensure correct calculation of const item
- table->get_fields_in_item_tree= TRUE;
- save_table_list= context->table_list;
- save_first_table= context->first_name_resolution_table;
- save_last_table= context->last_name_resolution_table;
- context->table_list= &tables;
- context->first_name_resolution_table= &tables;
- context->last_name_resolution_table= NULL;
- func_expr->walk(&Item::change_context_processor, 0, (uchar*) context);
- save_where= thd->where;
+ func_expr->walk(&Item::change_context_processor, 0,
+ (uchar*) &lex.select_lex.context);
thd->where= "partition function";
/*
In execution we must avoid the use of thd->change_item_tree since
@@ -991,45 +1025,33 @@ static bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table,
that does this during val_int must be disallowed as partition
function.
SEE Bug #21658
- */
- /*
+
This is a tricky call to prepare for since it can have a large number
of interesting side effects, both desirable and undesirable.
*/
-
{
- const bool save_use_only_table_context= thd->lex->use_only_table_context;
- thd->lex->use_only_table_context= TRUE;
- thd->lex->current_select->cur_pos_in_select_list= UNDEF_POS;
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;
-
+
if (!(error= func_expr->fix_fields(thd, (Item**)&func_expr)))
func_expr->walk(&Item::vcol_in_partition_func_processor, 0, NULL);
/*
- Restore agg_field/agg_func and allow_sum_func,
+ Restore agg_field/agg_func and allow_sum_func,
fix_fields should not affect mysql_select later, see Bug#46923.
*/
thd->lex->current_select->set_non_agg_field_used(save_agg_field);
thd->lex->current_select->set_agg_func_used(save_agg_func);
thd->lex->allow_sum_func= saved_allow_sum_func;
- thd->lex->use_only_table_context= save_use_only_table_context;
}
-
- context->table_list= save_table_list;
- context->first_name_resolution_table= save_first_table;
- context->last_name_resolution_table= save_last_table;
if (unlikely(error))
{
DBUG_PRINT("info", ("Field in partition function not part of table"));
- if (is_field_to_be_setup)
- clear_field_flag(table);
+ clear_field_flag(table);
goto end;
}
- thd->where= save_where;
if (unlikely(func_expr->const_item()))
{
my_error(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR, MYF(0));
@@ -1060,14 +1082,13 @@ static bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table,
if ((!is_sub_part) && (error= check_signed_flag(part_info)))
goto end;
- result= FALSE;
- if (is_field_to_be_setup)
- result= set_up_field_array(table, is_sub_part);
- if (!is_sub_part)
- part_info->fixed= TRUE;
+ result= set_up_field_array(table, is_sub_part);
end:
- table->get_fields_in_item_tree= FALSE;
- table->map= 0; //Restore old value
+ end_lex_with_single_table(thd, table, old_lex);
+#if !defined(DBUG_OFF)
+ func_expr->walk(&Item::change_context_processor, 0,
+ (uchar*) 0);
+#endif
DBUG_RETURN(result);
}
@@ -1243,9 +1264,9 @@ void check_range_capable_PF(TABLE *table)
static bool set_up_partition_bitmap(THD *thd, partition_info *part_info)
{
uint32 *bitmap_buf;
- uint bitmap_bits= part_info->no_subparts?
- (part_info->no_subparts* part_info->no_parts):
- part_info->no_parts;
+ uint bitmap_bits= part_info->num_subparts?
+ (part_info->num_subparts* part_info->num_parts):
+ part_info->num_parts;
uint bitmap_bytes= bitmap_buffer_size(bitmap_bits);
DBUG_ENTER("set_up_partition_bitmap");
@@ -1345,64 +1366,47 @@ static void set_up_partition_func_pointers(partition_info *part_info)
if (part_info->is_sub_partitioned())
{
+ part_info->get_partition_id= get_partition_id_with_sub;
if (part_info->part_type == RANGE_PARTITION)
{
- part_info->get_part_partition_id= get_partition_id_range;
+ if (part_info->column_list)
+ part_info->get_part_partition_id= get_partition_id_range_col;
+ else
+ part_info->get_part_partition_id= get_partition_id_range;
if (part_info->list_of_subpart_fields)
{
if (part_info->linear_hash_ind)
- {
- part_info->get_partition_id= get_partition_id_range_sub_linear_key;
part_info->get_subpartition_id= get_partition_id_linear_key_sub;
- }
else
- {
- part_info->get_partition_id= get_partition_id_range_sub_key;
part_info->get_subpartition_id= get_partition_id_key_sub;
- }
}
else
{
if (part_info->linear_hash_ind)
- {
- part_info->get_partition_id= get_partition_id_range_sub_linear_hash;
part_info->get_subpartition_id= get_partition_id_linear_hash_sub;
- }
else
- {
- part_info->get_partition_id= get_partition_id_range_sub_hash;
part_info->get_subpartition_id= get_partition_id_hash_sub;
- }
}
}
else /* LIST Partitioning */
{
- part_info->get_part_partition_id= get_partition_id_list;
+ if (part_info->column_list)
+ part_info->get_part_partition_id= get_partition_id_list_col;
+ else
+ part_info->get_part_partition_id= get_partition_id_list;
if (part_info->list_of_subpart_fields)
{
if (part_info->linear_hash_ind)
- {
- part_info->get_partition_id= get_partition_id_list_sub_linear_key;
part_info->get_subpartition_id= get_partition_id_linear_key_sub;
- }
else
- {
- part_info->get_partition_id= get_partition_id_list_sub_key;
part_info->get_subpartition_id= get_partition_id_key_sub;
- }
}
else
{
if (part_info->linear_hash_ind)
- {
- part_info->get_partition_id= get_partition_id_list_sub_linear_hash;
part_info->get_subpartition_id= get_partition_id_linear_hash_sub;
- }
else
- {
- part_info->get_partition_id= get_partition_id_list_sub_hash;
part_info->get_subpartition_id= get_partition_id_hash_sub;
- }
}
}
}
@@ -1411,9 +1415,19 @@ static void set_up_partition_func_pointers(partition_info *part_info)
part_info->get_part_partition_id= NULL;
part_info->get_subpartition_id= NULL;
if (part_info->part_type == RANGE_PARTITION)
- part_info->get_partition_id= get_partition_id_range;
+ {
+ if (part_info->column_list)
+ part_info->get_partition_id= get_partition_id_range_col;
+ else
+ part_info->get_partition_id= get_partition_id_range;
+ }
else if (part_info->part_type == LIST_PARTITION)
- part_info->get_partition_id= get_partition_id_list;
+ {
+ if (part_info->column_list)
+ part_info->get_partition_id= get_partition_id_list_col;
+ else
+ part_info->get_partition_id= get_partition_id_list;
+ }
else /* HASH partitioning */
{
if (part_info->list_of_part_fields)
@@ -1432,32 +1446,43 @@ static void set_up_partition_func_pointers(partition_info *part_info)
}
}
}
- if (part_info->full_part_charset_field_array)
- {
- DBUG_ASSERT(part_info->get_partition_id);
- part_info->get_partition_id_charset= part_info->get_partition_id;
- if (part_info->part_charset_field_array &&
- part_info->subpart_charset_field_array)
- part_info->get_partition_id= get_part_id_charset_func_all;
- else if (part_info->part_charset_field_array)
- part_info->get_partition_id= get_part_id_charset_func_part;
- else
- part_info->get_partition_id= get_part_id_charset_func_subpart;
- }
- if (part_info->part_charset_field_array &&
- part_info->is_sub_partitioned())
+ /*
+ We need special functions to handle character sets since they require copy
+ of field pointers and restore afterwards. For subpartitioned tables we do
+ the copy and restore individually on the part and subpart parts. For non-
+ subpartitioned tables we use the same functions as used for the parts part
+ of subpartioning.
+ Thus for subpartitioned tables the get_partition_id is always
+ get_partition_id_with_sub, even when character sets exists.
+ */
+ if (part_info->part_charset_field_array)
{
- DBUG_ASSERT(part_info->get_part_partition_id);
- part_info->get_part_partition_id_charset=
+ if (part_info->is_sub_partitioned())
+ {
+ DBUG_ASSERT(part_info->get_part_partition_id);
+ if (!part_info->column_list)
+ {
+ part_info->get_part_partition_id_charset=
part_info->get_part_partition_id;
- part_info->get_part_partition_id= get_part_part_id_charset_func;
+ part_info->get_part_partition_id= get_part_id_charset_func_part;
+ }
+ }
+ else
+ {
+ DBUG_ASSERT(part_info->get_partition_id);
+ if (!part_info->column_list)
+ {
+ part_info->get_part_partition_id_charset= part_info->get_partition_id;
+ part_info->get_part_partition_id= get_part_id_charset_func_part;
+ }
+ }
}
if (part_info->subpart_charset_field_array)
{
DBUG_ASSERT(part_info->get_subpartition_id);
part_info->get_subpartition_id_charset=
part_info->get_subpartition_id;
- part_info->get_subpartition_id= get_subpart_id_charset_func;
+ part_info->get_subpartition_id= get_part_id_charset_func_subpart;
}
DBUG_VOID_RETURN;
}
@@ -1465,22 +1490,22 @@ static void set_up_partition_func_pointers(partition_info *part_info)
/*
For linear hashing we need a mask which is on the form 2**n - 1 where
- 2**n >= no_parts. Thus if no_parts is 6 then mask is 2**3 - 1 = 8 - 1 = 7.
+ 2**n >= num_parts. Thus if num_parts is 6 then mask is 2**3 - 1 = 8 - 1 = 7.
SYNOPSIS
set_linear_hash_mask()
part_info Reference to partitioning data structure
- no_parts Number of parts in linear hash partitioning
+ num_parts Number of parts in linear hash partitioning
RETURN VALUE
NONE
*/
-void set_linear_hash_mask(partition_info *part_info, uint no_parts)
+void set_linear_hash_mask(partition_info *part_info, uint num_parts)
{
uint mask;
- for (mask= 1; mask < no_parts; mask<<=1)
+ for (mask= 1; mask < num_parts; mask<<=1)
;
part_info->linear_hash_mask= mask - 1;
}
@@ -1494,7 +1519,7 @@ void set_linear_hash_mask(partition_info *part_info, uint no_parts)
get_part_id_from_linear_hash()
hash_value Hash value calculated by HASH function or KEY function
mask Mask calculated previously by set_linear_hash_mask
- no_parts Number of partitions in HASH partitioned part
+ num_parts Number of partitions in HASH partitioned part
RETURN VALUE
part_id The calculated partition identity (starting at 0)
@@ -1507,11 +1532,11 @@ void set_linear_hash_mask(partition_info *part_info, uint no_parts)
*/
static uint32 get_part_id_from_linear_hash(longlong hash_value, uint mask,
- uint no_parts)
+ uint num_parts)
{
uint32 part_id= (uint32)(hash_value & mask);
- if (part_id >= no_parts)
+ if (part_id >= num_parts)
{
uint new_mask= ((mask + 1) >> 1) - 1;
part_id= (uint32)(hash_value & new_mask);
@@ -1655,7 +1680,7 @@ bool fix_partition_func(THD *thd, TABLE *table,
function is correct.
*/
if (part_info->linear_hash_ind)
- set_linear_hash_mask(part_info, part_info->no_subparts);
+ 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);
@@ -1665,13 +1690,11 @@ bool fix_partition_func(THD *thd, TABLE *table,
else
{
if (unlikely(fix_fields_part_func(thd, part_info->subpart_expr,
- table, TRUE, TRUE,
- is_create_table_ind)))
+ table, TRUE, is_create_table_ind)))
goto end;
if (unlikely(part_info->subpart_expr->result_type() != INT_RESULT))
{
- my_error(ER_PARTITION_FUNC_NOT_ALLOWED_ERROR, MYF(0),
- "SUBPARTITION");
+ part_info->report_part_expr_error(TRUE);
goto end;
}
}
@@ -1684,7 +1707,7 @@ bool fix_partition_func(THD *thd, TABLE *table,
if (part_info->part_type == HASH_PARTITION)
{
if (part_info->linear_hash_ind)
- set_linear_hash_mask(part_info, part_info->no_parts);
+ 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);
@@ -1694,34 +1717,42 @@ bool fix_partition_func(THD *thd, TABLE *table,
else
{
if (unlikely(fix_fields_part_func(thd, part_info->part_expr,
- table, FALSE, TRUE,
- is_create_table_ind)))
+ table, FALSE, is_create_table_ind)))
goto end;
if (unlikely(part_info->part_expr->result_type() != INT_RESULT))
{
- my_error(ER_PARTITION_FUNC_NOT_ALLOWED_ERROR, MYF(0), part_str);
+ part_info->report_part_expr_error(FALSE);
goto end;
}
- part_info->part_result_type= INT_RESULT;
}
+ part_info->fixed= TRUE;
}
else
{
const char *error_str;
- if (unlikely(fix_fields_part_func(thd, part_info->part_expr,
- table, FALSE, TRUE,
- is_create_table_ind)))
- goto end;
+ if (part_info->column_list)
+ {
+ List_iterator<char> it(part_info->part_field_list);
+ if (unlikely(handle_list_of_fields(it, table, part_info, FALSE)))
+ goto end;
+ }
+ else
+ {
+ 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= partition_keywords[PKW_RANGE].str;
- if (unlikely(part_info->check_range_constants()))
+ if (unlikely(part_info->check_range_constants(thd)))
goto end;
}
else if (part_info->part_type == LIST_PARTITION)
{
error_str= partition_keywords[PKW_LIST].str;
- if (unlikely(part_info->check_list_constants()))
+ if (unlikely(part_info->check_list_constants(thd)))
goto end;
}
else
@@ -1730,24 +1761,30 @@ bool fix_partition_func(THD *thd, TABLE *table,
my_error(ER_INCONSISTENT_PARTITION_INFO_ERROR, MYF(0));
goto end;
}
- if (unlikely(part_info->no_parts < 1))
+ if (unlikely(part_info->num_parts < 1))
{
my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), error_str);
goto end;
}
- if (unlikely(part_info->part_expr->result_type() != INT_RESULT))
+ if (unlikely(!part_info->column_list &&
+ part_info->part_expr->result_type() != INT_RESULT))
{
- my_error(ER_PARTITION_FUNC_NOT_ALLOWED_ERROR, MYF(0), part_str);
+ part_info->report_part_expr_error(FALSE);
goto end;
}
}
if (((part_info->part_type != HASH_PARTITION ||
- part_info->list_of_part_fields == FALSE) &&
- check_part_func_fields(part_info->part_field_array, TRUE)) ||
+ part_info->list_of_part_fields == FALSE) &&
+ !part_info->column_list &&
+ check_part_func_fields(part_info->part_field_array, TRUE)) ||
(part_info->list_of_subpart_fields == FALSE &&
part_info->is_sub_partitioned() &&
check_part_func_fields(part_info->subpart_field_array, TRUE)))
{
+ /*
+ Range/List/HASH (but not KEY) and not COLUMNS or HASH subpartitioning
+ with columns in the partitioning expression using unallowed charset.
+ */
my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
goto end;
}
@@ -1766,6 +1803,11 @@ bool fix_partition_func(THD *thd, TABLE *table,
my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
goto end;
}
+ if (unlikely(part_info->check_partition_field_length()))
+ {
+ my_error(ER_PARTITION_FIELDS_TOO_LONG, MYF(0));
+ goto end;
+ }
check_range_capable_PF(table);
set_up_partition_key_maps(table, part_info);
set_up_partition_func_pointers(part_info);
@@ -1788,9 +1830,9 @@ end:
static int add_write(File fptr, const char *buf, uint len)
{
- uint len_written= my_write(fptr, (const uchar*)buf, len, MYF(0));
+ uint ret_code= mysql_file_write(fptr, (const uchar*)buf, len, MYF(MY_FNABP));
- if (likely(len == len_written))
+ if (likely(ret_code == 0))
return 0;
else
return 1;
@@ -1839,14 +1881,8 @@ static int add_begin_parenthesis(File fptr)
static int add_part_key_word(File fptr, const char *key_string)
{
int err= add_string(fptr, key_string);
-
err+= add_space(fptr);
- return err + add_begin_parenthesis(fptr);
-}
-
-static int add_hash(File fptr)
-{
- return add_part_key_word(fptr, partition_keywords[PKW_HASH].str);
+ return err;
}
static int add_partition(File fptr)
@@ -1877,30 +1913,31 @@ static int add_subpartition_by(File fptr)
return err + add_partition_by(fptr);
}
-static int add_key_partition(File fptr, List<char> field_list)
+static int add_part_field_list(File fptr, List<char> field_list)
{
- uint i, no_fields;
- int err;
+ uint i, num_fields;
+ int err= 0;
List_iterator<char> part_it(field_list);
- err= add_part_key_word(fptr, partition_keywords[PKW_KEY].str);
- no_fields= field_list.elements;
+ num_fields= field_list.elements;
i= 0;
- while (i < no_fields)
+ err+= add_begin_parenthesis(fptr);
+ while (i < num_fields)
{
const char *field_str= part_it++;
String field_string("", 0, system_charset_info);
THD *thd= current_thd;
- ulonglong save_options= thd->options;
- thd->options= 0;
+ ulonglong save_options= thd->variables.option_bits;
+ thd->variables.option_bits&= ~OPTION_QUOTE_SHOW_CREATE;
append_identifier(thd, &field_string, field_str,
strlen(field_str));
- thd->options= save_options;
+ thd->variables.option_bits= save_options;
err+= add_string_object(fptr, &field_string);
- if (i != (no_fields-1))
+ if (i != (num_fields-1))
err+= add_comma(fptr);
i++;
}
+ err+= add_end_parenthesis(fptr);
return err;
}
@@ -1909,12 +1946,11 @@ static int add_name_string(File fptr, const char *name)
int err;
String name_string("", 0, system_charset_info);
THD *thd= current_thd;
- ulonglong save_options= thd->options;
-
- thd->options= 0;
+ ulonglong save_options= thd->variables.option_bits;
+ thd->variables.option_bits&= ~OPTION_QUOTE_SHOW_CREATE;
append_identifier(thd, &name_string, name,
strlen(name));
- thd->options= save_options;
+ thd->variables.option_bits= save_options;
err= add_string_object(fptr, &name_string);
return err;
}
@@ -1929,7 +1965,7 @@ static int add_int(File fptr, longlong number)
static int add_uint(File fptr, ulonglong number)
{
char buff[32];
- longlong2str(number, buff, 10, 1);
+ longlong2str(number, buff, 10);
return add_string(fptr, buff);
}
@@ -2013,37 +2049,269 @@ static int add_partition_options(File fptr, partition_element *p_elem)
return err + add_engine(fptr,p_elem->engine_type);
}
-static int add_partition_values(File fptr, partition_info *part_info, partition_element *p_elem)
+
+/*
+ Check partition fields for result type and if they need
+ to check the character set.
+
+ SYNOPSIS
+ check_part_field()
+ sql_type Type provided by user
+ field_name Name of field, used for error handling
+ result_type Out value: Result type of field
+ need_cs_check Out value: Do we need character set check
+
+ RETURN VALUES
+ TRUE Error
+ FALSE Ok
+*/
+
+static int check_part_field(enum_field_types sql_type,
+ const char *field_name,
+ Item_result *result_type,
+ bool *need_cs_check)
+{
+ if (sql_type >= MYSQL_TYPE_TINY_BLOB &&
+ sql_type <= MYSQL_TYPE_BLOB)
+ {
+ my_error(ER_BLOB_FIELD_IN_PART_FUNC_ERROR, MYF(0));
+ return TRUE;
+ }
+ switch (sql_type)
+ {
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_INT24:
+ *result_type= INT_RESULT;
+ *need_cs_check= FALSE;
+ return FALSE;
+ case MYSQL_TYPE_NEWDATE:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATETIME:
+ *result_type= STRING_RESULT;
+ *need_cs_check= TRUE;
+ return FALSE;
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_VAR_STRING:
+ *result_type= STRING_RESULT;
+ *need_cs_check= TRUE;
+ return FALSE;
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_NULL:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ case MYSQL_TYPE_BIT:
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ case MYSQL_TYPE_GEOMETRY:
+ goto error;
+ default:
+ goto error;
+ }
+error:
+ my_error(ER_FIELD_TYPE_NOT_ALLOWED_AS_PARTITION_FIELD, MYF(0),
+ field_name);
+ return TRUE;
+}
+
+
+/*
+ Find the given field's Create_field object using name of field
+
+ SYNOPSIS
+ get_sql_field()
+ field_name Field name
+ alter_info Info from ALTER TABLE/CREATE TABLE
+
+ RETURN VALUE
+ sql_field Object filled in by parser about field
+ NULL No field found
+*/
+
+static Create_field* get_sql_field(char *field_name,
+ Alter_info *alter_info)
+{
+ List_iterator<Create_field> it(alter_info->create_list);
+ Create_field *sql_field;
+ DBUG_ENTER("get_sql_field");
+
+ while ((sql_field= it++))
+ {
+ if (!(my_strcasecmp(system_charset_info,
+ sql_field->field_name,
+ field_name)))
+ {
+ DBUG_RETURN(sql_field);
+ }
+ }
+ DBUG_RETURN(NULL);
+}
+
+
+static int add_column_list_values(File fptr, partition_info *part_info,
+ part_elem_value *list_value,
+ HA_CREATE_INFO *create_info,
+ Alter_info *alter_info)
+{
+ int err= 0;
+ uint i;
+ List_iterator<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);
+
+ if (use_parenthesis)
+ err+= add_begin_parenthesis(fptr);
+ for (i= 0; i < num_elements; i++)
+ {
+ part_column_list_val *col_val= &list_value->col_val_array[i];
+ char *field_name= it++;
+ if (col_val->max_value)
+ err+= add_string(fptr, partition_keywords[PKW_MAXVALUE].str);
+ else if (col_val->null_value)
+ err+= add_string(fptr, "NULL");
+ else
+ {
+ char buffer[MAX_KEY_LENGTH];
+ String str(buffer, sizeof(buffer), &my_charset_bin);
+ Item *item_expr= col_val->item_expression;
+ if (item_expr->null_value)
+ err+= add_string(fptr, "NULL");
+ else
+ {
+ String *res;
+ CHARSET_INFO *field_cs;
+ bool need_cs_check= FALSE;
+ Item_result result_type= STRING_RESULT;
+
+ /*
+ This function is called at a very early stage, even before
+ we have prepared the sql_field objects. Thus we have to
+ find the proper sql_field object and get the character set
+ from that object.
+ */
+ if (create_info)
+ {
+ Create_field *sql_field;
+
+ if (!(sql_field= get_sql_field(field_name,
+ alter_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,
+ &result_type,
+ &need_cs_check))
+ return 1;
+ if (need_cs_check)
+ field_cs= get_sql_field_charset(sql_field, create_info);
+ else
+ field_cs= NULL;
+ }
+ else
+ {
+ Field *field= part_info->part_field_array[i];
+ result_type= field->result_type();
+ if (check_part_field(field->real_type(),
+ field->field_name,
+ &result_type,
+ &need_cs_check))
+ return 1;
+ DBUG_ASSERT(result_type == field->result_type());
+ if (need_cs_check)
+ field_cs= field->charset();
+ else
+ field_cs= NULL;
+ }
+ if (result_type != item_expr->result_type())
+ {
+ my_error(ER_WRONG_TYPE_COLUMN_VALUE_ERROR, MYF(0));
+ return 1;
+ }
+ if (field_cs && field_cs != item_expr->collation.collation)
+ {
+ if (!(item_expr= convert_charset_partition_constant(item_expr,
+ field_cs)))
+ {
+ my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
+ return 1;
+ }
+ }
+ {
+ String val_conv;
+ val_conv.set_charset(system_charset_info);
+ res= item_expr->val_str(&str);
+ if (get_cs_converted_part_value_from_string(current_thd,
+ item_expr, res,
+ &val_conv, field_cs,
+ (bool)(alter_info != NULL)))
+ return 1;
+ err+= add_string_object(fptr, &val_conv);
+ }
+ }
+ }
+ if (i != (num_elements - 1))
+ err+= add_string(fptr, comma_str);
+ }
+ if (use_parenthesis)
+ err+= add_end_parenthesis(fptr);
+ return err;
+}
+
+static int add_partition_values(File fptr, partition_info *part_info,
+ partition_element *p_elem,
+ HA_CREATE_INFO *create_info,
+ Alter_info *alter_info)
{
int err= 0;
if (part_info->part_type == RANGE_PARTITION)
{
err+= add_string(fptr, " VALUES LESS THAN ");
- if (!p_elem->max_value)
+ if (part_info->column_list)
{
+ List_iterator<part_elem_value> list_val_it(p_elem->list_val_list);
+ part_elem_value *list_value= list_val_it++;
err+= add_begin_parenthesis(fptr);
- if (p_elem->signed_flag)
- err+= add_int(fptr, p_elem->range_value);
- else
- err+= add_uint(fptr, p_elem->range_value);
+ err+= add_column_list_values(fptr, part_info, list_value,
+ create_info, alter_info);
err+= add_end_parenthesis(fptr);
}
else
- err+= add_string(fptr, partition_keywords[PKW_MAXVALUE].str);
+ {
+ if (!p_elem->max_value)
+ {
+ err+= add_begin_parenthesis(fptr);
+ if (p_elem->signed_flag)
+ err+= add_int(fptr, p_elem->range_value);
+ else
+ err+= add_uint(fptr, p_elem->range_value);
+ err+= add_end_parenthesis(fptr);
+ }
+ else
+ err+= add_string(fptr, partition_keywords[PKW_MAXVALUE].str);
+ }
}
else if (part_info->part_type == LIST_PARTITION)
{
uint i;
List_iterator<part_elem_value> list_val_it(p_elem->list_val_list);
err+= add_string(fptr, " VALUES IN ");
- uint no_items= p_elem->list_val_list.elements;
+ uint num_items= p_elem->list_val_list.elements;
err+= add_begin_parenthesis(fptr);
if (p_elem->has_null_value)
{
err+= add_string(fptr, "NULL");
- if (no_items == 0)
+ if (num_items == 0)
{
err+= add_end_parenthesis(fptr);
goto end;
@@ -2055,19 +2323,77 @@ static int add_partition_values(File fptr, partition_info *part_info, partition_
{
part_elem_value *list_value= list_val_it++;
- if (!list_value->unsigned_flag)
- err+= add_int(fptr, list_value->value);
+ if (part_info->column_list)
+ err+= add_column_list_values(fptr, part_info, list_value,
+ create_info, alter_info);
else
- err+= add_uint(fptr, list_value->value);
- if (i != (no_items-1))
+ {
+ if (!list_value->unsigned_flag)
+ err+= add_int(fptr, list_value->value);
+ else
+ err+= add_uint(fptr, list_value->value);
+ }
+ if (i != (num_items-1))
err+= add_comma(fptr);
- } while (++i < no_items);
+ } while (++i < num_items);
err+= add_end_parenthesis(fptr);
}
end:
return err;
}
+
+/**
+ Add 'KEY' word, with optional 'ALGORTIHM = N'.
+
+ @param fptr File to write to.
+ @param part_info partition_info holding the used key_algorithm
+ @param current_comment_start NULL, or comment string encapsulating the
+ PARTITION BY clause.
+
+ @return Operation status.
+ @retval 0 Success
+ @retval != 0 Failure
+*/
+
+static int add_key_with_algorithm(File fptr, partition_info *part_info,
+ const char *current_comment_start)
+{
+ int err= 0;
+ err+= add_part_key_word(fptr, partition_keywords[PKW_KEY].str);
+
+ /*
+ current_comment_start is given when called from SHOW CREATE TABLE,
+ Then only add ALGORITHM = 1, not the default 2 or non-set 0!
+ For .frm current_comment_start is NULL, then add ALGORITHM if != 0.
+ */
+ if (part_info->key_algorithm == partition_info::KEY_ALGORITHM_51 || // SHOW
+ (!current_comment_start && // .frm
+ (part_info->key_algorithm != partition_info::KEY_ALGORITHM_NONE)))
+ {
+ /* If we already are within a comment, end that comment first. */
+ if (current_comment_start)
+ err+= add_string(fptr, "*/ ");
+ err+= add_string(fptr, "/*!50531 ");
+ err+= add_part_key_word(fptr, partition_keywords[PKW_ALGORITHM].str);
+ err+= add_equal(fptr);
+ err+= add_space(fptr);
+ err+= add_int(fptr, part_info->key_algorithm);
+ err+= add_space(fptr);
+ err+= add_string(fptr, "*/ ");
+ if (current_comment_start)
+ {
+ /* Skip new line. */
+ if (current_comment_start[0] == '\n')
+ current_comment_start++;
+ err+= add_string(fptr, current_comment_start);
+ err+= add_space(fptr);
+ }
+ }
+ return err;
+}
+
+
/*
Generate the partition syntax from the partition data structure.
Useful for support of generating defaults, SHOW CREATE TABLES
@@ -2080,6 +2406,8 @@ end:
use_sql_alloc Allocate buffer from sql_alloc if true
otherwise use my_malloc
show_partition_options Should we display partition options
+ create_info Info generated by parser
+ alter_info Info generated by parser
RETURN VALUES
NULL error
@@ -2108,9 +2436,12 @@ end:
char *generate_partition_syntax(partition_info *part_info,
uint *buf_length,
bool use_sql_alloc,
- bool show_partition_options)
+ bool show_partition_options,
+ HA_CREATE_INFO *create_info,
+ Alter_info *alter_info,
+ const char *current_comment_start)
{
- uint i,j, tot_no_parts, no_subparts;
+ uint i,j, tot_num_parts, num_subparts;
partition_element *part_elem;
ulonglong buffer_length;
char path[FN_REFLEN];
@@ -2141,27 +2472,38 @@ char *generate_partition_syntax(partition_info *part_info,
if (part_info->linear_hash_ind)
err+= add_string(fptr, partition_keywords[PKW_LINEAR].str);
if (part_info->list_of_part_fields)
- err+= add_key_partition(fptr, part_info->part_field_list);
+ {
+ err+= add_key_with_algorithm(fptr, part_info,
+ current_comment_start);
+ err+= add_part_field_list(fptr, part_info->part_field_list);
+ }
else
- err+= add_hash(fptr);
+ err+= add_part_key_word(fptr, partition_keywords[PKW_HASH].str);
break;
default:
DBUG_ASSERT(0);
/* We really shouldn't get here, no use in continuing from here */
- my_error(ER_OUT_OF_RESOURCES, MYF(0));
- current_thd->fatal_error();
+ my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
DBUG_RETURN(NULL);
}
if (part_info->part_expr)
+ {
+ err+= add_begin_parenthesis(fptr);
err+= add_string_len(fptr, part_info->part_func_string,
part_info->part_func_len);
- err+= add_end_parenthesis(fptr);
- if ((!part_info->use_default_no_partitions) &&
+ err+= add_end_parenthesis(fptr);
+ }
+ else if (part_info->column_list)
+ {
+ err+= add_string(fptr, partition_keywords[PKW_COLUMNS].str);
+ err+= add_part_field_list(fptr, part_info->part_field_list);
+ }
+ if ((!part_info->use_default_num_partitions) &&
part_info->use_default_partitions)
{
err+= add_string(fptr, "\n");
err+= add_string(fptr, "PARTITIONS ");
- err+= add_int(fptr, part_info->no_parts);
+ err+= add_int(fptr, part_info->num_parts);
}
if (part_info->is_sub_partitioned())
{
@@ -2171,23 +2513,30 @@ char *generate_partition_syntax(partition_info *part_info,
if (part_info->linear_hash_ind)
err+= add_string(fptr, partition_keywords[PKW_LINEAR].str);
if (part_info->list_of_subpart_fields)
- err+= add_key_partition(fptr, part_info->subpart_field_list);
+ {
+ err+= add_key_with_algorithm(fptr, part_info,
+ current_comment_start);
+ err+= add_part_field_list(fptr, part_info->subpart_field_list);
+ }
else
- err+= add_hash(fptr);
+ err+= add_part_key_word(fptr, partition_keywords[PKW_HASH].str);
if (part_info->subpart_expr)
+ {
+ err+= add_begin_parenthesis(fptr);
err+= add_string_len(fptr, part_info->subpart_func_string,
part_info->subpart_func_len);
- err+= add_end_parenthesis(fptr);
- if ((!part_info->use_default_no_subpartitions) &&
+ err+= add_end_parenthesis(fptr);
+ }
+ if ((!part_info->use_default_num_subpartitions) &&
part_info->use_default_subpartitions)
{
err+= add_string(fptr, "\n");
err+= add_string(fptr, "SUBPARTITIONS ");
- err+= add_int(fptr, part_info->no_subparts);
+ err+= add_int(fptr, part_info->num_subparts);
}
}
- tot_no_parts= part_info->partitions.elements;
- no_subparts= part_info->no_subparts;
+ tot_num_parts= part_info->partitions.elements;
+ num_subparts= part_info->num_subparts;
if (!part_info->use_default_partitions)
{
@@ -2210,7 +2559,8 @@ char *generate_partition_syntax(partition_info *part_info,
first= FALSE;
err+= add_partition(fptr);
err+= add_name_string(fptr, part_elem->partition_name);
- err+= add_partition_values(fptr, part_info, part_elem);
+ err+= add_partition_values(fptr, part_info, part_elem,
+ create_info, alter_info);
if (!part_info->is_sub_partitioned() ||
part_info->use_default_subpartitions)
{
@@ -2231,7 +2581,7 @@ char *generate_partition_syntax(partition_info *part_info,
err+= add_name_string(fptr, part_elem->partition_name);
if (show_partition_options)
err+= add_partition_options(fptr, part_elem);
- if (j != (no_subparts-1))
+ if (j != (num_subparts-1))
{
err+= add_comma(fptr);
err+= add_string(fptr, "\n");
@@ -2240,19 +2590,20 @@ char *generate_partition_syntax(partition_info *part_info,
}
else
err+= add_end_parenthesis(fptr);
- } while (++j < no_subparts);
+ } while (++j < num_subparts);
}
}
- if (i == (tot_no_parts-1))
+ if (i == (tot_num_parts-1))
err+= add_end_parenthesis(fptr);
- } while (++i < tot_no_parts);
+ } while (++i < tot_num_parts);
}
if (err)
goto close_file;
- buffer_length= my_seek(fptr, 0L,MY_SEEK_END,MYF(0));
+ buffer_length= mysql_file_seek(fptr, 0L, MY_SEEK_END, MYF(0));
if (unlikely(buffer_length == MY_FILEPOS_ERROR))
goto close_file;
- if (unlikely(my_seek(fptr, 0L, MY_SEEK_SET, MYF(0)) == MY_FILEPOS_ERROR))
+ if (unlikely(mysql_file_seek(fptr, 0L, MY_SEEK_SET, MYF(0))
+ == MY_FILEPOS_ERROR))
goto close_file;
*buf_length= (uint)buffer_length;
if (use_sql_alloc)
@@ -2262,10 +2613,10 @@ char *generate_partition_syntax(partition_info *part_info,
if (!buf)
goto close_file;
- if (unlikely(my_read(fptr, (uchar*)buf, *buf_length, MYF(MY_FNABP))))
+ if (unlikely(mysql_file_read(fptr, (uchar*)buf, *buf_length, MYF(MY_FNABP))))
{
if (!use_sql_alloc)
- my_free(buf, MYF(0));
+ my_free(buf);
else
buf= NULL;
}
@@ -2273,7 +2624,7 @@ char *generate_partition_syntax(partition_info *part_info,
buf[*buf_length]= 0;
close_file:
- my_close(fptr, MYF(0));
+ mysql_file_close(fptr, MYF(0));
DBUG_RETURN(buf);
}
@@ -2374,10 +2725,82 @@ static uint32 calculate_key_value(Field **field_array)
{
ulong nr1= 1;
ulong nr2= 4;
+ bool use_51_hash;
+ use_51_hash= test((*field_array)->table->part_info->key_algorithm ==
+ partition_info::KEY_ALGORITHM_51);
do
{
Field *field= *field_array;
+ if (use_51_hash)
+ {
+ switch (field->real_type()) {
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_NEWDATE:
+ {
+ if (field->is_null())
+ {
+ nr1^= (nr1 << 1) | 1;
+ continue;
+ }
+ /* Force this to my_hash_sort_bin, which was used in 5.1! */
+ uint len= field->pack_length();
+ my_charset_bin.coll->hash_sort(&my_charset_bin, field->ptr, len,
+ &nr1, &nr2);
+ /* Done with this field, continue with next one. */
+ continue;
+ }
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_BIT:
+ /* Not affected, same in 5.1 and 5.5 */
+ break;
+ /*
+ ENUM/SET uses my_hash_sort_simple in 5.1 (i.e. my_charset_latin1)
+ and my_hash_sort_bin in 5.5!
+ */
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ {
+ if (field->is_null())
+ {
+ nr1^= (nr1 << 1) | 1;
+ continue;
+ }
+ /* Force this to my_hash_sort_bin, which was used in 5.1! */
+ uint len= field->pack_length();
+ my_charset_latin1.coll->hash_sort(&my_charset_latin1, field->ptr,
+ len, &nr1, &nr2);
+ continue;
+ }
+ /* These types should not be allowed for partitioning! */
+ case MYSQL_TYPE_NULL:
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_GEOMETRY:
+ /* fall through. */
+ default:
+ DBUG_ASSERT(0); // New type?
+ /* Fall through for default hashing (5.5). */
+ }
+ /* fall through, use collation based hashing. */
+ }
field->hash(&nr1, &nr2);
} while (*(++field_array));
return (uint32) nr1;
@@ -2392,14 +2815,14 @@ static uint32 calculate_key_value(Field **field_array)
get_part_id_for_sub()
loc_part_id Local partition id
sub_part_id Subpartition id
- no_subparts Number of subparts
+ num_subparts Number of subparts
*/
inline
static uint32 get_part_id_for_sub(uint32 loc_part_id, uint32 sub_part_id,
- uint no_subparts)
+ uint num_subparts)
{
- return (uint32)((loc_part_id * no_subparts) + sub_part_id);
+ return (uint32)((loc_part_id * num_subparts) + sub_part_id);
}
@@ -2408,7 +2831,7 @@ static uint32 get_part_id_for_sub(uint32 loc_part_id, uint32 sub_part_id,
SYNOPSIS
get_part_id_hash()
- no_parts Number of hash partitions
+ num_parts Number of hash partitions
part_expr Item tree of hash function
out:part_id The returned partition id
out:func_value Value of hash function
@@ -2418,7 +2841,7 @@ static uint32 get_part_id_for_sub(uint32 loc_part_id, uint32 sub_part_id,
FALSE Success
*/
-static int get_part_id_hash(uint no_parts,
+static int get_part_id_hash(uint num_parts,
Item *part_expr,
uint32 *part_id,
longlong *func_value)
@@ -2429,7 +2852,7 @@ static int get_part_id_hash(uint no_parts,
if (part_val_int(part_expr, func_value))
DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
- int_hash_id= *func_value % no_parts;
+ int_hash_id= *func_value % num_parts;
*part_id= int_hash_id < 0 ? (uint32) -int_hash_id : (uint32) int_hash_id;
DBUG_RETURN(FALSE);
@@ -2443,7 +2866,7 @@ static int get_part_id_hash(uint no_parts,
get_part_id_linear_hash()
part_info A reference to the partition_info struct where all the
desired information is given
- no_parts Number of hash partitions
+ num_parts Number of hash partitions
part_expr Item tree of hash function
out:part_id The returned partition id
out:func_value Value of hash function
@@ -2454,7 +2877,7 @@ static int get_part_id_hash(uint no_parts,
*/
static int get_part_id_linear_hash(partition_info *part_info,
- uint no_parts,
+ uint num_parts,
Item *part_expr,
uint32 *part_id,
longlong *func_value)
@@ -2466,7 +2889,7 @@ static int get_part_id_linear_hash(partition_info *part_info,
*part_id= get_part_id_from_linear_hash(*func_value,
part_info->linear_hash_mask,
- no_parts);
+ num_parts);
DBUG_RETURN(FALSE);
}
@@ -2477,7 +2900,7 @@ static int get_part_id_linear_hash(partition_info *part_info,
SYNOPSIS
get_part_id_key()
field_array Array of fields for PARTTION KEY
- no_parts Number of KEY partitions
+ num_parts Number of KEY partitions
RETURN VALUE
Calculated partition id
@@ -2485,12 +2908,12 @@ static int get_part_id_linear_hash(partition_info *part_info,
inline
static uint32 get_part_id_key(Field **field_array,
- uint no_parts,
+ uint num_parts,
longlong *func_value)
{
DBUG_ENTER("get_part_id_key");
*func_value= calculate_key_value(field_array);
- DBUG_RETURN((uint32) (*func_value % no_parts));
+ DBUG_RETURN((uint32) (*func_value % num_parts));
}
@@ -2502,7 +2925,7 @@ static uint32 get_part_id_key(Field **field_array,
part_info A reference to the partition_info struct where all the
desired information is given
field_array Array of fields for PARTTION KEY
- no_parts Number of KEY partitions
+ num_parts Number of KEY partitions
RETURN VALUE
Calculated partition id
@@ -2511,15 +2934,15 @@ static uint32 get_part_id_key(Field **field_array,
inline
static uint32 get_part_id_linear_key(partition_info *part_info,
Field **field_array,
- uint no_parts,
+ uint num_parts,
longlong *func_value)
{
- DBUG_ENTER("get_partition_id_linear_key");
+ DBUG_ENTER("get_part_id_linear_key");
*func_value= calculate_key_value(field_array);
DBUG_RETURN(get_part_id_from_linear_hash(*func_value,
part_info->linear_hash_mask,
- no_parts));
+ num_parts));
}
/*
@@ -2553,7 +2976,8 @@ static void copy_to_part_field_buffers(Field **ptr,
if (!field->maybe_null() || !field->is_null())
{
CHARSET_INFO *cs= ((Field_str*)field)->charset();
- uint len= field->pack_length();
+ uint max_len= field->pack_length();
+ uint data_len= field->data_length();
uchar *field_buf= *field_bufs;
/*
We only use the field buffer for VARCHAR and CHAR strings
@@ -2565,17 +2989,17 @@ static void copy_to_part_field_buffers(Field **ptr,
if (field->type() == MYSQL_TYPE_VARCHAR)
{
uint len_bytes= ((Field_varstring*)field)->length_bytes;
- my_strnxfrm(cs, field_buf + len_bytes, (len - len_bytes),
- field->ptr + len_bytes, field->field_length);
+ my_strnxfrm(cs, field_buf + len_bytes, max_len,
+ field->ptr + len_bytes, data_len);
if (len_bytes == 1)
- *field_buf= (uchar) field->field_length;
+ *field_buf= (uchar) data_len;
else
- int2store(field_buf, field->field_length);
+ int2store(field_buf, data_len);
}
else
{
- my_strnxfrm(cs, field_buf, len,
- field->ptr, field->field_length);
+ my_strnxfrm(cs, field_buf, max_len,
+ field->ptr, max_len);
}
field->ptr= field_buf;
}
@@ -2605,6 +3029,44 @@ static void restore_part_field_pointers(Field **ptr, uchar **restore_ptr)
return;
}
+/*
+ This function is used to calculate the partition id where all partition
+ fields have been prepared to point to a record where the partition field
+ values are bound.
+
+ SYNOPSIS
+ get_partition_id()
+ part_info A reference to the partition_info struct where all the
+ desired information is given
+ out:part_id The partition id is returned through this pointer
+ out:func_value Value of partition function (longlong)
+
+ RETURN VALUE
+ part_id Partition id of partition that would contain
+ row with given values of PF-fields
+ HA_ERR_NO_PARTITION_FOUND The fields of the partition function didn't
+ fit into any partition and thus the values of
+ the PF-fields are not allowed.
+
+ DESCRIPTION
+ A routine used from write_row, update_row and delete_row from any
+ handler supporting partitioning. It is also a support routine for
+ get_partition_set used to find the set of partitions needed to scan
+ for a certain index scan or full table scan.
+
+ It is actually 9 different variants of this function which are called
+ through a function pointer.
+
+ get_partition_id_list
+ get_partition_id_list_col
+ get_partition_id_range
+ get_partition_id_range_col
+ get_partition_id_hash_nosub
+ get_partition_id_key_nosub
+ get_partition_id_linear_hash_nosub
+ get_partition_id_linear_key_nosub
+ get_partition_id_with_sub
+*/
/*
This function is used to calculate the main partition to use in the case of
@@ -2626,67 +3088,26 @@ static void restore_part_field_pointers(Field **ptr, uchar **restore_ptr)
DESCRIPTION
- It is actually 6 different variants of this function which are called
+ It is actually 8 different variants of this function which are called
through a function pointer.
get_partition_id_list
+ get_partition_id_list_col
get_partition_id_range
+ get_partition_id_range_col
get_partition_id_hash_nosub
get_partition_id_key_nosub
get_partition_id_linear_hash_nosub
get_partition_id_linear_key_nosub
*/
-static int get_part_id_charset_func_subpart(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value)
-{
- int res;
- copy_to_part_field_buffers(part_info->subpart_charset_field_array,
- part_info->subpart_field_buffers,
- part_info->restore_subpart_field_ptrs);
- res= part_info->get_partition_id_charset(part_info, part_id, func_value);
- restore_part_field_pointers(part_info->subpart_charset_field_array,
- part_info->restore_subpart_field_ptrs);
- return res;
-}
-
-
static int get_part_id_charset_func_part(partition_info *part_info,
uint32 *part_id,
longlong *func_value)
{
int res;
- copy_to_part_field_buffers(part_info->part_charset_field_array,
- part_info->part_field_buffers,
- part_info->restore_part_field_ptrs);
- res= part_info->get_partition_id_charset(part_info, part_id, func_value);
- restore_part_field_pointers(part_info->part_charset_field_array,
- part_info->restore_part_field_ptrs);
- return res;
-}
-
+ DBUG_ENTER("get_part_id_charset_func_part");
-static int get_part_id_charset_func_all(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value)
-{
- int res;
- copy_to_part_field_buffers(part_info->full_part_field_array,
- part_info->full_part_field_buffers,
- part_info->restore_full_part_field_ptrs);
- res= part_info->get_partition_id_charset(part_info, part_id, func_value);
- restore_part_field_pointers(part_info->full_part_field_array,
- part_info->restore_full_part_field_ptrs);
- return res;
-}
-
-
-static int get_part_part_id_charset_func(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value)
-{
- int res;
copy_to_part_field_buffers(part_info->part_charset_field_array,
part_info->part_field_buffers,
part_info->restore_part_field_ptrs);
@@ -2694,21 +3115,58 @@ static int get_part_part_id_charset_func(partition_info *part_info,
part_id, func_value);
restore_part_field_pointers(part_info->part_charset_field_array,
part_info->restore_part_field_ptrs);
- return res;
+ DBUG_RETURN(res);
}
-static int get_subpart_id_charset_func(partition_info *part_info,
- uint32 *part_id)
+static int get_part_id_charset_func_subpart(partition_info *part_info,
+ uint32 *part_id)
{
int res;
+ DBUG_ENTER("get_part_id_charset_func_subpart");
+
copy_to_part_field_buffers(part_info->subpart_charset_field_array,
part_info->subpart_field_buffers,
part_info->restore_subpart_field_ptrs);
res= part_info->get_subpartition_id_charset(part_info, part_id);
restore_part_field_pointers(part_info->subpart_charset_field_array,
part_info->restore_subpart_field_ptrs);
- return res;
+ DBUG_RETURN(res);
+}
+
+int get_partition_id_list_col(partition_info *part_info,
+ uint32 *part_id,
+ longlong *func_value)
+{
+ part_column_list_val *list_col_array= part_info->list_col_array;
+ uint num_columns= part_info->part_field_list.elements;
+ int list_index, cmp;
+ int min_list_index= 0;
+ int max_list_index= part_info->num_list_values - 1;
+ DBUG_ENTER("get_partition_id_list_col");
+
+ while (max_list_index >= min_list_index)
+ {
+ list_index= (max_list_index + min_list_index) >> 1;
+ cmp= cmp_rec_and_tuple(list_col_array + list_index*num_columns,
+ num_columns);
+ if (cmp > 0)
+ min_list_index= list_index + 1;
+ else if (cmp < 0)
+ {
+ if (!list_index)
+ goto notfound;
+ max_list_index= list_index - 1;
+ }
+ else
+ {
+ *part_id= (uint32)list_col_array[list_index*num_columns].partition_id;
+ DBUG_RETURN(0);
+ }
+ }
+notfound:
+ *part_id= 0;
+ DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
}
@@ -2719,7 +3177,7 @@ int get_partition_id_list(partition_info *part_info,
LIST_PART_ENTRY *list_array= part_info->list_array;
int list_index;
int min_list_index= 0;
- int max_list_index= part_info->no_list_values - 1;
+ int max_list_index= part_info->num_list_values - 1;
longlong part_func_value;
int error= part_val_int(part_info->part_expr, &part_func_value);
longlong list_value;
@@ -2765,42 +3223,93 @@ notfound:
}
-/*
- Find the sub-array part_info->list_array that corresponds to given interval
+uint32 get_partition_id_cols_list_for_endpoint(partition_info *part_info,
+ bool left_endpoint,
+ bool include_endpoint,
+ uint32 nparts)
+{
+ part_column_list_val *list_col_array= part_info->list_col_array;
+ uint num_columns= part_info->part_field_list.elements;
+ uint list_index;
+ uint min_list_index= 0;
+ int cmp;
+ /* Notice that max_list_index = last_index + 1 here! */
+ uint max_list_index= part_info->num_list_values;
+ DBUG_ENTER("get_partition_id_cols_list_for_endpoint");
- SYNOPSIS
- get_list_array_idx_for_endpoint()
- part_info Partitioning info (partitioning type must be LIST)
- left_endpoint TRUE - the interval is [a; +inf) or (a; +inf)
- FALSE - the interval is (-inf; a] or (-inf; a)
- include_endpoint TRUE iff the interval includes the endpoint
+ /* Find the matching partition (including taking endpoint into account). */
+ do
+ {
+ /* Midpoint, adjusted down, so it can never be >= max_list_index. */
+ list_index= (max_list_index + min_list_index) >> 1;
+ cmp= cmp_rec_and_tuple_prune(list_col_array + list_index*num_columns,
+ nparts, left_endpoint, include_endpoint);
+ if (cmp > 0)
+ {
+ min_list_index= list_index + 1;
+ }
+ else
+ {
+ max_list_index= list_index;
+ if (cmp == 0)
+ break;
+ }
+ } while (max_list_index > min_list_index);
+ list_index= max_list_index;
+
+ /* Given value must be LESS THAN or EQUAL to the found partition. */
+ DBUG_ASSERT(list_index == part_info->num_list_values ||
+ (0 >= cmp_rec_and_tuple_prune(list_col_array +
+ list_index*num_columns,
+ nparts, left_endpoint,
+ include_endpoint)));
+ /* Given value must be GREATER THAN the previous partition. */
+ DBUG_ASSERT(list_index == 0 ||
+ (0 < cmp_rec_and_tuple_prune(list_col_array +
+ (list_index - 1)*num_columns,
+ nparts, left_endpoint,
+ include_endpoint)));
+
+ /* Include the right endpoint if not already passed end of array. */
+ if (!left_endpoint && include_endpoint && cmp == 0 &&
+ list_index < part_info->num_list_values)
+ list_index++;
- DESCRIPTION
- This function finds the sub-array of part_info->list_array where values of
- list_array[idx].list_value are contained within the specifed interval.
- list_array is ordered by list_value, so
- 1. For [a; +inf) or (a; +inf)-type intervals (left_endpoint==TRUE), the
- sought sub-array starts at some index idx and continues till array end.
- The function returns first number idx, such that
- list_array[idx].list_value is contained within the passed interval.
-
- 2. For (-inf; a] or (-inf; a)-type intervals (left_endpoint==FALSE), the
- sought sub-array starts at array start and continues till some last
- index idx.
- The function returns first number idx, such that
- list_array[idx].list_value is NOT contained within the passed interval.
- If all array elements are contained, part_info->no_list_values is
- returned.
+ DBUG_RETURN(list_index);
+}
- NOTE
- The caller will call this function and then will run along the sub-array of
- list_array to collect partition ids. If the number of list values is
- significantly higher then number of partitions, this could be slow and
- we could invent some other approach. The "run over list array" part is
- already wrapped in a get_next()-like function.
- RETURN
- The edge of corresponding sub-array of part_info->list_array
+/**
+ Find the sub-array part_info->list_array that corresponds to given interval.
+
+ @param part_info Partitioning info (partitioning type must be LIST)
+ @param left_endpoint TRUE - the interval is [a; +inf) or (a; +inf)
+ FALSE - the interval is (-inf; a] or (-inf; a)
+ @param include_endpoint TRUE iff the interval includes the endpoint
+
+ This function finds the sub-array of part_info->list_array where values of
+ list_array[idx].list_value are contained within the specifed interval.
+ list_array is ordered by list_value, so
+ 1. For [a; +inf) or (a; +inf)-type intervals (left_endpoint==TRUE), the
+ sought sub-array starts at some index idx and continues till array end.
+ The function returns first number idx, such that
+ list_array[idx].list_value is contained within the passed interval.
+
+ 2. For (-inf; a] or (-inf; a)-type intervals (left_endpoint==FALSE), the
+ sought sub-array starts at array start and continues till some last
+ index idx.
+ The function returns first number idx, such that
+ list_array[idx].list_value is NOT contained within the passed interval.
+ If all array elements are contained, part_info->num_list_values is
+ returned.
+
+ @note The caller will call this function and then will run along the
+ sub-array of list_array to collect partition ids. If the number of list
+ values is significantly higher then number of partitions, this could be slow
+ and we could invent some other approach. The "run over list array" part is
+ already wrapped in a get_next()-like function.
+
+ @return The index of corresponding sub-array of part_info->list_array.
*/
uint32 get_list_array_idx_for_endpoint_charset(partition_info *part_info,
@@ -2824,7 +3333,7 @@ uint32 get_list_array_idx_for_endpoint(partition_info *part_info,
{
LIST_PART_ENTRY *list_array= part_info->list_array;
uint list_index;
- uint min_list_index= 0, max_list_index= part_info->no_list_values - 1;
+ uint min_list_index= 0, max_list_index= part_info->num_list_values - 1;
longlong list_value;
/* Get the partitioning function value for the endpoint */
longlong part_func_value=
@@ -2854,7 +3363,7 @@ uint32 get_list_array_idx_for_endpoint(partition_info *part_info,
if (unsigned_flag)
part_func_value-= 0x8000000000000000ULL;
- DBUG_ASSERT(part_info->no_list_values);
+ DBUG_ASSERT(part_info->num_list_values);
do
{
list_index= (max_list_index + min_list_index) >> 1;
@@ -2879,12 +3388,49 @@ notfound:
}
+int get_partition_id_range_col(partition_info *part_info,
+ uint32 *part_id,
+ longlong *func_value)
+{
+ part_column_list_val *range_col_array= part_info->range_col_array;
+ uint num_columns= part_info->part_field_list.elements;
+ uint max_partition= part_info->num_parts - 1;
+ uint min_part_id= 0;
+ uint max_part_id= max_partition;
+ uint loc_part_id;
+ DBUG_ENTER("get_partition_id_range_col");
+
+ while (max_part_id > min_part_id)
+ {
+ loc_part_id= (max_part_id + min_part_id + 1) >> 1;
+ if (cmp_rec_and_tuple(range_col_array + loc_part_id*num_columns,
+ num_columns) >= 0)
+ min_part_id= loc_part_id + 1;
+ else
+ max_part_id= loc_part_id - 1;
+ }
+ loc_part_id= max_part_id;
+ if (loc_part_id != max_partition)
+ if (cmp_rec_and_tuple(range_col_array + loc_part_id*num_columns,
+ num_columns) >= 0)
+ loc_part_id++;
+ *part_id= (uint32)loc_part_id;
+ if (loc_part_id == max_partition &&
+ (cmp_rec_and_tuple(range_col_array + loc_part_id*num_columns,
+ num_columns) >= 0))
+ DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
+
+ 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)
{
longlong *range_array= part_info->range_int_array;
- uint max_partition= part_info->no_parts - 1;
+ uint max_partition= part_info->num_parts - 1;
uint min_part_id= 0;
uint max_part_id= max_partition;
uint loc_part_id;
@@ -2959,7 +3505,7 @@ int get_partition_id_range(partition_info *part_info,
represented by range_int_array[idx] has EMPTY intersection with the
passed interval.
If the interval represented by the last array element has non-empty
- intersection with the passed interval, part_info->no_parts is
+ intersection with the passed interval, part_info->num_parts is
returned.
RETURN
@@ -2988,7 +3534,7 @@ uint32 get_partition_id_range_for_endpoint(partition_info *part_info,
{
longlong *range_array= part_info->range_int_array;
longlong part_end_val;
- uint max_partition= part_info->no_parts - 1;
+ uint max_partition= part_info->num_parts - 1;
uint min_part_id= 0, max_part_id= max_partition, loc_part_id;
/* Get the partitioning function value for the endpoint */
longlong part_func_value=
@@ -3076,7 +3622,7 @@ int get_partition_id_hash_nosub(partition_info *part_info,
uint32 *part_id,
longlong *func_value)
{
- return get_part_id_hash(part_info->no_parts, part_info->part_expr,
+ return get_part_id_hash(part_info->num_parts, part_info->part_expr,
part_id, func_value);
}
@@ -3085,7 +3631,7 @@ int get_partition_id_linear_hash_nosub(partition_info *part_info,
uint32 *part_id,
longlong *func_value)
{
- return get_part_id_linear_hash(part_info, part_info->no_parts,
+ return get_part_id_linear_hash(part_info, part_info->num_parts,
part_info->part_expr, part_id, func_value);
}
@@ -3095,232 +3641,44 @@ int get_partition_id_key_nosub(partition_info *part_info,
longlong *func_value)
{
*part_id= get_part_id_key(part_info->part_field_array,
- part_info->no_parts, func_value);
+ part_info->num_parts, func_value);
return 0;
}
int get_partition_id_linear_key_nosub(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value)
+ uint32 *part_id,
+ longlong *func_value)
{
*part_id= get_part_id_linear_key(part_info,
part_info->part_field_array,
- part_info->no_parts, func_value);
+ part_info->num_parts, func_value);
return 0;
}
-int get_partition_id_range_sub_hash(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value)
-{
- uint32 loc_part_id, sub_part_id;
- uint no_subparts;
- longlong local_func_value;
- int error;
- DBUG_ENTER("get_partition_id_range_sub_hash");
- LINT_INIT(loc_part_id);
- LINT_INIT(sub_part_id);
-
- if (unlikely((error= get_partition_id_range(part_info, &loc_part_id,
- func_value))))
- {
- DBUG_RETURN(error);
- }
- no_subparts= part_info->no_subparts;
- if (unlikely((error= get_part_id_hash(no_subparts, part_info->subpart_expr,
- &sub_part_id, &local_func_value))))
- {
- DBUG_RETURN(error);
- }
-
- *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
- DBUG_RETURN(0);
-}
-
-
-int get_partition_id_range_sub_linear_hash(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value)
-{
- uint32 loc_part_id, sub_part_id;
- uint no_subparts;
- longlong local_func_value;
- int error;
- DBUG_ENTER("get_partition_id_range_sub_linear_hash");
- LINT_INIT(loc_part_id);
- LINT_INIT(sub_part_id);
-
- if (unlikely((error= get_partition_id_range(part_info, &loc_part_id,
- func_value))))
- {
- DBUG_RETURN(error);
- }
- no_subparts= part_info->no_subparts;
- if (unlikely((error= get_part_id_linear_hash(part_info, no_subparts,
- part_info->subpart_expr,
- &sub_part_id,
- &local_func_value))))
- {
- DBUG_RETURN(error);
- }
-
- *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
- DBUG_RETURN(0);
-}
-
-
-int get_partition_id_range_sub_key(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value)
-{
- uint32 loc_part_id, sub_part_id;
- uint no_subparts;
- longlong local_func_value;
- int error;
- DBUG_ENTER("get_partition_id_range_sub_key");
- LINT_INIT(loc_part_id);
-
- if (unlikely((error= get_partition_id_range(part_info, &loc_part_id,
- func_value))))
- {
- DBUG_RETURN(error);
- }
- no_subparts= part_info->no_subparts;
- sub_part_id= get_part_id_key(part_info->subpart_field_array,
- no_subparts, &local_func_value);
- *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
- DBUG_RETURN(0);
-}
-
-
-int get_partition_id_range_sub_linear_key(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value)
-{
- uint32 loc_part_id, sub_part_id;
- uint no_subparts;
- longlong local_func_value;
- int error;
- DBUG_ENTER("get_partition_id_range_sub_linear_key");
- LINT_INIT(loc_part_id);
-
- if (unlikely((error= get_partition_id_range(part_info, &loc_part_id,
- func_value))))
- {
- DBUG_RETURN(error);
- }
- no_subparts= part_info->no_subparts;
- sub_part_id= get_part_id_linear_key(part_info,
- part_info->subpart_field_array,
- no_subparts, &local_func_value);
- *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
- DBUG_RETURN(0);
-}
-
-
-int get_partition_id_list_sub_hash(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value)
-{
- uint32 loc_part_id, sub_part_id;
- uint no_subparts;
- longlong local_func_value;
- int error;
- DBUG_ENTER("get_partition_id_list_sub_hash");
- LINT_INIT(sub_part_id);
-
- if (unlikely((error= get_partition_id_list(part_info, &loc_part_id,
- func_value))))
- {
- DBUG_RETURN(error);
- }
- no_subparts= part_info->no_subparts;
- if (unlikely((error= get_part_id_hash(no_subparts, part_info->subpart_expr,
- &sub_part_id, &local_func_value))))
- {
- DBUG_RETURN(error);
- }
-
- *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
- DBUG_RETURN(0);
-}
-
-
-int get_partition_id_list_sub_linear_hash(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value)
-{
- uint32 loc_part_id, sub_part_id;
- uint no_subparts;
- longlong local_func_value;
- int error;
- DBUG_ENTER("get_partition_id_list_sub_linear_hash");
- LINT_INIT(sub_part_id);
-
- if (unlikely((error= get_partition_id_list(part_info, &loc_part_id,
- func_value))))
- {
- DBUG_RETURN(error);
- }
- no_subparts= part_info->no_subparts;
- if (unlikely((error= get_part_id_linear_hash(part_info, no_subparts,
- part_info->subpart_expr,
- &sub_part_id,
- &local_func_value))))
- {
- DBUG_RETURN(error);
- }
-
- *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
- DBUG_RETURN(0);
-}
-
-
-int get_partition_id_list_sub_key(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value)
+int get_partition_id_with_sub(partition_info *part_info,
+ uint32 *part_id,
+ longlong *func_value)
{
uint32 loc_part_id, sub_part_id;
- uint no_subparts;
- longlong local_func_value;
+ uint num_subparts;
int error;
- DBUG_ENTER("get_partition_id_range_sub_key");
+ DBUG_ENTER("get_partition_id_with_sub");
- if (unlikely((error= get_partition_id_list(part_info, &loc_part_id,
- func_value))))
+ if (unlikely((error= part_info->get_part_partition_id(part_info,
+ &loc_part_id,
+ func_value))))
{
DBUG_RETURN(error);
}
- no_subparts= part_info->no_subparts;
- sub_part_id= get_part_id_key(part_info->subpart_field_array,
- no_subparts, &local_func_value);
- *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
- DBUG_RETURN(0);
-}
-
-
-int get_partition_id_list_sub_linear_key(partition_info *part_info,
- uint32 *part_id,
- longlong *func_value)
-{
- uint32 loc_part_id, sub_part_id;
- uint no_subparts;
- longlong local_func_value;
- int error;
- DBUG_ENTER("get_partition_id_list_sub_linear_key");
-
- if (unlikely((error= get_partition_id_list(part_info, &loc_part_id,
- func_value))))
+ num_subparts= part_info->num_subparts;
+ if (unlikely((error= part_info->get_subpartition_id(part_info,
+ &sub_part_id))))
{
DBUG_RETURN(error);
- }
- no_subparts= part_info->no_subparts;
- sub_part_id= get_part_id_linear_key(part_info,
- part_info->subpart_field_array,
- no_subparts, &local_func_value);
- *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, no_subparts);
+ }
+ *part_id= get_part_id_for_sub(loc_part_id, sub_part_id, num_subparts);
DBUG_RETURN(0);
}
@@ -3353,7 +3711,7 @@ int get_partition_id_hash_sub(partition_info *part_info,
uint32 *part_id)
{
longlong func_value;
- return get_part_id_hash(part_info->no_subparts, part_info->subpart_expr,
+ return get_part_id_hash(part_info->num_subparts, part_info->subpart_expr,
part_id, &func_value);
}
@@ -3362,7 +3720,7 @@ int get_partition_id_linear_hash_sub(partition_info *part_info,
uint32 *part_id)
{
longlong func_value;
- return get_part_id_linear_hash(part_info, part_info->no_subparts,
+ return get_part_id_linear_hash(part_info, part_info->num_subparts,
part_info->subpart_expr, part_id,
&func_value);
}
@@ -3373,7 +3731,7 @@ int get_partition_id_key_sub(partition_info *part_info,
{
longlong func_value;
*part_id= get_part_id_key(part_info->subpart_field_array,
- part_info->no_subparts, &func_value);
+ part_info->num_subparts, &func_value);
return FALSE;
}
@@ -3384,7 +3742,7 @@ int get_partition_id_linear_key_sub(partition_info *part_info,
longlong func_value;
*part_id= get_part_id_linear_key(part_info,
part_info->subpart_field_array,
- part_info->no_subparts, &func_value);
+ part_info->num_subparts, &func_value);
return FALSE;
}
@@ -3683,18 +4041,18 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index,
const key_range *key_spec, part_id_range *part_spec)
{
partition_info *part_info= table->part_info;
- uint no_parts= part_info->get_tot_partitions();
+ uint num_parts= part_info->get_tot_partitions();
uint i, part_id;
- uint sub_part= no_parts;
- uint32 part_part= no_parts;
+ uint sub_part= num_parts;
+ uint32 part_part= num_parts;
KEY *key_info= NULL;
bool found_part_field= FALSE;
DBUG_ENTER("get_partition_set");
part_spec->start_part= 0;
- part_spec->end_part= no_parts - 1;
+ part_spec->end_part= num_parts - 1;
if ((index < MAX_KEY) &&
- key_spec->flag == (uint)HA_READ_KEY_EXACT &&
+ key_spec && key_spec->flag == (uint)HA_READ_KEY_EXACT &&
part_info->some_fields_in_PF.is_set(index))
{
key_info= table->key_info+index;
@@ -3729,7 +4087,7 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index,
{
if (get_sub_part_id_from_key(table, buf, key_info, key_spec, &sub_part))
{
- part_spec->start_part= no_parts;
+ part_spec->start_part= num_parts;
DBUG_VOID_RETURN;
}
}
@@ -3743,7 +4101,7 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index,
allowed values. Thus it is certain that the result of this
scan will be empty.
*/
- part_spec->start_part= no_parts;
+ part_spec->start_part= num_parts;
DBUG_VOID_RETURN;
}
}
@@ -3781,7 +4139,7 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index,
{
if (get_sub_part_id_from_key(table, buf, key_info, key_spec, &sub_part))
{
- part_spec->start_part= no_parts;
+ part_spec->start_part= num_parts;
clear_indicator_in_key_fields(key_info);
DBUG_VOID_RETURN;
}
@@ -3790,7 +4148,7 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index,
{
if (get_part_id_from_key(table,buf,key_info,key_spec,&part_part))
{
- part_spec->start_part= no_parts;
+ part_spec->start_part= num_parts;
clear_indicator_in_key_fields(key_info);
DBUG_VOID_RETURN;
}
@@ -3811,29 +4169,29 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index,
nothing or we have discovered a range of partitions with possible holes
in it. We need a bitvector to further the work here.
*/
- if (!(part_part == no_parts && sub_part == no_parts))
+ if (!(part_part == num_parts && sub_part == num_parts))
{
/*
We can only arrive here if we are using subpartitioning.
*/
- if (part_part != no_parts)
+ if (part_part != num_parts)
{
/*
We know the top partition and need to scan all underlying
subpartitions. This is a range without holes.
*/
- DBUG_ASSERT(sub_part == no_parts);
- part_spec->start_part= part_part * part_info->no_subparts;
- part_spec->end_part= part_spec->start_part+part_info->no_subparts - 1;
+ DBUG_ASSERT(sub_part == num_parts);
+ part_spec->start_part= part_part * part_info->num_subparts;
+ part_spec->end_part= part_spec->start_part+part_info->num_subparts - 1;
}
else
{
- DBUG_ASSERT(sub_part != no_parts);
+ DBUG_ASSERT(sub_part != num_parts);
part_spec->start_part= sub_part;
part_spec->end_part=sub_part+
- (part_info->no_subparts*(part_info->no_parts-1));
- for (i= 0, part_id= sub_part; i < part_info->no_parts;
- i++, part_id+= part_info->no_subparts)
+ (part_info->num_subparts*(part_info->num_parts-1));
+ for (i= 0, part_id= sub_part; i < part_info->num_parts;
+ i++, part_id+= part_info->num_subparts)
; //Set bit part_id in bit array
}
}
@@ -3905,7 +4263,6 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index,
bool mysql_unpack_partition(THD *thd,
char *part_buf, uint part_info_len,
- const char *part_state, uint part_state_len,
TABLE* table, bool is_create_table_ind,
handlerton *default_db_type,
bool *work_part_info_used)
@@ -3917,28 +4274,15 @@ bool mysql_unpack_partition(THD *thd,
LEX lex;
DBUG_ENTER("mysql_unpack_partition");
- thd->lex= &lex;
thd->variables.character_set_client= system_charset_info;
Parser_state parser_state;
if (parser_state.init(thd, part_buf, part_info_len))
goto end;
- lex_start(thd);
- *work_part_info_used= false;
- /*
- We need to use the current SELECT_LEX since I need to keep the
- Name_resolution_context object which is referenced from the
- Item_field objects.
- This is not a nice solution since if the parser uses current_select
- for anything else it will corrupt the current LEX object.
- Also, we need to make sure there even is a select -- if the statement
- was a "USE ...", current_select will be NULL, but we may still end up
- here if we try to log to a partitioned table. This is currently
- unsupported, but should still fail rather than crash!
- */
- if (!(thd->lex->current_select= old_lex->current_select))
+ if (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
@@ -3948,16 +4292,17 @@ bool mysql_unpack_partition(THD *thd,
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));
goto end;
}
- lex.part_info->part_state= part_state;
- lex.part_info->part_state_len= part_state_len;
+ part_info= lex.part_info;
DBUG_PRINT("info", ("Parse: %s", part_buf));
- if (parse_sql(thd, & parser_state, NULL))
+ if (parse_sql(thd, & parser_state, NULL) ||
+ part_info->fix_parser_data(thd))
{
thd->free_items();
goto end;
@@ -3978,45 +4323,27 @@ bool mysql_unpack_partition(THD *thd,
*/
DBUG_PRINT("info", ("Successful parse"));
- part_info= lex.part_info;
DBUG_PRINT("info", ("default engine = %s, default_db_type = %s",
ha_resolve_storage_engine_name(part_info->default_engine_type),
ha_resolve_storage_engine_name(default_db_type)));
if (is_create_table_ind && old_lex->sql_command == SQLCOM_CREATE_TABLE)
{
- if (old_lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
- {
- /*
- This code is executed when we create table in CREATE TABLE t1 LIKE t2.
- old_lex->query_tables contains table list element for t2 and the table
- we are opening has name t1.
- */
- if (partition_default_handling(table, part_info, FALSE,
- old_lex->query_tables->table->s->path.str))
- {
- result= TRUE;
- goto end;
- }
- }
- else
- {
- /*
- When we come here we are doing a create table. In this case we
- have already done some preparatory work on the old part_info
- object. We don't really need this new partition_info object.
- Thus we go back to the old partition info object.
- We need to free any memory objects allocated on item_free_list
- by the parser since we are keeping the old info from the first
- parser call in CREATE TABLE.
- We'll ensure that this object isn't put into table cache also
- just to ensure we don't get into strange situations with the
- item objects.
- */
- thd->free_items();
- part_info= thd->work_part_info;
- table->s->version= 0UL;
- *work_part_info_used= true;
- }
+ /*
+ When we come here we are doing a create table. In this case we
+ have already done some preparatory work on the old part_info
+ object. We don't really need this new partition_info object.
+ Thus we go back to the old partition info object.
+ We need to free any memory objects allocated on item_free_list
+ by the parser since we are keeping the old info from the first
+ parser call in CREATE TABLE.
+
+ This table object can not be used any more. However, since
+ this is CREATE TABLE, we know that it will be destroyed by the
+ caller, and rely on that.
+ */
+ thd->free_items();
+ part_info= thd->work_part_info;
+ *work_part_info_used= true;
}
table->part_info= part_info;
table->file->set_part_info(part_info);
@@ -4060,8 +4387,7 @@ bool mysql_unpack_partition(THD *thd,
result= FALSE;
end:
- lex_end(thd->lex);
- thd->lex= old_lex;
+ end_lex_with_single_table(thd, table, old_lex);
thd->variables.character_set_client= old_character_set_client;
DBUG_RETURN(result);
}
@@ -4099,60 +4425,35 @@ set_engine_all_partitions(partition_info *part_info,
partition_element *sub_elem= sub_it++;
sub_elem->engine_type= engine_type;
- } while (++j < part_info->no_subparts);
+ } while (++j < part_info->num_subparts);
}
- } while (++i < part_info->no_parts);
+ } while (++i < part_info->num_parts);
}
-/*
- SYNOPSIS
- fast_end_partition()
- thd Thread object
- out:copied Number of records copied
- out:deleted Number of records deleted
- table_list Table list with the one table in it
- empty Has nothing been done
- lpt Struct to be used by error handler
- RETURN VALUES
- FALSE Success
- TRUE Failure
- DESCRIPTION
- Support routine to handle the successful cases for partition
- management.
+/**
+ Support routine to handle the successful cases for partition management.
+
+ @param thd Thread object
+ @param copied Number of records copied
+ @param deleted Number of records deleted
+ @param table_list Table list with the one table in it
+
+ @return Operation status
+ @retval FALSE Success
+ @retval TRUE Failure
*/
static int fast_end_partition(THD *thd, ulonglong copied,
ulonglong deleted,
- TABLE *table,
- TABLE_LIST *table_list, bool is_empty,
- ALTER_PARTITION_PARAM_TYPE *lpt,
- bool written_bin_log)
+ TABLE_LIST *table_list)
{
- int error;
char tmp_name[80];
DBUG_ENTER("fast_end_partition");
thd->proc_info="end";
- if (!is_empty)
- query_cache_invalidate3(thd, table_list, 0);
-
- error= ha_autocommit_or_rollback(thd, 0);
- if (end_active_trans(thd))
- error= 1;
-
- if (error)
- {
- /* If error during commit, no need to rollback, it's done. */
- table->file->print_error(error, MYF(0));
- DBUG_RETURN(TRUE);
- }
-
- if ((!is_empty) && (!written_bin_log) &&
- (!thd->lex->no_write_to_binlog) &&
- write_bin_log(thd, FALSE, thd->query(), thd->query_length()))
- DBUG_RETURN(TRUE);
+ query_cache_invalidate3(thd, table_list, 0);
my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
(ulong) (copied + deleted),
@@ -4240,14 +4541,23 @@ error:
}
-/*
- Sets which partitions to be used in the command
+/**
+ Sets which partitions to be used in the command.
+
+ @param alter_info Alter_info pointer holding partition names and flags.
+ @param tab_part_info partition_info holding all partitions.
+ @param part_state Which state to set for the named partitions.
+
+ @return Operation status
+ @retval false Success
+ @retval true Failure
*/
-uint set_part_state(Alter_info *alter_info, partition_info *tab_part_info,
- enum partition_state part_state)
+
+bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info,
+ enum partition_state part_state)
{
uint part_count= 0;
- uint no_parts_found= 0;
+ uint num_parts_found= 0;
List_iterator<partition_element> part_it(tab_part_info->partitions);
do
@@ -4260,41 +4570,53 @@ uint set_part_state(Alter_info *alter_info, partition_info *tab_part_info,
/*
Mark the partition.
I.e mark the partition as a partition to be "changed" by
- analyzing/optimizing/rebuilding/checking/repairing
+ analyzing/optimizing/rebuilding/checking/repairing/...
*/
- no_parts_found++;
+ num_parts_found++;
part_elem->part_state= part_state;
DBUG_PRINT("info", ("Setting part_state to %u for partition %s",
part_state, part_elem->partition_name));
}
- } while (++part_count < tab_part_info->no_parts);
- return no_parts_found;
+ else
+ part_elem->part_state= PART_NORMAL;
+ } while (++part_count < tab_part_info->num_parts);
+
+ if (num_parts_found != alter_info->partition_names.elements &&
+ !(alter_info->flags & ALTER_ALL_PARTITION))
+ {
+ /* Not all given partitions found, revert and return failure */
+ part_it.rewind();
+ part_count= 0;
+ do
+ {
+ partition_element *part_elem= part_it++;
+ part_elem->part_state= PART_NORMAL;
+ } while (++part_count < tab_part_info->num_parts);
+ return true;
+ }
+ return false;
}
-/*
+/**
Prepare for ALTER TABLE of partition structure
- SYNOPSIS
- prep_alter_part_table()
- thd Thread object
- table Table object
- inout:alter_info Alter information
- inout:create_info Create info for CREATE TABLE
- old_db_type Old engine type
- out:partition_changed Boolean indicating whether partition changed
- out:fast_alter_partition Boolean indicating whether fast partition
- change is requested
+ @param[in] thd Thread object
+ @param[in] table Table object
+ @param[in,out] alter_info Alter information
+ @param[in,out] create_info Create info for CREATE TABLE
+ @param[in] old_db_type Old engine type
+ @param[out] partition_changed Boolean indicating whether partition changed
+ @param[out] fast_alter_table Internal temporary table allowing fast
+ partition change or NULL if not possible
- RETURN VALUES
- TRUE Error
- FALSE Success
- partition_changed
- fast_alter_partition
+ @return Operation status
+ @retval TRUE Error
+ @retval FALSE Success
- DESCRIPTION
+ @note
This method handles all preparations for ALTER TABLE for partitioned
- tables
+ tables.
We need to handle both partition management command such as Add Partition
and others here as well as an ALTER TABLE that completely changes the
partitioning and yet others that don't change anything at all. We start
@@ -4306,8 +4628,12 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
HA_CREATE_INFO *create_info,
handlerton *old_db_type,
bool *partition_changed,
- uint *fast_alter_partition)
+ char *db,
+ const char *table_name,
+ const char *path,
+ TABLE **fast_alter_table)
{
+ TABLE *new_table= NULL;
DBUG_ENTER("prep_alter_part_table");
/* Foreign keys on partitioned tables are not supported, waits for WL#148 */
@@ -4316,16 +4642,9 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
my_error(ER_FOREIGN_KEY_ON_PARTITIONED, MYF(0));
DBUG_RETURN(TRUE);
}
- /*
- We are going to manipulate the partition info on the table object
- so we need to ensure that the data structure of the table object
- is freed by setting version to 0. table->s->version= 0 forces a
- flush of the table object in close_thread_tables().
- */
- if (table->part_info)
- table->s->version= 0L;
thd->work_part_info= thd->lex->part_info;
+
if (thd->work_part_info &&
!(thd->work_part_info= thd->lex->part_info->get_clone()))
DBUG_RETURN(TRUE);
@@ -4338,25 +4657,51 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
ALTER_COALESCE_PARTITION | ALTER_REORGANIZE_PARTITION |
ALTER_TABLE_REORG | ALTER_REBUILD_PARTITION))
{
- partition_info *tab_part_info= table->part_info;
+ partition_info *tab_part_info;
partition_info *alt_part_info= thd->work_part_info;
uint flags= 0;
- if (!tab_part_info)
+ bool is_last_partition_reorged= FALSE;
+ part_elem_value *tab_max_elem_val= NULL;
+ part_elem_value *alt_max_elem_val= NULL;
+ longlong tab_max_range= 0, alt_max_range= 0;
+
+ if (!table->part_info)
{
my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
DBUG_RETURN(TRUE);
}
+
+ /*
+ Open our intermediate table, we will operate on a temporary instance
+ of the original table, to be able to skip copying all partitions.
+ Open it as a copy of the original table, and modify its partition_info
+ object to allow fast_alter_partition_table to perform the changes.
+ */
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
+ MDL_INTENTION_EXCLUSIVE));
+ new_table= open_table_uncached(thd, path, db, table_name, 0);
+ if (!new_table)
+ DBUG_RETURN(TRUE);
+
+ /*
+ This table may be used for copy rows between partitions
+ and also read/write columns when fixing the partition_info struct.
+ */
+ new_table->use_all_columns();
+
+ tab_part_info= new_table->part_info;
+
if (alter_info->flags & ALTER_TABLE_REORG)
{
uint new_part_no, curr_part_no;
if (tab_part_info->part_type != HASH_PARTITION ||
- tab_part_info->use_default_no_partitions)
+ tab_part_info->use_default_num_partitions)
{
my_error(ER_REORG_NO_PARAM_ERROR, MYF(0));
- DBUG_RETURN(TRUE);
+ goto err;
}
- new_part_no= table->file->get_default_no_partitions(create_info);
- curr_part_no= tab_part_info->no_parts;
+ new_part_no= new_table->file->get_default_no_partitions(create_info);
+ curr_part_no= tab_part_info->num_parts;
if (new_part_no == curr_part_no)
{
/*
@@ -4364,7 +4709,8 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
after the change as before. Thus we can reply ok immediately
without any changes at all.
*/
- *fast_alter_partition= TRUE;
+ *fast_alter_table= new_table;
+ thd->work_part_info= tab_part_info;
DBUG_RETURN(FALSE);
}
else if (new_part_no > curr_part_no)
@@ -4374,7 +4720,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
setting the flag for no default number of partitions
*/
alter_info->flags|= ALTER_ADD_PARTITION;
- thd->work_part_info->no_parts= new_part_no - curr_part_no;
+ thd->work_part_info->num_parts= new_part_no - curr_part_no;
}
else
{
@@ -4383,46 +4729,85 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
without setting the flag for no default number of partitions
*/
alter_info->flags|= ALTER_COALESCE_PARTITION;
- alter_info->no_parts= curr_part_no - new_part_no;
+ alter_info->num_parts= curr_part_no - new_part_no;
}
}
- if (!(flags= table->file->alter_table_flags(alter_info->flags)))
+ if (!(flags= new_table->file->alter_table_flags(alter_info->flags)))
{
my_error(ER_PARTITION_FUNCTION_FAILURE, MYF(0));
- DBUG_RETURN(1);
+ goto err;
}
- *fast_alter_partition=
- ((flags & (HA_FAST_CHANGE_PARTITION | HA_PARTITION_ONE_PHASE)) != 0);
- DBUG_PRINT("info", ("*fast_alter_partition: %d flags: 0x%x",
- *fast_alter_partition, flags));
- if (((alter_info->flags & ALTER_ADD_PARTITION) ||
- (alter_info->flags & ALTER_REORGANIZE_PARTITION)) &&
- (thd->work_part_info->part_type != tab_part_info->part_type) &&
- (thd->work_part_info->part_type != NOT_A_PARTITION))
+ if ((flags & (HA_FAST_CHANGE_PARTITION | HA_PARTITION_ONE_PHASE)) != 0)
+ *fast_alter_table= new_table;
+ DBUG_PRINT("info", ("*fast_alter_table: %p flags: 0x%x",
+ *fast_alter_table, flags));
+ if ((alter_info->flags & ALTER_ADD_PARTITION) ||
+ (alter_info->flags & ALTER_REORGANIZE_PARTITION))
{
- if (thd->work_part_info->part_type == RANGE_PARTITION)
- {
- my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0),
- "RANGE", "LESS THAN");
- }
- else if (thd->work_part_info->part_type == LIST_PARTITION)
+ if (thd->work_part_info->part_type != tab_part_info->part_type)
{
- DBUG_ASSERT(thd->work_part_info->part_type == LIST_PARTITION);
- my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0),
- "LIST", "IN");
+ if (thd->work_part_info->part_type == NOT_A_PARTITION)
+ {
+ if (tab_part_info->part_type == RANGE_PARTITION)
+ {
+ my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), "RANGE");
+ goto err;
+ }
+ else if (tab_part_info->part_type == LIST_PARTITION)
+ {
+ my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), "LIST");
+ goto err;
+ }
+ /*
+ Hash partitions can be altered without parser finds out about
+ that it is HASH partitioned. So no error here.
+ */
+ }
+ else
+ {
+ if (thd->work_part_info->part_type == RANGE_PARTITION)
+ {
+ my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0),
+ "RANGE", "LESS THAN");
+ }
+ else if (thd->work_part_info->part_type == LIST_PARTITION)
+ {
+ DBUG_ASSERT(thd->work_part_info->part_type == LIST_PARTITION);
+ my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0),
+ "LIST", "IN");
+ }
+ else if (tab_part_info->part_type == RANGE_PARTITION)
+ {
+ my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0),
+ "RANGE", "LESS THAN");
+ }
+ else
+ {
+ DBUG_ASSERT(tab_part_info->part_type == LIST_PARTITION);
+ my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0),
+ "LIST", "IN");
+ }
+ goto err;
+ }
}
- else if (tab_part_info->part_type == RANGE_PARTITION)
+ if ((tab_part_info->column_list &&
+ alt_part_info->num_columns != tab_part_info->num_columns) ||
+ (!tab_part_info->column_list &&
+ (tab_part_info->part_type == RANGE_PARTITION ||
+ tab_part_info->part_type == LIST_PARTITION) &&
+ alt_part_info->num_columns != 1U) ||
+ (!tab_part_info->column_list &&
+ tab_part_info->part_type == HASH_PARTITION &&
+ alt_part_info->num_columns != 0))
{
- my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0),
- "RANGE", "LESS THAN");
+ my_error(ER_PARTITION_COLUMN_LIST_ERROR, MYF(0));
+ goto err;
}
- else
+ alt_part_info->column_list= tab_part_info->column_list;
+ if (alt_part_info->fix_parser_data(thd))
{
- DBUG_ASSERT(tab_part_info->part_type == LIST_PARTITION);
- my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0),
- "LIST", "IN");
+ goto err;
}
- DBUG_RETURN(TRUE);
}
if (alter_info->flags & ALTER_ADD_PARTITION)
{
@@ -4432,9 +4817,9 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
partitioning scheme as currently set-up.
Partitions are always added at the end in ADD PARTITION.
*/
- uint no_new_partitions= alt_part_info->no_parts;
- uint no_orig_partitions= tab_part_info->no_parts;
- uint check_total_partitions= no_new_partitions + no_orig_partitions;
+ uint num_new_partitions= alt_part_info->num_parts;
+ uint num_orig_partitions= tab_part_info->num_parts;
+ uint check_total_partitions= num_new_partitions + num_orig_partitions;
uint new_total_partitions= check_total_partitions;
/*
We allow quite a lot of values to be supplied by defaults, however we
@@ -4444,42 +4829,42 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
tab_part_info->part_type != HASH_PARTITION)
{
my_error(ER_NO_BINLOG_ERROR, MYF(0));
- DBUG_RETURN(TRUE);
+ goto err;
}
if (tab_part_info->defined_max_value)
{
my_error(ER_PARTITION_MAXVALUE_ERROR, MYF(0));
- DBUG_RETURN(TRUE);
+ goto err;
}
- if (no_new_partitions == 0)
+ if (num_new_partitions == 0)
{
my_error(ER_ADD_PARTITION_NO_NEW_PARTITION, MYF(0));
- DBUG_RETURN(TRUE);
+ goto err;
}
if (tab_part_info->is_sub_partitioned())
{
- if (alt_part_info->no_subparts == 0)
- alt_part_info->no_subparts= tab_part_info->no_subparts;
- else if (alt_part_info->no_subparts != tab_part_info->no_subparts)
+ if (alt_part_info->num_subparts == 0)
+ alt_part_info->num_subparts= tab_part_info->num_subparts;
+ else if (alt_part_info->num_subparts != tab_part_info->num_subparts)
{
my_error(ER_ADD_PARTITION_SUBPART_ERROR, MYF(0));
- DBUG_RETURN(TRUE);
+ goto err;
}
check_total_partitions= new_total_partitions*
- alt_part_info->no_subparts;
+ alt_part_info->num_subparts;
}
if (check_total_partitions > MAX_PARTITIONS)
{
my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0));
- DBUG_RETURN(TRUE);
+ goto err;
}
alt_part_info->part_type= tab_part_info->part_type;
alt_part_info->subpart_type= tab_part_info->subpart_type;
- if (alt_part_info->set_up_defaults_for_partitioning(table->file,
- ULL(0),
- tab_part_info->no_parts))
+ if (alt_part_info->set_up_defaults_for_partitioning(new_table->file,
+ ULL(0),
+ tab_part_info->num_parts))
{
- DBUG_RETURN(TRUE);
+ goto err;
}
/*
Handling of on-line cases:
@@ -4543,7 +4928,7 @@ 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_partition &&
+ if (*fast_alter_table &&
tab_part_info->part_type == HASH_PARTITION)
{
uint part_no= 0, start_part= 1, start_sec_part= 1;
@@ -4552,7 +4937,7 @@ that are reorganised.
uint lower_2n= upper_2n >> 1;
bool all_parts= TRUE;
if (tab_part_info->linear_hash_ind &&
- no_new_partitions < upper_2n)
+ num_new_partitions < upper_2n)
{
/*
An analysis of which parts needs reorganisation shows that it is
@@ -4561,7 +4946,7 @@ that are reorganised.
onwards it starts again from partition 0 and goes on until
it reaches p(upper_2n - 1). If the last new partition reaches
beyond upper_2n - 1 then the first interval will end with
- p(lower_2n - 1) and start with p(no_orig_partitions - lower_2n).
+ p(lower_2n - 1) and start with p(num_orig_partitions - lower_2n).
If lower_2n partitions are added then p0 to p(lower_2n - 1) will
be reorganised which means that the two interval becomes one
interval at this point. Thus only when adding less than
@@ -4589,7 +4974,7 @@ that are reorganised.
to TRUE. In this case we don't get into this if-part at all.
*/
all_parts= FALSE;
- if (no_new_partitions >= lower_2n)
+ if (num_new_partitions >= lower_2n)
{
/*
In this case there is only one interval since the two intervals
@@ -4605,8 +4990,8 @@ that are reorganised.
Also in this case there is only one interval since we are not
going over a 2**n boundary
*/
- start_part= no_orig_partitions - lower_2n;
- end_part= start_part + (no_new_partitions - 1);
+ start_part= num_orig_partitions - lower_2n;
+ end_part= start_part + (num_new_partitions - 1);
}
else
{
@@ -4615,7 +5000,7 @@ that are reorganised.
new parts that would ensure that the intervals become
overlapping.
*/
- start_part= no_orig_partitions - lower_2n;
+ start_part= num_orig_partitions - lower_2n;
end_part= upper_2n - 1;
start_sec_part= 0;
end_sec_part= new_total_partitions - (upper_2n + 1);
@@ -4632,7 +5017,7 @@ that are reorganised.
{
p_elem->part_state= PART_CHANGED;
}
- } while (++part_no < no_orig_partitions);
+ } while (++part_no < num_orig_partitions);
}
/*
Need to concatenate the lists here to make it possible to check the
@@ -4648,15 +5033,15 @@ that are reorganised.
do
{
partition_element *part_elem= alt_it++;
- if (*fast_alter_partition)
+ if (*fast_alter_table)
part_elem->part_state= PART_TO_BE_ADDED;
if (tab_part_info->partitions.push_back(part_elem))
{
mem_alloc_error(1);
- DBUG_RETURN(TRUE);
+ goto err;
}
- } while (++part_count < no_new_partitions);
- tab_part_info->no_parts+= no_new_partitions;
+ } while (++part_count < num_new_partitions);
+ tab_part_info->num_parts+= num_new_partitions;
}
/*
If we specify partitions explicitly we don't use defaults anymore.
@@ -4671,7 +5056,7 @@ that are reorganised.
DBUG_PRINT("info", ("part_info: 0x%lx", (long) tab_part_info));
tab_part_info->use_default_partitions= FALSE;
}
- tab_part_info->use_default_no_partitions= FALSE;
+ tab_part_info->use_default_num_partitions= FALSE;
tab_part_info->is_auto_partitioned= FALSE;
}
}
@@ -4685,8 +5070,8 @@ that are reorganised.
command to drop the partition failed in the middle.
*/
uint part_count= 0;
- uint no_parts_dropped= alter_info->partition_names.elements;
- uint no_parts_found= 0;
+ uint num_parts_dropped= alter_info->partition_names.elements;
+ uint num_parts_found= 0;
List_iterator<partition_element> part_it(tab_part_info->partitions);
tab_part_info->is_auto_partitioned= FALSE;
@@ -4694,12 +5079,12 @@ that are reorganised.
tab_part_info->part_type == LIST_PARTITION))
{
my_error(ER_ONLY_ON_RANGE_LIST_PARTITION, MYF(0), "DROP");
- DBUG_RETURN(TRUE);
+ goto err;
}
- if (no_parts_dropped >= tab_part_info->no_parts)
+ if (num_parts_dropped >= tab_part_info->num_parts)
{
my_error(ER_DROP_LAST_PARTITION, MYF(0));
- DBUG_RETURN(TRUE);
+ goto err;
}
do
{
@@ -4710,60 +5095,56 @@ that are reorganised.
/*
Set state to indicate that the partition is to be dropped.
*/
- no_parts_found++;
+ num_parts_found++;
part_elem->part_state= PART_TO_BE_DROPPED;
}
- } while (++part_count < tab_part_info->no_parts);
- if (no_parts_found != no_parts_dropped)
+ } while (++part_count < tab_part_info->num_parts);
+ if (num_parts_found != num_parts_dropped)
{
my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "DROP");
- DBUG_RETURN(TRUE);
+ goto err;
}
- if (table->file->is_fk_defined_on_table_or_index(MAX_KEY))
+ if (new_table->file->is_fk_defined_on_table_or_index(MAX_KEY))
{
my_error(ER_ROW_IS_REFERENCED, MYF(0));
- DBUG_RETURN(TRUE);
+ goto err;
}
- tab_part_info->no_parts-= no_parts_dropped;
+ tab_part_info->num_parts-= num_parts_dropped;
}
else if (alter_info->flags & ALTER_REBUILD_PARTITION)
{
- uint no_parts_found;
- uint no_parts_opt= alter_info->partition_names.elements;
set_engine_all_partitions(tab_part_info,
tab_part_info->default_engine_type);
- no_parts_found= set_part_state(alter_info, tab_part_info, PART_CHANGED);
- if (no_parts_found != no_parts_opt &&
- (!(alter_info->flags & ALTER_ALL_PARTITION)))
+ if (set_part_state(alter_info, tab_part_info, PART_CHANGED))
{
my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "REBUILD");
- DBUG_RETURN(TRUE);
+ goto err;
}
- if (!(*fast_alter_partition))
+ if (!(*fast_alter_table))
{
- table->file->print_error(HA_ERR_WRONG_COMMAND, MYF(0));
- DBUG_RETURN(TRUE);
+ new_table->file->print_error(HA_ERR_WRONG_COMMAND, MYF(0));
+ goto err;
}
}
else if (alter_info->flags & ALTER_COALESCE_PARTITION)
{
- uint no_parts_coalesced= alter_info->no_parts;
- uint no_parts_remain= tab_part_info->no_parts - no_parts_coalesced;
+ uint num_parts_coalesced= alter_info->num_parts;
+ uint num_parts_remain= tab_part_info->num_parts - num_parts_coalesced;
List_iterator<partition_element> part_it(tab_part_info->partitions);
if (tab_part_info->part_type != HASH_PARTITION)
{
my_error(ER_COALESCE_ONLY_ON_HASH_PARTITION, MYF(0));
- DBUG_RETURN(TRUE);
+ goto err;
}
- if (no_parts_coalesced == 0)
+ if (num_parts_coalesced == 0)
{
my_error(ER_COALESCE_PARTITION_NO_PARTITION, MYF(0));
- DBUG_RETURN(TRUE);
+ goto err;
}
- if (no_parts_coalesced >= tab_part_info->no_parts)
+ if (num_parts_coalesced >= tab_part_info->num_parts)
{
my_error(ER_DROP_LAST_PARTITION, MYF(0));
- DBUG_RETURN(TRUE);
+ goto err;
}
/*
Online handling:
@@ -4802,50 +5183,50 @@ state of p1.
uint part_count= 0, start_part= 1, start_sec_part= 1;
uint end_part= 0, end_sec_part= 0;
bool all_parts= TRUE;
- if (*fast_alter_partition &&
+ if (*fast_alter_table &&
tab_part_info->linear_hash_ind)
{
uint upper_2n= tab_part_info->linear_hash_mask + 1;
uint lower_2n= upper_2n >> 1;
all_parts= FALSE;
- if (no_parts_coalesced >= lower_2n)
+ if (num_parts_coalesced >= lower_2n)
{
all_parts= TRUE;
}
- else if (no_parts_remain >= lower_2n)
+ else if (num_parts_remain >= lower_2n)
{
- end_part= tab_part_info->no_parts - (lower_2n + 1);
- start_part= no_parts_remain - lower_2n;
+ end_part= tab_part_info->num_parts - (lower_2n + 1);
+ start_part= num_parts_remain - lower_2n;
}
else
{
start_part= 0;
- end_part= tab_part_info->no_parts - (lower_2n + 1);
+ end_part= tab_part_info->num_parts - (lower_2n + 1);
end_sec_part= (lower_2n >> 1) - 1;
- start_sec_part= end_sec_part - (lower_2n - (no_parts_remain + 1));
+ start_sec_part= end_sec_part - (lower_2n - (num_parts_remain + 1));
}
}
do
{
partition_element *p_elem= part_it++;
- if (*fast_alter_partition &&
+ if (*fast_alter_table &&
(all_parts ||
(part_count >= start_part && part_count <= end_part) ||
(part_count >= start_sec_part && part_count <= end_sec_part)))
p_elem->part_state= PART_CHANGED;
- if (++part_count > no_parts_remain)
+ if (++part_count > num_parts_remain)
{
- if (*fast_alter_partition)
+ if (*fast_alter_table)
p_elem->part_state= PART_REORGED_DROPPED;
else
part_it.remove();
}
- } while (part_count < tab_part_info->no_parts);
- tab_part_info->no_parts= no_parts_remain;
+ } while (part_count < tab_part_info->num_parts);
+ tab_part_info->num_parts= num_parts_remain;
}
if (!(alter_info->flags & ALTER_TABLE_REORG))
{
- tab_part_info->use_default_no_partitions= FALSE;
+ tab_part_info->use_default_num_partitions= FALSE;
tab_part_info->is_auto_partitioned= FALSE;
}
}
@@ -4862,47 +5243,46 @@ state of p1.
range as those changed from.
This command can be used on RANGE and LIST partitions.
*/
- uint no_parts_reorged= alter_info->partition_names.elements;
- uint no_parts_new= thd->work_part_info->partitions.elements;
- partition_info *alt_part_info= thd->work_part_info;
+ uint num_parts_reorged= alter_info->partition_names.elements;
+ uint num_parts_new= thd->work_part_info->partitions.elements;
uint check_total_partitions;
tab_part_info->is_auto_partitioned= FALSE;
- if (no_parts_reorged > tab_part_info->no_parts)
+ if (num_parts_reorged > tab_part_info->num_parts)
{
my_error(ER_REORG_PARTITION_NOT_EXIST, MYF(0));
- DBUG_RETURN(TRUE);
+ goto err;
}
if (!(tab_part_info->part_type == RANGE_PARTITION ||
tab_part_info->part_type == LIST_PARTITION) &&
- (no_parts_new != no_parts_reorged))
+ (num_parts_new != num_parts_reorged))
{
my_error(ER_REORG_HASH_ONLY_ON_SAME_NO, MYF(0));
- DBUG_RETURN(TRUE);
+ goto err;
}
if (tab_part_info->is_sub_partitioned() &&
- alt_part_info->no_subparts &&
- alt_part_info->no_subparts != tab_part_info->no_subparts)
+ alt_part_info->num_subparts &&
+ alt_part_info->num_subparts != tab_part_info->num_subparts)
{
my_error(ER_PARTITION_WRONG_NO_SUBPART_ERROR, MYF(0));
- DBUG_RETURN(TRUE);
+ goto err;
}
- check_total_partitions= tab_part_info->no_parts + no_parts_new;
- check_total_partitions-= no_parts_reorged;
+ check_total_partitions= tab_part_info->num_parts + num_parts_new;
+ check_total_partitions-= num_parts_reorged;
if (check_total_partitions > MAX_PARTITIONS)
{
my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0));
- DBUG_RETURN(TRUE);
+ goto err;
}
alt_part_info->part_type= tab_part_info->part_type;
alt_part_info->subpart_type= tab_part_info->subpart_type;
- alt_part_info->no_subparts= tab_part_info->no_subparts;
+ alt_part_info->num_subparts= tab_part_info->num_subparts;
DBUG_ASSERT(!alt_part_info->use_default_partitions);
- if (alt_part_info->set_up_defaults_for_partitioning(table->file,
+ if (alt_part_info->set_up_defaults_for_partitioning(new_table->file,
ULL(0),
0))
{
- DBUG_RETURN(TRUE);
+ goto err;
}
/*
Online handling:
@@ -4944,9 +5324,7 @@ the generated partition syntax in a correct manner.
uint part_count= 0;
bool found_first= FALSE;
bool found_last= FALSE;
- bool is_last_partition_reorged;
uint drop_count= 0;
- longlong tab_max_range= 0, alt_max_range= 0;
do
{
partition_element *part_elem= tab_it++;
@@ -4956,37 +5334,51 @@ the generated partition syntax in a correct manner.
{
is_last_partition_reorged= TRUE;
drop_count++;
- tab_max_range= part_elem->range_value;
- if (*fast_alter_partition &&
+ if (tab_part_info->column_list)
+ {
+ List_iterator<part_elem_value> p(part_elem->list_val_list);
+ tab_max_elem_val= p++;
+ }
+ else
+ tab_max_range= part_elem->range_value;
+ if (*fast_alter_table &&
tab_part_info->temp_partitions.push_back(part_elem))
{
mem_alloc_error(1);
- DBUG_RETURN(TRUE);
+ goto err;
}
- if (*fast_alter_partition)
+ if (*fast_alter_table)
part_elem->part_state= PART_TO_BE_REORGED;
if (!found_first)
{
uint alt_part_count= 0;
- found_first= TRUE;
+ partition_element *alt_part_elem;
List_iterator<partition_element>
alt_it(alt_part_info->partitions);
+ found_first= TRUE;
do
{
- partition_element *alt_part_elem= alt_it++;
- alt_max_range= alt_part_elem->range_value;
- if (*fast_alter_partition)
+ alt_part_elem= alt_it++;
+ if (tab_part_info->column_list)
+ {
+ List_iterator<part_elem_value> p(alt_part_elem->list_val_list);
+ alt_max_elem_val= p++;
+ }
+ else
+ alt_max_range= alt_part_elem->range_value;
+
+ if (*fast_alter_table)
alt_part_elem->part_state= PART_TO_BE_ADDED;
if (alt_part_count == 0)
tab_it.replace(alt_part_elem);
else
tab_it.after(alt_part_elem);
- } while (++alt_part_count < no_parts_new);
+ } while (++alt_part_count < num_parts_new);
}
else if (found_last)
{
my_error(ER_CONSECUTIVE_REORG_PARTITIONS, MYF(0));
- DBUG_RETURN(TRUE);
+ goto err;
}
else
tab_it.remove();
@@ -4996,32 +5388,13 @@ the generated partition syntax in a correct manner.
if (found_first)
found_last= TRUE;
}
- } while (++part_count < tab_part_info->no_parts);
- if (drop_count != no_parts_reorged)
+ } while (++part_count < tab_part_info->num_parts);
+ if (drop_count != num_parts_reorged)
{
my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "REORGANIZE");
- DBUG_RETURN(TRUE);
+ goto err;
}
- if (tab_part_info->part_type == RANGE_PARTITION &&
- ((is_last_partition_reorged &&
- alt_max_range < tab_max_range) ||
- (!is_last_partition_reorged &&
- alt_max_range != tab_max_range)))
- {
- /*
- For range partitioning the total resulting range before and
- after the change must be the same except in one case. This is
- when the last partition is reorganised, in this case it is
- acceptable to increase the total range.
- The reason is that it is not allowed to have "holes" in the
- middle of the ranges and thus we should not allow to reorganise
- to create "holes". Also we should not allow using REORGANIZE
- to drop data.
- */
- my_error(ER_REORG_OUTSIDE_RANGE, MYF(0));
- DBUG_RETURN(TRUE);
- }
- tab_part_info->no_parts= check_total_partitions;
+ tab_part_info->num_parts= check_total_partitions;
}
}
else
@@ -5037,12 +5410,44 @@ the generated partition syntax in a correct manner.
!alt_part_info->use_default_subpartitions)
{
tab_part_info->use_default_subpartitions= FALSE;
- tab_part_info->use_default_no_subpartitions= FALSE;
+ tab_part_info->use_default_num_subpartitions= FALSE;
}
if (tab_part_info->check_partition_info(thd, (handlerton**)NULL,
- table->file, ULL(0), FALSE))
+ new_table->file, ULL(0), TRUE))
{
- DBUG_RETURN(TRUE);
+ goto err;
+ }
+ /*
+ The check below needs to be performed after check_partition_info
+ since this function "fixes" the item trees of the new partitions
+ to reorganize into
+ */
+ if (alter_info->flags == ALTER_REORGANIZE_PARTITION &&
+ tab_part_info->part_type == RANGE_PARTITION &&
+ ((is_last_partition_reorged &&
+ (tab_part_info->column_list ?
+ (tab_part_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(
+ alt_max_elem_val->col_val_array,
+ tab_max_elem_val->col_val_array) != 0) :
+ alt_max_range != tab_max_range))))
+ {
+ /*
+ For range partitioning the total resulting range before and
+ after the change must be the same except in one case. This is
+ when the last partition is reorganised, in this case it is
+ acceptable to increase the total range.
+ The reason is that it is not allowed to have "holes" in the
+ middle of the ranges and thus we should not allow to reorganise
+ to create "holes".
+ */
+ my_error(ER_REORG_OUTSIDE_RANGE, MYF(0));
+ goto err;
}
}
}
@@ -5096,7 +5501,9 @@ the generated partition syntax in a correct manner.
There was no partitioning before and no partitioning defined.
Obviously no work needed.
*/
- if (table->part_info)
+ partition_info *tab_part_info= table->part_info;
+
+ if (tab_part_info)
{
if (alter_info->flags & ALTER_REMOVE_PARTITIONING)
{
@@ -5104,7 +5511,7 @@ the generated partition syntax in a correct manner.
if (!(create_info->used_fields & HA_CREATE_USED_ENGINE))
{
DBUG_PRINT("info", ("No explicit engine used"));
- create_info->db_type= table->part_info->default_engine_type;
+ create_info->db_type= tab_part_info->default_engine_type;
}
DBUG_PRINT("info", ("New engine type: %s",
ha_resolve_storage_engine_name(create_info->db_type)));
@@ -5116,16 +5523,20 @@ the generated partition syntax in a correct manner.
/*
Retain partitioning but possibly with a new storage engine
beneath.
+
+ Create a copy of TABLE::part_info to be able to modify it freely.
*/
- thd->work_part_info= table->part_info;
+ if (!(tab_part_info= tab_part_info->get_clone()))
+ DBUG_RETURN(TRUE);
+ thd->work_part_info= tab_part_info;
if (create_info->used_fields & HA_CREATE_USED_ENGINE &&
- create_info->db_type != table->part_info->default_engine_type)
+ create_info->db_type != tab_part_info->default_engine_type)
{
/*
Make sure change of engine happens to all partitions.
*/
DBUG_PRINT("info", ("partition changed"));
- if (table->part_info->is_auto_partitioned)
+ if (tab_part_info->is_auto_partitioned)
{
/*
If the user originally didn't specify partitioning to be
@@ -5153,10 +5564,25 @@ the generated partition syntax in a correct manner.
Need to cater for engine types that can handle partition without
using the partition handler.
*/
- if (thd->work_part_info != table->part_info)
+ if (part_info != tab_part_info)
{
- DBUG_PRINT("info", ("partition changed"));
- *partition_changed= TRUE;
+ if (part_info->fix_parser_data(thd))
+ {
+ goto err;
+ }
+ /*
+ Compare the old and new part_info. If only key_algorithm
+ change is done, don't consider it as changed partitioning (to avoid
+ rebuild). This is to handle KEY (numeric_cols) partitioned tables
+ created in 5.1. For more info, see bug#14521864.
+ */
+ if (alter_info->flags != ALTER_PARTITION ||
+ !table->part_info ||
+ !table->part_info->has_same_partitioning(part_info))
+ {
+ DBUG_PRINT("info", ("partition changed"));
+ *partition_changed= true;
+ }
}
/*
Set up partition default_engine_type either from the create_info
@@ -5166,8 +5592,8 @@ the generated partition syntax in a correct manner.
part_info->default_engine_type= create_info->db_type;
else
{
- if (table->part_info)
- part_info->default_engine_type= table->part_info->default_engine_type;
+ if (tab_part_info)
+ part_info->default_engine_type= tab_part_info->default_engine_type;
else
part_info->default_engine_type= create_info->db_type;
}
@@ -5176,7 +5602,7 @@ the generated partition syntax in a correct manner.
if (check_native_partitioned(create_info, &is_native_partitioned,
part_info, thd))
{
- DBUG_RETURN(TRUE);
+ goto err;
}
if (!is_native_partitioned)
{
@@ -5186,6 +5612,17 @@ the generated partition syntax in a correct manner.
}
}
DBUG_RETURN(FALSE);
+err:
+ if (new_table)
+ {
+ /*
+ Only remove the intermediate table object and its share object,
+ do not remove the .frm file, since it is the original one.
+ */
+ close_temporary(new_table, 1, 0);
+ }
+ *fast_alter_table= NULL;
+ DBUG_RETURN(TRUE);
}
@@ -5220,20 +5657,35 @@ static bool mysql_change_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
char path[FN_REFLEN+1];
int error;
handler *file= lpt->table->file;
+ THD *thd= lpt->thd;
DBUG_ENTER("mysql_change_partitions");
build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0);
+
+ /* First lock the original tables */
+ if (file->ha_external_lock(thd, F_WRLCK))
+ DBUG_RETURN(TRUE);
+
+ /* Disable transactions for all new tables */
+ if (mysql_trans_prepare_alter_copy_data(thd))
+ 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 (error != ER_OUTOFMEMORY)
- file->print_error(error, MYF(0));
- else
- lpt->thd->fatal_error();
- DBUG_RETURN(TRUE);
+ file->print_error(error, MYF(error != ER_OUTOFMEMORY ? 0 : ME_FATALERROR));
}
- DBUG_RETURN(FALSE);
+
+ if (mysql_trans_commit_alter_copy_data(thd))
+ error= 1; /* The error has been reported */
+
+ if (file->ha_external_lock(thd, F_UNLCK))
+ error= 1;
+
+ DBUG_RETURN(test(error));
}
@@ -5317,8 +5769,8 @@ static bool mysql_drop_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
part_it.remove();
remove_count++;
}
- } while (++i < part_info->no_parts);
- part_info->no_parts-= remove_count;
+ } while (++i < part_info->num_parts);
+ part_info->num_parts-= remove_count;
DBUG_RETURN(FALSE);
}
@@ -5440,7 +5892,7 @@ static bool write_log_changed_partitions(ALTER_PARTITION_PARAM_TYPE *lpt,
char normal_path[FN_REFLEN];
List_iterator<partition_element> part_it(part_info->partitions);
uint temp_partitions= part_info->temp_partitions.elements;
- uint no_elements= part_info->partitions.elements;
+ uint num_elements= part_info->partitions.elements;
uint i= 0;
DBUG_ENTER("write_log_changed_partitions");
@@ -5453,7 +5905,7 @@ static bool write_log_changed_partitions(ALTER_PARTITION_PARAM_TYPE *lpt,
if (part_info->is_sub_partitioned())
{
List_iterator<partition_element> sub_it(part_elem->subpartitions);
- uint no_subparts= part_info->no_subparts;
+ uint num_subparts= part_info->num_subparts;
uint j= 0;
do
{
@@ -5482,7 +5934,7 @@ static bool write_log_changed_partitions(ALTER_PARTITION_PARAM_TYPE *lpt,
*next_entry= log_entry->entry_pos;
sub_elem->log_entry= log_entry;
insert_part_info_log_entry_list(part_info, log_entry);
- } while (++j < no_subparts);
+ } while (++j < num_subparts);
}
else
{
@@ -5510,7 +5962,7 @@ static bool write_log_changed_partitions(ALTER_PARTITION_PARAM_TYPE *lpt,
insert_part_info_log_entry_list(part_info, log_entry);
}
}
- } while (++i < no_elements);
+ } while (++i < num_elements);
DBUG_RETURN(FALSE);
}
@@ -5536,14 +5988,14 @@ static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt,
char tmp_path[FN_LEN];
List_iterator<partition_element> part_it(part_info->partitions);
List_iterator<partition_element> temp_it(part_info->temp_partitions);
- uint no_temp_partitions= part_info->temp_partitions.elements;
- uint no_elements= part_info->partitions.elements;
+ uint num_temp_partitions= part_info->temp_partitions.elements;
+ uint num_elements= part_info->partitions.elements;
DBUG_ENTER("write_log_dropped_partitions");
ddl_log_entry.action_type= DDL_LOG_DELETE_ACTION;
if (temp_list)
- no_elements= no_temp_partitions;
- while (no_elements--)
+ num_elements= num_temp_partitions;
+ while (num_elements--)
{
partition_element *part_elem;
if (temp_list)
@@ -5557,14 +6009,14 @@ static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt,
uint name_variant;
if (part_elem->part_state == PART_CHANGED ||
(part_elem->part_state == PART_TO_BE_ADDED &&
- no_temp_partitions))
+ num_temp_partitions))
name_variant= TEMP_PART_NAME;
else
name_variant= NORMAL_PART_NAME;
if (part_info->is_sub_partitioned())
{
List_iterator<partition_element> sub_it(part_elem->subpartitions);
- uint no_subparts= part_info->no_subparts;
+ uint num_subparts= part_info->num_subparts;
uint j= 0;
do
{
@@ -5584,7 +6036,7 @@ static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt,
*next_entry= log_entry->entry_pos;
sub_elem->log_entry= log_entry;
insert_part_info_log_entry_list(part_info, log_entry);
- } while (++j < no_subparts);
+ } while (++j < num_subparts);
}
else
{
@@ -5652,7 +6104,7 @@ static bool write_log_drop_shadow_frm(ALTER_PARTITION_PARAM_TYPE *lpt)
DBUG_ENTER("write_log_drop_shadow_frm");
build_table_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt);
- pthread_mutex_lock(&LOCK_gdl);
+ mysql_mutex_lock(&LOCK_gdl);
if (write_log_replace_delete_frm(lpt, 0UL, NULL,
(const char*)shadow_path, FALSE))
goto error;
@@ -5660,13 +6112,13 @@ static bool write_log_drop_shadow_frm(ALTER_PARTITION_PARAM_TYPE *lpt)
if (write_execute_ddl_log_entry(log_entry->entry_pos,
FALSE, &exec_log_entry))
goto error;
- pthread_mutex_unlock(&LOCK_gdl);
+ mysql_mutex_unlock(&LOCK_gdl);
set_part_info_exec_log_entry(part_info, exec_log_entry);
DBUG_RETURN(FALSE);
error:
release_part_info_log_entries(part_info->first_log_entry);
- pthread_mutex_unlock(&LOCK_gdl);
+ mysql_mutex_unlock(&LOCK_gdl);
part_info->first_log_entry= NULL;
my_error(ER_DDL_LOG_ERROR, MYF(0));
DBUG_RETURN(TRUE);
@@ -5700,7 +6152,7 @@ static bool write_log_rename_frm(ALTER_PARTITION_PARAM_TYPE *lpt)
build_table_filename(path, sizeof(path) - 1, lpt->db,
lpt->table_name, "", 0);
build_table_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt);
- pthread_mutex_lock(&LOCK_gdl);
+ mysql_mutex_lock(&LOCK_gdl);
if (write_log_replace_delete_frm(lpt, 0UL, shadow_path, path, TRUE))
goto error;
log_entry= part_info->first_log_entry;
@@ -5709,12 +6161,12 @@ static bool write_log_rename_frm(ALTER_PARTITION_PARAM_TYPE *lpt)
FALSE, &exec_log_entry))
goto error;
release_part_info_log_entries(old_first_log_entry);
- pthread_mutex_unlock(&LOCK_gdl);
+ mysql_mutex_unlock(&LOCK_gdl);
DBUG_RETURN(FALSE);
error:
release_part_info_log_entries(part_info->first_log_entry);
- pthread_mutex_unlock(&LOCK_gdl);
+ mysql_mutex_unlock(&LOCK_gdl);
part_info->first_log_entry= old_first_log_entry;
part_info->frm_log_entry= NULL;
my_error(ER_DDL_LOG_ERROR, MYF(0));
@@ -5752,7 +6204,7 @@ static bool write_log_drop_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
build_table_filename(path, sizeof(path) - 1, lpt->db,
lpt->table_name, "", 0);
build_table_shadow_filename(tmp_path, sizeof(tmp_path) - 1, lpt);
- pthread_mutex_lock(&LOCK_gdl);
+ mysql_mutex_lock(&LOCK_gdl);
if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path,
FALSE))
goto error;
@@ -5765,12 +6217,12 @@ static bool write_log_drop_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
FALSE, &exec_log_entry))
goto error;
release_part_info_log_entries(old_first_log_entry);
- pthread_mutex_unlock(&LOCK_gdl);
+ mysql_mutex_unlock(&LOCK_gdl);
DBUG_RETURN(FALSE);
error:
release_part_info_log_entries(part_info->first_log_entry);
- pthread_mutex_unlock(&LOCK_gdl);
+ mysql_mutex_unlock(&LOCK_gdl);
part_info->first_log_entry= old_first_log_entry;
part_info->frm_log_entry= NULL;
my_error(ER_DDL_LOG_ERROR, MYF(0));
@@ -5799,34 +6251,41 @@ static bool write_log_add_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
{
partition_info *part_info= lpt->part_info;
DDL_LOG_MEMORY_ENTRY *log_entry;
- DDL_LOG_MEMORY_ENTRY *exec_log_entry= NULL;
+ DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->exec_log_entry;
char tmp_path[FN_REFLEN + 1];
char path[FN_REFLEN + 1];
uint next_entry= 0;
+ DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->first_log_entry;
+ /* write_log_drop_shadow_frm(lpt) must have been run first */
+ 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_shadow_filename(tmp_path, sizeof(tmp_path) - 1, lpt);
- pthread_mutex_lock(&LOCK_gdl);
+ mysql_mutex_lock(&LOCK_gdl);
+
+ /* Relink the previous drop shadow frm entry */
+ if (old_first_log_entry)
+ next_entry= old_first_log_entry->entry_pos;
if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path,
FALSE))
goto error;
- if (write_log_replace_delete_frm(lpt, next_entry, NULL, tmp_path,
- FALSE))
- goto error;
log_entry= part_info->first_log_entry;
+
if (write_execute_ddl_log_entry(log_entry->entry_pos,
- FALSE, &exec_log_entry))
+ FALSE,
+ /* Reuse the old execute ddl_log_entry */
+ &exec_log_entry))
goto error;
- pthread_mutex_unlock(&LOCK_gdl);
+ mysql_mutex_unlock(&LOCK_gdl);
set_part_info_exec_log_entry(part_info, exec_log_entry);
DBUG_RETURN(FALSE);
error:
release_part_info_log_entries(part_info->first_log_entry);
- pthread_mutex_unlock(&LOCK_gdl);
- part_info->first_log_entry= NULL;
+ mysql_mutex_unlock(&LOCK_gdl);
+ part_info->first_log_entry= old_first_log_entry;
my_error(ER_DDL_LOG_ERROR, MYF(0));
DBUG_RETURN(TRUE);
}
@@ -5843,9 +6302,15 @@ error:
TRUE Error
FALSE Success
DESCRIPTION
- We will write log entries that specify to remove all partitions reorganised,
- to rename others to reflect the new naming scheme and to install the shadow
- frm file.
+ We will write log entries that specify to
+ 1) Install the shadow frm file.
+ 2) Remove all partitions reorganized. (To be able to reorganize a partition
+ to the same name. Like in REORGANIZE p0 INTO (p0, p1),
+ so that the later rename from the new p0-temporary name to p0 don't
+ fail because the partition already exists.
+ 3) Rename others to reflect the new naming scheme.
+
+ Note that it is written in the ddl log in reverse.
*/
static bool write_log_final_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
@@ -5859,30 +6324,35 @@ static bool write_log_final_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
uint next_entry= 0;
DBUG_ENTER("write_log_final_change_partition");
+ /*
+ Do not link any previous log entry.
+ 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_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt);
- pthread_mutex_lock(&LOCK_gdl);
+ 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_REORGANIZE_PARTITION))
goto error;
- if (write_log_changed_partitions(lpt, &next_entry, (const char*)path))
- goto error;
- if (write_log_replace_delete_frm(lpt, 0UL, shadow_path, path, TRUE))
+ if (write_log_replace_delete_frm(lpt, next_entry, shadow_path, path, TRUE))
goto error;
log_entry= part_info->first_log_entry;
part_info->frm_log_entry= log_entry;
+ /* Overwrite the revert execute log entry with this retry execute entry */
if (write_execute_ddl_log_entry(log_entry->entry_pos,
FALSE, &exec_log_entry))
goto error;
release_part_info_log_entries(old_first_log_entry);
- pthread_mutex_unlock(&LOCK_gdl);
+ mysql_mutex_unlock(&LOCK_gdl);
DBUG_RETURN(FALSE);
error:
release_part_info_log_entries(part_info->first_log_entry);
- pthread_mutex_unlock(&LOCK_gdl);
+ mysql_mutex_unlock(&LOCK_gdl);
part_info->first_log_entry= old_first_log_entry;
part_info->frm_log_entry= NULL;
my_error(ER_DDL_LOG_ERROR, MYF(0));
@@ -5909,7 +6379,7 @@ static void write_log_completed(ALTER_PARTITION_PARAM_TYPE *lpt,
DBUG_ENTER("write_log_completed");
DBUG_ASSERT(log_entry);
- pthread_mutex_lock(&LOCK_gdl);
+ mysql_mutex_lock(&LOCK_gdl);
if (write_execute_ddl_log_entry(0UL, TRUE, &log_entry))
{
/*
@@ -5923,7 +6393,7 @@ static void write_log_completed(ALTER_PARTITION_PARAM_TYPE *lpt,
}
release_part_info_log_entries(part_info->first_log_entry);
release_part_info_log_entries(part_info->exec_log_entry);
- pthread_mutex_unlock(&LOCK_gdl);
+ mysql_mutex_unlock(&LOCK_gdl);
part_info->exec_log_entry= NULL;
part_info->first_log_entry= NULL;
DBUG_VOID_RETURN;
@@ -5941,10 +6411,10 @@ static void write_log_completed(ALTER_PARTITION_PARAM_TYPE *lpt,
static void release_log_entries(partition_info *part_info)
{
- pthread_mutex_lock(&LOCK_gdl);
+ mysql_mutex_lock(&LOCK_gdl);
release_part_info_log_entries(part_info->first_log_entry);
release_part_info_log_entries(part_info->exec_log_entry);
- pthread_mutex_unlock(&LOCK_gdl);
+ mysql_mutex_unlock(&LOCK_gdl);
part_info->first_log_entry= NULL;
part_info->exec_log_entry= NULL;
}
@@ -5961,59 +6431,86 @@ static void release_log_entries(partition_info *part_info)
*/
static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt)
{
- int err;
- if (lpt->thd->locked_tables)
+ THD *thd= lpt->thd;
+
+ if (lpt->old_table)
+ close_all_tables_for_name(thd, lpt->old_table->s, HA_EXTRA_NOT_USED);
+ if (lpt->table)
{
/*
- Close the table if open, to remove/destroy the already altered
- table->part_info object, so that it is not reused.
+ Only remove the intermediate table object and its share object,
+ do not remove the .frm file, since it is the original one.
*/
- if (lpt->table->db_stat)
- abort_and_upgrade_lock_and_close_table(lpt);
- /*
- When we have the table locked, it is necessary to reopen the table
- since all table objects were closed and removed as part of the
- ALTER TABLE of partitioning structure.
- */
- pthread_mutex_lock(&LOCK_open);
- lpt->thd->in_lock_tables= 1;
- err= reopen_tables(lpt->thd, 1, 1);
- lpt->thd->in_lock_tables= 0;
- if (err)
- {
- /*
- Issue a warning since we weren't able to regain the lock again.
- We also need to unlink table from thread's open list and from
- table_cache
- */
- unlink_open_table(lpt->thd, lpt->table, FALSE);
- sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE");
- }
- pthread_mutex_unlock(&LOCK_open);
+ close_temporary(lpt->table, 1, 0);
}
+ lpt->table= 0;
+ lpt->old_table= 0;
+ lpt->table_list->table= 0;
+ if (thd->locked_tables_list.reopen_tables(thd))
+ sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE");
}
/*
- Handle errors for ALTER TABLE for partitioning
+ Unlock and close table before renaming and dropping partitions
SYNOPSIS
- handle_alter_part_error()
+ alter_close_tables()
lpt Struct carrying parameters
- not_completed Was request in complete phase when error occurred
+ close_old Close original table too
RETURN VALUES
- NONE
+ 0
+*/
+
+static int alter_close_tables(ALTER_PARTITION_PARAM_TYPE *lpt, bool close_old)
+{
+ DBUG_ENTER("alter_close_tables");
+ if (lpt->table->db_stat)
+ {
+ lpt->table->file->ha_close();
+ lpt->table->db_stat= 0; // Mark file closed
+ }
+ if (close_old && lpt->old_table)
+ {
+ close_all_tables_for_name(lpt->thd, lpt->old_table->s, HA_EXTRA_NOT_USED);
+ lpt->old_table= 0;
+ }
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Handle errors for ALTER TABLE for partitioning.
+
+ @param lpt Struct carrying parameters
+ @param action_completed The action must be completed, NOT reverted
+ @param drop_partition Partitions has not been dropped yet
+ @param frm_install The shadow frm-file has not yet been installed
+ @param close_table Table is still open, close it before reverting
*/
void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
- bool not_completed,
+ bool action_completed,
bool drop_partition,
- bool frm_install)
+ bool frm_install,
+ bool close_table)
{
partition_info *part_info= lpt->part_info;
DBUG_ENTER("handle_alter_part_error");
+ if (close_table)
+ {
+ /*
+ Since the error handling (ddl_log) needs to drop newly created
+ partitions they must be closed first to not issue errors.
+ But we still need some information from the part_info object,
+ so we clone it first to have a copy.
+ */
+ part_info= lpt->part_info->get_clone();
+ alter_close_tables(lpt, action_completed);
+ }
+
if (part_info->first_log_entry &&
- execute_ddl_log_entry(current_thd,
+ execute_ddl_log_entry(lpt->thd,
part_info->first_log_entry->entry_pos))
{
/*
@@ -6022,7 +6519,7 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
*/
write_log_completed(lpt, FALSE);
release_log_entries(part_info);
- if (not_completed)
+ if (!action_completed)
{
if (drop_partition)
{
@@ -6087,7 +6584,7 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
else
{
release_log_entries(part_info);
- if (not_completed)
+ if (!action_completed)
{
/*
We hit an error before things were completed but managed
@@ -6114,25 +6611,40 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
}
-/*
+/**
+ Downgrade an exclusive MDL lock if under LOCK TABLE.
+
+ If we don't downgrade the lock, it will not be downgraded or released
+ until the table is unlocked, resulting in blocking other threads using
+ the table.
+*/
+
+static void downgrade_mdl_if_lock_tables_mode(THD *thd, MDL_ticket *ticket,
+ enum_mdl_type type)
+{
+ if (thd->locked_tables_mode)
+ ticket->downgrade_exclusive_lock(type);
+}
+
+
+/**
Actually perform the change requested by ALTER TABLE of partitions
previously prepared.
- SYNOPSIS
- fast_alter_partition_table()
- thd Thread object
- table Table object
- alter_info ALTER TABLE info
- create_info Create info for CREATE TABLE
- table_list List of the table involved
- db Database name of new table
- table_name Table name of new table
+ @param thd Thread object
+ @param table Original table object
+ @param alter_info ALTER TABLE info
+ @param create_info Create info for CREATE TABLE
+ @param table_list List of the table involved
+ @param db Database name of new table
+ @param table_name Table name of new table
+ @param fast_alter_table Prepared table object
- RETURN VALUES
- TRUE Error
- FALSE Success
+ @return Operation status
+ @retval TRUE Error
+ @retval FALSE Success
- DESCRIPTION
+ @note
Perform all ALTER TABLE operations for partitioned tables that can be
performed fast without a full copy of the original table.
*/
@@ -6143,25 +6655,30 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
TABLE_LIST *table_list,
char *db,
const char *table_name,
- uint fast_alter_partition)
+ TABLE *fast_alter_table)
{
/* Set-up struct used to write frm files */
- partition_info *part_info= table->part_info;
+ partition_info *part_info;
ALTER_PARTITION_PARAM_TYPE lpt_obj;
ALTER_PARTITION_PARAM_TYPE *lpt= &lpt_obj;
- bool written_bin_log= TRUE;
- bool not_completed= TRUE;
+ bool action_completed= FALSE;
+ bool close_table_on_failure= FALSE;
bool frm_install= FALSE;
+ MDL_ticket *mdl_ticket= table->mdl_ticket;
+ DBUG_ASSERT(fast_alter_table);
DBUG_ENTER("fast_alter_partition_table");
+ part_info= fast_alter_table->part_info;
lpt->thd= thd;
+ lpt->table_list= table_list;
lpt->part_info= part_info;
lpt->alter_info= alter_info;
lpt->create_info= create_info;
lpt->db_options= create_info->table_options;
if (create_info->row_type == ROW_TYPE_DYNAMIC)
lpt->db_options|= HA_OPTION_PACK_RECORD;
- lpt->table= table;
+ lpt->table= fast_alter_table;
+ lpt->old_table= table;
lpt->key_info_buffer= 0;
lpt->key_count= 0;
lpt->db= db;
@@ -6170,12 +6687,12 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
lpt->deleted= 0;
lpt->pack_frm_data= NULL;
lpt->pack_frm_len= 0;
- thd->work_part_info= part_info;
/* Never update timestamp columns when alter */
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+ lpt->table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
- if (fast_alter_partition & HA_PARTITION_ONE_PHASE)
+ if (table->file->alter_table_flags(alter_info->flags) &
+ HA_PARTITION_ONE_PHASE)
{
/*
In the case where the engine supports one phase online partition
@@ -6249,20 +6766,18 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
0) Write an entry that removes the shadow frm file if crash occurs
1) Write the new frm file as a shadow frm
- 2) Write the ddl log to ensure that the operation is completed
- even in the presence of a MySQL Server crash
- 3) Lock the table in TL_WRITE_ONLY to ensure all other accesses to
- the table have completed. This ensures that other threads can not
- execute on the table in parallel.
- 4) Get a name lock on the table. This ensures that we can release all
- locks on the table and since no one can open the table, there can
- be no new threads accessing the table. They will be hanging on the
- name lock.
- 5) Close all tables that have already been opened but didn't stumble on
+ 2) Get an exclusive metadata lock on the table (waits for all active
+ transactions using this table). This ensures that we
+ can release all other locks on the table and since no one can open
+ the table, there can be no new threads accessing the table. They
+ will be hanging on this exclusive lock.
+ 3) Write the ddl log to ensure that the operation is completed
+ even in the presence of a MySQL Server crash (the log is executed
+ before any other threads are started, so there are no locking issues).
+ 4) Close all tables that have already been opened but didn't stumble on
the abort locked previously. This is done as part of the
- close_data_files_and_morph_locks call.
- 6) We are now ready to release all locks we got in this thread.
- 7) Write the bin log
+ alter_close_tables call.
+ 5) Write the bin log
Unfortunately the writing of the binlog is not synchronised with
other logging activities. So no matter in which order the binlog
is written compared to other activities there will always be cases
@@ -6273,40 +6788,54 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
require writing the statement first in the ddl log and then
when recovering from the crash read the binlog and insert it into
the binlog if not written already.
- 8) Install the previously written shadow frm file
- 9) Prepare handlers for drop of partitions
- 10) Drop the partitions
- 11) Remove entries from ddl log
- 12) Reopen table if under lock tables
- 13) Complete query
+ 6) Install the previously written shadow frm file
+ 7) Prepare handlers for drop of partitions
+ 8) Drop the partitions
+ 9) Remove entries from ddl log
+ 10) Reopen table if under lock tables
+ 11) Complete query
We insert Error injections at all places where it could be interesting
to test if recovery is properly done.
*/
if (write_log_drop_shadow_frm(lpt) ||
ERROR_INJECT_CRASH("crash_drop_partition_1") ||
+ ERROR_INJECT_ERROR("fail_drop_partition_1") ||
mysql_write_frm(lpt, WFRM_WRITE_SHADOW) ||
ERROR_INJECT_CRASH("crash_drop_partition_2") ||
- write_log_drop_partition(lpt) ||
+ ERROR_INJECT_ERROR("fail_drop_partition_2") ||
+ wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) ||
ERROR_INJECT_CRASH("crash_drop_partition_3") ||
- (not_completed= FALSE) ||
- abort_and_upgrade_lock_and_close_table(lpt) ||
+ ERROR_INJECT_ERROR("fail_drop_partition_3") ||
+ (close_table_on_failure= TRUE, FALSE) ||
+ write_log_drop_partition(lpt) ||
+ (action_completed= TRUE, FALSE) ||
+ ERROR_INJECT_CRASH("crash_drop_partition_4") ||
+ ERROR_INJECT_ERROR("fail_drop_partition_4") ||
+ alter_close_tables(lpt, action_completed) ||
+ (close_table_on_failure= FALSE, FALSE) ||
ERROR_INJECT_CRASH("crash_drop_partition_5") ||
+ ERROR_INJECT_ERROR("fail_drop_partition_5") ||
((!thd->lex->no_write_to_binlog) &&
(write_bin_log(thd, FALSE,
thd->query(), thd->query_length()), FALSE)) ||
ERROR_INJECT_CRASH("crash_drop_partition_6") ||
- ((frm_install= TRUE), FALSE) ||
+ ERROR_INJECT_ERROR("fail_drop_partition_6") ||
+ (frm_install= TRUE, FALSE) ||
mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
- ((frm_install= FALSE), FALSE) ||
+ (frm_install= FALSE, FALSE) ||
ERROR_INJECT_CRASH("crash_drop_partition_7") ||
+ ERROR_INJECT_ERROR("fail_drop_partition_7") ||
mysql_drop_partitions(lpt) ||
ERROR_INJECT_CRASH("crash_drop_partition_8") ||
+ ERROR_INJECT_ERROR("fail_drop_partition_8") ||
(write_log_completed(lpt, FALSE), FALSE) ||
ERROR_INJECT_CRASH("crash_drop_partition_9") ||
+ ERROR_INJECT_ERROR("fail_drop_partition_9") ||
(alter_partition_lock_handling(lpt), FALSE))
{
- handle_alter_part_error(lpt, not_completed, TRUE, frm_install);
+ handle_alter_part_error(lpt, action_completed, TRUE, frm_install,
+ close_table_on_failure);
goto err;
}
}
@@ -6325,53 +6854,65 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
0) Write an entry that removes the shadow frm file if crash occurs
1) Write the new frm file as a shadow frm file
- 2) Log the changes to happen in ddl log
- 2) Add the new partitions
- 3) Lock all partitions in TL_WRITE_ONLY to ensure that no users
- are still using the old partitioning scheme. Wait until all
- ongoing users have completed before progressing.
- 4) Get a name lock on the table. This ensures that we can release all
- locks on the table and since no one can open the table, there can
- be no new threads accessing the table. They will be hanging on the
- name lock.
- 5) Close all tables that have already been opened but didn't stumble on
- the abort locked previously. This is done as part of the
- close_data_files_and_morph_locks call.
- 6) Close all table handlers and unlock all handlers but retain name lock
- 7) Write binlog
- 8) Now the change is completed except for the installation of the
+ 2) Get an exclusive metadata lock on the table (waits for all active
+ transactions using this table). This ensures that we
+ can release all other locks on the table and since no one can open
+ the table, there can be no new threads accessing the table. They
+ will be hanging on this exclusive lock.
+ 3) Write an entry to remove the new parttions if crash occurs
+ 4) Add the new partitions.
+ 5) Close all instances of the table and remove them from the table cache.
+ 6) Write binlog
+ 7) Now the change is completed except for the installation of the
new frm file. We thus write an action in the log to change to
the shadow frm file
- 9) Install the new frm file of the table where the partitions are
+ 8) Install the new frm file of the table where the partitions are
added to the table.
- 10)Wait until all accesses using the old frm file has completed
- 11)Remove entries from ddl log
- 12)Reopen tables if under lock tables
- 13)Complete query
+ 9) Remove entries from ddl log
+ 10)Reopen tables if under lock tables
+ 11)Complete query
*/
- if (write_log_add_change_partition(lpt) ||
+ if (write_log_drop_shadow_frm(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_1") ||
+ ERROR_INJECT_ERROR("fail_add_partition_1") ||
mysql_write_frm(lpt, WFRM_WRITE_SHADOW) ||
ERROR_INJECT_CRASH("crash_add_partition_2") ||
- mysql_change_partitions(lpt) ||
+ ERROR_INJECT_ERROR("fail_add_partition_2") ||
+ wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) ||
ERROR_INJECT_CRASH("crash_add_partition_3") ||
- abort_and_upgrade_lock_and_close_table(lpt) ||
+ ERROR_INJECT_ERROR("fail_add_partition_3") ||
+ (close_table_on_failure= TRUE, FALSE) ||
+ write_log_add_change_partition(lpt) ||
+ ERROR_INJECT_CRASH("crash_add_partition_4") ||
+ ERROR_INJECT_ERROR("fail_add_partition_4") ||
+ mysql_change_partitions(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_5") ||
+ ERROR_INJECT_ERROR("fail_add_partition_5") ||
+ (close_table_on_failure= FALSE, FALSE) ||
+ alter_close_tables(lpt, action_completed) ||
+ ERROR_INJECT_CRASH("crash_add_partition_6") ||
+ ERROR_INJECT_ERROR("fail_add_partition_6") ||
((!thd->lex->no_write_to_binlog) &&
(write_bin_log(thd, FALSE,
thd->query(), thd->query_length()), FALSE)) ||
- ERROR_INJECT_CRASH("crash_add_partition_6") ||
- write_log_rename_frm(lpt) ||
- (not_completed= FALSE) ||
ERROR_INJECT_CRASH("crash_add_partition_7") ||
- ((frm_install= TRUE), FALSE) ||
- mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
+ ERROR_INJECT_ERROR("fail_add_partition_7") ||
+ write_log_rename_frm(lpt) ||
+ (action_completed= TRUE, FALSE) ||
ERROR_INJECT_CRASH("crash_add_partition_8") ||
- (write_log_completed(lpt, FALSE), FALSE) ||
+ ERROR_INJECT_ERROR("fail_add_partition_8") ||
+ (frm_install= TRUE, FALSE) ||
+ mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
+ (frm_install= FALSE, FALSE) ||
ERROR_INJECT_CRASH("crash_add_partition_9") ||
- (alter_partition_lock_handling(lpt), FALSE))
+ ERROR_INJECT_ERROR("fail_add_partition_9") ||
+ (write_log_completed(lpt, FALSE), FALSE) ||
+ ERROR_INJECT_CRASH("crash_add_partition_10") ||
+ ERROR_INJECT_ERROR("fail_add_partition_10") ||
+ (alter_partition_lock_handling(lpt), FALSE))
{
- handle_alter_part_error(lpt, not_completed, FALSE, frm_install);
+ handle_alter_part_error(lpt, action_completed, FALSE, frm_install,
+ close_table_on_failure);
goto err;
}
}
@@ -6414,78 +6955,96 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
removed in a crash situation
3) Add the new partitions
Copy from the reorganised partitions to the new partitions
- 4) Log that operation is completed and log all complete actions
+ 4) Get an exclusive metadata lock on the table (waits for all active
+ transactions using this table). This ensures that we
+ can release all other locks on the table and since no one can open
+ the table, there can be no new threads accessing the table. They
+ will be hanging on this exclusive lock.
+ 5) Log that operation is completed and log all complete actions
needed to complete operation from here
- 5) Lock all partitions in TL_WRITE_ONLY to ensure that no users
- are still using the old partitioning scheme. Wait until all
- ongoing users have completed before progressing.
- 6) Get a name lock of the table
- 7) Close all tables opened but not yet locked, after this call we are
- certain that no other thread is in the lock wait queue or has
- opened the table. The name lock will ensure that they are blocked
- on the open call.
- This is achieved also by close_data_files_and_morph_locks call.
- 8) Close all partitions opened by this thread, but retain name lock.
- 9) Write bin log
- 10) Prepare handlers for rename and delete of partitions
- 11) Rename and drop the reorged partitions such that they are no
- longer used and rename those added to their real new names.
- 12) Install the shadow frm file
- 13) Reopen the table if under lock tables
- 14) Complete query
+ 6) Write bin log
+ 7) Close all instances of the table and remove them from the table cache.
+ 8) Prepare handlers for rename and delete of partitions
+ 9) Rename and drop the reorged partitions such that they are no
+ longer used and rename those added to their real new names.
+ 10) Install the shadow frm file
+ 11) Reopen the table if under lock tables
+ 12) Complete query
*/
- if (write_log_add_change_partition(lpt) ||
+ if (write_log_drop_shadow_frm(lpt) ||
ERROR_INJECT_CRASH("crash_change_partition_1") ||
+ ERROR_INJECT_ERROR("fail_change_partition_1") ||
mysql_write_frm(lpt, WFRM_WRITE_SHADOW) ||
ERROR_INJECT_CRASH("crash_change_partition_2") ||
- mysql_change_partitions(lpt) ||
+ ERROR_INJECT_ERROR("fail_change_partition_2") ||
+ (close_table_on_failure= TRUE, FALSE) ||
+ write_log_add_change_partition(lpt) ||
ERROR_INJECT_CRASH("crash_change_partition_3") ||
- write_log_final_change_partition(lpt) ||
+ ERROR_INJECT_ERROR("fail_change_partition_3") ||
+ mysql_change_partitions(lpt) ||
ERROR_INJECT_CRASH("crash_change_partition_4") ||
- (not_completed= FALSE) ||
- abort_and_upgrade_lock_and_close_table(lpt) ||
+ ERROR_INJECT_ERROR("fail_change_partition_4") ||
+ wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) ||
+ ERROR_INJECT_CRASH("crash_change_partition_5") ||
+ ERROR_INJECT_ERROR("fail_change_partition_5") ||
+ write_log_final_change_partition(lpt) ||
+ (action_completed= TRUE, FALSE) ||
ERROR_INJECT_CRASH("crash_change_partition_6") ||
+ ERROR_INJECT_ERROR("fail_change_partition_6") ||
((!thd->lex->no_write_to_binlog) &&
(write_bin_log(thd, FALSE,
thd->query(), thd->query_length()), FALSE)) ||
ERROR_INJECT_CRASH("crash_change_partition_7") ||
+ ERROR_INJECT_ERROR("fail_change_partition_7") ||
+ ((frm_install= TRUE), FALSE) ||
mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
+ (frm_install= FALSE, FALSE) ||
ERROR_INJECT_CRASH("crash_change_partition_8") ||
- mysql_drop_partitions(lpt) ||
+ ERROR_INJECT_ERROR("fail_change_partition_8") ||
+ alter_close_tables(lpt, action_completed) ||
+ (close_table_on_failure= FALSE, FALSE) ||
ERROR_INJECT_CRASH("crash_change_partition_9") ||
- mysql_rename_partitions(lpt) ||
- ((frm_install= TRUE), FALSE) ||
+ ERROR_INJECT_ERROR("fail_change_partition_9") ||
+ mysql_drop_partitions(lpt) ||
ERROR_INJECT_CRASH("crash_change_partition_10") ||
- (write_log_completed(lpt, FALSE), FALSE) ||
+ ERROR_INJECT_ERROR("fail_change_partition_10") ||
+ mysql_rename_partitions(lpt) ||
ERROR_INJECT_CRASH("crash_change_partition_11") ||
+ ERROR_INJECT_ERROR("fail_change_partition_11") ||
+ (write_log_completed(lpt, FALSE), FALSE) ||
+ ERROR_INJECT_CRASH("crash_change_partition_12") ||
+ ERROR_INJECT_ERROR("fail_change_partition_12") ||
(alter_partition_lock_handling(lpt), FALSE))
{
- handle_alter_part_error(lpt, not_completed, FALSE, frm_install);
+ handle_alter_part_error(lpt, action_completed, FALSE, frm_install,
+ close_table_on_failure);
goto err;
}
}
+ downgrade_mdl_if_lock_tables_mode(thd, mdl_ticket, MDL_SHARED_NO_READ_WRITE);
/*
A final step is to write the query to the binlog and send ok to the
user
*/
- DBUG_RETURN(fast_end_partition(thd, lpt->copied, lpt->deleted,
- table, table_list, FALSE, NULL,
- written_bin_log));
+ DBUG_RETURN(fast_end_partition(thd, lpt->copied, lpt->deleted, table_list));
err:
- if (thd->locked_tables)
+ if (action_completed)
{
/*
- table->part_info was altered in prep_alter_part_table and must be
- destroyed and recreated, since otherwise it will be reused, since
- we are under LOCK TABLE.
+ Although error occurred, the action was forced to retry for completion.
+ Therefore we must close+reopen all instances of the table.
*/
- alter_partition_lock_handling(lpt);
+ (void) alter_partition_lock_handling(lpt);
}
else
{
- /* Force the table to be closed to avoid reuse of the table->part_info */
- close_thread_tables(thd);
+ /*
+ The failed action was reverted, leave the original table as is and
+ close/destroy the intermediate table object and its share.
+ */
+ close_temporary(lpt->table, 1, 0);
}
+ downgrade_mdl_if_lock_tables_mode(thd, mdl_ticket, MDL_SHARED_NO_READ_WRITE);
DBUG_RETURN(TRUE);
}
#endif
@@ -6578,7 +7137,8 @@ void set_key_field_ptr(KEY *key_info, const uchar *new_buf,
void mem_alloc_error(size_t size)
{
- my_error(ER_OUTOFMEMORY, MYF(0), static_cast<int>(size));
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR),
+ static_cast<int>(size));
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
@@ -6665,18 +7225,21 @@ void make_used_partitions_str(partition_info *part_info, String *parts_str)
definition)
IMPLEMENTATION
- There are two available interval analyzer functions:
- (1) get_part_iter_for_interval_via_mapping
- (2) get_part_iter_for_interval_via_walking
+ There are three available interval analyzer functions:
+ (1) get_part_iter_for_interval_via_mapping
+ (2) get_part_iter_for_interval_cols_via_map
+ (3) get_part_iter_for_interval_via_walking
- They both have limited applicability:
+ They all have limited applicability:
(1) is applicable for "PARTITION BY <RANGE|LIST>(func(t.field))", where
func is a monotonic function.
-
- (2) is applicable for
+
+ (2) is applicable for "PARTITION BY <RANGE|LIST> COLUMNS (field_list)
+
+ (3) is applicable for
"[SUB]PARTITION BY <any-partitioning-type>(any_func(t.integer_field))"
- If both are applicable, (1) is preferred over (2).
+ If both (1) and (3) are applicable, (1) is preferred over (3).
This function sets part_info::get_part_iter_for_interval according to
this criteria, and also sets some auxilary fields that the function
@@ -6696,10 +7259,19 @@ static void set_up_range_analysis_info(partition_info *part_info)
switch (part_info->part_type) {
case RANGE_PARTITION:
case LIST_PARTITION:
- if (part_info->part_expr->get_monotonicity_info() != NON_MONOTONIC)
+ if (!part_info->column_list)
+ {
+ if (part_info->part_expr->get_monotonicity_info() != NON_MONOTONIC)
+ {
+ part_info->get_part_iter_for_interval=
+ get_part_iter_for_interval_via_mapping;
+ goto setup_subparts;
+ }
+ }
+ else
{
part_info->get_part_iter_for_interval=
- get_part_iter_for_interval_via_mapping;
+ get_part_iter_for_interval_cols_via_map;
goto setup_subparts;
}
default:
@@ -6710,7 +7282,7 @@ static void set_up_range_analysis_info(partition_info *part_info)
Check if get_part_iter_for_interval_via_walking() can be used for
partitioning
*/
- if (part_info->no_part_fields == 1)
+ if (part_info->num_part_fields == 1)
{
Field *field= part_info->part_field_array[0];
switch (field->type()) {
@@ -6732,7 +7304,7 @@ setup_subparts:
Check if get_part_iter_for_interval_via_walking() can be used for
subpartitioning
*/
- if (part_info->no_subpart_fields == 1)
+ if (part_info->num_subpart_fields == 1)
{
Field *field= part_info->subpart_field_array[0];
switch (field->type()) {
@@ -6750,51 +7322,338 @@ setup_subparts:
}
+/*
+ This function takes a memory of packed fields in opt-range format
+ and stores it in record format. To avoid having to worry about how
+ the length of fields are calculated in opt-range format we send
+ an array of lengths used for each field in store_length_array.
+
+ SYNOPSIS
+ store_tuple_to_record()
+ pfield Field array
+ store_length_array Array of field lengths
+ value Memory where fields are stored
+ value_end End of memory
+
+ RETURN VALUE
+ nparts Number of fields assigned
+*/
+uint32 store_tuple_to_record(Field **pfield,
+ uint32 *store_length_array,
+ uchar *value,
+ uchar *value_end)
+{
+ /* This function is inspired by store_key_image_rec. */
+ uint32 nparts= 0;
+ uchar *loc_value;
+ while (value < value_end)
+ {
+ loc_value= value;
+ if ((*pfield)->real_maybe_null())
+ {
+ if (*loc_value)
+ (*pfield)->set_null();
+ else
+ (*pfield)->set_notnull();
+ loc_value++;
+ }
+ uint len= (*pfield)->pack_length();
+ (*pfield)->set_key_image(loc_value, len);
+ value+= *store_length_array;
+ store_length_array++;
+ nparts++;
+ pfield++;
+ }
+ return nparts;
+}
+
+/**
+ RANGE(columns) partitioning: compare partition value bound and probe tuple.
+
+ @param val Partition column values.
+ @param nvals_in_rec Number of (prefix) fields to compare.
+
+ @return Less than/Equal to/Greater than 0 if the record is L/E/G than val.
+
+ @note The partition value bound is always a full tuple (but may include the
+ MAXVALUE special value). The probe tuple may be a prefix of partitioning
+ tuple.
+*/
+
+static int cmp_rec_and_tuple(part_column_list_val *val, uint32 nvals_in_rec)
+{
+ partition_info *part_info= val->part_info;
+ Field **field= part_info->part_field_array;
+ Field **fields_end= field + nvals_in_rec;
+ int res;
+
+ for (; field != fields_end; field++, val++)
+ {
+ if (val->max_value)
+ return -1;
+ if ((*field)->is_null())
+ {
+ if (val->null_value)
+ continue;
+ return -1;
+ }
+ if (val->null_value)
+ return +1;
+ res= (*field)->cmp((const uchar*)val->column_value);
+ if (res)
+ return res;
+ }
+ return 0;
+}
+
+
+/**
+ Compare record and columns partition tuple including endpoint handling.
+
+ @param val Columns partition tuple
+ @param n_vals_in_rec Number of columns to compare
+ @param is_left_endpoint True if left endpoint (part_tuple < rec or
+ part_tuple <= rec)
+ @param include_endpoint If endpoint is included (part_tuple <= rec or
+ rec <= part_tuple)
+
+ @return Less than/Equal to/Greater than 0 if the record is L/E/G than
+ the partition tuple.
+
+ @see get_list_array_idx_for_endpoint() and
+ get_partition_id_range_for_endpoint().
+*/
+
+static int cmp_rec_and_tuple_prune(part_column_list_val *val,
+ uint32 n_vals_in_rec,
+ bool is_left_endpoint,
+ bool include_endpoint)
+{
+ int cmp;
+ Field **field;
+ if ((cmp= cmp_rec_and_tuple(val, n_vals_in_rec)))
+ return cmp;
+ field= val->part_info->part_field_array + n_vals_in_rec;
+ if (!(*field))
+ {
+ /* Full match. Only equal if including endpoint. */
+ if (include_endpoint)
+ return 0;
+
+ if (is_left_endpoint)
+ return +4; /* Start of range, part_tuple < rec, return higher. */
+ return -4; /* End of range, rec < part_tupe, return lesser. */
+ }
+ /*
+ The prefix is equal and there are more partition columns to compare.
+
+ If including left endpoint or not including right endpoint
+ then the record is considered lesser compared to the partition.
+
+ i.e:
+ part(10, x) <= rec(10, unknown) and rec(10, unknown) < part(10, x)
+ part <= rec -> lesser (i.e. this or previous partitions)
+ rec < part -> lesser (i.e. this or previous partitions)
+ */
+ if (is_left_endpoint == include_endpoint)
+ return -2;
+
+ /*
+ If right endpoint and the first additional partition value
+ is MAXVALUE, then the record is lesser.
+ */
+ if (!is_left_endpoint && (val + n_vals_in_rec)->max_value)
+ return -3;
+
+ /*
+ Otherwise the record is considered greater.
+
+ rec <= part -> greater (i.e. does not match this partition, seek higher).
+ part < rec -> greater (i.e. does not match this partition, seek higher).
+ */
+ return 2;
+}
+
+
typedef uint32 (*get_endpoint_func)(partition_info*, bool left_endpoint,
bool include_endpoint);
-/*
- Partitioning Interval Analysis: Initialize the iterator for "mapping" case
+typedef uint32 (*get_col_endpoint_func)(partition_info*, bool left_endpoint,
+ bool include_endpoint,
+ uint32 num_parts);
- SYNOPSIS
- get_part_iter_for_interval_via_mapping()
- part_info Partition info
- is_subpart TRUE - act for subpartitioning
- FALSE - act for partitioning
- min_value minimum field value, in opt_range key format.
- max_value minimum field value, in opt_range key format.
- flags Some combination of NEAR_MIN, NEAR_MAX, NO_MIN_RANGE,
- NO_MAX_RANGE.
- part_iter Iterator structure to be initialized
+/**
+ Get partition for RANGE COLUMNS endpoint.
- DESCRIPTION
- Initialize partition set iterator to walk over the interval in
- ordered-array-of-partitions (for RANGE partitioning) or
- ordered-array-of-list-constants (for LIST partitioning) space.
+ @param part_info Partitioning metadata.
+ @param is_left_endpoint True if left endpoint (const <=/< cols)
+ @param include_endpoint True if range includes the endpoint (<=/>=)
+ @param nparts Total number of partitions
- IMPLEMENTATION
- This function is used when partitioning is done by
- <RANGE|LIST>(ascending_func(t.field)), and we can map an interval in
- t.field space into a sub-array of partition_info::range_int_array or
- partition_info::list_array (see get_partition_id_range_for_endpoint,
- get_list_array_idx_for_endpoint for details).
-
- The function performs this interval mapping, and sets the iterator to
- traverse the sub-array and return appropriate partitions.
-
- RETURN
- 0 - No matching partitions (iterator not initialized)
- 1 - Ok, iterator intialized for traversal of matching partitions.
- -1 - All partitions would match (iterator not initialized)
+ @return Partition id of matching partition.
+
+ @see get_partition_id_cols_list_for_endpoint and
+ get_partition_id_range_for_endpoint.
+*/
+
+uint32 get_partition_id_cols_range_for_endpoint(partition_info *part_info,
+ bool is_left_endpoint,
+ bool include_endpoint,
+ uint32 nparts)
+{
+ uint min_part_id= 0, max_part_id= part_info->num_parts, loc_part_id;
+ part_column_list_val *range_col_array= part_info->range_col_array;
+ uint num_columns= part_info->part_field_list.elements;
+ DBUG_ENTER("get_partition_id_cols_range_for_endpoint");
+
+ /* Find the matching partition (including taking endpoint into account). */
+ do
+ {
+ /* Midpoint, adjusted down, so it can never be > last partition. */
+ loc_part_id= (max_part_id + min_part_id) >> 1;
+ if (0 <= cmp_rec_and_tuple_prune(range_col_array +
+ loc_part_id * num_columns,
+ nparts,
+ is_left_endpoint,
+ include_endpoint))
+ min_part_id= loc_part_id + 1;
+ else
+ max_part_id= loc_part_id;
+ } while (max_part_id > min_part_id);
+ loc_part_id= max_part_id;
+
+ /* Given value must be LESS THAN the found partition. */
+ DBUG_ASSERT(loc_part_id == part_info->num_parts ||
+ (0 > cmp_rec_and_tuple_prune(range_col_array +
+ loc_part_id * num_columns,
+ nparts, is_left_endpoint,
+ include_endpoint)));
+ /* Given value must be GREATER THAN or EQUAL to the previous partition. */
+ DBUG_ASSERT(loc_part_id == 0 ||
+ (0 <= cmp_rec_and_tuple_prune(range_col_array +
+ (loc_part_id - 1) * num_columns,
+ nparts, is_left_endpoint,
+ include_endpoint)));
+
+ if (!is_left_endpoint)
+ {
+ /* Set the end after this partition if not already after the last. */
+ if (loc_part_id < part_info->num_parts)
+ loc_part_id++;
+ }
+ DBUG_RETURN(loc_part_id);
+}
+
+
+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)
+{
+ uint32 nparts;
+ get_col_endpoint_func UNINIT_VAR(get_col_endpoint);
+ DBUG_ENTER("get_part_iter_for_interval_cols_via_map");
+
+ if (part_info->part_type == RANGE_PARTITION)
+ {
+ get_col_endpoint= get_partition_id_cols_range_for_endpoint;
+ part_iter->get_next= get_next_partition_id_range;
+ }
+ else if (part_info->part_type == LIST_PARTITION)
+ {
+ get_col_endpoint= get_partition_id_cols_list_for_endpoint;
+ part_iter->get_next= get_next_partition_id_list;
+ part_iter->part_info= part_info;
+ DBUG_ASSERT(part_info->num_list_values);
+ }
+ else
+ assert(0);
+
+ if (flags & NO_MIN_RANGE)
+ part_iter->part_nums.start= part_iter->part_nums.cur= 0;
+ else
+ {
+ // Copy from min_value to record
+ nparts= store_tuple_to_record(part_info->part_field_array,
+ store_length_array,
+ min_value,
+ min_value + min_len);
+ part_iter->part_nums.start= part_iter->part_nums.cur=
+ get_col_endpoint(part_info, TRUE, !(flags & NEAR_MIN),
+ nparts);
+ }
+ if (flags & NO_MAX_RANGE)
+ {
+ if (part_info->part_type == RANGE_PARTITION)
+ part_iter->part_nums.end= part_info->num_parts;
+ else /* LIST_PARTITION */
+ {
+ DBUG_ASSERT(part_info->part_type == LIST_PARTITION);
+ part_iter->part_nums.end= part_info->num_list_values;
+ }
+ }
+ else
+ {
+ // Copy from max_value to record
+ nparts= store_tuple_to_record(part_info->part_field_array,
+ store_length_array,
+ max_value,
+ max_value + max_len);
+ part_iter->part_nums.end= get_col_endpoint(part_info, FALSE,
+ !(flags & NEAR_MAX),
+ nparts);
+ }
+ if (part_iter->part_nums.start == part_iter->part_nums.end)
+ DBUG_RETURN(0);
+ DBUG_RETURN(1);
+}
+
+
+/**
+ Partitioning Interval Analysis: Initialize the iterator for "mapping" case
+
+ @param part_info Partition info
+ @param is_subpart TRUE - act for subpartitioning
+ FALSE - act for partitioning
+ @param store_length_array Ignored.
+ @param min_value minimum field value, in opt_range key format.
+ @param max_value minimum field value, in opt_range key format.
+ @param min_len Ignored.
+ @param max_len Ignored.
+ @param flags Some combination of NEAR_MIN, NEAR_MAX, NO_MIN_RANGE,
+ NO_MAX_RANGE.
+ @param part_iter Iterator structure to be initialized
+
+ @details Initialize partition set iterator to walk over the interval in
+ ordered-array-of-partitions (for RANGE partitioning) or
+ ordered-array-of-list-constants (for LIST partitioning) space.
+
+ This function is used when partitioning is done by
+ <RANGE|LIST>(ascending_func(t.field)), and we can map an interval in
+ t.field space into a sub-array of partition_info::range_int_array or
+ partition_info::list_array (see get_partition_id_range_for_endpoint,
+ get_list_array_idx_for_endpoint for details).
+
+ The function performs this interval mapping, and sets the iterator to
+ traverse the sub-array and return appropriate partitions.
+
+ @return Status of iterator
+ @retval 0 No matching partitions (iterator not initialized)
+ @retval 1 Ok, iterator intialized for traversal of matching partitions.
+ @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)
{
- DBUG_ASSERT(!is_subpart);
Field *field= part_info->part_field_array[0];
uint32 UNINIT_VAR(max_endpoint_val);
get_endpoint_func UNINIT_VAR(get_endpoint);
@@ -6803,6 +7662,11 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
MYSQL_TIME start_date;
bool check_zero_dates= false;
bool zero_in_start_date= true;
+ DBUG_ENTER("get_part_iter_for_interval_via_mapping");
+ DBUG_ASSERT(!is_subpart);
+ (void) store_length_array;
+ (void)min_len;
+ (void)max_len;
part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE;
if (part_info->part_type == RANGE_PARTITION)
@@ -6811,7 +7675,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
get_endpoint= get_partition_id_range_for_endpoint_charset;
else
get_endpoint= get_partition_id_range_for_endpoint;
- max_endpoint_val= part_info->no_parts;
+ max_endpoint_val= part_info->num_parts;
part_iter->get_next= get_next_partition_id_range;
}
else if (part_info->part_type == LIST_PARTITION)
@@ -6821,7 +7685,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
get_endpoint= get_list_array_idx_for_endpoint_charset;
else
get_endpoint= get_list_array_idx_for_endpoint;
- max_endpoint_val= part_info->no_list_values;
+ max_endpoint_val= part_info->num_list_values;
part_iter->get_next= get_next_partition_id_list;
part_iter->part_info= part_info;
if (max_endpoint_val == 0)
@@ -6834,7 +7698,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
part_iter->part_nums.start= part_iter->part_nums.end= 0;
part_iter->part_nums.cur= 0;
part_iter->ret_null_part= part_iter->ret_null_part_orig= TRUE;
- return -1;
+ DBUG_RETURN(-1);
}
}
else
@@ -6867,11 +7731,11 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
{
part_iter->ret_null_part= part_iter->ret_null_part_orig= TRUE;
part_iter->part_nums.start= part_iter->part_nums.cur= 0;
- if (*max_value && !(flags & NO_MAX_RANGE))
+ if (!(flags & NO_MAX_RANGE) && *max_value)
{
/* The right bound is X <= NULL, i.e. it is a "X IS NULL" interval */
part_iter->part_nums.end= 0;
- return 1;
+ DBUG_RETURN(1);
}
}
else
@@ -6895,7 +7759,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
part_iter->part_nums.cur= part_iter->part_nums.start= 0;
part_iter->part_nums.end= 0;
part_iter->ret_null_part= part_iter->ret_null_part_orig= TRUE;
- return 1;
+ DBUG_RETURN(1);
}
part_iter->part_nums.cur= part_iter->part_nums.start;
if (check_zero_dates && !part_info->part_expr->null_value)
@@ -6912,7 +7776,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
}
}
if (part_iter->part_nums.start == max_endpoint_val)
- return 0; /* No partitions */
+ DBUG_RETURN(0); /* No partitions */
}
}
@@ -6931,15 +7795,17 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
MYSQL_TIME end_date;
bool zero_in_end_date= field->get_date(&end_date, 0);
/*
- This is an optimization for TO_DAYS() to avoid scanning the NULL
- partition for ranges that cannot include a date with 0 as
+ This is an optimization for TO_DAYS()/TO_SECONDS() to avoid scanning
+ the NULL partition for ranges that cannot include a date with 0 as
month/day.
*/
DBUG_PRINT("info", ("zero end %u %04d-%02d-%02d",
zero_in_end_date,
end_date.year, end_date.month, end_date.day));
DBUG_ASSERT(!memcmp(((Item_func*) part_info->part_expr)->func_name(),
- "to_days", 7));
+ "to_days", 7) ||
+ !memcmp(((Item_func*) part_info->part_expr)->func_name(),
+ "to_seconds", 10));
if (!zero_in_end_date &&
start_date.month == end_date.month &&
start_date.year == end_date.year)
@@ -6947,14 +7813,14 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
}
if (part_iter->part_nums.start >= part_iter->part_nums.end &&
!part_iter->ret_null_part)
- return 0; /* No partitions */
+ DBUG_RETURN(0); /* No partitions */
}
- return 1; /* Ok, iterator initialized */
+ DBUG_RETURN(1); /* Ok, iterator initialized */
}
/* See get_part_iter_for_interval_via_walking for definition of what this is */
-#define MAX_RANGE_TO_WALK 10
+#define MAX_RANGE_TO_WALK 32
/*
@@ -6990,16 +7856,6 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
Intervals with +inf/-inf, and [NULL, c1] interval can be processed but
that is more tricky and I don't have time to do it right now.
- Additionally we have these requirements:
- * number of values in the interval must be less then number of
- [sub]partitions, and
- * Number of values in the interval must be less then MAX_RANGE_TO_WALK.
-
- The rationale behind these requirements is that if they are not met
- we're likely to hit most of the partitions and traversing the interval
- will only add overhead. So it's better return "all partitions used" in
- that case.
-
RETURN
0 - No matching partitions, iterator not initialized
1 - Some partitions would match, iterator intialized for traversing them
@@ -7007,25 +7863,32 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
*/
int get_part_iter_for_interval_via_walking(partition_info *part_info,
- bool is_subpart,
- uchar *min_value, uchar *max_value,
- uint flags,
- PARTITION_ITERATOR *part_iter)
+ 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;
partition_iter_func get_next_func;
+ DBUG_ENTER("get_part_iter_for_interval_via_walking");
+ (void)store_length_array;
+ (void)min_len;
+ (void)max_len;
+
part_iter->ret_null_part= part_iter->ret_null_part_orig= FALSE;
if (is_subpart)
{
field= part_info->subpart_field_array[0];
- total_parts= part_info->no_subparts;
+ total_parts= part_info->num_subparts;
get_next_func= get_next_subpartition_via_walking;
}
else
{
field= part_info->part_field_array[0];
- total_parts= part_info->no_parts;
+ total_parts= part_info->num_parts;
get_next_func= get_next_partition_via_walking;
}
@@ -7045,7 +7908,7 @@ int get_part_iter_for_interval_via_walking(partition_info *part_info,
if (!part_info->get_subpartition_id(part_info, &part_id))
{
init_single_partition_iterator(part_id, part_iter);
- return 1; /* Ok, iterator initialized */
+ DBUG_RETURN(1); /* Ok, iterator initialized */
}
}
else
@@ -7058,10 +7921,10 @@ int get_part_iter_for_interval_via_walking(partition_info *part_info,
if (!res)
{
init_single_partition_iterator(part_id, part_iter);
- return 1; /* Ok, iterator initialized */
+ DBUG_RETURN(1); /* Ok, iterator initialized */
}
}
- return 0; /* No partitions match */
+ DBUG_RETURN(0); /* No partitions match */
}
if ((field->real_maybe_null() &&
@@ -7069,7 +7932,7 @@ int get_part_iter_for_interval_via_walking(partition_info *part_info,
(!(flags & NO_MAX_RANGE) && *max_value))) || // X <? NULL
(flags & (NO_MIN_RANGE | NO_MAX_RANGE))) // -inf at any bound
{
- return -1; /* Can't handle this interval, have to use all partitions */
+ DBUG_RETURN(-1); /* Can't handle this interval, have to use all partitions */
}
/* Get integers for left and right interval bound */
@@ -7088,20 +7951,36 @@ int get_part_iter_for_interval_via_walking(partition_info *part_info,
an empty interval by "wrapping around" a + 4G-1 + 1 = a.
*/
if ((ulonglong)b - (ulonglong)a == ~0ULL)
- return -1;
+ DBUG_RETURN(-1);
a += test(flags & NEAR_MIN);
b += test(!(flags & NEAR_MAX));
ulonglong n_values= b - a;
-
- if (n_values > total_parts || n_values > MAX_RANGE_TO_WALK)
- return -1;
+
+ /*
+ Will it pay off to enumerate all values in the [a..b] range and evaluate
+ the partitioning function for every value? It depends on
+ 1. whether we'll be able to infer that some partitions are not used
+ 2. if time savings from not scanning these partitions will be greater
+ than time spent in enumeration.
+ We will assume that the cost of accessing one extra partition is greater
+ than the cost of evaluating the partitioning function O(#partitions).
+ This means we should jump at any chance to eliminate a partition, which
+ gives us this logic:
+
+ Do the enumeration if
+ - the number of values to enumerate is comparable to the number of
+ partitions, or
+ - there are not many values to enumerate.
+ */
+ if ((n_values > 2*total_parts) && n_values > MAX_RANGE_TO_WALK)
+ DBUG_RETURN(-1);
part_iter->field_vals.start= part_iter->field_vals.cur= a;
part_iter->field_vals.end= b;
part_iter->part_info= part_info;
part_iter->get_next= get_next_func;
- return 1;
+ DBUG_RETURN(1);
}
@@ -7149,8 +8028,9 @@ uint32 get_next_partition_id_range(PARTITION_ITERATOR* part_iter)
DESCRIPTION
This implementation of PARTITION_ITERATOR::get_next() is special for
- LIST partitioning: it enumerates partition ids in
- part_info->list_array[i] where i runs over [min_idx, max_idx] interval.
+ LIST partitioning: it enumerates partition ids in
+ part_info->list_array[i] (list_col_array[i*cols] for COLUMNS LIST
+ partitioning) where i runs over [min_idx, max_idx] interval.
The function conforms to partition_iter_func type.
RETURN
@@ -7172,8 +8052,16 @@ uint32 get_next_partition_id_list(PARTITION_ITERATOR *part_iter)
return NOT_A_PARTITION_ID;
}
else
- return part_iter->part_info->list_array[part_iter->
- part_nums.cur++].partition_id;
+ {
+ partition_info *part_info= part_iter->part_info;
+ uint32 num_part= part_iter->part_nums.cur++;
+ if (part_info->column_list)
+ {
+ uint num_columns= part_info->part_field_list.elements;
+ return part_info->list_col_array[num_part*num_columns].partition_id;
+ }
+ return part_info->list_array[num_part].partition_id;
+ }
}
@@ -7314,4 +8202,16 @@ void create_subpartition_name(char *out, const char *in1,
strxmov(out, in1, "#P#", transl_part_name,
"#SP#", transl_subpart_name, "#REN#", NullS);
}
+
+uint get_partition_field_store_length(Field *field)
+{
+ uint store_length;
+
+ store_length= field->key_length();
+ if (field->real_maybe_null())
+ store_length+= HA_KEY_NULL_LENGTH;
+ if (field->real_type() == MYSQL_TYPE_VARCHAR)
+ store_length+= HA_KEY_BLOB_LENGTH;
+ return store_length;
+}
#endif
diff --git a/sql/sql_partition.h b/sql/sql_partition.h
index 2f741c76594..951d58a655b 100644
--- a/sql/sql_partition.h
+++ b/sql/sql_partition.h
@@ -1,5 +1,7 @@
-/*
- Copyright (c) 2006, 2010, Oracle and/or its affiliates.
+#ifndef SQL_PARTITION_INCLUDED
+#define SQL_PARTITION_INCLUDED
+
+/* Copyright (c) 2006, 2013, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,32 +14,57 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifdef __GNUC__
#pragma interface /* gcc class implementation */
#endif
+#include "sql_list.h" /* List */
+#include "table.h" /* TABLE_LIST */
+
+class Alter_info;
+class Field;
+class String;
+class handler;
+class partition_info;
+struct TABLE;
+struct TABLE_LIST;
+typedef struct st_bitmap MY_BITMAP;
+typedef struct st_ha_create_information HA_CREATE_INFO;
+typedef struct st_key KEY;
+typedef struct st_key_range key_range;
+
/* Flags for partition handlers */
#define HA_CAN_PARTITION (1 << 0) /* Partition support */
#define HA_CAN_UPDATE_PARTITION_KEY (1 << 1)
#define HA_CAN_PARTITION_UNIQUE (1 << 2)
#define HA_USE_AUTO_PARTITION (1 << 3)
-/*typedef struct {
- ulonglong data_file_length;
- ulonglong max_data_file_length;
- ulonglong index_file_length;
- ulonglong delete_length;
- ha_rows records;
- ulong mean_rec_length;
- time_t create_time;
- time_t check_time;
- time_t update_time;
- ulonglong check_sum;
-} PARTITION_INFO;
-*/
+#define NORMAL_PART_NAME 0
+#define TEMP_PART_NAME 1
+#define RENAMED_PART_NAME 2
+
+typedef struct st_lock_param_type
+{
+ TABLE_LIST *table_list;
+ ulonglong copied;
+ ulonglong deleted;
+ THD *thd;
+ HA_CREATE_INFO *create_info;
+ Alter_info *alter_info;
+ TABLE *table;
+ TABLE *old_table;
+ KEY *key_info_buffer;
+ const char *db;
+ const char *table_name;
+ uchar *pack_frm_data;
+ uint key_count;
+ uint db_options;
+ size_t pack_frm_len;
+ partition_info *part_info;
+} ALTER_PARTITION_PARAM_TYPE;
+
typedef struct {
longlong list_value;
uint32 partition_id;
@@ -67,22 +94,24 @@ int get_part_for_delete(const uchar *buf, const uchar *rec0,
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);
-void set_linear_hash_mask(partition_info *part_info, uint no_parts);
+void set_linear_hash_mask(partition_info *part_info, uint num_parts);
bool fix_partition_func(THD *thd, TABLE *table, bool create_table_ind);
-char *generate_partition_syntax(partition_info *part_info,
- uint *buf_length, bool use_sql_alloc,
- bool show_partition_options);
-bool partition_key_modified(TABLE *table, const MY_BITMAP *fields);
void get_partition_set(const TABLE *table, uchar *buf, const uint index,
const key_range *key_spec,
part_id_range *part_spec);
+uint get_partition_field_store_length(Field *field);
+int get_cs_converted_part_value_from_string(THD *thd,
+ Item *item,
+ String *input_str,
+ String *output_str,
+ CHARSET_INFO *cs,
+ bool use_hex);
void get_full_part_id_from_key(const TABLE *table, uchar *buf,
KEY *key_info,
const key_range *key_spec,
part_id_range *part_spec);
bool mysql_unpack_partition(THD *thd, char *part_buf,
uint part_info_len,
- const char *part_state, uint part_state_len,
TABLE *table, bool is_create_table_ind,
handlerton *default_db_type,
bool *work_part_info_used);
@@ -95,6 +124,8 @@ uint32 get_partition_id_range_for_endpoint(partition_info *part_info,
bool include_endpoint);
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);
/*
A "Get next" function for partition iterator.
@@ -171,13 +202,16 @@ typedef struct st_partition_iter
SYNOPSIS
get_partitions_in_range_iter()
- part_info Partitioning info
- is_subpart
- min_val Left edge, field value in opt_range_key format.
- max_val Right edge, field value in opt_range_key format.
- flags Some combination of NEAR_MIN, NEAR_MAX, NO_MIN_RANGE,
- NO_MAX_RANGE.
- part_iter Iterator structure to be initialized
+ part_info Partitioning info
+ is_subpart
+ store_length_array Length of fields packed in opt_range_key format
+ min_val Left edge, field value in opt_range_key format
+ max_val Right edge, field value in opt_range_key format
+ min_len Length of minimum value
+ max_len Length of maximum value
+ flags Some combination of NEAR_MIN, NEAR_MAX, NO_MIN_RANGE,
+ NO_MAX_RANGE
+ part_iter Iterator structure to be initialized
DESCRIPTION
Functions with this signature are used to perform "Partitioning Interval
@@ -190,8 +224,9 @@ typedef struct st_partition_iter
The set of partitions is returned by initializing an iterator in *part_iter
NOTES
- There are currently two functions of this type:
+ There are currently three functions of this type:
- get_part_iter_for_interval_via_walking
+ - get_part_iter_for_interval_cols_via_map
- get_part_iter_for_interval_via_mapping
RETURN
@@ -202,9 +237,54 @@ typedef struct st_partition_iter
typedef int (*get_partitions_in_range_iter)(partition_info *part_info,
bool is_subpart,
+ uint32 *store_length_array,
uchar *min_val, uchar *max_val,
+ uint min_len, uint max_len,
uint flags,
PARTITION_ITERATOR *part_iter);
#include "partition_info.h"
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+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,
+ TABLE *fast_alter_table);
+bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info,
+ enum partition_state part_state);
+uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
+ HA_CREATE_INFO *create_info,
+ handlerton *old_db_type,
+ bool *partition_changed,
+ char *db,
+ const char *table_name,
+ const char *path,
+ TABLE **fast_alter_table);
+char *generate_partition_syntax(partition_info *part_info,
+ uint *buf_length, bool use_sql_alloc,
+ bool show_partition_options,
+ HA_CREATE_INFO *create_info,
+ Alter_info *alter_info,
+ const char *current_comment_start);
+bool partition_key_modified(TABLE *table, const MY_BITMAP *fields);
+#else
+#define partition_key_modified(X,Y) 0
+#endif
+
+void create_partition_name(char *out, const char *in1,
+ const char *in2, uint name_variant,
+ bool translate);
+void create_subpartition_name(char *out, const char *in1,
+ const char *in2, const char *in3,
+ uint name_variant);
+
+void set_field_ptr(Field **ptr, const uchar *new_buf, const uchar *old_buf);
+void set_key_field_ptr(KEY *key_info, const uchar *new_buf,
+ const uchar *old_buf);
+
+extern const LEX_STRING partition_keywords[];
+
+#endif /* SQL_PARTITION_INCLUDED */
diff --git a/sql/sql_partition_admin.cc b/sql/sql_partition_admin.cc
new file mode 100644
index 00000000000..a03dcc5c16e
--- /dev/null
+++ b/sql/sql_partition_admin.cc
@@ -0,0 +1,195 @@
+/* Copyright (c) 2010, 2013, 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 "sql_parse.h" // check_one_table_access
+#include "sql_table.h" // mysql_alter_table, etc.
+#include "sql_lex.h" // Sql_statement
+#include "sql_admin.h" // Analyze/Check/.._table_statement
+#include "sql_partition_admin.h" // Alter_table_*_partition
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+#include "ha_partition.h" // ha_partition
+#endif
+#include "sql_base.h" // open_and_lock_tables
+
+#ifndef WITH_PARTITION_STORAGE_ENGINE
+
+bool Partition_statement_unsupported::execute(THD *)
+{
+ DBUG_ENTER("Partition_statement_unsupported::execute");
+ /* error, partitioning support not compiled in... */
+ my_error(ER_FEATURE_DISABLED, MYF(0), "partitioning",
+ "--with-plugin-partition");
+ DBUG_RETURN(TRUE);
+}
+
+#else
+
+bool Alter_table_analyze_partition_statement::execute(THD *thd)
+{
+ bool res;
+ DBUG_ENTER("Alter_table_analyze_partition_statement::execute");
+
+ /*
+ Flag that it is an ALTER command which administrates partitions, used
+ by ha_partition
+ */
+ m_lex->alter_info.flags|= ALTER_ADMIN_PARTITION;
+
+ res= Analyze_table_statement::execute(thd);
+
+ DBUG_RETURN(res);
+}
+
+
+bool Alter_table_check_partition_statement::execute(THD *thd)
+{
+ bool res;
+ DBUG_ENTER("Alter_table_check_partition_statement::execute");
+
+ /*
+ Flag that it is an ALTER command which administrates partitions, used
+ by ha_partition
+ */
+ m_lex->alter_info.flags|= ALTER_ADMIN_PARTITION;
+
+ res= Check_table_statement::execute(thd);
+
+ DBUG_RETURN(res);
+}
+
+
+bool Alter_table_optimize_partition_statement::execute(THD *thd)
+{
+ bool res;
+ DBUG_ENTER("Alter_table_optimize_partition_statement::execute");
+
+ /*
+ Flag that it is an ALTER command which administrates partitions, used
+ by ha_partition
+ */
+ m_lex->alter_info.flags|= ALTER_ADMIN_PARTITION;
+
+ res= Optimize_table_statement::execute(thd);
+
+ DBUG_RETURN(res);
+}
+
+
+bool Alter_table_repair_partition_statement::execute(THD *thd)
+{
+ bool res;
+ DBUG_ENTER("Alter_table_repair_partition_statement::execute");
+
+ /*
+ Flag that it is an ALTER command which administrates partitions, used
+ by ha_partition
+ */
+ m_lex->alter_info.flags|= ALTER_ADMIN_PARTITION;
+
+ res= Repair_table_statement::execute(thd);
+
+ DBUG_RETURN(res);
+}
+
+
+bool Alter_table_truncate_partition_statement::execute(THD *thd)
+{
+ int error;
+ ha_partition *partition;
+ ulong timeout= thd->variables.lock_wait_timeout;
+ TABLE_LIST *first_table= thd->lex->select_lex.table_list.first;
+ bool binlog_stmt;
+ DBUG_ENTER("Alter_table_truncate_partition_statement::execute");
+
+ /*
+ Flag that it is an ALTER command which administrates partitions, used
+ by ha_partition.
+ */
+ m_lex->alter_info.flags|= ALTER_ADMIN_PARTITION |
+ ALTER_TRUNCATE_PARTITION;
+
+ /* Fix the lock types (not the same as ordinary ALTER TABLE). */
+ first_table->lock_type= TL_WRITE;
+ first_table->mdl_request.set_type(MDL_EXCLUSIVE);
+
+ /*
+ Check table permissions and open it with a exclusive lock.
+ Ensure it is a partitioned table and finally, upcast the
+ handler and invoke the partition truncate method. Lastly,
+ write the statement to the binary log if necessary.
+ */
+
+ if (check_one_table_access(thd, DROP_ACL, first_table))
+ DBUG_RETURN(TRUE);
+
+ if (open_and_lock_tables(thd, first_table, FALSE, 0))
+ DBUG_RETURN(TRUE);
+
+ /*
+ TODO: Add support for TRUNCATE PARTITION for NDB and other
+ engines supporting native partitioning.
+ */
+
+ if (!first_table->table || first_table->view ||
+ first_table->table->s->db_type() != partition_hton)
+ {
+ my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
+ /*
+ Under locked table modes this might still not be an exclusive
+ lock. Hence, upgrade the lock since the handler truncate method
+ mandates an exclusive metadata lock.
+ */
+ MDL_ticket *ticket= first_table->table->mdl_ticket;
+ if (thd->mdl_context.upgrade_shared_lock_to_exclusive(ticket, timeout))
+ DBUG_RETURN(TRUE);
+
+ tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN, first_table->db,
+ first_table->table_name, FALSE);
+
+ partition= (ha_partition *) first_table->table->file;
+
+ /* Invoke the handler method responsible for truncating the partition. */
+ if ((error= partition->truncate_partition(&thd->lex->alter_info,
+ &binlog_stmt)))
+ first_table->table->file->print_error(error, MYF(0));
+
+ /*
+ All effects of a truncate operation are committed even if the
+ operation fails. Thus, the query must be written to the binary
+ log. The exception is a unimplemented truncate method or failure
+ before any call to handler::truncate() is done.
+ Also, it is logged in statement format, regardless of the binlog format.
+ */
+ if (error != HA_ERR_WRONG_COMMAND && binlog_stmt)
+ error|= write_bin_log(thd, !error, thd->query(), thd->query_length());
+
+ /*
+ A locked table ticket was upgraded to a exclusive lock. After the
+ the query has been written to the binary log, downgrade the lock
+ to a shared one.
+ */
+ if (thd->locked_tables_mode)
+ ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
+
+ if (! error)
+ my_ok(thd);
+
+ DBUG_RETURN(error);
+}
+
+#endif /* WITH_PARTITION_STORAGE_ENGINE */
diff --git a/sql/sql_partition_admin.h b/sql/sql_partition_admin.h
new file mode 100644
index 00000000000..479371c3b4d
--- /dev/null
+++ b/sql/sql_partition_admin.h
@@ -0,0 +1,236 @@
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_PARTITION_ADMIN_H
+#define SQL_PARTITION_ADMIN_H
+
+#ifndef WITH_PARTITION_STORAGE_ENGINE
+
+/**
+ Stub class that returns a error if the partition storage engine is
+ not supported.
+*/
+class Partition_statement_unsupported : public Sql_statement
+{
+public:
+ Partition_statement_unsupported(LEX *lex)
+ : Sql_statement(lex)
+ {}
+
+ ~Partition_statement_unsupported()
+ {}
+
+ bool execute(THD *thd);
+};
+
+
+class Alter_table_analyze_partition_statement :
+ public Partition_statement_unsupported
+{
+public:
+ Alter_table_analyze_partition_statement(LEX *lex)
+ : Partition_statement_unsupported(lex)
+ {}
+
+ ~Alter_table_analyze_partition_statement()
+ {}
+};
+
+
+class Alter_table_check_partition_statement :
+ public Partition_statement_unsupported
+{
+public:
+ Alter_table_check_partition_statement(LEX *lex)
+ : Partition_statement_unsupported(lex)
+ {}
+
+ ~Alter_table_check_partition_statement()
+ {}
+};
+
+
+class Alter_table_optimize_partition_statement :
+ public Partition_statement_unsupported
+{
+public:
+ Alter_table_optimize_partition_statement(LEX *lex)
+ : Partition_statement_unsupported(lex)
+ {}
+
+ ~Alter_table_optimize_partition_statement()
+ {}
+};
+
+
+class Alter_table_repair_partition_statement :
+ public Partition_statement_unsupported
+{
+public:
+ Alter_table_repair_partition_statement(LEX *lex)
+ : Partition_statement_unsupported(lex)
+ {}
+
+ ~Alter_table_repair_partition_statement()
+ {}
+};
+
+
+class Alter_table_truncate_partition_statement :
+ public Partition_statement_unsupported
+{
+public:
+ Alter_table_truncate_partition_statement(LEX *lex)
+ : Partition_statement_unsupported(lex)
+ {}
+
+ ~Alter_table_truncate_partition_statement()
+ {}
+};
+
+
+#else
+
+/**
+ Class that represents the ALTER TABLE t1 ANALYZE PARTITION p statement.
+*/
+class Alter_table_analyze_partition_statement : public Analyze_table_statement
+{
+public:
+ /**
+ Constructor, used to represent a ALTER TABLE ANALYZE PARTITION statement.
+ @param lex the LEX structure for this statement.
+ */
+ Alter_table_analyze_partition_statement(LEX *lex)
+ : Analyze_table_statement(lex)
+ {}
+
+ ~Alter_table_analyze_partition_statement()
+ {}
+
+ /**
+ Execute a ALTER TABLE ANALYZE PARTITION statement at runtime.
+ @param thd the current thread.
+ @return false on success.
+ */
+ bool execute(THD *thd);
+};
+
+
+/**
+ Class that represents the ALTER TABLE t1 CHECK PARTITION p statement.
+*/
+class Alter_table_check_partition_statement : public Check_table_statement
+{
+public:
+ /**
+ Constructor, used to represent a ALTER TABLE CHECK PARTITION statement.
+ @param lex the LEX structure for this statement.
+ */
+ Alter_table_check_partition_statement(LEX *lex)
+ : Check_table_statement(lex)
+ {}
+
+ ~Alter_table_check_partition_statement()
+ {}
+
+ /**
+ Execute a ALTER TABLE CHECK PARTITION statement at runtime.
+ @param thd the current thread.
+ @return false on success.
+ */
+ bool execute(THD *thd);
+};
+
+
+/**
+ Class that represents the ALTER TABLE t1 OPTIMIZE PARTITION p statement.
+*/
+class Alter_table_optimize_partition_statement : public Optimize_table_statement
+{
+public:
+ /**
+ Constructor, used to represent a ALTER TABLE OPTIMIZE PARTITION statement.
+ @param lex the LEX structure for this statement.
+ */
+ Alter_table_optimize_partition_statement(LEX *lex)
+ : Optimize_table_statement(lex)
+ {}
+
+ ~Alter_table_optimize_partition_statement()
+ {}
+
+ /**
+ Execute a ALTER TABLE OPTIMIZE PARTITION statement at runtime.
+ @param thd the current thread.
+ @return false on success.
+ */
+ bool execute(THD *thd);
+};
+
+
+/**
+ Class that represents the ALTER TABLE t1 REPAIR PARTITION p statement.
+*/
+class Alter_table_repair_partition_statement : public Repair_table_statement
+{
+public:
+ /**
+ Constructor, used to represent a ALTER TABLE REPAIR PARTITION statement.
+ @param lex the LEX structure for this statement.
+ */
+ Alter_table_repair_partition_statement(LEX *lex)
+ : Repair_table_statement(lex)
+ {}
+
+ ~Alter_table_repair_partition_statement()
+ {}
+
+ /**
+ Execute a ALTER TABLE REPAIR PARTITION statement at runtime.
+ @param thd the current thread.
+ @return false on success.
+ */
+ bool execute(THD *thd);
+};
+
+
+/**
+ Class that represents the ALTER TABLE t1 TRUNCATE PARTITION p statement.
+*/
+class Alter_table_truncate_partition_statement : public Sql_statement
+{
+public:
+ /**
+ Constructor, used to represent a ALTER TABLE TRUNCATE PARTITION statement.
+ @param lex the LEX structure for this statement.
+ */
+ Alter_table_truncate_partition_statement(LEX *lex)
+ : Sql_statement(lex)
+ {}
+
+ virtual ~Alter_table_truncate_partition_statement()
+ {}
+
+ /**
+ Execute a ALTER TABLE TRUNCATE PARTITION statement at runtime.
+ @param thd the current thread.
+ @return false on success.
+ */
+ bool execute(THD *thd);
+};
+
+#endif /* WITH_PARTITION_STORAGE_ENGINE */
+#endif /* SQL_PARTITION_ADMIN_H */
diff --git a/sql/sql_plist.h b/sql/sql_plist.h
new file mode 100644
index 00000000000..e703b4c0f62
--- /dev/null
+++ b/sql/sql_plist.h
@@ -0,0 +1,290 @@
+#ifndef SQL_PLIST_H
+#define SQL_PLIST_H
+/* 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-1301 USA */
+
+
+#include <my_global.h>
+
+template <typename T, typename B, typename C, typename I>
+class I_P_List_iterator;
+class I_P_List_null_counter;
+template <typename T> class I_P_List_no_push_back;
+
+
+/**
+ Intrusive parameterized list.
+
+ Unlike I_List does not require its elements to be descendant of ilink
+ class and therefore allows them to participate in several such lists
+ simultaneously.
+
+ Unlike List is doubly-linked list and thus supports efficient deletion
+ of element without iterator.
+
+ @param T Type of elements which will belong to list.
+ @param B Class which via its methods specifies which members
+ of T should be used for participating in this list.
+ Here is typical layout of such class:
+
+ struct B
+ {
+ static inline T **next_ptr(T *el)
+ {
+ return &el->next;
+ }
+ static inline T ***prev_ptr(T *el)
+ {
+ return &el->prev;
+ }
+ };
+ @param C Policy class specifying how counting of elements in the list
+ should be done. Instance of this class is also used as a place
+ where information about number of list elements is stored.
+ @sa I_P_List_null_counter, I_P_List_counter
+ @param I Policy class specifying whether I_P_List should support
+ efficient push_back() operation. Instance of this class
+ is used as place where we store information to support
+ this operation.
+ @sa I_P_List_no_push_back, I_P_List_fast_push_back.
+*/
+
+template <typename T, typename B,
+ typename C = I_P_List_null_counter,
+ typename I = I_P_List_no_push_back<T> >
+class I_P_List : public C, public I
+{
+ T *m_first;
+
+ /*
+ Do not prohibit copying of I_P_List object to simplify their usage in
+ backup/restore scenarios. Note that performing any operations on such
+ is a bad idea.
+ */
+public:
+ I_P_List() : I(&m_first), m_first(NULL) {};
+ /*
+ empty() is used in many places in the code instead of a constructor, to
+ initialize a bzero-ed I_P_List instance.
+ */
+
+ inline void empty() { m_first= NULL; C::reset(); I::set_last(&m_first); }
+ inline bool is_empty() const { return (m_first == NULL); }
+ inline void push_front(T* a)
+ {
+ *B::next_ptr(a)= m_first;
+ if (m_first)
+ *B::prev_ptr(m_first)= B::next_ptr(a);
+ else
+ I::set_last(B::next_ptr(a));
+ m_first= a;
+ *B::prev_ptr(a)= &m_first;
+ C::inc();
+ }
+ inline void push_back(T *a)
+ {
+ T **last= I::get_last();
+ *B::next_ptr(a)= *last;
+ *last= a;
+ *B::prev_ptr(a)= last;
+ I::set_last(B::next_ptr(a));
+ C::inc();
+ }
+ inline void insert_after(T *pos, T *a)
+ {
+ if (pos == NULL)
+ push_front(a);
+ else
+ {
+ *B::next_ptr(a)= *B::next_ptr(pos);
+ *B::prev_ptr(a)= B::next_ptr(pos);
+ *B::next_ptr(pos)= a;
+ if (*B::next_ptr(a))
+ {
+ T *old_next= *B::next_ptr(a);
+ *B::prev_ptr(old_next)= B::next_ptr(a);
+ }
+ else
+ I::set_last(B::next_ptr(a));
+ C::inc();
+ }
+ }
+ inline void remove(T *a)
+ {
+ T *next= *B::next_ptr(a);
+ if (next)
+ *B::prev_ptr(next)= *B::prev_ptr(a);
+ else
+ I::set_last(*B::prev_ptr(a));
+ **B::prev_ptr(a)= next;
+ C::dec();
+ }
+ inline T* front() { return m_first; }
+ inline const T *front() const { return m_first; }
+ inline T* pop_front()
+ {
+ T *result= front();
+
+ if (result)
+ remove(result);
+
+ return result;
+ }
+ void swap(I_P_List<T, B, C> &rhs)
+ {
+ swap_variables(T *, m_first, rhs.m_first);
+ I::swap(rhs);
+ if (m_first)
+ *B::prev_ptr(m_first)= &m_first;
+ else
+ I::set_last(&m_first);
+ if (rhs.m_first)
+ *B::prev_ptr(rhs.m_first)= &rhs.m_first;
+ else
+ I::set_last(&rhs.m_first);
+ C::swap(rhs);
+ }
+#ifndef _lint
+ friend class I_P_List_iterator<T, B, C, I>;
+#endif
+ typedef I_P_List_iterator<T, B, C, I> Iterator;
+};
+
+
+/**
+ Iterator for I_P_List.
+*/
+
+template <typename T, typename B,
+ typename C = I_P_List_null_counter,
+ typename I = I_P_List_no_push_back<T> >
+class I_P_List_iterator
+{
+ const I_P_List<T, B, C, I> *list;
+ T *current;
+public:
+ I_P_List_iterator(const I_P_List<T, B, C, I> &a)
+ : list(&a), current(a.m_first) {}
+ I_P_List_iterator(const I_P_List<T, B, C, I> &a, T* current_arg)
+ : list(&a), current(current_arg) {}
+ inline void init(const I_P_List<T, B, C, I> &a)
+ {
+ list= &a;
+ current= a.m_first;
+ }
+ inline T* operator++(int)
+ {
+ T *result= current;
+ if (result)
+ current= *B::next_ptr(current);
+ return result;
+ }
+ inline T* operator++()
+ {
+ current= *B::next_ptr(current);
+ return current;
+ }
+ inline void rewind()
+ {
+ current= list->m_first;
+ }
+};
+
+
+/**
+ Hook class which via its methods specifies which members
+ of T should be used for participating in a intrusive list.
+*/
+
+template <typename T, T* T::*next, T** T::*prev>
+struct I_P_List_adapter
+{
+ static inline T **next_ptr(T *el) { return &(el->*next); }
+
+ static inline T ***prev_ptr(T *el) { return &(el->*prev); }
+};
+
+
+/**
+ Element counting policy class for I_P_List to be used in
+ cases when no element counting should be done.
+*/
+
+class I_P_List_null_counter
+{
+protected:
+ void reset() {}
+ void inc() {}
+ void dec() {}
+ void swap(I_P_List_null_counter &rhs) {}
+};
+
+
+/**
+ Element counting policy class for I_P_List which provides
+ basic element counting.
+*/
+
+class I_P_List_counter
+{
+ uint m_counter;
+protected:
+ I_P_List_counter() : m_counter (0) {}
+ void reset() {m_counter= 0;}
+ void inc() {m_counter++;}
+ void dec() {m_counter--;}
+ void swap(I_P_List_counter &rhs)
+ { swap_variables(uint, m_counter, rhs.m_counter); }
+public:
+ uint elements() const { return m_counter; }
+};
+
+
+/**
+ A null insertion policy class for I_P_List to be used
+ in cases when push_back() operation is not necessary.
+*/
+
+template <typename T> class I_P_List_no_push_back
+{
+protected:
+ I_P_List_no_push_back(T **a) {};
+ void set_last(T **a) {}
+ /*
+ 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) {}
+};
+
+
+/**
+ An insertion policy class for I_P_List which can
+ be used when fast push_back() operation is required.
+*/
+
+template <typename T> class I_P_List_fast_push_back
+{
+ T **m_last;
+protected:
+ I_P_List_fast_push_back(T **a) : m_last(a) { };
+ void set_last(T **a) { m_last= a; }
+ T** get_last() const { return m_last; }
+ void swap(I_P_List_fast_push_back<T> &rhs)
+ { swap_variables(T**, m_last, rhs.m_last); }
+};
+
+#endif
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 1547e576eca..e1784c1f027 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -1,5 +1,6 @@
/*
- Copyright (c) 2005, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2005, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,25 +13,42 @@
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 "mysql_priv.h"
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "sql_priv.h" // SHOW_MY_BOOL
+#include "unireg.h"
+#include "my_global.h" // REQUIRED by m_string.h
+#include "sql_class.h" // set_var.h: THD
+#include "sys_vars_shared.h"
+#include "sql_locale.h"
+#include "sql_plugin.h"
+#include "sql_parse.h" // check_table_access
+#include "sql_base.h" // close_mysql_tables
+#include "key.h" // key_copy
+#include "sql_show.h" // remove_status_vars, add_status_vars
+#include "strfunc.h" // find_set
+#include "sql_acl.h" // *_ACL
+#include "records.h" // init_read_record, end_read_record
#include <my_pthread.h>
#include <my_getopt.h>
+#include "sql_audit.h"
#include <mysql/plugin_auth.h>
+#include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT
+#include <mysql/plugin_auth.h>
+#include "sql_plugin_compat.h"
+
#define REPORT_TO_LOG 1
#define REPORT_TO_USER 2
-extern struct st_maria_plugin *mariadb_builtins[];
+extern struct st_maria_plugin *mysql_optional_plugins[];
+extern struct st_maria_plugin *mysql_mandatory_plugins[];
/**
@note The order of the enumeration is critical.
@see construct_options
*/
-static const char *global_plugin_typelib_names[]=
- { "OFF", "ON", "FORCE", NULL };
-enum enum_plugin_load_policy {PLUGIN_OFF, PLUGIN_ON, PLUGIN_FORCE};
+const char *global_plugin_typelib_names[]=
+ { "OFF", "ON", "FORCE", "FORCE_PLUS_PERMANENT", NULL };
static TYPELIB global_plugin_typelib=
{ array_elements(global_plugin_typelib_names)-1,
"", global_plugin_typelib_names, NULL };
@@ -39,7 +57,7 @@ static TYPELIB global_plugin_typelib=
char *opt_plugin_load= NULL;
char *opt_plugin_dir_ptr;
char opt_plugin_dir[FN_REFLEN];
-uint plugin_maturity;
+ulong plugin_maturity;
/*
not really needed now, this map will become essential when we add more
@@ -70,6 +88,9 @@ const LEX_STRING plugin_type_names[MYSQL_MAX_PLUGIN_TYPE_NUM]=
extern int initialize_schema_table(st_plugin_int *plugin);
extern int finalize_schema_table(st_plugin_int *plugin);
+extern int initialize_audit_plugin(st_plugin_int *plugin);
+extern int finalize_audit_plugin(st_plugin_int *plugin);
+
/*
The number of elements in both plugin_type_initialize and
plugin_type_deinitialize should equal to the number of plugins
@@ -77,12 +98,14 @@ extern int finalize_schema_table(st_plugin_int *plugin);
*/
plugin_type_init plugin_type_initialize[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{
- 0,ha_initialize_handlerton,0,0,initialize_schema_table, 0, 0, 0
+ 0,ha_initialize_handlerton,0,0,initialize_schema_table,
+ initialize_audit_plugin, 0, 0
};
plugin_type_init plugin_type_deinitialize[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{
- 0,ha_finalize_handlerton,0,0,finalize_schema_table, 0, 0, 0
+ 0,ha_finalize_handlerton,0,0,finalize_schema_table,
+ finalize_audit_plugin, 0, 0
};
#ifdef HAVE_DLOPEN
@@ -112,9 +135,9 @@ static int min_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
MYSQL_FTPARSER_INTERFACE_VERSION,
MYSQL_DAEMON_INTERFACE_VERSION,
MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION,
- 0xff00, /* audit plugins are supported in a later versions */
- 0xff00, /* replication plugins are supported in a later versions */
- MYSQL_AUTHENTICATION_INTERFACE_VERSION
+ MYSQL_AUDIT_INTERFACE_VERSION,
+ MYSQL_REPLICATION_INTERFACE_VERSION,
+ MIN_AUTHENTICATION_INTERFACE_VERSION
};
static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{
@@ -123,11 +146,39 @@ static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
MYSQL_FTPARSER_INTERFACE_VERSION,
MYSQL_DAEMON_INTERFACE_VERSION,
MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION,
- 0x0000, /* audit plugins are supported in a later versions */
- 0x0000, /* replication plugins are supported in a later versions */
+ MYSQL_AUDIT_INTERFACE_VERSION,
+ MYSQL_REPLICATION_INTERFACE_VERSION,
MYSQL_AUTHENTICATION_INTERFACE_VERSION
};
+static struct
+{
+ const char *plugin_name;
+ enum enum_plugin_load_option override;
+} override_plugin_load_policy[]={
+ /*
+ If the performance schema is compiled in,
+ treat the storage engine plugin as 'mandatory',
+ to suppress any plugin-level options such as '--performance-schema'.
+ This is specific to the performance schema, and is done on purpose:
+ the server-level option '--performance-schema' controls the overall
+ performance schema initialization, which consists of much more that
+ the underlying storage engine initialization.
+ See mysqld.cc, set_vars.cc.
+ Suppressing ways to interfere directly with the storage engine alone
+ prevents awkward situations where:
+ - the user wants the performance schema functionality, by using
+ '--enable-performance-schema' (the server option),
+ - yet disable explicitly a component needed for the functionality
+ to work, by using '--skip-performance-schema' (the plugin)
+ */
+ { "performance_schema", PLUGIN_FORCE },
+
+ /* we disable few other plugins by default */
+ { "ndbcluster", PLUGIN_OFF },
+ { "feedback", PLUGIN_OFF }
+};
+
/* support for Services */
#include "sql_plugin_services.h"
@@ -137,10 +188,11 @@ static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
following variables/structures.
We are always manipulating ref count, so a rwlock here is unneccessary.
*/
-pthread_mutex_t LOCK_plugin;
+mysql_mutex_t LOCK_plugin;
static DYNAMIC_ARRAY plugin_dl_array;
static DYNAMIC_ARRAY plugin_array;
static HASH plugin_hash[MYSQL_MAX_PLUGIN_TYPE_NUM];
+static MEM_ROOT plugin_mem_root;
static bool reap_needed= false;
static int plugin_array_version=0;
@@ -150,7 +202,7 @@ static bool initialized= 0;
write-lock on LOCK_system_variables_hash is required before modifying
the following variables/structures
*/
-static MEM_ROOT plugin_mem_root;
+static MEM_ROOT plugin_vars_mem_root;
static uint global_variables_dynamic_size= 0;
static HASH bookmark_hash;
@@ -193,6 +245,8 @@ struct st_mysql_sys_var
MYSQL_PLUGIN_VAR_HEADER;
};
+static SHOW_TYPE pluginvar_show_type(st_mysql_sys_var *plugin_var);
+
/*
sys_var class for access to all plugin variables visible to the user
@@ -202,35 +256,50 @@ class sys_var_pluginvar: public sys_var
public:
struct st_plugin_int *plugin;
struct st_mysql_sys_var *plugin_var;
+ /**
+ variable name from whatever is hard-coded in the plugin source
+ and doesn't have pluginname- prefix is replaced by an allocated name
+ with a plugin prefix. When plugin is uninstalled we need to restore the
+ pointer to point to the hard-coded value, because plugin may be
+ installed/uninstalled many times without reloading the shared object.
+ */
+ const char *orig_pluginvar_name;
static void *operator new(size_t size, MEM_ROOT *mem_root)
- { return (void*) alloc_root(mem_root, (uint) size); }
+ { return (void*) alloc_root(mem_root, size); }
static void operator delete(void *ptr_arg,size_t size)
{ TRASH(ptr_arg, size); }
- sys_var_pluginvar(const char *name_arg,
+ sys_var_pluginvar(sys_var_chain *chain, const char *name_arg,
struct st_mysql_sys_var *plugin_var_arg)
- :sys_var(name_arg), plugin_var(plugin_var_arg) {}
+ :sys_var(chain, name_arg, plugin_var_arg->comment,
+ (plugin_var_arg->flags & PLUGIN_VAR_THDLOCAL ? SESSION : GLOBAL) |
+ (plugin_var_arg->flags & PLUGIN_VAR_READONLY ? READONLY : 0),
+ 0, -1, NO_ARG, pluginvar_show_type(plugin_var_arg), 0, 0,
+ VARIABLE_NOT_IN_BINLOG, NULL, NULL, NULL),
+ plugin_var(plugin_var_arg), orig_pluginvar_name(plugin_var_arg->name)
+ { plugin_var->name= name_arg; }
sys_var_pluginvar *cast_pluginvar() { return this; }
- bool is_readonly() const { return plugin_var->flags & PLUGIN_VAR_READONLY; }
- bool check_type(enum_var_type type)
- { return !(plugin_var->flags & PLUGIN_VAR_THDLOCAL) && type != OPT_GLOBAL; }
bool check_update_type(Item_result type);
SHOW_TYPE show_type();
uchar* real_value_ptr(THD *thd, enum_var_type type);
TYPELIB* plugin_var_typelib(void);
- uchar* value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
- bool check(THD *thd, set_var *var);
- bool check_default(enum_var_type type) { return is_readonly(); }
- void set_default(THD *thd, enum_var_type type);
- bool update(THD *thd, set_var *var);
+ uchar* do_value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+ uchar* session_value_ptr(THD *thd, LEX_STRING *base)
+ { return do_value_ptr(thd, OPT_SESSION, base); }
+ uchar* global_value_ptr(THD *thd, LEX_STRING *base)
+ { return do_value_ptr(thd, OPT_GLOBAL, base); }
+ bool do_check(THD *thd, set_var *var);
+ virtual void session_save_default(THD *thd, set_var *var) {}
+ virtual void global_save_default(THD *thd, set_var *var) {}
+ bool session_update(THD *thd, set_var *var);
+ bool global_update(THD *thd, set_var *var);
};
/* prototypes */
-static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv);
-static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv,
- const char *list);
+static void plugin_load(MEM_ROOT *tmp_root);
+static bool plugin_load_list(MEM_ROOT *, const char *);
static int test_plugin_options(MEM_ROOT *, struct st_plugin_int *,
int *, char **);
static bool register_builtin(struct st_maria_plugin *, struct st_plugin_int *,
@@ -238,29 +307,19 @@ static bool register_builtin(struct st_maria_plugin *, struct st_plugin_int *,
static void unlock_variables(THD *thd, struct system_variables *vars);
static void cleanup_variables(THD *thd, struct system_variables *vars);
static void plugin_vars_free_values(sys_var *vars);
-static void plugin_opt_set_limits(struct my_option *options,
- const struct st_mysql_sys_var *opt);
-#define my_intern_plugin_lock(A,B) intern_plugin_lock(A,B CALLER_INFO)
-#define my_intern_plugin_lock_ci(A,B) intern_plugin_lock(A,B ORIG_CALLER_INFO)
-static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref plugin
- CALLER_INFO_PROTO);
+static void restore_pluginvar_names(sys_var *first);
+static void plugin_opt_set_limits(struct my_option *,
+ const struct st_mysql_sys_var *);
+#define my_intern_plugin_lock(A,B) intern_plugin_lock(A,B)
+#define my_intern_plugin_lock_ci(A,B) intern_plugin_lock(A,B)
+static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref plugin);
static void intern_plugin_unlock(LEX *lex, plugin_ref plugin);
static void reap_plugins(void);
-
-/* declared in set_var.cc */
-extern sys_var *intern_find_sys_var(const char *str, uint length, bool no_error);
-extern bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd,
- const char *name, longlong val);
-
-#ifdef EMBEDDED_LIBRARY
-/* declared in sql_base.cc */
-extern bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists);
-#endif /* EMBEDDED_LIBRARY */
-
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);
@@ -270,7 +329,7 @@ static void report_error(int where_to, uint error, ...)
if (where_to & REPORT_TO_LOG)
{
va_start(args, error);
- error_log_print(ERROR_LEVEL, ER(error), args);
+ error_log_print(ERROR_LEVEL, ER_DEFAULT(error), args);
va_end(args);
}
}
@@ -294,6 +353,20 @@ bool check_valid_path(const char *path, size_t len)
return prefix < len;
}
+static void fix_dl_name(MEM_ROOT *root, LEX_STRING *dl)
+{
+ const size_t so_ext_len= sizeof(SO_EXT) - 1;
+ if (my_strcasecmp(&my_charset_latin1, dl->str + dl->length - so_ext_len,
+ SO_EXT))
+ {
+ char *s= (char*)alloc_root(root, dl->length + so_ext_len + 1);
+ memcpy(s, dl->str, dl->length);
+ strcpy(s + dl->length, SO_EXT);
+ dl->str= s;
+ dl->length+= so_ext_len;
+ }
+}
+
/****************************************************************************
Value type thunks, allows the C world to play in the C++ world
@@ -338,6 +411,11 @@ static int item_val_int(struct st_mysql_value *value, long long *buf)
return 0;
}
+static int item_is_unsigned(struct st_mysql_value *value)
+{
+ Item *item= ((st_item_value_holder*)value)->item;
+ return item->unsigned_flag;
+}
static int item_val_real(struct st_mysql_value *value, double *buf)
{
@@ -404,9 +482,9 @@ static inline void free_plugin_mem(struct st_plugin_dl *p)
if (p->handle)
dlclose(p->handle);
#endif
- my_free(p->dl.str, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(p->dl.str);
if (p->allocated)
- my_free((uchar*)p->plugins, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(p->plugins);
}
@@ -481,7 +559,8 @@ static my_bool read_mysql_plugin_info(struct st_plugin_dl *plugin_dl,
if (!cur)
{
free_plugin_mem(plugin_dl);
- report_error(report, ER_OUTOFMEMORY, (int) plugin_dl->dl.length);
+ report_error(report, ER_OUTOFMEMORY,
+ static_cast<int>(plugin_dl->dl.length));
DBUG_RETURN(TRUE);
}
/*
@@ -566,7 +645,7 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl,
(plugin_dl->mariaversion >> 8) > (MARIA_PLUGIN_INTERFACE_VERSION >> 8))
{
free_plugin_mem(plugin_dl);
- report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, 0,
+ report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, ENOEXEC,
"plugin interface version mismatch");
DBUG_RETURN(TRUE);
}
@@ -606,7 +685,8 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl,
if (!cur)
{
free_plugin_mem(plugin_dl);
- report_error(report, ER_OUTOFMEMORY, (int) plugin_dl->dl.length);
+ report_error(report, ER_OUTOFMEMORY,
+ static_cast<int>(plugin_dl->dl.length));
DBUG_RETURN(TRUE);
}
/*
@@ -622,6 +702,8 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl,
sym= cur;
plugin_dl->allocated= true;
}
+ else
+ sym= ptr;
}
plugin_dl->plugins= (struct st_maria_plugin *)sym;
@@ -661,14 +743,14 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
}
bzero(&plugin_dl, sizeof(plugin_dl));
/* Compile dll path */
- dlpathlen=
- strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", dl->str, NullS) -
- dlpath;
+ strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", dl->str, NullS);
+ (void) unpack_filename(dlpath, dlpath);
plugin_dl.ref_count= 1;
/* Open new dll handle */
if (!(plugin_dl.handle= dlopen(dlpath, RTLD_NOW)))
{
const char *errmsg=dlerror();
+ dlpathlen= strlen(dlpath);
if (!strncmp(dlpath, errmsg, dlpathlen))
{ // if errmsg starts from dlpath, trim this prefix.
errmsg+=dlpathlen;
@@ -700,7 +782,7 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
{
if ((sym= dlsym(plugin_dl.handle, list_of_services[i].name)))
{
- uint ver= (uint)(intptr) *(void **)sym;
+ uint ver= (uint)(intptr)*(void**)sym;
if (ver > list_of_services[i].version ||
(ver >> 8) < (list_of_services[i].version >> 8))
{
@@ -708,10 +790,10 @@ 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, 0, buf);
+ report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, ENOEXEC, buf);
DBUG_RETURN(0);
}
- *(void **)sym= list_of_services[i].service;
+ *(void**)sym= list_of_services[i].service;
}
}
@@ -720,7 +802,8 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
if (! (plugin_dl.dl.str= (char*) my_malloc(plugin_dl.dl.length, MYF(0))))
{
free_plugin_mem(&plugin_dl);
- report_error(report, ER_OUTOFMEMORY, (int) plugin_dl.dl.length);
+ report_error(report, ER_OUTOFMEMORY,
+ static_cast<int>(plugin_dl.dl.length));
DBUG_RETURN(0);
}
plugin_dl.dl.length= copy_and_convert(plugin_dl.dl.str, plugin_dl.dl.length,
@@ -731,7 +814,8 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
if (! (tmp= plugin_dl_insert_or_reuse(&plugin_dl)))
{
free_plugin_mem(&plugin_dl);
- report_error(report, ER_OUTOFMEMORY, (int) sizeof(struct st_plugin_dl));
+ report_error(report, ER_OUTOFMEMORY,
+ static_cast<int>(sizeof(struct st_plugin_dl)));
DBUG_RETURN(0);
}
DBUG_RETURN(tmp);
@@ -749,7 +833,7 @@ static void plugin_dl_del(const LEX_STRING *dl)
uint i;
DBUG_ENTER("plugin_dl_del");
- safe_mutex_assert_owner(&LOCK_plugin);
+ mysql_mutex_assert_owner(&LOCK_plugin);
for (i= 0; i < plugin_dl_array.elements; i++)
{
@@ -781,21 +865,22 @@ static struct st_plugin_int *plugin_find_internal(const LEX_STRING *name, int ty
if (! initialized)
DBUG_RETURN(0);
- safe_mutex_assert_owner(&LOCK_plugin);
+ mysql_mutex_assert_owner(&LOCK_plugin);
if (type == MYSQL_ANY_PLUGIN)
{
for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
{
struct st_plugin_int *plugin= (st_plugin_int *)
- hash_search(&plugin_hash[i], (const uchar *)name->str, name->length);
+ my_hash_search(&plugin_hash[i], (const uchar *)name->str, name->length);
if (plugin)
DBUG_RETURN(plugin);
}
}
else
DBUG_RETURN((st_plugin_int *)
- hash_search(&plugin_hash[type], (const uchar *)name->str, name->length));
+ my_hash_search(&plugin_hash[type], (const uchar *)name->str,
+ name->length));
DBUG_RETURN(0);
}
@@ -805,14 +890,14 @@ static SHOW_COMP_OPTION plugin_status(const LEX_STRING *name, int type)
SHOW_COMP_OPTION rc= SHOW_OPTION_NO;
struct st_plugin_int *plugin;
DBUG_ENTER("plugin_is_ready");
- pthread_mutex_lock(&LOCK_plugin);
+ mysql_mutex_lock(&LOCK_plugin);
if ((plugin= plugin_find_internal(name, type)))
{
rc= SHOW_OPTION_DISABLED;
if (plugin->state == PLUGIN_IS_READY)
rc= SHOW_OPTION_YES;
}
- pthread_mutex_unlock(&LOCK_plugin);
+ mysql_mutex_unlock(&LOCK_plugin);
DBUG_RETURN(rc);
}
@@ -826,19 +911,19 @@ bool plugin_is_ready(const LEX_STRING *name, int type)
}
-SHOW_COMP_OPTION sys_var_have_plugin::get_option()
+SHOW_COMP_OPTION plugin_status(const char *name, size_t len, int type)
{
- LEX_STRING plugin_name= { (char *) plugin_name_str, plugin_name_len };
- return plugin_status(&plugin_name, plugin_type);
+ LEX_STRING plugin_name= { (char *) name, len };
+ return plugin_status(&plugin_name, type);
}
-static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc CALLER_INFO_PROTO)
+static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc)
{
st_plugin_int *pi= plugin_ref_to_int(rc);
DBUG_ENTER("intern_plugin_lock");
- safe_mutex_assert_owner(&LOCK_plugin);
+ mysql_mutex_assert_owner(&LOCK_plugin);
if (pi->state & (PLUGIN_IS_READY | PLUGIN_IS_UNINITIALIZED))
{
@@ -858,7 +943,7 @@ static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc CALLER_INFO_PROTO)
memory manager and/or valgrind to track locked references and
double unlocks to aid resolving reference counting problems.
*/
- if (!(plugin= (plugin_ref) my_malloc_ci(sizeof(pi), MYF(MY_WME))))
+ if (!(plugin= (plugin_ref) my_malloc(sizeof(pi), MYF(MY_WME))))
DBUG_RETURN(NULL);
*plugin= pi;
@@ -875,7 +960,7 @@ static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc CALLER_INFO_PROTO)
}
-plugin_ref plugin_lock(THD *thd, plugin_ref ptr CALLER_INFO_PROTO)
+plugin_ref plugin_lock(THD *thd, plugin_ref ptr)
{
LEX *lex= thd ? thd->lex : 0;
plugin_ref rc;
@@ -898,26 +983,29 @@ plugin_ref plugin_lock(THD *thd, plugin_ref ptr CALLER_INFO_PROTO)
without a mutex.
*/
if (! plugin_dlib(ptr))
+ {
+ plugin_ref_to_int(ptr)->locks_total++;
DBUG_RETURN(ptr);
+ }
#endif
- pthread_mutex_lock(&LOCK_plugin);
+ mysql_mutex_lock(&LOCK_plugin);
+ plugin_ref_to_int(ptr)->locks_total++;
rc= my_intern_plugin_lock_ci(lex, ptr);
- pthread_mutex_unlock(&LOCK_plugin);
+ mysql_mutex_unlock(&LOCK_plugin);
DBUG_RETURN(rc);
}
-plugin_ref plugin_lock_by_name(THD *thd, const LEX_STRING *name, int type
- CALLER_INFO_PROTO)
+plugin_ref plugin_lock_by_name(THD *thd, const LEX_STRING *name, int type)
{
LEX *lex= thd ? thd->lex : 0;
plugin_ref rc= NULL;
st_plugin_int *plugin;
DBUG_ENTER("plugin_lock_by_name");
- pthread_mutex_lock(&LOCK_plugin);
+ mysql_mutex_lock(&LOCK_plugin);
if ((plugin= plugin_find_internal(name, type)))
rc= my_intern_plugin_lock_ci(lex, plugin_int_to_ref(plugin));
- pthread_mutex_unlock(&LOCK_plugin);
+ mysql_mutex_unlock(&LOCK_plugin);
DBUG_RETURN(rc);
}
@@ -951,31 +1039,39 @@ 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, const LEX_STRING *dl,
- int *argc, char **argv, int report)
+ const LEX_STRING *name, LEX_STRING *dl, int report)
{
struct st_plugin_int tmp;
struct st_maria_plugin *plugin;
+ uint oks= 0, errs= 0;
DBUG_ENTER("plugin_add");
- if (plugin_find_internal(name, MYSQL_ANY_PLUGIN))
+ if (name->str && plugin_find_internal(name, MYSQL_ANY_PLUGIN))
{
report_error(report, ER_UDF_EXISTS, 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)))
DBUG_RETURN(TRUE);
/* Find plugin by name */
for (plugin= tmp.plugin_dl->plugins; plugin->info; plugin++)
{
- uint name_len= strlen(plugin->name);
- if (plugin->type >= 0 && plugin->type < MYSQL_MAX_PLUGIN_TYPE_NUM &&
- ! my_strnncoll(system_charset_info,
- (const uchar *)name->str, name->length,
- (const uchar *)plugin->name,
- name_len))
- {
+ tmp.name.str= (char *)plugin->name;
+ tmp.name.length= strlen(plugin->name);
+
+ if (plugin->type < 0 || plugin->type >= MYSQL_MAX_PLUGIN_TYPE_NUM)
+ continue; // invalid plugin
+
+ if (name->str && my_strnncoll(system_charset_info,
+ (const uchar *)name->str, name->length,
+ (const uchar *)tmp.name.str, tmp.name.length))
+ continue; // plugin name doesn't match
+
+ if (!name->str && plugin_find_internal(&tmp.name, MYSQL_ANY_PLUGIN))
+ continue; // already installed
+
struct st_plugin_int *tmp_plugin_ptr;
if (*(int*)plugin->info <
min_plugin_info_interface_version[plugin->type] ||
@@ -985,8 +1081,9 @@ static bool plugin_add(MEM_ROOT *tmp_root,
char buf[256];
strxnmov(buf, sizeof(buf) - 1, "API version for ",
plugin_type_names[plugin->type].str,
- " plugin is too different", NullS);
- report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, 0, buf);
+ " plugin ", tmp.name.str,
+ " not supported by this version of the server", NullS);
+ report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, ENOEXEC, buf);
goto err;
}
if (plugin_maturity_map[plugin->maturity] < plugin_maturity)
@@ -994,42 +1091,43 @@ static bool plugin_add(MEM_ROOT *tmp_root,
char buf[256];
strxnmov(buf, sizeof(buf) - 1, "Loading of ",
plugin_maturity_names[plugin->maturity],
- " plugins is prohibited by --plugin-maturity=",
+ " plugin ", tmp.name.str,
+ " is prohibited by --plugin-maturity=",
plugin_maturity_names[plugin_maturity],
NullS);
- report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, 0, buf);
+ report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, EPERM, buf);
goto err;
}
tmp.plugin= plugin;
- tmp.name.str= (char *)plugin->name;
- tmp.name.length= name_len;
tmp.ref_count= 0;
tmp.state= PLUGIN_IS_UNINITIALIZED;
- if (test_plugin_options(tmp_root, &tmp, argc, argv))
- tmp.state= PLUGIN_IS_DISABLED;
+ tmp.load_option= PLUGIN_ON;
- if ((tmp_plugin_ptr= plugin_insert_or_reuse(&tmp)))
- {
- plugin_array_version++;
- if (!my_hash_insert(&plugin_hash[plugin->type], (uchar*)tmp_plugin_ptr))
- {
- init_alloc_root(&tmp_plugin_ptr->mem_root, 4096, 4096);
- DBUG_RETURN(FALSE);
- }
+ if (!(tmp_plugin_ptr= plugin_insert_or_reuse(&tmp)))
+ goto err;
+ plugin_array_version++;
+ if (my_hash_insert(&plugin_hash[plugin->type], (uchar*)tmp_plugin_ptr))
tmp_plugin_ptr->state= PLUGIN_IS_FREED;
- }
- mysql_del_sys_var_chain(tmp.system_vars);
- goto err;
+ init_alloc_root(&tmp_plugin_ptr->mem_root, 4096, 4096);
+
+ if (name->str)
+ DBUG_RETURN(FALSE); // all done
+
+ oks++;
+ tmp.plugin_dl->ref_count++;
+ continue; // otherwise - go on
- /* plugin was disabled */
- plugin_dl_del(dl);
- DBUG_RETURN(FALSE);
- }
- }
- report_error(report, ER_CANT_FIND_DL_ENTRY, name->str);
err:
+ errs++;
+ if (name->str)
+ break;
+ }
+
+ if (errs == 0 && oks == 0) // no plugin was found
+ report_error(report, ER_CANT_FIND_DL_ENTRY, name->str);
+
plugin_dl_del(dl);
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(errs > 0 || oks == 0);
}
@@ -1040,17 +1138,17 @@ static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check)
deinitialization to deadlock if plugins have worker threads
with plugin locks
*/
- safe_mutex_assert_not_owner(&LOCK_plugin);
+ mysql_mutex_assert_not_owner(&LOCK_plugin);
if (plugin->plugin->status_vars)
{
#ifdef FIX_LATER
- /*
- We have a problem right now where we can not prepend without
- breaking backwards compatibility. We will fix this shortly so
- that engines have "use names" and we wil use those for
- CREATE TABLE, and use the plugin name then for adding automatic
- variable names.
+ /**
+ @todo
+ unfortunately, status variables were introduced without a
+ pluginname_ namespace, that is pluginname_ was not added automatically
+ to status variable names. It should be fixed together with the next
+ incompatible API change.
*/
SHOW_VAR array[2]= {
{plugin->plugin->name, (char*)plugin->plugin->status_vars, SHOW_ARRAY},
@@ -1081,6 +1179,10 @@ static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check)
}
plugin->state= PLUGIN_IS_UNINITIALIZED;
+ /* maintain the obsolete @@have_innodb variable */
+ if (!my_strcasecmp(&my_charset_latin1, plugin->name.str, "InnoDB"))
+ have_innodb= SHOW_OPTION_DISABLED;
+
/*
We do the check here because NDB has a worker THD which doesn't
exit until NDB is shut down.
@@ -1088,46 +1190,31 @@ static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check)
if (ref_check && plugin->ref_count)
sql_print_error("Plugin '%s' has ref_count=%d after deinitialization.",
plugin->name.str, plugin->ref_count);
-}
+ restore_pluginvar_names(plugin->system_vars);
+}
static void plugin_del(struct st_plugin_int *plugin)
{
- DBUG_ENTER("plugin_del(plugin)");
- safe_mutex_assert_owner(&LOCK_plugin);
+ DBUG_ENTER("plugin_del");
+ mysql_mutex_assert_owner(&LOCK_plugin);
/* Free allocated strings before deleting the plugin. */
plugin_vars_free_values(plugin->system_vars);
- hash_delete(&plugin_hash[plugin->plugin->type], (uchar*)plugin);
+ my_hash_delete(&plugin_hash[plugin->plugin->type], (uchar*)plugin);
if (plugin->plugin_dl)
plugin_dl_del(&plugin->plugin_dl->dl);
plugin->state= PLUGIN_IS_FREED;
plugin_array_version++;
- rw_wrlock(&LOCK_system_variables_hash);
- mysql_del_sys_var_chain(plugin->system_vars);
- rw_unlock(&LOCK_system_variables_hash);
free_root(&plugin->mem_root, MYF(0));
DBUG_VOID_RETURN;
}
-#ifdef NOT_USED
-
-static void plugin_del(const LEX_STRING *name)
-{
- struct st_plugin_int *plugin;
- DBUG_ENTER("plugin_del(name)");
- if ((plugin= plugin_find_internal(name, MYSQL_ANY_PLUGIN)))
- plugin_del(plugin);
- DBUG_VOID_RETURN;
-}
-
-#endif
-
static void reap_plugins(void)
{
uint count, idx;
struct st_plugin_int *plugin, **reap, **list;
- safe_mutex_assert_owner(&LOCK_plugin);
+ mysql_mutex_assert_owner(&LOCK_plugin);
if (!reap_needed)
return;
@@ -1148,13 +1235,13 @@ static void reap_plugins(void)
}
}
- pthread_mutex_unlock(&LOCK_plugin);
+ mysql_mutex_unlock(&LOCK_plugin);
list= reap;
while ((plugin= *(--list)))
plugin_deinitialize(plugin, true);
- pthread_mutex_lock(&LOCK_plugin);
+ mysql_mutex_lock(&LOCK_plugin);
while ((plugin= *(--reap)))
plugin_del(plugin);
@@ -1168,7 +1255,7 @@ static void intern_plugin_unlock(LEX *lex, plugin_ref plugin)
st_plugin_int *pi;
DBUG_ENTER("intern_plugin_unlock");
- safe_mutex_assert_owner(&LOCK_plugin);
+ mysql_mutex_assert_owner(&LOCK_plugin);
if (!plugin)
DBUG_VOID_RETURN;
@@ -1179,7 +1266,7 @@ static void intern_plugin_unlock(LEX *lex, plugin_ref plugin)
if (!pi->plugin_dl)
DBUG_VOID_RETURN;
#else
- my_free((uchar*) plugin, MYF(MY_WME));
+ my_free(plugin);
#endif
if (lex)
@@ -1222,10 +1309,10 @@ void plugin_unlock(THD *thd, plugin_ref plugin)
if (!plugin_dlib(plugin))
DBUG_VOID_RETURN;
#endif
- pthread_mutex_lock(&LOCK_plugin);
+ mysql_mutex_lock(&LOCK_plugin);
intern_plugin_unlock(lex, plugin);
reap_plugins();
- pthread_mutex_unlock(&LOCK_plugin);
+ mysql_mutex_unlock(&LOCK_plugin);
DBUG_VOID_RETURN;
}
@@ -1238,26 +1325,48 @@ void plugin_unlock_list(THD *thd, plugin_ref *list, uint count)
DBUG_VOID_RETURN;
DBUG_ASSERT(list);
- pthread_mutex_lock(&LOCK_plugin);
+ mysql_mutex_lock(&LOCK_plugin);
while (count--)
intern_plugin_unlock(lex, *list++);
reap_plugins();
- pthread_mutex_unlock(&LOCK_plugin);
+ mysql_mutex_unlock(&LOCK_plugin);
DBUG_VOID_RETURN;
}
-static int plugin_initialize(struct st_plugin_int *plugin)
+static int plugin_initialize(MEM_ROOT *tmp_root, struct st_plugin_int *plugin,
+ int *argc, char **argv, bool options_only)
{
int ret= 1;
- uint state;
DBUG_ENTER("plugin_initialize");
- safe_mutex_assert_owner(&LOCK_plugin);
- state= plugin->state;
+ mysql_mutex_assert_owner(&LOCK_plugin);
+ uint state= plugin->state;
DBUG_ASSERT(state == PLUGIN_IS_UNINITIALIZED);
- pthread_mutex_unlock(&LOCK_plugin);
+ mysql_mutex_unlock(&LOCK_plugin);
+
+ mysql_rwlock_wrlock(&LOCK_system_variables_hash);
+ if (test_plugin_options(tmp_root, plugin, argc, argv))
+ state= PLUGIN_IS_DISABLED;
+ mysql_rwlock_unlock(&LOCK_system_variables_hash);
+
+ if (options_only || state == PLUGIN_IS_DISABLED)
+ {
+ ret= 0;
+ goto err;
+ }
+
+ if (plugin->plugin_dl && global_system_variables.log_warnings >= 9)
+ {
+ void *sym= dlsym(plugin->plugin_dl->handle,
+ plugin->plugin_dl->mariaversion ?
+ maria_plugin_declarations_sym : plugin_declarations_sym);
+ DBUG_ASSERT(sym);
+ sql_print_information("Plugin %s loaded at %p",
+ plugin->name.str, sym);
+ }
+
if (plugin_type_initialize[plugin->plugin->type])
{
if ((*plugin_type_initialize[plugin->plugin->type])(plugin))
@@ -1319,8 +1428,17 @@ static int plugin_initialize(struct st_plugin_int *plugin)
ret= 0;
err:
- pthread_mutex_lock(&LOCK_plugin);
+ if (ret)
+ restore_pluginvar_names(plugin->system_vars);
+
+ mysql_mutex_lock(&LOCK_plugin);
plugin->state= state;
+
+ /* maintain the obsolete @@have_innodb variable */
+ if (!my_strcasecmp(&my_charset_latin1, plugin->name.str, "InnoDB"))
+ have_innodb= state & PLUGIN_IS_READY ? SHOW_OPTION_YES
+ : SHOW_OPTION_DISABLED;
+
DBUG_RETURN(ret);
}
@@ -1360,6 +1478,26 @@ static inline void convert_underscore_to_dash(char *str, int len)
*p= '-';
}
+#ifdef HAVE_PSI_INTERFACE
+static PSI_mutex_key key_LOCK_plugin;
+
+static PSI_mutex_info all_plugin_mutexes[]=
+{
+ { &key_LOCK_plugin, "LOCK_plugin", PSI_FLAG_GLOBAL}
+};
+
+static void init_plugin_psi_keys(void)
+{
+ const char* category= "sql";
+ int count;
+
+ if (PSI_server == NULL)
+ return;
+
+ count= array_elements(all_plugin_mutexes);
+ PSI_server->register_mutex(category, all_plugin_mutexes, count);
+}
+#endif /* HAVE_PSI_INTERFACE */
/*
The logic is that we first load and initialize all compiled in plugins.
@@ -1376,21 +1514,27 @@ int plugin_init(int *argc, char **argv, int flags)
struct st_maria_plugin *plugin;
struct st_plugin_int tmp, *plugin_ptr, **reap;
MEM_ROOT tmp_root;
- bool reaped_mandatory_plugin= FALSE;
+ bool reaped_mandatory_plugin= false;
+ bool mandatory= true;
DBUG_ENTER("plugin_init");
if (initialized)
DBUG_RETURN(0);
+#ifdef HAVE_PSI_INTERFACE
+ init_plugin_psi_keys();
+#endif
+
init_alloc_root(&plugin_mem_root, 4096, 4096);
+ init_alloc_root(&plugin_vars_mem_root, 4096, 4096);
init_alloc_root(&tmp_root, 4096, 4096);
- if (hash_init(&bookmark_hash, &my_charset_bin, 16, 0, 0,
- get_bookmark_hash_key, NULL, HASH_UNIQUE))
+ if (my_hash_init(&bookmark_hash, &my_charset_bin, 16, 0, 0,
+ get_bookmark_hash_key, NULL, HASH_UNIQUE))
goto err;
- pthread_mutex_init(&LOCK_plugin, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_plugin, &LOCK_plugin, MY_MUTEX_INIT_FAST);
if (my_init_dynamic_array(&plugin_dl_array,
sizeof(struct st_plugin_dl *),16,16) ||
@@ -1400,20 +1544,31 @@ int plugin_init(int *argc, char **argv, int flags)
for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
{
- if (hash_init(&plugin_hash[i], system_charset_info, 16, 0, 0,
- get_plugin_hash_key, NULL, HASH_UNIQUE))
+ if (my_hash_init(&plugin_hash[i], system_charset_info, 16, 0, 0,
+ get_plugin_hash_key, NULL, HASH_UNIQUE))
goto err;
}
- pthread_mutex_lock(&LOCK_plugin);
+ /* prepare debug_sync service */
+ DBUG_ASSERT(strcmp(list_of_services[5].name, "debug_sync_service") == 0);
+ list_of_services[5].service= *(void**)&debug_sync_C_callback_ptr;
+
+ mysql_mutex_lock(&LOCK_plugin);
initialized= 1;
/*
First we register builtin plugins
*/
- for (builtins= mariadb_builtins; *builtins; builtins++)
+ for (builtins= mysql_mandatory_plugins; *builtins || mandatory; builtins++)
{
+ if (!*builtins)
+ {
+ builtins= mysql_optional_plugins;
+ mandatory= false;
+ if (!*builtins)
+ break;
+ }
for (plugin= *builtins; plugin->info; plugin++)
{
if (opt_ignore_builtin_innodb &&
@@ -1426,11 +1581,20 @@ int plugin_init(int *argc, char **argv, int flags)
tmp.name.str= (char *)plugin->name;
tmp.name.length= strlen(plugin->name);
tmp.state= 0;
+ tmp.load_option= mandatory ? PLUGIN_FORCE : PLUGIN_ON;
+
+ for (i=0; i < array_elements(override_plugin_load_policy); i++)
+ {
+ if (!my_strcasecmp(&my_charset_latin1, plugin->name,
+ override_plugin_load_policy[i].plugin_name))
+ {
+ tmp.load_option= override_plugin_load_policy[i].override;
+ break;
+ }
+ }
+
free_root(&tmp_root, MYF(MY_MARK_BLOCKS_FREE));
- if (test_plugin_options(&tmp_root, &tmp, argc, argv))
- tmp.state= PLUGIN_IS_DISABLED;
- else
- tmp.state= PLUGIN_IS_UNINITIALIZED;
+ tmp.state= PLUGIN_IS_UNINITIALIZED;
if (register_builtin(plugin, &tmp, &plugin_ptr))
goto err_unlock;
@@ -1445,11 +1609,12 @@ int plugin_init(int *argc, char **argv, int flags)
mysqld --help for all other users, we will only initialize
MyISAM here.
*/
- if (!(flags & PLUGIN_INIT_SKIP_INITIALIZATION) || is_myisam)
+ if (plugin_initialize(&tmp_root, plugin_ptr, argc, argv, !is_myisam &&
+ (flags & PLUGIN_INIT_SKIP_INITIALIZATION)))
{
- if (plugin_ptr->state == PLUGIN_IS_UNINITIALIZED &&
- plugin_initialize(plugin_ptr))
+ if (plugin_ptr->load_option == PLUGIN_FORCE)
goto err_unlock;
+ plugin_ptr->state= PLUGIN_IS_DISABLED;
}
/*
@@ -1469,34 +1634,32 @@ int plugin_init(int *argc, char **argv, int flags)
/* should now be set to MyISAM storage engine */
DBUG_ASSERT(global_system_variables.table_plugin);
- pthread_mutex_unlock(&LOCK_plugin);
+ mysql_mutex_unlock(&LOCK_plugin);
/* Register all dynamic plugins */
if (!(flags & PLUGIN_INIT_SKIP_DYNAMIC_LOADING))
{
if (opt_plugin_load)
- plugin_load_list(&tmp_root, argc, argv, opt_plugin_load);
+ plugin_load_list(&tmp_root, opt_plugin_load);
if (!(flags & PLUGIN_INIT_SKIP_PLUGIN_TABLE))
- plugin_load(&tmp_root, argc, argv);
+ plugin_load(&tmp_root);
}
- if (flags & PLUGIN_INIT_SKIP_INITIALIZATION)
- goto end;
-
/*
Now we initialize all remaining plugins
*/
- pthread_mutex_lock(&LOCK_plugin);
+ mysql_mutex_lock(&LOCK_plugin);
reap= (st_plugin_int **) my_alloca((plugin_array.elements+1) * sizeof(void*));
*(reap++)= NULL;
for (i= 0; i < plugin_array.elements; i++)
{
plugin_ptr= *dynamic_element(&plugin_array, i, struct st_plugin_int **);
- if (plugin_ptr->state == PLUGIN_IS_UNINITIALIZED)
+ if (plugin_ptr->plugin_dl && plugin_ptr->state == PLUGIN_IS_UNINITIALIZED)
{
- if (plugin_initialize(plugin_ptr))
+ if (plugin_initialize(&tmp_root, plugin_ptr, argc, argv,
+ (flags & PLUGIN_INIT_SKIP_INITIALIZATION)))
{
plugin_ptr->state= PLUGIN_IS_DYING;
*(reap++)= plugin_ptr;
@@ -1509,26 +1672,26 @@ int plugin_init(int *argc, char **argv, int flags)
*/
while ((plugin_ptr= *(--reap)))
{
- pthread_mutex_unlock(&LOCK_plugin);
- if (plugin_ptr->is_mandatory)
+ mysql_mutex_unlock(&LOCK_plugin);
+ if (plugin_ptr->load_option == PLUGIN_FORCE ||
+ plugin_ptr->load_option == PLUGIN_FORCE_PLUS_PERMANENT)
reaped_mandatory_plugin= TRUE;
plugin_deinitialize(plugin_ptr, true);
- pthread_mutex_lock(&LOCK_plugin);
+ mysql_mutex_lock(&LOCK_plugin);
plugin_del(plugin_ptr);
}
- pthread_mutex_unlock(&LOCK_plugin);
+ mysql_mutex_unlock(&LOCK_plugin);
my_afree(reap);
if (reaped_mandatory_plugin)
goto err;
-end:
free_root(&tmp_root, MYF(0));
DBUG_RETURN(0);
err_unlock:
- pthread_mutex_unlock(&LOCK_plugin);
+ mysql_mutex_unlock(&LOCK_plugin);
err:
free_root(&tmp_root, MYF(0));
DBUG_RETURN(1);
@@ -1558,94 +1721,47 @@ static bool register_builtin(struct st_maria_plugin *plugin,
}
-#ifdef NOT_USED_YET
-/*
- Register a plugin at run time. (note, this doesn't initialize a plugin)
- Will be useful for embedded applications.
-
- SYNOPSIS
- plugin_register_builtin()
- thd current thread (used to store scratch data in mem_root)
- plugin static plugin to install
-
- RETURN
- false - plugin registered successfully
-*/
-bool plugin_register_builtin(THD *thd, struct st_maria_plugin *plugin)
-{
- struct st_plugin_int tmp, *ptr;
- bool result= true;
- int dummy_argc= 0;
- DBUG_ENTER("plugin_register_builtin");
-
- bzero(&tmp, sizeof(tmp));
- tmp.plugin= plugin;
- tmp.name.str= (char *)plugin->name;
- tmp.name.length= strlen(plugin->name);
-
- pthread_mutex_lock(&LOCK_plugin);
- rw_wrlock(&LOCK_system_variables_hash);
-
- if (test_plugin_options(thd->mem_root, &tmp, &dummy_argc, NULL))
- goto end;
- tmp.state= PLUGIN_IS_UNINITIALIZED;
- if ((result= register_builtin(plugin, &tmp, &ptr)))
- mysql_del_sys_var_chain(tmp.system_vars);
-
-end:
- rw_unlock(&LOCK_system_variables_hash);
- pthread_mutex_unlock(&LOCK_plugin);
-
- DBUG_RETURN(result);;
-}
-#endif /* NOT_USED_YET */
-
-
/*
called only by plugin_init()
*/
-static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv)
+static void plugin_load(MEM_ROOT *tmp_root)
{
+ THD thd;
TABLE_LIST tables;
TABLE *table;
READ_RECORD read_record_info;
int error;
- THD *new_thd;
+ THD *new_thd= &thd;
+ bool result;
#ifdef EMBEDDED_LIBRARY
- bool table_exists;
+ No_such_table_error_handler error_handler;
#endif /* EMBEDDED_LIBRARY */
DBUG_ENTER("plugin_load");
- if (!(new_thd= new THD))
- {
- sql_print_error("Can't allocate memory for plugin structures");
- delete new_thd;
- DBUG_VOID_RETURN;
- }
new_thd->thread_stack= (char*) &tables;
new_thd->store_globals();
- lex_start(new_thd);
new_thd->db= my_strdup("mysql", MYF(0));
new_thd->db_length= 5;
- bzero((uchar*)&tables, sizeof(tables));
- tables.alias= tables.table_name= (char*)"plugin";
- tables.lock_type= TL_READ;
- tables.db= new_thd->db;
+ bzero((char*) &thd.net, sizeof(thd.net));
+ tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_READ);
#ifdef EMBEDDED_LIBRARY
/*
When building an embedded library, if the mysql.plugin table
does not exist, we silently ignore the missing table
*/
- pthread_mutex_lock(&LOCK_open);
- if (check_if_table_exists(new_thd, &tables, &table_exists))
- table_exists= FALSE;
- pthread_mutex_unlock(&LOCK_open);
- if (!table_exists)
+ new_thd->push_internal_handler(&error_handler);
+#endif /* EMBEDDED_LIBRARY */
+
+ result= open_and_lock_tables(new_thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT);
+
+#ifdef EMBEDDED_LIBRARY
+ new_thd->pop_internal_handler();
+ if (error_handler.safely_trapped_errors())
goto end;
#endif /* EMBEDDED_LIBRARY */
- if (simple_open_n_lock_tables(new_thd, &tables))
+ if (result)
{
DBUG_PRINT("error",("Can't open plugin table"));
if (!opt_help)
@@ -1676,23 +1792,22 @@ static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv)
/*
there're no other threads running yet, so we don't need a mutex.
but plugin_add() before is designed to work in multi-threaded
- environment, and it uses safe_mutex_assert_owner(), so we lock
+ environment, and it uses mysql_mutex_assert_owner(), so we lock
the mutex here to satisfy the assert
*/
- pthread_mutex_lock(&LOCK_plugin);
- if (plugin_add(tmp_root, &name, &dl, argc, argv, REPORT_TO_LOG))
+ mysql_mutex_lock(&LOCK_plugin);
+ if (plugin_add(tmp_root, &name, &dl, REPORT_TO_LOG))
sql_print_warning("Couldn't load plugin named '%s' with soname '%s'.",
str_name.c_ptr(), str_dl.c_ptr());
free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
- pthread_mutex_unlock(&LOCK_plugin);
+ mysql_mutex_unlock(&LOCK_plugin);
}
if (error > 0)
sql_print_error(ER(ER_GET_ERRNO), my_errno);
end_read_record(&read_record_info);
- new_thd->version--; // Force close to free memory
+ table->m_needs_reopen= TRUE; // Force close to free memory
+ close_mysql_tables(new_thd);
end:
- close_thread_tables(new_thd);
- delete new_thd;
/* Remember that we don't have a THD */
my_pthread_setspecific_ptr(THR_THD, 0);
DBUG_VOID_RETURN;
@@ -1702,13 +1817,10 @@ end:
/*
called only by plugin_init()
*/
-static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv,
- const char *list)
+static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list)
{
char buffer[FN_REFLEN];
LEX_STRING name= {buffer, 0}, dl= {NULL, 0}, *str= &name;
- struct st_plugin_dl *plugin_dl;
- struct st_maria_plugin *plugin;
char *p= buffer;
DBUG_ENTER("plugin_load_list");
while (list)
@@ -1737,29 +1849,20 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv,
}
dl= name;
- pthread_mutex_lock(&LOCK_plugin);
- if ((plugin_dl= plugin_dl_add(&dl, REPORT_TO_LOG)))
- {
- for (plugin= plugin_dl->plugins; plugin->info; plugin++)
- {
- name.str= (char *) plugin->name;
- name.length= strlen(name.str);
-
- free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
- if (plugin_add(tmp_root, &name, &dl, argc, argv, REPORT_TO_LOG))
- goto error;
- }
- plugin_dl_del(&dl); // reduce ref count
- }
+ 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))
+ goto error;
}
else
{
free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
- pthread_mutex_lock(&LOCK_plugin);
- if (plugin_add(tmp_root, &name, &dl, argc, argv, REPORT_TO_LOG))
+ mysql_mutex_lock(&LOCK_plugin);
+ if (plugin_add(tmp_root, &name, &dl, REPORT_TO_LOG))
goto error;
}
- pthread_mutex_unlock(&LOCK_plugin);
+ mysql_mutex_unlock(&LOCK_plugin);
name.length= dl.length= 0;
dl.str= NULL; name.str= p= buffer;
str= &name;
@@ -1780,9 +1883,12 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv,
}
DBUG_RETURN(FALSE);
error:
- pthread_mutex_unlock(&LOCK_plugin);
- sql_print_error("Couldn't load plugin named '%s' with soname '%s'.",
- name.str, dl.str);
+ mysql_mutex_unlock(&LOCK_plugin);
+ if (name.str)
+ sql_print_error("Couldn't load plugin '%s' from '%s'.",
+ name.str, dl.str);
+ else
+ sql_print_error("Couldn't load plugins from '%s'.", dl.str);
DBUG_RETURN(TRUE);
}
@@ -1796,7 +1902,7 @@ void plugin_shutdown(void)
if (initialized)
{
- pthread_mutex_lock(&LOCK_plugin);
+ mysql_mutex_lock(&LOCK_plugin);
reap_needed= true;
@@ -1841,7 +1947,7 @@ void plugin_shutdown(void)
if (plugins[i]->state == PLUGIN_IS_DELETED)
plugins[i]->state= PLUGIN_IS_DYING;
}
- pthread_mutex_unlock(&LOCK_plugin);
+ mysql_mutex_unlock(&LOCK_plugin);
/*
We loop through all plugins and call deinit() if they have one.
@@ -1862,9 +1968,9 @@ void plugin_shutdown(void)
/*
It's perfectly safe not to lock LOCK_plugin, as there're no
concurrent threads anymore. But some functions called from here
- use safe_mutex_assert_owner(), so we lock the mutex to satisfy it
+ use mysql_mutex_assert_owner(), so we lock the mutex to satisfy it
*/
- pthread_mutex_lock(&LOCK_plugin);
+ mysql_mutex_lock(&LOCK_plugin);
/*
We defer checking ref_counts until after all plugins are deinitialized
@@ -1875,7 +1981,8 @@ void plugin_shutdown(void)
if (plugins[i]->ref_count)
sql_print_error("Plugin '%s' has ref_count=%d after shutdown.",
plugins[i]->name.str, plugins[i]->ref_count);
- if (plugins[i]->state & PLUGIN_IS_UNINITIALIZED)
+ if (plugins[i]->state & PLUGIN_IS_UNINITIALIZED ||
+ plugins[i]->state & PLUGIN_IS_DISABLED)
plugin_del(plugins[i]);
}
@@ -1885,10 +1992,10 @@ void plugin_shutdown(void)
cleanup_variables(NULL, &global_system_variables);
cleanup_variables(NULL, &max_system_variables);
- pthread_mutex_unlock(&LOCK_plugin);
+ mysql_mutex_unlock(&LOCK_plugin);
initialized= 0;
- pthread_mutex_destroy(&LOCK_plugin);
+ mysql_mutex_destroy(&LOCK_plugin);
my_afree(plugins);
}
@@ -1896,7 +2003,7 @@ void plugin_shutdown(void)
/* Dispose of the memory */
for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
- hash_free(&plugin_hash[i]);
+ my_hash_free(&plugin_hash[i]);
delete_dynamic(&plugin_array);
count= plugin_dl_array.elements;
@@ -1908,52 +2015,43 @@ void plugin_shutdown(void)
my_afree(dl);
delete_dynamic(&plugin_dl_array);
- hash_free(&bookmark_hash);
+ my_hash_free(&bookmark_hash);
free_root(&plugin_mem_root, MYF(0));
+ free_root(&plugin_vars_mem_root, MYF(0));
global_variables_dynamic_size= 0;
DBUG_VOID_RETURN;
}
+/**
+ complete plugin installation (after plugin_add).
-bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl)
+ That is, initialize it, and update mysql.plugin table
+*/
+static bool finalize_install(THD *thd, TABLE *table, const LEX_STRING *name,
+ int *argc, char **argv)
{
- TABLE_LIST tables;
- TABLE *table;
- int error, argc=orig_argc;
- char **argv=orig_argv;
- struct st_plugin_int *tmp;
- DBUG_ENTER("mysql_install_plugin");
+ struct st_plugin_int *tmp= plugin_find_internal(name, MYSQL_ANY_PLUGIN);
+ int error;
+ DBUG_ASSERT(tmp);
+ mysql_mutex_assert_owner(&LOCK_plugin); // because of tmp->state
- if (opt_noacl)
+ if (tmp->state != PLUGIN_IS_UNINITIALIZED)
{
- my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
- DBUG_RETURN(TRUE);
+ /* already installed */
+ return 0;
+ }
+ else
+ {
+ if (plugin_initialize(thd->mem_root, tmp, argc, argv, false))
+ {
+ report_error(REPORT_TO_USER, ER_CANT_INITIALIZE_UDF, name->str,
+ "Plugin initialization function failed.");
+ tmp->state= PLUGIN_IS_DELETED;
+ return 1;
+ }
}
-
- bzero(&tables, sizeof(tables));
- tables.db= (char *)"mysql";
- tables.table_name= tables.alias= (char *)"plugin";
- if (check_table_access(thd, INSERT_ACL, &tables, 1, FALSE))
- DBUG_RETURN(TRUE);
-
- /* need to open before acquiring LOCK_plugin or it will deadlock */
- if (! (table = open_ltable(thd, &tables, TL_WRITE, 0)))
- DBUG_RETURN(TRUE);
-
- pthread_mutex_lock(&LOCK_plugin);
- rw_wrlock(&LOCK_system_variables_hash);
-
- my_load_defaults(MYSQL_CONFIG_NAME, load_default_groups, &argc, &argv, NULL);
- error= plugin_add(thd->mem_root, name, dl, &argc, argv, REPORT_TO_USER);
- if (argv)
- free_defaults(argv);
- rw_unlock(&LOCK_system_variables_hash);
-
- if (error || !(tmp= plugin_find_internal(name, MYSQL_ANY_PLUGIN)))
- goto err;
-
if (tmp->state == PLUGIN_IS_DISABLED)
{
if (global_system_variables.log_warnings)
@@ -1961,16 +2059,6 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl
ER_CANT_INITIALIZE_UDF, ER(ER_CANT_INITIALIZE_UDF),
name->str, "Plugin is disabled");
}
- else
- {
- DBUG_ASSERT(tmp->state == PLUGIN_IS_UNINITIALIZED);
- if (plugin_initialize(tmp))
- {
- my_error(ER_CANT_INITIALIZE_UDF, MYF(0), name->str,
- "Plugin initialization function failed.");
- goto deinit;
- }
- }
/*
We do not replicate the INSTALL PLUGIN statement. Disable binlogging
@@ -1981,33 +2069,29 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl
table->use_all_columns();
restore_record(table, s->default_values);
table->field[0]->store(name->str, name->length, system_charset_info);
- table->field[1]->store(dl->str, dl->length, files_charset_info);
+ table->field[1]->store(tmp->plugin_dl->dl.str, tmp->plugin_dl->dl.length,
+ files_charset_info);
error= table->file->ha_write_row(table->record[0]);
reenable_binlog(thd);
if (error)
{
table->file->print_error(error, MYF(0));
- goto deinit;
+ tmp->state= PLUGIN_IS_DELETED;
+ return 1;
}
-
- pthread_mutex_unlock(&LOCK_plugin);
- DBUG_RETURN(FALSE);
-deinit:
- tmp->state= PLUGIN_IS_DELETED;
- reap_needed= true;
- reap_plugins();
-err:
- pthread_mutex_unlock(&LOCK_plugin);
- DBUG_RETURN(TRUE);
+ return 0;
}
-
-bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name)
+bool mysql_install_plugin(THD *thd, const LEX_STRING *name,
+ const LEX_STRING *dl_arg)
{
- TABLE *table;
TABLE_LIST tables;
- struct st_plugin_int *plugin;
- DBUG_ENTER("mysql_uninstall_plugin");
+ TABLE *table;
+ LEX_STRING dl= *dl_arg;
+ bool error;
+ int argc=orig_argc;
+ char **argv=orig_argv;
+ DBUG_ENTER("mysql_install_plugin");
if (opt_noacl)
{
@@ -2015,28 +2099,97 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name)
DBUG_RETURN(TRUE);
}
- bzero(&tables, sizeof(tables));
- tables.db= (char *)"mysql";
- tables.table_name= tables.alias= (char *)"plugin";
- if (check_table_access(thd, DELETE_ACL, &tables, 1, FALSE))
+ tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE);
+ if (check_table_access(thd, INSERT_ACL, &tables, FALSE, 1, FALSE))
DBUG_RETURN(TRUE);
/* need to open before acquiring LOCK_plugin or it will deadlock */
- if (! (table= open_ltable(thd, &tables, TL_WRITE, 0)))
+ if (! (table = open_ltable(thd, &tables, TL_WRITE,
+ MYSQL_LOCK_IGNORE_TIMEOUT)))
DBUG_RETURN(TRUE);
- pthread_mutex_lock(&LOCK_plugin);
- if (!(plugin= plugin_find_internal(name, MYSQL_ANY_PLUGIN)))
+ if (my_load_defaults(MYSQL_CONFIG_NAME, load_default_groups, &argc, &argv, NULL))
{
- my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PLUGIN", name->str);
+ report_error(REPORT_TO_USER, ER_PLUGIN_IS_NOT_LOADED, name->str);
+ DBUG_RETURN(TRUE);
+ }
+
+ /*
+ Pre-acquire audit plugins for events that may potentially occur
+ during [UN]INSTALL PLUGIN.
+
+ When audit event is triggered, audit subsystem acquires interested
+ plugins by walking through plugin list. Evidently plugin list
+ iterator protects plugin list by acquiring LOCK_plugin, see
+ plugin_foreach_with_mask().
+
+ On the other hand [UN]INSTALL PLUGIN is acquiring LOCK_plugin
+ rather for a long time.
+
+ When audit event is triggered during [UN]INSTALL PLUGIN, plugin
+ list iterator acquires the same lock (within the same thread)
+ second time.
+
+ This hack should be removed when LOCK_plugin is fixed so it
+ protects only what it supposed to protect.
+
+ See also mysql_uninstall_plugin() and initialize_audit_plugin()
+ */
+ unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE] =
+ { MYSQL_AUDIT_GENERAL_CLASSMASK };
+ 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)
goto err;
+
+ if (name->str)
+ error= finalize_install(thd, table, name, &argc, argv);
+ else
+ {
+ st_plugin_dl *plugin_dl= plugin_dl_find(&dl);
+ struct st_maria_plugin *plugin;
+ for (plugin= plugin_dl->plugins; plugin->info; plugin++)
+ {
+ LEX_STRING str= { const_cast<char*>(plugin->name), strlen(plugin->name) };
+ error|= finalize_install(thd, table, &str, &argc, argv);
+ }
}
- if (!plugin->plugin_dl)
+
+ if (error)
+ {
+ reap_needed= true;
+ reap_plugins();
+ }
+err:
+ mysql_mutex_unlock(&LOCK_plugin);
+ if (argv)
+ free_defaults(argv);
+ DBUG_RETURN(error);
+}
+
+
+static bool do_uninstall(THD *thd, TABLE *table, const LEX_STRING *name)
+{
+ struct st_plugin_int *plugin;
+ mysql_mutex_assert_owner(&LOCK_plugin);
+
+ if (!(plugin= plugin_find_internal(name, MYSQL_ANY_PLUGIN)) ||
+ plugin->state & (PLUGIN_IS_UNINITIALIZED | PLUGIN_IS_DYING))
{
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- WARN_PLUGIN_DELETE_BUILTIN, ER(WARN_PLUGIN_DELETE_BUILTIN));
my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PLUGIN", name->str);
- goto err;
+ return 1;
+ }
+ if (!plugin->plugin_dl)
+ {
+ my_error(ER_PLUGIN_DELETE_BUILTIN, MYF(0));
+ return 1;
+ }
+ if (plugin->load_option == PLUGIN_FORCE_PLUS_PERMANENT)
+ {
+ my_error(ER_PLUGIN_IS_PERMANENT, MYF(0), name->str);
+ return 1;
}
plugin->state= PLUGIN_IS_DELETED;
@@ -2045,15 +2198,14 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name)
WARN_PLUGIN_BUSY, ER(WARN_PLUGIN_BUSY));
else
reap_needed= true;
- reap_plugins();
- pthread_mutex_unlock(&LOCK_plugin);
+ uchar user_key[MAX_KEY_LENGTH];
table->use_all_columns();
table->field[0]->store(name->str, name->length, system_charset_info);
- if (! table->file->ha_index_read_idx_map(table->record[0], 0,
- (uchar *)table->field[0]->ptr,
- HA_WHOLE_KEY,
- HA_READ_KEY_EXACT))
+ key_copy(user_key, table->record[0], table->key_info,
+ table->key_info->key_length);
+ if (! table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
+ HA_WHOLE_KEY, HA_READ_KEY_EXACT))
{
int error;
/*
@@ -2067,13 +2219,89 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name)
if (error)
{
table->file->print_error(error, MYF(0));
- DBUG_RETURN(TRUE);
+ return 1;
}
}
- DBUG_RETURN(FALSE);
-err:
- pthread_mutex_unlock(&LOCK_plugin);
- DBUG_RETURN(TRUE);
+ return 0;
+}
+
+
+bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name,
+ const LEX_STRING *dl_arg)
+{
+ TABLE *table;
+ TABLE_LIST tables;
+ LEX_STRING dl= *dl_arg;
+ bool error= false;
+ DBUG_ENTER("mysql_uninstall_plugin");
+
+ if (opt_noacl)
+ {
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
+ DBUG_RETURN(TRUE);
+ }
+
+ tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE);
+
+ if (check_table_access(thd, DELETE_ACL, &tables, FALSE, 1, FALSE))
+ DBUG_RETURN(TRUE);
+
+ /* need to open before acquiring LOCK_plugin or it will deadlock */
+ if (! (table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
+ DBUG_RETURN(TRUE);
+
+ /*
+ Pre-acquire audit plugins for events that may potentially occur
+ during [UN]INSTALL PLUGIN.
+
+ When audit event is triggered, audit subsystem acquires interested
+ plugins by walking through plugin list. Evidently plugin list
+ iterator protects plugin list by acquiring LOCK_plugin, see
+ plugin_foreach_with_mask().
+
+ On the other hand [UN]INSTALL PLUGIN is acquiring LOCK_plugin
+ rather for a long time.
+
+ When audit event is triggered during [UN]INSTALL PLUGIN, plugin
+ list iterator acquires the same lock (within the same thread)
+ second time.
+
+ This hack should be removed when LOCK_plugin is fixed so it
+ protects only what it supposed to protect.
+
+ See also mysql_install_plugin() and initialize_audit_plugin()
+ */
+ unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE] =
+ { MYSQL_AUDIT_GENERAL_CLASSMASK };
+ mysql_audit_acquire_plugins(thd, event_class_mask);
+
+ mysql_mutex_lock(&LOCK_plugin);
+
+ if (name->str)
+ error= do_uninstall(thd, table, name);
+ else
+ {
+ fix_dl_name(thd->mem_root, &dl);
+ st_plugin_dl *plugin_dl= plugin_dl_find(&dl);
+ if (plugin_dl)
+ {
+ for (struct st_maria_plugin *plugin= plugin_dl->plugins;
+ plugin->info; plugin++)
+ {
+ LEX_STRING str= { const_cast<char*>(plugin->name), strlen(plugin->name) };
+ error|= do_uninstall(thd, table, &str);
+ }
+ }
+ else
+ {
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SONAME", dl.str);
+ error= true;
+ }
+ }
+ reap_plugins();
+
+ mysql_mutex_unlock(&LOCK_plugin);
+ DBUG_RETURN(error);
}
@@ -2090,7 +2318,7 @@ bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func,
state_mask= ~state_mask; // do it only once
- pthread_mutex_lock(&LOCK_plugin);
+ mysql_mutex_lock(&LOCK_plugin);
total= type == MYSQL_ANY_PLUGIN ? plugin_array.elements
: plugin_hash[type].records;
/*
@@ -2111,21 +2339,21 @@ bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func,
HASH *hash= plugin_hash + type;
for (idx= 0; idx < total; idx++)
{
- plugin= (struct st_plugin_int *) hash_element(hash, idx);
+ plugin= (struct st_plugin_int *) my_hash_element(hash, idx);
plugins[idx]= !(plugin->state & state_mask) ? plugin : NULL;
}
}
- pthread_mutex_unlock(&LOCK_plugin);
+ mysql_mutex_unlock(&LOCK_plugin);
for (idx= 0; idx < total; idx++)
{
if (unlikely(version != plugin_array_version))
{
- pthread_mutex_lock(&LOCK_plugin);
+ mysql_mutex_lock(&LOCK_plugin);
for (uint i=idx; i < total; i++)
if (plugins[i] && plugins[i]->state & state_mask)
plugins[i]=0;
- pthread_mutex_unlock(&LOCK_plugin);
+ mysql_mutex_unlock(&LOCK_plugin);
}
plugin= plugins[idx];
/* It will stop iterating on first engine error when "func" returns TRUE */
@@ -2147,7 +2375,14 @@ err:
#undef MYSQL_SYSVAR_NAME
#define MYSQL_SYSVAR_NAME(name) name
-#define PLUGIN_VAR_TYPEMASK 0x007f
+#define PLUGIN_VAR_TYPEMASK 0x7f
+#define BOOKMARK_MEMALLOC 0x80
+
+static inline char plugin_var_bookmark_key(uint flags)
+{
+ return (flags & PLUGIN_VAR_TYPEMASK) |
+ (flags & PLUGIN_VAR_MEMALLOC ? BOOKMARK_MEMALLOC : 0);
+}
#define EXTRA_OPTIONS 3 /* options for: 'foo', 'plugin-foo' and NULL */
@@ -2167,6 +2402,7 @@ typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_longlong_t, longlong);
typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_uint_t, uint);
typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_ulong_t, ulong);
typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_ulonglong_t, ulonglong);
+typedef DECLARE_MYSQL_SYSVAR_SIMPLE(sysvar_double_t, double);
typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_int_t, int);
typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_long_t, long);
@@ -2174,6 +2410,7 @@ typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_longlong_t, longlong);
typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_uint_t, uint);
typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_ulong_t, ulong);
typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_ulonglong_t, ulonglong);
+typedef DECLARE_MYSQL_THDVAR_SIMPLE(thdvar_double_t, double);
/****************************************************************************
@@ -2184,7 +2421,7 @@ static int check_func_bool(THD *thd, struct st_mysql_sys_var *var,
void *save, st_mysql_value *value)
{
char buff[STRING_BUFFER_USUAL_SIZE];
- const char *strvalue= "NULL", *str;
+ const char *str;
int result, length;
long long tmp;
@@ -2193,28 +2430,19 @@ static int check_func_bool(THD *thd, struct st_mysql_sys_var *var,
length= sizeof(buff);
if (!(str= value->val_str(value, buff, &length)) ||
(result= find_type(&bool_typelib, str, length, 1)-1) < 0)
- {
- if (str)
- strvalue= str;
goto err;
- }
}
else
{
if (value->val_int(value, &tmp) < 0)
goto err;
if (tmp != 0 && tmp != 1)
- {
- llstr(tmp, buff);
- strvalue= buff;
goto err;
- }
result= (int) tmp;
}
*(my_bool *) save= result ? 1 : 0;
return 0;
err:
- my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->name, strvalue);
return 1;
}
@@ -2222,60 +2450,87 @@ err:
static int check_func_int(THD *thd, struct st_mysql_sys_var *var,
void *save, st_mysql_value *value)
{
- my_bool fixed;
- long long tmp;
+ my_bool fixed1, fixed2;
+ long long orig, val;
struct my_option options;
- value->val_int(value, &tmp);
+ value->val_int(value, &orig);
+ val= orig;
plugin_opt_set_limits(&options, var);
if (var->flags & PLUGIN_VAR_UNSIGNED)
- *(uint *)save= (uint) getopt_ull_limit_value((ulonglong) tmp, &options,
- &fixed);
+ {
+ if ((fixed1= (!value->is_unsigned(value) && val < 0)))
+ val=0;
+ *(uint *)save= (uint) getopt_ull_limit_value((ulonglong) val, &options,
+ &fixed2);
+ }
else
- *(int *)save= (int) getopt_ll_limit_value(tmp, &options, &fixed);
+ {
+ if ((fixed1= (value->is_unsigned(value) && val < 0)))
+ val=LONGLONG_MAX;
+ *(int *)save= (int) getopt_ll_limit_value(val, &options, &fixed2);
+ }
- return throw_bounds_warning(thd, fixed, var->flags & PLUGIN_VAR_UNSIGNED,
- var->name, (longlong) tmp);
+ return throw_bounds_warning(thd, var->name, fixed1 || fixed2,
+ value->is_unsigned(value), (longlong) orig);
}
static int check_func_long(THD *thd, struct st_mysql_sys_var *var,
void *save, st_mysql_value *value)
{
- my_bool fixed;
- long long tmp;
+ my_bool fixed1, fixed2;
+ long long orig, val;
struct my_option options;
- value->val_int(value, &tmp);
+ value->val_int(value, &orig);
+ val= orig;
plugin_opt_set_limits(&options, var);
if (var->flags & PLUGIN_VAR_UNSIGNED)
- *(ulong *)save= (ulong) getopt_ull_limit_value((ulonglong) tmp, &options,
- &fixed);
+ {
+ if ((fixed1= (!value->is_unsigned(value) && val < 0)))
+ val=0;
+ *(ulong *)save= (ulong) getopt_ull_limit_value((ulonglong) val, &options,
+ &fixed2);
+ }
else
- *(long *)save= (long) getopt_ll_limit_value(tmp, &options, &fixed);
+ {
+ if ((fixed1= (value->is_unsigned(value) && val < 0)))
+ val=LONGLONG_MAX;
+ *(long *)save= (long) getopt_ll_limit_value(val, &options, &fixed2);
+ }
- return throw_bounds_warning(thd, fixed, var->flags & PLUGIN_VAR_UNSIGNED,
- var->name, (longlong) tmp);
+ return throw_bounds_warning(thd, var->name, fixed1 || fixed2,
+ value->is_unsigned(value), (longlong) orig);
}
static int check_func_longlong(THD *thd, struct st_mysql_sys_var *var,
void *save, st_mysql_value *value)
{
- my_bool fixed;
- long long tmp;
+ my_bool fixed1, fixed2;
+ long long orig, val;
struct my_option options;
- value->val_int(value, &tmp);
+ value->val_int(value, &orig);
+ val= orig;
plugin_opt_set_limits(&options, var);
if (var->flags & PLUGIN_VAR_UNSIGNED)
- *(ulonglong *)save= getopt_ull_limit_value((ulonglong) tmp, &options,
- &fixed);
+ {
+ if ((fixed1= (!value->is_unsigned(value) && val < 0)))
+ val=0;
+ *(ulonglong *)save= getopt_ull_limit_value((ulonglong) val, &options,
+ &fixed2);
+ }
else
- *(longlong *)save= getopt_ll_limit_value(tmp, &options, &fixed);
+ {
+ if ((fixed1= (value->is_unsigned(value) && val < 0)))
+ val=LONGLONG_MAX;
+ *(longlong *)save= getopt_ll_limit_value(val, &options, &fixed2);
+ }
- return throw_bounds_warning(thd, fixed, var->flags & PLUGIN_VAR_UNSIGNED,
- var->name, (longlong) tmp);
+ return throw_bounds_warning(thd, var->name, fixed1 || fixed2,
+ value->is_unsigned(value), (longlong) orig);
}
static int check_func_str(THD *thd, struct st_mysql_sys_var *var,
@@ -2297,7 +2552,7 @@ static int check_func_enum(THD *thd, struct st_mysql_sys_var *var,
void *save, st_mysql_value *value)
{
char buff[STRING_BUFFER_USUAL_SIZE];
- const char *strvalue= "NULL", *str;
+ const char *str;
TYPELIB *typelib;
long long tmp;
long result;
@@ -2313,28 +2568,20 @@ static int check_func_enum(THD *thd, struct st_mysql_sys_var *var,
length= sizeof(buff);
if (!(str= value->val_str(value, buff, &length)))
goto err;
- if ((result= (long)find_type(typelib, str, length, 1)-1) < 0)
- {
- strvalue= str;
+ if ((result= (long)find_type(typelib, str, length, 0) - 1) < 0)
goto err;
- }
}
else
{
if (value->val_int(value, &tmp))
goto err;
- if (tmp >= typelib->count)
- {
- llstr(tmp, buff);
- strvalue= buff;
+ if (tmp < 0 || tmp >= typelib->count)
goto err;
- }
result= (long) tmp;
}
*(long*)save= result;
return 0;
err:
- my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->name, strvalue);
return 1;
}
@@ -2343,7 +2590,7 @@ static int check_func_set(THD *thd, struct st_mysql_sys_var *var,
void *save, st_mysql_value *value)
{
char buff[STRING_BUFFER_USUAL_SIZE], *error= 0;
- const char *strvalue= "NULL", *str;
+ const char *str;
TYPELIB *typelib;
ulonglong result;
uint error_len= 0; // init as only set on error
@@ -2363,31 +2610,36 @@ static int check_func_set(THD *thd, struct st_mysql_sys_var *var,
result= find_set(typelib, str, length, NULL,
&error, &error_len, &not_used);
if (error_len)
- {
- strmake(buff, error, min(sizeof(buff) - 1, error_len));
- strvalue= buff;
goto err;
- }
}
else
{
if (value->val_int(value, (long long *)&result))
goto err;
- if (unlikely((result >= (ULL(1) << typelib->count)) &&
+ if (unlikely((result >= (1ULL << typelib->count)) &&
(typelib->count < sizeof(long)*8)))
- {
- llstr(result, buff);
- strvalue= buff;
goto err;
- }
}
*(ulonglong*)save= result;
return 0;
err:
- my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), var->name, strvalue);
return 1;
}
+static int check_func_double(THD *thd, struct st_mysql_sys_var *var,
+ void *save, st_mysql_value *value)
+{
+ double v;
+ my_bool fixed;
+ struct my_option option;
+
+ value->val_real(value, &v);
+ plugin_opt_set_limits(&option, var);
+ *(double *) save= getopt_double_limit_value(v, &option, &fixed);
+
+ return throw_bounds_warning(thd, var->name, fixed, v);
+}
+
static void update_func_bool(THD *thd, struct st_mysql_sys_var *var,
void *tgt, const void *save)
@@ -2420,15 +2672,25 @@ static void update_func_longlong(THD *thd, struct st_mysql_sys_var *var,
static void update_func_str(THD *thd, struct st_mysql_sys_var *var,
void *tgt, const void *save)
{
- char *old= *(char **) tgt;
- *(char **)tgt= *(char **) save;
+ char *value= *(char**) save;
if (var->flags & PLUGIN_VAR_MEMALLOC)
{
- *(char **)tgt= my_strdup(*(char **) save, MYF(0));
- my_free(old, MYF(0));
+ char *old= *(char**) tgt;
+ if (value)
+ *(char**) tgt= my_strdup(value, MYF(0));
+ else
+ *(char**) tgt= 0;
+ my_free(old);
}
+ else
+ *(char**) tgt= value;
}
+static void update_func_double(THD *thd, struct st_mysql_sys_var *var,
+ void *tgt, const void *save)
+{
+ *(double *) tgt= *(double *) save;
+}
/****************************************************************************
System Variables support
@@ -2442,12 +2704,12 @@ sys_var *find_sys_var(THD *thd, const char *str, uint length)
plugin_ref plugin;
DBUG_ENTER("find_sys_var");
- pthread_mutex_lock(&LOCK_plugin);
- rw_rdlock(&LOCK_system_variables_hash);
- if ((var= intern_find_sys_var(str, length, false)) &&
+ mysql_mutex_lock(&LOCK_plugin);
+ mysql_rwlock_rdlock(&LOCK_system_variables_hash);
+ if ((var= intern_find_sys_var(str, length)) &&
(pi= var->cast_pluginvar()))
{
- rw_unlock(&LOCK_system_variables_hash);
+ mysql_rwlock_unlock(&LOCK_system_variables_hash);
LEX *lex= thd ? thd->lex : 0;
if (!(plugin= my_intern_plugin_lock(lex, plugin_int_to_ref(pi->plugin))))
var= NULL; /* failed to lock it, it must be uninstalling */
@@ -2460,14 +2722,10 @@ sys_var *find_sys_var(THD *thd, const char *str, uint length)
}
}
else
- rw_unlock(&LOCK_system_variables_hash);
- pthread_mutex_unlock(&LOCK_plugin);
+ mysql_rwlock_unlock(&LOCK_system_variables_hash);
+ mysql_mutex_unlock(&LOCK_plugin);
- /*
- If the variable exists but the plugin it is associated with is not ready
- then the intern_plugin_lock did not raise an error, so we do it here.
- */
- if (pi && !var)
+ if (!var)
my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), (char*) str);
DBUG_RETURN(var);
}
@@ -2504,10 +2762,10 @@ static st_bookmark *find_bookmark(const char *plugin, const char *name,
else
memcpy(varname + 1, name, namelen + 1);
- varname[0]= flags & PLUGIN_VAR_TYPEMASK;
+ varname[0]= plugin_var_bookmark_key(flags);
- result= (st_bookmark*) hash_search(&bookmark_hash,
- (const uchar*) varname, length - 1);
+ result= (st_bookmark*) my_hash_search(&bookmark_hash,
+ (const uchar*) varname, length - 1);
my_afree(varname);
return result;
@@ -2547,6 +2805,9 @@ static st_bookmark *register_var(const char *plugin, const char *name,
case PLUGIN_VAR_STR:
size= sizeof(char*);
break;
+ case PLUGIN_VAR_DOUBLE:
+ size= sizeof(double);
+ break;
default:
DBUG_ASSERT(0);
return NULL;
@@ -2560,9 +2821,9 @@ static st_bookmark *register_var(const char *plugin, const char *name,
if (!(result= find_bookmark(NULL, varname + 1, flags)))
{
- result= (st_bookmark*) alloc_root(&plugin_mem_root,
+ result= (st_bookmark*) alloc_root(&plugin_vars_mem_root,
sizeof(struct st_bookmark) + length-1);
- varname[0]= flags & PLUGIN_VAR_TYPEMASK;
+ varname[0]= plugin_var_bookmark_key(flags);
memcpy(result->key, varname, length);
result->name_len= length - 2;
result->offset= -1;
@@ -2617,6 +2878,16 @@ static st_bookmark *register_var(const char *plugin, const char *name,
return result;
}
+static void restore_pluginvar_names(sys_var *first)
+{
+ mysql_del_sys_var_chain(first);
+ for (sys_var *var= first; var; var= var->next)
+ {
+ sys_var_pluginvar *pv= var->cast_pluginvar();
+ pv->plugin_var->name= pv->orig_pluginvar_name;
+ }
+}
+
/*
returns a pointer to the memory which holds the thd-local variable or
@@ -2626,11 +2897,14 @@ static st_bookmark *register_var(const char *plugin, const char *name,
*/
static uchar *intern_sys_var_ptr(THD* thd, int offset, bool global_lock)
{
+ DBUG_ENTER("intern_sys_var_ptr");
DBUG_ASSERT(offset >= 0);
DBUG_ASSERT((uint)offset <= global_system_variables.dynamic_variables_head);
if (!thd)
- return (uchar*) global_system_variables.dynamic_variables_ptr + offset;
+ DBUG_RETURN((uchar*) global_system_variables.dynamic_variables_ptr + offset);
+
+ mysql_mutex_assert_not_owner(&LOCK_open);
/*
dynamic_variables_head points to the largest valid offset
@@ -2640,7 +2914,7 @@ static uchar *intern_sys_var_ptr(THD* thd, int offset, bool global_lock)
{
uint idx;
- rw_rdlock(&LOCK_system_variables_hash);
+ mysql_rwlock_rdlock(&LOCK_system_variables_hash);
thd->variables.dynamic_variables_ptr= (char*)
my_realloc(thd->variables.dynamic_variables_ptr,
@@ -2648,9 +2922,9 @@ static uchar *intern_sys_var_ptr(THD* thd, int offset, bool global_lock)
MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
if (global_lock)
- pthread_mutex_lock(&LOCK_global_system_variables);
+ mysql_mutex_lock(&LOCK_global_system_variables);
- safe_mutex_assert_owner(&LOCK_global_system_variables);
+ mysql_mutex_assert_owner(&LOCK_global_system_variables);
memcpy(thd->variables.dynamic_variables_ptr +
thd->variables.dynamic_variables_size,
@@ -2667,12 +2941,14 @@ static uchar *intern_sys_var_ptr(THD* thd, int offset, bool global_lock)
{
sys_var_pluginvar *pi;
sys_var *var;
- st_bookmark *v= (st_bookmark*) hash_element(&bookmark_hash,idx);
+ st_bookmark *v= (st_bookmark*) my_hash_element(&bookmark_hash,idx);
- if (v->version <= thd->variables.dynamic_variables_version ||
- !(var= intern_find_sys_var(v->key + 1, v->name_len, true)) ||
+ if (v->version <= thd->variables.dynamic_variables_version)
+ continue; /* already in thd->variables */
+
+ if (!(var= intern_find_sys_var(v->key + 1, v->name_len)) ||
!(pi= var->cast_pluginvar()) ||
- v->key[0] != (pi->plugin_var->flags & PLUGIN_VAR_TYPEMASK))
+ v->key[0] != plugin_var_bookmark_key(pi->plugin_var->flags))
continue;
/* Here we do anything special that may be required of the data types */
@@ -2689,7 +2965,7 @@ static uchar *intern_sys_var_ptr(THD* thd, int offset, bool global_lock)
}
if (global_lock)
- pthread_mutex_unlock(&LOCK_global_system_variables);
+ mysql_mutex_unlock(&LOCK_global_system_variables);
thd->variables.dynamic_variables_version=
global_system_variables.dynamic_variables_version;
@@ -2698,9 +2974,9 @@ static uchar *intern_sys_var_ptr(THD* thd, int offset, bool global_lock)
thd->variables.dynamic_variables_size=
global_system_variables.dynamic_variables_size;
- rw_unlock(&LOCK_system_variables_hash);
+ mysql_rwlock_unlock(&LOCK_system_variables_hash);
}
- return (uchar*)thd->variables.dynamic_variables_ptr + offset;
+ DBUG_RETURN((uchar*)thd->variables.dynamic_variables_ptr + offset);
}
@@ -2747,6 +3023,11 @@ static char **mysql_sys_var_str(THD* thd, int offset)
return (char **) intern_sys_var_ptr(thd, offset, true);
}
+static double *mysql_sys_var_double(THD* thd, int offset)
+{
+ return (double *) intern_sys_var_ptr(thd, offset, true);
+}
+
void plugin_thdvar_init(THD *thd)
{
plugin_ref old_table_plugin= thd->variables.table_plugin;
@@ -2763,11 +3044,11 @@ void plugin_thdvar_init(THD *thd)
thd->variables.dynamic_variables_size= 0;
thd->variables.dynamic_variables_ptr= 0;
- pthread_mutex_lock(&LOCK_plugin);
+ mysql_mutex_lock(&LOCK_plugin);
thd->variables.table_plugin=
my_intern_plugin_lock(NULL, global_system_variables.table_plugin);
intern_plugin_unlock(NULL, old_table_plugin);
- pthread_mutex_unlock(&LOCK_plugin);
+ mysql_mutex_unlock(&LOCK_plugin);
DBUG_VOID_RETURN;
}
@@ -2791,36 +3072,31 @@ static void unlock_variables(THD *thd, struct system_variables *vars)
static void cleanup_variables(THD *thd, struct system_variables *vars)
{
st_bookmark *v;
- sys_var_pluginvar *pivar;
- sys_var *var;
- int flags;
uint idx;
- rw_rdlock(&LOCK_system_variables_hash);
+ mysql_rwlock_rdlock(&LOCK_system_variables_hash);
for (idx= 0; idx < bookmark_hash.records; idx++)
{
- v= (st_bookmark*) hash_element(&bookmark_hash, idx);
- if (v->version > vars->dynamic_variables_version ||
- !(var= intern_find_sys_var(v->key + 1, v->name_len, true)) ||
- !(pivar= var->cast_pluginvar()) ||
- v->key[0] != (pivar->plugin_var->flags & PLUGIN_VAR_TYPEMASK))
- continue;
+ v= (st_bookmark*) my_hash_element(&bookmark_hash, idx);
- flags= pivar->plugin_var->flags;
+ if (v->version > vars->dynamic_variables_version)
+ continue; /* not in vars */
- if ((flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR &&
- flags & PLUGIN_VAR_THDLOCAL && flags & PLUGIN_VAR_MEMALLOC)
+ DBUG_ASSERT((uint)v->offset <= vars->dynamic_variables_head);
+
+ if ((v->key[0] & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR &&
+ v->key[0] & BOOKMARK_MEMALLOC)
{
- char **ptr= (char**) pivar->real_value_ptr(thd, OPT_SESSION);
- my_free(*ptr, MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
+ char **ptr= (char**)(vars->dynamic_variables_ptr + v->offset);
+ my_free(*ptr);
*ptr= NULL;
}
}
- rw_unlock(&LOCK_system_variables_hash);
+ mysql_rwlock_unlock(&LOCK_system_variables_hash);
DBUG_ASSERT(vars->table_plugin == NULL);
- my_free(vars->dynamic_variables_ptr, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(vars->dynamic_variables_ptr);
vars->dynamic_variables_ptr= NULL;
vars->dynamic_variables_size= 0;
vars->dynamic_variables_version= 0;
@@ -2833,7 +3109,7 @@ void plugin_thdvar_cleanup(THD *thd)
plugin_ref *list;
DBUG_ENTER("plugin_thdvar_cleanup");
- pthread_mutex_lock(&LOCK_plugin);
+ mysql_mutex_lock(&LOCK_plugin);
unlock_variables(thd, &thd->variables);
cleanup_variables(thd, &thd->variables);
@@ -2847,7 +3123,7 @@ void plugin_thdvar_cleanup(THD *thd)
}
reap_plugins();
- pthread_mutex_unlock(&LOCK_plugin);
+ mysql_mutex_unlock(&LOCK_plugin);
reset_dynamic(&thd->lex->plugins);
@@ -2880,51 +3156,62 @@ static void plugin_vars_free_values(sys_var *vars)
/* Free the string from global_system_variables. */
char **valptr= (char**) piv->real_value_ptr(NULL, OPT_GLOBAL);
DBUG_PRINT("plugin", ("freeing value for: '%s' addr: 0x%lx",
- var->name, (long) valptr));
- my_free(*valptr, MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
+ var->name.str, (long) valptr));
+ my_free(*valptr);
*valptr= NULL;
}
}
DBUG_VOID_RETURN;
}
-
-bool sys_var_pluginvar::check_update_type(Item_result type)
+static SHOW_TYPE pluginvar_show_type(st_mysql_sys_var *plugin_var)
{
- if (is_readonly())
- return 1;
- switch (plugin_var->flags & PLUGIN_VAR_TYPEMASK) {
+ switch (plugin_var->flags & (PLUGIN_VAR_TYPEMASK | PLUGIN_VAR_UNSIGNED)) {
+ case PLUGIN_VAR_BOOL:
+ return SHOW_MY_BOOL;
case PLUGIN_VAR_INT:
+ return SHOW_SINT;
+ case PLUGIN_VAR_INT | PLUGIN_VAR_UNSIGNED:
+ return SHOW_UINT;
case PLUGIN_VAR_LONG:
+ return SHOW_SLONG;
+ case PLUGIN_VAR_LONG | PLUGIN_VAR_UNSIGNED:
+ return SHOW_ULONG;
case PLUGIN_VAR_LONGLONG:
- return type != INT_RESULT;
+ return SHOW_SLONGLONG;
+ case PLUGIN_VAR_LONGLONG | PLUGIN_VAR_UNSIGNED:
+ return SHOW_ULONGLONG;
case PLUGIN_VAR_STR:
- return type != STRING_RESULT;
+ return SHOW_CHAR_PTR;
+ case PLUGIN_VAR_ENUM:
+ case PLUGIN_VAR_SET:
+ return SHOW_CHAR;
+ case PLUGIN_VAR_DOUBLE:
+ return SHOW_DOUBLE;
default:
- return 0;
+ DBUG_ASSERT(0);
+ return SHOW_UNDEF;
}
}
-SHOW_TYPE sys_var_pluginvar::show_type()
+bool sys_var_pluginvar::check_update_type(Item_result type)
{
switch (plugin_var->flags & PLUGIN_VAR_TYPEMASK) {
- case PLUGIN_VAR_BOOL:
- return SHOW_MY_BOOL;
case PLUGIN_VAR_INT:
- return SHOW_INT;
case PLUGIN_VAR_LONG:
- return SHOW_LONG;
case PLUGIN_VAR_LONGLONG:
- return SHOW_LONGLONG;
+ return type != INT_RESULT;
case PLUGIN_VAR_STR:
- return SHOW_CHAR_PTR;
+ return type != STRING_RESULT;
case PLUGIN_VAR_ENUM:
+ case PLUGIN_VAR_BOOL:
case PLUGIN_VAR_SET:
- return SHOW_CHAR;
+ return type != STRING_RESULT && type != INT_RESULT;
+ case PLUGIN_VAR_DOUBLE:
+ return type != INT_RESULT && type != REAL_RESULT && type != DECIMAL_RESULT;
default:
- DBUG_ASSERT(0);
- return SHOW_UNDEF;
+ return true;
}
}
@@ -2961,8 +3248,8 @@ TYPELIB* sys_var_pluginvar::plugin_var_typelib(void)
}
-uchar* sys_var_pluginvar::value_ptr(THD *thd, enum_var_type type,
- LEX_STRING *base)
+uchar* sys_var_pluginvar::do_value_ptr(THD *thd, enum_var_type type,
+ LEX_STRING *base)
{
uchar* result;
@@ -2971,139 +3258,110 @@ uchar* sys_var_pluginvar::value_ptr(THD *thd, enum_var_type type,
if ((plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_ENUM)
result= (uchar*) get_type(plugin_var_typelib(), *(ulong*)result);
else if ((plugin_var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_SET)
- {
- char buffer[STRING_BUFFER_USUAL_SIZE];
- String str(buffer, sizeof(buffer), system_charset_info);
- TYPELIB *typelib= plugin_var_typelib();
- ulonglong mask= 1, value= *(ulonglong*) result;
- uint i;
-
- str.length(0);
- for (i= 0; i < typelib->count; i++, mask<<=1)
- {
- if (!(value & mask))
- continue;
- str.append(typelib->type_names[i], typelib->type_lengths
- ? typelib->type_lengths[i]
- : strlen(typelib->type_names[i]));
- str.append(',');
- }
-
- result= (uchar*) "";
- if (str.length())
- result= (uchar*) thd->strmake(str.ptr(), str.length()-1);
- }
+ result= (uchar*) set_to_string(thd, 0, *(ulonglong*) result,
+ plugin_var_typelib()->type_names);
return result;
}
-
-bool sys_var_pluginvar::check(THD *thd, set_var *var)
+bool sys_var_pluginvar::do_check(THD *thd, set_var *var)
{
st_item_value_holder value;
- DBUG_ASSERT(is_readonly() || plugin_var->check);
+ DBUG_ASSERT(!is_readonly());
+ DBUG_ASSERT(plugin_var->check);
value.value_type= item_value_type;
value.val_str= item_val_str;
value.val_int= item_val_int;
value.val_real= item_val_real;
+ value.is_unsigned= item_is_unsigned;
value.item= var->value;
- return is_readonly() ||
- plugin_var->check(thd, plugin_var, &var->save_result, &value);
+ return plugin_var->check(thd, plugin_var, &var->save_result, &value);
}
-
-void sys_var_pluginvar::set_default(THD *thd, enum_var_type type)
+bool sys_var_pluginvar::session_update(THD *thd, set_var *var)
{
- const void *src;
- void *tgt;
+ DBUG_ASSERT(!is_readonly());
+ DBUG_ASSERT(plugin_var->flags & PLUGIN_VAR_THDLOCAL);
+ DBUG_ASSERT(thd == current_thd);
- DBUG_ASSERT(is_readonly() || plugin_var->update);
-
- if (is_readonly())
- return;
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ void *tgt= real_value_ptr(thd, var->type);
+ const void *src= var->value ? (void*)&var->save_result
+ : (void*)real_value_ptr(thd, OPT_GLOBAL);
+ mysql_mutex_unlock(&LOCK_global_system_variables);
- pthread_mutex_lock(&LOCK_global_system_variables);
- tgt= real_value_ptr(thd, type);
- src= ((void **) (plugin_var + 1) + 1);
-
- if (plugin_var->flags & PLUGIN_VAR_THDLOCAL)
- {
- if (type != OPT_GLOBAL)
- src= real_value_ptr(thd, OPT_GLOBAL);
- else
- switch (plugin_var->flags & PLUGIN_VAR_TYPEMASK) {
- case PLUGIN_VAR_INT:
- src= &((thdvar_uint_t*) plugin_var)->def_val;
- break;
- case PLUGIN_VAR_LONG:
- src= &((thdvar_ulong_t*) plugin_var)->def_val;
- break;
- case PLUGIN_VAR_LONGLONG:
- src= &((thdvar_ulonglong_t*) plugin_var)->def_val;
- break;
- case PLUGIN_VAR_ENUM:
- src= &((thdvar_enum_t*) plugin_var)->def_val;
- break;
- case PLUGIN_VAR_SET:
- src= &((thdvar_set_t*) plugin_var)->def_val;
- break;
- case PLUGIN_VAR_BOOL:
- src= &((thdvar_bool_t*) plugin_var)->def_val;
- break;
- case PLUGIN_VAR_STR:
- src= &((thdvar_str_t*) plugin_var)->def_val;
- break;
- default:
- DBUG_ASSERT(0);
- }
- }
-
- /* thd must equal current_thd if PLUGIN_VAR_THDLOCAL flag is set */
- DBUG_ASSERT(!(plugin_var->flags & PLUGIN_VAR_THDLOCAL) ||
- thd == current_thd);
-
- if (!(plugin_var->flags & PLUGIN_VAR_THDLOCAL) || type == OPT_GLOBAL)
- {
- plugin_var->update(thd, plugin_var, tgt, src);
- pthread_mutex_unlock(&LOCK_global_system_variables);
- }
- else
- {
- pthread_mutex_unlock(&LOCK_global_system_variables);
- plugin_var->update(thd, plugin_var, tgt, src);
- }
+ plugin_var->update(thd, plugin_var, tgt, src);
+
+ return false;
}
-
-bool sys_var_pluginvar::update(THD *thd, set_var *var)
+bool sys_var_pluginvar::global_update(THD *thd, set_var *var)
{
- void *tgt;
-
- DBUG_ASSERT(is_readonly() || plugin_var->update);
+ DBUG_ASSERT(!is_readonly());
+ mysql_mutex_assert_owner(&LOCK_global_system_variables);
- /* thd must equal current_thd if PLUGIN_VAR_THDLOCAL flag is set */
- DBUG_ASSERT(!(plugin_var->flags & PLUGIN_VAR_THDLOCAL) ||
- thd == current_thd);
-
- if (is_readonly())
- return 1;
+ void *tgt= real_value_ptr(thd, var->type);
+ const void *src= &var->save_result;
- pthread_mutex_lock(&LOCK_global_system_variables);
- tgt= real_value_ptr(thd, var->type);
-
- if (!(plugin_var->flags & PLUGIN_VAR_THDLOCAL) || var->type == OPT_GLOBAL)
+ if (!var->value)
{
- /* variable we are updating has global scope, so we unlock after updating */
- plugin_var->update(thd, plugin_var, tgt, &var->save_result);
- pthread_mutex_unlock(&LOCK_global_system_variables);
- }
- else
- {
- pthread_mutex_unlock(&LOCK_global_system_variables);
- plugin_var->update(thd, plugin_var, tgt, &var->save_result);
+ switch (plugin_var->flags & (PLUGIN_VAR_TYPEMASK | PLUGIN_VAR_THDLOCAL)) {
+ case PLUGIN_VAR_INT:
+ src= &((sysvar_uint_t*) plugin_var)->def_val;
+ break;
+ case PLUGIN_VAR_LONG:
+ src= &((sysvar_ulong_t*) plugin_var)->def_val;
+ break;
+ case PLUGIN_VAR_LONGLONG:
+ src= &((sysvar_ulonglong_t*) plugin_var)->def_val;
+ break;
+ case PLUGIN_VAR_ENUM:
+ src= &((sysvar_enum_t*) plugin_var)->def_val;
+ break;
+ case PLUGIN_VAR_SET:
+ src= &((sysvar_set_t*) plugin_var)->def_val;
+ break;
+ case PLUGIN_VAR_BOOL:
+ src= &((sysvar_bool_t*) plugin_var)->def_val;
+ break;
+ case PLUGIN_VAR_STR:
+ src= &((sysvar_str_t*) plugin_var)->def_val;
+ break;
+ case PLUGIN_VAR_DOUBLE:
+ src= &((sysvar_double_t*) plugin_var)->def_val;
+ break;
+ case PLUGIN_VAR_INT | PLUGIN_VAR_THDLOCAL:
+ src= &((thdvar_uint_t*) plugin_var)->def_val;
+ break;
+ case PLUGIN_VAR_LONG | PLUGIN_VAR_THDLOCAL:
+ src= &((thdvar_ulong_t*) plugin_var)->def_val;
+ break;
+ case PLUGIN_VAR_LONGLONG | PLUGIN_VAR_THDLOCAL:
+ src= &((thdvar_ulonglong_t*) plugin_var)->def_val;
+ break;
+ case PLUGIN_VAR_ENUM | PLUGIN_VAR_THDLOCAL:
+ src= &((thdvar_enum_t*) plugin_var)->def_val;
+ break;
+ case PLUGIN_VAR_SET | PLUGIN_VAR_THDLOCAL:
+ src= &((thdvar_set_t*) plugin_var)->def_val;
+ break;
+ case PLUGIN_VAR_BOOL | PLUGIN_VAR_THDLOCAL:
+ src= &((thdvar_bool_t*) plugin_var)->def_val;
+ break;
+ case PLUGIN_VAR_STR | PLUGIN_VAR_THDLOCAL:
+ src= &((thdvar_str_t*) plugin_var)->def_val;
+ break;
+ case PLUGIN_VAR_DOUBLE | PLUGIN_VAR_THDLOCAL:
+ src= &((thdvar_double_t*) plugin_var)->def_val;
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
}
- return 0;
+
+ plugin_var->update(thd, plugin_var, tgt, src);
+ return false;
}
@@ -3114,6 +3372,13 @@ bool sys_var_pluginvar::update(THD *thd, set_var *var)
options->max_value= (opt)->max_val; \
options->block_size= (long) (opt)->blk_sz
+#define OPTION_SET_LIMITS_DOUBLE(options, opt) \
+ options->var_type= GET_DOUBLE; \
+ options->def_value= (longlong) getopt_double2ulonglong((opt)->def_val); \
+ options->min_value= (longlong) getopt_double2ulonglong((opt)->min_val); \
+ options->max_value= getopt_double2ulonglong((opt)->max_val); \
+ options->block_size= (long) (opt)->blk_sz;
+
static void plugin_opt_set_limits(struct my_option *options,
const struct st_mysql_sys_var *opt)
@@ -3153,7 +3418,7 @@ static void plugin_opt_set_limits(struct my_option *options,
options->typelib= ((sysvar_set_t*) opt)->typelib;
options->def_value= ((sysvar_set_t*) opt)->def_val;
options->min_value= options->block_size= 0;
- options->max_value= (ULL(1) << options->typelib->count) - 1;
+ options->max_value= (1ULL << options->typelib->count) - 1;
break;
case PLUGIN_VAR_BOOL:
options->var_type= GET_BOOL;
@@ -3164,6 +3429,9 @@ static void plugin_opt_set_limits(struct my_option *options,
GET_STR_ALLOC : GET_STR);
options->def_value= (intptr) ((sysvar_str_t*) opt)->def_val;
break;
+ case PLUGIN_VAR_DOUBLE:
+ OPTION_SET_LIMITS_DOUBLE(options, (sysvar_double_t*) opt);
+ break;
/* threadlocal variables */
case PLUGIN_VAR_INT | PLUGIN_VAR_THDLOCAL:
OPTION_SET_LIMITS(GET_INT, options, (thdvar_int_t*) opt);
@@ -3183,6 +3451,9 @@ static void plugin_opt_set_limits(struct my_option *options,
case PLUGIN_VAR_LONGLONG | PLUGIN_VAR_UNSIGNED | PLUGIN_VAR_THDLOCAL:
OPTION_SET_LIMITS(GET_ULL, options, (thdvar_ulonglong_t*) opt);
break;
+ case PLUGIN_VAR_DOUBLE | PLUGIN_VAR_THDLOCAL:
+ OPTION_SET_LIMITS_DOUBLE(options, (thdvar_double_t*) opt);
+ break;
case PLUGIN_VAR_ENUM | PLUGIN_VAR_THDLOCAL:
options->var_type= GET_ENUM;
options->typelib= ((thdvar_enum_t*) opt)->typelib;
@@ -3195,7 +3466,7 @@ static void plugin_opt_set_limits(struct my_option *options,
options->typelib= ((thdvar_set_t*) opt)->typelib;
options->def_value= ((thdvar_set_t*) opt)->def_val;
options->min_value= options->block_size= 0;
- options->max_value= (ULL(1) << options->typelib->count) - 1;
+ options->max_value= (1ULL << options->typelib->count) - 1;
break;
case PLUGIN_VAR_BOOL | PLUGIN_VAR_THDLOCAL:
options->var_type= GET_BOOL;
@@ -3266,40 +3537,49 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp,
DBUG_ENTER("construct_options");
- options[0].name= plugin_name_ptr= (char*) alloc_root(mem_root,
- plugin_name_len + 1);
+ plugin_name_ptr= (char*) alloc_root(mem_root, plugin_name_len + 1);
strcpy(plugin_name_ptr, plugin_name);
my_casedn_str(&my_charset_latin1, plugin_name_ptr);
convert_underscore_to_dash(plugin_name_ptr, plugin_name_len);
- /* support --skip-plugin-foo syntax */
- options[1].name= plugin_name_with_prefix_ptr= (char*) alloc_root(mem_root,
- plugin_name_len +
- plugin_dash.length + 1);
- strxmov(plugin_name_with_prefix_ptr, plugin_dash.str, options[0].name, NullS);
-
- options[0].id= options[1].id= 256; /* must be >255. dup id ok */
- options[0].var_type= options[1].var_type= GET_ENUM;
- options[0].arg_type= options[1].arg_type= OPT_ARG;
- options[0].def_value= options[1].def_value= 1; /* ON */
- options[0].typelib= options[1].typelib= &global_plugin_typelib;
-
- strxnmov(comment, max_comment_len, "Enable or disable ", plugin_name,
- " plugin. Possible values are ON, OFF, FORCE (don't start "
- "if the plugin fails to load).", NullS);
- options[0].comment= comment;
+ plugin_name_with_prefix_ptr= (char*) alloc_root(mem_root,
+ plugin_name_len +
+ plugin_dash.length + 1);
+ strxmov(plugin_name_with_prefix_ptr, plugin_dash.str, plugin_name_ptr, NullS);
+
+ if (tmp->load_option != PLUGIN_FORCE &&
+ tmp->load_option != PLUGIN_FORCE_PLUS_PERMANENT)
+ {
+ /* support --skip-plugin-foo syntax */
+ options[0].name= plugin_name_ptr;
+ options[1].name= plugin_name_with_prefix_ptr;
+ options[0].id= options[1].id= 0;
+ options[0].var_type= options[1].var_type= GET_ENUM;
+ options[0].arg_type= options[1].arg_type= OPT_ARG;
+ options[0].def_value= options[1].def_value= 1; /* ON */
+ options[0].typelib= options[1].typelib= &global_plugin_typelib;
+
+ strxnmov(comment, max_comment_len, "Enable or disable ", plugin_name,
+ " plugin. Possible values are ON, OFF, FORCE (don't start "
+ "if the plugin fails to load).", NullS);
+ options[0].comment= comment;
+ /*
+ Allocate temporary space for the value of the tristate.
+ This option will have a limited lifetime and is not used beyond
+ server initialization.
+ GET_ENUM value is an unsigned long integer.
+ */
+ options[0].value= options[1].value=
+ (uchar **)alloc_root(mem_root, sizeof(ulong));
+ *((ulong*) options[0].value)= (ulong) options[0].def_value;
- /*
- Allocate temporary space for the value of the tristate.
- This option will have a limited lifetime and is not used beyond
- server initialization.
- GET_ENUM value is an ulong.
- */
- options[0].value= options[1].value= (uchar **)alloc_root(mem_root,
- sizeof(ulong));
- *((ulong*) options[0].value)= *((ulong*) options[1].value)=
- (ulong) options[0].def_value;
+ options+= 2;
+ }
- options+= 2;
+ if (!my_strcasecmp(&my_charset_latin1, plugin_name_ptr, "NDBCLUSTER"))
+ {
+ plugin_name_ptr= const_cast<char*>("ndb"); // Use legacy "ndb" prefix
+ plugin_name_len= 3;
+ }
/*
Two passes as the 2nd pass will take pointer addresses for use
@@ -3336,6 +3616,9 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp,
case PLUGIN_VAR_SET:
((thdvar_set_t *) opt)->resolve= mysql_sys_var_ulonglong;
break;
+ case PLUGIN_VAR_DOUBLE:
+ ((thdvar_double_t *) opt)->resolve= mysql_sys_var_double;
+ break;
default:
sql_print_error("Unknown variable type code 0x%x in plugin '%s'.",
opt->flags, plugin_name);
@@ -3386,19 +3669,6 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp,
opt->name, plugin_name);
}
}
- /*
- PLUGIN_VAR_STR command-line options without PLUGIN_VAR_MEMALLOC, point
- directly to values in the argv[] array. For plugins started at the
- server startup, argv[] array is allocated with load_defaults(), and
- freed when the server is shut down. But for plugins loaded with
- INSTALL PLUGIN, the memory allocated with load_defaults() is freed with
- freed() at the end of mysql_install_plugin(). Which means we cannot
- allow any pointers into that area.
- Thus, for all plugins loaded after the server was started,
- we force all command-line options to be PLUGIN_VAR_MEMALLOC
- */
- if (mysqld_server_started && !(opt->flags & PLUGIN_VAR_NOCMDOPT))
- opt->flags|= PLUGIN_VAR_MEMALLOC;
break;
case PLUGIN_VAR_ENUM:
if (!opt->check)
@@ -3412,6 +3682,12 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp,
if (!opt->update)
opt->update= update_func_longlong;
break;
+ case PLUGIN_VAR_DOUBLE:
+ if (!opt->check)
+ opt->check= check_func_double;
+ if (!opt->update)
+ opt->update= update_func_double;
+ break;
default:
sql_print_error("Unknown variable type code 0x%x in plugin '%s'.",
opt->flags, plugin_name);
@@ -3460,7 +3736,7 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp,
options->name= optname;
options->comment= opt->comment;
options->app_type= opt;
- options->id= (options-1)->id + 1;
+ options->id= 0;
plugin_opt_set_limits(options, opt);
@@ -3501,13 +3777,20 @@ static my_option *construct_help_options(MEM_ROOT *mem_root,
bzero(opts, sizeof(my_option) * count);
+ /**
+ some plugin variables (those that don't have PLUGIN_VAR_NOSYSVAR flag)
+ have their names prefixed with the plugin name. Restore the names here
+ to get the correct (not double-prefixed) help text.
+ We won't need @@sysvars anymore and don't care about their proper names.
+ */
+ restore_pluginvar_names(p->system_vars);
+
if (construct_options(mem_root, p, opts))
DBUG_RETURN(NULL);
DBUG_RETURN(opts);
}
-
/**
Create and register system variables supplied from the plugin and
assigns initial values from corresponding command line arguments.
@@ -3532,34 +3815,22 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
int *argc, char **argv)
{
struct sys_var_chain chain= { NULL, NULL };
- my_bool can_disable;
bool disable_plugin;
- enum_plugin_load_policy plugin_load_policy= PLUGIN_ON;
+ enum_plugin_load_option plugin_load_option= tmp->load_option;
+
MEM_ROOT *mem_root= alloc_root_inited(&tmp->mem_root) ?
- &tmp->mem_root : &plugin_mem_root;
+ &tmp->mem_root : &plugin_vars_mem_root;
st_mysql_sys_var **opt;
my_option *opts= NULL;
+ LEX_STRING plugin_name;
char *varname;
int error;
- st_mysql_sys_var *o;
- sys_var *v;
+ sys_var *v __attribute__((unused));
struct st_bookmark *var;
uint len, count= EXTRA_OPTIONS;
DBUG_ENTER("test_plugin_options");
DBUG_ASSERT(tmp->plugin && tmp->name.str);
-#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
- /*
- The 'ndbcluster' storage engines is always disabled by default.
- */
- if (!my_strcasecmp(&my_charset_latin1, tmp->name.str, "ndbcluster"))
- plugin_load_policy= PLUGIN_OFF;
-#endif
-#ifdef WITH_FEEDBACK_PLUGIN
- if (!my_strcasecmp(&my_charset_latin1, tmp->name.str, "feedback"))
- plugin_load_policy= PLUGIN_OFF;
-#endif
-
for (opt= tmp->plugin->system_vars; opt && *opt; opt++)
count+= 2; /* --{plugin}-{optname} and --plugin-{plugin}-{optname} */
@@ -3582,9 +3853,11 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
We adjust the default value to account for the hardcoded exceptions
we have set for the federated and ndbcluster storage engines.
*/
- opts[0].def_value= opts[1].def_value= (int)plugin_load_policy;
+ if (tmp->load_option != PLUGIN_FORCE &&
+ tmp->load_option != PLUGIN_FORCE_PLUS_PERMANENT)
+ opts[0].def_value= opts[1].def_value= plugin_load_option;
- error= handle_options(argc, &argv, opts, get_one_plugin_option);
+ error= handle_options(argc, &argv, opts, NULL);
(*argc)++; /* add back one for the program name */
if (error)
@@ -3597,29 +3870,13 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
Set plugin loading policy from option value. First element in the option
list is always the <plugin name> option value.
*/
- plugin_load_policy= (enum_plugin_load_policy)*(ulong*)opts[0].value;
+ if (tmp->load_option != PLUGIN_FORCE &&
+ tmp->load_option != PLUGIN_FORCE_PLUS_PERMANENT)
+ plugin_load_option= (enum_plugin_load_option) *(ulong*) opts[0].value;
}
- disable_plugin= (plugin_load_policy == PLUGIN_OFF);
- /*
- The 'MyISAM' and 'Memory' storage engines currently can't be disabled.
- */
- can_disable=
- my_strcasecmp(&my_charset_latin1, tmp->name.str, "MyISAM") &&
- my_strcasecmp(&my_charset_latin1, tmp->name.str, "MEMORY");
-#ifdef USE_MARIA_FOR_TMP_TABLES
- if (!can_disable)
- can_disable= (my_strcasecmp(&my_charset_latin1, tmp->name.str, "Maria")
- != 0);
-#endif
-
- tmp->is_mandatory= (plugin_load_policy == PLUGIN_FORCE) || !can_disable;
-
- if (disable_plugin && !can_disable)
- {
- sql_print_warning("Plugin '%s' cannot be disabled", tmp->name.str);
- disable_plugin= FALSE;
- }
+ disable_plugin= (plugin_load_option == PLUGIN_OFF);
+ tmp->load_option= plugin_load_option;
/*
If the plugin is disabled it should not be initialized.
@@ -3634,34 +3891,58 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
DBUG_RETURN(1);
}
+ if (!my_strcasecmp(&my_charset_latin1, tmp->name.str, "NDBCLUSTER"))
+ {
+ plugin_name.str= const_cast<char*>("ndb"); // Use legacy "ndb" prefix
+ plugin_name.length= 3;
+ }
+ else
+ plugin_name= tmp->name;
+
error= 1;
for (opt= tmp->plugin->system_vars; opt && *opt; opt++)
{
- if (((o= *opt)->flags & PLUGIN_VAR_NOSYSVAR))
+ st_mysql_sys_var *o= *opt;
+
+ /*
+ PLUGIN_VAR_STR command-line options without PLUGIN_VAR_MEMALLOC, point
+ directly to values in the argv[] array. For plugins started at the
+ server startup, argv[] array is allocated with load_defaults(), and
+ freed when the server is shut down. But for plugins loaded with
+ INSTALL PLUGIN, the memory allocated with load_defaults() is freed with
+ freed() at the end of mysql_install_plugin(). Which means we cannot
+ allow any pointers into that area.
+ Thus, for all plugins loaded after the server was started,
+ we copy string values to a plugin's memroot.
+ */
+ if (mysqld_server_started &&
+ ((o->flags & (PLUGIN_VAR_STR | PLUGIN_VAR_NOCMDOPT |
+ PLUGIN_VAR_MEMALLOC)) == PLUGIN_VAR_STR))
+ {
+ sysvar_str_t* str= (sysvar_str_t *)o;
+ if (*str->value)
+ *str->value= strdup_root(mem_root, *str->value);
+ }
+
+ if (o->flags & PLUGIN_VAR_NOSYSVAR)
continue;
- if ((var= find_bookmark(tmp->name.str, o->name, o->flags)))
- v= new (mem_root) sys_var_pluginvar(var->key + 1, o);
+ if ((var= find_bookmark(plugin_name.str, o->name, o->flags)))
+ v= new (mem_root) sys_var_pluginvar(&chain, var->key + 1, o);
else
{
- len= tmp->name.length + strlen(o->name) + 2;
+ len= plugin_name.length + strlen(o->name) + 2;
varname= (char*) alloc_root(mem_root, len);
- strxmov(varname, tmp->name.str, "-", o->name, NullS);
+ strxmov(varname, plugin_name.str, "-", o->name, NullS);
my_casedn_str(&my_charset_latin1, varname);
convert_dash_to_underscore(varname, len-1);
- v= new (mem_root) sys_var_pluginvar(varname, o);
+ v= new (mem_root) sys_var_pluginvar(&chain, varname, o);
}
DBUG_ASSERT(v); /* check that an object was actually constructed */
- /*
- Add to the chain of variables.
- Done like this for easier debugging so that the
- pointer to v is not lost on optimized builds.
- */
- v->chain_sys_var(&chain);
} /* end for */
if (chain.first)
{
chain.last->next = NULL;
- if (mysql_add_sys_var_chain(chain.first, NULL))
+ if (mysql_add_sys_var_chain(chain.first))
{
sql_print_error("Plugin '%s' has conflicting system variables",
tmp->name.str);
@@ -3682,48 +3963,26 @@ err:
Help Verbose text with Plugin System Variables
****************************************************************************/
-static int option_cmp(my_option *a, my_option *b)
-{
- return my_strcasecmp(&my_charset_latin1, a->name, b->name);
-}
-
-void my_print_help_inc_plugins(my_option *main_options, uint size)
+void add_plugin_options(DYNAMIC_ARRAY *options, MEM_ROOT *mem_root)
{
- DYNAMIC_ARRAY all_options;
struct st_plugin_int *p;
- MEM_ROOT mem_root;
my_option *opt;
- init_alloc_root(&mem_root, 4096, 4096);
- my_init_dynamic_array(&all_options, sizeof(my_option), size, size/4);
-
- if (initialized)
- for (uint idx= 0; idx < plugin_array.elements; idx++)
- {
- p= *dynamic_element(&plugin_array, idx, struct st_plugin_int **);
-
- if (!(opt= construct_help_options(&mem_root, p)))
- continue;
-
- /* Only options with a non-NULL comment are displayed in help text */
- for (;opt->id; opt++)
- if (opt->comment)
- insert_dynamic(&all_options, (uchar*) opt);
- }
-
- for (;main_options->id; main_options++)
- insert_dynamic(&all_options, (uchar*) main_options);
-
- sort_dynamic(&all_options, (qsort_cmp) option_cmp);
+ if (!initialized)
+ return;
- /* main_options now points to the empty option terminator */
- insert_dynamic(&all_options, (uchar*) main_options);
+ for (uint idx= 0; idx < plugin_array.elements; idx++)
+ {
+ p= *dynamic_element(&plugin_array, idx, struct st_plugin_int **);
- my_print_help((my_option*) all_options.buffer);
- my_print_variables((my_option*) all_options.buffer);
+ if (!(opt= construct_help_options(mem_root, p)))
+ continue;
- delete_dynamic(&all_options);
- free_root(&mem_root, MYF(0));
+ /* Only options with a non-NULL comment are displayed in help text */
+ for (;opt->name; opt++)
+ if (opt->comment)
+ insert_dynamic(options, (uchar*) opt);
+ }
}
diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h
index 936c27a2290..be1cfcdcc4f 100644
--- a/sql/sql_plugin.h
+++ b/sql/sql_plugin.h
@@ -1,5 +1,5 @@
-/*
- Copyright (c) 2005, 2010, Oracle and/or its affiliates.
+/* Copyright (c) 2005, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2012, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,13 +12,42 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef _sql_plugin_h
#define _sql_plugin_h
+#include <my_global.h>
+
+/*
+ the following #define adds server-only members to enum_mysql_show_type,
+ that is defined in plugin.h
+*/
+#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 <mysql/plugin.h>
+#undef SHOW_always_last
+
+#include "m_string.h" /* LEX_STRING */
+#include "my_alloc.h" /* MEM_ROOT */
+
class sys_var;
+enum SHOW_COMP_OPTION { SHOW_OPTION_YES, SHOW_OPTION_NO, SHOW_OPTION_DISABLED};
+enum enum_plugin_load_option { PLUGIN_OFF, PLUGIN_ON, PLUGIN_FORCE,
+ PLUGIN_FORCE_PLUS_PERMANENT };
+extern const char *global_plugin_typelib_names[];
+
+#include <my_sys.h>
+
+#ifdef DBUG_OFF
+#define plugin_ref_to_int(A) A
+#define plugin_int_to_ref(A) A
+#else
+#define plugin_ref_to_int(A) (A ? A[0] : NULL)
+#define plugin_int_to_ref(A) &(A)
+#endif
/*
the following flags are valid for plugin_init()
@@ -70,10 +99,11 @@ struct st_plugin_int
struct st_plugin_dl *plugin_dl;
uint state;
uint ref_count; /* number of threads using the plugin */
+ uint locks_total; /* how many times the plugin was locked */
void *data; /* plugin type specific, e.g. handlerton */
MEM_ROOT mem_root; /* memory for dynamic plugin structures */
sys_var *system_vars; /* server variables for this plugin */
- bool is_mandatory; /* If true then plugin must not fail to load */
+ enum enum_plugin_load_option load_option; /* OFF, ON, FORCE, F+PERMANENT */
};
@@ -90,6 +120,7 @@ typedef struct st_plugin_int *plugin_ref;
#define plugin_data(pi,cast) ((cast)((pi)->data))
#define plugin_name(pi) (&((pi)->name))
#define plugin_state(pi) ((pi)->state)
+#define plugin_load_option(pi) ((pi)->load_option)
#define plugin_equals(p1,p2) ((p1) == (p2))
#else
typedef struct st_plugin_int **plugin_ref;
@@ -100,6 +131,7 @@ typedef struct st_plugin_int **plugin_ref;
#define plugin_data(pi,cast) ((cast)((pi)[0]->data))
#define plugin_name(pi) (&((pi)[0]->name))
#define plugin_state(pi) ((pi)[0]->state)
+#define plugin_load_option(pi) ((pi)[0]->load_option)
#define plugin_equals(p1,p2) ((p1) && (p2) && (p1)[0] == (p2)[0])
#endif
@@ -109,29 +141,31 @@ extern char *opt_plugin_load;
extern char *opt_plugin_dir_ptr;
extern char opt_plugin_dir[FN_REFLEN];
extern const LEX_STRING plugin_type_names[];
-extern uint plugin_maturity;
+extern ulong plugin_maturity;
extern TYPELIB plugin_maturity_values;
extern const char *plugin_maturity_names[];
extern int plugin_init(int *argc, char **argv, int init_flags);
extern void plugin_shutdown(void);
-extern void my_print_help_inc_plugins(struct my_option *options, uint size);
+void add_plugin_options(DYNAMIC_ARRAY *options, MEM_ROOT *mem_root);
extern bool plugin_is_ready(const LEX_STRING *name, int type);
-#define my_plugin_lock_by_name(A,B,C) plugin_lock_by_name(A,B,C CALLER_INFO)
-#define my_plugin_lock_by_name_ci(A,B,C) plugin_lock_by_name(A,B,C ORIG_CALLER_INFO)
-#define my_plugin_lock(A,B) plugin_lock(A,B CALLER_INFO)
-#define my_plugin_lock_ci(A,B) plugin_lock(A,B ORIG_CALLER_INFO)
-extern plugin_ref plugin_lock(THD *thd, plugin_ref ptr CALLER_INFO_PROTO);
+#define my_plugin_lock_by_name(A,B,C) plugin_lock_by_name(A,B,C)
+#define my_plugin_lock_by_name_ci(A,B,C) plugin_lock_by_name(A,B,C)
+#define my_plugin_lock(A,B) plugin_lock(A,B)
+#define my_plugin_lock_ci(A,B) plugin_lock(A,B)
+extern plugin_ref plugin_lock(THD *thd, plugin_ref ptr);
extern plugin_ref plugin_lock_by_name(THD *thd, const LEX_STRING *name,
- int type CALLER_INFO_PROTO);
+ 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);
+extern bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name,
+ const LEX_STRING *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);
+extern SHOW_COMP_OPTION plugin_status(const char *name, size_t len, int type);
extern bool check_valid_path(const char *path, size_t length);
typedef my_bool (plugin_foreach_func)(THD *thd,
diff --git a/sql/sql_plugin_compat.h b/sql/sql_plugin_compat.h
new file mode 100644
index 00000000000..5c7bb620575
--- /dev/null
+++ b/sql/sql_plugin_compat.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 2013 Sergei Golubchik and Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/* old plugin api structures, used for backward compatibility */
+
+#define upgrade_var(X) latest->X= X
+#define upgrade_str(X) strmake_buf(latest->X, X)
+#define downgrade_var(X) X= latest->X
+#define downgrade_str(X) strmake_buf(X, latest->X)
+
+/**************************************************************/
+/* Authentication API, version 0x0100 *************************/
+#define MIN_AUTHENTICATION_INTERFACE_VERSION 0x0100
+
+struct MYSQL_SERVER_AUTH_INFO_0x0100 {
+ char *user_name;
+ unsigned int user_name_length;
+ const char *auth_string;
+ unsigned long auth_string_length;
+ char authenticated_as[49];
+ char external_user[512];
+ int password_used;
+ const char *host_or_ip;
+ unsigned int host_or_ip_length;
+
+ void upgrade(MYSQL_SERVER_AUTH_INFO *latest)
+ {
+ upgrade_var(user_name);
+ upgrade_var(user_name_length);
+ upgrade_var(auth_string);
+ upgrade_var(auth_string_length);
+ upgrade_str(authenticated_as);
+ upgrade_str(external_user);
+ upgrade_var(password_used);
+ upgrade_var(host_or_ip);
+ upgrade_var(host_or_ip_length);
+ }
+ void downgrade(MYSQL_SERVER_AUTH_INFO *latest)
+ {
+ downgrade_var(user_name);
+ downgrade_var(user_name_length);
+ downgrade_var(auth_string);
+ downgrade_var(auth_string_length);
+ downgrade_str(authenticated_as);
+ downgrade_str(external_user);
+ downgrade_var(password_used);
+ downgrade_var(host_or_ip);
+ downgrade_var(host_or_ip_length);
+ }
+};
+
+/**************************************************************/
+
diff --git a/sql/sql_plugin_services.h b/sql/sql_plugin_services.h
index 497e2c8d6bc..ede8d9a675e 100644
--- a/sql/sql_plugin_services.h
+++ b/sql/sql_plugin_services.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2009 Sun Microsystems, Inc.
+/* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,7 +11,7 @@
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* support for Services */
#include <service_versions.h>
@@ -22,8 +22,6 @@ struct st_service_ref {
void *service;
};
-#ifdef HAVE_DLOPEN
-
static struct my_snprintf_service_st my_snprintf_handler = {
my_snprintf,
my_vsnprintf
@@ -38,6 +36,16 @@ static struct thd_alloc_service_st thd_alloc_handler= {
thd_make_lex_string
};
+static struct thd_wait_service_st thd_wait_handler= {
+ thd_wait_begin,
+ thd_wait_end
+};
+
+static struct my_thread_scheduler_service my_thread_scheduler_handler= {
+ my_thread_scheduler_set,
+ my_thread_scheduler_reset,
+};
+
static struct progress_report_service_st progress_report_handler= {
thd_progress_init,
thd_progress_report,
@@ -46,10 +54,29 @@ static struct progress_report_service_st progress_report_handler= {
set_thd_proc_info
};
-static struct st_service_ref list_of_services[] __attribute__((unused)) =
+static struct kill_statement_service_st thd_kill_statement_handler= {
+ thd_kill_level
+};
+
+static struct logger_service_st logger_service_handler= {
+ logger_init_mutexes,
+ logger_open,
+ logger_close,
+ logger_vprintf,
+ logger_printf,
+ logger_write,
+ logger_rotate
+};
+
+static struct st_service_ref list_of_services[]=
{
- { "my_snprintf_service", VERSION_my_snprintf, &my_snprintf_handler },
- { "thd_alloc_service", VERSION_thd_alloc, &thd_alloc_handler },
- { "progress_report_service", VERSION_progress_report, &progress_report_handler }
+ { "my_snprintf_service", VERSION_my_snprintf, &my_snprintf_handler },
+ { "thd_alloc_service", VERSION_thd_alloc, &thd_alloc_handler },
+ { "thd_wait_service", VERSION_thd_wait, &thd_wait_handler },
+ { "my_thread_scheduler_service", VERSION_my_thread_scheduler, &my_thread_scheduler_handler },
+ { "progress_report_service", VERSION_progress_report, &progress_report_handler },
+ { "debug_sync_service", VERSION_debug_sync, 0 }, // updated in plugin_init()
+ { "thd_kill_statement_service", VERSION_kill_statement, &thd_kill_statement_handler },
+ { "logger_service", VERSION_logger, &logger_service_handler },
};
-#endif
+
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 39989a5bc0c..e948813584d 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2002, 2012, Oracle and/or its affiliates.
- Copyright (c) 2008, 2014, Monty Program Ab.
+/* Copyright (c) 2002, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2013, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -84,19 +84,38 @@ When one supplies long data for a placeholder:
at statement execute.
*/
-#include "mysql_priv.h"
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_priv.h"
+#include "unireg.h"
+#include "sql_class.h" // set_var.h: THD
+#include "set_var.h"
+#include "sql_prepare.h"
+#include "sql_parse.h" // insert_precheck, update_precheck, delete_precheck
+#include "sql_base.h" // open_normal_and_derived_tables
+#include "sql_cache.h" // query_cache_*
+#include "sql_view.h" // create_view_precheck
+#include "sql_delete.h" // mysql_prepare_delete
#include "sql_select.h" // for JOIN
+#include "sql_insert.h" // upgrade_lock_type_for_insert, mysql_prepare_insert
+#include "sql_update.h" // mysql_prepare_update
+#include "sql_db.h" // mysql_opt_change_db, mysql_change_db
+#include "sql_acl.h" // *_ACL
+#include "sql_derived.h" // mysql_derived_prepare,
+ // mysql_handle_derived
#include "sql_cursor.h"
#include "sp_head.h"
#include "sp.h"
#include "sp_cache.h"
+#include "probes_mysql.h"
#ifdef EMBEDDED_LIBRARY
/* include MYSQL_BIND headers */
#include <mysql.h>
#else
#include <mysql_com.h>
#endif
+#include "lock.h" // MYSQL_OPEN_FORCE_SHARED_MDL
#include "sql_handler.h"
+#include "transaction.h" // trans_rollback_implicit
/**
A result class used to send cursor rows using the binary protocol.
@@ -107,7 +126,7 @@ class Select_fetch_protocol_binary: public select_send
Protocol_binary protocol;
public:
Select_fetch_protocol_binary(THD *thd);
- virtual bool send_fields(List<Item> &list, uint flags);
+ virtual bool send_result_set_metadata(List<Item> &list, uint flags);
virtual int send_data(List<Item> &items);
virtual bool send_eof();
#ifdef EMBEDDED_LIBRARY
@@ -136,6 +155,7 @@ public:
THD *thd;
Select_fetch_protocol_binary result;
Item_param **param_array;
+ Server_side_cursor *cursor;
uint param_count;
uint last_errno;
uint flags;
@@ -164,6 +184,7 @@ public:
bool execute_loop(String *expanded_query,
bool open_cursor,
uchar *packet_arg, uchar *packet_end_arg);
+ bool execute_server_runnable(Server_runnable *server_runnable);
/* Destroy this statement */
void deallocate();
private:
@@ -172,8 +193,6 @@ private:
SELECT_LEX and other classes).
*/
MEM_ROOT main_mem_root;
- /* Version of the stored functions cache at the time of prepare. */
- ulong m_sp_cache_version;
private:
bool set_db(const char *db, uint db_length);
bool set_parameters(String *expanded_query,
@@ -184,6 +203,78 @@ private:
void swap_prepared_statement(Prepared_statement *copy);
};
+/**
+ Execute one SQL statement in an isolated context.
+*/
+
+class Execute_sql_statement: public Server_runnable
+{
+public:
+ Execute_sql_statement(LEX_STRING sql_text);
+ virtual bool execute_server_code(THD *thd);
+private:
+ LEX_STRING m_sql_text;
+};
+
+
+class Ed_connection;
+
+/**
+ Protocol_local: a helper class to intercept the result
+ of the data written to the network.
+*/
+
+class Protocol_local :public Protocol
+{
+public:
+ Protocol_local(THD *thd, Ed_connection *ed_connection);
+ ~Protocol_local() { free_root(&m_rset_root, MYF(0)); }
+protected:
+ virtual void prepare_for_resend();
+ virtual bool write();
+ virtual bool store_null();
+ virtual bool store_tiny(longlong from);
+ virtual bool store_short(longlong from);
+ virtual bool store_long(longlong from);
+ virtual bool store_longlong(longlong from, bool unsigned_flag);
+ virtual bool store_decimal(const my_decimal *);
+ virtual bool store(const char *from, size_t length, CHARSET_INFO *cs);
+ virtual bool store(const char *from, size_t length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
+ virtual bool store(MYSQL_TIME *time, int decimals);
+ virtual bool store_date(MYSQL_TIME *time);
+ virtual bool store_time(MYSQL_TIME *time, int decimals);
+ virtual bool store(float value, uint32 decimals, String *buffer);
+ virtual bool store(double value, uint32 decimals, String *buffer);
+ virtual bool store(Field *field);
+
+ virtual bool send_result_set_metadata(List<Item> *list, uint flags);
+ virtual bool send_out_parameters(List<Item_param> *sp_params);
+#ifdef EMBEDDED_LIBRARY
+ void remove_last_row();
+#endif
+ virtual enum enum_protocol_type type() { return PROTOCOL_LOCAL; };
+
+ virtual bool send_ok(uint server_status, uint statement_warn_count,
+ ulonglong affected_rows, ulonglong last_insert_id,
+ const char *message);
+
+ virtual bool send_eof(uint server_status, uint statement_warn_count);
+ virtual bool send_error(uint sql_errno, const char *err_msg, const char* sqlstate);
+private:
+ bool store_string(const char *str, size_t length,
+ CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs);
+
+ bool store_column(const void *data, size_t length);
+ void opt_add_row_to_rset();
+private:
+ Ed_connection *m_connection;
+ MEM_ROOT m_rset_root;
+ List<Ed_row> *m_rset;
+ size_t m_column_count;
+ Ed_column *m_current_row;
+ Ed_column *m_current_column;
+};
/******************************************************************************
Implementation
@@ -253,7 +344,7 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
int2store(buff+5, columns);
int2store(buff+7, stmt->param_count);
buff[9]= 0; // Guard against a 4.1 client
- tmp= min(stmt->thd->total_warn_count, 65535);
+ tmp= min(stmt->thd->warning_info->statement_warn_count(), 65535);
int2store(buff+10, tmp);
/*
@@ -263,14 +354,14 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
error= my_net_write(net, buff, sizeof(buff));
if (stmt->param_count && ! error)
{
- error= thd->protocol_text.send_fields((List<Item> *)
+ error= thd->protocol_text.send_result_set_metadata((List<Item> *)
&stmt->lex->param_list,
Protocol::SEND_EOF);
}
if (!error)
/* Flag that a response has already been sent */
- thd->main_da.disable_status();
+ thd->stmt_da->disable_status();
DBUG_RETURN(error);
}
@@ -283,7 +374,7 @@ static bool send_prep_stmt(Prepared_statement *stmt,
thd->client_stmt_id= stmt->id;
thd->client_param_count= stmt->param_count;
thd->clear_error();
- thd->main_da.disable_status();
+ thd->stmt_da->disable_status();
return 0;
}
@@ -789,7 +880,7 @@ static bool insert_params_with_log(Prepared_statement *stmt, uchar *null_array,
if (param->state == Item_param::NO_VALUE)
DBUG_RETURN(1);
- if (param->limit_clause_param)
+ if (param->limit_clause_param && param->state != Item_param::INT_VALUE)
{
param->set_int(param->val_int(), MY_INT64_NUM_DECIMAL_DIGITS);
param->item_type= Item::INT_ITEM;
@@ -1069,9 +1160,9 @@ static bool insert_params_from_vars(Prepared_statement *stmt,
{
Item_param *param= *it;
varname= var_it++;
- entry= (user_var_entry*)hash_search(&stmt->thd->user_vars,
- (uchar*) varname->str,
- varname->length);
+ entry= (user_var_entry*)my_hash_search(&stmt->thd->user_vars,
+ (uchar*) varname->str,
+ varname->length);
if (param->set_from_user_var(stmt->thd, entry) ||
param->convert_str_value(stmt->thd))
DBUG_RETURN(1);
@@ -1106,7 +1197,7 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
uint32 length= 0;
THD *thd= stmt->thd;
- DBUG_ENTER("insert_params_from_vars");
+ DBUG_ENTER("insert_params_from_vars_with_log");
if (query->copy(stmt->query(), stmt->query_length(), default_charset_info))
DBUG_RETURN(1);
@@ -1116,8 +1207,8 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
Item_param *param= *it;
varname= var_it++;
- entry= (user_var_entry *) hash_search(&thd->user_vars, (uchar*) varname->str,
- varname->length);
+ entry= (user_var_entry *) my_hash_search(&thd->user_vars, (uchar*)
+ varname->str, varname->length);
/*
We have to call the setup_one_conversion_function() here to set
the parameter's members that might be needed further
@@ -1166,8 +1257,8 @@ static bool mysql_test_insert(Prepared_statement *stmt,
if (insert_precheck(thd, table_list))
goto error;
- upgrade_lock_type_for_insert(thd, &table_list->lock_type, duplic,
- values_list.elements > 1);
+ //upgrade_lock_type_for_insert(thd, &table_list->lock_type, duplic,
+ // values_list.elements > 1);
/*
open temporary memory pool for temporary data allocated by derived
tables & preparation procedure
@@ -1176,7 +1267,8 @@ static bool mysql_test_insert(Prepared_statement *stmt,
If we would use locks, then we have to ensure we are not using
TL_WRITE_DELAYED as having two such locks can cause table corruption.
*/
- if (open_normal_and_derived_tables(thd, table_list, 0, DT_INIT))
+ if (open_normal_and_derived_tables(thd, table_list,
+ MYSQL_OPEN_FORCE_SHARED_MDL, DT_INIT))
goto error;
if ((values= its++))
@@ -1257,7 +1349,7 @@ static int mysql_test_update(Prepared_statement *stmt,
DBUG_ENTER("mysql_test_update");
if (update_precheck(thd, table_list) ||
- open_tables(thd, &table_list, &table_count, 0))
+ open_tables(thd, &table_list, &table_count, MYSQL_OPEN_FORCE_SHARED_MDL))
goto error;
if (mysql_handle_derived(thd->lex, DT_INIT))
@@ -1346,7 +1438,7 @@ static bool mysql_test_delete(Prepared_statement *stmt,
DBUG_ENTER("mysql_test_delete");
if (delete_precheck(thd, table_list) ||
- open_tables(thd, &table_list, &table_count, 0))
+ open_tables(thd, &table_list, &table_count, MYSQL_OPEN_FORCE_SHARED_MDL))
goto error;
if (mysql_handle_derived(thd->lex, DT_INIT))
@@ -1404,19 +1496,20 @@ static int mysql_test_select(Prepared_statement *stmt,
ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL;
if (tables)
{
- if (check_table_access(thd, privilege, tables, UINT_MAX, FALSE))
+ if (check_table_access(thd, privilege, tables, FALSE, UINT_MAX, FALSE))
goto error;
}
- else if (check_access(thd, privilege, any_db,0,0,0,0))
+ else if (check_access(thd, privilege, any_db, NULL, NULL, 0, 0))
goto error;
if (!lex->result && !(lex->result= new (stmt->mem_root) select_send))
{
- my_error(ER_OUTOFMEMORY, MYF(0), static_cast<int>(sizeof(select_send)));
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR),
+ static_cast<int>(sizeof(select_send)));
goto error;
}
- if (open_normal_and_derived_tables(thd, tables, 0,
+ if (open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL,
DT_PREPARE | DT_CREATE))
goto error;
@@ -1443,7 +1536,7 @@ static int mysql_test_select(Prepared_statement *stmt,
unit->prepare call above.
*/
if (send_prep_stmt(stmt, lex->result->field_count(fields)) ||
- lex->result->send_fields(fields, Protocol::SEND_EOF) ||
+ lex->result->send_result_set_metadata(fields, Protocol::SEND_EOF) ||
thd->protocol->flush())
goto error;
DBUG_RETURN(2);
@@ -1474,10 +1567,11 @@ static bool mysql_test_do_fields(Prepared_statement *stmt,
THD *thd= stmt->thd;
DBUG_ENTER("mysql_test_do_fields");
- if (tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE))
+ if (tables && check_table_access(thd, SELECT_ACL, tables, FALSE,
+ UINT_MAX, FALSE))
DBUG_RETURN(TRUE);
- if (open_normal_and_derived_tables(thd, tables, 0,
+ if (open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL,
DT_PREPARE | DT_CREATE))
DBUG_RETURN(TRUE);
DBUG_RETURN(setup_fields(thd, 0, *values, MARK_COLUMNS_NONE, 0, 0));
@@ -1507,8 +1601,8 @@ static bool mysql_test_set_fields(Prepared_statement *stmt,
set_var_base *var;
if ((tables &&
- check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)) ||
- open_normal_and_derived_tables(thd, tables, 0,
+ check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)) ||
+ open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL,
DT_PREPARE | DT_CREATE))
goto error;
@@ -1545,8 +1639,8 @@ static bool mysql_test_call_fields(Prepared_statement *stmt,
Item *item;
if ((tables &&
- check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)) ||
- open_normal_and_derived_tables(thd, tables, 0, DT_PREPARE))
+ check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)) ||
+ open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL, DT_PREPARE))
goto err;
while ((item= it++))
@@ -1631,7 +1725,7 @@ select_like_stmt_test_with_open(Prepared_statement *stmt,
themself.
*/
THD *thd= stmt->thd;
- if (open_tables(thd, &tables, &table_count, 0))
+ if (open_tables(thd, &tables, &table_count, MYSQL_OPEN_FORCE_SHARED_MDL))
DBUG_RETURN(TRUE);
DBUG_RETURN(select_like_stmt_test(stmt, specific_prepare,
@@ -1658,36 +1752,33 @@ static bool mysql_test_create_table(Prepared_statement *stmt)
LEX *lex= stmt->lex;
SELECT_LEX *select_lex= &lex->select_lex;
bool res= FALSE;
- /* Skip first table, which is the table we are creating */
bool link_to_local;
- TABLE_LIST *create_table= lex->unlink_first_table(&link_to_local);
- TABLE_LIST *tables= lex->query_tables;
+ TABLE_LIST *create_table= lex->query_tables;
+ TABLE_LIST *tables= lex->create_last_non_select_table->next_global;
if (create_table_precheck(thd, tables, create_table))
DBUG_RETURN(TRUE);
if (select_lex->item_list.elements)
{
+ /* Base table and temporary table are not in the same name space. */
if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
- {
- lex->link_first_table_back(create_table, link_to_local);
- create_table->create= TRUE;
- /* Base table and temporary table are not in the same name space. */
- create_table->skip_temporary= true;
- }
+ create_table->open_type= OT_BASE_ONLY;
- if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, 0,
+ if (open_normal_and_derived_tables(stmt->thd, lex->query_tables,
+ MYSQL_OPEN_FORCE_SHARED_MDL,
DT_PREPARE | DT_CREATE))
DBUG_RETURN(TRUE);
- if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
- create_table= lex->unlink_first_table(&link_to_local);
-
select_lex->context.resolve_in_select_list= TRUE;
+ lex->unlink_first_table(&link_to_local);
+
res= select_like_stmt_test(stmt, 0, 0);
+
+ lex->link_first_table_back(create_table, link_to_local);
}
- else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
+ else
{
/*
Check that the source table exist, and also record
@@ -1695,13 +1786,12 @@ static bool mysql_test_create_table(Prepared_statement *stmt)
we validate metadata of all CREATE TABLE statements,
which keeps metadata validation code simple.
*/
- if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, 0,
+ if (open_normal_and_derived_tables(stmt->thd, lex->query_tables,
+ MYSQL_OPEN_FORCE_SHARED_MDL,
DT_PREPARE))
DBUG_RETURN(TRUE);
}
- /* put tables back for PS rexecuting */
- lex->link_first_table_back(create_table, link_to_local);
DBUG_RETURN(res);
}
@@ -1731,7 +1821,8 @@ static bool mysql_test_create_view(Prepared_statement *stmt)
if (create_view_precheck(thd, tables, view, lex->create_view_mode))
goto err;
- if (open_normal_and_derived_tables(thd, tables, 0, DT_PREPARE))
+ if (open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL,
+ DT_PREPARE))
goto err;
lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW;
@@ -1788,7 +1879,7 @@ static bool mysql_test_multidelete(Prepared_statement *stmt,
stmt->thd->lex->current_select= &stmt->thd->lex->select_lex;
if (add_item_to_list(stmt->thd, new Item_null()))
{
- my_error(ER_OUTOFMEMORY, MYF(0), 0);
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), 0);
goto error;
}
@@ -1918,7 +2009,7 @@ static int mysql_test_handler_read(Prepared_statement *stmt,
DBUG_RETURN(1);
}
if (send_prep_stmt(stmt, ha_table->fields.elements) ||
- lex->result->send_fields(ha_table->fields, Protocol::SEND_EOF) ||
+ lex->result->send_result_set_metadata(ha_table->fields, Protocol::SEND_EOF) ||
thd->protocol->flush())
DBUG_RETURN(1);
DBUG_RETURN(2);
@@ -1963,6 +2054,10 @@ static bool check_prepared_statement(Prepared_statement *stmt)
lex->select_lex.context.resolve_in_table_list_only(select_lex->
get_table_list());
+ /* Reset warning count for each query that uses tables */
+ if (tables)
+ thd->warning_info->opt_clear_warning_info(thd->query_id);
+
switch (sql_command) {
case SQLCOM_REPLACE:
case SQLCOM_INSERT:
@@ -2049,30 +2144,6 @@ static bool check_prepared_statement(Prepared_statement *stmt)
Note that we don't need to have cases in this list if they are
marked with CF_STATUS_COMMAND in sql_command_flags
*/
- case SQLCOM_SHOW_PROCESSLIST:
- case SQLCOM_SHOW_STORAGE_ENGINES:
- case SQLCOM_SHOW_PRIVILEGES:
- case SQLCOM_SHOW_COLUMN_TYPES:
- case SQLCOM_SHOW_ENGINE_LOGS:
- case SQLCOM_SHOW_ENGINE_STATUS:
- case SQLCOM_SHOW_ENGINE_MUTEX:
- case SQLCOM_SHOW_CREATE_DB:
- case SQLCOM_SHOW_GRANTS:
- case SQLCOM_SHOW_BINLOG_EVENTS:
- case SQLCOM_SHOW_MASTER_STAT:
- case SQLCOM_SHOW_SLAVE_STAT:
- case SQLCOM_SHOW_CREATE_PROC:
- case SQLCOM_SHOW_CREATE_FUNC:
- case SQLCOM_SHOW_CREATE_EVENT:
- case SQLCOM_SHOW_CREATE_TRIGGER:
- case SQLCOM_SHOW_CREATE:
- case SQLCOM_SHOW_PROC_CODE:
- case SQLCOM_SHOW_FUNC_CODE:
- case SQLCOM_SHOW_AUTHORS:
- case SQLCOM_SHOW_CONTRIBUTORS:
- case SQLCOM_SHOW_WARNS:
- case SQLCOM_SHOW_ERRORS:
- case SQLCOM_SHOW_BINLOGS:
case SQLCOM_DROP_TABLE:
case SQLCOM_RENAME_TABLE:
case SQLCOM_ALTER_TABLE:
@@ -2195,12 +2266,11 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
{
Protocol *save_protocol= thd->protocol;
Prepared_statement *stmt;
- bool error;
DBUG_ENTER("mysqld_stmt_prepare");
DBUG_PRINT("prep_query", ("%s", packet));
/* First of all clear possible warnings from the previous command */
- mysql_reset_thd_for_next_command(thd, opt_userstat_running);
+ mysql_reset_thd_for_next_command(thd);
if (! (stmt= new Prepared_statement(thd)))
goto end; /* out of memory: error is set in Sql_alloc */
@@ -2214,22 +2284,9 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
goto end;
}
- /* Reset warnings from previous command */
- mysql_reset_errors(thd, 0);
- sp_cache_flush_obsolete(&thd->sp_proc_cache);
- sp_cache_flush_obsolete(&thd->sp_func_cache);
-
thd->protocol= &thd->protocol_binary;
- if (!(specialflag & SPECIAL_NO_PRIOR))
- my_pthread_setprio(pthread_self(),QUERY_PRIOR);
-
- error= stmt->prepare(packet, packet_length);
-
- if (!(specialflag & SPECIAL_NO_PRIOR))
- my_pthread_setprio(pthread_self(),WAIT_PRIOR);
-
- if (error)
+ if (stmt->prepare(packet, packet_length))
{
/* Statement map deletes statement on erase */
thd->stmt_map.erase(stmt);
@@ -2237,6 +2294,9 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
thd->protocol= save_protocol;
+ sp_cache_enforce_limit(thd->sp_proc_cache, stored_program_cache_size);
+ sp_cache_enforce_limit(thd->sp_func_cache, stored_program_cache_size);
+
/* check_prepared_statemnt sends the metadata packet in case of success */
end:
DBUG_VOID_RETURN;
@@ -2280,9 +2340,9 @@ static const char *get_dynamic_sql_string(LEX *lex, uint *query_len)
convert it for error messages to be uniform.
*/
if ((entry=
- (user_var_entry*)hash_search(&thd->user_vars,
- (uchar*)lex->prepared_stmt_code.str,
- lex->prepared_stmt_code.length))
+ (user_var_entry*)my_hash_search(&thd->user_vars,
+ (uchar*)lex->prepared_stmt_code.str,
+ lex->prepared_stmt_code.length))
&& entry->value)
{
bool is_var_null;
@@ -2519,6 +2579,13 @@ void reinit_stmt_before_use(THD *thd, LEX *lex)
{
tables->reinit_before_use(thd);
}
+
+ /* Reset MDL tickets for procedures/functions */
+ for (Sroutine_hash_entry *rt=
+ (Sroutine_hash_entry*)thd->lex->sroutines_list.first;
+ rt; rt= rt->next)
+ rt->mdl_request.ticket= NULL;
+
/*
Cleanup of the special case of DELETE t1, t2 FROM t1, t2, t3 ...
(multi-delete). We do a full clean up, although at the moment all we
@@ -2593,7 +2660,7 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
packet+= 9; /* stmt_id + 5 bytes of flags */
/* First of all clear possible warnings from the previous command */
- mysql_reset_thd_for_next_command(thd, opt_userstat_running);
+ mysql_reset_thd_for_next_command(thd);
if (!(stmt= find_prepared_statement(thd, stmt_id)))
{
@@ -2603,21 +2670,21 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
DBUG_VOID_RETURN;
}
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+#if defined(ENABLED_PROFILING)
thd->profiling.set_query_source(stmt->query(), stmt->query_length());
#endif
DBUG_PRINT("exec_query", ("%s", stmt->query()));
DBUG_PRINT("info",("stmt: 0x%lx", (long) stmt));
- sp_cache_flush_obsolete(&thd->sp_proc_cache);
- sp_cache_flush_obsolete(&thd->sp_func_cache);
-
open_cursor= test(flags & (ulong) CURSOR_TYPE_READ_ONLY);
thd->protocol= &thd->protocol_binary;
stmt->execute_loop(&expanded_query, open_cursor, packet, packet_end);
thd->protocol= save_protocol;
+ sp_cache_enforce_limit(thd->sp_proc_cache, stored_program_cache_size);
+ sp_cache_enforce_limit(thd->sp_func_cache, stored_program_cache_size);
+
/* Close connection socket; for use with client testing (Bug#43560). */
DBUG_EXECUTE_IF("close_conn_after_stmt_execute", vio_close(thd->net.vio););
@@ -2692,7 +2759,7 @@ void mysqld_stmt_fetch(THD *thd, char *packet, uint packet_length)
DBUG_ENTER("mysqld_stmt_fetch");
/* First of all clear possible warnings from the previous command */
- mysql_reset_thd_for_next_command(thd, opt_userstat_running);
+ mysql_reset_thd_for_next_command(thd);
status_var_increment(thd->status_var.com_stmt_fetch);
if (!(stmt= find_prepared_statement(thd, stmt_id)))
@@ -2713,18 +2780,11 @@ void mysqld_stmt_fetch(THD *thd, char *packet, uint packet_length)
thd->stmt_arena= stmt;
thd->set_n_backup_statement(stmt, &stmt_backup);
- if (!(specialflag & SPECIAL_NO_PRIOR))
- my_pthread_setprio(pthread_self(), QUERY_PRIOR);
-
cursor->fetch(num_rows);
- if (!(specialflag & SPECIAL_NO_PRIOR))
- my_pthread_setprio(pthread_self(), WAIT_PRIOR);
-
if (!cursor->is_open())
{
stmt->close_cursor();
- thd->cursor= 0;
reset_stmt_params(stmt);
}
@@ -2759,7 +2819,7 @@ void mysqld_stmt_reset(THD *thd, char *packet)
DBUG_ENTER("mysqld_stmt_reset");
/* First of all clear possible warnings from the previous command */
- mysql_reset_thd_for_next_command(thd, opt_userstat_running);
+ mysql_reset_thd_for_next_command(thd);
status_var_increment(thd->status_var.com_stmt_reset);
if (!(stmt= find_prepared_statement(thd, stmt_id)))
@@ -2778,7 +2838,7 @@ void mysqld_stmt_reset(THD *thd, char *packet)
*/
reset_stmt_params(stmt);
- stmt->state= Query_arena::PREPARED;
+ stmt->state= Query_arena::STMT_PREPARED;
general_log_print(thd, thd->command, NullS);
@@ -2802,7 +2862,7 @@ void mysqld_stmt_close(THD *thd, char *packet)
Prepared_statement *stmt;
DBUG_ENTER("mysqld_stmt_close");
- thd->main_da.disable_status();
+ thd->stmt_da->disable_status();
if (!(stmt= find_prepared_statement(thd, stmt_id)))
DBUG_VOID_RETURN;
@@ -2851,31 +2911,6 @@ void mysql_sql_stmt_close(THD *thd)
}
-class Set_longdata_error_handler : public Internal_error_handler
-{
-public:
- Set_longdata_error_handler(Prepared_statement *statement)
- : stmt(statement)
- { }
-
-public:
- bool handle_error(uint sql_errno,
- const char *message,
- MYSQL_ERROR::enum_warning_level level,
- THD *)
- {
- stmt->state= Query_arena::ERROR;
- stmt->last_errno= sql_errno;
- strnmov(stmt->last_error, message, MYSQL_ERRMSG_SIZE);
-
- return TRUE;
- }
-
-private:
- Prepared_statement *stmt;
-};
-
-
/**
Handle long data in pieces from client.
@@ -2903,7 +2938,7 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
status_var_increment(thd->status_var.com_stmt_send_long_data);
- thd->main_da.disable_status();
+ thd->stmt_da->disable_status();
#ifndef EMBEDDED_LIBRARY
/* Minimal size of long data packet is 6 bytes */
if (packet_length < MYSQL_LONG_DATA_HEADER)
@@ -2922,7 +2957,7 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
if (param_number >= stmt->param_count)
{
/* Error will be sent in execute call */
- stmt->state= Query_arena::ERROR;
+ stmt->state= Query_arena::STMT_ERROR;
stmt->last_errno= ER_WRONG_ARGUMENTS;
sprintf(stmt->last_error, ER(ER_WRONG_ARGUMENTS),
"mysqld_stmt_send_long_data");
@@ -2932,19 +2967,26 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
param= stmt->param_array[param_number];
- Set_longdata_error_handler err_handler(stmt);
- /*
- Install handler that will catch any errors that can be generated
- during execution of Item_param::set_longdata() and propagate
- them to Statement::last_error.
- */
- thd->push_internal_handler(&err_handler);
+ Diagnostics_area new_stmt_da, *save_stmt_da= thd->stmt_da;
+ Warning_info new_warnning_info(thd->query_id, false);
+ Warning_info *save_warinig_info= thd->warning_info;
+
+ thd->stmt_da= &new_stmt_da;
+ thd->warning_info= &new_warnning_info;
+
#ifndef EMBEDDED_LIBRARY
param->set_longdata(packet, (ulong) (packet_end - packet));
#else
param->set_longdata(thd->extra_data, thd->extra_length);
#endif
- thd->pop_internal_handler();
+ if (thd->stmt_da->is_error())
+ {
+ stmt->state= Query_arena::STMT_ERROR;
+ stmt->last_errno= thd->stmt_da->sql_errno();
+ strncpy(stmt->last_error, thd->stmt_da->message(), MYSQL_ERRMSG_SIZE);
+ }
+ thd->stmt_da= save_stmt_da;
+ thd->warning_info= save_warinig_info;
general_log_print(thd, thd->command, NullS);
@@ -2960,19 +3002,19 @@ Select_fetch_protocol_binary::Select_fetch_protocol_binary(THD *thd_arg)
:protocol(thd_arg)
{}
-bool Select_fetch_protocol_binary::send_fields(List<Item> &list, uint flags)
+bool Select_fetch_protocol_binary::send_result_set_metadata(List<Item> &list, uint flags)
{
bool rc;
Protocol *save_protocol= thd->protocol;
/*
- Protocol::send_fields caches the information about column types:
+ Protocol::send_result_set_metadata caches the information about column types:
this information is later used to send data. Therefore, the same
dedicated Protocol object must be used for all operations with
a cursor.
*/
thd->protocol= &protocol;
- rc= select_send::send_fields(list, flags);
+ rc= select_send::send_result_set_metadata(list, flags);
thd->protocol= save_protocol;
return rc;
@@ -2980,8 +3022,15 @@ bool Select_fetch_protocol_binary::send_fields(List<Item> &list, uint flags)
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())
+ return true;
+
::my_eof(thd);
- return FALSE;
+ return false;
}
@@ -2997,20 +3046,104 @@ Select_fetch_protocol_binary::send_data(List<Item> &fields)
return rc;
}
+/*******************************************************************
+* Reprepare_observer
+*******************************************************************/
+/** Push an error to the error stack and return TRUE for now. */
+
+bool
+Reprepare_observer::report_error(THD *thd)
+{
+ /*
+ This 'error' is purely internal to the server:
+ - No exception handler is invoked,
+ - No condition is added in the condition area (warn_list).
+ The diagnostics area is set to an error status to enforce
+ that this thread execution stops and returns to the caller,
+ backtracking all the way to Prepared_statement::execute_loop().
+ */
+ thd->stmt_da->set_error_status(thd, ER_NEED_REPREPARE,
+ ER(ER_NEED_REPREPARE), "HY000");
+ m_invalidated= TRUE;
+
+ return TRUE;
+}
+
+
+/*******************************************************************
+* Server_runnable
+*******************************************************************/
+
+Server_runnable::~Server_runnable()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+Execute_sql_statement::
+Execute_sql_statement(LEX_STRING sql_text)
+ :m_sql_text(sql_text)
+{}
+
+
+/**
+ Parse and execute a statement. Does not prepare the query.
+
+ Allows to execute a statement from within another statement.
+ The main property of the implementation is that it does not
+ affect the environment -- i.e. you can run many
+ executions without having to cleanup/reset THD in between.
+*/
+
+bool
+Execute_sql_statement::execute_server_code(THD *thd)
+{
+ bool error;
+
+ if (alloc_query(thd, m_sql_text.str, m_sql_text.length))
+ return TRUE;
+
+ Parser_state parser_state;
+ if (parser_state.init(thd, thd->query(), thd->query_length()))
+ return TRUE;
+
+ parser_state.m_lip.multi_statements= FALSE;
+ lex_start(thd);
+
+ error= parse_sql(thd, &parser_state, NULL) || thd->is_error();
+
+ if (error)
+ goto end;
+
+ thd->lex->set_trg_event_type_for_tables();
+
+ error= mysql_execute_command(thd);
+
+ /* report error issued during command execution */
+ if (error == 0 && thd->spcont == NULL)
+ general_log_write(thd, COM_STMT_EXECUTE,
+ thd->query(), thd->query_length());
+
+end:
+ lex_end(thd->lex);
+
+ return error;
+}
+
/***************************************************************************
Prepared_statement
****************************************************************************/
Prepared_statement::Prepared_statement(THD *thd_arg)
:Statement(NULL, &main_mem_root,
- INITIALIZED, ++thd_arg->statement_id_counter),
+ STMT_INITIALIZED, ++thd_arg->statement_id_counter),
thd(thd_arg),
result(thd_arg),
param_array(0),
+ cursor(0),
param_count(0),
last_errno(0),
- flags((uint) IS_IN_USE),
- m_sp_cache_version(0)
+ flags((uint) IS_IN_USE)
{
init_sql_alloc(&main_mem_root, thd_arg->variables.query_alloc_block_size,
thd_arg->variables.query_prealloc_size);
@@ -3094,12 +3227,8 @@ void Prepared_statement::cleanup_stmt()
DBUG_ENTER("Prepared_statement::cleanup_stmt");
DBUG_PRINT("enter",("stmt: 0x%lx", (long) this));
- DBUG_ASSERT(lex->sphead == 0);
- /* The order is important */
- lex->unit.cleanup();
cleanup_items(free_list);
thd->cleanup_after_query();
- close_thread_tables(thd);
thd->rollback_item_tree_changes();
DBUG_VOID_RETURN;
@@ -3215,6 +3344,8 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
}
parser_state.m_lip.stmt_prepare_mode= TRUE;
+ parser_state.m_lip.multi_statements= FALSE;
+
lex_start(thd);
lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_PREPARE;
@@ -3240,6 +3371,12 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
*/
DBUG_ASSERT(thd->change_list.is_empty());
+ /*
+ Marker used to release metadata locks acquired while the prepared
+ statement is being checked.
+ */
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
+
/*
The only case where we should have items in the thd->free_list is
after stmt->set_params_from_vars(), which may in some cases create
@@ -3255,10 +3392,28 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
to PREPARE stmt FROM "CREATE PROCEDURE ..."
*/
DBUG_ASSERT(lex->sphead == NULL || error != 0);
- if (lex->sphead)
+ /* The order is important */
+ lex->unit.cleanup();
+
+ /* No need to commit statement transaction, it's not started. */
+ DBUG_ASSERT(thd->transaction.stmt.is_empty());
+
+ close_thread_tables(thd);
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+
+ /*
+ Transaction rollback was requested since MDL deadlock was discovered
+ while trying to open tables. Rollback transaction in all storage
+ engines including binary log and release all locks.
+
+ Once dynamic SQL is allowed as substatements the below if-statement
+ has to be adjusted to not do rollback in substatement.
+ */
+ DBUG_ASSERT(! thd->in_sub_stmt);
+ if (thd->transaction_rollback_request)
{
- delete lex->sphead;
- lex->sphead= NULL;
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
}
lex_end(lex);
@@ -3270,22 +3425,8 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
{
setup_set_params();
lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_PREPARE;
- state= Query_arena::PREPARED;
+ state= Query_arena::STMT_PREPARED;
flags&= ~ (uint) IS_IN_USE;
- /*
- This is for prepared statement validation purposes.
- A statement looks up and pre-loads all its stored functions
- at prepare. Later on, if a function is gone from the cache,
- execute may fail.
- Remember the cache version to be able to invalidate the prepared
- statement at execute if it changes.
- We only need to care about version of the stored functions cache:
- if a prepared statement uses a stored procedure, it's indirect,
- via a stored function. The only exception is SQLCOM_CALL,
- but the latter one looks up the stored procedure each time
- it's invoked, rather than once at prepare.
- */
- m_sp_cache_version= sp_cache_version(&thd->sp_func_cache);
/*
Log COM_EXECUTE to the general log. Note, that in case of SQL
@@ -3383,11 +3524,6 @@ Prepared_statement::set_parameters(String *expanded_query,
and execute of a new statement. If this happens repeatedly
more than MAX_REPREPARE_ATTEMPTS times, we give up.
- In future we need to be able to keep the metadata locks between
- prepare and execute, but right now open_and_lock_tables(), as
- well as close_thread_tables() are buried deep inside
- execution code (mysql_execute_command()).
-
@return TRUE if an error, FALSE if success
@retval TRUE either MAX_REPREPARE_ATTEMPTS has been reached,
or some general error
@@ -3405,18 +3541,27 @@ Prepared_statement::execute_loop(String *expanded_query,
Reprepare_observer reprepare_observer;
bool error;
int reprepare_attempt= 0;
+ bool need_set_parameters= true;
/* Check if we got an error when sending long data */
- if (state == Query_arena::ERROR)
+ if (state == Query_arena::STMT_ERROR)
{
my_message(last_errno, last_error, MYF(0));
return TRUE;
}
- if (set_parameters(expanded_query, packet, packet_end))
+reexecute:
+ if (need_set_parameters &&
+ set_parameters(expanded_query, packet, packet_end))
return TRUE;
-reexecute:
+ /*
+ if set_parameters() has generated warnings,
+ we need to repeat it when reexecuting, to recreate these
+ warnings.
+ */
+ need_set_parameters= thd->warning_info->statement_warn_count();
+
reprepare_observer.reset_reprepare_observer();
/*
@@ -3439,21 +3584,15 @@ reexecute:
thd->m_reprepare_observer = &reprepare_observer;
}
- if (!(specialflag & SPECIAL_NO_PRIOR))
- my_pthread_setprio(pthread_self(),QUERY_PRIOR);
-
error= execute(expanded_query, open_cursor) || thd->is_error();
- if (!(specialflag & SPECIAL_NO_PRIOR))
- my_pthread_setprio(pthread_self(), WAIT_PRIOR);
-
thd->m_reprepare_observer= NULL;
if (error && !thd->is_fatal_error && !thd->killed &&
reprepare_observer.is_invalidated() &&
reprepare_attempt++ < MAX_REPREPARE_ATTEMPTS)
{
- DBUG_ASSERT(thd->main_da.sql_errno() == ER_NEED_REPREPARE);
+ DBUG_ASSERT(thd->stmt_da->sql_errno() == ER_NEED_REPREPARE);
thd->clear_error();
error= reprepare();
@@ -3467,6 +3606,40 @@ reexecute:
}
+bool
+Prepared_statement::execute_server_runnable(Server_runnable *server_runnable)
+{
+ Statement stmt_backup;
+ bool error;
+ Query_arena *save_stmt_arena= thd->stmt_arena;
+ Item_change_list save_change_list;
+ thd->change_list.move_elements_to(&save_change_list);
+
+ state= STMT_CONVENTIONAL_EXECUTION;
+
+ if (!(lex= new (mem_root) st_lex_local))
+ return TRUE;
+
+ thd->set_n_backup_statement(this, &stmt_backup);
+ thd->set_n_backup_active_arena(this, &stmt_backup);
+ thd->stmt_arena= this;
+
+ error= server_runnable->execute_server_code(thd);
+
+ thd->cleanup_after_query();
+
+ thd->restore_active_arena(this, &stmt_backup);
+ thd->restore_backup_statement(this, &stmt_backup);
+ thd->stmt_arena= save_stmt_arena;
+
+ save_change_list.move_elements_to(&thd->change_list);
+
+ /* Items and memory will freed in destructor */
+
+ return error;
+}
+
+
/**
Reprepare this prepared statement.
@@ -3516,12 +3689,12 @@ Prepared_statement::reprepare()
#endif
/*
Clear possible warnings during reprepare, it has to be completely
- transparent to the user. We use mysql_reset_errors() since
+ transparent to the user. We use clear_warning_info() since
there were no separate query id issued for re-prepare.
Sic: we can't simply silence warnings during reprepare, because if
it's failed, we need to return all the warnings to the user.
*/
- mysql_reset_errors(thd, TRUE);
+ thd->warning_info->clear_warning_info(thd->query_id);
}
return error;
}
@@ -3600,13 +3773,12 @@ Prepared_statement::swap_prepared_statement(Prepared_statement *copy)
is allocated in the old arena.
*/
swap_variables(Item_param **, param_array, copy->param_array);
- /* Swap flags: this is perhaps unnecessary */
- swap_variables(uint, flags, copy->flags);
+ /* 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);
/* Ditto */
swap_variables(char *, db, copy->db);
- swap_variables(ulong, m_sp_cache_version, copy->m_sp_cache_version);
DBUG_ASSERT(db_length == copy->db_length);
DBUG_ASSERT(param_count == copy->param_count);
@@ -3660,19 +3832,6 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
}
/*
- Reprepare the statement if we're using stored functions
- and the version of the stored routines cache has changed.
- */
- if (lex->uses_stored_routines() &&
- m_sp_cache_version != sp_cache_version(&thd->sp_func_cache) &&
- thd->m_reprepare_observer &&
- thd->m_reprepare_observer->report_error(thd))
- {
- return TRUE;
- }
-
-
- /*
For SHOW VARIABLES lex->result is NULL, as it's a non-SELECT
command. For such queries we don't return an error and don't
open a cursor -- the client library will recognize this case and
@@ -3725,7 +3884,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
alloc_query(thd, (char*) expanded_query->ptr(),
expanded_query->length()))
{
- my_error(ER_OUTOFMEMORY, 0, expanded_query->length());
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), expanded_query->length());
goto error;
}
/*
@@ -3733,7 +3892,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
to point at it even after we restore from backup. This is ok, as
expanded query was allocated in thd->mem_root.
*/
- stmt_backup.set_query_inner(thd->query(), thd->query_length());
+ stmt_backup.set_query_inner(thd->query_string);
/*
At first execution of prepared statement we may perform logical
@@ -3749,8 +3908,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
/* Go! */
if (open_cursor)
- error= mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR,
- &result, &cursor);
+ error= mysql_open_cursor(thd, &result, &cursor);
else
{
/*
@@ -3761,7 +3919,20 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
if (query_cache_send_result_to_client(thd, thd->query(),
thd->query_length()) <= 0)
{
+ MYSQL_QUERY_EXEC_START(thd->query(),
+ thd->thread_id,
+ (char *) (thd->db ? thd->db : ""),
+ &thd->security_ctx->priv_user[0],
+ (char *) thd->security_ctx->host_or_ip,
+ 1);
error= mysql_execute_command(thd);
+ MYSQL_QUERY_EXEC_DONE(error);
+ }
+ else
+ {
+ thd->lex->sql_command= SQLCOM_SELECT;
+ status_var_increment(thd->status_var.com_stat[SQLCOM_SELECT]);
+ thd->update_stats();
}
}
@@ -3785,8 +3956,16 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
thd->set_statement(&stmt_backup);
thd->stmt_arena= old_stmt_arena;
- if (state == Query_arena::PREPARED)
- state= Query_arena::EXECUTED;
+ if (state == Query_arena::STMT_PREPARED)
+ state= Query_arena::STMT_EXECUTED;
+
+ if (error == 0 && this->lex->sql_command == SQLCOM_CALL)
+ {
+ if (is_sql_prepare())
+ thd->protocol_text.send_out_parameters(&this->lex->param_list);
+ else
+ thd->protocol->send_out_parameters(&this->lex->param_list);
+ }
/*
Log COM_EXECUTE to the general log. Note, that in case of SQL
@@ -3821,3 +4000,577 @@ void Prepared_statement::deallocate()
/* Statement map calls delete stmt on erase */
thd->stmt_map.erase(this);
}
+
+
+/***************************************************************************
+* Ed_result_set
+***************************************************************************/
+/**
+ Use operator delete to free memory of Ed_result_set.
+ Accessing members of a class after the class has been destroyed
+ is a violation of the C++ standard but is commonly used in the
+ server code.
+*/
+
+void Ed_result_set::operator delete(void *ptr, size_t size) throw ()
+{
+ if (ptr)
+ {
+ /*
+ Make a stack copy, otherwise free_root() will attempt to
+ write to freed memory.
+ */
+ MEM_ROOT own_root= ((Ed_result_set*) ptr)->m_mem_root;
+ free_root(&own_root, MYF(0));
+ }
+}
+
+
+/**
+ Initialize an instance of Ed_result_set.
+
+ Instances of the class, as well as all result set rows, are
+ always allocated in the memory root passed over as the second
+ argument. In the constructor, we take over ownership of the
+ memory root. It will be freed when the class is destroyed.
+
+ sic: Ed_result_est is not designed to be allocated on stack.
+*/
+
+Ed_result_set::Ed_result_set(List<Ed_row> *rows_arg,
+ size_t column_count_arg,
+ MEM_ROOT *mem_root_arg)
+ :m_mem_root(*mem_root_arg),
+ m_column_count(column_count_arg),
+ m_rows(rows_arg),
+ m_next_rset(NULL)
+{
+ /* Take over responsibility for the memory */
+ clear_alloc_root(mem_root_arg);
+}
+
+/***************************************************************************
+* Ed_result_set
+***************************************************************************/
+
+/**
+ Create a new "execute direct" connection.
+*/
+
+Ed_connection::Ed_connection(THD *thd)
+ :m_warning_info(thd->query_id, false),
+ m_thd(thd),
+ m_rsets(0),
+ m_current_rset(0)
+{
+}
+
+
+/**
+ Free all result sets of the previous statement, if any,
+ and reset warnings and errors.
+
+ Called before execution of the next query.
+*/
+
+void
+Ed_connection::free_old_result()
+{
+ while (m_rsets)
+ {
+ Ed_result_set *rset= m_rsets->m_next_rset;
+ delete m_rsets;
+ m_rsets= rset;
+ }
+ m_current_rset= m_rsets;
+ m_diagnostics_area.reset_diagnostics_area();
+ m_warning_info.clear_warning_info(m_thd->query_id);
+}
+
+
+/**
+ A simple wrapper that uses a helper class to execute SQL statements.
+*/
+
+bool
+Ed_connection::execute_direct(LEX_STRING sql_text)
+{
+ Execute_sql_statement execute_sql_statement(sql_text);
+ DBUG_PRINT("ed_query", ("%s", sql_text.str));
+
+ return execute_direct(&execute_sql_statement);
+}
+
+
+/**
+ Execute a fragment of server functionality without an effect on
+ thd, and store results in memory.
+
+ Conventions:
+ - the code fragment must finish with OK, EOF or ERROR.
+ - the code fragment doesn't have to close thread tables,
+ free memory, commit statement transaction or do any other
+ cleanup that is normally done in the end of dispatch_command().
+
+ @param server_runnable A code fragment to execute.
+*/
+
+bool Ed_connection::execute_direct(Server_runnable *server_runnable)
+{
+ bool rc= FALSE;
+ Protocol_local protocol_local(m_thd, this);
+ Prepared_statement stmt(m_thd);
+ Protocol *save_protocol= m_thd->protocol;
+ Diagnostics_area *save_diagnostics_area= m_thd->stmt_da;
+ Warning_info *save_warning_info= m_thd->warning_info;
+
+ DBUG_ENTER("Ed_connection::execute_direct");
+
+ free_old_result(); /* Delete all data from previous execution, if any */
+
+ m_thd->protocol= &protocol_local;
+ m_thd->stmt_da= &m_diagnostics_area;
+ m_thd->warning_info= &m_warning_info;
+
+ rc= stmt.execute_server_runnable(server_runnable);
+ m_thd->protocol->end_statement();
+
+ m_thd->protocol= save_protocol;
+ m_thd->stmt_da= save_diagnostics_area;
+ m_thd->warning_info= save_warning_info;
+ /*
+ Protocol_local makes use of m_current_rset to keep
+ track of the last result set, while adding result sets to the end.
+ Reset it to point to the first result set instead.
+ */
+ m_current_rset= m_rsets;
+
+ DBUG_RETURN(rc);
+}
+
+
+/**
+ A helper method that is called only during execution.
+
+ Although Ed_connection doesn't support multi-statements,
+ a statement may generate many result sets. All subsequent
+ result sets are appended to the end.
+
+ @pre This is called only by Protocol_local.
+*/
+
+void
+Ed_connection::add_result_set(Ed_result_set *ed_result_set)
+{
+ if (m_rsets)
+ {
+ m_current_rset->m_next_rset= ed_result_set;
+ /* While appending, use m_current_rset as a pointer to the tail. */
+ m_current_rset= ed_result_set;
+ }
+ else
+ m_current_rset= m_rsets= ed_result_set;
+}
+
+
+/**
+ Release ownership of the current result set to the client.
+
+ Since we use a simple linked list for result sets,
+ this method uses a linear search of the previous result
+ set to exclude the released instance from the list.
+
+ @todo Use double-linked list, when this is really used.
+
+ XXX: This has never been tested with more than one result set!
+
+ @pre There must be a result set.
+*/
+
+Ed_result_set *
+Ed_connection::store_result_set()
+{
+ Ed_result_set *ed_result_set;
+
+ DBUG_ASSERT(m_current_rset);
+
+ if (m_current_rset == m_rsets)
+ {
+ /* Assign the return value */
+ ed_result_set= m_current_rset;
+ /* Exclude the return value from the list. */
+ m_current_rset= m_rsets= m_rsets->m_next_rset;
+ }
+ else
+ {
+ Ed_result_set *prev_rset= m_rsets;
+ /* Assign the return value. */
+ ed_result_set= m_current_rset;
+
+ /* Exclude the return value from the list */
+ while (prev_rset->m_next_rset != m_current_rset)
+ prev_rset= ed_result_set->m_next_rset;
+ m_current_rset= prev_rset->m_next_rset= m_current_rset->m_next_rset;
+ }
+ ed_result_set->m_next_rset= NULL; /* safety */
+
+ return ed_result_set;
+}
+
+/*************************************************************************
+* Protocol_local
+**************************************************************************/
+
+Protocol_local::Protocol_local(THD *thd, Ed_connection *ed_connection)
+ :Protocol(thd),
+ m_connection(ed_connection),
+ m_rset(NULL),
+ m_column_count(0),
+ m_current_row(NULL),
+ m_current_column(NULL)
+{
+ clear_alloc_root(&m_rset_root);
+}
+
+/**
+ Called between two result set rows.
+
+ Prepare structures to fill result set rows.
+ Unfortunately, we can't return an error here. If memory allocation
+ fails, we'll have to return an error later. And so is done
+ in methods such as @sa store_column().
+*/
+
+void Protocol_local::prepare_for_resend()
+{
+ DBUG_ASSERT(alloc_root_inited(&m_rset_root));
+
+ opt_add_row_to_rset();
+ /* Start a new row. */
+ m_current_row= (Ed_column *) alloc_root(&m_rset_root,
+ sizeof(Ed_column) * m_column_count);
+ m_current_column= m_current_row;
+}
+
+
+/**
+ In "real" protocols this is called to finish a result set row.
+ Unused in the local implementation.
+*/
+
+bool Protocol_local::write()
+{
+ return FALSE;
+}
+
+/**
+ A helper function to add the current row to the current result
+ set. Called in @sa prepare_for_resend(), when a new row is started,
+ and in send_eof(), when the result set is finished.
+*/
+
+void Protocol_local::opt_add_row_to_rset()
+{
+ if (m_current_row)
+ {
+ /* Add the old row to the result set */
+ Ed_row *ed_row= new (&m_rset_root) Ed_row(m_current_row, m_column_count);
+ if (ed_row)
+ m_rset->push_back(ed_row, &m_rset_root);
+ }
+}
+
+
+/**
+ Add a NULL column to the current row.
+*/
+
+bool Protocol_local::store_null()
+{
+ if (m_current_column == NULL)
+ return TRUE; /* prepare_for_resend() failed to allocate memory. */
+
+ bzero(m_current_column, sizeof(*m_current_column));
+ ++m_current_column;
+ return FALSE;
+}
+
+
+/**
+ A helper method to add any column to the current row
+ in its binary form.
+
+ Allocates memory for the data in the result set memory root.
+*/
+
+bool Protocol_local::store_column(const void *data, size_t length)
+{
+ if (m_current_column == NULL)
+ return TRUE; /* prepare_for_resend() failed to allocate memory. */
+ /*
+ alloc_root() automatically aligns memory, so we don't need to
+ do any extra alignment if we're pointing to, say, an integer.
+ */
+ m_current_column->str= (char*) memdup_root(&m_rset_root,
+ data,
+ length + 1 /* Safety */);
+ if (! m_current_column->str)
+ return TRUE;
+ m_current_column->str[length]= '\0'; /* Safety */
+ m_current_column->length= length;
+ ++m_current_column;
+ return FALSE;
+}
+
+
+/**
+ Store a string value in a result set column, optionally
+ having converted it to character_set_results.
+*/
+
+bool
+Protocol_local::store_string(const char *str, size_t length,
+ CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs)
+{
+ /* Store with conversion */
+ uint error_unused;
+
+ if (dst_cs && !my_charset_same(src_cs, dst_cs) &&
+ src_cs != &my_charset_bin &&
+ dst_cs != &my_charset_bin)
+ {
+ if (convert->copy(str, length, src_cs, dst_cs, &error_unused))
+ return TRUE;
+ str= convert->ptr();
+ length= convert->length();
+ }
+ return store_column(str, length);
+}
+
+
+/** Store a tiny int as is (1 byte) in a result set column. */
+
+bool Protocol_local::store_tiny(longlong value)
+{
+ char v= (char) value;
+ return store_column(&v, 1);
+}
+
+
+/** Store a short as is (2 bytes, host order) in a result set column. */
+
+bool Protocol_local::store_short(longlong value)
+{
+ int16 v= (int16) value;
+ return store_column(&v, 2);
+}
+
+
+/** Store a "long" as is (4 bytes, host order) in a result set column. */
+
+bool Protocol_local::store_long(longlong value)
+{
+ int32 v= (int32) value;
+ return store_column(&v, 4);
+}
+
+
+/** Store a "longlong" as is (8 bytes, host order) in a result set column. */
+
+bool Protocol_local::store_longlong(longlong value, bool unsigned_flag)
+{
+ int64 v= (int64) value;
+ return store_column(&v, 8);
+}
+
+
+/** Store a decimal in string format in a result set column */
+
+bool Protocol_local::store_decimal(const my_decimal *value)
+{
+ char buf[DECIMAL_MAX_STR_LENGTH];
+ String str(buf, sizeof (buf), &my_charset_bin);
+ int rc;
+
+ rc= my_decimal2string(E_DEC_FATAL_ERROR, value, 0, 0, 0, &str);
+
+ if (rc)
+ return TRUE;
+
+ return store_column(str.ptr(), str.length());
+}
+
+
+/** Convert to cs_results and store a string. */
+
+bool Protocol_local::store(const char *str, size_t length,
+ CHARSET_INFO *src_cs)
+{
+ CHARSET_INFO *dst_cs;
+
+ dst_cs= m_connection->m_thd->variables.character_set_results;
+ return store_string(str, length, src_cs, dst_cs);
+}
+
+
+/** Store a string. */
+
+bool Protocol_local::store(const char *str, size_t length,
+ CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs)
+{
+ return store_string(str, length, src_cs, dst_cs);
+}
+
+
+/* Store MYSQL_TIME (in binary format) */
+
+bool Protocol_local::store(MYSQL_TIME *time, int decimals)
+{
+ if (decimals != AUTO_SEC_PART_DIGITS)
+ time->second_part= sec_part_truncate(time->second_part, decimals);
+ return store_column(time, sizeof(MYSQL_TIME));
+}
+
+
+/** Store MYSQL_TIME (in binary format) */
+
+bool Protocol_local::store_date(MYSQL_TIME *time)
+{
+ return store_column(time, sizeof(MYSQL_TIME));
+}
+
+
+/** Store MYSQL_TIME (in binary format) */
+
+bool Protocol_local::store_time(MYSQL_TIME *time, int decimals)
+{
+ if (decimals != AUTO_SEC_PART_DIGITS)
+ time->second_part= sec_part_truncate(time->second_part, decimals);
+ return store_column(time, sizeof(MYSQL_TIME));
+}
+
+
+/* Store a floating point number, as is. */
+
+bool Protocol_local::store(float value, uint32 decimals, String *buffer)
+{
+ return store_column(&value, sizeof(float));
+}
+
+
+/* Store a double precision number, as is. */
+
+bool Protocol_local::store(double value, uint32 decimals, String *buffer)
+{
+ return store_column(&value, sizeof (double));
+}
+
+
+/* Store a Field. */
+
+bool Protocol_local::store(Field *field)
+{
+ if (field->is_null())
+ return store_null();
+ return field->send_binary(this);
+}
+
+
+/** Called to start a new result set. */
+
+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);
+
+ if (! (m_rset= new (&m_rset_root) List<Ed_row>))
+ return TRUE;
+
+ m_column_count= columns->elements;
+
+ return FALSE;
+}
+
+
+/**
+ Normally this is a separate result set with OUT parameters
+ of stored procedures. Currently unsupported for the local
+ version.
+*/
+
+bool Protocol_local::send_out_parameters(List<Item_param> *sp_params)
+{
+ return FALSE;
+}
+
+
+/** Called for statements that don't have a result set, at statement end. */
+
+bool
+Protocol_local::send_ok(uint server_status, uint statement_warn_count,
+ ulonglong affected_rows, ulonglong last_insert_id,
+ const char *message)
+{
+ /*
+ Just make sure nothing is sent to the client, we have grabbed
+ the status information in the connection diagnostics area.
+ */
+ return FALSE;
+}
+
+
+/**
+ Called at the end of a result set. Append a complete
+ result set to the list in Ed_connection.
+
+ Don't send anything to the client, but instead finish
+ building of the result set at hand.
+*/
+
+bool Protocol_local::send_eof(uint server_status, uint statement_warn_count)
+{
+ Ed_result_set *ed_result_set;
+
+ DBUG_ASSERT(m_rset);
+
+ opt_add_row_to_rset();
+ m_current_row= 0;
+
+ ed_result_set= new (&m_rset_root) Ed_result_set(m_rset, m_column_count,
+ &m_rset_root);
+
+ m_rset= NULL;
+
+ if (! ed_result_set)
+ return TRUE;
+
+ /* In case of successful allocation memory ownership was transferred. */
+ DBUG_ASSERT(!alloc_root_inited(&m_rset_root));
+
+ /*
+ Link the created Ed_result_set instance into the list of connection
+ result sets. Never fails.
+ */
+ m_connection->add_result_set(ed_result_set);
+ return FALSE;
+}
+
+
+/** Called to send an error to the client at the end of a statement. */
+
+bool
+Protocol_local::send_error(uint sql_errno, const char *err_msg, const char*)
+{
+ /*
+ Just make sure that nothing is sent to the client (default
+ implementation).
+ */
+ return FALSE;
+}
+
+
+#ifdef EMBEDDED_LIBRARY
+void Protocol_local::remove_last_row()
+{ }
+#endif
diff --git a/sql/sql_prepare.h b/sql/sql_prepare.h
new file mode 100644
index 00000000000..e0891bbd188
--- /dev/null
+++ b/sql/sql_prepare.h
@@ -0,0 +1,368 @@
+#ifndef SQL_PREPARE_H
+#define SQL_PREPARE_H
+/* Copyright (c) 1995-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 "sql_error.h"
+
+class THD;
+struct LEX;
+
+/**
+ An interface that is used to take an action when
+ the locking module notices that a table version has changed
+ since the last execution. "Table" here may refer to any kind of
+ table -- a base table, a temporary table, a view or an
+ information schema table.
+
+ When we open and lock tables for execution of a prepared
+ statement, we must verify that they did not change
+ since statement prepare. If some table did change, the statement
+ parse tree *may* be no longer valid, e.g. in case it contains
+ optimizations that depend on table metadata.
+
+ This class provides an interface (a method) that is
+ invoked when such a situation takes place.
+ The implementation of the method simply reports an error, but
+ the exact details depend on the nature of the SQL statement.
+
+ At most 1 instance of this class is active at a time, in which
+ case THD::m_reprepare_observer is not NULL.
+
+ @sa check_and_update_table_version() for details of the
+ version tracking algorithm
+
+ @sa Open_tables_state::m_reprepare_observer for the life cycle
+ of metadata observers.
+*/
+
+class Reprepare_observer
+{
+public:
+ /**
+ Check if a change of metadata is OK. In future
+ the signature of this method may be extended to accept the old
+ and the new versions, but since currently the check is very
+ simple, we only need the THD to report an error.
+ */
+ bool report_error(THD *thd);
+ bool is_invalidated() const { return m_invalidated; }
+ void reset_reprepare_observer() { m_invalidated= FALSE; }
+private:
+ bool m_invalidated;
+};
+
+
+void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length);
+void mysqld_stmt_execute(THD *thd, char *packet, uint packet_length);
+void mysqld_stmt_close(THD *thd, char *packet);
+void mysql_sql_stmt_prepare(THD *thd);
+void mysql_sql_stmt_execute(THD *thd);
+void mysql_sql_stmt_close(THD *thd);
+void mysqld_stmt_fetch(THD *thd, char *packet, uint packet_length);
+void mysqld_stmt_reset(THD *thd, char *packet);
+void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
+void reinit_stmt_before_use(THD *thd, LEX *lex);
+
+/**
+ Execute a fragment of server code in an isolated context, so that
+ it doesn't leave any effect on THD. THD must have no open tables.
+ The code must not leave any open tables around.
+ The result of execution (if any) is stored in Ed_result.
+*/
+
+class Server_runnable
+{
+public:
+ virtual bool execute_server_code(THD *thd)= 0;
+ virtual ~Server_runnable();
+};
+
+
+/**
+ Execute direct interface.
+
+ @todo Implement support for prelocked mode.
+*/
+
+class Ed_row;
+
+/**
+ Ed_result_set -- a container with result set rows.
+ @todo Implement support for result set metadata and
+ automatic type conversion.
+*/
+
+class Ed_result_set: public Sql_alloc
+{
+public:
+ operator List<Ed_row>&() { return *m_rows; }
+ unsigned int size() const { return m_rows->elements; }
+
+ Ed_result_set(List<Ed_row> *rows_arg, size_t column_count,
+ MEM_ROOT *mem_root_arg);
+
+ /** We don't call member destructors, they all are POD types. */
+ ~Ed_result_set() {}
+
+ size_t get_field_count() const { return m_column_count; }
+
+ static void operator delete(void *ptr, size_t size) throw ();
+private:
+ Ed_result_set(const Ed_result_set &); /* not implemented */
+ Ed_result_set &operator=(Ed_result_set &); /* not implemented */
+private:
+ MEM_ROOT m_mem_root;
+ size_t m_column_count;
+ List<Ed_row> *m_rows;
+ Ed_result_set *m_next_rset;
+ friend class Ed_connection;
+};
+
+
+class Ed_connection
+{
+public:
+ /**
+ Construct a new "execute direct" connection.
+
+ The connection can be used to execute SQL statements.
+ If the connection failed to initialize, the error
+ will be returned on the attempt to execute a statement.
+
+ @pre thd must have no open tables
+ while the connection is used. However,
+ Ed_connection works okay in LOCK TABLES mode.
+ Other properties of THD, such as the current warning
+ information, errors, etc. do not matter and are
+ preserved by Ed_connection. One thread may have many
+ Ed_connections created for it.
+ */
+ Ed_connection(THD *thd);
+
+ /**
+ Execute one SQL statement.
+
+ Until this method is executed, no other methods of
+ Ed_connection can be used. Life cycle of Ed_connection is:
+
+ Initialized -> a statement has been executed ->
+ look at result, move to next result ->
+ look at result, move to next result ->
+ ...
+ moved beyond the last result == Initialized.
+
+ This method can be called repeatedly. Once it's invoked,
+ results of the previous execution are lost.
+
+ A result of execute_direct() can be either:
+
+ - success, no result set rows. In this case get_field_count()
+ returns 0. This happens after execution of INSERT, UPDATE,
+ DELETE, DROP and similar statements. Some other methods, such
+ as get_affected_rows() can be used to retrieve additional
+ result information.
+
+ - success, there are some result set rows (maybe 0). E.g.
+ happens after SELECT. In this case get_field_count() returns
+ the number of columns in a result set and store_result()
+ can be used to retrieve a result set..
+
+ - an error, methods to retrieve error information can
+ be used.
+
+ @return execution status
+ @retval FALSE success, use get_field_count()
+ to determine what to do next.
+ @retval TRUE error, use get_last_error()
+ to see the error number.
+ */
+ bool execute_direct(LEX_STRING sql_text);
+
+ /**
+ Same as the previous, but takes an instance of Server_runnable
+ instead of SQL statement text.
+
+ @return execution status
+
+ @retval FALSE success, use get_field_count()
+ if your code fragment is supposed to
+ return a result set
+ @retval TRUE failure
+ */
+ 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.
+
+ @sa Documentation for C API function
+ mysql_affected_rows().
+ */
+ ulonglong get_affected_rows() const
+ {
+ return m_diagnostics_area.affected_rows();
+ }
+
+ /**
+ Get the last insert id, if any.
+
+ @sa Documentation for mysql_insert_id().
+ */
+ ulonglong get_last_insert_id() const
+ {
+ return m_diagnostics_area.last_insert_id();
+ }
+
+ /**
+ Get the total number of warnings for the last executed
+ statement. Note, that there is only one warning list even
+ if a statement returns multiple results.
+
+ @sa Documentation for C API function
+ mysql_num_warnings().
+ */
+ ulong get_warn_count() const
+ {
+ return m_warning_info.warn_count();
+ }
+ /**
+ Get the server warnings as a result set.
+ The result set has fixed metadata:
+ The first column is the level.
+ The second is a numeric code.
+ The third is warning text.
+ */
+ List<MYSQL_ERROR> *get_warn_list() { return &m_warning_info.warn_list(); }
+ /**
+ The following members are only valid if execute_direct()
+ or move_to_next_result() returned an error.
+ They never fail, but if they are called when there is no
+ result, or no error, the result is not defined.
+ */
+ const char *get_last_error() const { return m_diagnostics_area.message(); }
+ unsigned int get_last_errno() const { return m_diagnostics_area.sql_errno(); }
+ const char *get_last_sqlstate() const { return m_diagnostics_area.get_sqlstate(); }
+
+ /**
+ Provided get_field_count() is not 0, this never fails. You don't
+ need to free the result set, this is done automatically when
+ you advance to the next result set or destroy the connection.
+ Not returning const because of List iterator not accepting
+ Should be used when you would like Ed_connection to manage
+ result set memory for you.
+ */
+ Ed_result_set *use_result_set() { return m_current_rset; }
+ /**
+ Provided get_field_count() is not 0, this never fails. You
+ must free the returned result set. This can be called only
+ once after execute_direct().
+ Should be used when you would like to get the results
+ and destroy the connection.
+ */
+ Ed_result_set *store_result_set();
+
+ /**
+ If the query returns multiple results, this method
+ can be checked if there is another result beyond the next
+ one.
+ Never fails.
+ */
+ bool has_next_result() const { return test(m_current_rset->m_next_rset); }
+ /**
+ Only valid to call if has_next_result() returned true.
+ Otherwise the result is undefined.
+ */
+ bool move_to_next_result()
+ {
+ m_current_rset= m_current_rset->m_next_rset;
+ return test(m_current_rset);
+ }
+
+ ~Ed_connection() { free_old_result(); }
+private:
+ Diagnostics_area m_diagnostics_area;
+ Warning_info m_warning_info;
+ /**
+ Execute direct interface does not support multi-statements, only
+ multi-results. So we never have a situation when we have
+ a mix of result sets and OK or error packets. We either
+ have a single result set, a single error, or a single OK,
+ or we have a series of result sets, followed by an OK or error.
+ */
+ THD *m_thd;
+ Ed_result_set *m_rsets;
+ Ed_result_set *m_current_rset;
+ friend class Protocol_local;
+private:
+ void free_old_result();
+ void add_result_set(Ed_result_set *ed_result_set);
+private:
+ Ed_connection(const Ed_connection &); /* not implemented */
+ Ed_connection &operator=(Ed_connection &); /* not implemented */
+};
+
+
+/** One result set column. */
+
+struct Ed_column: public LEX_STRING
+{
+ /** Implementation note: destructor for this class is never called. */
+};
+
+
+/** One result set record. */
+
+class Ed_row: public Sql_alloc
+{
+public:
+ const Ed_column &operator[](const unsigned int column_index) const
+ {
+ return *get_column(column_index);
+ }
+ const Ed_column *get_column(const unsigned int column_index) const
+ {
+ DBUG_ASSERT(column_index < size());
+ return m_column_array + column_index;
+ }
+ size_t size() const { return m_column_count; }
+
+ Ed_row(Ed_column *column_array_arg, size_t column_count_arg)
+ :m_column_array(column_array_arg),
+ m_column_count(column_count_arg)
+ {}
+private:
+ Ed_column *m_column_array;
+ size_t m_column_count; /* TODO: change to point to metadata */
+};
+
+#endif // SQL_PREPARE_H
diff --git a/sql/sql_priv.h b/sql/sql_priv.h
new file mode 100644
index 00000000000..749ee245aa7
--- /dev/null
+++ b/sql/sql_priv.h
@@ -0,0 +1,435 @@
+/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2014, Monty Program Ab.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/**
+ @file
+
+ @details
+ Mostly this file is used in the server. But a little part of it is used in
+ mysqlbinlog too (definition of SELECT_DISTINCT and others).
+ The consequence is that 90% of the file is wrapped in \#ifndef MYSQL_CLIENT,
+ except the part which must be in the server and in the client.
+*/
+
+#ifndef SQL_PRIV_INCLUDED
+#define SQL_PRIV_INCLUDED
+
+#ifndef MYSQL_CLIENT
+
+/*
+ Generates a warning that a feature is deprecated. After a specified
+ version asserts that the feature is removed.
+
+ Using it as
+
+ WARN_DEPRECATED(thd, 6,2, "BAD", "'GOOD'");
+
+ Will result in a warning
+
+ "The syntax 'BAD' is deprecated and will be removed in MySQL 6.2. Please
+ use 'GOOD' instead"
+
+ Note that in macro arguments BAD is not quoted, while 'GOOD' is.
+ Note that the version is TWO numbers, separated with a comma
+ (two macro arguments, that is)
+*/
+#define WARN_DEPRECATED(Thd,VerHi,VerLo,Old,New) \
+ do { \
+ compile_time_assert(MYSQL_VERSION_ID < VerHi * 10000 + VerLo * 100); \
+ if (((THD *) Thd) != NULL) \
+ push_warning_printf(((THD *) Thd), MYSQL_ERROR::WARN_LEVEL_WARN, \
+ ER_WARN_DEPRECATED_SYNTAX, \
+ ER(ER_WARN_DEPRECATED_SYNTAX), \
+ (Old), (New)); \
+ else \
+ sql_print_warning("The syntax '%s' is deprecated and will be removed " \
+ "in a future release. Please use %s instead.", \
+ (Old), (New)); \
+ } while(0)
+
+
+/*
+ Generates a warning that a feature is deprecated and there is no replacement.
+
+ Using it as
+
+ WARN_DEPRECATED_NO_REPLACEMENT(thd, "BAD");
+
+ Will result in a warning
+
+ "'BAD' is deprecated and will be removed in a future release."
+
+ Note that in macro arguments BAD is not quoted.
+*/
+
+#define WARN_DEPRECATED_NO_REPLACEMENT(Thd,Old) \
+ do { \
+ if (((THD *) Thd) != NULL) \
+ push_warning_printf(((THD *) Thd), MYSQL_ERROR::WARN_LEVEL_WARN, \
+ ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT, \
+ ER(ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT), \
+ (Old)); \
+ else \
+ sql_print_warning("'%s' is deprecated and will be removed " \
+ "in a future release.", (Old)); \
+ } while(0)
+
+/*************************************************************************/
+
+#endif
+
+/*
+ This is included in the server and in the client.
+ Options for select set by the yacc parser (stored in lex->options).
+
+ NOTE
+ log_event.h defines OPTIONS_WRITTEN_TO_BIN_LOG to specify what THD
+ options list are written into binlog. These options can NOT change their
+ values, or it will break replication between version.
+
+ context is encoded as following:
+ SELECT - SELECT_LEX_NODE::options
+ THD - THD::options
+ intern - neither. used only as
+ func(..., select_node->options | thd->options | OPTION_XXX, ...)
+
+ TODO: separate three contexts above, move them to separate bitfields.
+*/
+
+#define SELECT_DISTINCT (1ULL << 0) // SELECT, user
+#define SELECT_STRAIGHT_JOIN (1ULL << 1) // SELECT, user
+#define SELECT_DESCRIBE (1ULL << 2) // SELECT, user
+#define SELECT_SMALL_RESULT (1ULL << 3) // SELECT, user
+#define SELECT_BIG_RESULT (1ULL << 4) // SELECT, user
+#define OPTION_FOUND_ROWS (1ULL << 5) // SELECT, user
+#define OPTION_TO_QUERY_CACHE (1ULL << 6) // SELECT, user
+#define SELECT_NO_JOIN_CACHE (1ULL << 7) // intern
+/** always the opposite of OPTION_NOT_AUTOCOMMIT except when in fix_autocommit() */
+#define OPTION_AUTOCOMMIT (1ULL << 8) // THD, user
+#define OPTION_BIG_SELECTS (1ULL << 9) // THD, user
+#define OPTION_LOG_OFF (1ULL << 10) // THD, user
+#define OPTION_QUOTE_SHOW_CREATE (1ULL << 11) // THD, user, unused
+#define TMP_TABLE_ALL_COLUMNS (1ULL << 12) // SELECT, intern
+#define OPTION_WARNINGS (1ULL << 13) // THD, user
+#define OPTION_AUTO_IS_NULL (1ULL << 14) // THD, user, binlog
+#define OPTION_FOUND_COMMENT (1ULL << 15) // SELECT, intern, parser
+#define OPTION_SAFE_UPDATES (1ULL << 16) // THD, user
+#define OPTION_BUFFER_RESULT (1ULL << 17) // SELECT, user
+#define OPTION_BIN_LOG (1ULL << 18) // THD, user
+#define OPTION_NOT_AUTOCOMMIT (1ULL << 19) // THD, user
+#define OPTION_BEGIN (1ULL << 20) // THD, intern
+#define OPTION_TABLE_LOCK (1ULL << 21) // THD, intern
+#define OPTION_QUICK (1ULL << 22) // SELECT (for DELETE)
+#define OPTION_KEEP_LOG (1ULL << 23) // THD, user
+
+/* The following is used to detect a conflict with DISTINCT */
+#define SELECT_ALL (1ULL << 24) // SELECT, user, parser
+/** The following can be set when importing tables in a 'wrong order'
+ to suppress foreign key checks */
+#define OPTION_NO_FOREIGN_KEY_CHECKS (1ULL << 26) // THD, user, binlog
+/** The following speeds up inserts to InnoDB tables by suppressing unique
+ key checks in some cases */
+#define OPTION_RELAXED_UNIQUE_CHECKS (1ULL << 27) // THD, user, binlog
+#define SELECT_NO_UNLOCK (1ULL << 28) // SELECT, intern
+#define OPTION_SCHEMA_TABLE (1ULL << 29) // SELECT, intern
+/** Flag set if setup_tables already done */
+#define OPTION_SETUP_TABLES_DONE (1ULL << 30) // intern
+/** If not set then the thread will ignore all warnings with level notes. */
+#define OPTION_SQL_NOTES (1ULL << 31) // THD, user
+/**
+ Force the used temporary table to be a MyISAM table (because we will use
+ fulltext functions when reading from it.
+*/
+#define TMP_TABLE_FORCE_MYISAM (1ULL << 32)
+#define OPTION_PROFILING (1ULL << 33)
+/**
+ Indicates that this is a HIGH_PRIORITY SELECT.
+ Currently used only for printing of such selects.
+ Type of locks to be acquired is specified directly.
+*/
+#define SELECT_HIGH_PRIORITY (1ULL << 34) // SELECT, user
+/**
+ Is set in slave SQL thread when there was an
+ error on master, which, when is not reproducible
+ on slave (i.e. the query succeeds on slave),
+ is not terminal to the state of repliation,
+ and should be ignored. The slave SQL thread,
+ however, needs to rollback the effects of the
+ succeeded statement to keep replication consistent.
+*/
+#define OPTION_MASTER_SQL_ERROR (1ULL << 35)
+
+/*
+ Dont report errors for individual rows,
+ But just report error on commit (or read ofcourse)
+ Note! Reserved for use in MySQL Cluster
+*/
+#define OPTION_ALLOW_BATCH (ULL(1) << 36) // THD, intern (slave)
+#define OPTION_SKIP_REPLICATION (ULL(1) << 37) // THD, user
+
+/*
+ Check how many bytes are available on buffer.
+
+ @param buf_start Pointer to buffer start.
+ @param buf_current Pointer to the current position on buffer.
+ @param buf_len Buffer length.
+
+ @return Number of bytes available on event buffer.
+*/
+template <class T> T available_buffer(const char* buf_start,
+ const char* buf_current,
+ T buf_len)
+{
+ return buf_len - (buf_current - buf_start);
+}
+
+/*
+ Check if jump value is within buffer limits.
+
+ @param jump Number of positions we want to advance.
+ @param buf_start Pointer to buffer start
+ @param buf_current Pointer to the current position on buffer.
+ @param buf_len Buffer length.
+
+ @return True If jump value is within buffer limits.
+ False Otherwise.
+*/
+template <class T> bool valid_buffer_range(T jump,
+ const char* buf_start,
+ const char* buf_current,
+ T buf_len)
+{
+ return (jump <= available_buffer(buf_start, buf_current, buf_len));
+}
+
+/* The rest of the file is included in the server only */
+#ifndef MYSQL_CLIENT
+
+/* @@optimizer_switch flags. These must be in sync with optimizer_switch_typelib */
+#define OPTIMIZER_SWITCH_INDEX_MERGE (1ULL << 0)
+#define OPTIMIZER_SWITCH_INDEX_MERGE_UNION (1ULL << 1)
+#define OPTIMIZER_SWITCH_INDEX_MERGE_SORT_UNION (1ULL << 2)
+#define OPTIMIZER_SWITCH_INDEX_MERGE_INTERSECT (1ULL << 3)
+#define OPTIMIZER_SWITCH_INDEX_MERGE_SORT_INTERSECT (1ULL << 4)
+#define OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN (1ULL << 5)
+#define OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN (1ULL << 6)
+#define OPTIMIZER_SWITCH_DERIVED_MERGE (1ULL << 7)
+#define OPTIMIZER_SWITCH_DERIVED_WITH_KEYS (1ULL << 8)
+#define OPTIMIZER_SWITCH_FIRSTMATCH (1ULL << 9)
+#define OPTIMIZER_SWITCH_LOOSE_SCAN (1ULL << 10)
+#define OPTIMIZER_SWITCH_MATERIALIZATION (1ULL << 11)
+#define OPTIMIZER_SWITCH_IN_TO_EXISTS (1ULL << 12)
+#define OPTIMIZER_SWITCH_SEMIJOIN (1ULL << 13)
+#define OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE (1ULL << 14)
+#define OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN (1ULL << 15)
+#define OPTIMIZER_SWITCH_SUBQUERY_CACHE (1ULL << 16)
+/** If this is off, MRR is never used. */
+#define OPTIMIZER_SWITCH_MRR (1ULL << 17)
+/**
+ If OPTIMIZER_SWITCH_MRR is on and this is on, MRR is used depending on a
+ cost-based choice ("automatic"). If OPTIMIZER_SWITCH_MRR is on and this is
+ off, MRR is "forced" (i.e. used as long as the storage engine is capable of
+ doing it).
+*/
+#define OPTIMIZER_SWITCH_MRR_COST_BASED (1ULL << 18)
+#define OPTIMIZER_SWITCH_MRR_SORT_KEYS (1ULL << 19)
+#define OPTIMIZER_SWITCH_OUTER_JOIN_WITH_CACHE (1ULL << 20)
+#define OPTIMIZER_SWITCH_SEMIJOIN_WITH_CACHE (1ULL << 21)
+#define OPTIMIZER_SWITCH_JOIN_CACHE_INCREMENTAL (1ULL << 22)
+#define OPTIMIZER_SWITCH_JOIN_CACHE_HASHED (1ULL << 23)
+#define OPTIMIZER_SWITCH_JOIN_CACHE_BKA (1ULL << 24)
+#define OPTIMIZER_SWITCH_OPTIMIZE_JOIN_BUFFER_SIZE (1ULL << 25)
+#define OPTIMIZER_SWITCH_TABLE_ELIMINATION (1ULL << 26)
+#define OPTIMIZER_SWITCH_EXTENDED_KEYS (1ULL << 27)
+#define OPTIMIZER_SWITCH_LAST (1ULL << 27)
+
+#define OPTIMIZER_SWITCH_DEFAULT (OPTIMIZER_SWITCH_INDEX_MERGE | \
+ OPTIMIZER_SWITCH_INDEX_MERGE_UNION | \
+ OPTIMIZER_SWITCH_INDEX_MERGE_SORT_UNION | \
+ OPTIMIZER_SWITCH_INDEX_MERGE_INTERSECT | \
+ OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN | \
+ OPTIMIZER_SWITCH_DERIVED_MERGE | \
+ OPTIMIZER_SWITCH_DERIVED_WITH_KEYS | \
+ OPTIMIZER_SWITCH_TABLE_ELIMINATION | \
+ OPTIMIZER_SWITCH_IN_TO_EXISTS | \
+ OPTIMIZER_SWITCH_MATERIALIZATION | \
+ OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE|\
+ OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN|\
+ OPTIMIZER_SWITCH_OUTER_JOIN_WITH_CACHE | \
+ OPTIMIZER_SWITCH_SEMIJOIN_WITH_CACHE | \
+ OPTIMIZER_SWITCH_JOIN_CACHE_INCREMENTAL | \
+ OPTIMIZER_SWITCH_JOIN_CACHE_HASHED | \
+ OPTIMIZER_SWITCH_JOIN_CACHE_BKA | \
+ OPTIMIZER_SWITCH_SUBQUERY_CACHE | \
+ OPTIMIZER_SWITCH_SEMIJOIN | \
+ OPTIMIZER_SWITCH_FIRSTMATCH | \
+ OPTIMIZER_SWITCH_LOOSE_SCAN )
+/*
+ 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
+ contact the replication team because the replication code should then be
+ updated (to store more bytes on disk).
+
+ NOTE: When adding new SQL_MODE types, make sure to also add them to
+ the scripts used for creating the MySQL system tables
+ in scripts/mysql_system_tables.sql and scripts/mysql_system_tables_fix.sql
+
+*/
+
+/*
+ Flags below are set when we perform
+ context analysis of the statement and make
+ subqueries non-const. It prevents subquery
+ evaluation at context analysis stage.
+*/
+
+/*
+ Don't evaluate this subquery during statement prepare even if
+ it's a constant one. The flag is switched off in the end of
+ mysqld_stmt_prepare.
+*/
+#define CONTEXT_ANALYSIS_ONLY_PREPARE 1
+/*
+ Special JOIN::prepare mode: changing of query is prohibited.
+ When creating a view, we need to just check its syntax omitting
+ any optimizations: afterwards definition of the view will be
+ reconstructed by means of ::print() methods and written to
+ to an .frm file. We need this definition to stay untouched.
+*/
+#define CONTEXT_ANALYSIS_ONLY_VIEW 2
+/*
+ Don't evaluate this subquery during derived table prepare even if
+ it's a constant one.
+*/
+#define CONTEXT_ANALYSIS_ONLY_DERIVED 4
+/*
+ Don't evaluate constant sub-expressions of virtual column
+ expressions when opening tables
+*/
+#define CONTEXT_ANALYSIS_ONLY_VCOL_EXPR 8
+
+
+/*
+ Uncachable causes:
+*/
+/* This subquery has fields from outer query (put by user) */
+#define UNCACHEABLE_DEPENDENT_GENERATED 1
+/* This subquery contains functions with random result */
+#define UNCACHEABLE_RAND 2
+/* This subquery contains functions with side effect */
+#define UNCACHEABLE_SIDEEFFECT 4
+/* Forcing to save JOIN tables for explain */
+#define UNCACHEABLE_EXPLAIN 8
+/* For uncorrelated SELECT in an UNION with some correlated SELECTs */
+#define UNCACHEABLE_UNITED 16
+#define UNCACHEABLE_CHECKOPTION 32
+/*
+ This subquery has fields from outer query injected during
+ transformation process
+*/
+#define UNCACHEABLE_DEPENDENT_INJECTED 64
+/* This subquery has fields from outer query (any nature) */
+#define UNCACHEABLE_DEPENDENT (UNCACHEABLE_DEPENDENT_GENERATED | \
+ UNCACHEABLE_DEPENDENT_INJECTED)
+
+/* Used to check GROUP BY list in the MODE_ONLY_FULL_GROUP_BY mode */
+#define UNDEF_POS (-1)
+
+/* BINLOG_DUMP options */
+
+#define BINLOG_DUMP_NON_BLOCK 1
+#endif /* !MYSQL_CLIENT */
+
+#define BINLOG_SEND_ANNOTATE_ROWS_EVENT 2
+
+#ifndef MYSQL_CLIENT
+
+/*
+ Some defines for exit codes for ::is_equal class functions.
+*/
+#define IS_EQUAL_NO 0
+#define IS_EQUAL_YES 1
+#define IS_EQUAL_PACK_LENGTH 2
+
+enum enum_parsing_place
+{
+ NO_MATTER,
+ IN_HAVING,
+ SELECT_LIST,
+ IN_WHERE,
+ IN_ON,
+ IN_GROUP_BY,
+ PARSING_PLACE_SIZE /* always should be the last */
+};
+
+
+enum enum_var_type
+{
+ OPT_DEFAULT= 0, OPT_SESSION, OPT_GLOBAL
+};
+
+class sys_var;
+
+enum enum_yes_no_unknown
+{
+ TVL_YES, TVL_NO, TVL_UNKNOWN
+};
+
+#ifdef MYSQL_SERVER
+/*
+ External variables
+*/
+
+/* sql_yacc.cc */
+#ifndef DBUG_OFF
+extern void turn_parser_debug_on();
+
+#endif
+
+/**
+ convert a hex digit into number.
+*/
+
+inline int hexchar_to_int(char c)
+{
+ if (c <= '9' && c >= '0')
+ return c-'0';
+ c|=32;
+ if (c <= 'f' && c >= 'a')
+ return c-'a'+10;
+ return -1;
+}
+
+/* This must match the path length limit in the ER_NOT_RW_DIR error msg. */
+#define ER_NOT_RW_DIR_PATHSIZE 200
+
+#define IS_TABLESPACES_TABLESPACE_NAME 0
+#define IS_TABLESPACES_ENGINE 1
+#define IS_TABLESPACES_TABLESPACE_TYPE 2
+#define IS_TABLESPACES_LOGFILE_GROUP_NAME 3
+#define IS_TABLESPACES_EXTENT_SIZE 4
+#define IS_TABLESPACES_AUTOEXTEND_SIZE 5
+#define IS_TABLESPACES_MAXIMUM_SIZE 6
+#define IS_TABLESPACES_NODEGROUP_ID 7
+#define IS_TABLESPACES_TABLESPACE_COMMENT 8
+
+bool db_name_is_in_ignore_db_dirs_list(const char *dbase);
+
+#endif /* MYSQL_SERVER */
+
+#endif /* MYSQL_CLIENT */
+
+#endif /* SQL_PRIV_INCLUDED */
diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc
index 790fff70c7e..feb7810fa28 100644
--- a/sql/sql_profile.cc
+++ b/sql/sql_profile.cc
@@ -1,5 +1,4 @@
-/*
- Copyright (c) 2007, 2012, Oracle and/or its affiliates.
+/* Copyright (c) 2007, 2012, Oracle and/or its affiliates.
Copyright (c) 2008, 2012, Monty Program Ab
This program is free software; you can redistribute it and/or modify
@@ -13,8 +12,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@@ -32,8 +30,16 @@
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h" // REQUIRED: for other includes
+#include "sql_profile.h"
#include "my_sys.h"
+#include "sql_show.h" // schema_table_store_record
+#include "sql_class.h" // THD
+
+#ifdef _WIN32
+#pragma comment(lib,"psapi.lib")
+#endif
#define TIME_FLOAT_DIGITS 9
/** two vals encoded: (len*100)+dec */
@@ -48,7 +54,7 @@
int fill_query_profile_statistics_info(THD *thd, TABLE_LIST *tables,
Item *cond)
{
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+#if defined(ENABLED_PROFILING)
return(thd->profiling.fill_statistics_info(thd, tables, cond));
#else
my_error(ER_FEATURE_DISABLED, MYF(0), "SHOW PROFILE", "enable-profiling");
@@ -130,28 +136,31 @@ int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table)
}
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
+#if defined(ENABLED_PROFILING)
#define RUSAGE_USEC(tv) ((tv).tv_sec*1000*1000 + (tv).tv_usec)
#define RUSAGE_DIFF_USEC(tv1, tv2) (RUSAGE_USEC((tv1))-RUSAGE_USEC((tv2)))
-#ifdef __WIN__
-inline ULONGLONG FileTimeToQuadWord(FILETIME *ft)
+#ifdef _WIN32
+static ULONGLONG FileTimeToQuadWord(FILETIME *ft)
{
- ULONGLONG nrv = 0;
- nrv |= ft->dwHighDateTime;
- nrv <<= 32;
- nrv |= ft->dwLowDateTime;
- return nrv;
+ // Overlay FILETIME onto a ULONGLONG.
+ union {
+ ULONGLONG qwTime;
+ FILETIME ft;
+ } u;
+
+ u.ft = *ft;
+ return u.qwTime;
}
// Get time difference between to FILETIME objects in seconds.
-inline double GetTimeDiffInSeconds(FILETIME *a, FILETIME *b)
+static double GetTimeDiffInSeconds(FILETIME *a, FILETIME *b)
{
return ((FileTimeToQuadWord(a) - FileTimeToQuadWord(b)) / 1e7);
}
-#endif /* __WIN__ */
+#endif
PROF_MEASUREMENT::PROF_MEASUREMENT(QUERY_PROFILE *profile_arg, const char
*status_arg)
@@ -174,8 +183,7 @@ PROF_MEASUREMENT::PROF_MEASUREMENT(QUERY_PROFILE *profile_arg,
PROF_MEASUREMENT::~PROF_MEASUREMENT()
{
- if (allocated_status_memory != NULL)
- my_free(allocated_status_memory, MYF(0));
+ my_free(allocated_status_memory);
status= function= file= NULL;
}
@@ -242,8 +250,11 @@ void PROF_MEASUREMENT::collect()
time_usecs= my_interval_timer() / 1e3; /* ns to us */
#ifdef HAVE_GETRUSAGE
getrusage(RUSAGE_SELF, &rusage);
-#elif defined(__WIN__)
+#elif defined(_WIN32)
FILETIME ftDummy;
+ // NOTE: Get{Process|Thread}Times has a granularity of the clock interval,
+ // which is typically ~15ms. So intervals shorter than that will not be
+ // measurable by this function.
GetProcessTimes(GetCurrentProcess(), &ftDummy, &ftDummy, &ftKernel, &ftUser);
GetProcessIoCounters(GetCurrentProcess(), &io_count);
GetProcessMemoryInfo(GetCurrentProcess(), &mem_count, sizeof(mem_count));
@@ -267,8 +278,7 @@ QUERY_PROFILE::~QUERY_PROFILE()
while (! entries.is_empty())
delete entries.pop();
- if (query_source != NULL)
- my_free(query_source, MYF(0));
+ my_free(query_source);
}
/**
@@ -295,7 +305,7 @@ void QUERY_PROFILE::new_status(const char *status_arg,
DBUG_ASSERT(status_arg != NULL);
if ((function_arg != NULL) && (file_arg != NULL))
- prof= new PROF_MEASUREMENT(this, status_arg, function_arg, file_arg, line_arg);
+ prof= new PROF_MEASUREMENT(this, status_arg, function_arg, base_name(file_arg), line_arg);
else
prof= new PROF_MEASUREMENT(this, status_arg);
@@ -372,7 +382,7 @@ void PROFILING::start_new_query(const char *initial_state)
finish_current_query();
}
- enabled= (((thd)->options & OPTION_PROFILING) != 0);
+ enabled= ((thd->variables.option_bits & OPTION_PROFILING) != 0);
if (! enabled) DBUG_VOID_RETURN;
@@ -410,7 +420,7 @@ void PROFILING::finish_current_query()
status_change("ending", NULL, NULL, 0);
if ((enabled) && /* ON at start? */
- ((thd->options & OPTION_PROFILING) != 0) && /* and ON at end? */
+ ((thd->variables.option_bits & OPTION_PROFILING) != 0) && /* and ON at end? */
(current->query_source != NULL) &&
(! current->entries.is_empty()))
{
@@ -446,7 +456,7 @@ bool PROFILING::show_profiles()
MYSQL_TYPE_DOUBLE));
field_list.push_back(new Item_empty_string("Query", 40));
- if (thd->protocol->send_fields(&field_list,
+ if (thd->protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
@@ -624,7 +634,7 @@ int PROFILING::fill_statistics_info(THD *thd_arg, TABLE_LIST *tables, Item *cond
table->field[5]->store_decimal(&cpu_stime_decimal);
table->field[4]->set_notnull();
table->field[5]->set_notnull();
-#elif defined(__WIN__)
+#elif defined(_WIN32)
my_decimal cpu_utime_decimal, cpu_stime_decimal;
double2my_decimal(E_DEC_FATAL_ERROR,
diff --git a/sql/sql_profile.h b/sql/sql_profile.h
index 69cc12ae545..f8970bb162a 100644
--- a/sql/sql_profile.h
+++ b/sql/sql_profile.h
@@ -1,5 +1,4 @@
-/*
- Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2007, 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
@@ -12,12 +11,17 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef _SQL_PROFILE_H
#define _SQL_PROFILE_H
+class Item;
+struct TABLE_LIST;
+class THD;
+typedef struct st_field_info ST_FIELD_INFO;
+typedef struct st_schema_table ST_SCHEMA_TABLE;
+
extern ST_FIELD_INFO query_profile_statistics_info[];
int fill_query_profile_statistics_info(THD *thd, TABLE_LIST *tables, Item *cond);
int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table);
@@ -35,8 +39,9 @@ int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table);
#define PROFILE_ALL (uint)(~0)
-#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
-#include "mysql_priv.h"
+#if defined(ENABLED_PROFILING)
+#include "sql_priv.h"
+#include "unireg.h"
#ifdef __WIN__
#include <psapi.h>
@@ -81,7 +86,7 @@ public:
for (i= first; i != NULL; i= after_i)
{
after_i= i->next;
- my_free((char *) i, MYF(0));
+ my_free(i);
}
elements= 0;
}
@@ -128,7 +133,7 @@ public:
last= NULL;
first= first->next;
- my_free((char *)old_item, MYF(0));
+ my_free(old_item);
elements--;
return ret;
@@ -171,7 +176,7 @@ private:
char *status;
#ifdef HAVE_GETRUSAGE
struct rusage rusage;
-#elif defined(__WIN__)
+#elif defined(_WIN32)
FILETIME ftKernel, ftUser;
IO_COUNTERS io_count;
PROCESS_MEMORY_COUNTERS mem_count;
@@ -284,5 +289,5 @@ public:
int fill_statistics_info(THD *thd, TABLE_LIST *tables, Item *cond);
};
-# endif /* HAVE_PROFILING */
+# endif /* ENABLED_PROFILING */
#endif /* _SQL_PROFILE_H */
diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc
new file mode 100644
index 00000000000..914b9026014
--- /dev/null
+++ b/sql/sql_reload.cc
@@ -0,0 +1,527 @@
+/* Copyright (c) 2010, 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-1301 USA */
+
+#include "sql_reload.h"
+#include "sql_priv.h"
+#include "mysqld.h" // select_errors
+#include "sql_class.h" // THD
+#include "sql_acl.h" // acl_reload
+#include "sql_servers.h" // servers_reload
+#include "sql_connect.h" // reset_mqh
+#include "sql_base.h" // close_cached_tables
+#include "sql_db.h" // my_dbopt_cleanup
+#include "hostname.h" // hostname_cache_refresh
+#include "sql_repl.h" // reset_master, reset_slave
+#include "rpl_mi.h" // Master_info::data_lock
+#include "debug_sync.h"
+
+static void disable_checkpoints(THD *thd);
+
+/**
+ Reload/resets privileges and the different caches.
+
+ @param thd Thread handler (can be NULL!)
+ @param options What should be reset/reloaded (tables, privileges, slave...)
+ @param tables Tables to flush (if any)
+ @param write_to_binlog < 0 if there was an error while interacting with the binary log inside
+ reload_acl_and_cache,
+ 0 if we should not write to the binary log,
+ > 0 if we can write to the binlog.
+
+
+ @note Depending on 'options', it may be very bad to write the
+ query to the binlog (e.g. FLUSH SLAVE); this is a
+ pointer where reload_acl_and_cache() will put 0 if
+ it thinks we really should not write to the binlog.
+ Otherwise it will put 1.
+
+ @return Error status code
+ @retval 0 Ok
+ @retval !=0 Error; thd->killed is set or thd->is_error() is true
+*/
+
+bool reload_acl_and_cache(THD *thd, unsigned long options,
+ TABLE_LIST *tables, int *write_to_binlog)
+{
+ bool result=0;
+ select_errors=0; /* Write if more errors */
+ int tmp_write_to_binlog= *write_to_binlog= 1;
+
+ DBUG_ASSERT(!thd || !thd->in_sub_stmt);
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (options & REFRESH_GRANT)
+ {
+ THD *tmp_thd= 0;
+ /*
+ 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)))
+ {
+ thd->thread_stack= (char*) &tmp_thd;
+ thd->store_globals();
+ }
+
+ if (thd)
+ {
+ bool reload_acl_failed= acl_reload(thd);
+ bool reload_grants_failed= grant_reload(thd);
+ bool reload_servers_failed= servers_reload(thd);
+
+ if (reload_acl_failed || reload_grants_failed || reload_servers_failed)
+ {
+ result= 1;
+ /*
+ When an error is returned, my_message may have not been called and
+ the client will hang waiting for a response.
+ */
+ my_error(ER_UNKNOWN_ERROR, MYF(0));
+ }
+ }
+
+ if (tmp_thd)
+ {
+ delete tmp_thd;
+ /* Remember that we don't have a THD */
+ my_pthread_setspecific_ptr(THR_THD, 0);
+ thd= 0;
+ }
+ reset_mqh((LEX_USER *)NULL, TRUE);
+ }
+#endif
+ if (options & REFRESH_LOG)
+ {
+ /*
+ Flush the normal query log, the update log, the binary log,
+ the slow query log, the relay log (if it exists) and the log
+ tables.
+ */
+
+ options|= REFRESH_BINARY_LOG;
+ options|= REFRESH_RELAY_LOG;
+ options|= REFRESH_SLOW_LOG;
+ options|= REFRESH_GENERAL_LOG;
+ options|= REFRESH_ENGINE_LOG;
+ options|= REFRESH_ERROR_LOG;
+ }
+
+ 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));
+ result= 1;
+ }
+
+ if ((options & REFRESH_SLOW_LOG) && opt_slow_log)
+ logger.flush_slow_log();
+
+ if ((options & REFRESH_GENERAL_LOG) && opt_log)
+ logger.flush_general_log();
+
+ if (options & REFRESH_ENGINE_LOG)
+ if (ha_flush_logs(NULL))
+ result= 1;
+
+ if (options & REFRESH_BINARY_LOG)
+ {
+ /*
+ Writing this command to the binlog may result in infinite loops
+ when doing mysqlbinlog|mysql, and anyway it does not really make
+ sense to log it automatically (would cause more trouble to users
+ than it would help them)
+ */
+ tmp_write_to_binlog= 0;
+ if (mysql_bin_log.is_open())
+ {
+ if (mysql_bin_log.rotate_and_purge(true))
+ *write_to_binlog= -1;
+ }
+ }
+ if (options & REFRESH_RELAY_LOG)
+ {
+#ifdef HAVE_REPLICATION
+ mysql_mutex_lock(&active_mi->data_lock);
+ if (rotate_relay_log(active_mi))
+ *write_to_binlog= -1;
+ mysql_mutex_unlock(&active_mi->data_lock);
+#endif
+ }
+#ifdef HAVE_QUERY_CACHE
+ if (options & REFRESH_QUERY_CACHE_FREE)
+ {
+ query_cache.pack(thd); // FLUSH QUERY CACHE
+ options &= ~REFRESH_QUERY_CACHE; // Don't flush cache, just free memory
+ }
+ if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE))
+ {
+ query_cache.flush(); // RESET QUERY CACHE
+ }
+#endif /*HAVE_QUERY_CACHE*/
+
+ DBUG_ASSERT(!thd || thd->locked_tables_mode ||
+ !thd->mdl_context.has_locks() ||
+ thd->handler_tables_hash.records ||
+ thd->global_read_lock.is_acquired());
+
+ /*
+ Note that if REFRESH_READ_LOCK bit is set then REFRESH_TABLES is set too
+ (see sql_yacc.yy)
+ */
+ if (options & (REFRESH_TABLES | REFRESH_READ_LOCK))
+ {
+ if ((options & REFRESH_READ_LOCK) && thd)
+ {
+ /*
+ On the first hand we need write lock on the tables to be flushed,
+ on the other hand we must not try to aspire a global read lock
+ if we have a write locked table as this would lead to a deadlock
+ when trying to reopen (and re-lock) the table after the flush.
+ */
+ if (thd->locked_tables_mode)
+ {
+ my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
+ return 1;
+ }
+ /*
+ Writing to the binlog could cause deadlocks, as we don't log
+ UNLOCK TABLES
+ */
+ tmp_write_to_binlog= 0;
+ if (thd->global_read_lock.lock_global_read_lock(thd))
+ return 1; // Killed
+ if (close_cached_tables(thd, tables,
+ ((options & REFRESH_FAST) ? FALSE : TRUE),
+ thd->variables.lock_wait_timeout))
+ {
+ /*
+ NOTE: my_error() has been already called by reopen_tables() within
+ close_cached_tables().
+ */
+ result= 1;
+ }
+
+ if (thd->global_read_lock.make_global_read_lock_block_commit(thd)) // Killed
+ {
+ /* Don't leave things in a half-locked state */
+ thd->global_read_lock.unlock_global_read_lock(thd);
+ return 1;
+ }
+ if (options & REFRESH_CHECKPOINT)
+ disable_checkpoints(thd);
+ }
+ else
+ {
+ if (thd && thd->locked_tables_mode)
+ {
+ /*
+ If we are under LOCK TABLES we should have a write
+ lock on tables which we are going to flush.
+ */
+ if (tables)
+ {
+ for (TABLE_LIST *t= tables; t; t= t->next_local)
+ if (!find_table_for_mdl_upgrade(thd, t->db, t->table_name, false))
+ return 1;
+ }
+ else
+ {
+ /*
+ It is not safe to upgrade the metadata lock without GLOBAL IX lock.
+ This can happen with FLUSH TABLES <list> WITH READ LOCK as we in
+ these cases don't take a GLOBAL IX lock in order to be compatible
+ with global read lock.
+ */
+ if (thd->open_tables &&
+ !thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, "", "",
+ MDL_INTENTION_EXCLUSIVE))
+ {
+ my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0),
+ thd->open_tables->s->table_name.str);
+ return true;
+ }
+
+ for (TABLE *tab= thd->open_tables; tab; tab= tab->next)
+ {
+ if (! tab->mdl_ticket->is_upgradable_or_exclusive())
+ {
+ my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0),
+ tab->s->table_name.str);
+ return 1;
+ }
+ }
+ }
+ }
+
+ if (close_cached_tables(thd, tables,
+ ((options & REFRESH_FAST) ? FALSE : TRUE),
+ (thd ? thd->variables.lock_wait_timeout :
+ LONG_TIMEOUT)))
+ {
+ /*
+ NOTE: my_error() has been already called by reopen_tables() within
+ close_cached_tables().
+ */
+ result= 1;
+ }
+ }
+ my_dbopt_cleanup();
+ }
+ if (options & REFRESH_HOSTS)
+ hostname_cache_refresh();
+ if (thd && (options & REFRESH_STATUS))
+ refresh_status(thd);
+ if (options & REFRESH_THREADS)
+ flush_thread_cache();
+#ifdef HAVE_REPLICATION
+ if (options & REFRESH_MASTER)
+ {
+ DBUG_ASSERT(thd);
+ tmp_write_to_binlog= 0;
+ if (reset_master(thd))
+ {
+ /* NOTE: my_error() has been already called by reset_master(). */
+ result= 1;
+ }
+ }
+#endif
+#ifdef OPENSSL
+ if (options & REFRESH_DES_KEY_FILE)
+ {
+ if (des_key_file && load_des_key_file(des_key_file))
+ {
+ /* NOTE: my_error() has been already called by load_des_key_file(). */
+ result= 1;
+ }
+ }
+#endif
+#ifdef HAVE_REPLICATION
+ if (options & REFRESH_SLAVE)
+ {
+ tmp_write_to_binlog= 0;
+ mysql_mutex_lock(&LOCK_active_mi);
+ if (reset_slave(thd, active_mi))
+ {
+ /* NOTE: my_error() has been already called by reset_slave(). */
+ result= 1;
+ }
+ mysql_mutex_unlock(&LOCK_active_mi);
+ }
+#endif
+ if (options & REFRESH_USER_RESOURCES)
+ reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */
+ if (options & REFRESH_TABLE_STATS)
+ {
+ mysql_mutex_lock(&LOCK_global_table_stats);
+ free_global_table_stats();
+ init_global_table_stats();
+ mysql_mutex_unlock(&LOCK_global_table_stats);
+ }
+ if (options & REFRESH_INDEX_STATS)
+ {
+ mysql_mutex_lock(&LOCK_global_index_stats);
+ free_global_index_stats();
+ init_global_index_stats();
+ mysql_mutex_unlock(&LOCK_global_index_stats);
+ }
+ if (options & (REFRESH_USER_STATS | REFRESH_CLIENT_STATS))
+ {
+ mysql_mutex_lock(&LOCK_global_user_client_stats);
+ if (options & REFRESH_USER_STATS)
+ {
+ free_global_user_stats();
+ init_global_user_stats();
+ }
+ if (options & REFRESH_CLIENT_STATS)
+ {
+ free_global_client_stats();
+ init_global_client_stats();
+ }
+ mysql_mutex_unlock(&LOCK_global_user_client_stats);
+ }
+ if (*write_to_binlog != -1)
+ *write_to_binlog= tmp_write_to_binlog;
+ /*
+ If the query was killed then this function must fail.
+ */
+ return result || (thd ? thd->killed : 0);
+}
+
+
+/**
+ Implementation of FLUSH TABLES <table_list> WITH READ LOCK.
+
+ In brief: take exclusive locks, expel tables from the table
+ cache, reopen the tables, enter the 'LOCKED TABLES' mode,
+ downgrade the locks.
+ Note: the function is written to be called from
+ mysql_execute_command(), it is not reusable in arbitrary
+ execution context.
+
+ Required privileges
+ -------------------
+ Since the statement implicitly enters LOCK TABLES mode,
+ it requires LOCK TABLES privilege on every table.
+ But since the rest of FLUSH commands require
+ the global RELOAD_ACL, it also requires RELOAD_ACL.
+
+ Compatibility with the global read lock
+ ---------------------------------------
+ We don't wait for the GRL, since neither the
+ 5.1 combination that this new statement is intended to
+ replace (LOCK TABLE <list> WRITE; FLUSH TABLES;),
+ nor FLUSH TABLES WITH READ LOCK do.
+ @todo: this is not implemented, Dmitry disagrees.
+ Currently we wait for GRL in another connection,
+ but are compatible with a GRL in our own connection.
+
+ Behaviour under LOCK TABLES
+ ---------------------------
+ Bail out: i.e. don't perform an implicit UNLOCK TABLES.
+ This is not consistent with LOCK TABLES statement, but is
+ in line with behaviour of FLUSH TABLES WITH READ LOCK, and we
+ try to not introduce any new statements with implicit
+ semantics.
+
+ Compatibility with parallel updates
+ -----------------------------------
+ As a result, we will wait for all open transactions
+ against the tables to complete. After the lock downgrade,
+ new transactions will be able to read the tables, but not
+ write to them.
+
+ Differences from FLUSH TABLES <list>
+ -------------------------------------
+ - you can't flush WITH READ LOCK a non-existent table
+ - you can't flush WITH READ LOCK under LOCK TABLES
+
+ Effect on views and temporary tables.
+ ------------------------------------
+ You can only apply this command to existing base tables.
+ If a view with such name exists, ER_WRONG_OBJECT is returned.
+ If a temporary table with such name exists, it's ignored:
+ if there is a base table, it's used, otherwise ER_NO_SUCH_TABLE
+ is returned.
+
+ Handling of MERGE tables
+ ------------------------
+ For MERGE table this statement will open and lock child tables
+ for read (it is impossible to lock parent table without it).
+ Child tables won't be flushed unless they are explicitly present
+ in the statement's table list.
+
+ Implicit commit
+ ---------------
+ This statement causes an implicit commit before and
+ after it.
+
+ HANDLER SQL
+ -----------
+ If this connection has HANDLERs open against
+ some of the tables being FLUSHed, these handlers
+ are implicitly flushed (lose their position).
+*/
+
+bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables)
+{
+ Lock_tables_prelocking_strategy lock_tables_prelocking_strategy;
+ TABLE_LIST *table_list;
+
+ /*
+ This is called from SQLCOM_FLUSH, the transaction has
+ been committed implicitly.
+ */
+
+ if (thd->locked_tables_mode)
+ {
+ my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
+ goto error;
+ }
+
+ /*
+ Acquire SNW locks on tables to be flushed. Don't acquire global
+ IX and database-scope IX locks on the tables as this will make
+ this statement incompatible with FLUSH TABLES WITH READ LOCK.
+ */
+ if (lock_table_names(thd, all_tables, NULL,
+ thd->variables.lock_wait_timeout,
+ MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK))
+ goto error;
+
+ DEBUG_SYNC(thd,"flush_tables_with_read_lock_after_acquire_locks");
+
+ for (table_list= all_tables; table_list;
+ table_list= table_list->next_global)
+ {
+ /* Request removal of table from cache. */
+ tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED,
+ table_list->db,
+ table_list->table_name, FALSE);
+ /* Reset ticket to satisfy asserts in open_tables(). */
+ table_list->mdl_request.ticket= NULL;
+ }
+
+ /*
+ Before opening and locking tables the below call also waits
+ for old shares to go away, so the fact that we don't pass
+ MYSQL_LOCK_IGNORE_FLUSH flag to it is important.
+ Also we don't pass MYSQL_OPEN_HAS_MDL_LOCK flag as we want
+ to open underlying tables if merge table is flushed.
+ For underlying tables of the merge the below call has to
+ acquire SNW locks to ensure that they can be locked for
+ read without further waiting.
+ */
+ if (open_and_lock_tables(thd, all_tables, FALSE,
+ MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK,
+ &lock_tables_prelocking_strategy) ||
+ thd->locked_tables_list.init_locked_tables(thd))
+ {
+ goto error;
+ }
+ thd->variables.option_bits|= OPTION_TABLE_LOCK;
+
+ /*
+ We don't downgrade MDL_SHARED_NO_WRITE here as the intended
+ post effect of this call is identical to LOCK TABLES <...> READ,
+ and we didn't use thd->in_lock_talbes and
+ thd->sql_command= SQLCOM_LOCK_TABLES hacks to enter the LTM.
+ */
+
+ return FALSE;
+
+error:
+ return TRUE;
+}
+
+
+/**
+ Disable checkpoints for all handlers
+ This is released in unlock_global_read_lock()
+*/
+
+static void disable_checkpoints(THD *thd)
+{
+ if (!thd->global_disable_checkpoint)
+ {
+ thd->global_disable_checkpoint= 1;
+ if (!global_disable_checkpoint++)
+ ha_checkpoint_state(1); // Disable checkpoints
+ }
+}
+
diff --git a/sql/sql_reload.h b/sql/sql_reload.h
new file mode 100644
index 00000000000..ebb3d78c003
--- /dev/null
+++ b/sql/sql_reload.h
@@ -0,0 +1,26 @@
+#ifndef SQL_RELOAD_INCLUDED
+#define SQL_RELOAD_INCLUDED
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+class THD;
+struct TABLE_LIST;
+
+bool reload_acl_and_cache(THD *thd, unsigned long options,
+ TABLE_LIST *tables, int *write_to_binlog);
+
+bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables);
+
+#endif
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index 2cf4eca447c..ee7c0fd2f73 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -1,5 +1,6 @@
/*
- Copyright (c) 2000, 2010, Oracle and/or its affiliates.
+ Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2011, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,16 +13,23 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
Atomic rename of table; RENAME TABLE t1 to t2, tmp to t1 [,...]
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "sql_rename.h"
+#include "sql_cache.h" // query_cache_*
+#include "sql_table.h" // build_table_filename
+#include "sql_view.h" // mysql_frm_type, mysql_rename_view
#include "sql_trigger.h"
-#include "sql_handler.h"
+#include "lock.h" // MYSQL_OPEN_SKIP_TEMPORARY
+#include "sql_base.h" // tdc_remove_table, lock_table_names,
+#include "sql_handler.h" // mysql_ha_rm_tables
+#include "datadict.h"
static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list,
bool skip_error);
@@ -47,17 +55,14 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
if the user is trying to to do this in a transcation context
*/
- if (thd->locked_tables || thd->active_transaction())
+ if (thd->locked_tables_mode || thd->in_active_multi_stmt_transaction())
{
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
DBUG_RETURN(1);
}
- mysql_ha_rm_tables(thd, table_list, FALSE);
-
- if (wait_if_global_read_lock(thd,0,1))
- DBUG_RETURN(1);
+ mysql_ha_rm_tables(thd, table_list);
if (logger.is_log_table_enabled(QUERY_LOG_GENERAL) ||
logger.is_log_table_enabled(QUERY_LOG_SLOW))
@@ -136,14 +141,19 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
}
}
- pthread_mutex_lock(&LOCK_open);
- if (lock_table_names_exclusively(thd, table_list))
- {
- pthread_mutex_unlock(&LOCK_open);
+ if (lock_table_names(thd, table_list, 0, thd->variables.lock_wait_timeout,
+ MYSQL_OPEN_SKIP_TEMPORARY))
goto err;
- }
+
+ for (ren_table= table_list; ren_table; ren_table= ren_table->next_local)
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, ren_table->db,
+ ren_table->table_name, FALSE);
error=0;
+ /*
+ An exclusive lock on table names is satisfactory to ensure
+ no other thread accesses this table.
+ */
if ((ren_table=rename_tables(thd,table_list,0)))
{
/* Rename didn't succeed; rename back the tables in reverse order */
@@ -165,17 +175,6 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
error= 1;
}
- /*
- An exclusive lock on table names is satisfactory to ensure
- no other thread accesses this table.
- However, NDB assumes that handler::rename_tables is called under
- LOCK_open. And it indeed is, from ALTER TABLE.
- TODO: remove this limitation.
- We still should unlock LOCK_open as early as possible, to provide
- higher concurrency - query_cache_invalidate can take minutes to
- complete.
- */
- pthread_mutex_unlock(&LOCK_open);
if (!silent && !error)
{
@@ -187,12 +186,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
if (!error)
query_cache_invalidate3(thd, table_list, 0);
- pthread_mutex_lock(&LOCK_open);
- unlock_table_names(thd, table_list, (TABLE_LIST*) 0);
- pthread_mutex_unlock(&LOCK_open);
-
err:
- start_waiting_global_read_lock(thd);
DBUG_RETURN(error || binlog_error);
}
@@ -276,7 +270,7 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name,
DBUG_RETURN(1); // This can't be skipped
}
- frm_type= mysql_frm_type(thd, old_name, &table_type);
+ frm_type= dd_frm_type(thd, old_name, &table_type);
switch (frm_type)
{
case FRMTYPE_TABLE:
@@ -288,6 +282,7 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name,
{
if ((rc= Table_triggers_list::change_table_name(thd, ren_table->db,
old_alias,
+ ren_table->table_name,
new_db,
new_alias)))
{
@@ -300,7 +295,7 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name,
(void) mysql_rename_table(ha_resolve_by_legacy_type(thd,
table_type),
new_db, new_alias,
- ren_table->db, old_alias, 0);
+ ren_table->db, old_alias, NO_FK_CHECKS);
}
}
}
diff --git a/sql/sql_rename.h b/sql/sql_rename.h
new file mode 100644
index 00000000000..039a3b8b4a1
--- /dev/null
+++ b/sql/sql_rename.h
@@ -0,0 +1,27 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_RENAME_INCLUDED
+#define SQL_RENAME_INCLUDED
+
+class THD;
+struct TABLE_LIST;
+
+bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent);
+bool do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db,
+ char *new_table_name, char *new_table_alias,
+ bool skip_error);
+
+#endif /* SQL_RENAME_INCLUDED */
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index d249533cdca..5aea3f6d0de 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -1,6 +1,5 @@
-/*
- Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2008, 2014, Monty Program Ab.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,17 +12,20 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "sql_parse.h" // check_access
#ifdef HAVE_REPLICATION
#include "rpl_mi.h"
#include "sql_repl.h"
+#include "sql_acl.h" // SUPER_ACL
#include "log_event.h"
#include "rpl_filter.h"
#include <my_dir.h>
+#include "rpl_handler.h"
#include "debug_sync.h"
int max_binlog_dump_events = 0; // unlimited
@@ -32,8 +34,15 @@ my_bool opt_sporadic_binlog_dump_fail = 0;
static int binlog_dump_count = 0;
#endif
-extern TYPELIB binlog_checksum_typelib;
+/**
+ a copy of active_mi->rli->slave_skip_counter, for showing in SHOW VARIABLES,
+ INFORMATION_SCHEMA.GLOBAL_VARIABLES and @@sql_slave_skip_counter without
+ taking all the mutexes needed to access active_mi->rli->slave_skip_counter
+ properly.
+*/
+uint sql_slave_skip_counter;
+extern TYPELIB binlog_checksum_typelib;
/*
fake_rotate_event() builds a fake (=which does not exist physically in any
@@ -111,6 +120,32 @@ static int fake_rotate_event(NET* net, String* packet, char* log_file_name,
DBUG_RETURN(0);
}
+/*
+ Reset thread transmit packet buffer for event sending
+
+ This function allocates header bytes for event transmission, and
+ should be called before store the event data to the packet buffer.
+*/
+static int reset_transmit_packet(THD *thd, ushort flags,
+ ulong *ev_offset, const char **errmsg)
+{
+ int ret= 0;
+ String *packet= &thd->packet;
+
+ /* reserve and set default header */
+ packet->length(0);
+ packet->set("\0", 1, &my_charset_bin);
+
+ if (RUN_HOOK(binlog_transmit, reserve_header, (thd, flags, packet)))
+ {
+ *errmsg= "Failed to run hook 'reserve_header'";
+ my_errno= ER_UNKNOWN_ERROR;
+ ret= 1;
+ }
+ *ev_offset= packet->length();
+ return ret;
+}
+
static int send_file(THD *thd)
{
NET* net = &thd->net;
@@ -147,13 +182,14 @@ static int send_file(THD *thd)
if (!strcmp(fname,"/dev/null"))
goto end;
- if ((fd = my_open(fname, O_RDONLY, MYF(0))) < 0)
+ if ((fd= mysql_file_open(key_file_send_file,
+ fname, O_RDONLY, MYF(0))) < 0)
{
errmsg = "on open of file";
goto err;
}
- while ((long) (bytes= my_read(fd, buf, IO_SIZE, MYF(0))) > 0)
+ while ((long) (bytes= mysql_file_read(fd, buf, IO_SIZE, MYF(0))) > 0)
{
if (my_net_write(net, buf, bytes))
{
@@ -174,7 +210,7 @@ static int send_file(THD *thd)
err:
my_net_set_read_timeout(net, old_timeout);
if (fd >= 0)
- (void) my_close(fd, MYF(0));
+ mysql_file_close(fd, MYF(0));
if (errmsg)
{
sql_print_error("Failed in send_file() %s", errmsg);
@@ -289,7 +325,7 @@ void adjust_linfo_offsets(my_off_t purge_offset)
{
THD *tmp;
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
I_List_iterator<THD> it(threads);
while ((tmp=it++))
@@ -297,7 +333,7 @@ void adjust_linfo_offsets(my_off_t purge_offset)
LOG_INFO* linfo;
if ((linfo = tmp->current_linfo))
{
- pthread_mutex_lock(&linfo->lock);
+ mysql_mutex_lock(&linfo->lock);
/*
Index file offset can be less that purge offset only if
we just started reading the index file. In that case
@@ -307,10 +343,10 @@ void adjust_linfo_offsets(my_off_t purge_offset)
linfo->fatal = (linfo->index_file_offset != 0);
else
linfo->index_file_offset -= purge_offset;
- pthread_mutex_unlock(&linfo->lock);
+ mysql_mutex_unlock(&linfo->lock);
}
}
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
}
@@ -320,7 +356,7 @@ bool log_in_use(const char* log_name)
THD *tmp;
bool result = 0;
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
I_List_iterator<THD> it(threads);
while ((tmp=it++))
@@ -328,38 +364,25 @@ bool log_in_use(const char* log_name)
LOG_INFO* linfo;
if ((linfo = tmp->current_linfo))
{
- pthread_mutex_lock(&linfo->lock);
+ mysql_mutex_lock(&linfo->lock);
result = !memcmp(log_name, linfo->log_file_name, log_name_len);
- pthread_mutex_unlock(&linfo->lock);
+ mysql_mutex_unlock(&linfo->lock);
if (result)
break;
}
}
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
return result;
}
bool purge_error_message(THD* thd, int res)
{
- uint errmsg= 0;
-
- switch (res) {
- case 0: break;
- case LOG_INFO_EOF: errmsg= ER_UNKNOWN_TARGET_BINLOG; break;
- case LOG_INFO_IO: errmsg= ER_IO_ERR_LOG_INDEX_READ; break;
- case LOG_INFO_INVALID:errmsg= ER_BINLOG_PURGE_PROHIBITED; break;
- case LOG_INFO_SEEK: errmsg= ER_FSEEK_FAIL; break;
- case LOG_INFO_MEM: errmsg= ER_OUT_OF_RESOURCES; break;
- case LOG_INFO_FATAL: errmsg= ER_BINLOG_PURGE_FATAL_ERR; break;
- case LOG_INFO_IN_USE: errmsg= ER_LOG_IN_USE; break;
- case LOG_INFO_EMFILE: errmsg= ER_BINLOG_PURGE_EMFILE; break;
- default: errmsg= ER_LOG_PURGE_UNKNOWN_ERR; break;
- }
+ uint errcode;
- if (errmsg)
+ if ((errcode= purge_log_get_error_code(res)) != 0)
{
- my_message(errmsg, ER(errmsg), MYF(0));
+ my_message(errcode, ER(errcode), MYF(0));
return TRUE;
}
my_ok(thd);
@@ -436,7 +459,7 @@ Increase max_allowed_packet on master";
*errmsg = "memory allocation failed reading log event";
break;
case LOG_READ_TRUNC:
- *errmsg = "binlog truncated in the middle of event";
+ *errmsg = "binlog truncated in the middle of event; consider out of disk space on master";
break;
case LOG_READ_CHECKSUM_FAILURE:
*errmsg = "event read from binlog did not pass crc check";
@@ -449,9 +472,143 @@ Increase max_allowed_packet on master";
}
+/**
+ An auxiliary function for calling in mysql_binlog_send
+ to initialize the heartbeat timeout in waiting for a binlogged event.
+
+ @param[in] thd THD to access a user variable
+
+ @return heartbeat period an ulonglong of nanoseconds
+ or zero if heartbeat was not demanded by slave
+*/
+static ulonglong get_heartbeat_period(THD * thd)
+{
+ bool null_value;
+ LEX_STRING name= { C_STRING_WITH_LEN("master_heartbeat_period")};
+ user_var_entry *entry=
+ (user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str,
+ name.length);
+ return entry? entry->val_int(&null_value) : 0;
+}
+
/*
- TODO: Clean up loop to only have one call to send_file()
+ Function prepares and sends repliation heartbeat event.
+
+ @param net net object of THD
+ @param packet buffer to store the heartbeat instance
+ @param event_coordinates binlog file name and position of the last
+ real event master sent from binlog
+
+ @note
+ Among three essential pieces of heartbeat data Log_event::when
+ is computed locally.
+ The error to send is serious and should force terminating
+ the dump thread.
*/
+static int send_heartbeat_event(NET* net, String* packet,
+ const struct event_coordinates *coord,
+ uint8 checksum_alg_arg)
+{
+ DBUG_ENTER("send_heartbeat_event");
+ char header[LOG_EVENT_HEADER_LEN];
+ my_bool do_checksum= checksum_alg_arg != BINLOG_CHECKSUM_ALG_OFF &&
+ checksum_alg_arg != BINLOG_CHECKSUM_ALG_UNDEF;
+ /*
+ 'when' (the timestamp) is set to 0 so that slave could distinguish between
+ real and fake Rotate events (if necessary)
+ */
+ memset(header, 0, 4); // when
+
+ header[EVENT_TYPE_OFFSET] = HEARTBEAT_LOG_EVENT;
+
+ char* p= coord->file_name + dirname_length(coord->file_name);
+
+ uint ident_len = strlen(p);
+ ulong event_len = ident_len + LOG_EVENT_HEADER_LEN +
+ (do_checksum ? BINLOG_CHECKSUM_LEN : 0);
+ int4store(header + SERVER_ID_OFFSET, server_id);
+ int4store(header + EVENT_LEN_OFFSET, event_len);
+ int2store(header + FLAGS_OFFSET, 0);
+
+ int4store(header + LOG_POS_OFFSET, coord->pos); // log_pos
+
+ packet->append(header, sizeof(header));
+ packet->append(p, ident_len); // log_file_name
+
+ if (do_checksum)
+ {
+ char b[BINLOG_CHECKSUM_LEN];
+ ha_checksum crc= my_checksum(0L, NULL, 0);
+ crc= my_checksum(crc, (uchar*) header, sizeof(header));
+ crc= my_checksum(crc, (uchar*) p, ident_len);
+ int4store(b, crc);
+ packet->append(b, sizeof(b));
+ }
+
+ if (my_net_write(net, (uchar*) packet->ptr(), packet->length()) ||
+ net_flush(net))
+ {
+ DBUG_RETURN(-1);
+ }
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Helper function for mysql_binlog_send() to write an event down the slave
+ connection.
+
+ Returns NULL on success, error message string on error.
+*/
+static const char *
+send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
+ Log_event_type event_type, char *log_file_name,
+ IO_CACHE *log)
+{
+ my_off_t pos;
+
+ /* Do not send annotate_rows events unless slave requested it. */
+ if (event_type == ANNOTATE_ROWS_EVENT &&
+ !(flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT))
+ return NULL;
+
+ /*
+ Skip events with the @@skip_replication flag set, if slave requested
+ skipping of such events.
+ */
+ if (thd->variables.option_bits & OPTION_SKIP_REPLICATION)
+ {
+ /*
+ The first byte of the packet is a '\0' to distinguish it from an error
+ packet. So the actual event starts at offset +1.
+ */
+ uint16 event_flags= uint2korr(&((*packet)[FLAGS_OFFSET+1]));
+ if (event_flags & LOG_EVENT_SKIP_REPLICATION_F)
+ return NULL;
+ }
+
+ thd_proc_info(thd, "Sending binlog event to slave");
+
+ pos= my_b_tell(log);
+ if (RUN_HOOK(binlog_transmit, before_send_event,
+ (thd, flags, packet, log_file_name, pos)))
+ return "run 'before_send_event' hook failed";
+
+ if (my_net_write(net, (uchar*) packet->ptr(), packet->length()))
+ return "Failed on my_net_write()";
+
+ DBUG_PRINT("info", ("log event code %d", (*packet)[LOG_EVENT_OFFSET+1] ));
+ if (event_type == LOAD_EVENT)
+ {
+ if (send_file(thd))
+ return "failed in send_file()";
+ }
+
+ if (RUN_HOOK(binlog_transmit, after_send_event, (thd, flags, packet)))
+ return "Failed to run hook 'after_send_event'";
+
+ return NULL; /* Success */
+}
void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
ushort flags)
@@ -459,26 +616,53 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
LOG_INFO linfo;
char *log_file_name = linfo.log_file_name;
char search_file_name[FN_REFLEN], *name;
+
+ ulong ev_offset;
+
IO_CACHE log;
File file = -1;
- String* packet = &thd->packet;
+ String* const packet = &thd->packet;
int error;
- const char *errmsg = "Unknown error";
+ const char *errmsg = "Unknown error", *tmp_msg;
+ char error_text[MAX_SLAVE_ERRMSG]; // to be send to slave via my_message()
NET* net = &thd->net;
- pthread_mutex_t *log_lock;
- bool binlog_can_be_corrupted= FALSE;
+ mysql_mutex_t *log_lock;
+ mysql_cond_t *log_cond;
+
uint8 current_checksum_alg= BINLOG_CHECKSUM_ALG_UNDEF;
+ int old_max_allowed_packet= thd->variables.max_allowed_packet;
+
#ifndef DBUG_OFF
int left_events = max_binlog_dump_events;
#endif
- int old_max_allowed_packet= thd->variables.max_allowed_packet;
- bool is_active_binlog= false;
- my_off_t prev_pos= pos;
-
DBUG_ENTER("mysql_binlog_send");
DBUG_PRINT("enter",("log_ident: '%s' pos: %ld", log_ident, (long) pos));
bzero((char*) &log,sizeof(log));
+ /*
+ heartbeat_period from @master_heartbeat_period user variable
+ */
+ ulonglong heartbeat_period= get_heartbeat_period(thd);
+ struct timespec heartbeat_buf;
+ struct timespec *heartbeat_ts= NULL;
+ const LOG_POS_COORD start_coord= { log_ident, pos },
+ *p_start_coord= &start_coord;
+ LOG_POS_COORD coord_buf= { log_file_name, BIN_LOG_HEADER_SIZE },
+ *p_coord= &coord_buf;
+ if (heartbeat_period != LL(0))
+ {
+ heartbeat_ts= &heartbeat_buf;
+ set_timespec_nsec(*heartbeat_ts, 0);
+ }
+ if (global_system_variables.log_warnings > 1)
+ sql_print_information("Start binlog_dump to slave_server(%u), pos(%s, %lu)",
+ thd->server_id, log_ident, (ulong)pos);
+ if (RUN_HOOK(binlog_transmit, transmit_start, (thd, flags, log_ident, pos)))
+ {
+ errmsg= "Failed to run hook 'transmit_start'";
+ my_errno= ER_UNKNOWN_ERROR;
+ goto err;
+ }
#ifndef DBUG_OFF
if (opt_sporadic_binlog_dump_fail && (binlog_dump_count++ % 2))
@@ -517,9 +701,9 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
goto err;
}
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
thd->current_linfo = &linfo;
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
if ((file=open_binlog(&log, log_file_name, &errmsg)) < 0)
{
@@ -534,11 +718,9 @@ impossible position";
goto err;
}
- /*
- We need to start a packet with something other than 255
- to distinguish it from error
- */
- packet->set("\0", 1, &my_charset_bin); /* This is the start of a new packet */
+ /* reset transmit packet for the fake rotate event below */
+ if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg))
+ goto err;
/*
Tell the client about the log name with a fake Rotate event;
@@ -579,7 +761,7 @@ impossible position";
my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
goto err;
}
- packet->set("\0", 1, &my_charset_bin);
+
/*
Adding MAX_LOG_EVENT_HEADER_LEN, since a binlog event can become
this larger than the corresponding packet (query) sent
@@ -592,27 +774,34 @@ impossible position";
mysql_bin_log, and it's already inited, and it will be destroyed
only at shutdown).
*/
- log_lock = mysql_bin_log.get_log_lock();
+ p_coord->pos= pos; // the first hb matches the slave's last seen value
+ log_lock= mysql_bin_log.get_log_lock();
+ log_cond= mysql_bin_log.get_log_cond();
if (pos > BIN_LOG_HEADER_SIZE)
{
+ /* reset transmit packet for the event read from binary log
+ file */
+ if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg))
+ goto err;
+
/*
Try to find a Format_description_log_event at the beginning of
the binlog
*/
- if (!(error = Log_event::read_log_event(&log, packet, log_lock, 0,
- log_file_name, &is_active_binlog)))
- {
+ if (!(error = Log_event::read_log_event(&log, packet, log_lock, 0)))
+ {
/*
- The packet has offsets equal to the normal offsets in a binlog
- event +1 (the first character is \0).
+ The packet has offsets equal to the normal offsets in a
+ binlog event + ev_offset (the first ev_offset characters are
+ the header (default \0)).
*/
DBUG_PRINT("info",
("Looked for a Format_description_log_event, found event type %d",
- (*packet)[EVENT_TYPE_OFFSET+1]));
- if ((uchar)(*packet)[EVENT_TYPE_OFFSET+1] == FORMAT_DESCRIPTION_EVENT)
+ (*packet)[EVENT_TYPE_OFFSET+ev_offset]));
+ if ((*packet)[EVENT_TYPE_OFFSET+ev_offset] == FORMAT_DESCRIPTION_EVENT)
{
- current_checksum_alg= get_checksum_alg(packet->ptr() + 1,
- packet->length() - 1);
+ current_checksum_alg= get_checksum_alg(packet->ptr() + ev_offset,
+ packet->length() - ev_offset);
DBUG_ASSERT(current_checksum_alg == BINLOG_CHECKSUM_ALG_OFF ||
current_checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF ||
current_checksum_alg == BINLOG_CHECKSUM_ALG_CRC32);
@@ -628,32 +817,24 @@ impossible position";
"slaves that cannot process them");
goto err;
}
- /*
- If a binlog is not active, but LOG_EVENT_BINLOG_IN_USE_F
- flag is 1. That means it is not closed in normal way
- (E.g server crash) and may include corrupted events.
- */
- binlog_can_be_corrupted= (!is_active_binlog) &&
- test((*packet)[FLAGS_OFFSET+1] & LOG_EVENT_BINLOG_IN_USE_F);
-
- (*packet)[FLAGS_OFFSET+1] &= ~LOG_EVENT_BINLOG_IN_USE_F;
+ (*packet)[FLAGS_OFFSET+ev_offset] &= ~LOG_EVENT_BINLOG_IN_USE_F;
/*
mark that this event with "log_pos=0", so the slave
should not increment master's binlog position
(rli->group_master_log_pos)
*/
- int4store((char*) packet->ptr()+LOG_POS_OFFSET+1, 0);
+ int4store((char*) packet->ptr()+LOG_POS_OFFSET+ev_offset, 0);
/*
if reconnect master sends FD event with `created' as 0
to avoid destroying temp tables.
*/
int4store((char*) packet->ptr()+LOG_EVENT_MINIMAL_HEADER_LEN+
- ST_CREATED_OFFSET+1, (ulong) 0);
+ ST_CREATED_OFFSET+ev_offset, (ulong) 0);
/* fix the checksum due to latest changes in header */
if (current_checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
- fix_checksum(packet, 1);
+ fix_checksum(packet, ev_offset);
/* send it */
if (my_net_write(net, (uchar*) packet->ptr(), packet->length()))
@@ -680,8 +861,6 @@ impossible position";
Format_description_log_event will be found naturally if it is written.
*/
}
- /* reset the packet as we wrote to it in any case */
- packet->set("\0", 1, &my_charset_bin);
} /* end of if (pos > BIN_LOG_HEADER_SIZE); */
else
{
@@ -693,13 +872,19 @@ impossible position";
while (!net->error && net->vio != 0 && !thd->killed)
{
+ Log_event_type event_type= UNKNOWN_EVENT;
+
+ /* reset the transmit packet for the event read from binary log
+ file */
+ if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg))
+ goto err;
+
+ bool is_active_binlog= false;
while (!(error= Log_event::read_log_event(&log, packet, log_lock,
current_checksum_alg,
log_file_name,
&is_active_binlog)))
{
- DBUG_ASSERT(prev_pos < my_b_tell(&log));
- prev_pos= my_b_tell(&log);
#ifndef DBUG_OFF
if (max_binlog_dump_events && !left_events--)
{
@@ -709,15 +894,23 @@ impossible position";
goto err;
}
#endif
+ /*
+ log's filename does not change while it's active
+ */
+ p_coord->pos= uint4korr(packet->ptr() + ev_offset + LOG_POS_OFFSET);
+
+ event_type=
+ (Log_event_type)((uchar)(*packet)[LOG_EVENT_OFFSET+ev_offset]);
+#ifdef ENABLED_DEBUG_SYNC
DBUG_EXECUTE_IF("dump_thread_wait_before_send_xid",
{
- if ((*packet)[EVENT_TYPE_OFFSET+1] == XID_EVENT)
+ if (event_type == XID_EVENT)
{
net_flush(net);
const char act[]=
"now "
"wait_for signal.continue";
- DBUG_ASSERT(opt_debug_sync_timeout > 0);
+ DBUG_ASSERT(debug_sync_service);
DBUG_ASSERT(!debug_sync_set_action(thd,
STRING_WITH_LEN(act)));
const char act2[]=
@@ -727,11 +920,11 @@ impossible position";
STRING_WITH_LEN(act2)));
}
});
-
- if ((uchar) (*packet)[EVENT_TYPE_OFFSET+1] == FORMAT_DESCRIPTION_EVENT)
+#endif
+ if (event_type == FORMAT_DESCRIPTION_EVENT)
{
- current_checksum_alg= get_checksum_alg(packet->ptr() + 1,
- packet->length() - 1);
+ current_checksum_alg= get_checksum_alg(packet->ptr() + ev_offset,
+ packet->length() - ev_offset);
DBUG_ASSERT(current_checksum_alg == BINLOG_CHECKSUM_ALG_OFF ||
current_checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF ||
current_checksum_alg == BINLOG_CHECKSUM_ALG_CRC32);
@@ -747,79 +940,29 @@ impossible position";
"slaves that cannot process them");
goto err;
}
- binlog_can_be_corrupted= (!is_active_binlog) &&
- test((*packet)[FLAGS_OFFSET+1] & LOG_EVENT_BINLOG_IN_USE_F);
- (*packet)[FLAGS_OFFSET+1] &= ~LOG_EVENT_BINLOG_IN_USE_F;
+ (*packet)[FLAGS_OFFSET+ev_offset] &= ~LOG_EVENT_BINLOG_IN_USE_F;
}
- else if ((uchar)(*packet)[EVENT_TYPE_OFFSET+1] == STOP_EVENT)
- binlog_can_be_corrupted= FALSE;
- if ((uchar)(*packet)[EVENT_TYPE_OFFSET+1] != ANNOTATE_ROWS_EVENT ||
- (flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT))
+ if ((tmp_msg= send_event_to_slave(thd, net, packet, flags, event_type,
+ log_file_name, &log)))
{
- if (my_net_write(net, (uchar*) packet->ptr(), packet->length()))
- {
- errmsg = "Failed on my_net_write()";
- my_errno= ER_UNKNOWN_ERROR;
- goto err;
- }
-
- DBUG_EXECUTE_IF("dump_thread_wait_before_send_xid",
- {
- if ((*packet)[EVENT_TYPE_OFFSET+1] == XID_EVENT)
- {
- net_flush(net);
- }
- });
-
- DBUG_PRINT("info", ("log event code %d",
- (*packet)[LOG_EVENT_OFFSET+1] ));
- if ((uchar) (*packet)[LOG_EVENT_OFFSET+1] == LOAD_EVENT)
- {
- if (send_file(thd))
- {
- errmsg = "failed in send_file()";
- my_errno= ER_UNKNOWN_ERROR;
- goto err;
- }
- }
+ errmsg= tmp_msg;
+ my_errno= ER_UNKNOWN_ERROR;
+ goto err;
}
- packet->set("\0", 1, &my_charset_bin);
- }
- /*
- here we were reading binlog that was not closed properly (as a result
- of a crash ?). treat any corruption as EOF
- */
- if ((binlog_can_be_corrupted || is_active_binlog) &&
- (error != LOG_READ_MEM && error != LOG_READ_CHECKSUM_FAILURE &&
- error != LOG_READ_EOF))
- {
- test_for_non_eof_log_read_errors(error, &errmsg);
-
- if (is_active_binlog)
- {
- sql_print_warning("Failed to read an event from active binlog(%s,%lu). "
- "error: %s. Dump thread will try to read it again.",
- log_file_name, (ulong)prev_pos, errmsg);
- }
- else
- {
- sql_print_warning("Failed to read an event from inactive binlog"
- "(%s, %lu). error: %s. Dump thread found the binlog "
- "was not rotated correctly. It will jump to next "
- "binlog directly.",
- log_file_name, (ulong) prev_pos, errmsg);
- }
- errmsg= NULL;
+ DBUG_EXECUTE_IF("dump_thread_wait_before_send_xid",
+ {
+ if (event_type == XID_EVENT)
+ {
+ net_flush(net);
+ }
+ });
- /*
- If binlog is active, it will try to read the event again. Otherwise,
- skip the corrupted events and switch to next binlog.
- */
- my_b_seek(&log, prev_pos);
- error=LOG_READ_EOF;
+ /* reset transmit packet for next loop */
+ if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg))
+ goto err;
}
DBUG_EXECUTE_IF("wait_after_binlog_EOF",
@@ -874,6 +1017,11 @@ impossible position";
}
#endif
+ /* reset the transmit packet for the event read from binary log
+ file */
+ if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg))
+ goto err;
+
/*
No one will update the log while we are reading
now, but we'll be quick and just read one record
@@ -884,68 +1032,93 @@ impossible position";
has not been updated since last read.
*/
- pthread_mutex_lock(log_lock);
- switch (error= Log_event::read_log_event(&log, packet, (pthread_mutex_t*) 0,
+ mysql_mutex_lock(log_lock);
+ switch (error= Log_event::read_log_event(&log, packet, (mysql_mutex_t*) 0,
current_checksum_alg)) {
case 0:
/* we read successfully, so we'll need to send it to the slave */
- pthread_mutex_unlock(log_lock);
+ mysql_mutex_unlock(log_lock);
read_packet = 1;
- DBUG_ASSERT(prev_pos < my_b_tell(&log));
- prev_pos= my_b_tell(&log);
+ p_coord->pos= uint4korr(packet->ptr() + ev_offset + LOG_POS_OFFSET);
+ event_type=
+ (Log_event_type)((uchar)(*packet)[LOG_EVENT_OFFSET+ev_offset]);
break;
case LOG_READ_EOF:
+ {
+ int ret;
+ ulong signal_cnt;
DBUG_PRINT("wait",("waiting for data in binary log"));
if (thd->server_id==0) // for mysqlbinlog (mysqlbinlog.server_id==0)
{
- pthread_mutex_unlock(log_lock);
+ mysql_mutex_unlock(log_lock);
goto end;
}
- if (!thd->killed)
- {
- /* Note that the following call unlocks lock_log */
- mysql_bin_log.wait_for_update(thd, 0);
- }
- else
- pthread_mutex_unlock(log_lock);
- DBUG_PRINT("wait",("binary log received update"));
- break;
-
- default:
- pthread_mutex_unlock(log_lock);
- test_for_non_eof_log_read_errors(error, &errmsg);
- goto err;
- }
- if (read_packet)
- {
- if ((uchar)(*packet)[EVENT_TYPE_OFFSET+1] != ANNOTATE_ROWS_EVENT ||
- (flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT))
+#ifndef DBUG_OFF
+ ulong hb_info_counter= 0;
+#endif
+ const char* old_msg= thd->proc_info;
+ signal_cnt= mysql_bin_log.signal_cnt;
+ do
{
- thd_proc_info(thd, "Sending binlog event to slave");
- if (my_net_write(net, (uchar*) packet->ptr(), packet->length()) )
- {
- errmsg = "Failed on my_net_write()";
- my_errno= ER_UNKNOWN_ERROR;
- goto err;
- }
-
- if ((uchar)(*packet)[LOG_EVENT_OFFSET+1] == LOAD_EVENT)
+ if (heartbeat_period != 0)
{
- if (send_file(thd))
+ DBUG_ASSERT(heartbeat_ts);
+ set_timespec_nsec(*heartbeat_ts, heartbeat_period);
+ }
+ thd->enter_cond(log_cond, log_lock,
+ "Master has sent all binlog to slave; "
+ "waiting for binlog to be updated");
+ ret= mysql_bin_log.wait_for_update_bin_log(thd, heartbeat_ts);
+ DBUG_ASSERT(ret == 0 || (heartbeat_period != 0));
+ if (ret == ETIMEDOUT || ret == ETIME)
+ {
+#ifndef DBUG_OFF
+ if (hb_info_counter < 3)
+ {
+ sql_print_information("master sends heartbeat message");
+ hb_info_counter++;
+ if (hb_info_counter == 3)
+ sql_print_information("the rest of heartbeat info skipped ...");
+ }
+#endif
+ /* reset transmit packet for the heartbeat event */
+ if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg))
{
- errmsg = "failed in send_file()";
+ thd->exit_cond(old_msg);
+ goto err;
+ }
+ if (send_heartbeat_event(net, packet, p_coord, current_checksum_alg))
+ {
+ errmsg = "Failed on my_net_write()";
my_errno= ER_UNKNOWN_ERROR;
+ thd->exit_cond(old_msg);
goto err;
}
}
- }
- packet->set("\0", 1, &my_charset_bin);
- /*
- No need to net_flush because we will get to flush later when
- we hit EOF pretty quick
- */
+ else
+ {
+ DBUG_PRINT("wait",("binary log received update or a broadcast signal caught"));
+ }
+ } while (signal_cnt == mysql_bin_log.signal_cnt && !thd->killed);
+ thd->exit_cond(old_msg);
+ }
+ break;
+
+ default:
+ mysql_mutex_unlock(log_lock);
+ test_for_non_eof_log_read_errors(error, &errmsg);
+ goto err;
+ }
+
+ if (read_packet &&
+ (tmp_msg= send_event_to_slave(thd, net, packet, flags, event_type,
+ log_file_name, &log)))
+ {
+ errmsg= tmp_msg;
+ my_errno= ER_UNKNOWN_ERROR;
+ goto err;
}
log.error=0;
@@ -959,7 +1132,6 @@ impossible position";
thd_proc_info(thd, "Finished reading one binlog; switching to next binlog");
switch (mysql_bin_log.find_next_log(&linfo, 1)) {
case 0:
- prev_pos= BIN_LOG_HEADER_SIZE;
break;
case LOG_INFO_EOF:
if (mysql_bin_log.is_active(log_file_name))
@@ -977,8 +1149,12 @@ impossible position";
break;
end_io_cache(&log);
- (void) my_close(file, MYF(MY_WME));
+ mysql_file_close(file, MYF(MY_WME));
+ /* reset transmit packet for the possible fake rotate event */
+ if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg))
+ goto err;
+
/*
Call fake_rotate_event() in case the previous log (the one which
we have just finished reading) did not contain a Rotate event
@@ -996,26 +1172,44 @@ impossible position";
goto err;
}
- packet->length(0);
- packet->append('\0');
+ p_coord->file_name= log_file_name; // reset to the next
}
}
end:
end_io_cache(&log);
- (void)my_close(file, MYF(MY_WME));
+ mysql_file_close(file, MYF(MY_WME));
+ RUN_HOOK(binlog_transmit, transmit_stop, (thd, flags));
my_eof(thd);
thd_proc_info(thd, "Waiting to finalize termination");
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
thd->current_linfo = 0;
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
thd->variables.max_allowed_packet= old_max_allowed_packet;
DBUG_VOID_RETURN;
err:
thd_proc_info(thd, "Waiting to finalize termination");
+ if (my_errno == ER_MASTER_FATAL_ERROR_READING_BINLOG && my_b_inited(&log))
+ {
+ /*
+ detailing the fatal error message with coordinates
+ of the last position read.
+ */
+ my_snprintf(error_text, sizeof(error_text),
+ "%s; the first event '%s' at %lld, "
+ "the last event read from '%s' at %lld, "
+ "the last byte read from '%s' at %lld.",
+ errmsg,
+ my_basename(p_start_coord->file_name), p_start_coord->pos,
+ my_basename(p_coord->file_name), p_coord->pos,
+ my_basename(log_file_name), my_b_tell(&log));
+ }
+ else
+ strcpy(error_text, errmsg);
end_io_cache(&log);
+ RUN_HOOK(binlog_transmit, transmit_stop, (thd, flags));
/*
Exclude iteration through thread list
this is needed for purge_logs() - it will iterate through
@@ -1023,14 +1217,14 @@ err:
this mutex will make sure that it never tried to update our linfo
after we return from this stack frame
*/
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
thd->current_linfo = 0;
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
if (file >= 0)
- (void) my_close(file, MYF(MY_WME));
+ mysql_file_close(file, MYF(MY_WME));
thd->variables.max_allowed_packet= old_max_allowed_packet;
- my_message(my_errno, errmsg, MYF(0));
+ my_message(my_errno, error_text, MYF(0));
DBUG_VOID_RETURN;
}
@@ -1043,7 +1237,7 @@ err:
@param mi Pointer to Master_info object for the slave's IO thread.
- @param net_report If true, saves the exit status into thd->main_da.
+ @param net_report If true, saves the exit status into thd->stmt_da.
@retval 0 success
@retval 1 error
@@ -1054,7 +1248,7 @@ int start_slave(THD* thd , Master_info* mi, bool net_report)
int thread_mask;
DBUG_ENTER("start_slave");
- if (check_access(thd, SUPER_ACL, any_db,0,0,0,0))
+ if (check_access(thd, SUPER_ACL, any_db, NULL, NULL, 0, 0))
DBUG_RETURN(1);
lock_slave_threads(mi); // this allows us to cleanly read slave_running
// Get a mask of _stopped_ threads
@@ -1081,7 +1275,7 @@ int start_slave(THD* thd , Master_info* mi, bool net_report)
*/
if (thread_mask & SLAVE_SQL)
{
- pthread_mutex_lock(&mi->rli.data_lock);
+ mysql_mutex_lock(&mi->rli.data_lock);
if (thd->lex->mi.pos)
{
@@ -1093,8 +1287,7 @@ int start_slave(THD* thd , Master_info* mi, bool net_report)
We don't check thd->lex->mi.log_file_name for NULL here
since it is checked in sql_yacc.yy
*/
- strmake(mi->rli.until_log_name, thd->lex->mi.log_file_name,
- sizeof(mi->rli.until_log_name)-1);
+ strmake_buf(mi->rli.until_log_name, thd->lex->mi.log_file_name);
}
else if (thd->lex->mi.relay_log_pos)
{
@@ -1102,8 +1295,7 @@ int start_slave(THD* thd , Master_info* mi, bool net_report)
slave_errno=ER_BAD_SLAVE_UNTIL_COND;
mi->rli.until_condition= Relay_log_info::UNTIL_RELAY_POS;
mi->rli.until_log_pos= thd->lex->mi.relay_log_pos;
- strmake(mi->rli.until_log_name, thd->lex->mi.relay_log_name,
- sizeof(mi->rli.until_log_name)-1);
+ strmake_buf(mi->rli.until_log_name, thd->lex->mi.relay_log_name);
}
else
mi->rli.clear_until_condition();
@@ -1139,7 +1331,7 @@ int start_slave(THD* thd , Master_info* mi, bool net_report)
ER(ER_MISSING_SKIP_SLAVE));
}
- pthread_mutex_unlock(&mi->rli.data_lock);
+ mysql_mutex_unlock(&mi->rli.data_lock);
}
else if (thd->lex->mi.pos || thd->lex->mi.relay_log_pos)
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_UNTIL_COND_IGNORED,
@@ -1185,7 +1377,7 @@ int start_slave(THD* thd , Master_info* mi, bool net_report)
@param mi Pointer to Master_info object for the slave's IO thread.
- @param net_report If true, saves the exit status into thd->main_da.
+ @param net_report If true, saves the exit status into thd->stmt_da.
@retval 0 success
@retval 1 error
@@ -1198,7 +1390,7 @@ int stop_slave(THD* thd, Master_info* mi, bool net_report )
if (!thd)
thd = current_thd;
- if (check_access(thd, SUPER_ACL, any_db,0,0,0,0))
+ if (check_access(thd, SUPER_ACL, any_db, NULL, NULL, 0, 0))
DBUG_RETURN(1);
thd_proc_info(thd, "Killing slave");
int thread_mask;
@@ -1282,14 +1474,9 @@ int reset_slave(THD *thd, Master_info* mi)
goto err;
}
- /*
- Clear master's log coordinates and reset host/user/etc to the values
- specified in mysqld's options (only for good display of SHOW SLAVE STATUS;
- next init_master_info() (in start_slave() for example) would have set them
- the same way; but here this is for the case where the user does SHOW SLAVE
- STATUS; before doing START SLAVE;
- */
- init_master_info_with_options(mi);
+ /* Clear master's log coordinates and associated information */
+ mi->clear_in_memory_info(thd->lex->reset_slave_info.all);
+
/*
Reset errors (the idea is that we forget about the
old master).
@@ -1302,19 +1489,22 @@ int reset_slave(THD *thd, Master_info* mi)
end_master_info(mi);
// and delete these two files
fn_format(fname, master_info_file, mysql_data_home, "", 4+32);
- if (my_stat(fname, &stat_area, MYF(0)) && my_delete(fname, MYF(MY_WME)))
+ if (mysql_file_stat(key_file_master_info, fname, &stat_area, MYF(0)) &&
+ mysql_file_delete(key_file_master_info, fname, MYF(MY_WME)))
{
error=1;
goto err;
}
// delete relay_log_info_file
fn_format(fname, relay_log_info_file, mysql_data_home, "", 4+32);
- if (my_stat(fname, &stat_area, MYF(0)) && my_delete(fname, MYF(MY_WME)))
+ if (mysql_file_stat(key_file_relay_log_info, fname, &stat_area, MYF(0)) &&
+ mysql_file_delete(key_file_relay_log_info, fname, MYF(MY_WME)))
{
error=1;
goto err;
}
+ RUN_HOOK(binlog_relay_io, after_reset_slave, (thd, mi));
err:
unlock_slave_threads(mi);
if (error)
@@ -1326,7 +1516,7 @@ err:
Kill all Binlog_dump threads which previously talked to the same slave
("same" means with the same server id). Indeed, if the slave stops, if the
- Binlog_dump thread is waiting (pthread_cond_wait) for binlog update, then it
+ Binlog_dump thread is waiting (mysql_cond_wait) for binlog update, then it
will keep existing until a query is written to the binlog. If the master is
idle, then this could last long, and if the slave reconnects, we could have 2
Binlog_dump threads in SHOW PROCESSLIST, until a query is written to the
@@ -1344,7 +1534,7 @@ err:
void kill_zombie_dump_threads(uint32 slave_server_id)
{
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
I_List_iterator<THD> it(threads);
THD *tmp;
@@ -1353,11 +1543,11 @@ void kill_zombie_dump_threads(uint32 slave_server_id)
if (tmp->command == COM_BINLOG_DUMP &&
tmp->server_id == slave_server_id)
{
- pthread_mutex_lock(&tmp->LOCK_thd_kill); // Lock from delete
+ mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
break;
}
}
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
if (tmp)
{
/*
@@ -1366,10 +1556,36 @@ void kill_zombie_dump_threads(uint32 slave_server_id)
again. We just to do kill the thread ourselves.
*/
tmp->awake(KILL_QUERY);
- pthread_mutex_unlock(&tmp->LOCK_thd_kill);
+ mysql_mutex_unlock(&tmp->LOCK_thd_data);
}
}
+/**
+ Get value for a string parameter with error checking
+
+ Note that in case of error the original string should not be updated!
+
+ @ret 0 ok
+ @ret 1 error
+*/
+
+static bool get_string_parameter(char *to, const char *from, size_t length,
+ const char *name, CHARSET_INFO *cs)
+{
+ if (from) // Empty paramaters allowed
+ {
+ size_t from_length= strlen(from);
+ uint 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, length / cs->mbmaxlen);
+ return 1;
+ }
+ memcpy(to, from, from_length+1);
+ }
+ return 0;
+}
+
/**
Execute a CHANGE MASTER statement.
@@ -1388,6 +1604,7 @@ bool change_master(THD* thd, Master_info* mi)
int thread_mask;
const char* errmsg= 0;
bool need_relay_log_purge= 1;
+ bool ret= FALSE;
char saved_host[HOSTNAME_LENGTH + 1];
uint saved_port;
char saved_log_name[FN_REFLEN];
@@ -1396,22 +1613,35 @@ bool change_master(THD* thd, Master_info* mi)
lock_slave_threads(mi);
init_thread_mask(&thread_mask,mi,0 /*not inverse*/);
+ LEX_MASTER_INFO* lex_mi= &thd->lex->mi;
if (thread_mask) // We refuse if any slave thread is running
{
my_message(ER_SLAVE_MUST_STOP, ER(ER_SLAVE_MUST_STOP), MYF(0));
- unlock_slave_threads(mi);
- DBUG_RETURN(TRUE);
+ ret= TRUE;
+ goto err;
}
thd_proc_info(thd, "Changing master");
- LEX_MASTER_INFO* lex_mi= &thd->lex->mi;
+ /*
+ We need to check if there is an empty master_host. Otherwise
+ change master succeeds, a master.info file is created containing
+ empty master_host string and when issuing: start slave; an error
+ is thrown stating that the server is not configured as slave.
+ (See BUG#28796).
+ */
+ if(lex_mi->host && !*lex_mi->host)
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), "MASTER_HOST");
+ unlock_slave_threads(mi);
+ DBUG_RETURN(TRUE);
+ }
// TODO: see if needs re-write
if (init_master_info(mi, master_info_file, relay_log_info_file, 0,
thread_mask))
{
my_message(ER_MASTER_INFO, ER(ER_MASTER_INFO), MYF(0));
- unlock_slave_threads(mi);
- DBUG_RETURN(TRUE);
+ ret= TRUE;
+ goto err;
}
/*
@@ -1423,9 +1653,9 @@ bool change_master(THD* thd, Master_info* mi)
/*
Before processing the command, save the previous state.
*/
- strmake(saved_host, mi->host, HOSTNAME_LENGTH);
+ strmake_buf(saved_host, mi->host);
saved_port= mi->port;
- strmake(saved_log_name, mi->master_log_name, FN_REFLEN - 1);
+ strmake_buf(saved_log_name, mi->master_log_name);
saved_log_pos= mi->master_log_pos;
/*
@@ -1440,42 +1670,80 @@ bool change_master(THD* thd, Master_info* mi)
}
if (lex_mi->log_file_name)
- strmake(mi->master_log_name, lex_mi->log_file_name,
- sizeof(mi->master_log_name)-1);
+ strmake_buf(mi->master_log_name, lex_mi->log_file_name);
if (lex_mi->pos)
{
mi->master_log_pos= lex_mi->pos;
}
DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos));
- if (lex_mi->host)
- strmake(mi->host, lex_mi->host, sizeof(mi->host)-1);
- if (lex_mi->user)
- strmake(mi->user, lex_mi->user, sizeof(mi->user)-1);
- if (lex_mi->password)
- strmake(mi->password, lex_mi->password, sizeof(mi->password)-1);
+ if (get_string_parameter(mi->host, lex_mi->host, sizeof(mi->host)-1,
+ "MASTER_HOST", system_charset_info) ||
+ get_string_parameter(mi->user, lex_mi->user, sizeof(mi->user)-1,
+ "MASTER_USER", system_charset_info) ||
+ get_string_parameter(mi->password, lex_mi->password,
+ sizeof(mi->password)-1, "MASTER_PASSWORD",
+ &my_charset_bin))
+ {
+ ret= TRUE;
+ goto err;
+ }
+
if (lex_mi->port)
mi->port = lex_mi->port;
if (lex_mi->connect_retry)
mi->connect_retry = lex_mi->connect_retry;
+ if (lex_mi->heartbeat_opt != LEX_MASTER_INFO::LEX_MI_UNCHANGED)
+ mi->heartbeat_period = lex_mi->heartbeat_period;
+ else
+ mi->heartbeat_period= (float) min(SLAVE_MAX_HEARTBEAT_PERIOD,
+ (slave_net_timeout/2.0));
+ mi->received_heartbeats= LL(0); // counter lives until master is CHANGEd
+ /*
+ reset the last time server_id list if the current CHANGE MASTER
+ is mentioning IGNORE_SERVER_IDS= (...)
+ */
+ if (lex_mi->repl_ignore_server_ids_opt == LEX_MASTER_INFO::LEX_MI_ENABLE)
+ reset_dynamic(&mi->ignore_server_ids);
+ for (uint i= 0; i < lex_mi->repl_ignore_server_ids.elements; i++)
+ {
+ ulong s_id;
+ get_dynamic(&lex_mi->repl_ignore_server_ids, (uchar*) &s_id, i);
+ if (s_id == ::server_id && replicate_same_server_id)
+ {
+ my_error(ER_SLAVE_IGNORE_SERVER_IDS, MYF(0), static_cast<int>(s_id));
+ ret= TRUE;
+ goto err;
+ }
+ else
+ {
+ if (bsearch((const ulong *) &s_id,
+ mi->ignore_server_ids.buffer,
+ mi->ignore_server_ids.elements, sizeof(ulong),
+ (int (*) (const void*, const void*))
+ change_master_server_id_cmp) == NULL)
+ insert_dynamic(&mi->ignore_server_ids, (uchar*) &s_id);
+ }
+ }
+ sort_dynamic(&mi->ignore_server_ids, (qsort_cmp) change_master_server_id_cmp);
- if (lex_mi->ssl != LEX_MASTER_INFO::SSL_UNCHANGED)
- mi->ssl= (lex_mi->ssl == LEX_MASTER_INFO::SSL_ENABLE);
+ if (lex_mi->ssl != LEX_MASTER_INFO::LEX_MI_UNCHANGED)
+ mi->ssl= (lex_mi->ssl == LEX_MASTER_INFO::LEX_MI_ENABLE);
- if (lex_mi->ssl_verify_server_cert != LEX_MASTER_INFO::SSL_UNCHANGED)
+ if (lex_mi->ssl_verify_server_cert != LEX_MASTER_INFO::LEX_MI_UNCHANGED)
mi->ssl_verify_server_cert=
- (lex_mi->ssl_verify_server_cert == LEX_MASTER_INFO::SSL_ENABLE);
+ (lex_mi->ssl_verify_server_cert == LEX_MASTER_INFO::LEX_MI_ENABLE);
if (lex_mi->ssl_ca)
- strmake(mi->ssl_ca, lex_mi->ssl_ca, sizeof(mi->ssl_ca)-1);
+ strmake_buf(mi->ssl_ca, lex_mi->ssl_ca);
if (lex_mi->ssl_capath)
- strmake(mi->ssl_capath, lex_mi->ssl_capath, sizeof(mi->ssl_capath)-1);
+ strmake_buf(mi->ssl_capath, lex_mi->ssl_capath);
if (lex_mi->ssl_cert)
- strmake(mi->ssl_cert, lex_mi->ssl_cert, sizeof(mi->ssl_cert)-1);
+ strmake_buf(mi->ssl_cert, lex_mi->ssl_cert);
if (lex_mi->ssl_cipher)
- strmake(mi->ssl_cipher, lex_mi->ssl_cipher, sizeof(mi->ssl_cipher)-1);
+ strmake_buf(mi->ssl_cipher, lex_mi->ssl_cipher);
if (lex_mi->ssl_key)
- strmake(mi->ssl_key, lex_mi->ssl_key, sizeof(mi->ssl_key)-1);
+ strmake_buf(mi->ssl_key, lex_mi->ssl_key);
#ifndef HAVE_OPENSSL
if (lex_mi->ssl || lex_mi->ssl_ca || lex_mi->ssl_capath ||
lex_mi->ssl_cert || lex_mi->ssl_cipher || lex_mi->ssl_key ||
@@ -1487,10 +1755,10 @@ bool change_master(THD* thd, Master_info* mi)
if (lex_mi->relay_log_name)
{
need_relay_log_purge= 0;
- strmake(mi->rli.group_relay_log_name,lex_mi->relay_log_name,
- sizeof(mi->rli.group_relay_log_name)-1);
- strmake(mi->rli.event_relay_log_name,lex_mi->relay_log_name,
- sizeof(mi->rli.event_relay_log_name)-1);
+ char relay_log_name[FN_REFLEN];
+ mi->rli.relay_log.make_log_name(relay_log_name, lex_mi->relay_log_name);
+ strmake_buf(mi->rli.group_relay_log_name, relay_log_name);
+ strmake_buf(mi->rli.event_relay_log_name, relay_log_name);
}
if (lex_mi->relay_log_pos)
@@ -1526,8 +1794,7 @@ bool change_master(THD* thd, Master_info* mi)
*/
mi->master_log_pos = max(BIN_LOG_HEADER_SIZE,
mi->rli.group_master_log_pos);
- strmake(mi->master_log_name, mi->rli.group_master_log_name,
- sizeof(mi->master_log_name)-1);
+ strmake_buf(mi->master_log_name, mi->rli.group_master_log_name);
}
/*
Relay log's IO_CACHE may not be inited, if rli->inited==0 (server was never
@@ -1536,8 +1803,8 @@ bool change_master(THD* thd, Master_info* mi)
if (flush_master_info(mi, FALSE, FALSE))
{
my_error(ER_RELAY_LOG_INIT, MYF(0), "Failed to flush master info file");
- unlock_slave_threads(mi);
- DBUG_RETURN(TRUE);
+ ret= TRUE;
+ goto err;
}
if (need_relay_log_purge)
{
@@ -1548,8 +1815,8 @@ bool change_master(THD* thd, Master_info* mi)
&errmsg))
{
my_error(ER_RELAY_LOG_FAIL, MYF(0), errmsg);
- unlock_slave_threads(mi);
- DBUG_RETURN(TRUE);
+ ret= TRUE;
+ goto err;
}
}
else
@@ -1564,8 +1831,8 @@ bool change_master(THD* thd, Master_info* mi)
&msg, 0))
{
my_error(ER_RELAY_LOG_INIT, MYF(0), msg);
- unlock_slave_threads(mi);
- DBUG_RETURN(TRUE);
+ ret= TRUE;
+ goto err;
}
}
/*
@@ -1580,13 +1847,12 @@ bool change_master(THD* thd, Master_info* mi)
*/
mi->rli.group_master_log_pos= mi->master_log_pos;
DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos));
- strmake(mi->rli.group_master_log_name,mi->master_log_name,
- sizeof(mi->rli.group_master_log_name)-1);
+ strmake_buf(mi->rli.group_master_log_name,mi->master_log_name);
if (!mi->rli.group_master_log_name[0]) // uninitialized case
mi->rli.group_master_log_pos=0;
- pthread_mutex_lock(&mi->rli.data_lock);
+ mysql_mutex_lock(&mi->rli.data_lock);
mi->rli.abort_pos_wait++; /* for MASTER_POS_WAIT() to abort */
/* Clear the errors, for a clean start */
mi->rli.clear_error();
@@ -1608,13 +1874,15 @@ bool change_master(THD* thd, Master_info* mi)
not exist anymore).
*/
flush_relay_log_info(&mi->rli);
- pthread_cond_broadcast(&mi->data_cond);
- pthread_mutex_unlock(&mi->rli.data_lock);
+ mysql_cond_broadcast(&mi->data_cond);
+ mysql_mutex_unlock(&mi->rli.data_lock);
+err:
unlock_slave_threads(mi);
thd_proc_info(thd, 0);
- my_ok(thd);
- DBUG_RETURN(FALSE);
+ if (ret == FALSE)
+ my_ok(thd);
+ DBUG_RETURN(ret);
}
@@ -1635,24 +1903,11 @@ int reset_master(THD* thd)
ER(ER_FLUSH_MASTER_BINLOG_CLOSED), MYF(ME_BELL+ME_WAITTANG));
return 1;
}
- return mysql_bin_log.reset_logs(thd);
-}
-
-int cmp_master_pos(const char* log_file_name1, ulonglong log_pos1,
- const char* log_file_name2, ulonglong log_pos2)
-{
- int res;
- size_t log_file_name1_len= strlen(log_file_name1);
- size_t log_file_name2_len= strlen(log_file_name2);
- // We assume that both log names match up to '.'
- if (log_file_name1_len == log_file_name2_len)
- {
- if ((res= strcmp(log_file_name1, log_file_name2)))
- return res;
- return (log_pos1 < log_pos2) ? -1 : (log_pos1 == log_pos2) ? 0 : 1;
- }
- return ((log_file_name1_len < log_file_name2_len) ? -1 : 1);
+ if (mysql_bin_log.reset_logs(thd))
+ return 1;
+ RUN_HOOK(binlog_transmit, after_reset_master, (thd, 0 /* flags */));
+ return 0;
}
@@ -1673,27 +1928,44 @@ bool mysql_show_binlog_events(THD* thd)
bool ret = TRUE;
IO_CACHE log;
File file = -1;
+ MYSQL_BIN_LOG *binary_log= NULL;
int old_max_allowed_packet= thd->variables.max_allowed_packet;
LOG_INFO linfo;
DBUG_ENTER("mysql_show_binlog_events");
Log_event::init_show_field_list(&field_list);
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
Format_description_log_event *description_event= new
Format_description_log_event(3); /* MySQL 4.0 by default */
- /*
- Wait for handlers to insert any pending information
- into the binlog. For e.g. ndb which updates the binlog asynchronously
- this is needed so that the uses sees all its own commands in the binlog
- */
- ha_binlog_wait(thd);
+ DBUG_ASSERT(thd->lex->sql_command == SQLCOM_SHOW_BINLOG_EVENTS ||
+ thd->lex->sql_command == SQLCOM_SHOW_RELAYLOG_EVENTS);
- if (mysql_bin_log.is_open())
+ /* select wich binary log to use: binlog or relay */
+ if ( thd->lex->sql_command == SQLCOM_SHOW_BINLOG_EVENTS )
+ {
+ /*
+ Wait for handlers to insert any pending information
+ into the binlog. For e.g. ndb which updates the binlog asynchronously
+ this is needed so that the uses sees all its own commands in the binlog
+ */
+ ha_binlog_wait(thd);
+
+ binary_log= &mysql_bin_log;
+ }
+ else /* showing relay log contents */
+ {
+ if (!active_mi)
+ DBUG_RETURN(TRUE);
+
+ binary_log= &(active_mi->rli.relay_log);
+ }
+
+ if (binary_log->is_open())
{
LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
SELECT_LEX_UNIT *unit= &thd->lex->unit;
@@ -1701,7 +1973,7 @@ bool mysql_show_binlog_events(THD* thd)
my_off_t pos = max(BIN_LOG_HEADER_SIZE, lex_mi->pos); // user-friendly
char search_file_name[FN_REFLEN], *name;
const char *log_file_name = lex_mi->log_file_name;
- pthread_mutex_t *log_lock = mysql_bin_log.get_log_lock();
+ mysql_mutex_t *log_lock = binary_log->get_log_lock();
Log_event* ev;
unit->set_limit(thd->lex->current_select);
@@ -1710,21 +1982,21 @@ bool mysql_show_binlog_events(THD* thd)
name= search_file_name;
if (log_file_name)
- mysql_bin_log.make_log_name(search_file_name, log_file_name);
+ binary_log->make_log_name(search_file_name, log_file_name);
else
name=0; // Find first log
linfo.index_file_offset = 0;
- if (mysql_bin_log.find_log_pos(&linfo, name, 1))
+ if (binary_log->find_log_pos(&linfo, name, 1))
{
errmsg = "Could not find target log";
goto err;
}
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
thd->current_linfo = &linfo;
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
if ((file=open_binlog(&log, linfo.log_file_name, &errmsg)) < 0)
goto err;
@@ -1734,7 +2006,7 @@ bool mysql_show_binlog_events(THD* thd)
*/
thd->variables.max_allowed_packet += MAX_LOG_EVENT_HEADER;
- pthread_mutex_lock(log_lock);
+ mysql_mutex_lock(log_lock);
/*
open_binlog() sought to position 4.
@@ -1744,7 +2016,7 @@ bool mysql_show_binlog_events(THD* thd)
This code will fail on a mixed relay log (one which has Format_desc then
Rotate then Format_desc).
*/
- ev = Log_event::read_log_event(&log,(pthread_mutex_t*)0,description_event,
+ ev= Log_event::read_log_event(&log, (mysql_mutex_t*)0, description_event,
opt_master_verify_checksum);
if (ev)
{
@@ -1766,7 +2038,7 @@ bool mysql_show_binlog_events(THD* thd)
}
for (event_count = 0;
- (ev = Log_event::read_log_event(&log,(pthread_mutex_t*) 0,
+ (ev = Log_event::read_log_event(&log, (mysql_mutex_t*) 0,
description_event,
opt_master_verify_checksum)); )
{
@@ -1778,7 +2050,7 @@ bool mysql_show_binlog_events(THD* thd)
{
errmsg = "Net error";
delete ev;
- pthread_mutex_unlock(log_lock);
+ mysql_mutex_unlock(log_lock);
goto err;
}
@@ -1792,11 +2064,11 @@ bool mysql_show_binlog_events(THD* thd)
if (event_count < limit_end && log.error)
{
errmsg = "Wrong offset or I/O error";
- pthread_mutex_unlock(log_lock);
+ mysql_mutex_unlock(log_lock);
goto err;
}
- pthread_mutex_unlock(log_lock);
+ mysql_mutex_unlock(log_lock);
}
// Check that linfo is still on the function scope.
DEBUG_SYNC(thd, "after_show_binlog_events");
@@ -1808,7 +2080,7 @@ err:
if (file >= 0)
{
end_io_cache(&log);
- (void) my_close(file, MYF(MY_WME));
+ mysql_file_close(file, MYF(MY_WME));
}
if (errmsg)
@@ -1817,9 +2089,9 @@ err:
else
my_eof(thd);
- pthread_mutex_lock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_thread_count);
thd->current_linfo = 0;
- pthread_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
thd->variables.max_allowed_packet= old_max_allowed_packet;
DBUG_RETURN(ret);
}
@@ -1845,7 +2117,7 @@ bool show_binlog_info(THD* thd)
field_list.push_back(new Item_empty_string("Binlog_Do_DB",255));
field_list.push_back(new Item_empty_string("Binlog_Ignore_DB",255));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
protocol->prepare_for_resend();
@@ -1890,23 +2162,23 @@ bool show_binlogs(THD* thd)
if (!mysql_bin_log.is_open())
{
- my_message(ER_NO_BINARY_LOGGING, ER(ER_NO_BINARY_LOGGING), MYF(0));
+ my_error(ER_NO_BINARY_LOGGING, MYF(0));
DBUG_RETURN(TRUE);
}
field_list.push_back(new Item_empty_string("Log_name", 255));
field_list.push_back(new Item_return_int("File_size", 20,
MYSQL_TYPE_LONGLONG));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
- pthread_mutex_lock(mysql_bin_log.get_log_lock());
+ mysql_mutex_lock(mysql_bin_log.get_log_lock());
mysql_bin_log.lock_index();
index_file=mysql_bin_log.get_index_file();
mysql_bin_log.raw_get_current_log(&cur); // dont take mutex
- pthread_mutex_unlock(mysql_bin_log.get_log_lock()); // lockdep, OK
+ mysql_mutex_unlock(mysql_bin_log.get_log_lock()); // lockdep, OK
cur_dir_len= dirname_length(cur.log_file_name);
@@ -1929,11 +2201,12 @@ bool show_binlogs(THD* thd)
else
{
/* this is an old log, open it and find the size */
- if ((file= my_open(fname, O_RDONLY | O_SHARE | O_BINARY,
- MYF(0))) >= 0)
+ if ((file= mysql_file_open(key_file_binlog,
+ fname, O_RDONLY | O_SHARE | O_BINARY,
+ MYF(0))) >= 0)
{
- file_length= (ulonglong) my_seek(file, 0L, MY_SEEK_END, MYF(0));
- my_close(file, MYF(0));
+ file_length= (ulonglong) mysql_file_seek(file, 0L, MY_SEEK_END, MYF(0));
+ mysql_file_close(file, MYF(0));
}
}
protocol->store(file_length);
@@ -1970,7 +2243,7 @@ int log_loaded_block(IO_CACHE* file)
uchar* buffer= (uchar*) my_b_get_buffer_start(file);
uint max_event_size= current_thd->variables.max_allowed_packet;
lf_info= (LOAD_FILE_INFO*) file->arg;
- if (lf_info->thd->current_stmt_binlog_row_based)
+ if (lf_info->thd->is_current_stmt_binlog_format_row())
DBUG_RETURN(0);
if (lf_info->last_pos_in_file != HA_POS_ERROR &&
lf_info->last_pos_in_file >= my_b_get_pos_in_file(file))
@@ -2003,140 +2276,4 @@ int log_loaded_block(IO_CACHE* file)
DBUG_RETURN(0);
}
-/*
- Replication System Variables
-*/
-
-class sys_var_slave_skip_counter :public sys_var
-{
-public:
- sys_var_slave_skip_counter(sys_var_chain *chain, const char *name_arg)
- :sys_var(name_arg)
- { chain_sys_var(chain); }
- bool check(THD *thd, set_var *var);
- bool update(THD *thd, set_var *var);
- bool check_type(enum_var_type type) { return type != OPT_GLOBAL; }
- /*
- We can't retrieve the value of this, so we don't have to define
- type() or value_ptr()
- */
-};
-
-class sys_var_sync_binlog_period :public sys_var_long_ptr
-{
-public:
- sys_var_sync_binlog_period(sys_var_chain *chain, const char *name_arg,
- ulong *value_ptr)
- :sys_var_long_ptr(chain, name_arg,value_ptr) {}
- bool update(THD *thd, set_var *var);
-};
-
-static sys_var_chain vars = { NULL, NULL };
-
-static sys_var_const sys_log_slave_updates(&vars, "log_slave_updates",
- OPT_GLOBAL, SHOW_MY_BOOL,
- (uchar*) &opt_log_slave_updates);
-static sys_var_const
-sys_replicate_annotate_row_events(&vars,
- "replicate_annotate_row_events",
- OPT_GLOBAL, SHOW_MY_BOOL,
- (uchar*) &opt_replicate_annotate_row_events);
-static sys_var_const sys_relay_log(&vars, "relay_log",
- OPT_GLOBAL, SHOW_CHAR_PTR,
- (uchar*) &opt_relay_logname);
-static sys_var_const sys_relay_log_index(&vars, "relay_log_index",
- OPT_GLOBAL, SHOW_CHAR_PTR,
- (uchar*) &opt_relaylog_index_name);
-static sys_var_const sys_relay_log_info_file(&vars, "relay_log_info_file",
- OPT_GLOBAL, SHOW_CHAR_PTR,
- (uchar*) &relay_log_info_file);
-static sys_var_bool_ptr sys_relay_log_purge(&vars, "relay_log_purge",
- &relay_log_purge);
-static sys_var_const sys_relay_log_space_limit(&vars,
- "relay_log_space_limit",
- OPT_GLOBAL, SHOW_LONGLONG,
- (uchar*)
- &relay_log_space_limit);
-static sys_var_const sys_slave_load_tmpdir(&vars, "slave_load_tmpdir",
- OPT_GLOBAL, SHOW_CHAR_PTR,
- (uchar*) &slave_load_tmpdir);
-static sys_var_long_ptr sys_slave_net_timeout(&vars, "slave_net_timeout",
- &slave_net_timeout);
-static sys_var_const sys_slave_skip_errors(&vars, "slave_skip_errors",
- OPT_GLOBAL, SHOW_CHAR,
- (uchar*) slave_skip_error_names);
-static sys_var_long_ptr sys_slave_trans_retries(&vars, "slave_transaction_retries",
- &slave_trans_retries);
-static sys_var_sync_binlog_period sys_sync_binlog_period(&vars, "sync_binlog", &sync_binlog_period);
-static sys_var_slave_skip_counter sys_slave_skip_counter(&vars, "sql_slave_skip_counter");
-static sys_var_bool_ptr sys_master_verify_checksum(&vars,
- "master_verify_checksum",
- &opt_master_verify_checksum);
-static sys_var_bool_ptr sys_slave_sql_verify_checksum(&vars,
- "slave_sql_verify_checksum",
- &opt_slave_sql_verify_checksum);
-
-
-bool sys_var_slave_skip_counter::check(THD *thd, set_var *var)
-{
- int result= 0;
- pthread_mutex_lock(&LOCK_active_mi);
- pthread_mutex_lock(&active_mi->rli.run_lock);
- if (active_mi->rli.slave_running)
- {
- my_message(ER_SLAVE_MUST_STOP, ER(ER_SLAVE_MUST_STOP), MYF(0));
- result=1;
- }
- pthread_mutex_unlock(&active_mi->rli.run_lock);
- pthread_mutex_unlock(&LOCK_active_mi);
- var->save_result.ulong_value= (ulong) var->value->val_int();
- return result;
-}
-
-
-bool sys_var_slave_skip_counter::update(THD *thd, set_var *var)
-{
- pthread_mutex_lock(&LOCK_active_mi);
- pthread_mutex_lock(&active_mi->rli.run_lock);
- /*
- The following test should normally never be true as we test this
- in the check function; To be safe against multiple
- SQL_SLAVE_SKIP_COUNTER request, we do the check anyway
- */
- if (!active_mi->rli.slave_running)
- {
- pthread_mutex_lock(&active_mi->rli.data_lock);
- active_mi->rli.slave_skip_counter= var->save_result.ulong_value;
- pthread_mutex_unlock(&active_mi->rli.data_lock);
- }
- pthread_mutex_unlock(&active_mi->rli.run_lock);
- pthread_mutex_unlock(&LOCK_active_mi);
- return 0;
-}
-
-
-bool sys_var_sync_binlog_period::update(THD *thd, set_var *var)
-{
- sync_binlog_period= (ulong) var->save_result.ulonglong_value;
- return 0;
-}
-
-int init_replication_sys_vars()
-{
- if (mysql_add_sys_var_chain(vars.first, my_long_options))
- {
- /* should not happen */
- fprintf(stderr, "failed to initialize replication system variables");
- unireg_abort(1);
- }
- return 0;
-}
-
-#elif defined(__WIN__)
-
-// Remove linker warning 4221 about empty file
-namespace { char dummy; };
-
#endif /* HAVE_REPLICATION */
-
-
diff --git a/sql/sql_repl.h b/sql/sql_repl.h
index f9e1dfd45a4..c5a0b31388e 100644
--- a/sql/sql_repl.h
+++ b/sql/sql_repl.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000-2008 MySQL AB
+/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,7 +11,10 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_REPL_INCLUDED
+#define SQL_REPL_INCLUDED
#include "rpl_filter.h"
@@ -22,9 +25,9 @@ typedef struct st_slave_info
{
uint32 server_id;
uint32 rpl_recovery_rank, master_id;
- char host[HOSTNAME_LENGTH+1];
+ char host[HOSTNAME_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1];
char user[USERNAME_LENGTH+1];
- char password[MAX_PASSWORD_LENGTH+1];
+ char password[MAX_PASSWORD_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1];
uint16 port;
THD* thd;
} SLAVE_INFO;
@@ -40,8 +43,6 @@ int start_slave(THD* thd, Master_info* mi, bool net_report);
int stop_slave(THD* thd, Master_info* mi, bool net_report);
bool change_master(THD* thd, Master_info* mi);
bool mysql_show_binlog_events(THD* thd);
-int cmp_master_pos(const char* log_file_name1, ulonglong log_pos1,
- const char* log_file_name2, ulonglong log_pos2);
int reset_slave(THD *thd, Master_info* mi);
int reset_master(THD* thd);
bool purge_master_logs(THD* thd, const char* to_log);
@@ -62,6 +63,8 @@ typedef struct st_load_file_info
int log_loaded_block(IO_CACHE* file);
int init_replication_sys_vars();
+void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, ushort flags);
#endif /* HAVE_REPLICATION */
+#endif /* SQL_REPL_INCLUDED */
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index eae06393c75..bdb2d1c3346 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1,5 +1,5 @@
-/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2009, 2014, Monty Program Ab.
+/* Copyright (c) 2000, 2013 Oracle and/or its affiliates.
+ Copyright (c) 2009, 2013 Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -29,11 +29,30 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
#include "sql_select.h"
-#include "sql_cursor.h"
+#include "sql_cache.h" // query_cache_*
+#include "sql_table.h" // primary_key_name
+#include "probes_mysql.h"
+#include "key.h" // key_copy, key_cmp, key_cmp_if_same
+#include "lock.h" // mysql_unlock_some_tables,
+ // mysql_unlock_read_tables
+#include "sql_show.h" // append_identifier
+#include "sql_base.h" // setup_wild, setup_fields, fill_record
+#include "sql_parse.h" // check_stack_overrun
+#include "sql_partition.h" // make_used_partitions_str
+#include "sql_acl.h" // *_ACL
+#include "sql_test.h" // print_where, print_keyuse_array,
+ // print_sjm, print_plan, TEST_join
+#include "records.h" // init_read_record, end_read_record
+#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 "debug_sync.h" // DEBUG_SYNC
#include <m_ctype.h>
#include <my_bit.h>
#include <hash.h>
@@ -76,9 +95,11 @@ static bool best_extension_by_limited_search(JOIN *join,
double read_time, uint depth,
uint prune_level);
static uint determine_search_depth(JOIN* join);
+C_MODE_START
static int join_tab_cmp(const void *dummy, const void* ptr1, const void* ptr2);
static int join_tab_cmp_straight(const void *dummy, const void* ptr1, const void* ptr2);
static int join_tab_cmp_embedded_first(const void *emb, const void* ptr1, const void *ptr2);
+C_MODE_END
/*
TODO: 'find_best' is here only temporarily until 'greedy_search' is
tested and approved.
@@ -130,30 +151,24 @@ static COND *optimize_cond(JOIN *join, COND *conds,
bool ignore_on_conds,
Item::cond_result *cond_value,
COND_EQUAL **cond_equal);
-static bool const_expression_in_where(COND *conds,Item *item, Item **comp_item);
+bool const_expression_in_where(COND *conds,Item *item, Item **comp_item);
static bool create_internal_tmp_table_from_heap2(THD *, TABLE *,
ENGINE_COLUMNDEF *, ENGINE_COLUMNDEF **,
- int, bool, handlerton *, const char *);
+ int, bool, handlerton *, const char *, bool *);
static int do_select(JOIN *join,List<Item> *fields,TABLE *tmp_table,
Procedure *proc);
-static enum_nested_loop_state
-evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
- int error);
+static enum_nested_loop_state evaluate_join_record(JOIN *, JOIN_TAB *, int);
static enum_nested_loop_state
evaluate_null_complemented_join_record(JOIN *join, JOIN_TAB *join_tab);
static enum_nested_loop_state
end_send(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
-enum_nested_loop_state
-end_send_group(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
static enum_nested_loop_state
end_write(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
static enum_nested_loop_state
end_update(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
static enum_nested_loop_state
end_unique_update(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
-enum_nested_loop_state
-end_write_group(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
static int test_if_group_changed(List<Cached_item> &list);
static int join_read_const_table(JOIN_TAB *tab, POSITION *pos);
@@ -193,6 +208,14 @@ static COND *make_cond_for_table_from_pred(THD *thd, Item *root_cond,
static Item* part_of_refkey(TABLE *form,Field *field);
uint find_shortest_key(TABLE *table, const key_map *usable_keys);
+static bool test_if_cheaper_ordering(const JOIN_TAB *tab,
+ ORDER *order, TABLE *table,
+ key_map usable_keys, int key,
+ ha_rows select_limit,
+ int *new_key, int *new_key_direction,
+ ha_rows *new_select_limit,
+ uint *new_used_key_parts= NULL,
+ uint *saved_best_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);
@@ -218,7 +241,8 @@ static ORDER *create_distinct_group(THD *thd, Item **ref_pointer_array,
List<Item> &all_fields,
bool *all_order_by_fields_used);
static bool test_if_subpart(ORDER *a,ORDER *b);
-static TABLE *get_sort_by_table(ORDER *a,ORDER *b,List<TABLE_LIST> &tables);
+static TABLE *get_sort_by_table(ORDER *a,ORDER *b,List<TABLE_LIST> &tables,
+ table_map const_tables);
static void calc_group_buffer(JOIN *join,ORDER *group);
static bool make_group_fields(JOIN *main_join, JOIN *curr_join);
static bool alloc_group_fields(JOIN *join,ORDER *group);
@@ -237,6 +261,7 @@ static void update_tmptable_sum_func(Item_sum **func,TABLE *tmp_table);
static void copy_sum_funcs(Item_sum **func_ptr, Item_sum **end);
static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab);
static bool setup_sum_funcs(THD *thd, Item_sum **func_ptr);
+static bool prepare_sum_aggregators(Item_sum **func_ptr, bool need_distinct);
static bool init_sum_functions(Item_sum **func, Item_sum **end);
static bool update_sum_func(Item_sum **func);
static void select_describe(JOIN *join, bool need_tmp_table,bool need_order,
@@ -250,6 +275,11 @@ Item_equal *find_item_equal(COND_EQUAL *cond_equal, Field *field,
JOIN_TAB *first_depth_first_tab(JOIN* join);
JOIN_TAB *next_depth_first_tab(JOIN* join, JOIN_TAB* tab);
+enum enum_exec_or_opt {WALK_OPTIMIZATION_TABS , WALK_EXECUTION_TABS};
+JOIN_TAB *first_breadth_first_tab(JOIN *join, enum enum_exec_or_opt tabs_kind);
+JOIN_TAB *next_breadth_first_tab(JOIN *join, enum enum_exec_or_opt tabs_kind,
+ JOIN_TAB *tab);
+
/**
This handles SELECT with and without UNION.
*/
@@ -260,6 +290,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
bool res;
register SELECT_LEX *select_lex = &lex->select_lex;
DBUG_ENTER("handle_select");
+ MYSQL_SELECT_START(thd->query());
if (select_lex->master_unit()->is_union() ||
select_lex->master_unit()->fake_select_lex)
@@ -283,7 +314,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
select_lex->group_list.first,
select_lex->having,
lex->proc_list.first,
- select_lex->options | thd->options |
+ select_lex->options | thd->variables.option_bits |
setup_tables_done_option,
result, unit, select_lex);
}
@@ -291,8 +322,24 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
thd->is_error()));
res|= thd->is_error();
if (unlikely(res))
- result->abort();
+ result->abort_result_set();
+ if (thd->killed == ABORT_QUERY)
+ {
+ /*
+ If LIMIT ROWS EXAMINED interrupted query execution, issue a warning,
+ continue with normal processing and produce an incomplete query result.
+ */
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT,
+ ER(ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT),
+ thd->accessed_rows_and_keys,
+ thd->lex->limit_rows_examined->val_uint());
+ thd->reset_killed();
+ }
+ /* Disable LIMIT ROWS EXAMINED after query execution. */
+ thd->lex->limit_rows_examined_cnt= ULONGLONG_MAX;
+ MYSQL_SELECT_DONE((int) res, (ulong) thd->limit_found_rows);
DBUG_RETURN(res);
}
@@ -329,7 +376,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
function is, in turn, aggregated in the query block where the outer
field was resolved or some query nested therein, then the
Item_direct_ref class should be used. Also it should be used if we are
- grouping by a subquery containing the outer field.
+ grouping by a subquery that references this outer field.
The resolution is done here and not at the fix_fields() stage as
it can be done only after aggregate functions are fixed and pulled up to
@@ -449,6 +496,7 @@ fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
static
void remove_redundant_subquery_clauses(st_select_lex *subq_select_lex)
{
+ DBUG_ENTER("remove_redundant_subquery_clauses");
Item_subselect *subq_predicate= subq_select_lex->master_unit()->item;
/*
The removal should happen for IN, ALL, ANY and EXISTS subqueries,
@@ -458,7 +506,7 @@ void remove_redundant_subquery_clauses(st_select_lex *subq_select_lex)
b) SELECT a, (<single row subquery) FROM t1
*/
if (subq_predicate->substype() == Item_subselect::SINGLEROW_SUBS)
- return;
+ DBUG_VOID_RETURN;
/* A subquery that is not single row should be one of IN/ALL/ANY/EXISTS. */
DBUG_ASSERT (subq_predicate->substype() == Item_subselect::EXISTS_SUBS ||
@@ -468,6 +516,7 @@ void remove_redundant_subquery_clauses(st_select_lex *subq_select_lex)
{
subq_select_lex->join->select_distinct= false;
subq_select_lex->options&= ~SELECT_DISTINCT;
+ DBUG_PRINT("info", ("DISTINCT removed"));
}
/*
@@ -477,8 +526,13 @@ void remove_redundant_subquery_clauses(st_select_lex *subq_select_lex)
if (subq_select_lex->group_list.elements &&
!subq_select_lex->with_sum_func && !subq_select_lex->join->having)
{
+ for (ORDER *ord= subq_select_lex->group_list.first; ord; ord= ord->next)
+ {
+ (*ord->item)->walk(&Item::eliminate_subselect_processor, FALSE, NULL);
+ }
subq_select_lex->join->group_list= NULL;
subq_select_lex->group_list.empty();
+ DBUG_PRINT("info", ("GROUP BY removed"));
}
/*
@@ -493,6 +547,7 @@ void remove_redundant_subquery_clauses(st_select_lex *subq_select_lex)
subq_select_lex->group_list.empty();
}
*/
+ DBUG_VOID_RETURN;
}
@@ -509,25 +564,25 @@ inline int setup_without_group(THD *thd, Item **ref_pointer_array,
ORDER *group, bool *hidden_group_fields)
{
int res;
- nesting_map save_allow_sum_func=thd->lex->allow_sum_func ;
+ st_select_lex *const select= thd->lex->current_select;
+ nesting_map save_allow_sum_func= thd->lex->allow_sum_func;
/*
Need to save the value, so we can turn off only any new non_agg_field_used
additions coming from the WHERE
*/
- const bool saved_non_agg_field_used=
- thd->lex->current_select->non_agg_field_used();
+ const bool saved_non_agg_field_used= select->non_agg_field_used();
DBUG_ENTER("setup_without_group");
- thd->lex->allow_sum_func&= ~(1 << thd->lex->current_select->nest_level);
+ thd->lex->allow_sum_func&= ~((nesting_map)1 << select->nest_level);
res= setup_conds(thd, tables, leaves, conds);
/* it's not wrong to have non-aggregated columns in a WHERE */
- thd->lex->current_select->set_non_agg_field_used(saved_non_agg_field_used);
+ select->set_non_agg_field_used(saved_non_agg_field_used);
- thd->lex->allow_sum_func|= 1 << thd->lex->current_select->nest_level;
+ thd->lex->allow_sum_func|= (nesting_map)1 << select->nest_level;
res= res || setup_order(thd, ref_pointer_array, tables, fields, all_fields,
order);
- thd->lex->allow_sum_func&= ~(1 << thd->lex->current_select->nest_level);
+ thd->lex->allow_sum_func&= ~((nesting_map)1 << select->nest_level);
res= res || setup_group(thd, ref_pointer_array, tables, fields, all_fields,
group, hidden_group_fields);
thd->lex->allow_sum_func= save_allow_sum_func;
@@ -607,7 +662,7 @@ JOIN::prepare(Item ***rref_pointer_array,
*/
if (select_lex->master_unit()->item && // 1)
select_lex->first_cond_optimization && // 2)
- !(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW)) // 3)
+ !thd->lex->is_view_context_analysis()) // 3)
{
remove_redundant_subquery_clauses(select_lex);
}
@@ -687,7 +742,7 @@ JOIN::prepare(Item ***rref_pointer_array,
{
nesting_map save_allow_sum_func= thd->lex->allow_sum_func;
thd->where="having clause";
- thd->lex->allow_sum_func|= 1 << select_lex_arg->nest_level;
+ thd->lex->allow_sum_func|= (nesting_map)1 << select_lex_arg->nest_level;
select_lex->having_fix_field= 1;
/*
Wrap alone field in HAVING clause in case it will be outer field of subquery
@@ -700,7 +755,6 @@ JOIN::prepare(Item ***rref_pointer_array,
(having->fix_fields(thd, &having) ||
having->check_cols(1)));
select_lex->having_fix_field= 0;
- select_lex->having= having;
if (having_fix_rc || thd->is_error())
DBUG_RETURN(-1); /* purecov: inspected */
@@ -919,16 +973,19 @@ JOIN::optimize()
{
ulonglong select_opts_for_readinfo;
uint no_jbuf_after;
-
DBUG_ENTER("JOIN::optimize");
+
do_send_rows = (unit->select_limit_cnt) ? 1 : 0;
// to prevent double initialization on EXPLAIN
if (optimized)
DBUG_RETURN(0);
optimized= 1;
+ DEBUG_SYNC(thd, "before_join_optimize");
+
thd_proc_info(thd, "optimizing");
set_allowed_join_cache_types();
+ need_distinct= TRUE;
/* Run optimize phase for all derived tables/views used in this SELECT. */
if (select_lex->handle_derived(thd->lex, DT_OPTIMIZE))
@@ -959,7 +1016,10 @@ JOIN::optimize()
}
eval_select_list_used_tables();
-
+
+ if (optimize_constant_subqueries())
+ DBUG_RETURN(1);
+
table_count= select_lex->leaf_tables.elements;
if (setup_ftfuncs(select_lex)) /* should be after having->fix_fields */
@@ -1080,7 +1140,8 @@ JOIN::optimize()
part of the nested outer join, and we can't do partition pruning
(TODO: check if this limitation can be lifted)
*/
- if (!tbl->embedding)
+ if (!tbl->embedding ||
+ (tbl->embedding && tbl->embedding->sj_on_expr))
{
Item *prune_cond= tbl->on_expr? tbl->on_expr : conds;
tbl->table->no_partitions_used= prune_partitions(thd, tbl->table,
@@ -1160,7 +1221,8 @@ JOIN::optimize()
goto setup_subq_exit;
}
error= -1; // Error is sent to client
- sort_by_table= get_sort_by_table(order, group_list, select_lex->leaf_tables);
+ /* get_sort_by_table() call used to be here: */
+ MEM_UNDEFINED(&sort_by_table, sizeof(sort_by_table));
/* Calculate how to do the join */
thd_proc_info(thd, "statistics");
@@ -1203,7 +1265,7 @@ JOIN::optimize()
error= 0;
goto setup_subq_exit;
}
- if (!(thd->options & OPTION_BIG_SELECTS) &&
+ if (!(thd->variables.option_bits & OPTION_BIG_SELECTS) &&
best_read > (double) thd->variables.max_join_size &&
!(select_options & SELECT_DESCRIBE))
{ /* purecov: inspected */
@@ -1211,7 +1273,7 @@ JOIN::optimize()
error= -1;
DBUG_RETURN(1);
}
- if (const_tables && !thd->locked_tables &&
+ if (const_tables && !thd->locked_tables_mode &&
!(select_options & SELECT_NO_UNLOCK))
mysql_unlock_some_tables(thd, table, const_tables);
if (!conds && outer_join)
@@ -1253,6 +1315,12 @@ JOIN::optimize()
{
conds= substitute_for_best_equal_field(NO_PARTICULAR_TAB, conds,
cond_equal, map2table);
+ if (thd->is_error())
+ {
+ error= 1;
+ DBUG_PRINT("error",("Error from substitute_for_best_equal"));
+ DBUG_RETURN(1);
+ }
conds->update_used_tables();
DBUG_EXECUTE("where",
print_where(conds,
@@ -1264,7 +1332,8 @@ JOIN::optimize()
Perform the optimization on fields evaluation mentioned above
for all on expressions.
*/
- for (JOIN_TAB *tab= first_linear_tab(this, WITHOUT_CONST_TABLES); tab;
+ JOIN_TAB *tab;
+ for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES); tab;
tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
{
if (*tab->on_expr_ref)
@@ -1273,6 +1342,12 @@ JOIN::optimize()
*tab->on_expr_ref,
tab->cond_equal,
map2table);
+ if (thd->is_error())
+ {
+ error= 1;
+ DBUG_PRINT("error",("Error from substitute_for_best_equal"));
+ DBUG_RETURN(1);
+ }
(*tab->on_expr_ref)->update_used_tables();
}
}
@@ -1281,7 +1356,7 @@ JOIN::optimize()
Perform the optimization on fields evaliation mentioned above
for all used ref items.
*/
- for (JOIN_TAB *tab= first_linear_tab(this, WITHOUT_CONST_TABLES); tab;
+ for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES); tab;
tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
{
uint key_copy_index=0;
@@ -1320,6 +1395,12 @@ JOIN::optimize()
new store_key_const_item(*tab->ref.key_copy[key_copy_index],
item);
}
+ else if (item->const_item())
+ {
+ tab->ref.key_copy[key_copy_index]=
+ new store_key_item(*tab->ref.key_copy[key_copy_index],
+ item, TRUE);
+ }
else
{
store_key_field *field_copy= ((store_key_field *)key_copy);
@@ -1338,6 +1419,9 @@ JOIN::optimize()
conds=new Item_int((longlong) 0,1); // Always false
}
+ /* Cache constant expressions in WHERE, HAVING, ON clauses. */
+ cache_const_exprs();
+
if (make_join_select(this, select, conds))
{
zero_result_cause=
@@ -1530,7 +1614,11 @@ JOIN::optimize()
if (test_if_subpart(group_list, order) ||
(!group_list && tmp_table_param.sum_func_count))
+ {
order=0;
+ if (is_indexed_agg_distinct(this, NULL))
+ sort_and_group= 0;
+ }
// Can't use sort on head table if using join buffering
if (full_join || hash_join)
@@ -1562,11 +1650,11 @@ JOIN::optimize()
*/
no_jbuf_after= 1 ? table_count : make_join_orderinfo(this);
+ // Don't use join buffering when we use MATCH
select_opts_for_readinfo=
(select_options & (SELECT_DESCRIBE | SELECT_NO_JOIN_CACHE)) |
(select_lex->ftfunc_list->elements ? SELECT_NO_JOIN_CACHE : 0);
- // No cache for MATCH == 'Don't use join buffering when we use MATCH'.
if (make_join_readinfo(this, select_opts_for_readinfo, no_jbuf_after))
DBUG_RETURN(1);
@@ -1686,7 +1774,14 @@ JOIN::optimize()
join_tab element of the plan for its access method.
*/
if (join_tab->is_using_loose_index_scan())
+ {
tmp_table_param.precomputed_group_by= TRUE;
+ if (join_tab->is_using_agg_loose_index_scan())
+ {
+ need_distinct= FALSE;
+ tmp_table_param.precomputed_group_by= FALSE;
+ }
+ }
error= 0;
@@ -1723,6 +1818,19 @@ int JOIN::init_execution()
DBUG_ASSERT(!(select_options & SELECT_DESCRIBE));
initialized= true;
+ /*
+ Enable LIMIT ROWS EXAMINED during query execution if:
+ (1) This JOIN is the outermost query (not a subquery or derived table)
+ This ensures that the limit is enabled when actual execution begins, and
+ not if a subquery is evaluated during optimization of the outer query.
+ (2) This JOIN is not the result of a UNION. In this case do not apply the
+ limit in order to produce the partial query result stored in the
+ UNION temp table.
+ */
+ if (!select_lex->outer_select() && // (1)
+ select_lex != select_lex->master_unit()->fake_select_lex) // (2)
+ thd->lex->set_limit_rows_examined();
+
/* Create a tmp table if distinct or if the sort is too complicated */
if (need_tmp)
{
@@ -1749,15 +1857,10 @@ int JOIN::init_execution()
if (!(exec_tmp_table1=
create_tmp_table(thd, &tmp_table_param, all_fields,
- tmp_group,
- group_list ? 0 : select_distinct,
+ tmp_group, group_list ? 0 : select_distinct,
group_list && simple_group,
- select_options,
- tmp_rows_limit,
- (char *) "")))
- {
+ select_options, tmp_rows_limit, "")))
DBUG_RETURN(1);
- }
/*
We don't have to store rows in temp table that doesn't match HAVING if:
@@ -1782,6 +1885,7 @@ int JOIN::init_execution()
HA_POS_ERROR, HA_POS_ERROR, FALSE) ||
alloc_group_fields(this, group_list) ||
make_sum_func_list(all_fields, fields_list, 1) ||
+ prepare_sum_aggregators(sum_funcs, need_distinct) ||
setup_sum_funcs(thd, sum_funcs))
{
DBUG_RETURN(1);
@@ -1791,6 +1895,7 @@ int JOIN::init_execution()
else
{
if (make_sum_func_list(all_fields, fields_list, 0) ||
+ prepare_sum_aggregators(sum_funcs, need_distinct) ||
setup_sum_funcs(thd, sum_funcs))
{
DBUG_RETURN(1);
@@ -1876,7 +1981,8 @@ bool JOIN::setup_subquery_caches()
if (conds)
conds= conds->transform(&Item::expr_cache_insert_transformer,
(uchar*) thd);
- for (JOIN_TAB *tab= first_linear_tab(this, WITHOUT_CONST_TABLES);
+ JOIN_TAB *tab;
+ for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
tab; tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
{
if (tab->select_cond)
@@ -2013,6 +2119,7 @@ JOIN::reinit()
ULL(0));
first_record= 0;
+ cleaned= false;
if (exec_tmp_table1)
{
@@ -2038,7 +2145,8 @@ JOIN::reinit()
/* need to reset ref access state (see join_read_key) */
if (join_tab)
{
- for (JOIN_TAB *tab= first_linear_tab(this, WITH_CONST_TABLES); tab;
+ JOIN_TAB *tab;
+ for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITH_CONST_TABLES); tab;
tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
{
tab->ref.key_err= TRUE;
@@ -2123,6 +2231,7 @@ JOIN::exec()
{
List<Item> *columns_list= &fields_list;
int tmp_error;
+
DBUG_ENTER("JOIN::exec");
thd_proc_info(thd, "executing");
@@ -2147,8 +2256,8 @@ JOIN::exec()
(zero_result_cause?zero_result_cause:"No tables used"));
else
{
- if (result->send_fields(*columns_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ if (result->send_result_set_metadata(*columns_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
{
DBUG_VOID_RETURN;
}
@@ -2161,6 +2270,7 @@ JOIN::exec()
In this case JOIN::exec must check for JOIN::having_value, in the
same way it checks for JOIN::cond_value.
*/
+ DBUG_ASSERT(error == 0);
if (cond_value != Item::COND_FALSE &&
having_value != Item::COND_FALSE &&
(!conds || conds->val_int()) &&
@@ -2171,16 +2281,15 @@ JOIN::exec()
procedure->end_of_records()) : result->send_data(fields_list)> 0))
error= 1;
else
- {
- error= (int) result->send_eof();
send_records= ((select_options & OPTION_FOUND_ROWS) ? 1 :
thd->sent_row_count);
- }
}
else
- {
- error=(int) result->send_eof();
send_records= 0;
+ if (!error)
+ {
+ join_free(); // Unlock all cursors
+ error= (int) result->send_eof();
}
}
/* Single select (without union) always returns 0 or 1 row */
@@ -2297,7 +2406,12 @@ JOIN::exec()
List<Item> *curr_all_fields= &all_fields;
List<Item> *curr_fields_list= &fields_list;
TABLE *curr_tmp_table= 0;
- bool tmp_having_used_tables_updated= FALSE;
+ /*
+ curr_join->join_free() will call JOIN::cleanup(full=TRUE). It will not
+ be safe to call update_used_tables() after that.
+ */
+ if (curr_join->tmp_having)
+ curr_join->tmp_having->update_used_tables();
/*
Initialize examined rows here because the values from all join parts
@@ -2457,8 +2571,7 @@ JOIN::exec()
curr_join->select_distinct &&
!curr_join->group_list,
1, curr_join->select_options,
- HA_POS_ERROR,
- (char *) "")))
+ HA_POS_ERROR, "")))
DBUG_VOID_RETURN;
curr_join->exec_tmp_table2= exec_tmp_table2;
}
@@ -2495,7 +2608,9 @@ JOIN::exec()
}
}
if (curr_join->make_sum_func_list(*curr_all_fields, *curr_fields_list,
- 1, TRUE))
+ 1, TRUE) ||
+ prepare_sum_aggregators(curr_join->sum_funcs,
+ !curr_join->join_tab->is_using_agg_loose_index_scan()))
DBUG_VOID_RETURN;
curr_join->group_list= 0;
if (!curr_join->sort_and_group &&
@@ -2547,16 +2662,6 @@ JOIN::exec()
curr_join->select_distinct=0; /* Each row is unique */
- /*
- curr_join->join_free() will call JOIN::cleanup(full=TRUE). It will not
- be safe to call update_used_tables() after that.
- */
- if (curr_join->tmp_having)
- {
- curr_join->tmp_having->update_used_tables();
- tmp_having_used_tables_updated= TRUE;
- }
-
curr_join->join_free(); /* Free quick selects */
if (curr_join->select_distinct && ! curr_join->group_list)
@@ -2621,21 +2726,22 @@ JOIN::exec()
if (curr_join->make_sum_func_list(*curr_all_fields, *curr_fields_list,
1, TRUE) ||
+ prepare_sum_aggregators(curr_join->sum_funcs,
+ !curr_join->join_tab ||
+ !curr_join->join_tab->
+ is_using_agg_loose_index_scan()) ||
setup_sum_funcs(curr_join->thd, curr_join->sum_funcs) ||
thd->is_fatal_error)
DBUG_VOID_RETURN;
}
if (curr_join->group_list || curr_join->order)
{
- DBUG_PRINT("info",("Sorting for send_fields"));
+ DBUG_PRINT("info",("Sorting for send_result_set_metadata"));
thd_proc_info(thd, "Sorting result");
/* If we have already done the group, add HAVING to sorted table */
if (curr_join->tmp_having && ! curr_join->group_list &&
! curr_join->sort_and_group)
{
- // Some tables may have been const
- if (!tmp_having_used_tables_updated)
- curr_join->tmp_having->update_used_tables();
JOIN_TAB *curr_table= &curr_join->join_tab[curr_join->const_tables];
table_map used_tables= (curr_join->const_table_map |
curr_table->table->map);
@@ -2759,35 +2865,13 @@ JOIN::exec()
curr_join->fields= curr_fields_list;
curr_join->procedure= procedure;
- if (is_top_level_join() && thd->cursor && table_count != const_tables)
- {
- /*
- We are here if this is JOIN::exec for the last select of the main unit
- and the client requested to open a cursor.
- We check that not all tables are constant because this case is not
- handled by do_select() separately, and this case is not implemented
- for cursors yet.
- */
- DBUG_ASSERT(error == 0);
- /*
- curr_join is used only for reusable joins - that is,
- to perform SELECT for each outer row (like in subselects).
- This join is main, so we know for sure that curr_join == join.
- */
- DBUG_ASSERT(curr_join == this);
- /* Open cursor for the last join sweep */
- error= thd->cursor->open(this);
- }
- else
- {
- thd_proc_info(thd, "Sending data");
- DBUG_PRINT("info", ("%s", thd->proc_info));
- result->send_fields((procedure ? curr_join->procedure_fields_list :
- *curr_fields_list),
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
- error= do_select(curr_join, curr_fields_list, NULL, procedure);
- thd->limit_found_rows= curr_join->send_records;
- }
+ thd_proc_info(thd, "Sending data");
+ DBUG_PRINT("info", ("%s", thd->proc_info));
+ result->send_result_set_metadata((procedure ? curr_join->procedure_fields_list :
+ *curr_fields_list),
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
+ error= do_select(curr_join, curr_fields_list, NULL, procedure);
+ thd->limit_found_rows= curr_join->send_records;
/* Accumulate the counts from all join iterations of all join parts. */
thd->examined_row_count+= curr_join->examined_rows;
@@ -2826,8 +2910,9 @@ JOIN::destroy()
{
if (join_tab != tmp_join->join_tab)
{
- for (JOIN_TAB *tab= first_linear_tab(this, WITH_CONST_TABLES); tab;
- tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
+ JOIN_TAB *tab;
+ for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITH_CONST_TABLES);
+ tab; tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
{
tab->cleanup();
}
@@ -3008,16 +3093,6 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
join->exec();
- if (thd->cursor && thd->cursor->is_open())
- {
- /*
- A cursor was opened for the last sweep in exec().
- We are here only if this is mysql_select for top-level SELECT_LEX_UNIT
- and there were no error.
- */
- free_join= 0;
- }
-
if (thd->lex->describe & DESCRIBE_EXTENDED)
{
select_lex->where= join->conds_history;
@@ -3046,9 +3121,7 @@ static ha_rows get_quick_record_count(THD *thd, SQL_SELECT *select,
{
int error;
DBUG_ENTER("get_quick_record_count");
-#ifndef EMBEDDED_LIBRARY // Avoid compiler warning
uchar buff[STACK_BUFF_ALLOC];
-#endif
if (check_stack_overrun(thd, STACK_MIN_SIZE, buff))
DBUG_RETURN(0); // Fatal error flag is set
if (select)
@@ -3119,7 +3192,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
stat_ref=(JOIN_TAB**) join->thd->alloc(sizeof(JOIN_TAB*)*
(MAX_TABLES + table_count + 1));
stat_vector= stat_ref + MAX_TABLES;
- table_vector=(TABLE**) join->thd->alloc(sizeof(TABLE*)*(table_count*2));
+ table_vector=(TABLE**) join->thd->calloc(sizeof(TABLE*)*(table_count*2));
join->positions= new (join->thd->mem_root) POSITION[(table_count+1)];
/*
best_positions is ok to allocate with alloc() as we copy things to it with
@@ -3340,7 +3413,10 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
goto error; // Fatal error
}
else
+ {
found_const_table_map|= s->table->map;
+ s->table->pos_in_table_list->optimized_away= TRUE;
+ }
}
}
@@ -3467,13 +3543,22 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
The effect of this is that we don't do const substitution for
such tables.
*/
- if (eq_part.is_prefix(table->key_info[key].key_parts) &&
+ KEY *keyinfo= table->key_info + key;
+ uint key_parts= table->actual_n_key_parts(keyinfo);
+ if (eq_part.is_prefix(key_parts) &&
!table->fulltext_searched &&
(!embedding || (embedding->sj_on_expr && !embedding->embedding)))
{
- if (table->key_info[key].flags & HA_NOSAME)
+ key_map base_part, base_const_ref, base_eq_part;
+ base_part.set_prefix(keyinfo->key_parts);
+ base_const_ref= const_ref;
+ base_const_ref.intersect(base_part);
+ base_eq_part= eq_part;
+ base_eq_part.intersect(base_part);
+ if (table->actual_key_flags(keyinfo) & HA_NOSAME)
{
- if (const_ref == eq_part &&
+
+ if (base_const_ref == base_eq_part &&
!has_expensive_keyparts &&
!((outer_join & table->map) &&
(*s->on_expr_ref)->is_expensive()))
@@ -3499,7 +3584,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
else
found_ref|= refs; // Table is const if all refs are const
}
- else if (const_ref == eq_part)
+ else if (base_const_ref == base_eq_part)
s->const_keys.set_bit(key);
}
}
@@ -3507,6 +3592,9 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
}
} while (join->const_table_map & found_ref && ref_changed);
+ join->sort_by_table= get_sort_by_table(join->order, join->group_list,
+ join->select_lex->leaf_tables,
+ join->const_table_map);
/*
Update info on indexes that can be used for search lookups as
reading const tables may has added new sargable predicates.
@@ -3677,6 +3765,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
DBUG_RETURN(TRUE);
join->join_tab=stat;
+ join->top_join_tab_count= table_count;
join->map2table=stat_ref;
join->table= table_vector;
join->const_tables=const_count;
@@ -3737,6 +3826,8 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
if (join->choose_subquery_plan(all_table_map & ~join->const_table_map))
goto error;
+ DEBUG_SYNC(join->thd, "inside_make_join_statistics");
+
/* Generate an execution plan from the found optimal join order. */
DBUG_RETURN(join->thd->killed || get_best_combination(join));
@@ -3898,8 +3989,10 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end,
new_fields->null_rejecting);
}
else if (old->eq_func && new_fields->eq_func &&
- ((old->val->const_item() && old->val->is_null()) ||
- new_fields->val->is_null()))
+ ((old->val->const_item() && !old->val->is_expensive() &&
+ old->val->is_null()) ||
+ (!new_fields->val->is_expensive() &&
+ new_fields->val->is_null())))
{
/* field = expression OR field IS NULL */
old->level= and_level;
@@ -3913,7 +4006,8 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end,
Remember the NOT NULL value unless the value does not depend
on other tables.
*/
- if (!old->val->used_tables() && old->val->is_null())
+ if (!old->val->used_tables() && !old->val->is_expensive() &&
+ old->val->is_null())
old->val= new_fields->val;
}
else
@@ -4101,14 +4195,14 @@ add_key_field(JOIN *join,
(*sargables)->arg_value= value;
(*sargables)->num_values= num_values;
}
+ if (!eq_func) // eq_func is NEVER true when num_values > 1
+ return;
/*
We can't use indexes when comparing a string index to a
number or two strings if the effective collation
of the operation differ from the field collation.
*/
- if (!eq_func)
- return;
if (field->cmp_type() == STRING_RESULT)
{
@@ -4565,7 +4659,8 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array, KEY_FIELD *key_field)
if (form->key_info[key].flags & (HA_FULLTEXT | HA_SPATIAL))
continue; // ToDo: ft-keys in non-ft queries. SerG
- uint key_parts= (uint) form->key_info[key].key_parts;
+ KEY *keyinfo= form->key_info+key;
+ uint key_parts= form->actual_n_key_parts(keyinfo);
for (uint part=0 ; part < key_parts ; part++)
{
if (field->eq(form->key_info[key].key_part[part].field))
@@ -4612,19 +4707,19 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array,
cond_func=(Item_func_match *)cond;
else if (func->arg_count == 2)
{
- Item *arg0= func->arguments()[0],
- *arg1= func->arguments()[1];
+ Item *arg0=(Item *)(func->arguments()[0]),
+ *arg1=(Item *)(func->arguments()[1]);
if (arg1->const_item() && arg1->cols() == 1 &&
arg0->type() == Item::FUNC_ITEM &&
((Item_func *) arg0)->functype() == Item_func::FT_FUNC &&
((functype == Item_func::GE_FUNC && arg1->val_real() > 0) ||
- (functype == Item_func::GT_FUNC && arg1->val_real() >= 0)))
+ (functype == Item_func::GT_FUNC && arg1->val_real() >=0)))
cond_func= (Item_func_match *) arg0;
else if (arg0->const_item() && arg0->cols() == 1 &&
arg1->type() == Item::FUNC_ITEM &&
((Item_func *) arg1)->functype() == Item_func::FT_FUNC &&
((functype == Item_func::LE_FUNC && arg0->val_real() > 0) ||
- (functype == Item_func::LT_FUNC && arg0->val_real() >= 0)))
+ (functype == Item_func::LT_FUNC && arg0->val_real() >=0)))
cond_func= (Item_func_match *) arg1;
}
}
@@ -4997,7 +5092,7 @@ static bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse,
save_pos++;
}
i= (uint) (save_pos-(KEYUSE*) keyuse->buffer);
- VOID(set_dynamic(keyuse,(uchar*) &key_end,i));
+ (void) set_dynamic(keyuse,(uchar*) &key_end,i);
keyuse->elements= i;
return FALSE;
@@ -5051,6 +5146,107 @@ 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.
+
+
+ Check if the query is a subject to AGGFN(DISTINCT) using loose index scan
+ (QUICK_GROUP_MIN_MAX_SELECT).
+ Optionally (if out_args is supplied) will push the arguments of
+ AGGFN(DISTINCT) to the list
+
+ Check for every COUNT(DISTINCT), AVG(DISTINCT) or
+ SUM(DISTINCT). These can be resolved by Loose Index Scan as long
+ as all the aggregate distinct functions refer to the same
+ fields. Thus:
+
+ SELECT AGGFN(DISTINCT a, b), AGGFN(DISTINCT b, a)... => can use LIS
+ SELECT AGGFN(DISTINCT a), AGGFN(DISTINCT a) ... => can use LIS
+ SELECT AGGFN(DISTINCT a, b), AGGFN(DISTINCT a) ... => cannot use LIS
+ SELECT AGGFN(DISTINCT a), AGGFN(DISTINCT b) ... => cannot use LIS
+ etc.
+
+ @param join the join to check
+ @param[out] out_args Collect the arguments of the aggregate functions
+ to a list. We don't worry about duplicates as
+ these will be sorted out later in
+ get_best_group_min_max.
+
+ @return does the query qualify for indexed AGGFN(DISTINCT)
+ @retval true it does
+ @retval false AGGFN(DISTINCT) must apply distinct in it.
+*/
+
+bool
+is_indexed_agg_distinct(JOIN *join, List<Item_field> *out_args)
+{
+ Item_sum **sum_item_ptr;
+ bool result= false;
+ Field_map first_aggdistinct_fields;
+
+ if (join->table_count != 1 || /* reference more than 1 table */
+ join->select_distinct || /* or a DISTINCT */
+ join->select_lex->olap == ROLLUP_TYPE) /* Check (B3) for ROLLUP */
+ return false;
+
+ if (join->make_sum_func_list(join->all_fields, join->fields_list, true))
+ return false;
+
+ for (sum_item_ptr= join->sum_funcs; *sum_item_ptr; sum_item_ptr++)
+ {
+ Item_sum *sum_item= *sum_item_ptr;
+ Field_map cur_aggdistinct_fields;
+ Item *expr;
+ /* aggregate is not AGGFN(DISTINCT) or more than 1 argument to it */
+ switch (sum_item->sum_func())
+ {
+ case Item_sum::MIN_FUNC:
+ case Item_sum::MAX_FUNC:
+ continue;
+ case Item_sum::COUNT_DISTINCT_FUNC:
+ break;
+ case Item_sum::AVG_DISTINCT_FUNC:
+ case Item_sum::SUM_DISTINCT_FUNC:
+ if (sum_item->get_arg_count() == 1)
+ break;
+ /* fall through */
+ default: return false;
+ }
+ /*
+ We arrive here for every COUNT(DISTINCT),AVG(DISTINCT) or SUM(DISTINCT).
+ Collect the arguments of the aggregate functions to a list.
+ We don't worry about duplicates as these will be sorted out later in
+ get_best_group_min_max
+ */
+ for (uint i= 0; i < sum_item->get_arg_count(); i++)
+ {
+ expr= sum_item->get_arg(i);
+ /* The AGGFN(DISTINCT) arg is not an attribute? */
+ if (expr->real_item()->type() != Item::FIELD_ITEM)
+ return false;
+
+ Item_field* item= static_cast<Item_field*>(expr->real_item());
+ if (out_args)
+ out_args->push_back(item);
+
+ cur_aggdistinct_fields.set_bit(item->field->field_index);
+ result= true;
+ }
+ /*
+ If there are multiple aggregate functions, make sure that they all
+ refer to exactly the same set of columns.
+ */
+ if (first_aggdistinct_fields.is_clear_all())
+ first_aggdistinct_fields.merge(cur_aggdistinct_fields);
+ else if (first_aggdistinct_fields != cur_aggdistinct_fields)
+ return false;
+ }
+
+ return result;
+}
+
+
+/**
Discover the indexes that can be used for GROUP BY or DISTINCT queries.
If the query has a GROUP BY clause, find all indexes that contain all
@@ -5092,6 +5288,10 @@ add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab)
item->walk(&Item::collect_item_field_processor, 0,
(uchar*) &indexed_fields);
}
+ else if (is_indexed_agg_distinct(join, &indexed_fields))
+ {
+ join->sort_and_group= 1;
+ }
else
return;
@@ -5245,6 +5445,8 @@ best_access_path(JOIN *join,
for (keyuse=s->keyuse ; keyuse->table == table ;)
{
KEY *keyinfo;
+ ulong key_flags;
+ uint key_parts;
key_part_map found_part= 0;
table_map found_ref= 0;
uint key= keyuse->key;
@@ -5271,6 +5473,8 @@ best_access_path(JOIN *join,
}
keyinfo= table->key_info+key;
+ key_parts= table->actual_n_key_parts(keyinfo);
+ key_flags= table->actual_key_flags(keyinfo);
/* Calculate how many key segments of the current key we can use */
start_key= keyuse;
@@ -5351,11 +5555,12 @@ 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,keyinfo->key_parts) &&
+ if (found_part == PREV_BITS(uint, key_parts) &&
!ref_or_null_part)
{ /* use eq key */
max_key_part= (uint) ~0;
- if ((keyinfo->flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME)
+ if ((key_flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME ||
+ test(key_flags & HA_EXT_NOSAME))
{
tmp = prev_record_reads(join->positions, idx, found_ref);
records=1.0;
@@ -5391,7 +5596,8 @@ best_access_path(JOIN *join,
}
else
{
- if (!(records=keyinfo->rec_per_key[keyinfo->key_parts-1]))
+ uint key_parts= table->actual_n_key_parts(keyinfo);
+ if (!(records=keyinfo->rec_per_key[key_parts-1]))
{ /* Prefer longer keys */
records=
((double) s->records / (double) rec *
@@ -5412,7 +5618,8 @@ best_access_path(JOIN *join,
in ReuseRangeEstimateForRef-3.
*/
if (table->quick_keys.is_set(key) &&
- (const_part & ((1 << table->quick_key_parts[key])-1)) ==
+ (const_part &
+ (((key_part_map)1 << table->quick_key_parts[key])-1)) ==
(((key_part_map)1 << table->quick_key_parts[key])-1) &&
table->quick_n_ranges[key] == 1 &&
records > (double) table->quick_rows[key])
@@ -5576,7 +5783,8 @@ best_access_path(JOIN *join,
*/
if (table->quick_keys.is_set(key) &&
table->quick_key_parts[key] <= max_key_part &&
- const_part & (1 << table->quick_key_parts[key]) &&
+ const_part &
+ ((key_part_map)1 << table->quick_key_parts[key]) &&
table->quick_n_ranges[key] == 1 + test(ref_or_null_part &
const_part) &&
records > (double) table->quick_rows[key])
@@ -5598,7 +5806,6 @@ best_access_path(JOIN *join,
tmp= best_time; // Do nothing
}
- DBUG_ASSERT(tmp > 0 || record_count == 0);
tmp += s->startup_cost;
loose_scan_opt.check_ref_access_part2(key, start_key, records, tmp);
} /* not ft_key */
@@ -6393,7 +6600,8 @@ greedy_search(JOIN *join,
uint size_remain; // cardinality of remaining_tables
POSITION best_pos;
JOIN_TAB *best_table; // the next plan node to be added to the curr QEP
- uint n_tables; // ==join->tables or # tables in the sj-mat nest we're optimizing
+ // ==join->tables or # tables in the sj-mat nest we're optimizing
+ uint n_tables __attribute__((unused));
DBUG_ENTER("greedy_search");
@@ -6447,7 +6655,7 @@ greedy_search(JOIN *join,
on exit.
*/
bool is_interleave_error __attribute__((unused))=
- check_interleaving_with_nj(best_table);
+ check_interleaving_with_nj (best_table);
/* This has been already checked by best_extension_by_limited_search */
DBUG_ASSERT(!is_interleave_error);
@@ -6623,6 +6831,32 @@ void JOIN::get_prefix_cost_and_fanout(uint n_tables,
/**
+ Estimate the number of rows that query execution will read.
+
+ @todo This is a very pessimistic upper bound. Use join selectivity
+ when available to produce a more realistic number.
+*/
+
+double JOIN::get_examined_rows()
+{
+ ha_rows examined_rows;
+ double prev_fanout= 1;
+ JOIN_TAB *tab= first_breadth_first_tab(this, WALK_OPTIMIZATION_TABS);
+ JOIN_TAB *prev_tab= tab;
+
+ examined_rows= tab->get_examined_rows();
+
+ while ((tab= next_breadth_first_tab(this, WALK_OPTIMIZATION_TABS, tab)))
+ {
+ prev_fanout *= prev_tab->records_read;
+ examined_rows+= (ha_rows) (tab->get_examined_rows() * prev_fanout);
+ prev_tab= tab;
+ }
+ return examined_rows;
+}
+
+
+/**
Find a good, possibly optimal, query execution plan (QEP) by a possibly
exhaustive search.
@@ -7227,23 +7461,30 @@ prev_record_reads(POSITION *positions, uint idx, table_map found_ref)
Enumerate join tabs in breadth-first fashion, including const tables.
*/
-JOIN_TAB *first_breadth_first_tab(JOIN *join)
+JOIN_TAB *first_breadth_first_tab(JOIN *join, enum enum_exec_or_opt tabs_kind)
{
- return join->join_tab; /* There's always one (i.e. first) table */
+ /* There's always one (i.e. first) table */
+ return (tabs_kind == WALK_EXECUTION_TABS)? join->join_tab:
+ join->table_access_tabs;
}
-JOIN_TAB *next_breadth_first_tab(JOIN *join, JOIN_TAB *tab)
+JOIN_TAB *next_breadth_first_tab(JOIN *join, enum enum_exec_or_opt tabs_kind,
+ JOIN_TAB *tab)
{
+ JOIN_TAB* const first_top_tab= first_breadth_first_tab(join, tabs_kind);
+ const uint n_top_tabs_count= (tabs_kind == WALK_EXECUTION_TABS)?
+ join->top_join_tab_count:
+ join->top_table_access_tabs_count;
if (!tab->bush_root_tab)
{
/* We're at top level. Get the next top-level tab */
tab++;
- if (tab < join->join_tab + join->top_join_tab_count)
+ if (tab < first_top_tab + n_top_tabs_count)
return tab;
/* No more top-level tabs. Switch to enumerating SJM nest children */
- tab= join->join_tab;
+ tab= first_top_tab;
}
else
{
@@ -7267,7 +7508,7 @@ JOIN_TAB *next_breadth_first_tab(JOIN *join, JOIN_TAB *tab)
Ok, "tab" points to a top-level table, and we need to find the next SJM
nest and enter it.
*/
- for (; tab < join->join_tab + join->top_join_tab_count; tab++)
+ for (; tab < first_top_tab + n_top_tabs_count; tab++)
{
if (tab->bush_children)
return tab->bush_children->start;
@@ -7276,10 +7517,10 @@ JOIN_TAB *next_breadth_first_tab(JOIN *join, JOIN_TAB *tab)
}
-JOIN_TAB *first_top_level_tab(JOIN *join, enum enum_with_const_tables with_const)
+JOIN_TAB *first_top_level_tab(JOIN *join, enum enum_with_const_tables const_tbls)
{
JOIN_TAB *tab= join->join_tab;
- if (with_const == WITH_CONST_TABLES)
+ if (const_tbls == WITHOUT_CONST_TABLES)
{
if (join->const_tables == join->table_count)
return NULL;
@@ -7291,21 +7532,31 @@ JOIN_TAB *first_top_level_tab(JOIN *join, enum enum_with_const_tables with_const
JOIN_TAB *next_top_level_tab(JOIN *join, JOIN_TAB *tab)
{
- tab= next_breadth_first_tab(join, tab);
+ tab= next_breadth_first_tab(join, WALK_EXECUTION_TABS, tab);
if (tab && tab->bush_root_tab)
tab= NULL;
return tab;
}
-JOIN_TAB *first_linear_tab(JOIN *join, enum enum_with_const_tables const_tbls)
+JOIN_TAB *first_linear_tab(JOIN *join,
+ enum enum_with_bush_roots include_bush_roots,
+ enum enum_with_const_tables const_tbls)
{
JOIN_TAB *first= join->join_tab;
if (const_tbls == WITHOUT_CONST_TABLES)
first+= join->const_tables;
- if (first < join->join_tab + join->top_join_tab_count)
- return first;
- return NULL; /* All tables were const tables */
+
+ if (first >= join->join_tab + join->top_join_tab_count)
+ return NULL; /* All are const tables */
+
+ if (first->bush_children && include_bush_roots == WITHOUT_BUSH_ROOTS)
+ {
+ /* This JOIN_TAB is a SJM nest; Start from first table in nest */
+ return first->bush_children->start;
+ }
+
+ return first;
}
@@ -7592,6 +7843,12 @@ get_best_combination(JOIN *join)
join->top_join_tab_count= join->join_tab_ranges.head()->end -
join->join_tab_ranges.head()->start;
+ /*
+ Save pointers to select join tabs for SHOW EXPLAIN
+ */
+ join->table_access_tabs= join->join_tab;
+ join->top_table_access_tabs_count= join->top_join_tab_count;
+
update_depend_map(join);
DBUG_RETURN(0);
}
@@ -7663,6 +7920,7 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab,
key_parts)))
DBUG_RETURN(TRUE);
keyinfo->usable_key_parts= keyinfo->key_parts = key_parts;
+ keyinfo->ext_key_parts= keyinfo->key_parts;
keyinfo->key_part= key_part_info;
keyinfo->key_length=0;
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
@@ -7707,6 +7965,10 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab,
keyuse++;
} while (keyuse->table == table && keyuse->is_for_hash_join());
+ keyinfo->ext_key_parts= keyinfo->key_parts;
+ keyinfo->ext_key_flags= keyinfo->flags;
+ keyinfo->ext_key_part_map= 0;
+
join_tab->hj_key= keyinfo;
DBUG_RETURN(FALSE);
@@ -7867,7 +8129,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
j->ref.items[i]=keyuse->val; // Save for cond removal
j->ref.cond_guards[i]= keyuse->cond_guard;
if (keyuse->null_rejecting)
- j->ref.null_rejecting |= 1 << i;
+ j->ref.null_rejecting|= (key_part_map)1 << i;
keyuse_uses_no_tables= keyuse_uses_no_tables && !keyuse->used_tables;
if (!keyuse->val->used_tables() && !thd->lex->describe)
{ // Compare against constant
@@ -7903,10 +8165,15 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
*ref_key=0; // end_marker
if (j->type == JT_FT)
DBUG_RETURN(0);
+ ulong key_flags= j->table->actual_key_flags(keyinfo);
if (j->type == JT_CONST)
j->table->const_table= 1;
- else if (((keyinfo->flags & (HA_NOSAME | HA_NULL_PART_KEY)) != HA_NOSAME) ||
- keyparts != keyinfo->key_parts || null_ref_key)
+ else if (!((keyparts == keyinfo->key_parts &&
+ ((key_flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME)) ||
+ (keyparts > keyinfo->key_parts && // true only for extended keys
+ test(key_flags & HA_EXT_NOSAME) &&
+ keyparts == keyinfo->ext_key_parts)) ||
+ null_ref_key)
{
/* Must read with repeat */
j->type= null_ref_key ? JT_REF_OR_NULL : JT_REF;
@@ -7926,6 +8193,9 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
}
else
j->type=JT_EQ_REF;
+
+ j->read_record.unlock_row= (j->type == JT_EQ_REF)?
+ join_read_key_unlock_row : rr_unlock_row;
DBUG_RETURN(0);
}
@@ -7958,6 +8228,7 @@ get_store_key(THD *thd, KEYUSE *keyuse, table_map used_tables,
key_part->length,
((Item_field*) keyuse->val->real_item())->field,
keyuse->val->real_item()->full_name());
+
return new store_key_item(thd,
key_part->field,
key_buff + maybe_null,
@@ -7967,37 +8238,6 @@ get_store_key(THD *thd, KEYUSE *keyuse, table_map used_tables,
}
/**
- This function is only called for const items on fields which are keys.
-
- @return
- returns 1 if there was some conversion made when the field was stored.
-*/
-
-bool
-store_val_in_field(Field *field, Item *item, enum_check_fields check_flag)
-{
- bool error;
- TABLE *table= field->table;
- THD *thd= table->in_use;
- ha_rows cuted_fields=thd->cuted_fields;
- my_bitmap_map *old_map= dbug_tmp_use_all_columns(table,
- table->write_set);
-
- /*
- we should restore old value of count_cuted_fields because
- store_val_in_field can be called from mysql_insert
- with select_insert, which make count_cuted_fields= 1
- */
- enum_check_fields old_count_cuted_fields= thd->count_cuted_fields;
- thd->count_cuted_fields= check_flag;
- error= item->save_in_field(field, 1);
- thd->count_cuted_fields= old_count_cuted_fields;
- dbug_tmp_restore_column_map(table->write_set, old_map);
- return error || cuted_fields != thd->cuted_fields;
-}
-
-
-/**
@details Initialize a JOIN as a query execution plan
that accesses a single table via a table scan.
@@ -8066,36 +8306,17 @@ JOIN::make_simple_join(JOIN *parent, TABLE *temp_table)
row_limit= unit->select_limit_cnt;
do_send_rows= row_limit ? 1 : 0;
- join_tab->use_join_cache= FALSE;
- join_tab->cache=0; /* No caching */
+ bzero(join_tab, sizeof(JOIN_TAB));
join_tab->table=temp_table;
- join_tab->cache_select= 0;
- join_tab->select=0;
- join_tab->select_cond= 0; // Avoid valgrind warning
join_tab->set_select_cond(NULL, __LINE__);
- join_tab->quick=0;
join_tab->type= JT_ALL; /* Map through all records */
join_tab->keys.init();
join_tab->keys.set_all(); /* test everything in quick */
- join_tab->info=0;
- join_tab->on_expr_ref=0;
- join_tab->last_inner= 0;
- join_tab->first_unmatched= 0;
join_tab->ref.key = -1;
join_tab->shortcut_for_distinct= false;
join_tab->read_first_record= join_init_read_record;
- join_tab->preread_init_done= FALSE;
join_tab->join= this;
join_tab->ref.key_parts= 0;
- join_tab->keep_current_rowid= FALSE;
- join_tab->flush_weedout_table= join_tab->check_weed_out_table= NULL;
- join_tab->do_firstmatch= NULL;
- join_tab->loosescan_match_tab= NULL;
- join_tab->emb_sj_nest= NULL;
- join_tab->pre_idx_push_select_cond= NULL;
- join_tab->bush_root_tab= NULL;
- join_tab->bush_children= NULL;
- join_tab->last_leaf_in_bush= FALSE;
bzero((char*) &join_tab->read_record,sizeof(join_tab->read_record));
temp_table->status=0;
temp_table->null_row=0;
@@ -8175,9 +8396,10 @@ inline void add_cond_and_fix(THD *thd, Item **e1, Item *e2)
static void add_not_null_conds(JOIN *join)
{
+ JOIN_TAB *tab;
DBUG_ENTER("add_not_null_conds");
- for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_CONST_TABLES);
+ for (tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
tab;
tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS))
{
@@ -8186,19 +8408,18 @@ static void add_not_null_conds(JOIN *join)
{
for (uint keypart= 0; keypart < tab->ref.key_parts; keypart++)
{
- if (tab->ref.null_rejecting & (1 << keypart))
+ if (tab->ref.null_rejecting & ((key_part_map)1 << keypart))
{
Item *item= tab->ref.items[keypart];
Item *notnull;
Item *real= item->real_item();
- if (real->basic_const_item())
+ if (real->const_item() && real->type() != Item::FIELD_ITEM &&
+ !real->is_expensive())
{
/*
It could be constant instead of field after constant
propagation.
*/
- DBUG_ASSERT(real->is_expensive() || // prevent early expensive eval
- !real->is_null()); // NULLs are not propagated
continue;
}
DBUG_ASSERT(real->type() == Item::FIELD_ITEM);
@@ -8349,7 +8570,7 @@ make_outerjoin_info(JOIN *join)
tab->table->pos_in_table_list being set.
*/
JOIN_TAB *tab;
- for (tab= first_linear_tab(join, WITHOUT_CONST_TABLES);
+ for (tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
tab;
tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS))
{
@@ -8361,7 +8582,7 @@ make_outerjoin_info(JOIN *join)
}
}
- for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_CONST_TABLES); tab;
+ for (JOIN_TAB *tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES); tab;
tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS))
{
TABLE *table= tab->table;
@@ -8665,7 +8886,11 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
if (tab->table)
{
tab->table->file->pushed_cond= NULL;
- if (thd->variables.engine_condition_pushdown && !first_inner_tab)
+ if (((thd->variables.optimizer_switch &
+ OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN) ||
+ (tab->table->file->ha_table_flags() &
+ HA_MUST_USE_TABLE_CONDITION_PUSHDOWN)) &&
+ !first_inner_tab)
{
COND *push_cond=
make_cond_for_table(thd, tmp, current_map, current_map,
@@ -8695,11 +8920,12 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
{
/* Use quick key read if it's a constant and it's not used
with key reading */
- if ((tab->needed_reg.is_clear_all() && tab->type != JT_EQ_REF
- && tab->type != JT_FT &&
- ((tab->type != JT_REF && tab->type != JT_CONST) ||
- (uint) tab->ref.key == tab->quick->index)) || is_hj)
- {
+ if ((tab->needed_reg.is_clear_all() && tab->type != JT_EQ_REF &&
+ tab->type != JT_FT &&
+ ((tab->type != JT_CONST && tab->type != JT_REF) ||
+ (uint) tab->ref.key == tab->quick->index)) || is_hj)
+ {
+ DBUG_ASSERT(tab->quick->is_valid());
sel->quick=tab->quick; // Use value from get_quick_...
sel->quick_keys.clear_all();
sel->needed_reg.clear_all();
@@ -9151,7 +9377,7 @@ bool generate_derived_keys(DYNAMIC_ARRAY *keyuse_array)
void JOIN::drop_unused_derived_keys()
{
JOIN_TAB *tab;
- for (tab= first_linear_tab(this, WITHOUT_CONST_TABLES);
+ for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
tab;
tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
{
@@ -9450,7 +9676,7 @@ end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
if (table->file->is_fatal_error(error, HA_CHECK_DUP) &&
create_internal_tmp_table_from_heap(thd, table,
sjm->sjm_table_param.start_recinfo,
- &sjm->sjm_table_param.recinfo, error, 1))
+ &sjm->sjm_table_param.recinfo, error, 1, NULL))
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
}
}
@@ -9852,7 +10078,7 @@ void check_join_cache_usage_for_tables(JOIN *join, ulonglong options,
JOIN_TAB *tab;
JOIN_TAB *prev_tab;
- for (tab= first_linear_tab(join, WITHOUT_CONST_TABLES);
+ for (tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
tab;
tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS))
{
@@ -9860,7 +10086,7 @@ void check_join_cache_usage_for_tables(JOIN *join, ulonglong options,
}
uint idx= join->const_tables;
- for (tab= first_linear_tab(join, WITHOUT_CONST_TABLES);
+ for (tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
tab;
tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS))
{
@@ -9954,7 +10180,8 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
tab->partial_join_cardinality= 1;
JOIN_TAB *prev_tab= NULL;
- for (tab= first_linear_tab(join, WITHOUT_CONST_TABLES), i= join->const_tables;
+ i= join->const_tables;
+ for (tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
tab;
prev_tab=tab, tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS))
{
@@ -9979,7 +10206,7 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
check_join_cache_usage_for_tables(join, options, no_jbuf_after);
JOIN_TAB *first_tab;
- for (tab= first_tab= first_linear_tab(join, WITHOUT_CONST_TABLES);
+ for (tab= first_tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
tab;
tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS))
{
@@ -10033,10 +10260,7 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
join_read_system :join_read_const;
if (table->covering_keys.is_set(tab->ref.key) &&
!table->no_keyread)
- {
- table->key_read=1;
- table->file->extra(HA_EXTRA_KEYREAD);
- }
+ table->enable_keyread();
else if ((!jcl || jcl > 4) && !tab->ref.is_access_triggered())
push_index_cond(tab, tab->ref.key);
break;
@@ -10045,10 +10269,7 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
/* fall through */
if (table->covering_keys.is_set(tab->ref.key) &&
!table->no_keyread)
- {
- table->key_read=1;
- table->file->extra(HA_EXTRA_KEYREAD);
- }
+ table->enable_keyread();
else if ((!jcl || jcl > 4) && !tab->ref.is_access_triggered())
push_index_cond(tab, tab->ref.key);
break;
@@ -10132,20 +10353,25 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
else if (!table->covering_keys.is_clear_all() &&
!(tab->select && tab->select->quick))
{ // Only read index tree
+ if (tab->loosescan_match_tab)
+ tab->index= tab->loosescan_key;
+ else
+ {
#ifdef BAD_OPTIMIZATION
- /*
- It has turned out that the below change, while speeding things
- up for disk-bound loads, slows them down for cases when the data
- is in disk cache (see BUG#35850):
- See bug #26447: "Using the clustered index for a table scan
- is always faster than using a secondary index".
- */
- if (table->s->primary_key != MAX_KEY &&
- table->file->primary_key_is_clustered())
- tab->index= table->s->primary_key;
- else
+ /*
+ It has turned out that the below change, while speeding things
+ up for disk-bound loads, slows them down for cases when the data
+ is in disk cache (see BUG#35850):
+ See bug #26447: "Using the clustered index for a table scan
+ is always faster than using a secondary index".
+ */
+ if (table->s->primary_key != MAX_KEY &&
+ table->file->primary_key_is_clustered())
+ tab->index= table->s->primary_key;
+ else
#endif
- tab->index=find_shortest_key(table, & table->covering_keys);
+ tab->index=find_shortest_key(table, & table->covering_keys);
+ }
tab->read_first_record= join_read_first;
/* Read with index_first / index_next */
tab->type= tab->type == JT_ALL ? JT_NEXT : JT_HASH_NEXT;
@@ -10186,16 +10412,34 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
join->group_list ?
join->join_tab+join->const_tables :
join->get_sort_by_join_tab();
- if (sort_by_tab)
+ /*
+ It could be that sort_by_tab==NULL, and the plan is to use filesort()
+ on the first table.
+ */
+ if (join->order)
+ {
+ join->simple_order= 0;
+ join->need_tmp= 1;
+ }
+
+ if (join->group && !join->group_optimized_away)
+ {
+ join->need_tmp= 1;
+ join->simple_group= 0;
+ }
+
+ if (sort_by_tab)
{
join->need_tmp= 1;
join->simple_order= join->simple_group= 0;
- if (sort_by_tab->type == JT_NEXT)
+ if (sort_by_tab->type == JT_NEXT &&
+ !sort_by_tab->table->covering_keys.is_set(sort_by_tab->index))
{
sort_by_tab->type= JT_ALL;
sort_by_tab->read_first_record= join_init_read_record;
}
- else if (sort_by_tab->type == JT_HASH_NEXT)
+ else if (sort_by_tab->type == JT_HASH_NEXT &&
+ !sort_by_tab->table->covering_keys.is_set(sort_by_tab->index))
{
sort_by_tab->type= JT_HASH;
sort_by_tab->read_first_record= join_init_read_record;
@@ -10340,6 +10584,51 @@ double JOIN_TAB::scan_time()
return res;
}
+
+/**
+ Estimate the number of rows that a an access method will read from a table.
+
+ @todo: why not use JOIN_TAB::found_records
+*/
+
+ha_rows JOIN_TAB::get_examined_rows()
+{
+ ha_rows examined_rows;
+
+ if (select && select->quick)
+ examined_rows= select->quick->records;
+ else if (type == JT_NEXT || type == JT_ALL ||
+ type == JT_HASH || type ==JT_HASH_NEXT)
+ {
+ if (limit)
+ {
+ /*
+ @todo This estimate is wrong, a LIMIT query may examine much more rows
+ than the LIMIT itself.
+ */
+ examined_rows= limit;
+ }
+ else
+ {
+ if (table->is_filled_at_execution())
+ examined_rows= records;
+ else
+ {
+ /*
+ handler->info(HA_STATUS_VARIABLE) has been called in
+ make_join_statistics()
+ */
+ examined_rows= table->file->stats.records;
+ }
+ }
+ }
+ else
+ examined_rows= (ha_rows) records_read;
+
+ return examined_rows;
+}
+
+
/**
Initialize the join_tab before reading.
Currently only derived table/view materialization is done here.
@@ -10509,7 +10798,7 @@ void JOIN::join_free()
Optimization: if not EXPLAIN and we are done with the JOIN,
free all tables.
*/
- bool full= !(select_lex->uncacheable);
+ bool full= !(select_lex->uncacheable) && !(thd->lex->describe);
bool can_unlock= full;
DBUG_ENTER("JOIN::join_free");
@@ -10540,7 +10829,7 @@ void JOIN::join_free()
We are not using tables anymore
Unlock all tables. We may be in an INSERT .... SELECT statement.
*/
- if (can_unlock && lock && thd->lock &&
+ if (can_unlock && lock && thd->lock && ! thd->locked_tables_mode &&
!(select_options & SELECT_NO_UNLOCK) &&
!select_lex->subquery_in_having &&
(select_lex == (thd->lex->unit.fake_select_lex ?
@@ -10584,28 +10873,72 @@ void JOIN::cleanup(bool full)
*/
if (table_count > const_tables) // Test for not-const tables
{
- free_io_cache(table[const_tables]);
- filesort_free_buffers(table[const_tables],full);
+ JOIN_TAB *first_tab= first_top_level_tab(this, WITHOUT_CONST_TABLES);
+ if (first_tab->table)
+ {
+ free_io_cache(first_tab->table);
+ filesort_free_buffers(first_tab->table, full);
+ }
}
-
if (full)
{
- for (tab= first_linear_tab(this, WITH_CONST_TABLES); tab;
- tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
+ JOIN_TAB *sort_tab= first_linear_tab(this, WITH_BUSH_ROOTS,
+ WITHOUT_CONST_TABLES);
+ if (pre_sort_join_tab)
{
- tab->cleanup();
+ if (sort_tab && sort_tab->select == pre_sort_join_tab->select)
+ {
+ pre_sort_join_tab->select= NULL;
+ }
+ else
+ clean_pre_sort_join_tab();
}
- table= 0;
+ /*
+ Call cleanup() on join tabs used by the join optimization
+ (join->join_tab may now be pointing to result of make_simple_join
+ reading from the temporary table)
+
+ We also need to check table_count to handle various degenerate joins
+ w/o tables: they don't have some members initialized and
+ WALK_OPTIMIZATION_TABS may not work correctly for them.
+ */
+ enum enum_exec_or_opt tabs_kind;
+ if (first_breadth_first_tab(this, WALK_OPTIMIZATION_TABS))
+ tabs_kind= WALK_OPTIMIZATION_TABS;
+ else
+ tabs_kind= WALK_EXECUTION_TABS;
+ if (table_count)
+ {
+ for (tab= first_breadth_first_tab(this, tabs_kind); tab;
+ tab= next_breadth_first_tab(this, tabs_kind, tab))
+ {
+ tab->cleanup();
+ }
+
+ if (tabs_kind == WALK_OPTIMIZATION_TABS &&
+ first_breadth_first_tab(this, WALK_OPTIMIZATION_TABS) !=
+ first_breadth_first_tab(this, WALK_EXECUTION_TABS))
+ {
+ JOIN_TAB *jt= first_breadth_first_tab(this, WALK_EXECUTION_TABS);
+ /* We've walked optimization tabs. do execution ones too */
+ if (jt)
+ jt->cleanup();
+ }
+ }
+ cleaned= true;
+
}
else
{
- for (tab= first_linear_tab(this, WITH_CONST_TABLES); tab;
+ for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITH_CONST_TABLES); tab;
tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
{
if (tab->table)
{
- DBUG_PRINT("info", ("close index: %s.%s", tab->table->s->db.str,
- tab->table->s->table_name.str));
+ DBUG_PRINT("info", ("close index: %s.%s alias: %s",
+ tab->table->s->db.str,
+ tab->table->s->table_name.str,
+ tab->table->alias.c_ptr()));
tab->table->file->ha_index_or_rnd_end();
}
}
@@ -10645,6 +10978,22 @@ void JOIN::cleanup(bool full)
tmp_join->tmp_table_param.save_copy_field= 0;
}
tmp_table_param.cleanup();
+
+ if (!join_tab)
+ {
+ List_iterator<TABLE_LIST> li(*join_list);
+ TABLE_LIST *table_ref;
+ while ((table_ref= li++))
+ {
+ if (table_ref->table &&
+ table_ref->jtbm_subselect &&
+ table_ref->jtbm_subselect->is_jtbm_const_tab)
+ {
+ free_tmp_table(thd, table_ref->table);
+ table_ref->table= NULL;
+ }
+ }
+ }
}
DBUG_VOID_RETURN;
}
@@ -10743,7 +11092,9 @@ only_eq_ref_tables(JOIN *join,ORDER *order,table_map tables)
static void update_depend_map(JOIN *join)
{
- for (JOIN_TAB *join_tab= first_linear_tab(join, WITH_CONST_TABLES); join_tab;
+ JOIN_TAB *join_tab;
+ for (join_tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITH_CONST_TABLES);
+ join_tab;
join_tab= next_linear_tab(join, join_tab, WITH_BUSH_ROOTS))
{
TABLE_REF *ref= &join_tab->ref;
@@ -10795,6 +11146,8 @@ static void update_depend_map_for_order(JOIN *join, ORDER *order)
Remove all constants and check if ORDER only contains simple
expressions.
+ We also remove all duplicate expressions, keeping only the first one.
+
simple_order is set to 1 if sort_order only uses fields from head table
and the head table is not a LEFT JOIN table.
@@ -10802,9 +11155,10 @@ static void update_depend_map_for_order(JOIN *join, ORDER *order)
@param first_order List of SORT or GROUP order
@param cond WHERE statement
@param change_list Set to 1 if we should remove things from list.
- If this is not set, then only simple_order is
- calculated.
- @param simple_order Set to 1 if we are only using simple expressions
+ If this is not set, then only simple_order is
+ calculated.
+ @param simple_order Set to 1 if we are only using simple
+ expressions.
@return
Returns new sort order
@@ -10817,7 +11171,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
if (join->table_count == join->const_tables)
return change_list ? 0 : first_order; // No need to sort
- ORDER *order,**prev_ptr;
+ ORDER *order,**prev_ptr, *tmp_order;
table_map first_table;
table_map not_const_tables= ~join->const_table_map;
table_map ref;
@@ -10831,7 +11185,6 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
first_is_base_table= TRUE;
}
-
/*
Cleanup to avoid interference of calls of this function for
ORDER BY and GROUP BY
@@ -10883,8 +11236,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
*simple_order=0;
else
{
- Item *comp_item=0;
- if (cond && const_expression_in_where(cond,order->item[0], &comp_item))
+ if (cond && const_expression_in_where(cond,order->item[0]))
{
DBUG_PRINT("info",("removing: %s", order->item[0]->full_name()));
continue;
@@ -10901,6 +11253,17 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
}
}
}
+ /* Remove ORDER BY entries that we have seen before */
+ for (tmp_order= first_order;
+ tmp_order != order;
+ tmp_order= tmp_order->next)
+ {
+ if (tmp_order->item[0]->eq(order->item[0],1))
+ break;
+ }
+ if (tmp_order != order)
+ continue; // Duplicate order by. Remove
+
if (change_list)
*prev_ptr= order; // use this entry
prev_ptr= &order->next;
@@ -10914,6 +11277,46 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
}
+/**
+ Filter out ORDER items those are equal to constants in WHERE
+
+ This function is a limited version of remove_const() for use
+ with non-JOIN statements (i.e. single-table UPDATE and DELETE).
+
+
+ @param order Linked list of ORDER BY arguments
+ @param cond WHERE expression
+
+ @return pointer to new filtered ORDER list or NULL if whole list eliminated
+
+ @note
+ This function overwrites input order list.
+*/
+
+ORDER *simple_remove_const(ORDER *order, COND *where)
+{
+ if (!order || !where)
+ return order;
+
+ ORDER *first= NULL, *prev= NULL;
+ for (; order; order= order->next)
+ {
+ DBUG_ASSERT(!order->item[0]->with_sum_func); // should never happen
+ if (!const_expression_in_where(where, order->item[0]))
+ {
+ if (!first)
+ first= order;
+ if (prev)
+ prev->next= order;
+ prev= order;
+ }
+ }
+ if (prev)
+ prev->next= NULL;
+ return first;
+}
+
+
static int
return_zero_rows(JOIN *join, select_result *result, List<TABLE_LIST> &tables,
List<Item> &fields, bool send_row, ulonglong select_options,
@@ -10958,7 +11361,7 @@ return_zero_rows(JOIN *join, select_result *result, List<TABLE_LIST> &tables,
if (having && having->val_int() == 0)
send_row=0;
}
- if (!(result->send_fields(fields,
+ if (!(result->send_result_set_metadata(fields,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)))
{
bool send_error= FALSE;
@@ -11276,15 +11679,15 @@ static bool check_simple_equality(Item *left_item, Item *right_item,
{
bool copyfl;
- if (field_item->result_type() == STRING_RESULT)
+ if (field_item->cmp_type() == STRING_RESULT)
{
CHARSET_INFO *cs= ((Field_str*) field_item->field)->charset();
if (!item)
{
Item_func_eq *eq_item;
- if ((eq_item= new Item_func_eq(orig_left_item, orig_right_item)))
+ if (!(eq_item= new Item_func_eq(orig_left_item, orig_right_item)) ||
+ eq_item->set_cmp_func())
return FALSE;
- eq_item->set_cmp_func();
eq_item->quick_fix_field();
item= eq_item;
}
@@ -11374,9 +11777,9 @@ static bool check_row_equality(THD *thd, Item *left_row, Item_row *right_row,
if (!is_converted)
{
Item_func_eq *eq_item;
- if (!(eq_item= new Item_func_eq(left_item, right_item)))
+ if (!(eq_item= new Item_func_eq(left_item, right_item)) ||
+ eq_item->set_cmp_func())
return FALSE;
- eq_item->set_cmp_func();
eq_item->quick_fix_field();
eq_list->push_back(eq_item);
}
@@ -12071,21 +12474,22 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels,
/*
If we're inside an SJM-nest (current_sjm!=NULL), and the multi-equality
doesn't include a constant, we should produce equality with the first
- of the equals in this SJM.
+ of the equal items in this SJM (except for the first element inside the
+ SJM. For that, we produce the equality with the "head" item).
In other cases, get the "head" item, which is either first of the
equals on top level, or the constant.
*/
- Item *head_item= (!item_const && current_sjm)? current_sjm_head: head;
+ Item *head_item= (!item_const && current_sjm &&
+ 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;
eq_item= new Item_func_eq(field_item->real_item(), head_item);
- if (!eq_item)
+ if (!eq_item || eq_item->set_cmp_func())
return 0;
- eq_item->set_cmp_func();
eq_item->quick_fix_field();
}
current_sjm= field_sjm;
@@ -12202,7 +12606,7 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels,
Item_equal::get_first() for details.
@return
- The transformed condition
+ The transformed condition, or NULL in case of error
*/
static COND* substitute_for_best_equal_field(JOIN_TAB *context_tab,
@@ -12437,7 +12841,6 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
left_item->collation.collation == value->collation.collation))
{
Item *tmp=value->clone_item();
-
if (tmp)
{
tmp->collation.set(right_item->collation);
@@ -12461,7 +12864,6 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
right_item->collation.collation == value->collation.collation))
{
Item *tmp= value->clone_item();
-
if (tmp)
{
tmp->collation.set(left_item->collation);
@@ -13157,13 +13559,13 @@ static void restore_prev_nj_state(JOIN_TAB *last)
bool was_fully_covered= nest->is_fully_covered();
+ join->cur_embedding_map|= nest->nj_map;
+
if (--nest->counter == 0)
join->cur_embedding_map&= ~nest->nj_map;
if (!was_fully_covered)
break;
-
- join->cur_embedding_map|= nest->nj_map;
}
}
}
@@ -13406,6 +13808,57 @@ void propagate_new_equalities(THD *thd, Item *cond,
}
}
+/*
+ Check if cond_is_datetime_is_null() is true for the condition cond, or
+ for any of its AND/OR-children
+*/
+bool cond_has_datetime_is_null(Item *cond)
+{
+ if (cond_is_datetime_is_null(cond))
+ return true;
+
+ if (cond->type() == Item::COND_ITEM)
+ {
+ List<Item> *cond_arg_list= ((Item_cond*) cond)->argument_list();
+ List_iterator<Item> li(*cond_arg_list);
+ Item *item;
+ while ((item= li++))
+ {
+ if (cond_has_datetime_is_null(item))
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ Check if passed condtition has for of
+
+ not_null_date_col IS NULL
+
+ where not_null_date_col has a datte or datetime type
+*/
+
+bool cond_is_datetime_is_null(Item *cond)
+{
+ if (cond->type() == Item::FUNC_ITEM &&
+ ((Item_func*) cond)->functype() == Item_func::ISNULL_FUNC)
+ {
+ Item **args= ((Item_func_isnull*) cond)->arguments();
+ if (args[0]->type() == Item::FIELD_ITEM)
+ {
+ Field *field=((Item_field*) args[0])->field;
+
+ if (((field->type() == MYSQL_TYPE_DATE) ||
+ (field->type() == MYSQL_TYPE_DATETIME)) &&
+ (field->flags & NOT_NULL_FLAG))
+ {
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
/**
@@ -13490,8 +13943,8 @@ void propagate_new_equalities(THD *thd, Item *cond,
=> SELECT * FROM t1 WHERE (b = 5) AND (a = 5)
*/
-COND *
-remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
+static COND *
+internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
{
if (cond->type() == Item::COND_ITEM)
{
@@ -13535,7 +13988,7 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
while ((item=li++))
{
- Item *new_item=remove_eq_conds(thd, item, &tmp_cond_value);
+ Item *new_item=internal_remove_eq_conds(thd, item, &tmp_cond_value);
if (!new_item)
{
/* This can happen only when item is converted to TRUE or FALSE */
@@ -13686,7 +14139,7 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
*/
if (is_simplifiable_cond)
{
- if (!(cond= remove_eq_conds(thd, cond, cond_value)))
+ if (!(cond= internal_remove_eq_conds(thd, cond, cond_value)))
return cond;
}
should_fix_fields= 1;
@@ -13704,8 +14157,96 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
return item;
}
}
- else if (cond->type() == Item::FUNC_ITEM &&
- ((Item_func*) cond)->functype() == Item_func::ISNULL_FUNC)
+ else if (cond_is_datetime_is_null(cond))
+ {
+ /* fix to replace 'NULL' dates with '0' (shreeve@uci.edu) */
+ /*
+ See BUG#12594011
+ Documentation says that
+ SELECT datetime_notnull d FROM t1 WHERE d IS NULL
+ shall return rows where d=='0000-00-00'
+
+ Thus, for DATE and DATETIME columns defined as NOT NULL,
+ "date_notnull IS NULL" has to be modified to
+ "date_notnull IS NULL OR date_notnull == 0" (if outer join)
+ "date_notnull == 0" (otherwise)
+
+ */
+ Item **args= ((Item_func_isnull*) cond)->arguments();
+ Field *field=((Item_field*) args[0])->field;
+
+ Item *item0= new(thd->mem_root) Item_int((longlong)0, 1);
+ Item *eq_cond= new(thd->mem_root) Item_func_eq(args[0], item0);
+ if (!eq_cond)
+ return cond;
+
+ if (field->table->pos_in_table_list->is_inner_table_of_outer_join())
+ {
+ // outer join: transform "col IS NULL" to "col IS NULL or col=0"
+ Item *or_cond= new(thd->mem_root) Item_cond_or(eq_cond, cond);
+ if (!or_cond)
+ return cond;
+ cond= or_cond;
+ }
+ else
+ {
+ // not outer join: transform "col IS NULL" to "col=0"
+ cond= eq_cond;
+ }
+
+ cond->fix_fields(thd, &cond);
+
+ if (cond->const_item() && !cond->is_expensive())
+ {
+ *cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE;
+ return (COND*) 0;
+ }
+ }
+ else if (cond->const_item() && !cond->is_expensive())
+ {
+ *cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE;
+ return (COND*) 0;
+ }
+ else if ((*cond_value= cond->eq_cmp_result()) != Item::COND_OK)
+ { // boolan compare function
+ Item *left_item= ((Item_func*) cond)->arguments()[0];
+ Item *right_item= ((Item_func*) cond)->arguments()[1];
+ if (left_item->eq(right_item,1))
+ {
+ if (!left_item->maybe_null ||
+ ((Item_func*) cond)->functype() == Item_func::EQUAL_FUNC)
+ return (COND*) 0; // Compare of identical items
+ }
+ }
+ *cond_value=Item::COND_OK;
+ return cond; // Point at next and level
+}
+
+/**
+ Remove const and eq items. Return new item, or NULL if no condition
+ cond_value is set to according:
+ COND_OK query is possible (field = constant)
+ COND_TRUE always true ( 1 = 1 )
+ COND_FALSE always false ( 1 = 2 )
+
+ SYNPOSIS
+ remove_eq_conds()
+ thd THD environment
+ cond the condition to handle
+ cond_value the resulting value of the condition
+
+ NOTES
+ calls the inner_remove_eq_conds to check all the tree reqursively
+
+ RETURN
+ *COND with the simplified condition
+*/
+
+COND *
+remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
+{
+ if (cond->type() == Item::FUNC_ITEM &&
+ ((Item_func*) cond)->functype() == Item_func::ISNULL_FUNC)
{
/*
Handles this special case for some ODBC applications:
@@ -13723,12 +14264,12 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
{
Field *field=((Item_field*) args[0])->field;
if (field->flags & AUTO_INCREMENT_FLAG && !field->table->maybe_null &&
- (thd->options & OPTION_AUTO_IS_NULL) &&
+ (thd->variables.option_bits & OPTION_AUTO_IS_NULL) &&
(thd->first_successful_insert_id_in_prev_stmt > 0 &&
thd->substitute_null_with_insert_id))
{
#ifdef HAVE_QUERY_CACHE
- query_cache_abort(&thd->net);
+ query_cache_abort(&thd->query_cache_tls);
#endif
COND *new_cond;
if ((new_cond= new Item_func_eq(args[0],
@@ -13749,57 +14290,13 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
clear for next row
*/
thd->substitute_null_with_insert_id= FALSE;
- }
- /* fix to replace 'NULL' dates with '0' (shreeve@uci.edu) */
- else if (((field->type() == MYSQL_TYPE_DATE) ||
- (field->type() == MYSQL_TYPE_DATETIME)) &&
- (field->flags & NOT_NULL_FLAG))
- {
- COND *eq_cond;
- if (!(eq_cond= new Item_func_eq(args[0],new Item_int("0", 0, 2))))
- return cond;
-
- if (field->table->pos_in_table_list->is_inner_table_of_outer_join())
- {
- // outer join: transform "col IS NULL" to "col IS NULL or col=0"
- Item *or_cond= new Item_cond_or(eq_cond, cond);
- if (!or_cond)
- return cond;
- cond= or_cond;
- }
- else
- {
- // not outer join: transform "col IS NULL" to "col=0"
- cond= eq_cond;
- }
- cond->fix_fields(thd, &cond);
+ *cond_value= Item::COND_OK;
+ return cond;
}
}
- if (cond->const_item() && !cond->is_expensive())
- {
- *cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE;
- return (COND*) 0;
- }
}
- else if (cond->const_item() && !cond->is_expensive())
- {
- *cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE;
- return (COND*) 0;
- }
- else if ((*cond_value= cond->eq_cmp_result()) != Item::COND_OK)
- { // boolan compare function
- Item *left_item= ((Item_func*) cond)->arguments()[0];
- Item *right_item= ((Item_func*) cond)->arguments()[1];
- if (left_item->eq(right_item,1))
- {
- if (!left_item->maybe_null ||
- ((Item_func*) cond)->functype() == Item_func::EQUAL_FUNC)
- return (COND*) 0; // Compare of identical items
- }
- }
- *cond_value=Item::COND_OK;
- return cond; // Point at next and level
+ return internal_remove_eq_conds(thd, cond, cond_value); // Scan all the condition
}
@@ -13835,13 +14332,50 @@ test_if_equality_guarantees_uniqueness(Item *l, Item *r)
l->collation.collation == r->collation.collation);
}
-/**
- Return TRUE if the item is a const value in all the WHERE clause.
+
+/*
+ Return TRUE if i1 and i2 (if any) are equal items,
+ or if i1 is a wrapper item around the f2 field.
*/
-static bool
-const_expression_in_where(COND *cond, Item *comp_item, Item **const_item)
+static bool equal(Item *i1, Item *i2, Field *f2)
{
+ DBUG_ASSERT((i2 == NULL) ^ (f2 == NULL));
+
+ if (i2 != NULL)
+ return i1->eq(i2, 1);
+ else if (i1->type() == Item::FIELD_ITEM)
+ return f2->eq(((Item_field *) i1)->field);
+ else
+ return FALSE;
+}
+
+
+/**
+ Test if a field or an item is equal to a constant value in WHERE
+
+ @param cond WHERE clause expression
+ @param comp_item Item to find in WHERE expression
+ (if comp_field != NULL)
+ @param comp_field Field to find in WHERE expression
+ (if comp_item != NULL)
+ @param[out] const_item intermediate arg, set to Item pointer to NULL
+
+ @return TRUE if the field is a constant value in WHERE
+
+ @note
+ comp_item and comp_field parameters are mutually exclusive.
+*/
+bool
+const_expression_in_where(COND *cond, Item *comp_item, Field *comp_field,
+ Item **const_item)
+{
+ DBUG_ASSERT((comp_item == NULL) ^ (comp_field == NULL));
+
+ Item *intermediate= NULL;
+ if (const_item == NULL)
+ const_item= &intermediate;
+
if (cond->type() == Item::COND_ITEM)
{
bool and_level= (((Item_cond*) cond)->functype()
@@ -13850,7 +14384,8 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item)
Item *item;
while ((item=li++))
{
- bool res=const_expression_in_where(item, comp_item, const_item);
+ bool res=const_expression_in_where(item, comp_item, comp_field,
+ const_item);
if (res) // Is a const value
{
if (and_level)
@@ -13862,14 +14397,14 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item)
return and_level ? 0 : 1;
}
else if (cond->eq_cmp_result() != Item::COND_OK)
- { // boolan compare function
+ { // boolean compare function
Item_func* func= (Item_func*) cond;
if (func->functype() != Item_func::EQUAL_FUNC &&
func->functype() != Item_func::EQ_FUNC)
return 0;
Item *left_item= ((Item_func*) cond)->arguments()[0];
Item *right_item= ((Item_func*) cond)->arguments()[1];
- if (left_item->eq(comp_item,1))
+ if (equal(left_item, comp_item, comp_field))
{
if (test_if_equality_guarantees_uniqueness (left_item, right_item))
{
@@ -13879,7 +14414,7 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item)
return 1;
}
}
- else if (right_item->eq(comp_item,1))
+ else if (equal(right_item, comp_item, comp_field))
{
if (test_if_equality_guarantees_uniqueness (right_item, left_item))
{
@@ -13893,6 +14428,7 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item)
return 0;
}
+
/****************************************************************************
Create internal temporary table
****************************************************************************/
@@ -14146,7 +14682,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
Item_sum *item_sum=(Item_sum*) item;
result= item_sum->create_tmp_field(group, table, convert_blob_length);
if (!result)
- thd->fatal_error();
+ my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
return result;
}
case Item::FIELD_ITEM:
@@ -14312,7 +14848,7 @@ void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps)
Create a temp table according to a field list.
Given field pointers are changed to point at tmp_table for
- send_fields. The table object is self contained: it's
+ send_result_set_metadata. The table object is self contained: it's
allocated in its own memory root, as well as Field objects
created for table columns.
This function will replace Item_sum items in 'fields' list with
@@ -14338,7 +14874,8 @@ 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,
- char *table_alias, bool do_not_open)
+ const char *table_alias, bool do_not_open,
+ bool keep_row_order)
{
MEM_ROOT *mem_root_save, own_root;
TABLE *table;
@@ -14373,7 +14910,8 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
save_sum_fields|= param->precomputed_group_by;
DBUG_ENTER("create_tmp_table");
DBUG_PRINT("enter",
- ("distinct: %d save_sum_fields: %d rows_limit: %lu group: %d",
+ ("table_alias: '%s' distinct: %d save_sum_fields: %d "
+ "rows_limit: %lu group: %d", table_alias,
(int) distinct, (int) save_sum_fields,
(ulong) rows_limit,test(group)));
@@ -14423,7 +14961,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
can't index BIT fields.
*/
(*tmp->item)->marker=4; // Store null in key
- if ((*tmp->item)->max_length >= CONVERT_IF_BIGGER_TO_BLOB)
+ if ((*tmp->item)->too_big_for_varchar())
using_unique_constraint=1;
}
if (param->group_length >= MAX_BLOB_WIDTH)
@@ -14514,6 +15052,8 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
share->primary_key= MAX_KEY; // Indicate no primary key
share->keys_for_keyread.init();
share->keys_in_use.init();
+ if (param->schema_table)
+ share->db= INFORMATION_SCHEMA_NAME;
/* Calculate which type of fields we will store in the temporary table */
@@ -14726,10 +15266,10 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
/* If result table is small; use a heap */
/* future: storage engine selection can be made dynamic? */
- if (blob_count || using_unique_constraint ||
- (select_options & (OPTION_BIG_TABLES | SELECT_SMALL_RESULT)) ==
- OPTION_BIG_TABLES || (select_options & TMP_TABLE_FORCE_MYISAM) ||
- !thd->variables.tmp_table_size)
+ if (blob_count || using_unique_constraint
+ || (thd->variables.big_tables && !(select_options & SELECT_SMALL_RESULT))
+ || (select_options & TMP_TABLE_FORCE_MYISAM)
+ || thd->variables.tmp_table_size == 0)
{
share->db_plugin= ha_lock_engine(0, TMP_ENGINE_HTON);
table->file= get_new_handler(share, &table->mem_root,
@@ -14814,25 +15354,10 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
if (!(field->flags & NOT_NULL_FLAG))
{
- if (field->flags & GROUP_FLAG && !using_unique_constraint)
- {
- /*
- We have to reserve one byte here for NULL bits,
- as this is updated by 'end_update()'
- */
- *pos++=0; // Null is stored here
- recinfo->length=1;
- recinfo->type=FIELD_NORMAL;
- recinfo++;
- bzero((uchar*) recinfo,sizeof(*recinfo));
- }
- else
- {
- recinfo->null_bit= 1 << (null_count & 7);
- recinfo->null_pos= null_count/8;
- }
+ recinfo->null_bit= (uint8)1 << (null_count & 7);
+ recinfo->null_pos= null_count/8;
field->move_field(pos,null_flags+null_count/8,
- 1 << (null_count & 7));
+ (uint8)1 << (null_count & 7));
null_count++;
}
else
@@ -14935,7 +15460,9 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
share->keys_in_use.set_bit(0);
keyinfo->key_part=key_part_info;
keyinfo->flags=HA_NOSAME | HA_BINARY_PACK_KEY | HA_PACK_KEY;
+ keyinfo->ext_key_flags= keyinfo->flags;
keyinfo->usable_key_parts=keyinfo->key_parts= param->group_parts;
+ keyinfo->ext_key_parts= keyinfo->key_parts;
keyinfo->key_length=0;
keyinfo->rec_per_key=0;
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
@@ -14959,7 +15486,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
(ha_base_keytype) key_part_info->type == HA_KEYTYPE_VARTEXT1 ||
(ha_base_keytype) key_part_info->type == HA_KEYTYPE_VARTEXT2) ?
0 : FIELDFLAG_BINARY;
-
+ key_part_info->key_part_flag= 0;
if (!using_unique_constraint)
{
cur_group->buff=(char*) group_buff;
@@ -15034,6 +15561,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
null_pack_length-=hidden_null_pack_length;
keyinfo->key_parts= ((field_count-param->hidden_field_count)+
(share->uniques ? test(null_pack_length) : 0));
+ keyinfo->ext_key_parts= keyinfo->key_parts;
table->distinct= 1;
share->keys= 1;
if (!(key_part_info= (KEY_PART_INFO*)
@@ -15046,6 +15574,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
table->key_info= table->s->key_info= keyinfo;
keyinfo->key_part=key_part_info;
keyinfo->flags=HA_NOSAME | HA_NULL_ARE_EQUAL | HA_BINARY_PACK_KEY | HA_PACK_KEY;
+ keyinfo->ext_key_flags= keyinfo->flags;
keyinfo->key_length= 0; // Will compute the sum of the parts below.
keyinfo->name= (char*) "distinct_key";
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
@@ -15148,6 +15677,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
share->db_record_offset= 1;
table->used_for_duplicate_elimination= (param->sum_func_count == 0 &&
(table->group || table->distinct));
+ table->keep_row_order= keep_row_order;
if (!do_not_open)
{
@@ -15226,6 +15756,7 @@ TABLE *create_virtual_tmp_table(THD *thd, List<Create_field> &field_list)
bzero(share, sizeof(*share));
table->field= field;
table->s= share;
+ table->temp_pool_slot= MY_BIT_NONE;
share->blob_field= blob_field;
share->fields= field_count;
share->blob_ptr_size= portable_sizeof_char_ptr;
@@ -15287,12 +15818,24 @@ TABLE *create_virtual_tmp_table(THD *thd, List<Create_field> &field_list)
{
cur_field->move_field(field_pos, (uchar*) null_pos, null_bit);
null_bit<<= 1;
- if (null_bit == (1 << 8))
+ if (null_bit == (uint)1 << 8)
{
++null_pos;
null_bit= 1;
}
}
+ if (cur_field->type() == MYSQL_TYPE_BIT &&
+ cur_field->key_type() == HA_KEYTYPE_BIT)
+ {
+ /* This is a Field_bit since key_type is HA_KEYTYPE_BIT */
+ static_cast<Field_bit*>(cur_field)->set_bit_ptr(null_pos, null_bit);
+ null_bit+= cur_field->field_length & 7;
+ if (null_bit > 7)
+ {
+ null_pos++;
+ null_bit-= 8;
+ }
+ }
cur_field->reset();
field_pos+= cur_field->pack_length();
@@ -15324,7 +15867,7 @@ bool open_tmp_table(TABLE *table)
}
-#if defined(WITH_ARIA_STORAGE_ENGINE) && defined(USE_MARIA_FOR_TMP_TABLES)
+#if defined(WITH_ARIA_STORAGE_ENGINE) && defined(USE_ARIA_FOR_TMP_TABLES)
/*
Create internal (MyISAM or Maria) temporary table
@@ -15453,8 +15996,8 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
}
bzero((char*) &create_info,sizeof(create_info));
- if ((options & (OPTION_BIG_TABLES | SELECT_SMALL_RESULT)) ==
- OPTION_BIG_TABLES)
+ /* Use long data format, to ensure we never get a 'table is full' error */
+ if (!(options & SELECT_SMALL_RESULT))
create_info.data_file_length= ~(ulonglong) 0;
/*
@@ -15472,7 +16015,8 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
table->no_rows ? NO_RECORD :
(share->reclength < 64 &&
!share->blob_fields ? STATIC_RECORD :
- table->used_for_duplicate_elimination ?
+ table->used_for_duplicate_elimination ||
+ table->keep_row_order ?
DYNAMIC_RECORD : BLOCK_RECORD),
share->keys, &keydef,
(uint) (*recinfo-start_recinfo),
@@ -15498,13 +16042,15 @@ bool create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
ENGINE_COLUMNDEF *start_recinfo,
ENGINE_COLUMNDEF **recinfo,
int error,
- bool ignore_last_dupp_key_error)
+ bool ignore_last_dupp_key_error,
+ bool *is_duplicate)
{
return create_internal_tmp_table_from_heap2(thd, table,
start_recinfo, recinfo, error,
ignore_last_dupp_key_error,
maria_hton,
- "converting HEAP to Aria");
+ "converting HEAP to Aria",
+ is_duplicate);
}
#else
@@ -15631,8 +16177,7 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
MI_CREATE_INFO create_info;
bzero((char*) &create_info,sizeof(create_info));
- if ((options & (OPTION_BIG_TABLES | SELECT_SMALL_RESULT)) ==
- OPTION_BIG_TABLES)
+ if (!(options & SELECT_SMALL_RESULT))
create_info.data_file_length= ~(ulonglong) 0;
if ((error=mi_create(share->table_name.str, share->keys, &keydef,
@@ -15664,13 +16209,15 @@ bool create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
ENGINE_COLUMNDEF *start_recinfo,
ENGINE_COLUMNDEF **recinfo,
int error,
- bool ignore_last_dupp_key_error)
+ bool ignore_last_dupp_key_error,
+ bool *is_duplicate)
{
return create_internal_tmp_table_from_heap2(thd, table,
start_recinfo, recinfo, error,
ignore_last_dupp_key_error,
myisam_hton,
- "converting HEAP to MyISAM");
+ "converting HEAP to MyISAM",
+ is_duplicate);
}
#endif /* WITH_MARIA_STORAGE_ENGINE */
@@ -15689,13 +16236,16 @@ create_internal_tmp_table_from_heap2(THD *thd, TABLE *table,
int error,
bool ignore_last_dupp_key_error,
handlerton *hton,
- const char *proc_info)
+ const char *proc_info,
+ bool *is_duplicate)
{
TABLE new_table;
TABLE_SHARE share;
const char *save_proc_info;
int write_err= 0;
DBUG_ENTER("create_internal_tmp_table_from_heap2");
+ if (is_duplicate)
+ *is_duplicate= FALSE;
if (table->s->db_type() != heap_hton ||
error != HA_ERR_RECORD_FILE_FULL)
@@ -15704,8 +16254,7 @@ create_internal_tmp_table_from_heap2(THD *thd, TABLE *table,
We don't want this error to be converted to a warning, e.g. in case of
INSERT IGNORE ... SELECT.
*/
- thd->fatal_error();
- table->file->print_error(error,MYF(0));
+ table->file->print_error(error, MYF(ME_FATALERROR));
DBUG_RETURN(1);
}
new_table= *table;
@@ -15723,7 +16272,7 @@ create_internal_tmp_table_from_heap2(THD *thd, TABLE *table,
if (create_internal_tmp_table(&new_table, table->key_info, start_recinfo,
recinfo,
thd->lex->select_lex.options |
- thd->options))
+ thd->variables.option_bits))
goto err2;
if (open_tmp_table(&new_table))
goto err1;
@@ -15767,6 +16316,13 @@ create_internal_tmp_table_from_heap2(THD *thd, TABLE *table,
if (new_table.file->is_fatal_error(write_err, HA_CHECK_DUP) ||
!ignore_last_dupp_key_error)
goto err;
+ if (is_duplicate)
+ *is_duplicate= TRUE;
+ }
+ else
+ {
+ if (is_duplicate)
+ *is_duplicate= FALSE;
}
/* remove heap table and change to use myisam table */
@@ -15809,13 +16365,15 @@ free_tmp_table(THD *thd, TABLE *entry)
MEM_ROOT own_root= entry->mem_root;
const char *save_proc_info;
DBUG_ENTER("free_tmp_table");
- DBUG_PRINT("enter",("table: %s",entry->alias.c_ptr()));
+ DBUG_PRINT("enter",("table: %s alias: %s",entry->s->table_name.str,
+ entry->alias.c_ptr()));
save_proc_info=thd->proc_info;
thd_proc_info(thd, "removing tmp table");
if (entry->file && entry->created)
{
+ entry->file->ha_index_or_rnd_end();
if (entry->db_stat)
entry->file->ha_drop_table(entry->s->table_name.str);
else
@@ -15832,6 +16390,7 @@ free_tmp_table(THD *thd, TABLE *entry)
bitmap_lock_clear_bit(&temp_pool, entry->temp_pool_slot);
plugin_unlock(0, entry->s->db_plugin);
+ entry->alias.free();
free_root(&own_root, MYF(0)); /* the table is allocated in its own root */
thd_proc_info(thd, save_proc_info);
@@ -15944,16 +16503,16 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
if (table)
{
- VOID(table->file->extra(HA_EXTRA_WRITE_CACHE));
+ (void) table->file->extra(HA_EXTRA_WRITE_CACHE);
empty_record(table);
if (table->group && join->tmp_table_param.sum_func_count &&
table->s->keys && !table->file->inited)
{
- int tmp_error;
- if ((tmp_error= table->file->ha_index_init(0, 0)))
+ rc= table->file->ha_index_init(0, 0);
+ if (rc)
{
- table->file->print_error(tmp_error, MYF(0)); /* purecov: inspected */
- DBUG_RETURN(-1); /* purecov: inspected */
+ table->file->print_error(rc, MYF(0));
+ DBUG_RETURN(-1);
}
}
}
@@ -15988,7 +16547,6 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
so we don't touch it here.
*/
join->examined_rows++;
- join->thd->row_count++;
DBUG_ASSERT(join->examined_rows <= 1);
}
else if (join->send_row_on_empty_set())
@@ -16000,6 +16558,13 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
rc= join->result->send_data(*columns_list) > 0;
}
}
+ /*
+ An error can happen when evaluating the conds
+ (the join condition and piece of where clause
+ relevant to this join table).
+ */
+ if (join->thd->is_error())
+ error= NESTED_LOOP_ERROR;
}
else
{
@@ -16008,34 +16573,15 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
error= NESTED_LOOP_NO_MORE_ROWS;
else
error= sub_select(join,join_tab,0);
- if (error == NESTED_LOOP_OK || error == NESTED_LOOP_NO_MORE_ROWS)
+ if ((error == NESTED_LOOP_OK || error == NESTED_LOOP_NO_MORE_ROWS) &&
+ join->thd->killed != ABORT_QUERY)
error= sub_select(join,join_tab,1);
if (error == NESTED_LOOP_QUERY_LIMIT)
error= NESTED_LOOP_OK; /* select_limit used */
}
- if (error == NESTED_LOOP_NO_MORE_ROWS)
+ if (error == NESTED_LOOP_NO_MORE_ROWS || join->thd->killed == ABORT_QUERY)
error= NESTED_LOOP_OK;
- if (table == NULL) // If sending data to client
- {
- /*
- The following will unlock all cursors if the command wasn't an
- update command
- */
- join->join_free(); // Unlock all cursors
- }
- if (error == NESTED_LOOP_OK)
- {
- /*
- Sic: this branch works even if rc != 0, e.g. when
- send_data above returns an error.
- */
- if (table == NULL && join->result->send_eof()) // If sending data to client
- rc= 1; // Don't send error
- DBUG_PRINT("info",("%ld records output", (long) join->send_records));
- }
- else
- rc= -1;
if (table)
{
int tmp, new_errno= 0;
@@ -16052,6 +16598,29 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
if (new_errno)
table->file->print_error(new_errno,MYF(0));
}
+ else
+ {
+ /*
+ The following will unlock all cursors if the command wasn't an
+ update command
+ */
+ join->join_free(); // Unlock all cursors
+ }
+ if (error == NESTED_LOOP_OK)
+ {
+ /*
+ Sic: this branch works even if rc != 0, e.g. when
+ send_data above returns an error.
+ */
+ if (!table) // If sending data to client
+ {
+ if (join->result->send_eof())
+ rc= 1; // Don't send error
+ }
+ DBUG_PRINT("info",("%ld records output", (long) join->send_records));
+ }
+ else
+ rc= -1;
#ifndef DBUG_OFF
if (rc)
{
@@ -16312,52 +16881,38 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
if (!join_tab->preread_init_done && join_tab->preread_init())
DBUG_RETURN(NESTED_LOOP_ERROR);
- if (join->resume_nested_loop)
- {
- /* If not the last table, plunge down the nested loop */
- if (join_tab < join->join_tab + join->top_join_tab_count - 1)
- rc= (*join_tab->next_select)(join, join_tab + 1, 0);
- else
- {
- join->resume_nested_loop= FALSE;
- rc= NESTED_LOOP_OK;
- }
- }
- else
- {
- join->return_tab= join_tab;
+ join->return_tab= join_tab;
- if (join_tab->last_inner)
- {
- /* join_tab is the first inner table for an outer join operation. */
+ if (join_tab->last_inner)
+ {
+ /* join_tab is the first inner table for an outer join operation. */
- /* Set initial state of guard variables for this table.*/
- join_tab->found=0;
- join_tab->not_null_compl= 1;
+ /* Set initial state of guard variables for this table.*/
+ join_tab->found=0;
+ join_tab->not_null_compl= 1;
- /* Set first_unmatched for the last inner table of this group */
- join_tab->last_inner->first_unmatched= join_tab;
- if (join_tab->on_precond && !join_tab->on_precond->val_int())
- rc= NESTED_LOOP_NO_MORE_ROWS;
- }
- join->thd->row_count= 0;
-
- if (rc != NESTED_LOOP_NO_MORE_ROWS &&
- (rc= join_tab_execution_startup(join_tab)) < 0)
- DBUG_RETURN(rc);
+ /* Set first_unmatched for the last inner table of this group */
+ join_tab->last_inner->first_unmatched= join_tab;
+ if (join_tab->on_precond && !join_tab->on_precond->val_int())
+ rc= NESTED_LOOP_NO_MORE_ROWS;
+ }
+ join->thd->warning_info->reset_current_row_for_warning();
- if (join_tab->loosescan_match_tab)
- join_tab->loosescan_match_tab->found_match= FALSE;
+ if (rc != NESTED_LOOP_NO_MORE_ROWS &&
+ (rc= join_tab_execution_startup(join_tab)) < 0)
+ DBUG_RETURN(rc);
+
+ if (join_tab->loosescan_match_tab)
+ join_tab->loosescan_match_tab->found_match= FALSE;
- if (rc != NESTED_LOOP_NO_MORE_ROWS)
- {
- error= (*join_tab->read_first_record)(join_tab);
- if (join_tab->keep_current_rowid)
- join_tab->table->file->position(join_tab->table->record[0]);
- rc= evaluate_join_record(join, join_tab, error);
- }
+ if (rc != NESTED_LOOP_NO_MORE_ROWS)
+ {
+ error= (*join_tab->read_first_record)(join_tab);
+ if (!error && join_tab->keep_current_rowid)
+ join_tab->table->file->position(join_tab->table->record[0]);
+ rc= evaluate_join_record(join, join_tab, error);
}
-
+
/*
Note: psergey has added the 2nd part of the following condition; the
change should probably be made in 5.1, too.
@@ -16485,14 +17040,26 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
will be re-evaluated again. It could be fixed, but, probably,
it's not worth doing now.
*/
+ /*
+ not_exists_optimize has been created from a
+ select_cond containing 'is_null'. This 'is_null'
+ predicate is still present on any 'tab' with
+ 'not_exists_optimize'. Furthermore, the usual rules
+ for condition guards also applies for
+ 'not_exists_optimize' -> When 'is_null==false' we
+ know all cond. guards are open and we can apply
+ the 'not_exists_optimize'.
+ */
+ DBUG_ASSERT(!(tab->table->reginfo.not_exists_optimize &&
+ !tab->select_cond));
+
if (tab->select_cond && !tab->select_cond->val_int())
{
/* The condition attached to table tab is false */
+
if (tab == join_tab)
{
found= 0;
- if (tab->table->reginfo.not_exists_optimize)
- DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
}
else
{
@@ -16501,10 +17068,21 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
not to the last table of the current nest level.
*/
join->return_tab= tab;
- if (tab->table->reginfo.not_exists_optimize)
- DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
- else
- DBUG_RETURN(NESTED_LOOP_OK);
+ }
+
+ if (tab->table->reginfo.not_exists_optimize)
+ {
+ /*
+ When not_exists_optimize is set: No need to further
+ explore more rows of 'tab' for this partial result.
+ Any found 'tab' matches are known to evaluate to 'false'.
+ Returning .._NO_MORE_ROWS will skip rem. 'tab' rows.
+ */
+ DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
+ }
+ else if (tab != join_tab)
+ {
+ DBUG_RETURN(NESTED_LOOP_OK);
}
}
}
@@ -16544,7 +17122,6 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
(See above join->return_tab= tab).
*/
join->examined_rows++;
- join->thd->row_count++;
DBUG_PRINT("counts", ("join->examined_rows++: %lu",
(ulong) join->examined_rows));
@@ -16553,6 +17130,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
enum enum_nested_loop_state rc;
/* A match from join_tab is found for the current partial join. */
rc= (*join_tab->next_select)(join, join_tab+1, 0);
+ join->thd->warning_info->inc_current_row_for_warning();
if (rc != NESTED_LOOP_OK && rc != NESTED_LOOP_NO_MORE_ROWS)
DBUG_RETURN(rc);
if (return_tab < join->return_tab)
@@ -16569,7 +17147,10 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
}
else
+ {
+ join->thd->warning_info->inc_current_row_for_warning();
join_tab->read_record.unlock_row(join_tab);
+ }
}
else
{
@@ -16578,7 +17159,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
with the beginning coinciding with the current partial join.
*/
join->examined_rows++;
- join->thd->row_count++;
+ join->thd->warning_info->inc_current_row_for_warning();
join_tab->read_record.unlock_row(join_tab);
}
DBUG_RETURN(NESTED_LOOP_OK);
@@ -16689,10 +17270,10 @@ int report_error(TABLE *table, int error)
print them to the .err log
*/
if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT
- && !table->in_use->killed)
+ && error != HA_ERR_TABLE_DEF_CHANGED && !table->in_use->killed)
{
- push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_ERROR, error,
- "Got error %d when reading table `%s`.`%s`",
+ push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN, error,
+ "Got error %d when reading table %`s.%`s",
error, table->s->db.str, table->s->table_name.str);
sql_print_error("Got error %d when reading table '%s'",
error, table->s->path.str);
@@ -16763,7 +17344,7 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos)
/* Mark for EXPLAIN that the row was not found */
pos->records_read=0.0;
pos->ref_depend_map= 0;
- if (!table->maybe_null || error > 0)
+ if (!table->pos_in_table_list->outer_join || error > 0)
DBUG_RETURN(error);
}
/*
@@ -16789,7 +17370,7 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos)
/* Mark for EXPLAIN that the row was not found */
pos->records_read=0.0;
pos->ref_depend_map= 0;
- if (!table->maybe_null || error > 0)
+ if (!table->pos_in_table_list->outer_join || error > 0)
DBUG_RETURN(error);
}
}
@@ -16965,7 +17546,12 @@ int join_read_key2(THD *thd, JOIN_TAB *tab, TABLE *table, TABLE_REF *table_ref)
int error;
if (!table->file->inited)
{
- table->file->ha_index_init(table_ref->key, (tab ? tab->sorted : TRUE));
+ error= table->file->ha_index_init(table_ref->key, tab ? tab->sorted : TRUE);
+ if (error)
+ {
+ (void) report_error(table, error);
+ return 1;
+ }
}
/* TODO: Why don't we do "Late NULLs Filtering" here? */
@@ -17056,13 +17642,18 @@ join_read_always_key(JOIN_TAB *tab)
{
if ((error= table->file->ha_index_init(tab->ref.key, tab->sorted)))
{
- table->file->print_error(error, MYF(0));/* purecov: inspected */
- return(1); /* purecov: inspected */
+ (void) report_error(table, error);
+ return 1;
}
}
if (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))))
+ {
+ 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),
@@ -17087,16 +17678,20 @@ join_read_last_key(JOIN_TAB *tab)
int error;
TABLE *table= tab->table;
- if (!table->file->inited)
+ if (!table->file->inited &&
+ (error= table->file->ha_index_init(tab->ref.key, tab->sorted)))
{
- if ((error= table->file->ha_index_init(tab->ref.key, tab->sorted)))
- {
- table->file->print_error(error, MYF(0));/* purecov: inspected */
- return(1); /* purecov: inspected */
- }
+ (void) report_error(table, error);
+ return 1;
}
+
if (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))))
+ {
+ 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),
@@ -17166,9 +17761,8 @@ join_init_quick_read_record(JOIN_TAB *tab)
}
-int init_read_record_seq(JOIN_TAB *tab)
+int read_first_record_seq(JOIN_TAB *tab)
{
- tab->read_record.read_record= rr_sequential;
if (tab->read_record.table->file->ha_rnd_init_with_error(1))
return 1;
return (*tab->read_record.read_record)(&tab->read_record);
@@ -17230,6 +17824,8 @@ join_read_first(JOIN_TAB *tab)
{
int error= 0;
TABLE *table=tab->table;
+ DBUG_ENTER("join_read_first");
+
if (table->covering_keys.is_set(tab->index) && !table->no_keyread &&
!table->key_read)
table->enable_keyread();
@@ -17246,9 +17842,9 @@ join_read_first(JOIN_TAB *tab)
{
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
report_error(table, error);
- return -1;
+ DBUG_RETURN(-1);
}
- return 0;
+ DBUG_RETURN(0);
}
@@ -17268,6 +17864,8 @@ join_read_last(JOIN_TAB *tab)
{
TABLE *table=tab->table;
int error= 0;
+ DBUG_ENTER("join_read_first");
+
if (table->covering_keys.is_set(tab->index) && !table->no_keyread &&
!table->key_read)
table->enable_keyread();
@@ -17281,9 +17879,9 @@ join_read_last(JOIN_TAB *tab)
if (!error)
error= table->file->prepare_index_scan();
if (error || (error= tab->table->file->ha_index_last(tab->table->record[0])))
- return report_error(table, error);
+ DBUG_RETURN(report_error(table, error));
- return 0;
+ DBUG_RETURN(0);
}
@@ -17306,14 +17904,10 @@ join_ft_read_first(JOIN_TAB *tab)
if (!table->file->inited &&
(error= table->file->ha_index_init(tab->ref.key, 1)))
{
- table->file->print_error(error, MYF(0)); /* purecov: inspected */
- return(1); /* purecov: inspected */
+ (void) report_error(table, error);
+ return 1;
}
-#if NOT_USED_YET
- /* as ft-key doesn't use store_key's, see also FT_SELECT::init() */
- if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref))
- return -1;
-#endif
+
table->file->ft_init();
if ((error= table->file->ha_ft_read(table->record[0])))
@@ -17400,6 +17994,18 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_ENTER("end_send");
if (!end_of_records)
{
+ if (join->table_count &&
+ (join->join_tab->is_using_loose_index_scan() ||
+ /*
+ When order by used a loose scan as its input, the quick select may
+ be attached to pre_sort_join_tab.
+ */
+ (join->pre_sort_join_tab &&
+ join->pre_sort_join_tab->is_using_loose_index_scan())))
+ {
+ /* Copy non-aggregated fields when loose index scan is used. */
+ copy_fields(&join->tmp_table_param);
+ }
if (join->having && join->having->val_int() == 0)
DBUG_RETURN(NESTED_LOOP_OK); // Didn't match having
if (join->procedure)
@@ -17572,7 +18178,7 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (end_of_records)
DBUG_RETURN(NESTED_LOOP_OK);
join->first_record=1;
- VOID(test_if_group_changed(join->group_fields));
+ (void) test_if_group_changed(join->group_fields);
}
if (idx < (int) join->send_group_parts)
{
@@ -17604,33 +18210,12 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
TABLE *table=join->tmp_table;
DBUG_ENTER("end_write");
- if (join->thd->killed) // Aborted by user
- {
- join->thd->send_kill_message();
- DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
- }
if (!end_of_records)
{
copy_fields(&join->tmp_table_param);
if (copy_funcs(join->tmp_table_param.items_to_copy, join->thd))
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
-#ifdef TO_BE_DELETED
- if (!table->uniques) // If not unique handling
- {
- /* Copy null values from group to row */
- ORDER *group;
- for (group=table->group ; group ; group=group->next)
- {
- Item *item= *group->item;
- if (item->maybe_null)
- {
- Field *field=item->get_tmp_table_field();
- field->ptr[-1]= (uchar) (field->is_null() ? 1 : 0);
- }
- }
- }
-#endif
if (!join->having || join->having->val_int())
{
int error;
@@ -17639,11 +18224,14 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
{
if (!table->file->is_fatal_error(error, HA_CHECK_DUP))
goto end;
+ bool is_duplicate;
if (create_internal_tmp_table_from_heap(join->thd, table,
join->tmp_table_param.start_recinfo,
&join->tmp_table_param.recinfo,
- error,1))
+ error, 1, &is_duplicate))
DBUG_RETURN(NESTED_LOOP_ERROR); // Not a table_is_full error
+ if (is_duplicate)
+ goto end;
table->s->uniques=0; // To ensure rows are the same
}
if (++join->send_records >= join->tmp_table_param.end_write_records &&
@@ -17653,11 +18241,15 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT);
join->do_send_rows=0;
join->unit->select_limit_cnt = HA_POS_ERROR;
- DBUG_RETURN(NESTED_LOOP_OK);
}
}
}
end:
+ if (join->thd->killed)
+ {
+ join->thd->send_kill_message();
+ DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
+ }
DBUG_RETURN(NESTED_LOOP_OK);
}
@@ -17675,11 +18267,6 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (end_of_records)
DBUG_RETURN(NESTED_LOOP_OK);
- if (join->thd->killed) // Aborted by user
- {
- join->thd->send_kill_message();
- DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
- }
join->found_records++;
copy_fields(&join->tmp_table_param); // Groups are copied twice.
@@ -17705,22 +18292,9 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
table->file->print_error(error,MYF(0)); /* purecov: inspected */
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
}
- DBUG_RETURN(NESTED_LOOP_OK);
+ goto end;
}
- /*
- Copy null bits from group key to table
- We can't copy all data as the key may have different format
- as the row data (for example as with VARCHAR keys)
- */
- KEY_PART_INFO *key_part;
- for (group=table->group,key_part=table->key_info[0].key_part;
- group ;
- group=group->next,key_part++)
- {
- if (key_part->null_bit)
- memcpy(table->record[0]+key_part->offset, group->buff, 1);
- }
init_tmptable_sum_functions(join->sum_funcs);
if (copy_funcs(join->tmp_table_param.items_to_copy, join->thd))
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
@@ -17729,17 +18303,24 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (create_internal_tmp_table_from_heap(join->thd, table,
join->tmp_table_param.start_recinfo,
&join->tmp_table_param.recinfo,
- error, 0))
+ 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)))
{
- table->file->print_error(error, MYF(0));/* purecov: inspected */
- DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(NESTED_LOOP_ERROR);
}
+
join->join_tab[join->top_join_tab_count-1].next_select=end_unique_update;
}
join->send_records++;
+end:
+ if (join->thd->killed)
+ {
+ join->thd->send_kill_message();
+ DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
+ }
DBUG_RETURN(NESTED_LOOP_OK);
}
@@ -17756,11 +18337,6 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (end_of_records)
DBUG_RETURN(NESTED_LOOP_OK);
- if (join->thd->killed) // Aborted by user
- {
- join->thd->send_kill_message();
- DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
- }
init_tmptable_sum_functions(join->sum_funcs);
copy_fields(&join->tmp_table_param); // Groups are copied twice.
@@ -17790,6 +18366,11 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
}
}
+ if (join->thd->killed)
+ {
+ join->thd->send_kill_message();
+ DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
+ }
DBUG_RETURN(NESTED_LOOP_OK);
}
@@ -17803,11 +18384,6 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
int idx= -1;
DBUG_ENTER("end_write_group");
- if (join->thd->killed)
- { // Aborted by user
- join->thd->send_kill_message();
- DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
- }
if (!join->first_record || end_of_records ||
(idx=test_if_group_changed(join->group_fields)) >= 0)
{
@@ -17832,7 +18408,7 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
create_internal_tmp_table_from_heap(join->thd, table,
join->tmp_table_param.start_recinfo,
&join->tmp_table_param.recinfo,
- error, 0))
+ error, 0, NULL))
DBUG_RETURN(NESTED_LOOP_ERROR);
}
if (join->rollup.state != ROLLUP::STATE_NONE)
@@ -17841,15 +18417,15 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_ERROR);
}
if (end_of_records)
- DBUG_RETURN(NESTED_LOOP_OK);
+ goto end;
}
}
else
{
if (end_of_records)
- DBUG_RETURN(NESTED_LOOP_OK);
+ goto end;
join->first_record=1;
- VOID(test_if_group_changed(join->group_fields));
+ (void) test_if_group_changed(join->group_fields);
}
if (idx < (int) join->send_group_parts)
{
@@ -17860,13 +18436,19 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_ERROR);
if (join->procedure)
join->procedure->add();
- DBUG_RETURN(NESTED_LOOP_OK);
+ goto end;
}
}
if (update_sum_func(join->sum_funcs))
DBUG_RETURN(NESTED_LOOP_ERROR);
if (join->procedure)
join->procedure->add();
+end:
+ if (join->thd->killed)
+ {
+ join->thd->send_kill_message();
+ DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
+ }
DBUG_RETURN(NESTED_LOOP_OK);
}
@@ -17942,7 +18524,7 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item)
field->real_type() != MYSQL_TYPE_VARCHAR &&
(field->type() != MYSQL_TYPE_FLOAT || field->decimals() == 0))
{
- return !store_val_in_field(field, right_item, CHECK_FIELD_WARN);
+ return !right_item->save_in_field_no_warnings(field, 1);
}
}
}
@@ -18203,7 +18785,7 @@ make_cond_after_sjm(Item *root_cond, Item *cond, table_map tables,
new_cond->argument_list()->push_back(fix);
}
/*
- Item_cond_and do not need fix_fields for execution, its parameters
+ Item_cond_or do not need fix_fields for execution, its parameters
are fixed or do not need fix_fields, too
*/
new_cond->quick_fix_field();
@@ -18316,7 +18898,8 @@ part_of_refkey(TABLE *table,Field *field)
@param order Sort order
@param table Table to sort
@param idx Index to check
- @param used_key_parts Return value for used key parts.
+ @param used_key_parts [out] NULL by default, otherwise return value for
+ used key parts.
@note
@@ -18335,13 +18918,14 @@ part_of_refkey(TABLE *table,Field *field)
*/
static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
- uint *used_key_parts)
+ uint *used_key_parts= NULL)
{
KEY_PART_INFO *key_part,*key_part_end;
key_part=table->key_info[idx].key_part;
key_part_end=key_part+table->key_info[idx].key_parts;
key_part_map const_key_parts=table->const_key_parts[idx];
int reverse=0;
+ uint key_parts;
my_bool on_pk_suffix= FALSE;
DBUG_ENTER("test_if_order_by_key");
@@ -18364,33 +18948,54 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
key as a suffix to the secondary keys. If it has continue to check
the primary key as a suffix.
*/
- if (!on_pk_suffix &&
+ if (!on_pk_suffix && (table->key_info[idx].ext_key_part_map & 1) &&
(table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) &&
table->s->primary_key != MAX_KEY &&
table->s->primary_key != idx)
{
+ KEY_PART_INFO *start,*end;
+ uint pk_part_idx= 0;
on_pk_suffix= TRUE;
- key_part= table->key_info[table->s->primary_key].key_part;
- key_part_end=key_part+table->key_info[table->s->primary_key].key_parts;
+ start= key_part= table->key_info[table->s->primary_key].key_part;
const_key_parts=table->const_key_parts[table->s->primary_key];
+ /*
+ Calculate true key_part_end and const_key_parts
+ (we have to stop as first not continous primary key part)
+ */
+ for (key_part_end= key_part,
+ end= key_part+table->key_info[table->s->primary_key].key_parts;
+ key_part_end < end; key_part_end++, pk_part_idx++)
+ {
+ /* Found hole in the pk_parts; Abort */
+ if (!(table->key_info[idx].ext_key_part_map &
+ (((key_part_map) 1) << pk_part_idx)))
+ break;
+ }
+
+ /* Adjust const_key_parts */
+ const_key_parts&= (((key_part_map) 1) << pk_part_idx) -1;
+
for (; const_key_parts & 1 ; const_key_parts>>= 1)
- key_part++;
+ key_part++;
/*
- The primary and secondary key parts were all const (i.e. there's
- one row). The sorting doesn't matter.
+ Test if the primary key parts were all const (i.e. there's one row).
+ The sorting doesn't matter.
*/
- if (key_part == key_part_end && reverse == 0)
+ if (key_part ==
+ start+table->key_info[table->s->primary_key].key_parts &&
+ reverse == 0)
{
- *used_key_parts= 0;
- DBUG_RETURN(1);
+ key_parts= 0;
+ reverse= 1; // Key is ok to use
+ goto ok;
}
}
else
DBUG_RETURN(0);
}
- if (key_part->field != field)
+ if (key_part->field != field || !field->part_of_sortkey.is_set(idx))
DBUG_RETURN(0);
/* set flag to 1 if we can use read-next on key, else to -1 */
@@ -18399,14 +19004,15 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
if (reverse && flag != reverse)
DBUG_RETURN(0);
reverse=flag; // Remember if reverse
- key_part++;
+ if (key_part < key_part_end)
+ key_part++;
}
if (on_pk_suffix)
{
uint used_key_parts_secondary= table->key_info[idx].key_parts;
uint used_key_parts_pk=
(uint) (key_part - table->key_info[table->s->primary_key].key_part);
- *used_key_parts= used_key_parts_pk + used_key_parts_secondary;
+ key_parts= used_key_parts_pk + used_key_parts_secondary;
if (reverse == -1 &&
(!(table->file->index_flags(idx, used_key_parts_secondary - 1, 1) &
@@ -18417,11 +19023,14 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
}
else
{
- *used_key_parts= (uint) (key_part - table->key_info[idx].key_part);
+ key_parts= (uint) (key_part - table->key_info[idx].key_part);
if (reverse == -1 &&
- !(table->file->index_flags(idx, *used_key_parts-1, 1) & HA_READ_PREV))
+ !(table->file->index_flags(idx, key_parts-1, 1) & HA_READ_PREV))
reverse= 0; // Index can't be used
}
+ok:
+ if (used_key_parts != NULL)
+ *used_key_parts= key_parts;
DBUG_RETURN(reverse);
}
@@ -18504,7 +19113,6 @@ test_if_subkey(ORDER *order, TABLE *table, uint ref, uint ref_key_parts,
uint nr;
uint min_length= (uint) ~0;
uint best= MAX_KEY;
- uint not_used;
KEY_PART_INFO *ref_key_part= table->key_info[ref].key_part;
KEY_PART_INFO *ref_key_part_end= ref_key_part + ref_key_parts;
@@ -18515,7 +19123,7 @@ test_if_subkey(ORDER *order, TABLE *table, uint ref, uint ref_key_parts,
table->key_info[nr].key_parts >= ref_key_parts &&
is_subkey(table->key_info[nr].key_part, ref_key_part,
ref_key_part_end) &&
- test_if_order_by_key(order, table, nr, &not_used))
+ test_if_order_by_key(order, table, nr))
{
min_length= table->key_info[nr].key_length;
best= nr;
@@ -18675,11 +19283,11 @@ find_field_in_item_list (Field *field, void *data)
*/
static bool
-test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit_arg,
+test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
bool no_changes, const key_map *map)
{
int ref_key;
- uint ref_key_parts;
+ uint UNINIT_VAR(ref_key_parts);
int order_direction= 0;
uint used_key_parts= 0;
TABLE *table=tab->table;
@@ -18690,12 +19298,8 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit_arg,
bool orig_cond_saved= false;
int best_key= -1;
bool changed_key= false;
- ha_rows best_select_limit;
DBUG_ENTER("test_if_skip_sort_order");
- LINT_INIT(ref_key_parts);
- LINT_INIT(best_select_limit);
-
/* Check that we are always called with first non-const table */
DBUG_ASSERT(tab == tab->join->join_tab + tab->join->const_tables);
@@ -18843,231 +19447,17 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit_arg,
goto check_reverse_order;
}
{
- /*
- Check whether there is an index compatible with the given order
- usage of which is cheaper than usage of the ref_key index (ref_key>=0)
- or a table scan.
- It may be the case if ORDER/GROUP BY is used with LIMIT.
- */
- uint nr;
- key_map keys;
- uint best_key_parts;
+ uint UNINIT_VAR(best_key_parts);
uint saved_best_key_parts= 0;
- int best_key_direction;
- ha_rows best_records;
- double read_time;
- bool is_best_covering= FALSE;
- double fanout= 1;
+ int best_key_direction= 0;
JOIN *join= tab->join;
- uint tablenr= tab - join->join_tab;
ha_rows table_records= table->file->stats.records;
- bool group= join->group && order == join->group_list;
- ha_rows ref_key_quick_rows= HA_POS_ERROR;
- LINT_INIT(best_key_parts);
- LINT_INIT(best_key_direction);
- LINT_INIT(best_records);
-
- /*
- If not used with LIMIT, only use keys if the whole query can be
- resolved with a key; This is because filesort() is usually faster than
- retrieving all rows through an index.
- */
- if (select_limit_arg >= table_records)
- {
- keys= *table->file->keys_to_use_for_scanning();
- keys.merge(table->covering_keys);
-
- /*
- We are adding here also the index specified in FORCE INDEX clause,
- if any.
- This is to allow users to use index in ORDER BY.
- */
- if (table->force_index)
- keys.merge(group ? table->keys_in_use_for_group_by :
- table->keys_in_use_for_order_by);
- keys.intersect(usable_keys);
- }
- else
- keys= usable_keys;
- if (ref_key >= 0 && ref_key != MAX_KEY &&
- table->covering_keys.is_set(ref_key))
- ref_key_quick_rows= table->quick_rows[ref_key];
-
- read_time= join->best_positions[tablenr].read_time;
- for (uint i= tablenr+1; i < join->table_count; i++)
- fanout*= join->best_positions[i].records_read; // fanout is always >= 1
-
- for (nr=0; nr < table->s->keys ; nr++)
- {
- int direction;
- ha_rows select_limit= select_limit_arg;
-
- if (keys.is_set(nr) &&
- (direction= test_if_order_by_key(order, table, nr, &used_key_parts)))
- {
- /*
- At this point we are sure that ref_key is a non-ordering
- key (where "ordering key" is a key that will return rows
- in the order required by ORDER BY).
- */
- DBUG_ASSERT (ref_key != (int) nr);
-
- bool is_covering= (table->covering_keys.is_set(nr) ||
- (table->file->index_flags(nr, 0, 1) &
- HA_CLUSTERED_INDEX));
-
- /*
- Don't use an index scan with ORDER BY without limit.
- For GROUP BY without limit always use index scan
- if there is a suitable index.
- Why we hold to this asymmetry hardly can be explained
- rationally. It's easy to demonstrate that using
- temporary table + filesort could be cheaper for grouping
- queries too.
- */
- if (is_covering ||
- select_limit != HA_POS_ERROR ||
- (ref_key < 0 && (group || table->force_index)))
- {
- double rec_per_key;
- double index_scan_time;
- KEY *keyinfo= tab->table->key_info+nr;
- if (select_limit == HA_POS_ERROR)
- select_limit= table_records;
- if (group)
- {
- /*
- Used_key_parts can be larger than keyinfo->key_parts
- when using a secondary index clustered with a primary
- key (e.g. as in Innodb).
- See Bug #28591 for details.
- */
- uint used_index_parts= keyinfo->key_parts;
- uint used_pk_parts= 0;
- if (used_key_parts > used_index_parts)
- used_pk_parts= used_key_parts-used_index_parts;
- rec_per_key= used_key_parts ?
- keyinfo->rec_per_key[used_key_parts-1] : 1;
- /* Take into account the selectivity of the used pk prefix */
- if (used_pk_parts)
- {
- KEY *pkinfo=tab->table->key_info+table->s->primary_key;
- /*
- If the values of of records per key for the prefixes
- of the primary key are considered unknown we assume
- they are equal to 1.
- */
- if (used_key_parts == pkinfo->key_parts ||
- pkinfo->rec_per_key[0] == 0)
- rec_per_key= 1;
- if (rec_per_key > 1)
- {
- rec_per_key*= pkinfo->rec_per_key[used_pk_parts-1];
- rec_per_key/= pkinfo->rec_per_key[0];
- /*
- The value of rec_per_key for the extended key has
- to be adjusted accordingly if some components of
- the secondary key are included in the primary key.
- */
- for(uint i= 0; i < used_pk_parts; i++)
- {
- if (pkinfo->key_part[i].field->key_start.is_set(nr))
- {
- /*
- We presume here that for any index rec_per_key[i] != 0
- if rec_per_key[0] != 0.
- */
- DBUG_ASSERT(pkinfo->rec_per_key[i]);
- rec_per_key*= pkinfo->rec_per_key[i-1];
- rec_per_key/= pkinfo->rec_per_key[i];
- }
- }
- }
- }
- set_if_bigger(rec_per_key, 1);
- /*
- With a grouping query each group containing on average
- rec_per_key records produces only one row that will
- be included into the result set.
- */
- if (select_limit > table_records/rec_per_key)
- select_limit= table_records;
- else
- select_limit= (ha_rows) (select_limit*rec_per_key);
- } /* group */
-
- /*
- If tab=tk is not the last joined table tn then to get first
- L records from the result set we can expect to retrieve
- only L/fanout(tk,tn) where fanout(tk,tn) says how many
- rows in the record set on average will match each row tk.
- Usually our estimates for fanouts are too pessimistic.
- So the estimate for L/fanout(tk,tn) will be too optimistic
- and as result we'll choose an index scan when using ref/range
- access + filesort will be cheaper.
- */
- if (select_limit_arg != HA_POS_ERROR)
- select_limit= (ha_rows) (select_limit < fanout ?
- 1 : select_limit/fanout);
- /*
- We assume that each of the tested indexes is not correlated
- with ref_key. Thus, to select first N records we have to scan
- N/selectivity(ref_key) index entries.
- selectivity(ref_key) = #scanned_records/#table_records =
- table->quick_condition_rows/table_records.
- In any case we can't select more than #table_records.
- N/(table->quick_condition_rows/table_records) > table_records
- <=> N > table->quick_condition_rows.
- */
- if (select_limit > table->quick_condition_rows)
- select_limit= table_records;
- else
- select_limit= (ha_rows) (select_limit *
- (double) table_records /
- table->quick_condition_rows);
- rec_per_key= keyinfo->rec_per_key[keyinfo->key_parts-1];
- set_if_bigger(rec_per_key, 1);
- /*
- Here we take into account the fact that rows are
- accessed in sequences rec_per_key records in each.
- Rows in such a sequence are supposed to be ordered
- by rowid/primary key. When reading the data
- in a sequence we'll touch not more pages than the
- table file contains.
- TODO. Use the formula for a disk sweep sequential access
- to calculate the cost of accessing data rows for one
- index entry.
- */
- index_scan_time= select_limit/rec_per_key *
- min(rec_per_key, table->file->scan_time());
- if ((ref_key < 0 && (group || table->force_index || is_covering)) ||
- index_scan_time < read_time)
- {
- ha_rows quick_records= table_records;
- if ((is_best_covering && !is_covering) ||
- (is_covering && ref_key_quick_rows < select_limit))
- continue;
- if (table->quick_keys.is_set(nr))
- quick_records= table->quick_rows[nr];
- if (best_key < 0 ||
- (select_limit <= min(quick_records,best_records) ?
- keyinfo->key_parts < best_key_parts :
- quick_records < best_records) ||
- (!is_best_covering && is_covering))
- {
- best_key= nr;
- best_key_parts= keyinfo->key_parts;
- saved_best_key_parts= used_key_parts;
- best_records= quick_records;
- is_best_covering= is_covering;
- best_key_direction= direction;
- best_select_limit= select_limit;
- }
- }
- }
- }
- }
+ test_if_cheaper_ordering(tab, order, table, usable_keys,
+ ref_key, select_limit,
+ &best_key, &best_key_direction,
+ &select_limit, &best_key_parts,
+ &saved_best_key_parts);
/*
filesort() and join cache are usually faster than reading in
@@ -19075,7 +19465,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit_arg,
index is clustered key.
*/
if (best_key < 0 ||
- ((select_limit_arg >= table_records) &&
+ ((select_limit >= table_records) &&
(tab->type == JT_ALL &&
tab->join->table_count > tab->join->const_tables + 1) &&
!(table->file->index_flags(best_key, 0, 1) & HA_CLUSTERED_INDEX)))
@@ -19171,13 +19561,15 @@ check_reverse_order:
orig_cond= 0;
orig_cond_saved= false;
}
+
table->file->ha_index_or_rnd_end();
if (tab->join->select_options & SELECT_DESCRIBE)
{
tab->ref.key= -1;
tab->ref.key_parts= 0;
- if (best_select_limit < table->file->stats.records)
- tab->limit= best_select_limit;
+ if (select_limit < table->file->stats.records)
+ tab->limit= select_limit;
+ table->disable_keyread();
}
}
else if (tab->type != JT_ALL || tab->select->quick)
@@ -19215,18 +19607,10 @@ check_reverse_order:
{
if (select && select->quick)
{
- QUICK_SELECT_DESC *tmp;
- bool error= FALSE;
-
/* ORDER BY range_key DESC */
- tmp= new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick),
- used_key_parts, &error);
- if (tmp && select->quick == save_quick)
- save_quick= 0; // ::QUICK_SELECT_DESC consumed it
-
- if (!tmp || error)
+ QUICK_SELECT_I *tmp= select->quick->make_reverse(used_key_parts);
+ if (!tmp)
{
- delete tmp;
tab->limit= 0;
goto use_filesort; // Reverse sort failed -> filesort
}
@@ -19238,8 +19622,9 @@ check_reverse_order:
tab->set_cond(tab->select->pre_idx_push_select_cond);
tab->table->file->cancel_pushed_idx_cond();
}
-
- select->quick= tmp;
+ if (select->quick == save_quick)
+ save_quick= 0; // make_reverse() consumed it
+ select->set_quick(tmp);
}
else if (tab->type != JT_NEXT && tab->type != JT_REF_OR_NULL &&
tab->ref.key >= 0 && tab->ref.key_parts <= used_key_parts)
@@ -19307,7 +19692,7 @@ use_filesort:
SYNOPSIS
create_sort_index()
thd Thread handler
- tab Table to sort (in join structure)
+ join Join with table to sort
order How table should be sorted
filesort_limit Max number of rows that needs to be sorted
select_limit Max number of rows in final output
@@ -19317,8 +19702,8 @@ use_filesort:
IMPLEMENTATION
- - If there is an index that can be used, 'tab' is modified to use
- this index.
+ - If there is an index that can be used, the first non-const join_tab in
+ 'join' is modified to use this index.
- If no index, create with filesort() an index file that can be used to
retrieve rows in order (should be done with 'read_record').
The sorted data is stored in tab->table and will be freed when calling
@@ -19340,6 +19725,8 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
TABLE *table;
SQL_SELECT *select;
JOIN_TAB *tab;
+ int err= 0;
+ bool quick_created= FALSE;
DBUG_ENTER("create_sort_index");
if (join->table_count == join->const_tables)
@@ -19347,18 +19734,61 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
tab= join->join_tab + join->const_tables;
table= tab->table;
select= tab->select;
+
+ JOIN_TAB *save_pre_sort_join_tab= NULL;
+ if (join->pre_sort_join_tab)
+ {
+ /*
+ we've already been in this function, and stashed away the original access
+ method in join->pre_sort_join_tab, restore it now.
+ */
+
+ /* First, restore state of the handler */
+ if (join->pre_sort_index != MAX_KEY)
+ {
+ if (table->file->ha_index_or_rnd_end())
+ goto err;
+ if (join->pre_sort_idx_pushed_cond)
+ {
+ table->file->idx_cond_push(join->pre_sort_index,
+ join->pre_sort_idx_pushed_cond);
+ }
+ }
+ else
+ {
+ if (table->file->ha_index_or_rnd_end() ||
+ table->file->ha_rnd_init(TRUE))
+ goto err;
+ }
+
+ /* Second, restore access method parameters */
+ tab->records= join->pre_sort_join_tab->records;
+ tab->select= join->pre_sort_join_tab->select;
+ tab->select_cond= join->pre_sort_join_tab->select_cond;
+ tab->type= join->pre_sort_join_tab->type;
+ tab->read_first_record= join->pre_sort_join_tab->read_first_record;
+
+ save_pre_sort_join_tab= join->pre_sort_join_tab;
+ join->pre_sort_join_tab= NULL;
+ }
+ else
+ {
+ /*
+ Save index #, save index condition. Do it right now, because MRR may
+ */
+ if (table->file->inited == handler::INDEX)
+ {
+ join->pre_sort_index= table->file->active_index;
+ join->pre_sort_idx_pushed_cond= table->file->pushed_idx_cond;
+ // no need to save key_read
+ }
+ else
+ join->pre_sort_index= MAX_KEY;
+ }
/* Currently ORDER BY ... LIMIT is not supported in subqueries. */
DBUG_ASSERT(join->group_list || !join->is_in_subquery());
- /*
- If we have a select->quick object that is created outside of
- create_sort_index() and this is part of a subquery that
- potentially can be executed multiple times then we should not
- delete the quick object on exit from this function.
- */
- bool keep_quick= select && select->quick && join->join_tab_save;
-
/*
When there is SQL_BIG_RESULT do not sort using index for GROUP BY,
and thus force sorting on disk unless a group min-max optimization
@@ -19413,7 +19843,7 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
get_quick_select_for_ref(thd, table, &tab->ref,
tab->found_records))))
goto err;
- DBUG_ASSERT(!keep_quick);
+ quick_created= TRUE;
}
}
@@ -19427,7 +19857,27 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
table->sort.found_records=filesort(thd, table,join->sortorder, length,
select, filesort_limit, 0,
&examined_rows);
+
+ if (quick_created)
+ {
+ /* This will delete the quick select. */
+ select->cleanup();
+ }
+
+ if (!join->pre_sort_join_tab)
+ {
+ if (save_pre_sort_join_tab)
+ join->pre_sort_join_tab= save_pre_sort_join_tab;
+ else if (!(join->pre_sort_join_tab= (JOIN_TAB*)thd->alloc(sizeof(JOIN_TAB))))
+ goto err;
+ }
+
+ *(join->pre_sort_join_tab)= *tab;
+
+ /*TODO: here, close the index scan, cancel index-only read. */
tab->records= table->sort.found_records; // For SQL_CALC_ROWS
+#if 0
+ /* MariaDB doesn't need the following: */
if (select)
{
/*
@@ -19443,6 +19893,18 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
tablesort_result_cache= table->sort.io_cache;
table->sort.io_cache= NULL;
+
+ if (select->quick &&
+ select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
+ {
+ tab->filesort_used_loose_index_scan= true;
+
+ QUICK_GROUP_MIN_MAX_SELECT *minmax_quick=
+ static_cast<QUICK_GROUP_MIN_MAX_SELECT*>(select->quick);
+ if (minmax_quick->is_agg_distinct())
+ tab->filesort_used_loose_index_scan_agg_distinct= true;
+ }
+
/*
If a quick object was created outside of create_sort_index()
that might be reused, then do not call select->cleanup() since
@@ -19451,72 +19913,85 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
if (!keep_quick)
{
select->cleanup();
- /*
- The select object should now be ready for the next use. If it
- is re-used then there exists a backup copy of this join tab
- which has the pointer to it. The join tab will be restored in
- JOIN::reset(). So here we just delete the pointer to it.
- */
- tab->select= NULL;
- // If we deleted the quick select object we need to clear quick_keys
+
+ // If we deleted the quick object we need to clear quick_keys
table->quick_keys.clear_all();
+ table->intersect_keys.clear_all();
}
+ else
+ {
+ // Need to close the index scan in order to re-use the handler
+ tab->select->quick->range_end();
+ }
+
+ /*
+ The select object is now ready for the next use. To avoid that
+ the select object is used when reading the records in sorted
+ order we set the pointer to it to NULL. The select pointer will
+ be restored from the saved_select pointer when this select
+ operation is completed (@see JOIN::exec). This ensures that it
+ will be re-used when filesort is used by subqueries that are
+ executed multiple times.
+ */
+ tab->saved_select= tab->select;
+ tab->select= NULL;
+
// Restore the output resultset
table->sort.io_cache= tablesort_result_cache;
}
+#endif
+ tab->select=NULL;
tab->set_select_cond(NULL, __LINE__);
- tab->last_inner= 0;
- tab->first_unmatched= 0;
tab->type=JT_ALL; // Read with normal read_record
tab->read_first_record= join_init_read_record;
+ tab->table->file->ha_index_or_rnd_end();
+
+ if (err)
+ goto err;
+
tab->join->examined_rows+=examined_rows;
- table->disable_keyread(); // Restore if we used indexes
DBUG_RETURN(table->sort.found_records == HA_POS_ERROR);
err:
DBUG_RETURN(-1);
}
-#ifdef NOT_YET
-/**
- Add the HAVING criteria to table->select.
-*/
-static bool fix_having(JOIN *join, Item **having)
+void JOIN::clean_pre_sort_join_tab()
{
- (*having)->update_used_tables(); // Some tables may have been const
- JOIN_TAB *table=&join->join_tab[join->const_tables];
- table_map used_tables= join->const_table_map | table->table->map;
+ //TABLE *table= pre_sort_join_tab->table;
+ /*
+ Note: we can come here for fake_select_lex object. That object will have
+ the table already deleted by st_select_lex_unit::cleanup().
+ We rely on that fake_select_lex didn't have quick select.
+ */
+#if 0
+ if (pre_sort_join_tab->select && pre_sort_join_tab->select->quick)
+ {
+ /*
+ We need to preserve tablesort's output resultset here, because
+ QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT (called by
+ SQL_SELECT::cleanup()) may free it assuming it's the result of the quick
+ select operation that we no longer need. Note that all the other parts of
+ this data structure are cleaned up when
+ QUICK_INDEX_MERGE_SELECT::get_next encounters end of data, so the next
+ SQL_SELECT::cleanup() call changes sort.io_cache alone.
+ */
+ IO_CACHE *tablesort_result_cache;
- DBUG_EXECUTE("where",print_where(*having,"having", QT_ORDINARY););
- Item* sort_table_cond= make_cond_for_table(join->thd, *having, used_tables,
- used_tables, MAX_TABLES,
- FALSE, FALSE);
- if (sort_table_cond)
+ tablesort_result_cache= table->sort.io_cache;
+ table->sort.io_cache= NULL;
+ pre_sort_join_tab->select->cleanup();
+ table->quick_keys.clear_all(); // as far as we cleanup select->quick
+ table->intersect_keys.clear_all();
+ table->sort.io_cache= tablesort_result_cache;
+ }
+#endif
+ //table->disable_keyread(); // Restore if we used indexes
+ if (pre_sort_join_tab->select && pre_sort_join_tab->select->quick)
{
- if (!table->select)
- if (!(table->select=new SQL_SELECT))
- return 1;
- if (!table->select->cond)
- table->select->cond=sort_table_cond;
- else // This should never happen
- if (!(table->select->cond= new Item_cond_and(table->select->cond,
- sort_table_cond)) ||
- table->select->cond->fix_fields(join->thd, &table->select->cond))
- return 1;
- table->set_select_cond(table->select->cond, __LINE__);
- table->select_cond->top_level_item();
- DBUG_EXECUTE("where",print_where(table->select_cond,
- "select and having",
- QT_ORDINARY););
- *having= make_cond_for_table(join->thd, *having,
- ~ (table_map) 0,~used_tables,
- MAX_TABLES, FALSE, FALSE);
- DBUG_EXECUTE("where",
- print_where(*having,"having after make_cond", QT_ORDINARY););
+ pre_sort_join_tab->select->cleanup();
}
- return 0;
}
-#endif
/**
@@ -19570,6 +20045,7 @@ remove_duplicates(JOIN *join, TABLE *table, List<Item> &fields, Item *having)
ulong keylength= 0;
uint field_count;
THD *thd= join->thd;
+
DBUG_ENTER("remove_duplicates");
table->reginfo.lock_type=TL_WRITE;
@@ -19594,6 +20070,14 @@ remove_duplicates(JOIN *join, TABLE *table, List<Item> &fields, Item *having)
for (Field **ptr=first_field; *ptr; ptr++)
keylength+= (*ptr)->sort_length() + (*ptr)->maybe_null();
+ /*
+ Disable LIMIT ROWS EXAMINED in order to avoid interrupting prematurely
+ duplicate removal, and produce a possibly incomplete query result.
+ */
+ thd->lex->limit_rows_examined_cnt= ULONGLONG_MAX;
+ if (thd->killed == ABORT_QUERY)
+ thd->reset_killed();
+
free_io_cache(table); // Safety
table->file->info(HA_STATUS_VARIABLE);
if (table->s->db_type() == heap_hton ||
@@ -19605,6 +20089,8 @@ remove_duplicates(JOIN *join, TABLE *table, List<Item> &fields, Item *having)
else
error=remove_dup_with_compare(join->thd, table, first_field, having);
+ if (join->select_lex != join->select_lex->master_unit()->fake_select_lex)
+ thd->lex->set_limit_rows_examined();
free_blobs(first_field);
DBUG_RETURN(error);
}
@@ -19650,7 +20136,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
}
if (copy_blobs(first_field))
{
- my_message(ER_OUTOFMEMORY, ER(ER_OUTOFMEMORY), MYF(0));
+ my_message(ER_OUTOFMEMORY, ER(ER_OUTOFMEMORY), MYF(ME_FATALERROR));
error=0;
goto err;
}
@@ -19731,10 +20217,10 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
for (ptr= first_field, field_length=field_lengths ; *ptr ; ptr++)
(*field_length++)= (*ptr)->sort_length();
- if (hash_init(&hash, &my_charset_bin, (uint) file->stats.records, 0,
- key_length, (hash_get_key) 0, 0, 0))
+ if (my_hash_init(&hash, &my_charset_bin, (uint) file->stats.records, 0,
+ key_length, (my_hash_get_key) 0, 0, 0))
{
- my_free((char*) key_buffer,MYF(0));
+ my_free(key_buffer);
DBUG_RETURN(1);
}
@@ -19788,15 +20274,15 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
}
key_pos+=extra_length;
}
- my_free((char*) key_buffer,MYF(0));
- hash_free(&hash);
+ my_free(key_buffer);
+ my_hash_free(&hash);
file->extra(HA_EXTRA_NO_CACHE);
(void) file->ha_rnd_end();
DBUG_RETURN(0);
err:
- my_free((char*) key_buffer,MYF(0));
- hash_free(&hash);
+ my_free(key_buffer);
+ my_hash_free(&hash);
file->extra(HA_EXTRA_NO_CACHE);
(void) file->ha_rnd_end();
if (error)
@@ -19844,7 +20330,6 @@ SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length,
}
-
/*
eq_ref: Create the lookup key and check if it is the same as saved key
@@ -19979,7 +20464,7 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
order_item->full_name(), thd->where);
return TRUE;
}
- order->item= ref_pointer_array + count - 1;
+ thd->change_item_tree((Item**)&order->item, (Item*)(ref_pointer_array + count - 1));
order->in_field_list= 1;
order->counter= count;
order->counter_used= 1;
@@ -20012,7 +20497,7 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
order_item_type == Item::REF_ITEM)
{
from_field= find_field_in_tables(thd, (Item_ident*) order_item, tables,
- NULL, &view_ref, IGNORE_ERRORS, TRUE,
+ NULL, &view_ref, IGNORE_ERRORS, FALSE,
FALSE);
if (!from_field)
from_field= (Field*) not_found_field;
@@ -20073,7 +20558,7 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
if (!order_item->fixed &&
(order_item->fix_fields(thd, order->item) ||
(order_item= *order->item)->check_cols(1) ||
- thd->is_fatal_error))
+ thd->is_error()))
return TRUE; /* Wrong field. */
uint el= all_fields.elements;
@@ -20266,7 +20751,7 @@ setup_new_fields(THD *thd, List<Item> &fields,
optimize away 'order by'.
*/
-static ORDER *
+ORDER *
create_distinct_group(THD *thd, Item **ref_pointer_array,
ORDER *order_list, List<Item> &fields,
List<Item> &all_fields,
@@ -20428,7 +20913,8 @@ test_if_subpart(ORDER *a,ORDER *b)
*/
static TABLE *
-get_sort_by_table(ORDER *a,ORDER *b, List<TABLE_LIST> &tables)
+get_sort_by_table(ORDER *a,ORDER *b, List<TABLE_LIST> &tables,
+ table_map const_tables)
{
TABLE_LIST *table;
List_iterator<TABLE_LIST> ti(tables);
@@ -20442,6 +20928,23 @@ get_sort_by_table(ORDER *a,ORDER *b, List<TABLE_LIST> &tables)
for (; a && b; a=a->next,b=b->next)
{
+ /* Skip elements of a that are constant */
+ while (!((*a->item)->used_tables() & ~const_tables))
+ {
+ if (!(a= a->next))
+ break;
+ }
+
+ /* Skip elements of b that are constant */
+ while (!((*b->item)->used_tables() & ~const_tables))
+ {
+ if (!(b= b->next))
+ break;
+ }
+
+ if (!a || !b)
+ break;
+
if (!(*a->item)->eq(*b->item,1))
DBUG_RETURN(0);
map|=a->item[0]->used_tables();
@@ -20532,8 +21035,7 @@ calc_group_buffer(JOIN *join,ORDER *group)
default:
/* This case should never be choosen */
DBUG_ASSERT(0);
- my_error(ER_OUT_OF_RESOURCES, MYF(0));
- join->thd->fatal_error();
+ my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
}
}
parts++;
@@ -20657,7 +21159,7 @@ test_if_group_changed(List<Cached_item> &list)
Only FIELD_ITEM:s and FUNC_ITEM:s needs to be saved between groups.
Change old item_field to use a new field with points at saved fieldvalue
- This function is only called before use of send_fields.
+ This function is only called before use of send_result_set_metadata.
@param thd THD pointer
@param param temporary table parameters
@@ -20688,7 +21190,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
Item *pos;
List_iterator_fast<Item> li(all_fields);
Copy_field *copy= NULL;
- IF_DBUG(Copy_field *copy_start);
+ Copy_field *copy_start __attribute__((unused));
res_selected_fields.empty();
res_all_fields.empty();
List_iterator_fast<Item> itr(res_all_fields);
@@ -20701,7 +21203,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
goto err2;
param->copy_funcs.empty();
- IF_DBUG(copy_start= copy);
+ copy_start= copy;
for (i= 0; (pos= li++); i++)
{
Field *field;
@@ -20899,7 +21401,7 @@ bool JOIN::alloc_func_list()
Initialize 'sum_funcs' array with all Item_sum objects.
@param field_list All items
- @param send_fields Items in select list
+ @param send_result_set_metadata Items in select list
@param before_group_by Set to 1 if this is called before GROUP BY handling
@param recompute Set to TRUE if sum_funcs must be recomputed
@@ -20909,7 +21411,7 @@ bool JOIN::alloc_func_list()
1 error
*/
-bool JOIN::make_sum_func_list(List<Item> &field_list, List<Item> &send_fields,
+bool JOIN::make_sum_func_list(List<Item> &field_list, List<Item> &send_result_set_metadata,
bool before_group_by, bool recompute)
{
List_iterator_fast<Item> it(field_list);
@@ -20931,7 +21433,7 @@ bool JOIN::make_sum_func_list(List<Item> &field_list, List<Item> &send_fields,
if (before_group_by && rollup.state == ROLLUP::STATE_INITED)
{
rollup.state= ROLLUP::STATE_READY;
- if (rollup_make_fields(field_list, send_fields, &func))
+ if (rollup_make_fields(field_list, send_result_set_metadata, &func))
DBUG_RETURN(TRUE); // Should never happen
}
else if (rollup.state == ROLLUP::STATE_NONE)
@@ -21001,11 +21503,11 @@ change_to_use_tmp_fields(THD *thd, Item **ref_pointer_array,
if (!suv || !new_field)
DBUG_RETURN(true); // Fatal error
/*
- We are replacing the argument of Item_func_set_user_var after its value
- has been read. The argument's null_value should be set by now, so we
- must set it explicitly for the replacement argument since the null_value
- may be read without any preceeding call to val_*().
- */
+ We are replacing the argument of Item_func_set_user_var after its value
+ has been read. The argument's null_value should be set by now, so we
+ must set it explicitly for the replacement argument since the null_value
+ may be read without any preceeding call to val_*().
+ */
new_field->update_null_value();
List<Item> list;
list.push_back(new_field);
@@ -21131,7 +21633,22 @@ static bool setup_sum_funcs(THD *thd, Item_sum **func_ptr)
DBUG_ENTER("setup_sum_funcs");
while ((func= *(func_ptr++)))
{
- if (func->setup(thd))
+ if (func->aggregator_setup(thd))
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+static bool prepare_sum_aggregators(Item_sum **func_ptr, bool need_distinct)
+{
+ Item_sum *func;
+ DBUG_ENTER("prepare_sum_aggregators");
+ while ((func= *(func_ptr++)))
+ {
+ if (func->set_aggregator(need_distinct && func->has_with_distinct() ?
+ Aggregator::DISTINCT_AGGREGATOR :
+ Aggregator::SIMPLE_AGGREGATOR))
DBUG_RETURN(TRUE);
}
DBUG_RETURN(FALSE);
@@ -21175,13 +21692,13 @@ init_sum_functions(Item_sum **func_ptr, Item_sum **end_ptr)
{
for (; func_ptr != end_ptr ;func_ptr++)
{
- if ((*func_ptr)->reset())
+ if ((*func_ptr)->reset_and_add())
return 1;
}
/* If rollup, calculate the upper sum levels */
for ( ; *func_ptr ; func_ptr++)
{
- if ((*func_ptr)->add())
+ if ((*func_ptr)->aggregator_add())
return 1;
}
return 0;
@@ -21193,7 +21710,7 @@ update_sum_func(Item_sum **func_ptr)
{
Item_sum *func;
for (; (func= (Item_sum*) *func_ptr) ; func_ptr++)
- if (func->add())
+ if (func->aggregator_add())
return 1;
return 0;
}
@@ -21749,7 +22266,7 @@ int JOIN::rollup_write_data(uint idx, TABLE *table_arg)
if (create_internal_tmp_table_from_heap(thd, table_arg,
tmp_table_param.start_recinfo,
&tmp_table_param.recinfo,
- write_error, 0))
+ write_error, 0, NULL))
return 1;
}
}
@@ -21900,8 +22417,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
bool printing_materialize_nest= FALSE;
uint select_id= join->select_lex->select_number;
- for (JOIN_TAB *tab= first_breadth_first_tab(join); tab;
- tab= next_breadth_first_tab(join, tab))
+ for (JOIN_TAB *tab= first_breadth_first_tab(join, WALK_OPTIMIZATION_TABS); tab;
+ tab= next_breadth_first_tab(join, WALK_OPTIMIZATION_TABS, tab))
{
if (tab->bush_root_tab)
{
@@ -21983,10 +22500,9 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
}
else
{
- TABLE_LIST *real_table= table->pos_in_table_list;
+ TABLE_LIST *real_table= table->pos_in_table_list;
item_list.push_back(new Item_string(real_table->alias,
- strlen(real_table->alias),
- cs));
+ strlen(real_table->alias), cs));
}
/* "partitions" column */
if (join->thd->lex->describe & DESCRIBE_PARTITIONS)
@@ -22139,32 +22655,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
}
else
{
- ha_rows examined_rows;
- if (tab->select && tab->select->quick)
- examined_rows= tab->select->quick->records;
- else if (tab->type == JT_NEXT || tab->type == JT_ALL || is_hj)
- {
- if (tab->limit)
- examined_rows= tab->limit;
- else
- {
- if (tab->table->is_filled_at_execution())
- {
- examined_rows= tab->records;
- }
- else
- {
- /*
- handler->info(HA_STATUS_VARIABLE) has been called in
- make_join_statistics()
- */
- examined_rows= tab->table->file->stats.records;
- }
- }
- }
- else
- examined_rows=(ha_rows)tab->records_read;
-
+ ha_rows examined_rows= tab->get_examined_rows();
+
item_list.push_back(new Item_int((longlong) (ulonglong) examined_rows,
MY_INT64_NUM_DECIMAL_DIGITS));
@@ -22245,7 +22737,11 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
{
const COND *pushed_cond= tab->table->file->pushed_cond;
- if (thd->variables.engine_condition_pushdown && pushed_cond)
+ if (((thd->variables.optimizer_switch &
+ OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN) ||
+ (tab->table->file->ha_table_flags() &
+ HA_MUST_USE_TABLE_CONDITION_PUSHDOWN)) &&
+ pushed_cond)
{
extra.append(STRING_WITH_LEN("; Using where with pushed "
"condition"));
@@ -22281,7 +22777,12 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
if (key_read)
{
if (quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
+ {
+ QUICK_GROUP_MIN_MAX_SELECT *qgs=
+ (QUICK_GROUP_MIN_MAX_SELECT *) tab->select->quick;
extra.append(STRING_WITH_LEN("; Using index for group-by"));
+ qgs->append_loose_scan_type(&extra);
+ }
else
extra.append(STRING_WITH_LEN("; Using index"));
}
@@ -22458,7 +22959,7 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
first->group_list.first,
first->having,
thd->lex->proc_list.first,
- first->options | thd->options | SELECT_DESCRIBE,
+ first->options | thd->variables.option_bits | SELECT_DESCRIBE,
result, unit, first);
}
DBUG_RETURN(res || thd->is_error());
@@ -22487,6 +22988,8 @@ static void print_table_array(THD *thd,
(curr->nested_join && !(curr->nested_join->used_tables &
~eliminated_tables))))
{
+ /* as of 5.5, print_join doesnt put eliminated elements into array */
+ DBUG_ASSERT(0);
continue;
}
@@ -22512,6 +23015,21 @@ static void print_table_array(THD *thd,
}
+/*
+ Check if the passed table is
+ - a base table which was eliminated, or
+ - a join nest which only contained eliminated tables (and so was eliminated,
+ too)
+*/
+
+static bool is_eliminated_table(table_map eliminated_tables, TABLE_LIST *tbl)
+{
+ return eliminated_tables &&
+ ((tbl->table && (tbl->table->map & eliminated_tables)) ||
+ (tbl->nested_join && !(tbl->nested_join->used_tables &
+ ~eliminated_tables)));
+}
+
/**
Print joins from the FROM clause.
@@ -22529,13 +23047,36 @@ static void print_join(THD *thd,
{
/* List is reversed => we should reverse it before using */
List_iterator_fast<TABLE_LIST> ti(*tables);
- TABLE_LIST **table= (TABLE_LIST **)thd->alloc(sizeof(TABLE_LIST*) *
- tables->elements);
- if (table == 0)
+ TABLE_LIST **table;
+ uint non_const_tables= 0;
+
+ for (TABLE_LIST *t= ti++; t ; t= ti++)
+ {
+ /*
+ See comment in print_table_array() about the second part of the
+ condition
+ */
+ if (!t->optimized_away && !is_eliminated_table(eliminated_tables, t))
+ non_const_tables++;
+ }
+ if (!non_const_tables)
+ {
+ str->append(STRING_WITH_LEN("dual"));
+ return; // all tables were optimized away
+ }
+ ti.rewind();
+
+ if (!(table= (TABLE_LIST **)thd->alloc(sizeof(TABLE_LIST*) *
+ non_const_tables)))
return; // out of memory
- for (TABLE_LIST **t= table + (tables->elements - 1); t >= table; t--)
- *t= ti++;
+ TABLE_LIST *tmp, **t= table + (non_const_tables - 1);
+ while ((tmp= ti++))
+ {
+ if (tmp->optimized_away || is_eliminated_table(eliminated_tables, tmp))
+ continue;
+ *t--= tmp;
+ }
DBUG_ASSERT(tables->elements >= 1);
/*
@@ -22552,7 +23093,7 @@ static void print_join(THD *thd,
*/
if ((*table)->sj_inner_tables)
{
- TABLE_LIST **end= table + tables->elements;
+ TABLE_LIST **end= table + non_const_tables;
for (TABLE_LIST **t2= table; t2!=end; t2++)
{
if (!(*t2)->sj_inner_tables)
@@ -22565,7 +23106,7 @@ static void print_join(THD *thd,
}
}
print_table_array(thd, eliminated_tables, str, table,
- table + tables->elements, query_type);
+ table + non_const_tables, query_type);
}
/**
@@ -22732,11 +23273,21 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
str->append(STRING_WITH_LEN("select "));
+ if (join && join->cleaned)
+ {
+ /*
+ JOIN already cleaned up so it is dangerous to print items
+ because temporary tables they pointed on could be freed.
+ */
+ str->append('#');
+ str->append(select_number);
+ return;
+ }
+
/* First add options */
if (options & SELECT_STRAIGHT_JOIN)
str->append(STRING_WITH_LEN("straight_join "));
- if ((thd->lex->lock_option == TL_READ_HIGH_PRIORITY) &&
- (this == &thd->lex->select_lex))
+ if (options & SELECT_HIGH_PRIORITY)
str->append(STRING_WITH_LEN("high_priority "));
if (options & SELECT_DISTINCT)
str->append(STRING_WITH_LEN("distinct "));
@@ -23107,5 +23658,426 @@ JOIN::reoptimize(Item *added_where, table_map join_tables,
/**
+ Cache constant expressions in WHERE, HAVING, ON conditions.
+*/
+
+void JOIN::cache_const_exprs()
+{
+ bool cache_flag= FALSE;
+ bool *analyzer_arg= &cache_flag;
+
+ /* No need in cache if all tables are constant. */
+ if (const_tables == table_count)
+ return;
+
+ if (conds)
+ conds->compile(&Item::cache_const_expr_analyzer, (uchar **)&analyzer_arg,
+ &Item::cache_const_expr_transformer, (uchar *)&cache_flag);
+ cache_flag= FALSE;
+ if (having)
+ having->compile(&Item::cache_const_expr_analyzer, (uchar **)&analyzer_arg,
+ &Item::cache_const_expr_transformer, (uchar *)&cache_flag);
+
+ for (JOIN_TAB *tab= first_depth_first_tab(this); tab;
+ tab= next_depth_first_tab(this, tab))
+ {
+ if (*tab->on_expr_ref)
+ {
+ cache_flag= FALSE;
+ (*tab->on_expr_ref)->compile(&Item::cache_const_expr_analyzer,
+ (uchar **)&analyzer_arg,
+ &Item::cache_const_expr_transformer,
+ (uchar *)&cache_flag);
+ }
+ }
+}
+
+
+/**
+ Find a cheaper access key than a given @a key
+
+ @param tab NULL or JOIN_TAB of the accessed table
+ @param order Linked list of ORDER BY arguments
+ @param table Table if tab == NULL or tab->table
+ @param usable_keys Key map to find a cheaper key in
+ @param ref_key
+ * 0 <= key < MAX_KEY - key number (hint) to start the search
+ * -1 - no key number provided
+ @param select_limit LIMIT value
+ @param [out] new_key Key number if success, otherwise undefined
+ @param [out] new_key_direction Return -1 (reverse) or +1 if success,
+ otherwise undefined
+ @param [out] new_select_limit Return adjusted LIMIT
+ @param [out] new_used_key_parts NULL by default, otherwise return number
+ of new_key prefix columns if success
+ or undefined if the function fails
+ @param [out] saved_best_key_parts NULL by default, otherwise preserve the
+ value for further use in QUICK_SELECT_DESC
+
+ @note
+ This function takes into account table->quick_condition_rows statistic
+ (that is calculated by the make_join_statistics function).
+ However, single table procedures such as mysql_update() and mysql_delete()
+ never call make_join_statistics, so they have to update it manually
+ (@see get_index_for_order()).
+*/
+
+static bool
+test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
+ key_map usable_keys, int ref_key,
+ ha_rows select_limit_arg,
+ int *new_key, int *new_key_direction,
+ ha_rows *new_select_limit, uint *new_used_key_parts,
+ uint *saved_best_key_parts)
+{
+ DBUG_ENTER("test_if_cheaper_ordering");
+ /*
+ Check whether there is an index compatible with the given order
+ usage of which is cheaper than usage of the ref_key index (ref_key>=0)
+ or a table scan.
+ It may be the case if ORDER/GROUP BY is used with LIMIT.
+ */
+ ha_rows best_select_limit= HA_POS_ERROR;
+ JOIN *join= tab ? tab->join : NULL;
+ uint nr;
+ key_map keys;
+ uint best_key_parts= 0;
+ int best_key_direction= 0;
+ ha_rows best_records= 0;
+ double read_time;
+ int best_key= -1;
+ bool is_best_covering= FALSE;
+ double fanout= 1;
+ ha_rows table_records= table->file->stats.records;
+ bool group= join && join->group && order == join->group_list;
+ ha_rows ref_key_quick_rows= HA_POS_ERROR;
+ const bool has_limit= (select_limit_arg != HA_POS_ERROR);
+
+ /*
+ If not used with LIMIT, only use keys if the whole query can be
+ resolved with a key; This is because filesort() is usually faster than
+ retrieving all rows through an index.
+ */
+ if (select_limit_arg >= table_records)
+ {
+ keys= *table->file->keys_to_use_for_scanning();
+ keys.merge(table->covering_keys);
+
+ /*
+ We are adding here also the index specified in FORCE INDEX clause,
+ if any.
+ This is to allow users to use index in ORDER BY.
+ */
+ if (table->force_index)
+ keys.merge(group ? table->keys_in_use_for_group_by :
+ table->keys_in_use_for_order_by);
+ keys.intersect(usable_keys);
+ }
+ else
+ keys= usable_keys;
+
+ if (ref_key >= 0 && ref_key != MAX_KEY &&
+ table->covering_keys.is_set(ref_key))
+ ref_key_quick_rows= table->quick_rows[ref_key];
+
+ if (join)
+ {
+ uint tablenr= tab - join->join_tab;
+ read_time= join->best_positions[tablenr].read_time;
+ for (uint i= tablenr+1; i < join->table_count; i++)
+ fanout*= join->best_positions[i].records_read; // fanout is always >= 1
+ }
+ else
+ read_time= table->file->scan_time();
+
+ for (nr=0; nr < table->s->keys ; nr++)
+ {
+ int direction;
+ ha_rows select_limit= select_limit_arg;
+ uint used_key_parts= 0;
+
+ if (keys.is_set(nr) &&
+ (direction= test_if_order_by_key(order, table, nr, &used_key_parts)))
+ {
+ /*
+ At this point we are sure that ref_key is a non-ordering
+ key (where "ordering key" is a key that will return rows
+ in the order required by ORDER BY).
+ */
+ DBUG_ASSERT (ref_key != (int) nr);
+
+ bool is_covering= (table->covering_keys.is_set(nr) ||
+ (table->file->index_flags(nr, 0, 1) &
+ HA_CLUSTERED_INDEX));
+ /*
+ Don't use an index scan with ORDER BY without limit.
+ For GROUP BY without limit always use index scan
+ if there is a suitable index.
+ Why we hold to this asymmetry hardly can be explained
+ rationally. It's easy to demonstrate that using
+ temporary table + filesort could be cheaper for grouping
+ queries too.
+ */
+ if (is_covering ||
+ select_limit != HA_POS_ERROR ||
+ (ref_key < 0 && (group || table->force_index)))
+ {
+ double rec_per_key;
+ double index_scan_time;
+ KEY *keyinfo= table->key_info+nr;
+ if (select_limit == HA_POS_ERROR)
+ select_limit= table_records;
+ if (group)
+ {
+ /*
+ Used_key_parts can be larger than keyinfo->key_parts
+ when using a secondary index clustered with a primary
+ key (e.g. as in Innodb).
+ See Bug #28591 for details.
+ */
+ uint used_index_parts= keyinfo->key_parts;
+ uint used_pk_parts= 0;
+ if (used_key_parts > used_index_parts)
+ used_pk_parts= used_key_parts-used_index_parts;
+ rec_per_key= used_key_parts ?
+ keyinfo->rec_per_key[used_key_parts-1] : 1;
+ /* Take into account the selectivity of the used pk prefix */
+ if (used_pk_parts)
+ {
+ KEY *pkinfo=tab->table->key_info+table->s->primary_key;
+ /*
+ If the values of of records per key for the prefixes
+ of the primary key are considered unknown we assume
+ they are equal to 1.
+ */
+ if (used_key_parts == pkinfo->key_parts ||
+ pkinfo->rec_per_key[0] == 0)
+ rec_per_key= 1;
+ if (rec_per_key > 1)
+ {
+ rec_per_key*= pkinfo->rec_per_key[used_pk_parts-1];
+ rec_per_key/= pkinfo->rec_per_key[0];
+ /*
+ The value of rec_per_key for the extended key has
+ to be adjusted accordingly if some components of
+ the secondary key are included in the primary key.
+ */
+ for(uint i= 0; i < used_pk_parts; i++)
+ {
+ if (pkinfo->key_part[i].field->key_start.is_set(nr))
+ {
+ /*
+ We presume here that for any index rec_per_key[i] != 0
+ if rec_per_key[0] != 0.
+ */
+ DBUG_ASSERT(pkinfo->rec_per_key[i]);
+ DBUG_ASSERT(i > 0);
+ rec_per_key*= pkinfo->rec_per_key[i-1];
+ rec_per_key/= pkinfo->rec_per_key[i];
+ }
+ }
+ }
+ }
+ set_if_bigger(rec_per_key, 1);
+ /*
+ With a grouping query each group containing on average
+ rec_per_key records produces only one row that will
+ be included into the result set.
+ */
+ if (select_limit > table_records/rec_per_key)
+ select_limit= table_records;
+ else
+ select_limit= (ha_rows) (select_limit*rec_per_key);
+ } /* group */
+
+ /*
+ If tab=tk is not the last joined table tn then to get first
+ L records from the result set we can expect to retrieve
+ only L/fanout(tk,tn) where fanout(tk,tn) says how many
+ rows in the record set on average will match each row tk.
+ Usually our estimates for fanouts are too pessimistic.
+ So the estimate for L/fanout(tk,tn) will be too optimistic
+ and as result we'll choose an index scan when using ref/range
+ access + filesort will be cheaper.
+ */
+ select_limit= (ha_rows) (select_limit < fanout ?
+ 1 : select_limit/fanout);
+ /*
+ We assume that each of the tested indexes is not correlated
+ with ref_key. Thus, to select first N records we have to scan
+ N/selectivity(ref_key) index entries.
+ selectivity(ref_key) = #scanned_records/#table_records =
+ table->quick_condition_rows/table_records.
+ In any case we can't select more than #table_records.
+ N/(table->quick_condition_rows/table_records) > table_records
+ <=> N > table->quick_condition_rows.
+ */
+ if (select_limit > table->quick_condition_rows)
+ select_limit= table_records;
+ else
+ select_limit= (ha_rows) (select_limit *
+ (double) table_records /
+ table->quick_condition_rows);
+ rec_per_key= keyinfo->rec_per_key[keyinfo->key_parts-1];
+ set_if_bigger(rec_per_key, 1);
+ /*
+ Here we take into account the fact that rows are
+ accessed in sequences rec_per_key records in each.
+ Rows in such a sequence are supposed to be ordered
+ by rowid/primary key. When reading the data
+ in a sequence we'll touch not more pages than the
+ table file contains.
+ TODO. Use the formula for a disk sweep sequential access
+ to calculate the cost of accessing data rows for one
+ index entry.
+ */
+ index_scan_time= select_limit/rec_per_key *
+ min(rec_per_key, table->file->scan_time());
+ if ((ref_key < 0 && (group || table->force_index || is_covering)) ||
+ index_scan_time < read_time)
+ {
+ ha_rows quick_records= table_records;
+ if ((is_best_covering && !is_covering) ||
+ (is_covering && ref_key_quick_rows < select_limit))
+ continue;
+ if (table->quick_keys.is_set(nr))
+ quick_records= table->quick_rows[nr];
+ if (best_key < 0 ||
+ (select_limit <= min(quick_records,best_records) ?
+ keyinfo->key_parts < best_key_parts :
+ quick_records < best_records) ||
+ (!is_best_covering && is_covering))
+ {
+ best_key= nr;
+ best_key_parts= keyinfo->key_parts;
+ if (saved_best_key_parts)
+ *saved_best_key_parts= used_key_parts;
+ best_records= quick_records;
+ is_best_covering= is_covering;
+ best_key_direction= direction;
+ best_select_limit= select_limit;
+ }
+ }
+ }
+ }
+ }
+
+ if (best_key < 0 || best_key == ref_key)
+ DBUG_RETURN(FALSE);
+
+ *new_key= best_key;
+ *new_key_direction= best_key_direction;
+ *new_select_limit= has_limit ? best_select_limit : table_records;
+ if (new_used_key_parts != NULL)
+ *new_used_key_parts= best_key_parts;
+
+ DBUG_RETURN(TRUE);
+}
+
+
+/**
+ Find a key to apply single table UPDATE/DELETE by a given ORDER
+
+ @param order Linked list of ORDER BY arguments
+ @param table Table to find a key
+ @param select Pointer to access/update select->quick (if any)
+ @param limit LIMIT clause parameter
+ @param [out] need_sort TRUE if filesort needed
+ @param [out] reverse
+ TRUE if the key is reversed again given ORDER (undefined if key == MAX_KEY)
+
+ @return
+ - MAX_KEY if no key found (need_sort == TRUE)
+ - MAX_KEY if quick select result order is OK (need_sort == FALSE)
+ - key number (either index scan or quick select) (need_sort == FALSE)
+
+ @note
+ Side effects:
+ - may deallocate or deallocate and replace select->quick;
+ - may set table->quick_condition_rows and table->quick_rows[...]
+ to table->file->stats.records.
+*/
+
+uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
+ ha_rows limit, bool *need_sort, bool *reverse)
+{
+ if (!order)
+ {
+ *need_sort= FALSE;
+ if (select && select->quick)
+ return select->quick->index; // index or MAX_KEY, use quick select as is
+ else
+ return table->file->key_used_on_scan; // MAX_KEY or index for some engines
+ }
+
+ if (!is_simple_order(order)) // just to cut further expensive checks
+ {
+ *need_sort= TRUE;
+ return MAX_KEY;
+ }
+
+ if (select && select->quick)
+ {
+ if (select->quick->index == MAX_KEY)
+ {
+ *need_sort= TRUE;
+ return MAX_KEY;
+ }
+
+ uint used_key_parts;
+ switch (test_if_order_by_key(order, table, select->quick->index,
+ &used_key_parts)) {
+ case 1: // desired order
+ *need_sort= FALSE;
+ return select->quick->index;
+ case 0: // unacceptable order
+ *need_sort= TRUE;
+ return MAX_KEY;
+ case -1: // desired order, but opposite direction
+ {
+ QUICK_SELECT_I *reverse_quick;
+ if ((reverse_quick=
+ select->quick->make_reverse(used_key_parts)))
+ {
+ select->set_quick(reverse_quick);
+ *need_sort= FALSE;
+ return select->quick->index;
+ }
+ else
+ {
+ *need_sort= TRUE;
+ return MAX_KEY;
+ }
+ }
+ }
+ DBUG_ASSERT(0);
+ }
+ else if (limit != HA_POS_ERROR)
+ { // check if some index scan & LIMIT is more efficient than filesort
+
+ /*
+ Update quick_condition_rows since single table UPDATE/DELETE procedures
+ don't call make_join_statistics() and leave this variable uninitialized.
+ */
+ table->quick_condition_rows= table->file->stats.records;
+
+ int key, direction;
+ if (test_if_cheaper_ordering(NULL, order, table,
+ table->keys_in_use_for_order_by, -1,
+ limit,
+ &key, &direction, &limit) &&
+ !is_key_used(table, key, table->write_set))
+ {
+ *need_sort= FALSE;
+ *reverse= (direction < 0);
+ return key;
+ }
+ }
+ *need_sort= TRUE;
+ return MAX_KEY;
+}
+
+
+/**
@} (end of group Query_Optimizer)
*/
diff --git a/sql/sql_select.h b/sql/sql_select.h
index fc70181a76e..ce57376a3ec 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -1,5 +1,8 @@
-/* Copyright (c) 2000, 2011, Oracle and/or its affiliates.
- Copyright (c) 2008-2011 Monty Program Ab
+#ifndef SQL_SELECT_INCLUDED
+#define SQL_SELECT_INCLUDED
+
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,11 +15,8 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#ifndef SQL_SELECT_INCLUDED
-#define SQL_SELECT_INCLUDED
/**
@file
@@ -30,9 +30,15 @@
#include "procedure.h"
#include <myisam.h>
+#include "sql_array.h" /* Array */
+#include "records.h" /* READ_RECORD */
+#include "opt_range.h" /* SQL_SELECT, QUICK_SELECT_I */
+
-#if defined(WITH_ARIA_STORAGE_ENGINE) && defined(USE_MARIA_FOR_TMP_TABLES)
+#if defined(WITH_ARIA_STORAGE_ENGINE)
#include <maria.h>
+#endif
+#if defined(USE_ARIA_FOR_TMP_TABLES)
#define TMP_ENGINE_HTON maria_hton
#else
#define TMP_ENGINE_HTON myisam_hton
@@ -180,17 +186,6 @@ enum sj_strategy_enum
typedef enum_nested_loop_state
(*Next_select_func)(JOIN *, struct st_join_table *, bool);
-
-/*
- Function prototype for reading first record for a join tab
-
- RETURN
- 0 - OK
- -1 - Record not found
- Other - A fatal error
-*/
-typedef int (*Read_record_func)(struct st_join_table *tab);
-
Next_select_func setup_end_select_func(JOIN *join);
int rr_sequential(READ_RECORD *info);
int rr_sequential_and_unpack(READ_RECORD *info);
@@ -257,7 +252,7 @@ typedef struct st_join_table {
*/
uint packed_info;
- Read_record_func read_first_record;
+ READ_RECORD::Setup_func read_first_record;
Next_select_func next_select;
READ_RECORD read_record;
/*
@@ -265,8 +260,8 @@ typedef struct st_join_table {
if it is executed by an alternative full table scan when the left operand of
the subquery predicate is evaluated to NULL.
*/
- Read_record_func save_read_first_record;/* to save read_first_record */
- int (*save_read_record) (READ_RECORD *);/* to save read_record.read_record */
+ READ_RECORD::Setup_func save_read_first_record;/* to save read_first_record */
+ READ_RECORD::Read_func save_read_record;/* to save read_record.read_record */
double worst_seeks;
key_map const_keys; /**< Keys with constant part */
key_map checked_keys; /**< Keys checked in find_best */
@@ -419,6 +414,11 @@ typedef struct st_join_table {
(select->quick->get_type() ==
QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX));
}
+ bool is_using_agg_loose_index_scan ()
+ {
+ return (is_using_loose_index_scan() &&
+ ((QUICK_GROUP_MIN_MAX_SELECT *)select->quick)->is_agg_distinct());
+ }
bool is_inner_table_of_semi_join_with_first_match()
{
return first_sj_inner_tab != NULL;
@@ -513,6 +513,7 @@ typedef struct st_join_table {
return (is_hash_join_key_no(key) ? hj_key : table->key_info+key);
}
double scan_time();
+ ha_rows get_examined_rows();
bool preread_init();
bool is_sjm_nest() { return test(bush_children); }
@@ -884,7 +885,7 @@ protected:
~Join_plan_state()
{
delete_dynamic(&keyuse);
- my_free(best_positions, MYF(0));
+ my_free(best_positions);
}
};
@@ -907,6 +908,27 @@ protected:
public:
JOIN_TAB *join_tab, **best_ref;
+
+ /*
+ Saved join_tab for pre_sorting. create_sort_index() will save here..
+ */
+ JOIN_TAB *pre_sort_join_tab;
+ uint pre_sort_index;
+ Item *pre_sort_idx_pushed_cond;
+ void clean_pre_sort_join_tab();
+
+ /*
+ For "Using temporary+Using filesort" queries, JOIN::join_tab can point to
+ either:
+ 1. array of join tabs describing how to run the select, or
+ 2. array of single join tab describing read from the temporary table.
+
+ SHOW EXPLAIN code needs to read/show #1. This is why two next members are
+ there for saving it.
+ */
+ JOIN_TAB *table_access_tabs;
+ uint top_table_access_tabs_count;
+
JOIN_TAB **map2table; ///< mapping between table indexes and JOIN_TABs
JOIN_TAB *join_tab_save; ///< saved join_tab for subquery reexecution
@@ -942,6 +964,8 @@ public:
uint top_join_tab_count;
uint send_group_parts;
bool group; /**< If query contains GROUP BY clause */
+ bool need_distinct;
+
/**
Indicates that grouping will be performed on the result set during
query execution. This field belongs to query execution.
@@ -952,11 +976,6 @@ public:
bool first_record,full_join, no_field_update;
bool hash_join;
bool do_send_rows;
- /**
- TRUE when we want to resume nested loop iterations when
- fetching data from a cursor
- */
- bool resume_nested_loop;
table_map const_table_map;
/**
Bitmap of semijoin tables that the current partial plan decided
@@ -1139,6 +1158,8 @@ public:
bool skip_sort_order;
bool need_tmp, hidden_group_fields;
+ /* TRUE if there was full cleunap of the JOIN */
+ bool cleaned;
DYNAMIC_ARRAY keyuse;
Item::cond_result cond_value, having_value;
/**
@@ -1237,7 +1258,6 @@ public:
sort_and_group= 0;
first_record= 0;
do_send_rows= 1;
- resume_nested_loop= FALSE;
send_records= 0;
found_records= 0;
fetch_limit= HA_POS_ERROR;
@@ -1260,6 +1280,7 @@ public:
no_order= 0;
simple_order= 0;
simple_group= 0;
+ need_distinct= 0;
skip_sort_order= 0;
need_tmp= 0;
hidden_group_fields= 0; /*safety*/
@@ -1271,6 +1292,7 @@ public:
zero_result_cause= 0;
optimized= 0;
initialized= 0;
+ cleaned= 0;
cond_equal= 0;
having_equal= 0;
exec_const_cond= 0;
@@ -1290,8 +1312,15 @@ public:
outer_ref_cond= pseudo_bits_cond= NULL;
in_to_exists_where= NULL;
in_to_exists_having= NULL;
+ pre_sort_join_tab= NULL;
emb_sjm_nest= NULL;
sjm_lookup_tables= 0;
+ /*
+ The following is needed because JOIN::cleanup(true) may be called for
+ joins for which JOIN::optimize was aborted with an error before a proper
+ query plan was produced
+ */
+ table_access_tabs= NULL;
}
int prepare(Item ***rref_pointer_array, TABLE_LIST *tables, uint wind_num,
@@ -1308,6 +1337,7 @@ public:
bool alloc_func_list();
bool flatten_subqueries();
bool optimize_unflattened_subqueries();
+ bool optimize_constant_subqueries();
bool make_sum_func_list(List<Item> &all_fields, List<Item> &send_fields,
bool before_group_by, bool recompute= FALSE);
@@ -1353,6 +1383,7 @@ public:
return (unit == &thd->lex->unit && (unit->fake_select_lex == 0 ||
select_lex == unit->fake_select_lex));
}
+ void cache_const_exprs();
inline table_map all_tables_map()
{
return (table_map(1) << table_count) - 1;
@@ -1406,6 +1437,7 @@ public:
void get_prefix_cost_and_fanout(uint n_tables,
double *read_time_arg,
double *record_count_arg);
+ double get_examined_rows();
/* defined in opt_subselect.cc */
bool transform_max_min_subquery();
/* True if this JOIN is a subquery under an IN predicate. */
@@ -1426,7 +1458,9 @@ private:
enum enum_with_bush_roots { WITH_BUSH_ROOTS, WITHOUT_BUSH_ROOTS};
enum enum_with_const_tables { WITH_CONST_TABLES, WITHOUT_CONST_TABLES};
-JOIN_TAB *first_linear_tab(JOIN *join, enum enum_with_const_tables const_tbls);
+JOIN_TAB *first_linear_tab(JOIN *join,
+ enum enum_with_bush_roots include_bush_roots,
+ enum enum_with_const_tables const_tbls);
JOIN_TAB *next_linear_tab(JOIN* join, JOIN_TAB* tab,
enum enum_with_bush_roots include_bush_roots);
@@ -1438,10 +1472,8 @@ typedef struct st_select_check {
} SELECT_CHECK;
extern const char *join_type_str[];
-void TEST_join(JOIN *join);
/* Extern functions in sql_select.cc */
-bool store_val_in_field(Field *field, Item *val, enum_check_fields check_flag);
void count_field_types(SELECT_LEX *select_lex, TMP_TABLE_PARAM *param,
List<Item> &fields, bool reset_with_sum_func);
bool setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
@@ -1450,13 +1482,13 @@ bool setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
uint elements, List<Item> &fields);
void copy_fields(TMP_TABLE_PARAM *param);
bool copy_funcs(Item **func_ptr, const THD *thd);
-bool create_internal_tmp_table_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param,
- int error, bool ignore_last_dupp_error);
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,
Item_field *item, uint convert_blob_length);
+bool is_indexed_agg_distinct(JOIN *join, List<Item_field> *out_args);
+
/* functions from opt_sum.cc */
bool simple_pred(Item_func *func_item, Item **args, bool *inv_order);
int opt_sum_query(THD* thd,
@@ -1513,8 +1545,9 @@ public:
enum store_key_result result;
THD *thd= to_field->table->in_use;
enum_check_fields saved_count_cuted_fields= thd->count_cuted_fields;
- ulong sql_mode= thd->variables.sql_mode;
+ ulonglong sql_mode= thd->variables.sql_mode;
thd->variables.sql_mode&= ~(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE);
+ thd->variables.sql_mode|= MODE_INVALID_DATES;
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
@@ -1696,6 +1729,52 @@ bool error_if_full_join(JOIN *join);
int report_error(TABLE *table, int error);
int safe_index_read(JOIN_TAB *tab);
COND *remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value);
+int get_quick_record(SQL_SELECT *select);
+SORT_FIELD * make_unireg_sortorder(ORDER *order, uint *length,
+ SORT_FIELD *sortorder);
+int setup_order(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
+ List<Item> &fields, List <Item> &all_fields, ORDER *order);
+int setup_group(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
+ List<Item> &fields, List<Item> &all_fields, ORDER *order,
+ bool *hidden_group_fields);
+bool fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
+ Item **ref_pointer_array);
+int join_read_key2(THD *thd, struct st_join_table *tab, TABLE *table,
+ struct st_table_ref *table_ref);
+
+bool handle_select(THD *thd, LEX *lex, select_result *result,
+ ulong setup_tables_done_option);
+bool mysql_select(THD *thd, Item ***rref_pointer_array,
+ TABLE_LIST *tables, uint wild_num, List<Item> &list,
+ COND *conds, uint og_num, ORDER *order, ORDER *group,
+ Item *having, ORDER *proc_param, ulonglong select_type,
+ select_result *result, SELECT_LEX_UNIT *unit,
+ SELECT_LEX *select_lex);
+void free_underlaid_joins(THD *thd, SELECT_LEX *select);
+bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit,
+ select_result *result);
+Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
+ Item ***copy_func, Field **from_field,
+ Field **def_field,
+ bool group, bool modify_item,
+ bool table_cant_handle_bit_fields,
+ bool make_copy_field,
+ uint convert_blob_length);
+bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
+ ENGINE_COLUMNDEF *start_recinfo,
+ ENGINE_COLUMNDEF **recinfo,
+ ulonglong options, my_bool big_tables);
+
+/*
+ General routine to change field->ptr of a NULL-terminated array of Field
+ objects. Useful when needed to call val_int, val_str or similar and the
+ field data is not in table->record[0] but in some other structure.
+ set_key_field_ptr changes all fields of an index using a key_info object.
+ All methods presume that there is at least one field to change.
+*/
+
+TABLE *create_virtual_tmp_table(THD *thd, List<Create_field> &field_list);
+
int test_if_item_cache_changed(List<Cached_item> &list);
int join_init_read_record(JOIN_TAB *tab);
int join_read_record_no_init(JOIN_TAB *tab);
@@ -1711,7 +1790,7 @@ void optimize_wo_join_buffering(JOIN *join, uint first_tab, uint last_tab,
double *outer_rec_count, double *reopt_cost);
Item_equal *find_item_equal(COND_EQUAL *cond_equal, Field *field,
bool *inherited_fl);
-bool test_if_ref(COND *root_cond,
+extern bool test_if_ref(Item *,
Item_field *left_item,Item *right_item);
inline bool optimizer_flag(THD *thd, uint flag)
@@ -1719,6 +1798,15 @@ inline bool optimizer_flag(THD *thd, uint flag)
return (thd->variables.optimizer_switch & flag);
}
+uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
+ ha_rows limit, bool *need_sort, bool *reverse);
+ORDER *simple_remove_const(ORDER *order, COND *where);
+bool const_expression_in_where(COND *cond, Item *comp_item,
+ Field *comp_field= NULL,
+ Item **const_item= NULL);
+bool cond_is_datetime_is_null(Item *cond);
+bool cond_has_datetime_is_null(Item *cond);
+
/* Table elimination entry point function */
void eliminate_tables(JOIN *join);
@@ -1737,12 +1825,14 @@ void push_index_cond(JOIN_TAB *tab, uint keyno);
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,
- char* alias, bool do_not_open=FALSE);
+ const char* 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,
ENGINE_COLUMNDEF *start_recinfo,
ENGINE_COLUMNDEF **recinfo,
- int error, bool ignore_last_dupp_key_error);
+ int error, bool ignore_last_dupp_key_error,
+ bool *is_duplicate);
bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
ENGINE_COLUMNDEF *start_recinfo,
ENGINE_COLUMNDEF **recinfo,
diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc
index 61a36d1bbea..dce679a883f 100644
--- a/sql/sql_servers.cc
+++ b/sql/sql_servers.cc
@@ -1,6 +1,4 @@
-/*
- Copyright (c) 2006-2008 MySQL AB, 2009, 2010 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,8 +11,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
@@ -36,12 +33,18 @@
currently running transactions etc will not be disrupted.
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "sql_servers.h"
+#include "unireg.h"
+#include "sql_base.h" // close_mysql_tables
+#include "records.h" // init_read_record, end_read_record
#include "hash_filo.h"
#include <m_ctype.h>
#include <stdarg.h>
#include "sp_head.h"
#include "sp.h"
+#include "transaction.h"
+#include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT
/*
We only use 1 mutex to guard the data structures - THR_LOCK_servers.
@@ -50,7 +53,7 @@
static HASH servers_cache;
static MEM_ROOT mem;
-static rw_lock_t THR_LOCK_servers;
+static mysql_rwlock_t THR_LOCK_servers;
static bool get_server_from_table_to_cache(TABLE *table);
@@ -92,6 +95,26 @@ static uchar *servers_cache_get_key(FOREIGN_SERVER *server, size_t *length,
DBUG_RETURN((uchar*) server->server_name);
}
+#ifdef HAVE_PSI_INTERFACE
+static PSI_rwlock_key key_rwlock_THR_LOCK_servers;
+
+static PSI_rwlock_info all_servers_cache_rwlocks[]=
+{
+ { &key_rwlock_THR_LOCK_servers, "THR_LOCK_servers", PSI_FLAG_GLOBAL}
+};
+
+static void init_servers_cache_psi_keys(void)
+{
+ const char* category= "sql";
+ int count;
+
+ if (PSI_server == NULL)
+ return;
+
+ count= array_elements(all_servers_cache_rwlocks);
+ PSI_server->register_rwlock(category, all_servers_cache_rwlocks, count);
+}
+#endif /* HAVE_PSI_INTERFACE */
/*
Initialize structures responsible for servers used in federated
@@ -118,20 +141,24 @@ bool servers_init(bool dont_read_servers_table)
bool return_val= FALSE;
DBUG_ENTER("servers_init");
+#ifdef HAVE_PSI_INTERFACE
+ init_servers_cache_psi_keys();
+#endif
+
/* init the mutex */
- if (my_rwlock_init(&THR_LOCK_servers, NULL))
+ if (mysql_rwlock_init(key_rwlock_THR_LOCK_servers, &THR_LOCK_servers))
DBUG_RETURN(TRUE);
/* initialise our servers cache */
- if (hash_init(&servers_cache, system_charset_info, 32, 0, 0,
- (hash_get_key) servers_cache_get_key, 0, 0))
+ if (my_hash_init(&servers_cache, system_charset_info, 32, 0, 0,
+ (my_hash_get_key) servers_cache_get_key, 0, 0))
{
return_val= TRUE; /* we failed, out of memory? */
goto end;
}
/* Initialize the mem root for data */
- init_alloc_root(&mem, ACL_ALLOC_BLOCK_SIZE, 0);
+ init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0);
if (dont_read_servers_table)
goto end;
@@ -143,7 +170,6 @@ bool servers_init(bool dont_read_servers_table)
DBUG_RETURN(TRUE);
thd->thread_stack= (char*) &thd;
thd->store_globals();
- lex_start(thd);
/*
It is safe to call servers_reload() since servers_* arrays and hashes which
will be freed there are global static objects and thus are initialized
@@ -183,7 +209,7 @@ static bool servers_load(THD *thd, TABLE_LIST *tables)
my_hash_reset(&servers_cache);
free_root(&mem, MYF(0));
- init_alloc_root(&mem, ACL_ALLOC_BLOCK_SIZE, 0);
+ init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0);
if (init_read_record(&read_record_info,thd,table=tables[0].table,NULL,1,0,
FALSE))
@@ -228,30 +254,20 @@ bool servers_reload(THD *thd)
bool return_val= TRUE;
DBUG_ENTER("servers_reload");
- if (thd->locked_tables)
- { // Can't have locked tables here
- thd->lock=thd->locked_tables;
- thd->locked_tables=0;
- close_thread_tables(thd);
- }
-
DBUG_PRINT("info", ("locking servers_cache"));
- rw_wrlock(&THR_LOCK_servers);
+ mysql_rwlock_wrlock(&THR_LOCK_servers);
- bzero((char*) tables, sizeof(tables));
- tables[0].alias= tables[0].table_name= (char*) "servers";
- tables[0].db= (char*) "mysql";
- tables[0].lock_type= TL_READ;
+ tables[0].init_one_table("mysql", 5, "servers", 7, "servers", TL_READ);
- if (simple_open_n_lock_tables(thd, tables))
+ if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{
/*
Execution might have been interrupted; only print the error message
if an error condition has been raised.
*/
- if (thd->main_da.is_error())
+ if (thd->stmt_da->is_error())
sql_print_error("Can't open and lock privilege tables: %s",
- thd->main_da.message());
+ thd->stmt_da->message());
return_val= FALSE;
goto end;
}
@@ -265,9 +281,9 @@ bool servers_reload(THD *thd)
}
end:
- close_thread_tables(thd);
+ close_mysql_tables(thd);
DBUG_PRINT("info", ("unlocking servers_cache"));
- rw_unlock(&THR_LOCK_servers);
+ mysql_rwlock_unlock(&THR_LOCK_servers);
DBUG_RETURN(return_val);
}
@@ -375,12 +391,10 @@ insert_server(THD *thd, FOREIGN_SERVER *server)
DBUG_ENTER("insert_server");
- bzero((char*) &tables, sizeof(tables));
- tables.db= (char*) "mysql";
- tables.alias= tables.table_name= (char*) "servers";
+ tables.init_one_table("mysql", 5, "servers", 7, "servers", TL_WRITE);
/* need to open before acquiring THR_LOCK_plugin or it will deadlock */
- if (! (table= open_ltable(thd, &tables, TL_WRITE, 0)))
+ if (! (table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
goto end;
/* insert the server into the table */
@@ -520,6 +534,7 @@ int insert_server_record(TABLE *table, FOREIGN_SERVER *server)
{
int error;
DBUG_ENTER("insert_server_record");
+ tmp_disable_binlog(table->in_use);
table->use_all_columns();
empty_record(table);
@@ -556,6 +571,8 @@ int insert_server_record(TABLE *table, FOREIGN_SERVER *server)
}
else
error= ER_FOREIGN_SERVER_EXISTS;
+
+ reenable_binlog(table->in_use);
DBUG_RETURN(error);
}
@@ -593,17 +610,15 @@ int drop_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
DBUG_PRINT("info", ("server name server->server_name %s",
server_options->server_name));
- bzero((char*) &tables, sizeof(tables));
- tables.db= (char*) "mysql";
- tables.alias= tables.table_name= (char*) "servers";
+ tables.init_one_table("mysql", 5, "servers", 7, "servers", TL_WRITE);
- rw_wrlock(&THR_LOCK_servers);
+ mysql_rwlock_wrlock(&THR_LOCK_servers);
/* hit the memory hit first */
if ((error= delete_server_record_in_cache(server_options)))
goto end;
- if (! (table= open_ltable(thd, &tables, TL_WRITE, 0)))
+ if (! (table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
{
error= my_errno;
goto end;
@@ -612,16 +627,16 @@ int drop_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
error= delete_server_record(table, name.str, name.length);
/* close the servers table before we call closed_cached_connection_tables */
- close_thread_tables(thd);
+ close_mysql_tables(thd);
- if (close_cached_connection_tables(thd, TRUE, &name))
+ if (close_cached_connection_tables(thd, &name))
{
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_UNKNOWN_ERROR, "Server connection in use");
}
end:
- rw_unlock(&THR_LOCK_servers);
+ mysql_rwlock_unlock(&THR_LOCK_servers);
DBUG_RETURN(error);
}
@@ -656,9 +671,10 @@ delete_server_record_in_cache(LEX_SERVER_OPTIONS *server_options)
server_options->server_name_length));
- if (!(server= (FOREIGN_SERVER *) hash_search(&servers_cache,
- (uchar*) server_options->server_name,
- server_options->server_name_length)))
+ if (!(server= (FOREIGN_SERVER *)
+ my_hash_search(&servers_cache,
+ (uchar*) server_options->server_name,
+ server_options->server_name_length)))
{
DBUG_PRINT("info", ("server_name %s length %d not found!",
server_options->server_name,
@@ -673,8 +689,8 @@ delete_server_record_in_cache(LEX_SERVER_OPTIONS *server_options)
server->server_name,
server->server_name_length));
- VOID(hash_delete(&servers_cache, (uchar*) server));
-
+ my_hash_delete(&servers_cache, (uchar*) server);
+
error= 0;
end:
@@ -716,11 +732,10 @@ int update_server(THD *thd, FOREIGN_SERVER *existing, FOREIGN_SERVER *altered)
TABLE_LIST tables;
DBUG_ENTER("update_server");
- bzero((char*) &tables, sizeof(tables));
- tables.db= (char*)"mysql";
- tables.alias= tables.table_name= (char*)"servers";
+ tables.init_one_table("mysql", 5, "servers", 7, "servers",
+ TL_WRITE);
- if (!(table= open_ltable(thd, &tables, TL_WRITE, 0)))
+ if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
{
error= my_errno;
goto end;
@@ -780,7 +795,7 @@ int update_server_record_in_cache(FOREIGN_SERVER *existing,
/*
delete the existing server struct from the server cache
*/
- VOID(hash_delete(&servers_cache, (uchar*)existing));
+ my_hash_delete(&servers_cache, (uchar*)existing);
/*
Insert the altered server struct into the server cache
@@ -867,6 +882,7 @@ update_server_record(TABLE *table, FOREIGN_SERVER *server)
{
int error=0;
DBUG_ENTER("update_server_record");
+ tmp_disable_binlog(table->in_use);
table->use_all_columns();
/* set the field that's the PK to the value we're looking for */
table->field[0]->store(server->server_name,
@@ -900,6 +916,7 @@ update_server_record(TABLE *table, FOREIGN_SERVER *server)
}
end:
+ reenable_binlog(table->in_use);
DBUG_RETURN(error);
}
@@ -925,6 +942,7 @@ delete_server_record(TABLE *table,
{
int error;
DBUG_ENTER("delete_server_record");
+ tmp_disable_binlog(table->in_use);
table->use_all_columns();
/* set the field that's the PK to the value we're looking for */
@@ -946,6 +964,7 @@ delete_server_record(TABLE *table,
table->file->print_error(error, MYF(0));
}
+ reenable_binlog(table->in_use);
DBUG_RETURN(error);
}
@@ -972,11 +991,11 @@ int create_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
DBUG_PRINT("info", ("server_options->server_name %s",
server_options->server_name));
- rw_wrlock(&THR_LOCK_servers);
+ mysql_rwlock_wrlock(&THR_LOCK_servers);
/* hit the memory first */
- if (hash_search(&servers_cache, (uchar*) server_options->server_name,
- server_options->server_name_length))
+ if (my_hash_search(&servers_cache, (uchar*) server_options->server_name,
+ server_options->server_name_length))
goto end;
@@ -993,7 +1012,7 @@ int create_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
DBUG_PRINT("info", ("error returned %d", error));
end:
- rw_unlock(&THR_LOCK_servers);
+ mysql_rwlock_unlock(&THR_LOCK_servers);
DBUG_RETURN(error);
}
@@ -1022,11 +1041,11 @@ int alter_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
DBUG_PRINT("info", ("server_options->server_name %s",
server_options->server_name));
- rw_wrlock(&THR_LOCK_servers);
+ mysql_rwlock_wrlock(&THR_LOCK_servers);
- if (!(existing= (FOREIGN_SERVER *) hash_search(&servers_cache,
- (uchar*) name.str,
- name.length)))
+ if (!(existing= (FOREIGN_SERVER *) my_hash_search(&servers_cache,
+ (uchar*) name.str,
+ name.length)))
goto end;
altered= (FOREIGN_SERVER *)alloc_root(&mem,
@@ -1037,9 +1056,9 @@ int alter_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
error= update_server(thd, existing, altered);
/* close the servers table before we call closed_cached_connection_tables */
- close_thread_tables(thd);
+ close_mysql_tables(thd);
- if (close_cached_connection_tables(thd, FALSE, &name))
+ if (close_cached_connection_tables(thd, &name))
{
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_UNKNOWN_ERROR, "Server connection in use");
@@ -1047,7 +1066,7 @@ int alter_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
end:
DBUG_PRINT("info", ("error returned %d", error));
- rw_unlock(&THR_LOCK_servers);
+ mysql_rwlock_unlock(&THR_LOCK_servers);
DBUG_RETURN(error);
}
@@ -1205,7 +1224,7 @@ prepare_server_struct_for_update(LEX_SERVER_OPTIONS *server_options,
void servers_free(bool end)
{
DBUG_ENTER("servers_free");
- if (!hash_inited(&servers_cache))
+ if (!my_hash_inited(&servers_cache))
DBUG_VOID_RETURN;
if (!end)
{
@@ -1213,9 +1232,9 @@ void servers_free(bool end)
my_hash_reset(&servers_cache);
DBUG_VOID_RETURN;
}
- rwlock_destroy(&THR_LOCK_servers);
+ mysql_rwlock_destroy(&THR_LOCK_servers);
free_root(&mem,MYF(0));
- hash_free(&servers_cache);
+ my_hash_free(&servers_cache);
DBUG_VOID_RETURN;
}
@@ -1295,10 +1314,10 @@ FOREIGN_SERVER *get_server_by_name(MEM_ROOT *mem, const char *server_name,
}
DBUG_PRINT("info", ("locking servers_cache"));
- rw_rdlock(&THR_LOCK_servers);
- if (!(server= (FOREIGN_SERVER *) hash_search(&servers_cache,
- (uchar*) server_name,
- server_name_length)))
+ mysql_rwlock_rdlock(&THR_LOCK_servers);
+ if (!(server= (FOREIGN_SERVER *) my_hash_search(&servers_cache,
+ (uchar*) server_name,
+ server_name_length)))
{
DBUG_PRINT("info", ("server_name %s length %u not found!",
server_name, (unsigned) server_name_length));
@@ -1309,7 +1328,7 @@ FOREIGN_SERVER *get_server_by_name(MEM_ROOT *mem, const char *server_name,
server= clone_server(mem, server, buff);
DBUG_PRINT("info", ("unlocking servers_cache"));
- rw_unlock(&THR_LOCK_servers);
+ mysql_rwlock_unlock(&THR_LOCK_servers);
DBUG_RETURN(server);
}
diff --git a/sql/sql_servers.h b/sql/sql_servers.h
index 08e9ac9852e..a6186a85ae2 100644
--- a/sql/sql_servers.h
+++ b/sql/sql_servers.h
@@ -1,4 +1,7 @@
-/* Copyright (c) 2006, 2007 MySQL AB
+#ifndef SQL_SERVERS_INCLUDED
+#define SQL_SERVERS_INCLUDED
+
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,8 +16,13 @@
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" /* uint */
#include "slave.h" // for tables_ok(), rpl_filter
+class THD;
+typedef struct st_lex_server_options LEX_SERVER_OPTIONS;
+typedef struct st_mem_root MEM_ROOT;
+
/* structs */
typedef struct st_federated_server
{
@@ -41,3 +49,5 @@ int alter_server(THD *thd, LEX_SERVER_OPTIONS *server_options);
/* lookup functions */
FOREIGN_SERVER *get_server_by_name(MEM_ROOT *mem, const char *server_name,
FOREIGN_SERVER *server_buffer);
+
+#endif /* SQL_SERVERS_INCLUDED */
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index af5e4836499..f6ed5702ce5 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
Copyright (c) 2009, 2014, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
@@ -10,29 +10,54 @@
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 */
+ 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 */
/* Function with list databases, tables or fields */
-#include "mysql_priv.h"
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_priv.h"
+#include "unireg.h"
+#include "sql_acl.h" // fill_schema_*_privileges
#include "sql_select.h" // For select_describe
+#include "sql_base.h" // close_tables_for_reopen
#include "create_options.h"
#include "sql_show.h"
+#include "sql_table.h" // filename_to_tablename,
+ // primary_key_name,
+ // build_table_filename
#include "repl_failsafe.h"
-#include "sp_head.h"
+#include "sql_parse.h" // check_access, check_table_access
+#include "sql_partition.h" // partition_element
+#include "sql_derived.h" // mysql_derived_prepare,
+ // mysql_handle_derived,
+#include "sql_db.h" // check_db_dir_existence, load_db_opt_by_name
+#include "sql_time.h" // interval_type_to_name
+#include "tztime.h" // struct Time_zone
+#include "sql_acl.h" // TABLE_ACLS, check_grant, DB_ACLS, acl_get,
+ // check_grant_db
+#include "filesort.h" // filesort_free_buffers
#include "sp.h"
+#include "sp_head.h"
+#include "sp_pcontext.h"
+#include "set_var.h"
#include "sql_trigger.h"
+#include "sql_derived.h"
+#include "sql_connect.h"
#include "authors.h"
#include "contributors.h"
+#include "sql_partition.h"
#ifdef HAVE_EVENT_SCHEDULER
#include "events.h"
#include "event_data_objects.h"
#endif
#include <my_dir.h>
+#include "lock.h" // MYSQL_OPEN_IGNORE_FLUSH
#include "debug_sync.h"
+#include "datadict.h" // dd_frm_type()
+#include "keycaches.h"
#define STR_OR_NIL(S) ((S) ? (S) : "<nil>")
@@ -83,6 +108,14 @@ static const char *ha_choice_values[] = {"", "0", "1"};
static void store_key_options(THD *thd, String *packet, TABLE *table,
KEY *key_info);
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+static void get_cs_converted_string_value(THD *thd,
+ String *input_str,
+ String *output_str,
+ CHARSET_INFO *cs,
+ bool use_hex);
+#endif
+
static void
append_algorithm(TABLE_LIST *table, String *buff);
@@ -197,27 +230,27 @@ static my_bool show_plugins(THD *thd, plugin_ref plugin,
strlen(PLUGIN_LICENSE_PROPRIETARY_STRING), cs);
break;
}
- table->field[9]->set_notnull();
- if ((uint) plug->maturity <= MariaDB_PLUGIN_MATURITY_STABLE)
- table->field[10]->store(maturity_name[plug->maturity].str,
+ table->field[10]->store(
+ global_plugin_typelib_names[plugin_load_option(plugin)],
+ strlen(global_plugin_typelib_names[plugin_load_option(plugin)]),
+ cs);
+
+ if (plug->maturity <= MariaDB_PLUGIN_MATURITY_STABLE)
+ table->field[11]->store(maturity_name[plug->maturity].str,
maturity_name[plug->maturity].length,
cs);
else
- {
- DBUG_ASSERT(0);
- table->field[10]->store("Unknown", 7, cs);
- }
- table->field[10]->set_notnull();
+ table->field[11]->store("Unknown", 7, cs);
if (plug->version_info)
{
- table->field[11]->store(plug->version_info,
+ table->field[12]->store(plug->version_info,
strlen(plug->version_info), cs);
- table->field[11]->set_notnull();
+ table->field[12]->set_notnull();
}
else
- table->field[11]->set_null();
+ table->field[12]->set_null();
return schema_table_store_record(thd, table);
}
@@ -251,7 +284,7 @@ bool mysqld_show_authors(THD *thd)
field_list.push_back(new Item_empty_string("Location",40));
field_list.push_back(new Item_empty_string("Comment",80));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
@@ -285,7 +318,7 @@ bool mysqld_show_contributors(THD *thd)
field_list.push_back(new Item_empty_string("Location",40));
field_list.push_back(new Item_empty_string("Comment",80));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
@@ -335,6 +368,7 @@ static struct show_privileges_st sys_privileges[]=
{"Insert", "Tables", "To insert data into tables"},
{"Lock tables","Databases","To use LOCK TABLES (together with SELECT privilege)"},
{"Process", "Server Admin", "To view the plain text of currently executing queries"},
+ {"Proxy", "Server Admin", "To make proxy user possible"},
{"References", "Databases,Tables", "To have references on tables"},
{"Reload", "Server Admin", "To reload or refresh tables, logs and privileges"},
{"Replication client","Server Admin","To ask where the slave or master servers are"},
@@ -345,6 +379,7 @@ static struct show_privileges_st sys_privileges[]=
{"Shutdown","Server Admin", "To shut down the server"},
{"Super","Server Admin","To use KILL thread, SET GLOBAL, CHANGE MASTER, etc."},
{"Trigger","Tables", "To use triggers"},
+ {"Create tablespace", "Server Admin", "To create/alter/drop tablespaces"},
{"Update", "Tables", "To update existing rows"},
{"Usage","Server Admin","No privileges - allow connect only"},
{NullS, NullS, NullS}
@@ -360,7 +395,7 @@ bool mysqld_show_privileges(THD *thd)
field_list.push_back(new Item_empty_string("Context",15));
field_list.push_back(new Item_empty_string("Comment",NAME_CHAR_LEN));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
@@ -496,7 +531,7 @@ ignore_db_dirs_reset()
LEX_STRING **elt;
while (NULL!= (elt= (LEX_STRING **) pop_dynamic(&ignore_db_dirs_array)))
if (elt && *elt)
- my_free(*elt, MYF(0));
+ my_free(*elt);
}
@@ -511,7 +546,7 @@ ignore_db_dirs_free()
{
if (opt_ignore_db_dirs)
{
- my_free(opt_ignore_db_dirs, MYF(0));
+ my_free(opt_ignore_db_dirs);
opt_ignore_db_dirs= NULL;
}
ignore_db_dirs_reset();
@@ -534,7 +569,7 @@ ignore_db_dirs_free()
static void dispose_db_dir(void *ptr)
{
- my_free(ptr, MYF(0));
+ my_free(ptr);
}
@@ -547,8 +582,6 @@ ignore_db_dirs_process_additions()
LEX_STRING *dir;
- DBUG_ASSERT(opt_ignore_db_dirs == NULL);
-
skip_ignored_dir_check= TRUE;
if (my_hash_init(&ignore_db_dirs_hash,
@@ -656,94 +689,6 @@ db_name_is_in_ignore_db_dirs_list(const char *directory)
}
-/***************************************************************************
- List all column types
-***************************************************************************/
-
-struct show_column_type_st
-{
- const char *type;
- uint size;
- const char *min_value;
- const char *max_value;
- uint precision;
- uint scale;
- const char *nullable;
- const char *auto_increment;
- const char *unsigned_attr;
- const char *zerofill;
- const char *searchable;
- const char *case_sensitivity;
- const char *default_value;
- const char *comment;
-};
-
-/* TODO: Add remaning types */
-
-static struct show_column_type_st sys_column_types[]=
-{
- {"tinyint",
- 1, "-128", "127", 0, 0, "YES", "YES",
- "NO", "YES", "YES", "NO", "NULL,0",
- "A very small integer"},
- {"tinyint unsigned",
- 1, "0" , "255", 0, 0, "YES", "YES",
- "YES", "YES", "YES", "NO", "NULL,0",
- "A very small integer"},
-};
-
-bool mysqld_show_column_types(THD *thd)
-{
- List<Item> field_list;
- Protocol *protocol= thd->protocol;
- DBUG_ENTER("mysqld_show_column_types");
-
- field_list.push_back(new Item_empty_string("Type",30));
- field_list.push_back(new Item_int("Size",(longlong) 1,
- MY_INT64_NUM_DECIMAL_DIGITS));
- field_list.push_back(new Item_empty_string("Min_Value",20));
- field_list.push_back(new Item_empty_string("Max_Value",20));
- field_list.push_back(new Item_return_int("Prec", 4, MYSQL_TYPE_SHORT));
- field_list.push_back(new Item_return_int("Scale", 4, MYSQL_TYPE_SHORT));
- field_list.push_back(new Item_empty_string("Nullable",4));
- field_list.push_back(new Item_empty_string("Auto_Increment",4));
- field_list.push_back(new Item_empty_string("Unsigned",4));
- field_list.push_back(new Item_empty_string("Zerofill",4));
- field_list.push_back(new Item_empty_string("Searchable",4));
- field_list.push_back(new Item_empty_string("Case_Sensitive",4));
- field_list.push_back(new Item_empty_string("Default",NAME_CHAR_LEN));
- field_list.push_back(new Item_empty_string("Comment",NAME_CHAR_LEN));
-
- if (protocol->send_fields(&field_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
- DBUG_RETURN(TRUE);
-
- /* TODO: Change the loop to not use 'i' */
- for (uint i=0; i < sizeof(sys_column_types)/sizeof(sys_column_types[0]); i++)
- {
- protocol->prepare_for_resend();
- protocol->store(sys_column_types[i].type, system_charset_info);
- protocol->store((ulonglong) sys_column_types[i].size);
- protocol->store(sys_column_types[i].min_value, system_charset_info);
- protocol->store(sys_column_types[i].max_value, system_charset_info);
- protocol->store_short((longlong) sys_column_types[i].precision);
- protocol->store_short((longlong) sys_column_types[i].scale);
- protocol->store(sys_column_types[i].nullable, system_charset_info);
- protocol->store(sys_column_types[i].auto_increment, system_charset_info);
- protocol->store(sys_column_types[i].unsigned_attr, system_charset_info);
- protocol->store(sys_column_types[i].zerofill, system_charset_info);
- protocol->store(sys_column_types[i].searchable, system_charset_info);
- protocol->store(sys_column_types[i].case_sensitivity, system_charset_info);
- protocol->store(sys_column_types[i].default_value, system_charset_info);
- protocol->store(sys_column_types[i].comment, system_charset_info);
- if (protocol->write())
- DBUG_RETURN(TRUE);
- }
- my_eof(thd);
- DBUG_RETURN(FALSE);
-}
-
-
/*
find_files() - find files in a given directory.
@@ -822,7 +767,7 @@ find_files(THD *thd, List<LEX_STRING> *files, const char *db,
end= strend(buff);
if (end != buff && end[-1] == FN_LIBCHAR)
end[-1]= 0; // Remove end FN_LIBCHAR
- if (!my_stat(buff, file->mystat, MYF(0)))
+ if (!mysql_file_stat(key_file_misc, buff, file->mystat, MYF(0)))
continue;
}
#endif
@@ -878,7 +823,7 @@ find_files(THD *thd, List<LEX_STRING> *files, const char *db,
table_list.table_name= uname;
table_list.table_name_length= file_name_len;
table_list.grant.privilege=col_access;
- if (check_grant(thd, TABLE_ACLS, &table_list, 1, 1, 1))
+ if (check_grant(thd, TABLE_ACLS, &table_list, TRUE, 1, TRUE))
continue;
}
#endif
@@ -893,7 +838,7 @@ find_files(THD *thd, List<LEX_STRING> *files, const char *db,
DBUG_PRINT("info",("found: %d files", files->elements));
my_dirend(dirp);
- VOID(ha_find_files(thd, db, path, wild, dir, files));
+ (void) ha_find_files(thd, db, path, wild, dir, files);
DBUG_RETURN(FIND_FILES_OK);
}
@@ -962,9 +907,11 @@ public:
return m_view_access_denied_message_ptr;
}
- bool handle_error(uint sql_errno, const char *message,
- MYSQL_ERROR::enum_warning_level level, THD *thd) {
- /*
+ bool handle_condition(THD *thd, uint sql_errno, const char * /* sqlstate */,
+ MYSQL_ERROR::enum_warning_level level,
+ const char *message, MYSQL_ERROR ** /* cond_hdl */)
+ {
+ /*
The handler does not handle the errors raised by itself.
At this point we know if top_view is really a view.
*/
@@ -974,7 +921,7 @@ public:
m_handling= TRUE;
bool is_handled;
-
+
switch (sql_errno)
{
case ER_TABLEACCESS_DENIED_ERROR:
@@ -990,8 +937,12 @@ public:
is_handled= TRUE;
break;
+ case ER_BAD_FIELD_ERROR:
+ case ER_SP_DOES_NOT_EXIST:
case ER_NO_SUCH_TABLE:
- /* Established behavior: warn if underlying tables are missing. */
+ case ER_NO_SUCH_TABLE_IN_ENGINE:
+ /* Established behavior: warn if underlying tables, columns, or functions
+ are missing. */
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_VIEW_INVALID,
ER(ER_VIEW_INVALID),
@@ -1000,15 +951,6 @@ public:
is_handled= TRUE;
break;
- case ER_SP_DOES_NOT_EXIST:
- /* Established behavior: warn if underlying functions are missing. */
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_VIEW_INVALID,
- ER(ER_VIEW_INVALID),
- m_top_view->get_db_name(),
- m_top_view->get_table_name());
- is_handled= TRUE;
- break;
default:
is_handled= FALSE;
}
@@ -1040,23 +982,37 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
Protocol *protocol= thd->protocol;
char buff[2048];
String buffer(buff, sizeof(buff), system_charset_info);
- bool retval= TRUE; // Assume error
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));
+ /*
+ Metadata locks taken during SHOW CREATE should be released when
+ the statmement completes as it is an information statement.
+ */
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
+
/* We want to preserve the tree for views. */
thd->lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW;
{
+ /*
+ Use open_tables() directly rather than open_normal_and_derived_tables().
+ This ensures that close_thread_tables() is not called if open tables fails
+ and the error is ignored. This allows us to handle broken views nicely.
+ */
+ uint counter;
Show_create_error_handler view_error_suppressor(thd, table_list);
thd->push_internal_handler(&view_error_suppressor);
- bool error= open_normal_and_derived_tables(thd, table_list, 0,
- DT_PREPARE | DT_CREATE);
+ bool open_error=
+ open_tables(thd, &table_list, &counter,
+ MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL) ||
+ mysql_handle_derived(thd->lex, DT_PREPARE);
thd->pop_internal_handler();
- if (error && (thd->killed || thd->main_da.is_error()))
- goto error;
+ if (open_error && (thd->killed || thd->is_error()))
+ goto exit;
}
/* TODO: add environment variables show when it become possible */
@@ -1064,7 +1020,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
{
my_error(ER_WRONG_OBJECT, MYF(0),
table_list->db, table_list->table_name, "VIEW");
- goto error;
+ goto exit;
}
buffer.length(0);
@@ -1076,7 +1032,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
view_store_create_info(thd, table_list, &buffer) :
store_create_info(thd, table_list, &buffer, NULL,
FALSE /* show_database */)))
- goto error;
+ goto exit;
if (table_list->view)
{
@@ -1096,9 +1052,10 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
max(buffer.length(),1024)));
}
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
- goto error;
+ goto exit;
+
protocol->prepare_for_resend();
if (table_list->view)
protocol->store(table_list->view_name.str, system_charset_info);
@@ -1126,13 +1083,16 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
protocol->store(buffer.ptr(), buffer.length(), buffer.charset());
if (protocol->write())
- goto error;
+ goto exit;
+ error= FALSE;
my_eof(thd);
- retval= FALSE; // ok
-error:
- DBUG_RETURN(retval);
+exit:
+ close_thread_tables(thd);
+ /* Release any metadata locks taken during SHOW CREATE. */
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+ DBUG_RETURN(error);
}
bool mysqld_show_create_db(THD *thd, char *dbname,
@@ -1165,7 +1125,7 @@ bool mysqld_show_create_db(THD *thd, char *dbname,
DBUG_RETURN(TRUE);
}
#endif
- if (is_schema_db(dbname))
+ if (is_infoschema_db(dbname))
{
dbname= INFORMATION_SCHEMA_NAME.str;
create.default_table_charset= system_charset_info;
@@ -1184,7 +1144,7 @@ bool mysqld_show_create_db(THD *thd, char *dbname,
field_list.push_back(new Item_empty_string("Database",NAME_CHAR_LEN));
field_list.push_back(new Item_empty_string("Create Database",1024));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
@@ -1230,7 +1190,8 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild)
DBUG_ENTER("mysqld_list_fields");
DBUG_PRINT("enter",("table: %s",table_list->table_name));
- if (open_normal_and_derived_tables(thd, table_list, 0,
+ if (open_normal_and_derived_tables(thd, table_list,
+ MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL,
DT_PREPARE | DT_CREATE))
DBUG_VOID_RETURN;
table= table_list->table;
@@ -1253,41 +1214,12 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild)
}
restore_record(table, s->default_values); // Get empty record
table->use_all_columns();
- if (thd->protocol->send_fields(&field_list, Protocol::SEND_DEFAULTS))
+ if (thd->protocol->send_result_set_metadata(&field_list, Protocol::SEND_DEFAULTS))
DBUG_VOID_RETURN;
my_eof(thd);
DBUG_VOID_RETURN;
}
-
-int
-mysqld_dump_create_info(THD *thd, TABLE_LIST *table_list, int fd)
-{
- Protocol *protocol= thd->protocol;
- String *packet= protocol->storage_packet();
- DBUG_ENTER("mysqld_dump_create_info");
- DBUG_PRINT("enter",("table: %s",table_list->table->s->table_name.str));
-
- protocol->prepare_for_resend();
- if (store_create_info(thd, table_list, packet, NULL,
- FALSE /* show_database */))
- DBUG_RETURN(-1);
-
- if (fd < 0)
- {
- if (protocol->write())
- DBUG_RETURN(-1);
- protocol->flush();
- }
- else
- {
- if (my_write(fd, (const uchar*) packet->ptr(), packet->length(),
- MYF(MY_WME)))
- DBUG_RETURN(-1);
- }
- DBUG_RETURN(0);
-}
-
/*
Go through all character combinations and ensure that sql_lex.cc can
parse it as an identifier.
@@ -1354,7 +1286,7 @@ append_identifier(THD *thd, String *packet, const char *name, uint length)
it's a keyword
*/
- VOID(packet->reserve(length*2 + 2));
+ (void) packet->reserve(length*2 + 2);
quote_char= (char) q;
if (packet->append(&quote_char, 1, system_charset_info))
return true;
@@ -1410,7 +1342,7 @@ int get_quote_char_for_identifier(THD *thd, const char *name, uint length)
if (length &&
!is_keyword(name,length) &&
!require_quotes(name, length) &&
- !(thd->options & OPTION_QUOTE_SHOW_CREATE))
+ !(thd->variables.option_bits & OPTION_QUOTE_SHOW_CREATE))
return EOF;
if (thd->variables.sql_mode & MODE_ANSI_QUOTES)
return '"';
@@ -1448,20 +1380,21 @@ static void append_directory(THD *thd, String *packet, const char *dir_type,
#define LIST_PROCESS_HOST_LEN 64
-static bool get_field_default_value(THD *thd, TABLE *table,
+static bool get_field_default_value(THD *thd, Field *timestamp_field,
Field *field, String *def_value,
bool quoted)
{
bool has_default;
bool has_now_default;
enum enum_field_types field_type= field->type();
+
/*
- We are using CURRENT_TIMESTAMP instead of NOW because it is
- more standard
+ We are using CURRENT_TIMESTAMP instead of NOW because it is
+ more standard
*/
- has_now_default= table->timestamp_field == field &&
- field->unireg_check != Field::TIMESTAMP_UN_FIELD;
-
+ has_now_default= (timestamp_field == field &&
+ field->unireg_check != Field::TIMESTAMP_UN_FIELD);
+
has_default= (field_type != FIELD_TYPE_BLOB &&
!(field->flags & NO_DEFAULT_VALUE_FLAG) &&
field->unireg_check != Field::NEXT_NUMBER &&
@@ -1480,7 +1413,7 @@ static bool get_field_default_value(THD *thd, TABLE *table,
if (field_type == MYSQL_TYPE_BIT)
{
longlong dec= field->val_int();
- char *ptr= longlong2str(dec, tmp + 2, 2, 1);
+ char *ptr= longlong2str(dec, tmp + 2, 2);
uint32 length= (uint32) (ptr - tmp);
tmp[0]= 'b';
tmp[1]= '\'';
@@ -1590,6 +1523,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
MODE_MYSQL323 |
MODE_MYSQL40)) != 0;
my_bitmap_map *old_map;
+ int error= 0;
DBUG_ENTER("store_create_info");
DBUG_PRINT("enter",("table: %s", table->s->table_name.str));
@@ -1704,7 +1638,8 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
}
if (!field->vcol_info &&
- get_field_default_value(thd, table, field, &def_value, 1))
+ get_field_default_value(thd, table->timestamp_field,
+ field, &def_value, 1))
{
packet->append(STRING_WITH_LEN(" DEFAULT "));
packet->append(def_value.ptr(), def_value.length(), system_charset_info);
@@ -1815,18 +1750,6 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
#ifdef WITH_PARTITION_STORAGE_ENGINE
show_table_options= TRUE;
#endif /* WITH_PARTITION_STORAGE_ENGINE */
- /*
- Get possible table space definitions and append them
- to the CREATE TABLE statement
- */
-
- if ((for_str= file->get_tablespace_name(thd,0,0)))
- {
- packet->append(STRING_WITH_LEN(" /*!50100 TABLESPACE "));
- packet->append(for_str, strlen(for_str));
- packet->append(STRING_WITH_LEN(" STORAGE DISK */"));
- my_free(for_str, MYF(0));
- }
/*
IF check_create_info
@@ -1868,8 +1791,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
end= longlong10_to_str(create_info.auto_increment_value, buff,10);
packet->append(buff, (uint) (end - buff));
}
-
-
+
if (share->table_charset &&
!(thd->variables.sql_mode & MODE_MYSQL323) &&
!(thd->variables.sql_mode & MODE_MYSQL40))
@@ -1963,27 +1885,35 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
{
- /*
- Partition syntax for CREATE TABLE is at the end of the syntax.
- */
- uint part_syntax_len;
- char *part_syntax;
if (table->part_info &&
- (!table->part_info->is_auto_partitioned) &&
- ((part_syntax= generate_partition_syntax(table->part_info,
+ !((table->s->db_type()->partition_flags() & HA_USE_AUTO_PARTITION) &&
+ table->part_info->is_auto_partitioned))
+ {
+ /*
+ Partition syntax for CREATE TABLE is at the end of the syntax.
+ */
+ uint part_syntax_len;
+ char *part_syntax;
+ String comment_start;
+ table->part_info->set_show_version_string(&comment_start);
+ if ((part_syntax= generate_partition_syntax(table->part_info,
&part_syntax_len,
FALSE,
- show_table_options))))
- {
- packet->append(STRING_WITH_LEN("\n/*!50100"));
- packet->append(part_syntax, part_syntax_len);
- packet->append(STRING_WITH_LEN(" */"));
- my_free(part_syntax, MYF(0));
+ show_table_options,
+ NULL, NULL,
+ comment_start.c_ptr())))
+ {
+ packet->append(comment_start);
+ if (packet->append(part_syntax, part_syntax_len) ||
+ packet->append(STRING_WITH_LEN(" */")))
+ error= 1;
+ my_free(part_syntax);
+ }
}
}
#endif
tmp_restore_column_map(table->read_set, old_map);
- DBUG_RETURN(0);
+ DBUG_RETURN(error);
}
@@ -2023,6 +1953,14 @@ static void store_key_options(THD *thd, String *packet, TABLE *table,
end= longlong10_to_str(key_info->block_size, buff, 10);
packet->append(buff, (uint) (end - buff));
}
+ DBUG_ASSERT(test(key_info->flags & HA_USES_COMMENT) ==
+ (key_info->comment.length > 0));
+ if (key_info->flags & HA_USES_COMMENT)
+ {
+ packet->append(STRING_WITH_LEN(" COMMENT "));
+ append_unescaped(packet, key_info->comment.str,
+ key_info->comment.length);
+ }
}
}
@@ -2176,7 +2114,7 @@ public:
time_t start_time;
uint command;
const char *user,*host,*db,*proc_info,*state_info;
- char *query;
+ CSET_STRING query_string;
double progress;
};
@@ -2184,6 +2122,30 @@ public:
template class I_List<thread_info>;
#endif
+static const char *thread_state_info(THD *tmp)
+{
+#ifndef EMBEDDED_LIBRARY
+ if (tmp->net.reading_or_writing)
+ {
+ if (tmp->net.reading_or_writing == 2)
+ return "Writing to net";
+ else if (tmp->command == COM_SLEEP)
+ return "";
+ else
+ return "Reading from net";
+ }
+ else
+#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;
+ }
+}
+
void mysqld_list_processes(THD *thd,const char *user, bool verbose)
{
Item *field;
@@ -2195,7 +2157,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
DBUG_ENTER("mysqld_list_processes");
field_list.push_back(new Item_int("Id", 0, MY_INT32_NUM_DECIMAL_DIGITS));
- field_list.push_back(new Item_empty_string("User",16));
+ field_list.push_back(new Item_empty_string("User", USERNAME_CHAR_LENGTH));
field_list.push_back(new Item_empty_string("Host",LIST_PROCESS_HOST_LEN));
field_list.push_back(field=new Item_empty_string("db",NAME_CHAR_LEN));
field->maybe_null=1;
@@ -2206,103 +2168,90 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
field->maybe_null=1;
field_list.push_back(field=new Item_empty_string("Info",max_query_length));
field->maybe_null=1;
- if (!thd->variables.old_mode)
+ if (!thd->variables.old_mode &&
+ !(thd->variables.old_behavior & OLD_MODE_NO_PROGRESS_INFO))
{
field_list.push_back(field= new Item_float("Progress", 0.0, 3, 7));
field->maybe_null= 0;
}
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_VOID_RETURN;
- VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list
- if (!thd->killed)
+ if (thd->killed)
+ DBUG_VOID_RETURN;
+
+ mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
+ I_List_iterator<THD> it(threads);
+ THD *tmp;
+ while ((tmp=it++))
{
- I_List_iterator<THD> it(threads);
- THD *tmp;
- while ((tmp=it++))
+ Security_context *tmp_sctx= tmp->security_ctx;
+ struct st_my_thread_var *mysys_var;
+ if ((tmp->vio_ok() || tmp->system_thread) &&
+ (!user || (tmp_sctx->user && !strcmp(tmp_sctx->user, user))))
{
- Security_context *tmp_sctx= tmp->security_ctx;
- struct st_my_thread_var *mysys_var;
- if ((tmp->vio_ok() || tmp->system_thread) &&
- (!user || (tmp_sctx->user && !strcmp(tmp_sctx->user, user))))
- {
- thread_info *thd_info= new thread_info;
-
- thd_info->thread_id=tmp->thread_id;
- thd_info->user= thd->strdup(tmp_sctx->user ? tmp_sctx->user :
- (tmp->system_thread ?
- "system user" : "unauthenticated user"));
- if (tmp->peer_port && (tmp_sctx->host || tmp_sctx->ip) &&
- thd->security_ctx->host_or_ip[0])
- {
- if ((thd_info->host= (char*) thd->alloc(LIST_PROCESS_HOST_LEN+1)))
- my_snprintf((char *) thd_info->host, LIST_PROCESS_HOST_LEN,
- "%s:%u", tmp_sctx->host_or_ip, tmp->peer_port);
- }
- else
- thd_info->host= thd->strdup(tmp_sctx->host_or_ip[0] ?
- tmp_sctx->host_or_ip :
- tmp_sctx->host ? tmp_sctx->host : "");
- thd_info->command=(int) tmp->command;
- pthread_mutex_lock(&tmp->LOCK_thd_data);
- if ((mysys_var= tmp->mysys_var))
- pthread_mutex_lock(&mysys_var->mutex);
- thd_info->proc_info= (char*) (tmp->killed >= KILL_QUERY ?
- "Killed" : 0);
-#ifndef EMBEDDED_LIBRARY
- thd_info->state_info= (char*) (tmp->net.reading_or_writing ?
- (tmp->net.reading_or_writing == 2 ?
- "Writing to net" :
- thd_info->command == COM_SLEEP ? "" :
- "Reading from net") :
- tmp->proc_info ? tmp->proc_info :
- tmp->mysys_var &&
- tmp->mysys_var->current_cond ?
- "Waiting on cond" : NullS);
-#else
- thd_info->state_info= (char*)"Writing to net";
-#endif
- if (mysys_var)
- pthread_mutex_unlock(&mysys_var->mutex);
- pthread_mutex_unlock(&tmp->LOCK_thd_data);
-
- thd_info->start_time= tmp->start_time;
+ thread_info *thd_info= new thread_info;
- thd_info->query=0;
- thd_info->progress= 0.0;
-
- /* Lock THD mutex that protects its data when looking at it. */
- pthread_mutex_lock(&tmp->LOCK_thd_data);
- if (tmp->query())
- {
- uint length= min(max_query_length, tmp->query_length());
- thd_info->query= (char*) thd->strmake(tmp->query(),length);
- }
-
- /*
- 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= 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);
- }
+ thd_info->thread_id=tmp->thread_id;
+ thd_info->user= thd->strdup(tmp_sctx->user ? tmp_sctx->user :
+ (tmp->system_thread ?
+ "system user" : "unauthenticated user"));
+ if (tmp->peer_port && (tmp_sctx->host || tmp_sctx->ip) &&
+ thd->security_ctx->host_or_ip[0])
+ {
+ if ((thd_info->host= (char*) thd->alloc(LIST_PROCESS_HOST_LEN+1)))
+ my_snprintf((char *) thd_info->host, LIST_PROCESS_HOST_LEN,
+ "%s:%u", tmp_sctx->host_or_ip, tmp->peer_port);
+ }
+ else
+ thd_info->host= thd->strdup(tmp_sctx->host_or_ip[0] ?
+ tmp_sctx->host_or_ip :
+ tmp_sctx->host ? tmp_sctx->host : "");
+ thd_info->command=(int) tmp->command;
+ 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);
- if ((thd_info->db= tmp->db)) // Safe test
- thd_info->db= thd->strdup(thd_info->db);
- pthread_mutex_unlock(&tmp->LOCK_thd_data);
+ /* Lock THD mutex that protects its data when looking at it. */
+ if (tmp->query())
+ {
+ uint length= 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());
+ }
- thread_infos.append(thd_info);
+ /*
+ 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= 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;
+ thd_info->start_time= tmp->start_time;
+ mysql_mutex_unlock(&tmp->LOCK_thd_data);
+ thread_infos.append(thd_info);
}
}
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ mysql_mutex_unlock(&LOCK_thread_count);
thread_info *thd_info;
time_t now= my_time(0);
@@ -2325,8 +2274,10 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
else
protocol->store_null();
protocol->store(thd_info->state_info, system_charset_info);
- protocol->store(thd_info->query, system_charset_info);
- if (!thd->variables.old_mode)
+ protocol->store(thd_info->query_string.str(),
+ thd_info->query_string.charset());
+ if (!thd->variables.old_mode &&
+ !(thd->variables.old_behavior & OLD_MODE_NO_PROGRESS_INFO))
protocol->store(thd_info->progress, 3, &store_buffer);
if (protocol->write())
break; /* purecov: inspected */
@@ -2341,12 +2292,14 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
CHARSET_INFO *cs= system_charset_info;
char *user;
my_hrtime_t unow= my_hrtime();
- DBUG_ENTER("fill_process_list");
+ DBUG_ENTER("fill_schema_processlist");
+
+ DEBUG_SYNC(thd,"fill_schema_processlist_after_unow");
user= thd->security_ctx->master_access & PROCESS_ACL ?
NullS : thd->security_ctx->priv_user;
- VOID(pthread_mutex_lock(&LOCK_thread_count));
+ mysql_mutex_lock(&LOCK_thread_count);
if (!thd->killed)
{
@@ -2383,8 +2336,16 @@ 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))
+ {
+ table->field[3]->store(db, strlen(db), cs);
+ table->field[3]->set_notnull();
+ }
+
if ((mysys_var= tmp->mysys_var))
- pthread_mutex_lock(&mysys_var->mutex);
+ mysql_mutex_lock(&mysys_var->mutex);
/* COMMAND */
if ((val= (char *) ((tmp->killed >= KILL_QUERY ?
"Killed" : 0))))
@@ -2393,39 +2354,27 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
table->field[4]->store(command_name[tmp->command].str,
command_name[tmp->command].length, cs);
/* MYSQL_TIME */
- const ulonglong utime= (tmp->start_time ?
- (unow.val - tmp->start_time * HRTIME_RESOLUTION -
- tmp->start_time_sec_part) : 0);
+ ulonglong start_utime= tmp->start_time * HRTIME_RESOLUTION + tmp->start_time_sec_part;
+ ulonglong utime= start_utime && start_utime < unow.val
+ ? unow.val - start_utime : 0;
table->field[5]->store(utime / HRTIME_RESOLUTION, TRUE);
/* STATE */
-#ifndef EMBEDDED_LIBRARY
- val= (char*) (tmp->net.reading_or_writing ?
- (tmp->net.reading_or_writing == 2 ?
- "Writing to net" :
- tmp->command == COM_SLEEP ? "" :
- "Reading from net") :
- tmp->proc_info ? tmp->proc_info :
- tmp->mysys_var &&
- tmp->mysys_var->current_cond ?
- "Waiting on cond" : NullS);
-#else
- val= (char *) (tmp->proc_info ? tmp->proc_info : NullS);
-#endif
- if (val)
+ if ((val= thread_state_info(tmp)))
{
table->field[6]->store(val, strlen(val), cs);
table->field[6]->set_notnull();
}
if (mysys_var)
- pthread_mutex_unlock(&mysys_var->mutex);
+ 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. */
- pthread_mutex_lock(&tmp->LOCK_thd_data);
+ mysql_mutex_lock(&tmp->LOCK_thd_data);
if (tmp->query())
{
table->field[7]->store(tmp->query(),
@@ -2445,25 +2394,17 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
table->field[11]->store((double) tmp->progress.counter /
(double) max_counter*100.0);
}
-
- /* DB */
- if ((db= tmp->db))
- {
- table->field[3]->store(db, strlen(db), cs);
- table->field[3]->set_notnull();
- }
-
- pthread_mutex_unlock(&tmp->LOCK_thd_data);
+ mysql_mutex_unlock(&tmp->LOCK_thd_data);
if (schema_table_store_record(thd, table))
{
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ mysql_mutex_unlock(&LOCK_thread_count);
DBUG_RETURN(1);
}
}
}
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ mysql_mutex_unlock(&LOCK_thread_count);
DBUG_RETURN(0);
}
@@ -2473,10 +2414,13 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
static DYNAMIC_ARRAY all_status_vars;
static bool status_vars_inited= 0;
+
+C_MODE_START
static int show_var_cmp(const void *var1, const void *var2)
{
return strcmp(((SHOW_VAR*)var1)->name, ((SHOW_VAR*)var2)->name);
}
+C_MODE_END
/*
deletes all the SHOW_UNDEF elements from the array and calls
@@ -2522,7 +2466,7 @@ int add_status_vars(SHOW_VAR *list)
{
int res= 0;
if (status_vars_inited)
- pthread_mutex_lock(&LOCK_status);
+ mysql_mutex_lock(&LOCK_status);
if (!all_status_vars.buffer && // array is not allocated yet - do it now
my_init_dynamic_array(&all_status_vars, sizeof(SHOW_VAR), 200, 20))
{
@@ -2537,7 +2481,7 @@ int add_status_vars(SHOW_VAR *list)
sort_dynamic(&all_status_vars, show_var_cmp);
err:
if (status_vars_inited)
- pthread_mutex_unlock(&LOCK_status);
+ mysql_mutex_unlock(&LOCK_status);
return res;
}
@@ -2599,7 +2543,7 @@ void remove_status_vars(SHOW_VAR *list)
{
if (status_vars_inited)
{
- pthread_mutex_lock(&LOCK_status);
+ mysql_mutex_lock(&LOCK_status);
SHOW_VAR *all= dynamic_element(&all_status_vars, 0, SHOW_VAR *);
for (; list->name; list++)
@@ -2620,7 +2564,7 @@ void remove_status_vars(SHOW_VAR *list)
}
}
shrink_var_array(&all_status_vars);
- pthread_mutex_unlock(&LOCK_status);
+ mysql_mutex_unlock(&LOCK_status);
}
else
{
@@ -2704,11 +2648,11 @@ static bool show_status_array(THD *thd, const char *wild,
char *value=var->value;
const char *pos, *end; // We assign a lot of const's
- pthread_mutex_lock(&LOCK_global_system_variables);
if (show_type == SHOW_SYS)
{
sys_var *var= ((sys_var *) value);
show_type= var->show_type();
+ mysql_mutex_lock(&LOCK_global_system_variables);
value= (char*) var->value_ptr(thd, value_type, &null_lex_str);
charset= var->charset(thd);
}
@@ -2723,19 +2667,20 @@ static bool show_status_array(THD *thd, const char *wild,
value= ((char *) status_var + (intptr) value);
/* fall through */
case SHOW_DOUBLE:
- end= buff + my_sprintf(buff, (buff, "%f", *(double*) value));
+ /* 6 is the default precision for '%f' in sprintf() */
+ end= buff + my_fcvt(*(double *) value, 6, buff, NULL);
break;
case SHOW_LONG_STATUS:
value= ((char *) status_var + (intptr) value);
/* fall through */
- case SHOW_LONG:
+ case SHOW_ULONG:
case SHOW_LONG_NOFLUSH: // the difference lies in refresh_status()
end= int10_to_str(*(long*) value, buff, 10);
break;
case SHOW_LONGLONG_STATUS:
value= ((char *) status_var + (intptr) value);
/* fall through */
- case SHOW_LONGLONG:
+ case SHOW_ULONGLONG:
end= longlong10_to_str(*(longlong*) value, buff, 10);
break;
case SHOW_HA_ROWS:
@@ -2747,9 +2692,18 @@ static bool show_status_array(THD *thd, const char *wild,
case SHOW_MY_BOOL:
end= strmov(buff, *(my_bool*) value ? "ON" : "OFF");
break;
- case SHOW_INT:
+ case SHOW_UINT:
+ end= int10_to_str((long) *(uint*) value, buff, 10);
+ break;
+ case SHOW_SINT:
end= int10_to_str((long) *(int*) value, buff, -10);
break;
+ case SHOW_SLONG:
+ end= int10_to_str(*(long*) value, buff, -10);
+ break;
+ case SHOW_SLONGLONG:
+ end= longlong10_to_str(*(longlong*) value, buff, -10);
+ break;
case SHOW_HAVE:
{
SHOW_COMP_OPTION tmp= *(SHOW_COMP_OPTION*) value;
@@ -2768,9 +2722,26 @@ static bool show_status_array(THD *thd, const char *wild,
{
if (!(pos= *(char**) value))
pos= "";
+
+ DBUG_EXECUTE_IF("alter_server_version_str",
+ if (!my_strcasecmp(system_charset_info,
+ variables->name,
+ "version")) {
+ pos= "some-other-version";
+ });
+
end= strend(pos);
break;
}
+ case SHOW_LEX_STRING:
+ {
+ LEX_STRING *ls=(LEX_STRING*)value;
+ if (!(pos= ls->str))
+ end= pos= "";
+ else
+ end= pos + ls->length;
+ break;
+ }
case SHOW_UNDEF:
break; // Return empty string
case SHOW_SYS: // Cannot happen
@@ -2778,11 +2749,13 @@ static bool show_status_array(THD *thd, const char *wild,
DBUG_ASSERT(0);
break;
}
- pthread_mutex_unlock(&LOCK_global_system_variables);
table->field[1]->store(pos, (uint32) (end - pos), charset);
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
table->field[1]->set_notnull();
+ if (var->type == SHOW_SYS)
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+
if (schema_table_store_record(thd, table))
{
res= TRUE;
@@ -2796,7 +2769,7 @@ end:
DBUG_RETURN(res);
}
-#ifdef COMPLEAT_PATCH_NOT_ADDED_YET
+#ifdef COMPLETE_PATCH_NOT_ADDED_YET
/*
Aggregate values for mapped_user entries by their role.
@@ -2813,10 +2786,10 @@ end:
static int aggregate_user_stats(HASH *all_user_stats, HASH *agg_user_stats)
{
DBUG_ENTER("aggregate_user_stats");
- if (hash_init(agg_user_stats, system_charset_info,
+ if (my_hash_init(agg_user_stats, system_charset_info,
max(all_user_stats->records, 1),
- 0, 0, (hash_get_key)get_key_user_stats,
- (hash_free_key)free_user_stats, 0))
+ 0, 0, (my_hash_get_key)get_key_user_stats,
+ (my_hash_free_key)free_user_stats, 0))
{
sql_print_error("Malloc in aggregate_user_stats failed");
DBUG_RETURN(1);
@@ -2824,11 +2797,11 @@ static int aggregate_user_stats(HASH *all_user_stats, HASH *agg_user_stats)
for (uint i= 0; i < all_user_stats->records; i++)
{
- USER_STATS *user= (USER_STATS*)hash_element(all_user_stats, i);
+ USER_STATS *user= (USER_STATS*)my_hash_element(all_user_stats, i);
USER_STATS *agg_user;
uint name_length= strlen(user->priv_user);
- if (!(agg_user= (USER_STATS*) hash_search(agg_user_stats,
+ if (!(agg_user= (USER_STATS*) my_hash_search(agg_user_stats,
(uchar*)user->priv_user,
name_length)))
{
@@ -2907,7 +2880,7 @@ int send_user_stats(THD* thd, HASH *all_user_stats, TABLE *table)
for (uint i= 0; i < all_user_stats->records; i++)
{
uint j= 0;
- USER_STATS *user_stats= (USER_STATS*) hash_element(all_user_stats, i);
+ USER_STATS *user_stats= (USER_STATS*) my_hash_element(all_user_stats, i);
table->field[j++]->store(user_stats->user, user_stats->user_name_length,
system_charset_info);
@@ -2962,17 +2935,17 @@ int fill_schema_user_stats(THD* thd, TABLE_LIST* tables, COND* cond)
int result;
DBUG_ENTER("fill_schema_user_stats");
- if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
- DBUG_RETURN(1);
+ if (check_global_access(thd, SUPER_ACL | PROCESS_ACL, true))
+ DBUG_RETURN(0);
/*
Iterates through all the global stats and sends them to the client.
Pattern matching on the client IP is supported.
*/
- pthread_mutex_lock(&LOCK_global_user_client_stats);
+ mysql_mutex_lock(&LOCK_global_user_client_stats);
result= send_user_stats(thd, &global_user_stats, table) != 0;
- pthread_mutex_unlock(&LOCK_global_user_client_stats);
+ mysql_mutex_unlock(&LOCK_global_user_client_stats);
DBUG_PRINT("exit", ("result: %d", result));
DBUG_RETURN(result);
@@ -2997,17 +2970,17 @@ int fill_schema_client_stats(THD* thd, TABLE_LIST* tables, COND* cond)
int result;
DBUG_ENTER("fill_schema_client_stats");
- if (check_global_access(thd, SUPER_ACL | PROCESS_ACL))
- DBUG_RETURN(1);
+ if (check_global_access(thd, SUPER_ACL | PROCESS_ACL, true))
+ DBUG_RETURN(0);
/*
Iterates through all the global stats and sends them to the client.
Pattern matching on the client IP is supported.
*/
- pthread_mutex_lock(&LOCK_global_user_client_stats);
+ mysql_mutex_lock(&LOCK_global_user_client_stats);
result= send_user_stats(thd, &global_client_stats, table) != 0;
- pthread_mutex_unlock(&LOCK_global_user_client_stats);
+ mysql_mutex_unlock(&LOCK_global_user_client_stats);
DBUG_PRINT("exit", ("result: %d", result));
DBUG_RETURN(result);
@@ -3021,12 +2994,12 @@ int fill_schema_table_stats(THD *thd, TABLE_LIST *tables, COND *cond)
TABLE *table= tables->table;
DBUG_ENTER("fill_schema_table_stats");
- pthread_mutex_lock(&LOCK_global_table_stats);
+ mysql_mutex_lock(&LOCK_global_table_stats);
for (uint i= 0; i < global_table_stats.records; i++)
{
char *end_of_schema;
TABLE_STATS *table_stats=
- (TABLE_STATS*)hash_element(&global_table_stats, i);
+ (TABLE_STATS*)my_hash_element(&global_table_stats, i);
TABLE_LIST tmp_table;
size_t schema_length, table_name_length;
@@ -3038,9 +3011,8 @@ int fill_schema_table_stats(THD *thd, TABLE_LIST *tables, COND *cond)
tmp_table.db= table_stats->table;
tmp_table.table_name= end_of_schema+1;
tmp_table.grant.privilege= 0;
- if (check_access(thd, SELECT_ACL | EXTRA_ACL, tmp_table.db,
- &tmp_table.grant.privilege, 0, 0,
- is_schema_db(tmp_table.db)) ||
+ if (check_access(thd, SELECT_ACL, tmp_table.db,
+ &tmp_table.grant.privilege, NULL, 0, 1) ||
check_grant(thd, SELECT_ACL, &tmp_table, 1, UINT_MAX,
1))
continue;
@@ -3055,11 +3027,11 @@ int fill_schema_table_stats(THD *thd, TABLE_LIST *tables, COND *cond)
TRUE);
if (schema_table_store_record(thd, table))
{
- VOID(pthread_mutex_unlock(&LOCK_global_table_stats));
+ mysql_mutex_unlock(&LOCK_global_table_stats);
DBUG_RETURN(1);
}
}
- pthread_mutex_unlock(&LOCK_global_table_stats);
+ mysql_mutex_unlock(&LOCK_global_table_stats);
DBUG_RETURN(0);
}
@@ -3071,11 +3043,11 @@ int fill_schema_index_stats(THD *thd, TABLE_LIST *tables, COND *cond)
TABLE *table= tables->table;
DBUG_ENTER("fill_schema_index_stats");
- pthread_mutex_lock(&LOCK_global_index_stats);
+ mysql_mutex_lock(&LOCK_global_index_stats);
for (uint i= 0; i < global_index_stats.records; i++)
{
INDEX_STATS *index_stats =
- (INDEX_STATS*) hash_element(&global_index_stats, i);
+ (INDEX_STATS*) my_hash_element(&global_index_stats, i);
TABLE_LIST tmp_table;
char *index_name;
size_t schema_name_length, table_name_length, index_name_length;
@@ -3084,9 +3056,8 @@ int fill_schema_index_stats(THD *thd, TABLE_LIST *tables, COND *cond)
tmp_table.db= index_stats->index;
tmp_table.table_name= strend(index_stats->index)+1;
tmp_table.grant.privilege= 0;
- if (check_access(thd, SELECT_ACL | EXTRA_ACL, tmp_table.db,
- &tmp_table.grant.privilege, 0, 0,
- is_schema_db(tmp_table.db)) ||
+ if (check_access(thd, SELECT_ACL, tmp_table.db,
+ &tmp_table.grant.privilege, NULL, 0, 1) ||
check_grant(thd, SELECT_ACL, &tmp_table, 1, UINT_MAX, 1))
continue;
@@ -3105,11 +3076,11 @@ int fill_schema_index_stats(THD *thd, TABLE_LIST *tables, COND *cond)
if (schema_table_store_record(thd, table))
{
- VOID(pthread_mutex_unlock(&LOCK_global_index_stats));
+ mysql_mutex_unlock(&LOCK_global_index_stats);
DBUG_RETURN(1);
}
}
- pthread_mutex_unlock(&LOCK_global_index_stats);
+ mysql_mutex_unlock(&LOCK_global_index_stats);
DBUG_RETURN(0);
}
@@ -3121,7 +3092,7 @@ void calc_sum_of_all_status(STATUS_VAR *to)
DBUG_ENTER("calc_sum_of_all_status");
/* Ensure that thread id not killed during loop */
- VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list
+ mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
I_List_iterator<THD> it(threads);
THD *tmp;
@@ -3132,8 +3103,8 @@ void calc_sum_of_all_status(STATUS_VAR *to)
/* Add to this status from existing threads */
while ((tmp= it++))
add_to_status(to, &tmp->status_var);
-
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
+
+ mysql_mutex_unlock(&LOCK_thread_count);
DBUG_VOID_RETURN;
}
@@ -3141,10 +3112,38 @@ void calc_sum_of_all_status(STATUS_VAR *to)
/* This is only used internally, but we need it here as a forward reference */
extern ST_SCHEMA_TABLE schema_tables[];
+/**
+ Condition pushdown used for INFORMATION_SCHEMA / SHOW queries.
+ This structure is to implement an optimization when
+ accessing data dictionary data in the INFORMATION_SCHEMA
+ or SHOW commands.
+ When the query contain a TABLE_SCHEMA or TABLE_NAME clause,
+ narrow the search for data based on the constraints given.
+*/
typedef struct st_lookup_field_values
{
- LEX_STRING db_value, table_value;
- bool wild_db_value, wild_table_value;
+ /**
+ Value of a TABLE_SCHEMA clause.
+ Note that this value length may exceed @c NAME_LEN.
+ @sa wild_db_value
+ */
+ LEX_STRING db_value;
+ /**
+ Value of a TABLE_NAME clause.
+ Note that this value length may exceed @c NAME_LEN.
+ @sa wild_table_value
+ */
+ LEX_STRING table_value;
+ /**
+ True when @c db_value is a LIKE clause,
+ false when @c db_value is an '=' clause.
+ */
+ bool wild_db_value;
+ /**
+ True when @c table_value is a LIKE clause,
+ false when @c table_value is an '=' clause.
+ */
+ bool wild_table_value;
} LOOKUP_FIELD_VALUES;
@@ -3169,7 +3168,7 @@ bool schema_table_store_record(THD *thd, TABLE *table)
{
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))
+ &param->recinfo, error, 0, NULL))
return 1;
}
@@ -3182,7 +3181,7 @@ static int make_table_list(THD *thd, SELECT_LEX *sel,
{
Table_ident *table_ident;
table_ident= new Table_ident(thd, *db_name, *table_name, 1);
- if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ))
+ if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ, MDL_SHARED_READ))
return 1;
return 0;
}
@@ -3226,13 +3225,13 @@ bool get_lookup_value(THD *thd, Item_func *item_func,
Item_field *item_field;
CHARSET_INFO *cs= system_charset_info;
- if (item_func->arguments()[0]->type() == Item::FIELD_ITEM &&
+ if (item_func->arguments()[0]->real_item()->type() == Item::FIELD_ITEM &&
item_func->arguments()[1]->const_item())
{
idx_field= 0;
idx_val= 1;
}
- else if (item_func->arguments()[1]->type() == Item::FIELD_ITEM &&
+ else if (item_func->arguments()[1]->real_item()->type() == Item::FIELD_ITEM &&
item_func->arguments()[0]->const_item())
{
idx_field= 1;
@@ -3241,7 +3240,7 @@ bool get_lookup_value(THD *thd, Item_func *item_func,
else
return 0;
- item_field= (Item_field*) item_func->arguments()[idx_field];
+ item_field= (Item_field*) item_func->arguments()[idx_field]->real_item();
if (table->table != item_field->field->table)
return 0;
tmp_str= item_func->arguments()[idx_val]->val_str(&str_buff);
@@ -3549,13 +3548,24 @@ int make_db_list(THD *thd, List<LEX_STRING> *files,
/*
- If we have db lookup vaule we just add it to list and
- exit from the function
+ If we have db lookup value we just add it to list and
+ exit from the function.
+ We don't do this for database names longer than the maximum
+ name length.
*/
if (lookup_field_vals->db_value.str)
{
- if (is_schema_db(lookup_field_vals->db_value.str,
- lookup_field_vals->db_value.length))
+ if (lookup_field_vals->db_value.length > NAME_LEN)
+ {
+ /*
+ Impossible value for a database name,
+ found in a WHERE DATABASE_NAME = 'xxx' clause.
+ */
+ return 0;
+ }
+
+ if (is_infoschema_db(lookup_field_vals->db_value.str,
+ lookup_field_vals->db_value.length))
{
*with_i_schema= 1;
if (files->push_back(i_s_name_copy))
@@ -3690,6 +3700,15 @@ make_table_name_list(THD *thd, List<LEX_STRING> *table_names, LEX *lex,
if (!lookup_field_vals->wild_table_value &&
lookup_field_vals->table_value.str)
{
+ if (lookup_field_vals->table_value.length > NAME_LEN)
+ {
+ /*
+ Impossible value for a table name,
+ found in a WHERE TABLE_NAME = 'xxx' clause.
+ */
+ return 0;
+ }
+
if (with_i_schema)
{
LEX_STRING *name;
@@ -3712,9 +3731,9 @@ make_table_name_list(THD *thd, List<LEX_STRING> *table_names, LEX *lex,
Check that table is relevant in current transaction.
(used for ndb engine, see ndbcluster_find_files(), ha_ndbcluster.cc)
*/
- VOID(ha_find_files(thd, db_name->str, path,
+ (void) ha_find_files(thd, db_name->str, path,
lookup_field_vals->table_value.str, 0,
- table_names));
+ table_names);
}
return 0;
}
@@ -3763,20 +3782,24 @@ make_table_name_list(THD *thd, List<LEX_STRING> *table_names, LEX *lex,
@param open_tables_state_backup Open_tables_state object which is used
to save/restore original status of
variables related to open tables state.
+ @param can_deadlock Indicates that deadlocks are possible
+ due to metadata locks, so to avoid
+ them we should not wait in case if
+ conflicting lock is present.
@retval FALSE - Success.
@retval TRUE - Failure.
*/
-
static bool
fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys,
TABLE *table, ST_SCHEMA_TABLE *schema_table,
LEX_STRING *orig_db_name,
LEX_STRING *orig_table_name,
- Open_tables_state *open_tables_state_backup)
+ Open_tables_backup *open_tables_state_backup,
+ bool can_deadlock)
{
Query_arena i_s_arena(thd->mem_root,
- Query_arena::CONVENTIONAL_EXECUTION),
+ Query_arena::STMT_CONVENTIONAL_EXECUTION),
backup_arena, *old_arena;
LEX *old_lex= thd->lex, temp_lex, *lex;
LEX_STRING db_name, table_name;
@@ -3860,11 +3883,15 @@ fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys,
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()'
+ SQLCOM_SHOW_FIELDS is used because it satisfies
+ 'only_view_structure()'.
*/
lex->sql_command= SQLCOM_SHOW_FIELDS;
result= open_normal_and_derived_tables(thd, table_list,
- MYSQL_LOCK_IGNORE_FLUSH,
+ (MYSQL_OPEN_IGNORE_FLUSH |
+ MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
+ (can_deadlock ?
+ MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0)),
DT_PREPARE | DT_CREATE);
/*
Restore old value of sql_command back as it is being looked at in
@@ -3872,6 +3899,8 @@ fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys,
*/
lex->sql_command= old_lex->sql_command;
+ DEBUG_SYNC(thd, "after_open_table_ignore_flush");
+
/*
XXX: show_table_list has a flag i_is_requested,
and when it's set, open_normal_and_derived_tables()
@@ -3884,14 +3913,14 @@ fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys,
of backward compatibility.
*/
if (!is_show_fields_or_keys && result && thd->is_error() &&
- thd->main_da.sql_errno() == ER_NO_SUCH_TABLE)
+ thd->stmt_da->sql_errno() == ER_NO_SUCH_TABLE)
{
/*
Hide error for a non-existing table.
For example, this error can occur when we use a where condition
with a db name and table, but the table does not exist.
*/
- result= 0;
+ result= false;
thd->clear_error();
}
else
@@ -3902,6 +3931,7 @@ fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys,
orig_table_name);
}
+
end:
lex->unit.cleanup();
@@ -3918,6 +3948,12 @@ end:
*/
thd->temporary_tables= NULL;
close_thread_tables(thd);
+ /*
+ Release metadata lock we might have acquired.
+ See comment in fill_schema_table_from_frm() for details.
+ */
+ thd->mdl_context.rollback_to_savepoint(open_tables_state_backup->mdl_system_tables_svp);
+
thd->lex= old_lex;
thd->stmt_arena= old_arena;
@@ -3957,7 +3993,7 @@ static int fill_schema_table_names(THD *thd, TABLE_LIST *tables,
char path[FN_REFLEN + 1];
(void) build_table_filename(path, sizeof(path) - 1, db_name->str,
table_name->str, reg_ext, 0);
- switch (mysql_frm_type(thd, path, &not_used)) {
+ switch (dd_frm_type(thd, path, &not_used)) {
case FRMTYPE_ERROR:
table->field[3]->store(STRING_WITH_LEN("ERROR"),
system_charset_info);
@@ -3973,7 +4009,7 @@ static int fill_schema_table_names(THD *thd, TABLE_LIST *tables,
default:
DBUG_ASSERT(0);
}
- if (thd->is_error() && thd->main_da.sql_errno() == ER_NO_SUCH_TABLE)
+ if (thd->is_error() && thd->stmt_da->sql_errno() == ER_NO_SUCH_TABLE)
{
thd->clear_error();
return 0;
@@ -4036,6 +4072,61 @@ uint get_table_open_method(TABLE_LIST *tables,
/**
+ Try acquire high priority share metadata lock on a table (with
+ optional wait for conflicting locks to go away).
+
+ @param thd Thread context.
+ @param mdl_request Pointer to memory to be used for MDL_request
+ object for a lock request.
+ @param table Table list element for the table
+ @param can_deadlock Indicates that deadlocks are possible due to
+ metadata locks, so to avoid them we should not
+ wait in case if conflicting lock is present.
+
+ @note This is an auxiliary function to be used in cases when we want to
+ access table's description by looking up info in TABLE_SHARE without
+ going through full-blown table open.
+ @note This function assumes that there are no other metadata lock requests
+ in the current metadata locking context.
+
+ @retval FALSE No error, if lock was obtained TABLE_LIST::mdl_request::ticket
+ is set to non-NULL value.
+ @retval TRUE Some error occured (probably thread was killed).
+*/
+
+static bool
+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,
+ MDL_SHARED_HIGH_PRIO, MDL_TRANSACTION);
+
+ if (can_deadlock)
+ {
+ /*
+ When .FRM is being open in order to get data for an I_S table,
+ we might have some tables not only open but also locked.
+ E.g. this happens when a SHOW or I_S statement is run
+ under LOCK TABLES or inside a stored function.
+ By waiting for the conflicting metadata lock to go away we
+ might create a deadlock which won't entirely belong to the
+ MDL subsystem and thus won't be detectable by this subsystem's
+ deadlock detector. To avoid such situation, when there are
+ other locked tables, we prefer not to wait on a conflicting
+ lock.
+ */
+ error= thd->mdl_context.try_acquire_lock(&table->mdl_request);
+ }
+ else
+ error= thd->mdl_context.acquire_lock(&table->mdl_request,
+ thd->variables.lock_wait_timeout);
+
+ return error;
+}
+
+
+/**
@brief Fill I_S table with data from FRM file only
@param[in] thd thread handler
@@ -4044,6 +4135,13 @@ uint get_table_open_method(TABLE_LIST *tables,
@param[in] db_name database name
@param[in] table_name table name
@param[in] schema_table_idx I_S table index
+ @param[in] open_tables_state_backup Open_tables_state object which is used
+ to save/restore original state of metadata
+ locks.
+ @param[in] can_deadlock Indicates that deadlocks are possible
+ due to metadata locks, so to avoid
+ them we should not wait in case if
+ conflicting lock is present.
@return Operation status
@retval 0 Table is processed and we can continue
@@ -4052,33 +4150,106 @@ uint get_table_open_method(TABLE_LIST *tables,
open_tables function for this table
*/
-static int fill_schema_table_from_frm(THD *thd,TABLE *table,
+static int fill_schema_table_from_frm(THD *thd, TABLE_LIST *tables,
ST_SCHEMA_TABLE *schema_table,
LEX_STRING *db_name,
LEX_STRING *table_name,
- enum enum_schema_tables schema_table_idx)
+ enum enum_schema_tables schema_table_idx,
+ Open_tables_backup *open_tables_state_backup,
+ bool can_deadlock)
{
+ TABLE *table= tables->table;
TABLE_SHARE *share;
TABLE tbl;
TABLE_LIST table_list;
uint res= 0;
- int error;
+ int not_used;
+ my_hash_value_type hash_value;
char key[MAX_DBKEY_LENGTH];
uint key_length;
+ char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1];
bzero((char*) &table_list, sizeof(TABLE_LIST));
bzero((char*) &tbl, sizeof(TABLE));
- table_list.table_name= table_name->str;
- table_list.db= db_name->str;
+ DBUG_ASSERT(db_name->length <= NAME_LEN);
+ DBUG_ASSERT(table_name->length <= NAME_LEN);
+
+ if (lower_case_table_names)
+ {
+ /*
+ In lower_case_table_names > 0 metadata locking and table definition
+ cache subsystems require normalized (lowercased) database and table
+ names as input.
+ */
+ 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;
+ }
+ else
+ {
+ table_list.table_name= table_name->str;
+ table_list.db= db_name->str;
+ }
+
+ /*
+ TODO: investigate if in this particular situation we can get by
+ simply obtaining internal lock of the data-dictionary
+ instead of obtaining full-blown metadata lock.
+ */
+ if (try_acquire_high_prio_shared_mdl_lock(thd, &table_list, can_deadlock))
+ {
+ /*
+ Some error occured (most probably we have been killed while
+ waiting for conflicting locks to go away), let the caller to
+ handle the situation.
+ */
+ return 1;
+ }
+
+ if (! table_list.mdl_request.ticket)
+ {
+ /*
+ We are in situation when we have encountered conflicting metadata
+ lock and deadlocks can occur due to waiting for it to go away.
+ So instead of waiting skip this table with an appropriate warning.
+ */
+ DBUG_ASSERT(can_deadlock);
+
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_I_S_SKIPPED_TABLE,
+ ER(ER_WARN_I_S_SKIPPED_TABLE),
+ table_list.db, table_list.table_name);
+ return 0;
+ }
+
+ if (schema_table->i_s_requested_object & OPEN_TRIGGER_ONLY)
+ {
+ init_sql_alloc(&tbl.mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
+ if (!Table_triggers_list::check_n_load(thd, db_name->str,
+ table_name->str, &tbl, 1))
+ {
+ table_list.table= &tbl;
+ res= schema_table->process_table(thd, &table_list, table,
+ res, db_name, table_name);
+ delete tbl.triggers;
+ }
+ free_root(&tbl.mem_root, MYF(0));
+ goto end;
+ }
+
key_length= create_table_def_key(thd, key, &table_list, 0);
- pthread_mutex_lock(&LOCK_open);
+ hash_value= my_calc_hash(&table_def_cache, (uchar*) key, key_length);
+ mysql_mutex_lock(&LOCK_open);
share= get_table_share(thd, &table_list, key,
- key_length, OPEN_VIEW, &error);
+ key_length, OPEN_VIEW, &not_used, hash_value);
if (!share)
{
res= 0;
- goto err;
+ goto end_unlock;
}
if (share->is_view)
@@ -4087,7 +4258,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table,
{
/* skip view processing */
res= 0;
- goto err1;
+ goto end_share;
}
else if (schema_table->i_s_requested_object & OPEN_VIEW_FULL)
{
@@ -4096,35 +4267,92 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table,
open_normal_and_derived_tables()
*/
res= 1;
- goto err1;
+ goto end_share;
}
}
- if (share->is_view ||
- !open_table_from_share(thd, share, table_name->str, 0,
- (READ_KEYINFO | COMPUTE_TYPES |
- EXTRA_RECORD | OPEN_FRM_FILE_ONLY),
+ if (share->is_view)
+ {
+ if (open_new_frm(thd, share, table_name->str,
+ (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
+ HA_GET_INDEX | HA_TRY_READ_ONLY),
+ READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD |
+ OPEN_VIEW_NO_PARSE,
+ thd->open_options, &tbl, &table_list, thd->mem_root))
+ goto end_share;
+ table_list.view= (LEX*) share->is_view;
+ res= schema_table->process_table(thd, &table_list, table,
+ res, db_name, table_name);
+ goto end_share;
+ }
+
+ if (!open_table_from_share(thd, share, table_name->str, 0,
+ (EXTRA_RECORD | OPEN_FRM_FILE_ONLY),
thd->open_options, &tbl, FALSE))
{
tbl.s= share;
table_list.table= &tbl;
- table_list.view= (st_lex*) share->is_view;
+ table_list.view= (LEX*) share->is_view;
res= schema_table->process_table(thd, &table_list, table,
res, db_name, table_name);
- closefrm(&tbl, true);
- goto err;
+ free_root(&tbl.mem_root, MYF(0));
}
-err1:
- release_table_share(share, RELEASE_NORMAL);
+end_share:
+ release_table_share(share);
-err:
- pthread_mutex_unlock(&LOCK_open);
+end_unlock:
+ mysql_mutex_unlock(&LOCK_open);
+
+end:
+ /*
+ Release metadata lock we might have acquired.
+
+ Without this step metadata locks acquired for each table processed
+ will be accumulated. In situation when a lot of tables are processed
+ by I_S query this will result in transaction with too many metadata
+ locks. As result performance of acquisition of new lock will suffer.
+
+ Of course, the fact that we don't hold metadata lock on tables which
+ were processed till the end of I_S query makes execution less isolated
+ from concurrent DDL. Consequently one might get 'dirty' results from
+ such a query. But we have never promised serializability of I_S queries
+ anyway.
+
+ We don't have any tables open since we took backup, so rolling back to
+ savepoint is safe.
+ */
+ DBUG_ASSERT(thd->open_tables == NULL);
+ thd->mdl_context.rollback_to_savepoint(open_tables_state_backup->mdl_system_tables_svp);
thd->clear_error();
return res;
}
+class Warnings_only_error_handler : public Internal_error_handler
+{
+public:
+ bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl)
+ {
+ if (sql_errno == ER_PARSE_ERROR ||
+ sql_errno == ER_TRG_NO_DEFINER ||
+ sql_errno == ER_TRG_NO_CREATION_CTX)
+ return true;
+
+ if (level != MYSQL_ERROR::WARN_LEVEL_ERROR)
+ return false;
+
+ if (!thd->stmt_da->is_error())
+ thd->stmt_da->set_error_status(thd, sql_errno, msg, sqlstate);
+ return true; // handled!
+ }
+};
+
/**
@brief Fill I_S tables whose data are retrieved
@@ -4159,21 +4387,38 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
List_iterator_fast<LEX_STRING> it(db_names);
COND *partial_cond= 0;
int error= 1;
- Open_tables_state open_tables_state_backup;
+ Open_tables_backup open_tables_state_backup;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *sctx= thd->security_ctx;
#endif
uint table_open_method;
+ bool can_deadlock;
DBUG_ENTER("get_all_tables");
/*
+ 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
+ under LOCK TABLES or is part of transaction which also uses other
+ tables waiting for metadata locks which happens below might result
+ in deadlocks.
+ To avoid them we don't wait if conflicting metadata lock is
+ encountered and skip table with emitting an appropriate warning.
+ */
+ can_deadlock= thd->mdl_context.has_locks();
+
+ /*
We should not introduce deadlocks even if we already have some
tables open and locked, since we won't lock tables which we will
- open and will ignore possible name-locks for these tables.
+ open and will ignore pending exclusive metadata locks for these
+ tables by using high-priority requests for shared metadata locks.
*/
thd->reset_n_backup_open_tables_state(&open_tables_state_backup);
- /*
+ schema_table_idx= get_schema_table_idx(schema_table);
+ tables->table_open_method= table_open_method=
+ get_table_open_method(tables, schema_table, schema_table_idx);
+ DBUG_PRINT("open_method", ("%d", tables->table_open_method));
+ /*
this branch processes SHOW FIELDS, SHOW INDEXES commands.
see sql_parse.cc, prepare_schema_table() function where
this values are initialized
@@ -4191,11 +4436,11 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
error= fill_schema_table_by_open(thd, TRUE,
table, schema_table,
&db_name, &table_name,
- &open_tables_state_backup);
+ &open_tables_state_backup,
+ can_deadlock);
goto err;
}
- schema_table_idx= get_schema_table_idx(schema_table);
if (get_lookup_field_values(thd, cond, tables, &lookup_field_vals))
{
error= 0;
@@ -4234,9 +4479,6 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
else
partial_cond= make_cond_for_info_schema(cond, tables);
- tables->table_open_method= table_open_method=
- get_table_open_method(tables, schema_table, schema_table_idx);
-
if (lex->describe)
{
/* EXPLAIN SELECT */
@@ -4249,15 +4491,15 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
it.rewind(); /* To get access to new elements in basis list */
while ((db_name= it++))
{
+ DBUG_ASSERT(db_name->length <= NAME_LEN);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (!(check_access(thd,SELECT_ACL, db_name->str,
- &thd->col_access, 0, 1, with_i_schema) ||
+ if (!(check_access(thd, SELECT_ACL, db_name->str,
+ &thd->col_access, NULL, 0, 1) ||
(!thd->col_access && check_grant_db(thd, db_name->str))) ||
sctx->master_access & (DB_ACLS | SHOW_DB_ACL) ||
acl_get(sctx->host, sctx->ip, sctx->priv_user, db_name->str, 0))
#endif
{
- thd->no_warnings_for_error= 1;
List<LEX_STRING> table_names;
int res= make_table_name_list(thd, &table_names, lex,
&lookup_field_vals,
@@ -4270,6 +4512,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
List_iterator_fast<LEX_STRING> it_files(table_names);
while ((table_name= it_files++))
{
+ DBUG_ASSERT(table_name->length <= NAME_LEN);
restore_record(table, s->default_values);
table->field[schema_table->idx_field1]->
store(db_name->str, db_name->length, system_charset_info);
@@ -4288,6 +4531,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
(!lookup_field_vals.table_value.length ||
lookup_field_vals.wild_table_value))
{
+ table->field[0]->store(STRING_WITH_LEN("def"), system_charset_info);
if (schema_table_store_record(thd, table))
goto err; /* Out of space in temporary table */
continue;
@@ -4300,29 +4544,29 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
table_name, with_i_schema))
continue;
}
+ else if (schema_table_idx == SCH_TRIGGERS && with_i_schema)
+ {
+ continue;
+ }
else
{
if (!(table_open_method & ~OPEN_FRM_ONLY) &&
!with_i_schema)
{
- my_bool res;
- uint8 save_context_analysis_only= lex->context_analysis_only;
- lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW;
- res= fill_schema_table_from_frm(thd, table, schema_table,
- db_name,
- table_name, schema_table_idx);
- lex->context_analysis_only= save_context_analysis_only;
- if (!res)
+ if (!fill_schema_table_from_frm(thd, tables, schema_table,
+ db_name, table_name,
+ schema_table_idx,
+ &open_tables_state_backup,
+ can_deadlock))
continue;
}
- thd->no_warnings_for_error= 1;
-
DEBUG_SYNC(thd, "before_open_in_get_all_tables");
if (fill_schema_table_by_open(thd, FALSE,
table, schema_table,
db_name, table_name,
- &open_tables_state_backup))
+ &open_tables_state_backup,
+ can_deadlock))
goto err;
}
}
@@ -4347,6 +4591,7 @@ bool store_schema_shemata(THD* thd, TABLE *table, LEX_STRING *db_name,
CHARSET_INFO *cs)
{
restore_record(table, s->default_values);
+ table->field[0]->store(STRING_WITH_LEN("def"), system_charset_info);
table->field[1]->store(db_name->str, db_name->length, system_charset_info);
table->field[2]->store(cs->csname, strlen(cs->csname), system_charset_info);
table->field[3]->store(cs->name, strlen(cs->name), system_charset_info);
@@ -4375,8 +4620,8 @@ int fill_schema_schemata(THD *thd, TABLE_LIST *tables, COND *cond)
if (get_lookup_field_values(thd, cond, tables, &lookup_field_vals))
DBUG_RETURN(0);
DBUG_PRINT("INDEX VALUES",("db_name: %s table_name: %s",
- val_or_null(lookup_field_vals.db_value.str),
- val_or_null(lookup_field_vals.table_value.str)));
+ lookup_field_vals.db_value.str,
+ lookup_field_vals.table_value.str));
if (make_db_list(thd, &db_names, &lookup_field_vals,
&with_i_schema))
DBUG_RETURN(1);
@@ -4395,13 +4640,14 @@ int fill_schema_schemata(THD *thd, TABLE_LIST *tables, COND *cond)
path_len= build_table_filename(path, sizeof(path) - 1,
lookup_field_vals.db_value.str, "", "", 0);
path[path_len-1]= 0;
- if (!my_stat(path,&stat_info,MYF(0)))
+ if (!mysql_file_stat(key_file_misc, path, &stat_info, MYF(0)))
DBUG_RETURN(0);
}
List_iterator_fast<LEX_STRING> it(db_names);
while ((db_name=it++))
{
+ DBUG_ASSERT(db_name->length <= NAME_LEN);
if (with_i_schema) // information schema name is always first in list
{
if (store_schema_shemata(thd, table, db_name,
@@ -4438,6 +4684,7 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
DBUG_ENTER("get_schema_tables_record");
restore_record(table, s->default_values);
+ 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);
@@ -4470,6 +4717,7 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
#ifdef WITH_PARTITION_STORAGE_ENGINE
bool is_partitioned= FALSE;
#endif
+
if (share->tmp_table == SYSTEM_TMP_TABLE)
table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"), cs);
else if (share->tmp_table)
@@ -4483,86 +4731,107 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
continue;
table->field[i]->set_notnull();
}
+
+ /* Collect table info from the table share */
+
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (share->db_type() == partition_hton &&
- share->partition_info_len)
+ share->partition_info_str_len)
{
tmp_db_type= share->default_part_db_type;
is_partitioned= TRUE;
}
#endif
+
tmp_buff= (char *) ha_resolve_storage_engine_name(tmp_db_type);
table->field[4]->store(tmp_buff, strlen(tmp_buff), cs);
table->field[5]->store((longlong) share->frm_version, TRUE);
str.length(0);
+
if (share->min_rows)
{
str.qs_append(STRING_WITH_LEN(" min_rows="));
str.qs_append(share->min_rows);
}
+
if (share->max_rows)
{
str.qs_append(STRING_WITH_LEN(" max_rows="));
str.qs_append(share->max_rows);
}
+
if (share->avg_row_length)
{
str.qs_append(STRING_WITH_LEN(" avg_row_length="));
str.qs_append(share->avg_row_length);
}
+
if (share->db_create_options & HA_OPTION_PACK_KEYS)
str.qs_append(STRING_WITH_LEN(" pack_keys=1"));
+
if (share->db_create_options & HA_OPTION_NO_PACK_KEYS)
str.qs_append(STRING_WITH_LEN(" pack_keys=0"));
+
/* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */
if (share->db_create_options & HA_OPTION_CHECKSUM)
str.qs_append(STRING_WITH_LEN(" checksum=1"));
+
if (share->page_checksum != HA_CHOICE_UNDEF)
{
str.qs_append(STRING_WITH_LEN(" page_checksum="));
str.qs_append(ha_choice_values[(uint) share->page_checksum]);
}
+
if (share->db_create_options & HA_OPTION_DELAY_KEY_WRITE)
str.qs_append(STRING_WITH_LEN(" delay_key_write=1"));
+
if (share->row_type != ROW_TYPE_DEFAULT)
{
str.qs_append(STRING_WITH_LEN(" row_format="));
str.qs_append(ha_row_type[(uint) share->row_type]);
}
+
if (share->key_block_size)
{
str.qs_append(STRING_WITH_LEN(" key_block_size="));
str.qs_append(share->key_block_size);
}
+
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (is_partitioned)
str.qs_append(STRING_WITH_LEN(" partitioned"));
#endif
+
if (share->transactional != HA_CHOICE_UNDEF)
{
str.qs_append(STRING_WITH_LEN(" transactional="));
str.qs_append(ha_choice_values[(uint) share->transactional]);
}
append_create_options(thd, &str, share->option_list);
+
if (str.length())
table->field[19]->store(str.ptr()+1, str.length()-1, cs);
tmp_buff= (share->table_charset ?
share->table_charset->name : "default");
+
table->field[17]->store(tmp_buff, strlen(tmp_buff), cs);
if (share->comment.str)
table->field[20]->store(share->comment.str, share->comment.length, cs);
- if (file)
+ /* Collect table info from the storage engine */
+
+ 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)
goto err;
-
+
enum row_type row_type = file->get_row_type();
switch (row_type) {
case ROW_TYPE_NOT_USED:
@@ -4591,7 +4860,9 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
tmp_buff= "Page";
break;
}
+
table->field[6]->store(tmp_buff, strlen(tmp_buff), cs);
+
if (!tables->schema_table)
{
table->field[7]->store((longlong) file->stats.records, TRUE);
@@ -4649,21 +4920,154 @@ err:
column with the error text, and clear the error so that the operation
can continue.
*/
- const char *error= thd->is_error() ? thd->main_da.message() : "";
+ const char *error= thd->is_error() ? thd->stmt_da->message() : "";
table->field[20]->store(error, strlen(error), cs);
if (thd->is_error())
{
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->main_da.sql_errno(), thd->main_da.message());
+ thd->stmt_da->sql_errno(), thd->stmt_da->message());
thd->clear_error();
}
}
-
+
DBUG_RETURN(schema_table_store_record(thd, table));
}
+/**
+ @brief Store field characteristics into appropriate I_S table columns
+
+ @param[in] table I_S table
+ @param[in] field processed field
+ @param[in] cs I_S table charset
+ @param[in] offset offset from beginning of table
+ to DATE_TYPE column in I_S table
+
+ @return void
+*/
+
+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);
+
+ field->sql_type(column_type);
+ /* DTD_IDENTIFIER column */
+ table->field[offset + 8]->store(column_type.ptr(), column_type.length(), cs);
+ table->field[offset + 8]->set_notnull();
+ /*
+ DATA_TYPE column:
+ MySQL column type has the following format:
+ base_type [(dimension)] [unsigned] [zerofill].
+ For DATA_TYPE column we extract only base type.
+ */
+ tmp_buff= strchr(column_type.c_ptr_safe(), '(');
+ if (!tmp_buff)
+ /*
+ if there is no dimention part then check the presence of
+ [unsigned] [zerofill] attributes and cut them of if exist.
+ */
+ tmp_buff= strchr(column_type.c_ptr_safe(), ' ');
+ table->field[offset]->store(column_type.ptr(),
+ (tmp_buff ? 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;
+ /* CHARACTER_MAXIMUM_LENGTH column*/
+ table->field[offset + 1]->store(char_max_len, TRUE);
+ table->field[offset + 1]->set_notnull();
+ /* CHARACTER_OCTET_LENGTH column */
+ table->field[offset + 2]->store((longlong) octet_max_length, TRUE);
+ table->field[offset + 2]->set_notnull();
+ }
+
+ /*
+ Calculate field_length and decimals.
+ They are set to -1 if they should not be set (we should return NULL)
+ */
+
+ field_length= -1;
+ decimals= field->decimals();
+ 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:
+ /* DATETIME_PRECISION column */
+ table->field[offset + 5]->store((longlong) field->decimals(), TRUE);
+ table->field[offset + 5]->set_notnull();
+ break;
+ default:
+ break;
+ }
+
+ /* NUMERIC_PRECISION column */
+ if (field_length >= 0)
+ {
+ table->field[offset + 3]->store((longlong) field_length, TRUE);
+ table->field[offset + 3]->set_notnull();
+
+ /* NUMERIC_SCALE column */
+ if (decimals >= 0)
+ {
+ table->field[offset + 4]->store((longlong) decimals, TRUE);
+ table->field[offset + 4]->set_notnull();
+ }
+ }
+ if (field->has_charset())
+ {
+ /* CHARACTER_SET_NAME column*/
+ tmp_buff= field->charset()->csname;
+ table->field[offset + 6]->store(tmp_buff, strlen(tmp_buff), cs);
+ table->field[offset + 6]->set_notnull();
+ /* COLLATION_NAME column */
+ tmp_buff= field->charset()->name;
+ table->field[offset + 7]->store(tmp_buff, strlen(tmp_buff), cs);
+ table->field[offset + 7]->set_notnull();
+ }
+}
+
+
static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
LEX_STRING *db_name,
@@ -4673,7 +5077,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
const char *wild= lex->wild ? lex->wild->ptr() : NullS;
CHARSET_INFO *cs= system_charset_info;
TABLE *show_table;
- Field **ptr,*field;
+ Field **ptr, *field, *timestamp_field;
int count;
DBUG_ENTER("get_schema_column_record");
@@ -4687,7 +5091,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
*/
if (thd->is_error())
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->main_da.sql_errno(), thd->main_da.message());
+ thd->stmt_da->sql_errno(), thd->stmt_da->message());
thd->clear_error();
res= 0;
}
@@ -4696,31 +5100,30 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
show_table= tables->table;
count= 0;
- restore_record(show_table, s->default_values);
+ ptr= show_table->field;
+ timestamp_field= show_table->timestamp_field;
show_table->use_all_columns(); // Required for default
+ restore_record(show_table, s->default_values);
- for (ptr= show_table->field; (field= *ptr) ; ptr++)
+ for (; (field= *ptr) ; ptr++)
{
- const char *tmp_buff;
uchar *pos;
- bool is_blob;
- uint flags=field->flags;
char tmp[MAX_FIELD_WIDTH];
String type(tmp,sizeof(tmp), system_charset_info);
- int decimals, field_length;
+
+ DEBUG_SYNC(thd, "get_schema_column");
if (wild && wild[0] &&
wild_case_compare(system_charset_info, field->field_name,wild))
continue;
- flags= field->flags;
count++;
/* Get default row, with all NULL fields set to NULL */
restore_record(table, s->default_values);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
uint col_access;
- check_access(thd,SELECT_ACL | EXTRA_ACL, db_name->str,
+ check_access(thd,SELECT_ACL, db_name->str,
&tables->grant.privilege, 0, 0, test(tables->schema_table));
col_access= get_column_grant(thd, &tables->grant,
db_name->str, table_name->str,
@@ -4739,120 +5142,22 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
table->field[18]->store(tmp+1,end == tmp ? 0 : (uint) (end-tmp-1), cs);
#endif
+ 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),
cs);
table->field[4]->store((longlong) count, TRUE);
- field->sql_type(type);
- table->field[15]->store(type.ptr(), type.length(), cs);
- /*
- MySQL column type has the following format:
- base_type [(dimension)] [unsigned] [zerofill].
- For DATA_TYPE column we extract only base type.
- */
- tmp_buff= strchr(type.c_ptr_safe(), '(');
- if (!tmp_buff)
- /*
- if there is no dimention part then check the presence of
- [unsigned] [zerofill] attributes and cut them of if exist.
- */
- tmp_buff= strchr(type.ptr(), ' ');
- table->field[7]->store(type.ptr(),
- (tmp_buff ? tmp_buff - type.ptr() :
- type.length()), cs);
- if (get_field_default_value(thd, show_table, field, &type, 0))
+ if (get_field_default_value(thd, timestamp_field, field, &type, 0))
{
table->field[5]->store(type.ptr(), type.length(), cs);
table->field[5]->set_notnull();
}
- pos=(uchar*) ((flags & NOT_NULL_FLAG) ? "NO" : "YES");
+ pos=(uchar*) ((field->flags & NOT_NULL_FLAG) ? "NO" : "YES");
table->field[6]->store((const char*) pos,
strlen((const char*) pos), 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;
- table->field[8]->store(char_max_len, TRUE);
- table->field[8]->set_notnull();
- table->field[9]->store((longlong) octet_max_length, TRUE);
- table->field[9]->set_notnull();
- }
-
- /*
- Calculate field_length and decimals.
- They are set to -1 if they should not be set (we should return NULL)
- */
-
- field_length= -1;
- decimals= field->decimals();
- 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:
- table->field[12]->store((longlong) field->decimals(), TRUE);
- table->field[12]->set_notnull();
- break;
- default:
- break;
- }
-
- if (field_length >= 0)
- {
- table->field[10]->store((longlong) field_length, TRUE);
- table->field[10]->set_notnull();
- if (decimals >= 0)
- {
- table->field[11]->store((longlong) decimals, TRUE);
- table->field[11]->set_notnull();
- }
- }
-
- if (field->has_charset())
- {
- pos=(uchar*) field->charset()->csname;
- table->field[13]->store((const char*) pos,
- strlen((const char*) pos), cs);
- table->field[13]->set_notnull();
- pos=(uchar*) field->charset()->name;
- table->field[14]->store((const char*) pos,
- strlen((const char*) pos), cs);
- table->field[14]->set_notnull();
- }
+ store_column_type(table, field, cs, 7);
pos=(uchar*) ((field->flags & PRI_KEY_FLAG) ? "PRI" :
(field->flags & UNIQUE_KEY_FLAG) ? "UNI" :
(field->flags & MULTIPLE_KEY_FLAG) ? "MUL":"");
@@ -4861,7 +5166,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
if (field->unireg_check == Field::NEXT_NUMBER)
table->field[17]->store(STRING_WITH_LEN("auto_increment"), cs);
- if (show_table->timestamp_field == field &&
+ if (timestamp_field == field &&
field->unireg_check != Field::TIMESTAMP_DN_FIELD)
table->field[17]->store(STRING_WITH_LEN("on update CURRENT_TIMESTAMP"),
cs);
@@ -4880,7 +5185,6 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
}
-
int fill_schema_charsets(THD *thd, TABLE_LIST *tables, COND *cond)
{
CHARSET_INFO **cs;
@@ -4888,7 +5192,9 @@ int fill_schema_charsets(THD *thd, TABLE_LIST *tables, COND *cond)
TABLE *table= tables->table;
CHARSET_INFO *scs= system_charset_info;
- for (cs= all_charsets ; cs < all_charsets+255 ; cs++)
+ for (cs= all_charsets ;
+ cs < all_charsets + array_elements(all_charsets) ;
+ cs++)
{
CHARSET_INFO *tmp_cs= cs[0];
if (tmp_cs && (tmp_cs->state & MY_CS_PRIMARY) &&
@@ -4993,7 +5299,9 @@ int fill_schema_collation(THD *thd, TABLE_LIST *tables, COND *cond)
const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS;
TABLE *table= tables->table;
CHARSET_INFO *scs= system_charset_info;
- for (cs= all_charsets ; cs < all_charsets+255 ; cs++ )
+ for (cs= all_charsets ;
+ cs < all_charsets + array_elements(all_charsets) ;
+ cs++ )
{
CHARSET_INFO **cl;
CHARSET_INFO *tmp_cs= cs[0];
@@ -5001,7 +5309,9 @@ int fill_schema_collation(THD *thd, TABLE_LIST *tables, COND *cond)
(tmp_cs->state & MY_CS_HIDDEN) ||
!(tmp_cs->state & MY_CS_PRIMARY))
continue;
- for (cl= all_charsets; cl < all_charsets+255 ;cl ++)
+ for (cl= all_charsets;
+ cl < all_charsets + array_elements(all_charsets) ;
+ cl ++)
{
CHARSET_INFO *tmp_cl= cl[0];
if (!tmp_cl || !(tmp_cl->state & MY_CS_AVAILABLE) ||
@@ -5034,17 +5344,22 @@ int fill_schema_coll_charset_app(THD *thd, TABLE_LIST *tables, COND *cond)
CHARSET_INFO **cs;
TABLE *table= tables->table;
CHARSET_INFO *scs= system_charset_info;
- for (cs= all_charsets ; cs < all_charsets+255 ; cs++ )
+ for (cs= all_charsets ;
+ cs < all_charsets + array_elements(all_charsets) ;
+ cs++ )
{
CHARSET_INFO **cl;
CHARSET_INFO *tmp_cs= cs[0];
if (!tmp_cs || !(tmp_cs->state & MY_CS_AVAILABLE) ||
!(tmp_cs->state & MY_CS_PRIMARY))
continue;
- for (cl= all_charsets; cl < all_charsets+255 ;cl ++)
+ for (cl= all_charsets;
+ cl < all_charsets + array_elements(all_charsets) ;
+ cl ++)
{
CHARSET_INFO *tmp_cl= cl[0];
if (!tmp_cl || !(tmp_cl->state & MY_CS_AVAILABLE) ||
+ (tmp_cl->state & MY_CS_HIDDEN) ||
!my_charset_same(tmp_cs,tmp_cl))
continue;
restore_record(table, s->default_values);
@@ -5067,74 +5382,295 @@ static inline void copy_field_as_string(Field *to_field, Field *from_field)
}
+/**
+ @brief Store record into I_S.PARAMETERS table
+
+ @param[in] thd thread handler
+ @param[in] table I_S table
+ @param[in] proc_table 'mysql.proc' table
+ @param[in] wild wild string, not used for now,
+ will be useful
+ if we add 'SHOW PARAMETERs'
+ @param[in] full_access if 1 user has privileges on the routine
+ @param[in] sp_user user in 'user@host' format
+
+ @return Operation status
+ @retval 0 ok
+ @retval 1 error
+*/
+
+bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table,
+ const char *wild, bool full_access,
+ const char *sp_user)
+{
+ 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);
+ sp_head *sp;
+ stored_procedure_type routine_type;
+ bool free_sp_head;
+ 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();
+
+ if (!full_access)
+ full_access= !strcmp(sp_user, definer.ptr());
+ if (!full_access &&
+ check_some_routine_access(thd, sp_db.ptr(),sp_name.ptr(),
+ routine_type == TYPE_ENUM_PROCEDURE))
+ 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);
+
+ if (sp)
+ {
+ Field *field;
+ Create_field *field_def;
+ String tmp_string;
+ if (routine_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[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_def= &sp->m_return_field_def;
+ field= make_field(&share, (uchar*) 0, field_def->length,
+ (uchar*) "", 0, field_def->pack_flag,
+ field_def->sql_type, field_def->charset,
+ field_def->geom_type, Field::NONE,
+ field_def->interval, "");
+
+ field->table= &tbl;
+ tbl.in_use= thd;
+ store_column_type(table, field, cs, 6);
+ if (schema_table_store_record(thd, table))
+ {
+ free_table_share(&share);
+ if (free_sp_head)
+ delete sp;
+ DBUG_RETURN(1);
+ }
+ }
+
+ sp_pcontext *spcont= sp->get_parse_context();
+ uint params= spcont->context_var_count();
+ for (uint i= 0 ; i < params ; i++)
+ {
+ const char *tmp_buff;
+ sp_variable_t *spvar= spcont->find_variable(i);
+ field_def= &spvar->field_def;
+ switch (spvar->mode) {
+ case sp_param_in:
+ tmp_buff= "IN";
+ break;
+ case sp_param_out:
+ tmp_buff= "OUT";
+ break;
+ case sp_param_inout:
+ tmp_buff= "INOUT";
+ break;
+ default:
+ tmp_buff= "";
+ break;
+ }
+
+ 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[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);
+
+ field= make_field(&share, (uchar*) 0, field_def->length,
+ (uchar*) "", 0, field_def->pack_flag,
+ field_def->sql_type, field_def->charset,
+ field_def->geom_type, Field::NONE,
+ field_def->interval, spvar->name.str);
+
+ field->table= &tbl;
+ tbl.in_use= thd;
+ store_column_type(table, field, cs, 6);
+ if (schema_table_store_record(thd, table))
+ {
+ free_table_share(&share);
+ if (free_sp_head)
+ delete sp;
+ DBUG_RETURN(1);
+ }
+ }
+ if (free_sp_head)
+ delete sp;
+ }
+ free_table_share(&share);
+ DBUG_RETURN(0);
+}
+
+
bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table,
const char *wild, bool full_access, const char *sp_user)
{
MYSQL_TIME time;
LEX *lex= thd->lex;
CHARSET_INFO *cs= system_charset_info;
- char sp_db_buff[SAFE_NAME_LEN + 1], sp_name_buff[SAFE_NAME_LEN + 1],
- definer_buff[USERNAME_LENGTH + HOSTNAME_LENGTH + 2];
+ 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[0]->val_str(&sp_db);
- proc_table->field[1]->val_str(&sp_name);
- proc_table->field[11]->val_str(&definer);
+ 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);
if (!full_access)
full_access= !strcmp(sp_user, definer.c_ptr_safe());
if (!full_access &&
check_some_routine_access(thd, sp_db.c_ptr_safe(), sp_name.c_ptr_safe(),
- proc_table->field[2]->val_int() ==
- TYPE_ENUM_PROCEDURE))
+ proc_table->field[MYSQL_PROC_MYSQL_TYPE]->
+ val_int() == TYPE_ENUM_PROCEDURE))
return 0;
if ((lex->sql_command == SQLCOM_SHOW_STATUS_PROC &&
- proc_table->field[2]->val_int() == TYPE_ENUM_PROCEDURE) ||
+ proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int() ==
+ TYPE_ENUM_PROCEDURE) ||
(lex->sql_command == SQLCOM_SHOW_STATUS_FUNC &&
- proc_table->field[2]->val_int() == TYPE_ENUM_FUNCTION) ||
+ proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int() ==
+ TYPE_ENUM_FUNCTION) ||
(sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) == 0)
{
restore_record(table, s->default_values);
if (!wild || !wild[0] || !wild_case_compare(system_charset_info,
sp_name.c_ptr_safe(), wild))
{
- int enum_idx= (int) proc_table->field[5]->val_int();
+ int enum_idx= (int) proc_table->field[MYSQL_PROC_FIELD_ACCESS]->val_int();
table->field[3]->store(sp_name.ptr(), sp_name.length(), cs);
- copy_field_as_string(table->field[0], proc_table->field[3]);
+
+ 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);
- copy_field_as_string(table->field[4], proc_table->field[2]);
- if (proc_table->field[2]->val_int() == TYPE_ENUM_FUNCTION)
+ 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)
{
- copy_field_as_string(table->field[5], proc_table->field[9]);
- table->field[5]->set_notnull();
+ 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);
+
+ if (sp)
+ {
+ char path[FN_REFLEN];
+ TABLE_SHARE share;
+ TABLE tbl;
+ Field *field;
+ Create_field *field_def= &sp->m_return_field_def;
+
+ bzero((char*) &tbl, sizeof(TABLE));
+ (void) build_table_filename(path, sizeof(path), "", "", "", 0);
+ init_tmp_table_share(thd, &share, "", 0, "", path);
+ field= make_field(&share, (uchar*) 0, field_def->length,
+ (uchar*) "", 0, field_def->pack_flag,
+ field_def->sql_type, field_def->charset,
+ field_def->geom_type, Field::NONE,
+ field_def->interval, "");
+
+ field->table= &tbl;
+ tbl.in_use= thd;
+ store_column_type(table, field, cs, 5);
+ free_table_share(&share);
+ if (free_sp_head)
+ delete sp;
+ }
}
+
if (full_access)
{
- copy_field_as_string(table->field[7], proc_table->field[19]);
- table->field[7]->set_notnull();
+ copy_field_as_string(table->field[15],
+ proc_table->field[MYSQL_PROC_FIELD_BODY_UTF8]);
+ table->field[15]->set_notnull();
}
- table->field[6]->store(STRING_WITH_LEN("SQL"), cs);
- table->field[10]->store(STRING_WITH_LEN("SQL"), cs);
- copy_field_as_string(table->field[11], proc_table->field[6]);
- table->field[12]->store(sp_data_access_name[enum_idx].str,
+ table->field[14]->store(STRING_WITH_LEN("SQL"), cs);
+ table->field[18]->store(STRING_WITH_LEN("SQL"), cs);
+ copy_field_as_string(table->field[19],
+ proc_table->field[MYSQL_PROC_FIELD_DETERMINISTIC]);
+ table->field[20]->store(sp_data_access_name[enum_idx].str,
sp_data_access_name[enum_idx].length , cs);
- copy_field_as_string(table->field[14], proc_table->field[7]);
+ copy_field_as_string(table->field[22],
+ proc_table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]);
bzero((char *)&time, sizeof(time));
- ((Field_timestamp *) proc_table->field[12])->get_time(&time);
- table->field[15]->store_time(&time);
+ ((Field_timestamp *) proc_table->field[MYSQL_PROC_FIELD_CREATED])->
+ get_time(&time);
+ table->field[23]->store_time(&time);
bzero((char *)&time, sizeof(time));
- ((Field_timestamp *) proc_table->field[13])->get_time(&time);
- table->field[16]->store_time(&time);
- copy_field_as_string(table->field[17], proc_table->field[14]);
- copy_field_as_string(table->field[18], proc_table->field[15]);
- table->field[19]->store(definer.ptr(), definer.length(), cs);
- copy_field_as_string(table->field[20], proc_table->field[16]);
- copy_field_as_string(table->field[21], proc_table->field[17]);
- copy_field_as_string(table->field[22], proc_table->field[18]);
+ ((Field_timestamp *) proc_table->field[MYSQL_PROC_FIELD_MODIFIED])->
+ get_time(&time);
+ table->field[24]->store_time(&time);
+ copy_field_as_string(table->field[25],
+ proc_table->field[MYSQL_PROC_FIELD_SQL_MODE]);
+ copy_field_as_string(table->field[26],
+ proc_table->field[MYSQL_PROC_FIELD_COMMENT]);
+
+ table->field[27]->store(definer.ptr(), definer.length(), cs);
+ copy_field_as_string(table->field[28],
+ proc_table->
+ field[MYSQL_PROC_FIELD_CHARACTER_SET_CLIENT]);
+ copy_field_as_string(table->field[29],
+ proc_table->
+ field[MYSQL_PROC_FIELD_COLLATION_CONNECTION]);
+ copy_field_as_string(table->field[30],
+ proc_table->field[MYSQL_PROC_FIELD_DB_COLLATION]);
return schema_table_store_record(thd, table);
}
@@ -5152,7 +5688,9 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
TABLE *table= tables->table;
bool full_access;
char definer[USER_HOST_BUFF_SIZE];
- Open_tables_state open_tables_state_backup;
+ Open_tables_backup open_tables_state_backup;
+ enum enum_schema_tables schema_table_idx=
+ get_schema_table_idx(tables->schema_table);
DBUG_ENTER("fill_schema_proc");
strxmov(definer, thd->security_ctx->priv_user, "@",
@@ -5164,25 +5702,37 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
proc_tables.table_name= proc_tables.alias= (char*) "proc";
proc_tables.table_name_length= 4;
proc_tables.lock_type= TL_READ;
- full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, 1, TRUE);
+ full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, FALSE,
+ 1, TRUE);
if (!(proc_table= open_proc_table_for_read(thd, &open_tables_state_backup)))
{
DBUG_RETURN(1);
}
- proc_table->file->ha_index_init(0, 1);
+
+ if (proc_table->file->ha_index_init(0, 1))
+ {
+ res= 1;
+ goto err;
+ }
+
if ((res= proc_table->file->ha_index_first(proc_table->record[0])))
{
res= (res == HA_ERR_END_OF_FILE) ? 0 : 1;
goto err;
}
- if (store_schema_proc(thd, table, proc_table, wild, full_access, definer))
+
+ if (schema_table_idx == SCH_PROCEDURES ?
+ store_schema_proc(thd, table, proc_table, wild, full_access, definer) :
+ store_schema_params(thd, table, proc_table, wild, full_access, definer))
{
res= 1;
goto err;
}
while (!proc_table->file->ha_index_next(proc_table->record[0]))
{
- if (store_schema_proc(thd, table, proc_table, wild, full_access, definer))
+ if (schema_table_idx == SCH_PROCEDURES ?
+ store_schema_proc(thd, table, proc_table, wild, full_access, definer):
+ store_schema_params(thd, table, proc_table, wild, full_access, definer))
{
res= 1;
goto err;
@@ -5190,7 +5740,9 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
}
err:
- proc_table->file->ha_index_end();
+ if (proc_table->file->inited)
+ (void) proc_table->file->ha_index_end();
+
close_system_tables(thd, &open_tables_state_backup);
DBUG_RETURN(res);
}
@@ -5213,7 +5765,7 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables,
*/
if (thd->is_error())
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->main_da.sql_errno(), thd->main_da.message());
+ thd->stmt_da->sql_errno(), thd->stmt_da->message());
thd->clear_error();
res= 0;
}
@@ -5234,6 +5786,7 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables,
for (uint j=0 ; j < key_info->key_parts ; j++,key_part++)
{
restore_record(table, s->default_values);
+ 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((longlong) ((key_info->flags &
@@ -5281,6 +5834,11 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables,
else
table->field[14]->store("", 0, cs);
table->field[14]->set_notnull();
+ DBUG_ASSERT(test(key_info->flags & HA_USES_COMMENT) ==
+ (key_info->comment.length > 0));
+ if (key_info->flags & HA_USES_COMMENT)
+ table->field[15]->store(key_info->comment.str,
+ key_info->comment.length, cs);
if (schema_table_store_record(thd, table))
DBUG_RETURN(1);
}
@@ -5296,24 +5854,15 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables,
LEX_STRING *table_name)
{
CHARSET_INFO *cs= system_charset_info;
- DBUG_ENTER("get_schema_views_record");
- LEX_STRING *tmp_db_name, *tmp_table_name;
char definer[USER_HOST_BUFF_SIZE];
uint definer_len;
bool updatable_view;
- /*
- if SELECT FROM I_S.VIEWS uses only fields
- which have OPEN_FRM_ONLY flag then 'tables'
- structure is zeroed and only tables->view is set.
- (see fill_schema_table_from_frm() function).
- So we should disable other fields filling.
- */
- bool only_share= !tables->definer.user.str;
+ DBUG_ENTER("get_schema_views_record");
if (tables->view)
{
Security_context *sctx= thd->security_ctx;
- if (!only_share && !tables->allowed_show)
+ if (!tables->allowed_show)
{
if (!my_strcasecmp(system_charset_info, tables->definer.user.str,
sctx->priv_user) &&
@@ -5331,55 +5880,62 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables,
TABLE_LIST table_list;
uint view_access;
memset(&table_list, 0, sizeof(table_list));
- table_list.db= tables->view_db.str;
- table_list.table_name= tables->view_name.str;
+ table_list.db= tables->db;
+ table_list.table_name= tables->table_name;
table_list.grant.privilege= thd->col_access;
view_access= get_table_grant(thd, &table_list);
- if ((view_access & (SHOW_VIEW_ACL|SELECT_ACL)) ==
- (SHOW_VIEW_ACL|SELECT_ACL))
- tables->allowed_show= TRUE;
+ if ((view_access & (SHOW_VIEW_ACL|SELECT_ACL)) ==
+ (SHOW_VIEW_ACL|SELECT_ACL))
+ tables->allowed_show= TRUE;
}
}
#endif
}
restore_record(table, s->default_values);
- tmp_db_name= &tables->view_db;
- tmp_table_name= &tables->view_name;
- if (only_share)
+ 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);
+
+ if (tables->allowed_show)
{
- tmp_db_name= db_name;
- tmp_table_name= table_name;
+ table->field[3]->store(tables->view_body_utf8.str,
+ tables->view_body_utf8.length,
+ cs);
}
- table->field[1]->store(tmp_db_name->str, tmp_db_name->length, cs);
- table->field[2]->store(tmp_table_name->str, tmp_table_name->length, cs);
- if (!only_share)
- {
- if (tables->allowed_show)
- {
- table->field[3]->store(tables->view_body_utf8.str,
- tables->view_body_utf8.length,
- cs);
- }
- if (tables->with_check != VIEW_CHECK_NONE)
- {
- if (tables->with_check == VIEW_CHECK_LOCAL)
- table->field[4]->store(STRING_WITH_LEN("LOCAL"), cs);
- else
- table->field[4]->store(STRING_WITH_LEN("CASCADED"), cs);
- }
+ if (tables->with_check != VIEW_CHECK_NONE)
+ {
+ if (tables->with_check == VIEW_CHECK_LOCAL)
+ table->field[4]->store(STRING_WITH_LEN("LOCAL"), cs);
else
- table->field[4]->store(STRING_WITH_LEN("NONE"), cs);
+ table->field[4]->store(STRING_WITH_LEN("CASCADED"), cs);
+ }
+ else
+ table->field[4]->store(STRING_WITH_LEN("NONE"), cs);
+ /*
+ Only try to fill in the information about view updatability
+ if it is requested as part of the top-level query (i.e.
+ it's select * from i_s.views, as opposed to, say, select
+ security_type from i_s.views). Do not try to access the
+ underlying tables if there was an error when opening the
+ view: all underlying tables are released back to the table
+ definition cache on error inside open_normal_and_derived_tables().
+ If a field is not assigned explicitly, it defaults to NULL.
+ */
+ if (res == FALSE &&
+ table->pos_in_table_list->table_open_method & OPEN_FULL_TABLE)
+ {
updatable_view= 0;
if (tables->algorithm != VIEW_ALGORITHM_TMPTABLE)
{
/*
- We should use tables->view->select_lex.item_list here and
- can not use Field_iterator_view because the view always uses
- temporary algorithm during opening for I_S and
- TABLE_LIST fields 'field_translation' & 'field_translation_end'
- are uninitialized is this case.
+ We should use tables->view->select_lex.item_list here
+ and can not use Field_iterator_view because the view
+ always uses temporary algorithm during opening for I_S
+ and TABLE_LIST fields 'field_translation'
+ & 'field_translation_end' are uninitialized is this
+ case.
*/
List<Item> *fields= &tables->view->select_lex.item_list;
List_iterator<Item> it(*fields);
@@ -5404,29 +5960,31 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables,
table->field[5]->store(STRING_WITH_LEN("YES"), cs);
else
table->field[5]->store(STRING_WITH_LEN("NO"), cs);
- definer_len= (strxmov(definer, tables->definer.user.str, "@",
- tables->definer.host.str, NullS) - definer);
- table->field[6]->store(definer, definer_len, cs);
- if (tables->view_suid)
- table->field[7]->store(STRING_WITH_LEN("DEFINER"), cs);
- else
- table->field[7]->store(STRING_WITH_LEN("INVOKER"), cs);
+ }
- table->field[8]->store(tables->view_creation_ctx->get_client_cs()->csname,
- strlen(tables->view_creation_ctx->
- get_client_cs()->csname), cs);
+ definer_len= (strxmov(definer, tables->definer.user.str, "@",
+ tables->definer.host.str, NullS) - definer);
+ table->field[6]->store(definer, definer_len, cs);
+ if (tables->view_suid)
+ table->field[7]->store(STRING_WITH_LEN("DEFINER"), cs);
+ else
+ table->field[7]->store(STRING_WITH_LEN("INVOKER"), cs);
+
+ table->field[8]->store(tables->view_creation_ctx->get_client_cs()->csname,
+ strlen(tables->view_creation_ctx->
+ get_client_cs()->csname), cs);
+
+ table->field[9]->store(tables->view_creation_ctx->
+ get_connection_cl()->name,
+ strlen(tables->view_creation_ctx->
+ get_connection_cl()->name), cs);
- table->field[9]->store(tables->view_creation_ctx->
- get_connection_cl()->name,
- strlen(tables->view_creation_ctx->
- get_connection_cl()->name), cs);
- }
if (schema_table_store_record(thd, table))
DBUG_RETURN(1);
if (res && thd->is_error())
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->main_da.sql_errno(), thd->main_da.message());
+ thd->stmt_da->sql_errno(), thd->stmt_da->message());
}
if (res)
thd->clear_error();
@@ -5440,6 +5998,7 @@ bool store_constraints(THD *thd, TABLE *table, LEX_STRING *db_name,
{
CHARSET_INFO *cs= system_charset_info;
restore_record(table, s->default_values);
+ table->field[0]->store(STRING_WITH_LEN("def"), cs);
table->field[1]->store(db_name->str, db_name->length, cs);
table->field[2]->store(key_name, key_len, cs);
table->field[3]->store(db_name->str, db_name->length, cs);
@@ -5459,7 +6018,7 @@ static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables,
{
if (thd->is_error())
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->main_da.sql_errno(), thd->main_da.message());
+ thd->stmt_da->sql_errno(), thd->stmt_da->message());
thd->clear_error();
DBUG_RETURN(0);
}
@@ -5469,6 +6028,9 @@ static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables,
TABLE *show_table= tables->table;
KEY *key_info=show_table->key_info;
uint primary_key= show_table->s->primary_key;
+ show_table->file->info(HA_STATUS_VARIABLE |
+ HA_STATUS_NO_LOCK |
+ HA_STATUS_TIME);
for (uint i=0 ; i < show_table->s->keys ; i++, key_info++)
{
if (i != primary_key && !(key_info->flags & HA_NOSAME))
@@ -5496,8 +6058,8 @@ static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables,
while ((f_key_info=it++))
{
if (store_constraints(thd, table, db_name, table_name,
- f_key_info->forein_id->str,
- strlen(f_key_info->forein_id->str),
+ f_key_info->foreign_id->str,
+ strlen(f_key_info->foreign_id->str),
"FOREIGN KEY", 11))
DBUG_RETURN(1);
}
@@ -5521,10 +6083,12 @@ static bool store_trigger(THD *thd, TABLE *table, LEX_STRING *db_name,
LEX_STRING sql_mode_rep;
restore_record(table, s->default_values);
+ table->field[0]->store(STRING_WITH_LEN("def"), cs);
table->field[1]->store(db_name->str, db_name->length, cs);
table->field[2]->store(trigger_name->str, trigger_name->length, cs);
table->field[3]->store(trg_event_type_names[event].str,
trg_event_type_names[event].length, cs);
+ table->field[4]->store(STRING_WITH_LEN("def"), cs);
table->field[5]->store(db_name->str, db_name->length, cs);
table->field[6]->store(table_name->str, table_name->length, cs);
table->field[9]->store(trigger_stmt->str, trigger_stmt->length, cs);
@@ -5534,7 +6098,7 @@ static bool store_trigger(THD *thd, TABLE *table, LEX_STRING *db_name,
table->field[14]->store(STRING_WITH_LEN("OLD"), cs);
table->field[15]->store(STRING_WITH_LEN("NEW"), cs);
- sys_var::make_set(thd, sql_mode, &sql_mode_typelib, &sql_mode_rep);
+ sql_mode_string_representation(thd, sql_mode, &sql_mode_rep);
table->field[17]->store(sql_mode_rep.str, sql_mode_rep.length, cs);
table->field[18]->store(definer_buffer->str, definer_buffer->length, cs);
table->field[19]->store(client_cs_name->str, client_cs_name->length, cs);
@@ -5560,7 +6124,7 @@ static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables,
{
if (thd->is_error())
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->main_da.sql_errno(), thd->main_da.message());
+ thd->stmt_da->sql_errno(), thd->stmt_da->message());
thd->clear_error();
DBUG_RETURN(0);
}
@@ -5569,7 +6133,7 @@ static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables,
Table_triggers_list *triggers= tables->table->triggers;
int event, timing;
- if (check_table_access(thd, TRIGGER_ACL, tables, 1, TRUE))
+ if (check_table_access(thd, TRIGGER_ACL, tables, FALSE, 1, TRUE))
goto ret;
for (event= 0; event < (int)TRG_EVENT_MAX; event++)
@@ -5619,8 +6183,10 @@ void store_key_column_usage(TABLE *table, LEX_STRING *db_name,
longlong idx)
{
CHARSET_INFO *cs= system_charset_info;
+ table->field[0]->store(STRING_WITH_LEN("def"), cs);
table->field[1]->store(db_name->str, db_name->length, cs);
table->field[2]->store(key_name, key_len, cs);
+ table->field[3]->store(STRING_WITH_LEN("def"), cs);
table->field[4]->store(db_name->str, db_name->length, cs);
table->field[5]->store(table_name->str, table_name->length, cs);
table->field[6]->store(con_type, con_len, cs);
@@ -5639,7 +6205,7 @@ static int get_schema_key_column_usage_record(THD *thd,
{
if (thd->is_error())
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->main_da.sql_errno(), thd->main_da.message());
+ thd->stmt_da->sql_errno(), thd->stmt_da->message());
thd->clear_error();
DBUG_RETURN(0);
}
@@ -5649,6 +6215,9 @@ static int get_schema_key_column_usage_record(THD *thd,
TABLE *show_table= tables->table;
KEY *key_info=show_table->key_info;
uint primary_key= show_table->s->primary_key;
+ show_table->file->info(HA_STATUS_VARIABLE |
+ HA_STATUS_NO_LOCK |
+ HA_STATUS_TIME);
for (uint i=0 ; i < show_table->s->keys ; i++, key_info++)
{
if (i != primary_key && !(key_info->flags & HA_NOSAME))
@@ -5689,8 +6258,8 @@ 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,
- f_key_info->forein_id->str,
- f_key_info->forein_id->length,
+ f_key_info->foreign_id->str,
+ f_key_info->foreign_id->length,
f_info->str, f_info->length,
(longlong) f_idx);
table->field[8]->store((longlong) f_idx, TRUE);
@@ -5716,7 +6285,8 @@ static int get_schema_key_column_usage_record(THD *thd,
#ifdef WITH_PARTITION_STORAGE_ENGINE
-static void collect_partition_expr(List<char> &field_list, String *str)
+static void collect_partition_expr(THD *thd, List<char> &field_list,
+ String *str)
{
List_iterator<char> part_it(field_list);
ulong no_fields= field_list.elements;
@@ -5724,12 +6294,63 @@ static void collect_partition_expr(List<char> &field_list, String *str)
str->length(0);
while ((field_str= part_it++))
{
- str->append(field_str);
+ append_identifier(thd, str, field_str, strlen(field_str));
if (--no_fields != 0)
str->append(",");
}
return;
}
+
+
+/*
+ Convert a string in a given character set to a string which can be
+ used for FRM file storage in which case use_hex is TRUE and we store
+ the character constants as hex strings in the character set encoding
+ their field have. In the case of SHOW CREATE TABLE and the
+ PARTITIONS information schema table we instead provide utf8 strings
+ to the user and convert to the utf8 character set.
+
+ SYNOPSIS
+ get_cs_converted_part_value_from_string()
+ item Item from which constant comes
+ input_str String as provided by val_str after
+ conversion to character set
+ output_str Out value: The string created
+ cs Character set string is encoded in
+ NULL for INT_RESULT's here
+ use_hex TRUE => hex string created
+ FALSE => utf8 constant string created
+
+ RETURN VALUES
+ TRUE Error
+ FALSE Ok
+*/
+
+int get_cs_converted_part_value_from_string(THD *thd,
+ Item *item,
+ String *input_str,
+ String *output_str,
+ CHARSET_INFO *cs,
+ bool use_hex)
+{
+ if (item->result_type() == INT_RESULT)
+ {
+ longlong value= item->val_int();
+ output_str->set(value, system_charset_info);
+ return FALSE;
+ }
+ if (!input_str)
+ {
+ my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
+ return TRUE;
+ }
+ get_cs_converted_string_value(thd,
+ input_str,
+ output_str,
+ cs,
+ use_hex);
+ return FALSE;
+}
#endif
@@ -5740,9 +6361,10 @@ static void store_schema_partitions_record(THD *thd, TABLE *schema_table,
{
TABLE* table= schema_table;
CHARSET_INFO *cs= system_charset_info;
- PARTITION_INFO stat_info;
+ PARTITION_STATS stat_info;
MYSQL_TIME time;
file->get_dynamic_partition_info(&stat_info, part_id);
+ table->field[0]->store(STRING_WITH_LEN("def"), cs);
table->field[12]->store((longlong) stat_info.records, TRUE);
table->field[13]->store((longlong) stat_info.mean_rec_length, TRUE);
table->field[14]->store((longlong) stat_info.data_file_length, TRUE);
@@ -5799,10 +6421,7 @@ static void store_schema_partitions_record(THD *thd, TABLE *schema_table,
{
char *ts= showing_table->file->get_tablespace_name(thd,0,0);
if(ts)
- {
table->field[24]->store(ts, strlen(ts), cs);
- my_free(ts, MYF(0));
- }
else
table->field[24]->set_null();
}
@@ -5810,6 +6429,51 @@ static void store_schema_partitions_record(THD *thd, TABLE *schema_table,
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)
+{
+ uint num_elements= part_info->part_field_list.elements;
+ uint i;
+ DBUG_ENTER("get_partition_column_description");
+
+ for (i= 0; i < num_elements; i++)
+ {
+ part_column_list_val *col_val= &list_value->col_val_array[i];
+ if (col_val->max_value)
+ tmp_str.append(partition_keywords[PKW_MAXVALUE].str);
+ else if (col_val->null_value)
+ tmp_str.append("NULL");
+ else
+ {
+ char buffer[MAX_KEY_LENGTH];
+ String str(buffer, sizeof(buffer), &my_charset_bin);
+ String val_conv;
+ Item *item= col_val->item_expression;
+
+ if (!(item= part_info->get_column_item(item,
+ part_info->part_field_array[i])))
+ {
+ DBUG_RETURN(1);
+ }
+ String *res= item->val_str(&str);
+ if (get_cs_converted_part_value_from_string(thd, item, res, &val_conv,
+ part_info->part_field_array[i]->charset(),
+ FALSE))
+ {
+ DBUG_RETURN(1);
+ }
+ tmp_str.append(val_conv);
+ }
+ if (i != num_elements - 1)
+ tmp_str.append(",");
+ }
+ DBUG_RETURN(0);
+}
+#endif /* WITH_PARTITION_STORAGE_ENGINE */
static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
TABLE *table, bool res,
@@ -5831,7 +6495,7 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
{
if (thd->is_error())
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->main_da.sql_errno(), thd->main_da.message());
+ thd->stmt_da->sql_errno(), thd->stmt_da->message());
thd->clear_error();
DBUG_RETURN(0);
}
@@ -5845,6 +6509,7 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
uint part_pos= 0, part_id= 0;
restore_record(table, s->default_values);
+ 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);
@@ -5852,12 +6517,18 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
/* Partition method*/
switch (part_info->part_type) {
case RANGE_PARTITION:
- table->field[7]->store(partition_keywords[PKW_RANGE].str,
- partition_keywords[PKW_RANGE].length, cs);
- break;
case LIST_PARTITION:
- table->field[7]->store(partition_keywords[PKW_LIST].str,
- partition_keywords[PKW_LIST].length, cs);
+ tmp_res.length(0);
+ if (part_info->part_type == RANGE_PARTITION)
+ tmp_res.append(partition_keywords[PKW_RANGE].str,
+ partition_keywords[PKW_RANGE].length);
+ else
+ tmp_res.append(partition_keywords[PKW_LIST].str,
+ partition_keywords[PKW_LIST].length);
+ if (part_info->column_list)
+ tmp_res.append(partition_keywords[PKW_COLUMNS].str,
+ partition_keywords[PKW_COLUMNS].length);
+ table->field[7]->store(tmp_res.ptr(), tmp_res.length(), cs);
break;
case HASH_PARTITION:
tmp_res.length(0);
@@ -5874,8 +6545,7 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
break;
default:
DBUG_ASSERT(0);
- my_error(ER_OUT_OF_RESOURCES, MYF(0));
- current_thd->fatal_error();
+ my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
DBUG_RETURN(1);
}
table->field[7]->set_notnull();
@@ -5888,7 +6558,7 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
}
else if (part_info->list_of_part_fields)
{
- collect_partition_expr(part_info->part_field_list, &tmp_str);
+ collect_partition_expr(thd, part_info->part_field_list, &tmp_str);
table->field[9]->store(tmp_str.ptr(), tmp_str.length(), cs);
}
table->field[9]->set_notnull();
@@ -5917,7 +6587,7 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
}
else if (part_info->list_of_subpart_fields)
{
- collect_partition_expr(part_info->subpart_field_list, &tmp_str);
+ collect_partition_expr(thd, part_info->subpart_field_list, &tmp_str);
table->field[10]->store(tmp_str.ptr(), tmp_str.length(), cs);
}
table->field[10]->set_notnull();
@@ -5935,36 +6605,70 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
/* Partition description */
if (part_info->part_type == RANGE_PARTITION)
{
- if (part_elem->range_value != LONGLONG_MAX)
- table->field[11]->store((longlong) part_elem->range_value, FALSE);
+ if (part_info->column_list)
+ {
+ 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,
+ tmp_str))
+ {
+ DBUG_RETURN(1);
+ }
+ table->field[11]->store(tmp_str.ptr(), tmp_str.length(), cs);
+ }
else
- table->field[11]->store(partition_keywords[PKW_MAXVALUE].str,
+ {
+ if (part_elem->range_value != LONGLONG_MAX)
+ table->field[11]->store((longlong) part_elem->range_value, FALSE);
+ else
+ table->field[11]->store(partition_keywords[PKW_MAXVALUE].str,
partition_keywords[PKW_MAXVALUE].length, cs);
+ }
table->field[11]->set_notnull();
}
else if (part_info->part_type == LIST_PARTITION)
{
List_iterator<part_elem_value> list_val_it(part_elem->list_val_list);
part_elem_value *list_value;
- uint no_items= part_elem->list_val_list.elements;
+ uint num_items= part_elem->list_val_list.elements;
tmp_str.length(0);
tmp_res.length(0);
if (part_elem->has_null_value)
{
tmp_str.append("NULL");
- if (no_items > 0)
+ if (num_items > 0)
tmp_str.append(",");
}
while ((list_value= list_val_it++))
{
- if (!list_value->unsigned_flag)
- tmp_res.set(list_value->value, cs);
+ 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))
+ {
+ DBUG_RETURN(1);
+ }
+ if (part_info->part_field_list.elements > 1U)
+ tmp_str.append(")");
+ }
else
- tmp_res.set((ulonglong)list_value->value, cs);
- tmp_str.append(tmp_res);
- if (--no_items != 0)
+ {
+ if (!list_value->unsigned_flag)
+ tmp_res.set(list_value->value, cs);
+ else
+ tmp_res.set((ulonglong)list_value->value, cs);
+ tmp_str.append(tmp_res);
+ }
+ if (--num_items != 0)
tmp_str.append(",");
- };
+ }
table->field[11]->store(tmp_str.ptr(), tmp_str.length(), cs);
table->field[11]->set_notnull();
}
@@ -6013,52 +6717,6 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
}
-#ifdef NOT_USED
-static interval_type get_real_interval_type(interval_type i_type)
-{
- switch (i_type) {
- case INTERVAL_YEAR:
- return INTERVAL_YEAR;
-
- case INTERVAL_QUARTER:
- case INTERVAL_YEAR_MONTH:
- case INTERVAL_MONTH:
- return INTERVAL_MONTH;
-
- case INTERVAL_WEEK:
- case INTERVAL_DAY:
- return INTERVAL_DAY;
-
- case INTERVAL_DAY_HOUR:
- case INTERVAL_HOUR:
- return INTERVAL_HOUR;
-
- case INTERVAL_DAY_MINUTE:
- case INTERVAL_HOUR_MINUTE:
- case INTERVAL_MINUTE:
- return INTERVAL_MINUTE;
-
- case INTERVAL_DAY_SECOND:
- case INTERVAL_HOUR_SECOND:
- case INTERVAL_MINUTE_SECOND:
- case INTERVAL_SECOND:
- return INTERVAL_SECOND;
-
- case INTERVAL_DAY_MICROSECOND:
- case INTERVAL_HOUR_MICROSECOND:
- case INTERVAL_MINUTE_MICROSECOND:
- case INTERVAL_SECOND_MICROSECOND:
- case INTERVAL_MICROSECOND:
- return INTERVAL_MICROSECOND;
- case INTERVAL_LAST:
- DBUG_ASSERT(0);
- }
- DBUG_ASSERT(0);
- return INTERVAL_SECOND;
-}
-
-#endif
-
#ifdef HAVE_EVENT_SCHEDULER
/*
Loads an event from mysql.event and copies it's data to a row of
@@ -6101,12 +6759,10 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
has access.
*/
if (thd->lex->sql_command != SQLCOM_SHOW_EVENTS &&
- check_access(thd, EVENT_ACL, et.dbname.str, 0, 0, 1,
- is_schema_db(et.dbname.str, et.dbname.length)))
+ check_access(thd, EVENT_ACL, et.dbname.str, NULL, NULL, 0, 1))
DBUG_RETURN(0);
- /* ->field[0] is EVENT_CATALOG and is by default NULL */
-
+ sch_table->field[ISE_EVENT_CATALOG]->store(STRING_WITH_LEN("def"), scs);
sch_table->field[ISE_EVENT_SCHEMA]->
store(et.dbname.str, et.dbname.length,scs);
sch_table->field[ISE_EVENT_NAME]->
@@ -6124,7 +6780,7 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
/* SQL_MODE */
{
LEX_STRING sql_mode;
- sys_var::make_set(thd, et.sql_mode, &sql_mode_typelib, &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);
}
@@ -6278,17 +6934,17 @@ int fill_variables(THD *thd, TABLE_LIST *tables, COND *cond)
bool upper_case_names= (schema_table_idx != SCH_VARIABLES);
bool sorted_vars= (schema_table_idx == SCH_VARIABLES);
- if (lex->option_type == OPT_GLOBAL ||
+ if ((sorted_vars && lex->option_type == OPT_GLOBAL) ||
schema_table_idx == SCH_GLOBAL_VARIABLES)
option_type= OPT_GLOBAL;
COND *partial_cond= make_cond_for_info_schema(cond, tables);
- rw_rdlock(&LOCK_system_variables_hash);
- res= show_status_array(thd, wild, enumerate_sys_vars(thd, sorted_vars),
- option_type, NULL, "", tables->table, upper_case_names,
- partial_cond);
- rw_unlock(&LOCK_system_variables_hash);
+ mysql_rwlock_rdlock(&LOCK_system_variables_hash);
+ res= show_status_array(thd, wild, enumerate_sys_vars(thd, sorted_vars, option_type),
+ option_type, NULL, "", tables->table,
+ upper_case_names, partial_cond);
+ mysql_rwlock_unlock(&LOCK_system_variables_hash);
DBUG_RETURN(res);
}
@@ -6329,14 +6985,14 @@ int fill_status(THD *thd, TABLE_LIST *tables, COND *cond)
if (partial_cond)
partial_cond->val_int();
- pthread_mutex_lock(&LOCK_status);
+ mysql_mutex_lock(&LOCK_status);
if (option_type == OPT_GLOBAL)
calc_sum_of_all_status(&tmp);
res= show_status_array(thd, wild,
(SHOW_VAR *)all_status_vars.buffer,
option_type, tmp1, "", tables->table,
upper_case_names, partial_cond);
- pthread_mutex_unlock(&LOCK_status);
+ mysql_mutex_unlock(&LOCK_status);
DBUG_RETURN(res);
}
@@ -6371,7 +7027,7 @@ get_referential_constraints_record(THD *thd, TABLE_LIST *tables,
{
if (thd->is_error())
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->main_da.sql_errno(), thd->main_da.message());
+ thd->stmt_da->sql_errno(), thd->stmt_da->message());
thd->clear_error();
DBUG_RETURN(0);
}
@@ -6379,6 +7035,9 @@ get_referential_constraints_record(THD *thd, TABLE_LIST *tables,
{
List<FOREIGN_KEY_INFO> f_key_list;
TABLE *show_table= tables->table;
+ show_table->file->info(HA_STATUS_VARIABLE |
+ HA_STATUS_NO_LOCK |
+ HA_STATUS_TIME);
show_table->file->get_foreign_key_list(thd, &f_key_list);
FOREIGN_KEY_INFO *f_key_info;
@@ -6386,11 +7045,13 @@ get_referential_constraints_record(THD *thd, TABLE_LIST *tables,
while ((f_key_info= it++))
{
restore_record(table, s->default_values);
+ table->field[0]->store(STRING_WITH_LEN("def"), cs);
table->field[1]->store(db_name->str, db_name->length, cs);
table->field[9]->store(table_name->str, table_name->length, cs);
- table->field[2]->store(f_key_info->forein_id->str,
- f_key_info->forein_id->length, cs);
- table->field[4]->store(f_key_info->referenced_db->str,
+ table->field[2]->store(f_key_info->foreign_id->str,
+ f_key_info->foreign_id->length, cs);
+ table->field[3]->store(STRING_WITH_LEN("def"), cs);
+ table->field[4]->store(f_key_info->referenced_db->str,
f_key_info->referenced_db->length, cs);
table->field[10]->store(f_key_info->referenced_table->str,
f_key_info->referenced_table->length, cs);
@@ -6422,7 +7083,7 @@ struct schema_table_ref
ST_FIELD_INFO user_stats_fields_info[]=
{
- {"USER", USERNAME_LENGTH, MYSQL_TYPE_STRING, 0, 0, "User", SKIP_OPEN_TABLE},
+ {"USER", USERNAME_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, "User", SKIP_OPEN_TABLE},
{"TOTAL_CONNECTIONS", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Total_connections",SKIP_OPEN_TABLE},
{"CONCURRENT_CONNECTIONS", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Concurrent_connections",SKIP_OPEN_TABLE},
{"CONNECTED_TIME", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Connected_time",SKIP_OPEN_TABLE},
@@ -6520,6 +7181,7 @@ 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)) {
+ my_plugin_lock(thd, plugin);
p_schema_table->schema_table= schema_table;
DBUG_RETURN(1);
}
@@ -6700,8 +7362,8 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
tmp_table_param->schema_table= 1;
SELECT_LEX *select_lex= thd->lex->current_select;
if (!(table= create_tmp_table(thd, tmp_table_param,
- field_list, (ORDER*) 0, 0, 0,
- (select_lex->options | thd->options |
+ field_list, (ORDER*) 0, 0, 0,
+ (select_lex->options | thd->variables.option_bits |
TMP_TABLE_ALL_COLUMNS),
HA_POS_ERROR, table_list->alias)))
DBUG_RETURN(0);
@@ -6876,7 +7538,7 @@ int make_character_sets_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
int make_proc_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
{
- int fields_arr[]= {2, 3, 4, 19, 16, 15, 14, 18, 20, 21, 22, -1};
+ int fields_arr[]= {2, 3, 4, 27, 24, 23, 22, 26, 28, 29, 30, -1};
int *field_num= fields_arr;
ST_FIELD_INFO *field_info;
Name_resolution_context *context= &thd->lex->select_lex.context;
@@ -7013,7 +7675,7 @@ int make_schema_select(THD *thd, SELECT_LEX *sel,
strlen(schema_table->table_name), 0);
if (schema_table->old_format(thd, schema_table) || /* Handle old syntax */
!sel->add_table_to_list(thd, new Table_ident(thd, db, table, 0),
- 0, 0, TL_READ))
+ 0, 0, TL_READ, MDL_SHARED_READ))
{
DBUG_RETURN(1);
}
@@ -7040,10 +7702,15 @@ bool get_schema_tables_result(JOIN *join,
THD *thd= join->thd;
LEX *lex= thd->lex;
bool result= 0;
+ const char *old_proc_info;
DBUG_ENTER("get_schema_tables_result");
- thd->no_warnings_for_error= 1;
- for (JOIN_TAB *tab= first_linear_tab(join, WITH_CONST_TABLES);
+ Warnings_only_error_handler err_handler;
+ thd->push_internal_handler(&err_handler);
+ old_proc_info= thd_proc_info(thd, "Filling schema table");
+
+ JOIN_TAB *tab;
+ for (tab= first_linear_tab(join, WITHOUT_BUSH_ROOTS, WITH_CONST_TABLES);
tab;
tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS))
{
@@ -7095,6 +7762,7 @@ bool get_schema_tables_result(JOIN *join,
else
table_list->table->file->stats.records= 0;
+
if (table_list->schema_table->fill_table(thd, table_list,
tab->select_cond))
{
@@ -7102,44 +7770,65 @@ bool get_schema_tables_result(JOIN *join,
join->error= 1;
tab->read_record.table->file= table_list->table->file;
table_list->schema_table_state= executed_place;
- if (!thd->is_error())
- my_error(ER_UNKNOWN_ERROR, MYF(0));
break;
}
tab->read_record.table->file= table_list->table->file;
table_list->schema_table_state= executed_place;
}
}
- thd->no_warnings_for_error= 0;
+ thd->pop_internal_handler();
+ if (thd->is_error())
+ {
+ /*
+ This hack is here, because I_S code uses thd->clear_error() a lot.
+ Which means, a Warnings_only_error_handler cannot handle the error
+ corectly as it does not know whether an error is real (e.g. caused
+ by tab->select_cond->val_int()) or will be cleared later.
+ Thus it ignores all errors, and the real one (that is, the error
+ that was not cleared) is pushed now.
+
+ It also means that an audit plugin cannot process the error correctly
+ either. See also thd->clear_error()
+ */
+ thd->warning_info->push_warning(thd,
+ thd->stmt_da->sql_errno(),
+ thd->stmt_da->get_sqlstate(),
+ MYSQL_ERROR::WARN_LEVEL_ERROR,
+ thd->stmt_da->message());
+ }
+ else if (result)
+ my_error(ER_UNKNOWN_ERROR, MYF(0));
+ thd_proc_info(thd, old_proc_info);
DBUG_RETURN(result);
}
-struct run_hton_fill_schema_files_args
+struct run_hton_fill_schema_table_args
{
TABLE_LIST *tables;
COND *cond;
};
-static my_bool run_hton_fill_schema_files(THD *thd, plugin_ref plugin,
+static my_bool run_hton_fill_schema_table(THD *thd, plugin_ref plugin,
void *arg)
{
- struct run_hton_fill_schema_files_args *args=
- (run_hton_fill_schema_files_args *) arg;
+ struct run_hton_fill_schema_table_args *args=
+ (run_hton_fill_schema_table_args *) arg;
handlerton *hton= plugin_data(plugin, handlerton *);
- if(hton->fill_files_table && hton->state == SHOW_OPTION_YES)
- hton->fill_files_table(hton, thd, args->tables, args->cond);
+ if (hton->fill_is_table && hton->state == SHOW_OPTION_YES)
+ hton->fill_is_table(hton, thd, args->tables, args->cond,
+ get_schema_table_idx(args->tables->schema_table));
return false;
}
-int fill_schema_files(THD *thd, TABLE_LIST *tables, COND *cond)
+int hton_fill_schema_table(THD *thd, TABLE_LIST *tables, COND *cond)
{
- DBUG_ENTER("fill_schema_files");
+ DBUG_ENTER("hton_fill_schema_table");
- struct run_hton_fill_schema_files_args args;
+ struct run_hton_fill_schema_table_args args;
args.tables= tables;
args.cond= cond;
- plugin_foreach(thd, run_hton_fill_schema_files,
+ plugin_foreach(thd, run_hton_fill_schema_table,
MYSQL_STORAGE_ENGINE_PLUGIN, &args);
DBUG_RETURN(0);
@@ -7192,47 +7881,48 @@ int store_key_cache_table_record(THD *thd, TABLE *table,
DBUG_RETURN(err);
}
-
-int fill_key_cache_tables(THD *thd, TABLE_LIST *tables, COND *cond)
+int run_fill_key_cache_tables(const char *name, KEY_CACHE *key_cache, void *p)
{
- TABLE *table= tables->table;
- I_List_iterator<NAMED_LIST> it(key_caches);
- NAMED_LIST *element;
- DBUG_ENTER("fill_key_cache_tables");
-
- while ((element= it++))
- {
- KEY_CACHE *key_cache= (KEY_CACHE *) element->data;
+ DBUG_ENTER("run_fill_key_cache_tables");
- if (!key_cache->key_cache_inited)
- continue;
+ if (!key_cache->key_cache_inited)
+ DBUG_RETURN(0);
- uint partitions= key_cache->partitions;
- DBUG_ASSERT(partitions <= MAX_KEY_CACHE_PARTITIONS);
+ TABLE *table= (TABLE *)p;
+ THD *thd= table->in_use;
+ uint partitions= key_cache->partitions;
+ size_t namelen= strlen(name);
+ DBUG_ASSERT(partitions <= MAX_KEY_CACHE_PARTITIONS);
- if (partitions)
+ if (partitions)
+ {
+ for (uint i= 0; i < partitions; i++)
{
- for (uint i= 0; i < partitions; i++)
- {
- if (store_key_cache_table_record(thd, table,
- element->name, element->name_length,
- key_cache, partitions, i+1))
- DBUG_RETURN(1);
- }
+ if (store_key_cache_table_record(thd, table, name, namelen,
+ key_cache, partitions, i+1))
+ DBUG_RETURN(1);
}
-
- if (store_key_cache_table_record(thd, table,
- element->name, element->name_length,
- key_cache, partitions, 0))
- DBUG_RETURN(1);
}
+
+ if (store_key_cache_table_record(thd, table, name, namelen,
+ key_cache, partitions, 0))
+ DBUG_RETURN(1);
DBUG_RETURN(0);
}
+int fill_key_cache_tables(THD *thd, TABLE_LIST *tables, COND *cond)
+{
+ DBUG_ENTER("fill_key_cache_tables");
+
+ int res= process_key_caches(run_fill_key_cache_tables, tables->table);
+
+ DBUG_RETURN(res);
+}
+
ST_FIELD_INFO schema_fields_info[]=
{
- {"CATALOG_NAME", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"CATALOG_NAME", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"SCHEMA_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Database",
SKIP_OPEN_TABLE},
{"DEFAULT_CHARACTER_SET_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0,
@@ -7246,7 +7936,7 @@ ST_FIELD_INFO schema_fields_info[]=
ST_FIELD_INFO tables_fields_info[]=
{
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name",
SKIP_OPEN_TABLE},
@@ -7278,14 +7968,15 @@ ST_FIELD_INFO tables_fields_info[]=
(MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Checksum", OPEN_FULL_TABLE},
{"CREATE_OPTIONS", 255, MYSQL_TYPE_STRING, 0, 1, "Create_options",
OPEN_FRM_ONLY},
- {"TABLE_COMMENT", 80, MYSQL_TYPE_STRING, 0, 0, "Comment", OPEN_FRM_ONLY},
+ {"TABLE_COMMENT", TABLE_COMMENT_MAXLEN, MYSQL_TYPE_STRING, 0, 0,
+ "Comment", OPEN_FRM_ONLY},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
ST_FIELD_INFO columns_fields_info[]=
{
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FRM_ONLY},
+ {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
{"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
{"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
{"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Field",
@@ -7314,7 +8005,8 @@ ST_FIELD_INFO columns_fields_info[]=
{"COLUMN_KEY", 3, MYSQL_TYPE_STRING, 0, 0, "Key", OPEN_FRM_ONLY},
{"EXTRA", 27, MYSQL_TYPE_STRING, 0, 0, "Extra", OPEN_FRM_ONLY},
{"PRIVILEGES", 80, MYSQL_TYPE_STRING, 0, 0, "Privileges", OPEN_FRM_ONLY},
- {"COLUMN_COMMENT", 255, MYSQL_TYPE_STRING, 0, 0, "Comment", OPEN_FRM_ONLY},
+ {"COLUMN_COMMENT", COLUMN_COMMENT_MAXLEN, MYSQL_TYPE_STRING, 0, 0,
+ "Comment", OPEN_FRM_ONLY},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
@@ -7361,12 +8053,12 @@ ST_FIELD_INFO engines_fields_info[]=
ST_FIELD_INFO events_fields_info[]=
{
- {"EVENT_CATALOG", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"EVENT_CATALOG", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"EVENT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Db",
SKIP_OPEN_TABLE},
{"EVENT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name",
SKIP_OPEN_TABLE},
- {"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, "Definer", SKIP_OPEN_TABLE},
+ {"DEFINER", DEFINER_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, "Definer", SKIP_OPEN_TABLE},
{"TIME_ZONE", 64, MYSQL_TYPE_STRING, 0, 0, "Time zone", SKIP_OPEN_TABLE},
{"EVENT_BODY", 8, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"EVENT_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
@@ -7410,13 +8102,22 @@ ST_FIELD_INFO coll_charset_app_fields_info[]=
ST_FIELD_INFO proc_fields_info[]=
{
{"SPECIFIC_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"ROUTINE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"ROUTINE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"ROUTINE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Db",
SKIP_OPEN_TABLE},
{"ROUTINE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name",
SKIP_OPEN_TABLE},
{"ROUTINE_TYPE", 9, MYSQL_TYPE_STRING, 0, 0, "Type", SKIP_OPEN_TABLE},
- {"DTD_IDENTIFIER", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"DATA_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"CHARACTER_MAXIMUM_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"CHARACTER_OCTET_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"NUMERIC_PRECISION", 21 , MYSQL_TYPE_LONG, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"NUMERIC_SCALE", 21 , MYSQL_TYPE_LONG, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"DATETIME_PRECISION", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG,
+ 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY},
+ {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"DTD_IDENTIFIER", 65535, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
{"ROUTINE_BODY", 8, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"ROUTINE_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
{"EXTERNAL_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
@@ -7432,9 +8133,9 @@ ST_FIELD_INFO proc_fields_info[]=
{"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 0, "Created", SKIP_OPEN_TABLE},
{"LAST_ALTERED", 0, MYSQL_TYPE_DATETIME, 0, 0, "Modified", SKIP_OPEN_TABLE},
{"SQL_MODE", 32*256, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"ROUTINE_COMMENT", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Comment",
+ {"ROUTINE_COMMENT", 65535, MYSQL_TYPE_STRING, 0, 0, "Comment",
SKIP_OPEN_TABLE},
- {"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, "Definer", SKIP_OPEN_TABLE},
+ {"DEFINER", DEFINER_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, "Definer", SKIP_OPEN_TABLE},
{"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
"character_set_client", SKIP_OPEN_TABLE},
{"COLLATION_CONNECTION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
@@ -7447,7 +8148,7 @@ ST_FIELD_INFO proc_fields_info[]=
ST_FIELD_INFO stat_fields_info[]=
{
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FRM_ONLY},
+ {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
{"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
{"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table", OPEN_FRM_ONLY},
{"NON_UNIQUE", 1, MYSQL_TYPE_LONGLONG, 0, 0, "Non_unique", OPEN_FRM_ONLY},
@@ -7465,24 +8166,26 @@ ST_FIELD_INFO stat_fields_info[]=
{"NULLABLE", 3, MYSQL_TYPE_STRING, 0, 0, "Null", OPEN_FRM_ONLY},
{"INDEX_TYPE", 16, MYSQL_TYPE_STRING, 0, 0, "Index_type", OPEN_FULL_TABLE},
{"COMMENT", 16, MYSQL_TYPE_STRING, 0, 1, "Comment", OPEN_FRM_ONLY},
+ {"INDEX_COMMENT", INDEX_COMMENT_MAXLEN, MYSQL_TYPE_STRING, 0, 0,
+ "Index_comment", OPEN_FRM_ONLY},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
ST_FIELD_INFO view_fields_info[]=
{
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FRM_ONLY},
+ {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
{"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
{"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"VIEW_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"CHECK_OPTION", 8, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
+ {"VIEW_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
+ {"CHECK_OPTION", 8, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
{"IS_UPDATABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"SECURITY_TYPE", 7, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
+ {"DEFINER", DEFINER_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
+ {"SECURITY_TYPE", 7, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
{"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FULL_TABLE},
+ OPEN_FRM_ONLY},
{"COLLATION_CONNECTION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FULL_TABLE},
+ OPEN_FRM_ONLY},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
@@ -7490,7 +8193,7 @@ ST_FIELD_INFO view_fields_info[]=
ST_FIELD_INFO user_privileges_fields_info[]=
{
{"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"PRIVILEGE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
@@ -7500,7 +8203,7 @@ ST_FIELD_INFO user_privileges_fields_info[]=
ST_FIELD_INFO schema_privileges_fields_info[]=
{
{"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"PRIVILEGE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
@@ -7511,7 +8214,7 @@ ST_FIELD_INFO schema_privileges_fields_info[]=
ST_FIELD_INFO table_privileges_fields_info[]=
{
{"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"PRIVILEGE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
@@ -7523,7 +8226,7 @@ ST_FIELD_INFO table_privileges_fields_info[]=
ST_FIELD_INFO column_privileges_fields_info[]=
{
{"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
@@ -7535,7 +8238,7 @@ ST_FIELD_INFO column_privileges_fields_info[]=
ST_FIELD_INFO table_constraints_fields_info[]=
{
- {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
+ {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
{"CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
OPEN_FULL_TABLE},
{"CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
@@ -7550,12 +8253,12 @@ ST_FIELD_INFO table_constraints_fields_info[]=
ST_FIELD_INFO key_column_usage_fields_info[]=
{
- {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
+ {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
{"CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
OPEN_FULL_TABLE},
{"CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
OPEN_FULL_TABLE},
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
+ {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
{"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
{"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
{"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
@@ -7574,7 +8277,7 @@ ST_FIELD_INFO key_column_usage_fields_info[]=
ST_FIELD_INFO table_names_fields_info[]=
{
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_NAME", NAME_CHAR_LEN + MYSQL50_TABLE_NAME_PREFIX_LENGTH,
MYSQL_TYPE_STRING, 0, 0, "Tables_in_", SKIP_OPEN_TABLE},
@@ -7597,45 +8300,45 @@ ST_FIELD_INFO open_tables_fields_info[]=
ST_FIELD_INFO triggers_fields_info[]=
{
- {"TRIGGER_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
- {"TRIGGER_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
+ {"TRIGGER_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
+ {"TRIGGER_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
{"TRIGGER_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Trigger",
- OPEN_FULL_TABLE},
- {"EVENT_MANIPULATION", 6, MYSQL_TYPE_STRING, 0, 0, "Event", OPEN_FULL_TABLE},
- {"EVENT_OBJECT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0,
- OPEN_FULL_TABLE},
+ OPEN_FRM_ONLY},
+ {"EVENT_MANIPULATION", 6, MYSQL_TYPE_STRING, 0, 0, "Event", OPEN_FRM_ONLY},
+ {"EVENT_OBJECT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0,
+ OPEN_FRM_ONLY},
{"EVENT_OBJECT_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FULL_TABLE},
+ OPEN_FRM_ONLY},
{"EVENT_OBJECT_TABLE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table",
- OPEN_FULL_TABLE},
- {"ACTION_ORDER", 4, MYSQL_TYPE_LONGLONG, 0, 0, 0, OPEN_FULL_TABLE},
- {"ACTION_CONDITION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
+ OPEN_FRM_ONLY},
+ {"ACTION_ORDER", 4, MYSQL_TYPE_LONGLONG, 0, 0, 0, OPEN_FRM_ONLY},
+ {"ACTION_CONDITION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FRM_ONLY},
{"ACTION_STATEMENT", 65535, MYSQL_TYPE_STRING, 0, 0, "Statement",
- OPEN_FULL_TABLE},
- {"ACTION_ORIENTATION", 9, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"ACTION_TIMING", 6, MYSQL_TYPE_STRING, 0, 0, "Timing", OPEN_FULL_TABLE},
+ OPEN_FRM_ONLY},
+ {"ACTION_ORIENTATION", 9, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
+ {"ACTION_TIMING", 6, MYSQL_TYPE_STRING, 0, 0, "Timing", OPEN_FRM_ONLY},
{"ACTION_REFERENCE_OLD_TABLE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
- OPEN_FULL_TABLE},
+ OPEN_FRM_ONLY},
{"ACTION_REFERENCE_NEW_TABLE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
- OPEN_FULL_TABLE},
- {"ACTION_REFERENCE_OLD_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"ACTION_REFERENCE_NEW_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 1, "Created", OPEN_FULL_TABLE},
- {"SQL_MODE", 32*256, MYSQL_TYPE_STRING, 0, 0, "sql_mode", OPEN_FULL_TABLE},
- {"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, "Definer", OPEN_FULL_TABLE},
+ OPEN_FRM_ONLY},
+ {"ACTION_REFERENCE_OLD_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
+ {"ACTION_REFERENCE_NEW_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
+ {"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 1, "Created", OPEN_FRM_ONLY},
+ {"SQL_MODE", 32*256, MYSQL_TYPE_STRING, 0, 0, "sql_mode", OPEN_FRM_ONLY},
+ {"DEFINER", DEFINER_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, "Definer", OPEN_FRM_ONLY},
{"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
- "character_set_client", OPEN_FULL_TABLE},
+ "character_set_client", OPEN_FRM_ONLY},
{"COLLATION_CONNECTION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
- "collation_connection", OPEN_FULL_TABLE},
+ "collation_connection", OPEN_FRM_ONLY},
{"DATABASE_COLLATION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
- "Database Collation", OPEN_FULL_TABLE},
+ "Database Collation", OPEN_FRM_ONLY},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
ST_FIELD_INFO partitions_fields_info[]=
{
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
+ {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
{"TABLE_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
{"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
{"PARTITION_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
@@ -7645,7 +8348,7 @@ ST_FIELD_INFO partitions_fields_info[]=
(MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FULL_TABLE},
{"SUBPARTITION_ORDINAL_POSITION", 21 , MYSQL_TYPE_LONGLONG, 0,
(MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FULL_TABLE},
- {"PARTITION_METHOD", 12, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
+ {"PARTITION_METHOD", 18, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
{"SUBPARTITION_METHOD", 12, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
{"PARTITION_EXPRESSION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
{"SUBPARTITION_EXPRESSION", 65535, MYSQL_TYPE_STRING, 0, 1, 0,
@@ -7688,7 +8391,8 @@ ST_FIELD_INFO variables_fields_info[]=
ST_FIELD_INFO processlist_fields_info[]=
{
{"ID", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Id", SKIP_OPEN_TABLE},
- {"USER", 16, MYSQL_TYPE_STRING, 0, 0, "User", SKIP_OPEN_TABLE},
+ {"USER", USERNAME_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, "User",
+ SKIP_OPEN_TABLE},
{"HOST", LIST_PROCESS_HOST_LEN, MYSQL_TYPE_STRING, 0, 0, "Host",
SKIP_OPEN_TABLE},
{"DB", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, "Db", SKIP_OPEN_TABLE},
@@ -7720,8 +8424,9 @@ ST_FIELD_INFO plugin_fields_info[]=
{"PLUGIN_LIBRARY_VERSION", 20, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
{"PLUGIN_AUTHOR", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
{"PLUGIN_DESCRIPTION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
- {"PLUGIN_LICENSE", 80, MYSQL_TYPE_STRING, 0, 1, "License", SKIP_OPEN_TABLE},
- {"PLUGIN_MATURITY", 12, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"PLUGIN_LICENSE", 80, MYSQL_TYPE_STRING, 0, 0, "License", SKIP_OPEN_TABLE},
+ {"LOAD_OPTION", 64, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"PLUGIN_MATURITY", 12, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"PLUGIN_AUTH_VERSION", 80, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
@@ -7733,7 +8438,7 @@ ST_FIELD_INFO files_fields_info[]=
{"FILE_TYPE", 20, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLESPACE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
SKIP_OPEN_TABLE},
- {"TABLE_CATALOG", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
+ {"TABLE_CATALOG", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
{"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
{"LOGFILE_GROUP_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
@@ -7794,12 +8499,12 @@ void init_fill_schema_files_row(TABLE* table)
ST_FIELD_INFO referential_constraints_fields_info[]=
{
- {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
+ {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
{"CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
OPEN_FULL_TABLE},
{"CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
OPEN_FULL_TABLE},
- {"UNIQUE_CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0,
+ {"UNIQUE_CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0,
OPEN_FULL_TABLE},
{"UNIQUE_CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
OPEN_FULL_TABLE},
@@ -7815,6 +8520,53 @@ ST_FIELD_INFO referential_constraints_fields_info[]=
};
+ST_FIELD_INFO parameters_fields_info[]=
+{
+ {"SPECIFIC_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
+ {"SPECIFIC_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
+ OPEN_FULL_TABLE},
+ {"SPECIFIC_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
+ {"ORDINAL_POSITION", 21 , MYSQL_TYPE_LONG, 0, 0, 0, OPEN_FULL_TABLE},
+ {"PARAMETER_MODE", 5, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
+ {"PARAMETER_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
+ {"DATA_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
+ {"CHARACTER_MAXIMUM_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, 0, OPEN_FULL_TABLE},
+ {"CHARACTER_OCTET_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, 0, OPEN_FULL_TABLE},
+ {"NUMERIC_PRECISION", 21 , MYSQL_TYPE_LONG, 0, 1, 0, OPEN_FULL_TABLE},
+ {"NUMERIC_SCALE", 21 , MYSQL_TYPE_LONG, 0, 1, 0, OPEN_FULL_TABLE},
+ {"DATETIME_PRECISION", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG,
+ 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY},
+ {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
+ {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
+ {"DTD_IDENTIFIER", 65535, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
+ {"ROUTINE_TYPE", 9, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}
+};
+
+
+ST_FIELD_INFO tablespaces_fields_info[]=
+{
+ {"TABLESPACE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
+ SKIP_OPEN_TABLE},
+ {"ENGINE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"TABLESPACE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL,
+ 0, SKIP_OPEN_TABLE},
+ {"LOGFILE_GROUP_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL,
+ 0, SKIP_OPEN_TABLE},
+ {"EXTENT_SIZE", 21, MYSQL_TYPE_LONGLONG, 0,
+ MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED, 0, SKIP_OPEN_TABLE},
+ {"AUTOEXTEND_SIZE", 21, MYSQL_TYPE_LONGLONG, 0,
+ MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED, 0, SKIP_OPEN_TABLE},
+ {"MAXIMUM_SIZE", 21, MYSQL_TYPE_LONGLONG, 0,
+ MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED, 0, SKIP_OPEN_TABLE},
+ {"NODEGROUP_ID", 21, MYSQL_TYPE_LONGLONG, 0,
+ MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED, 0, SKIP_OPEN_TABLE},
+ {"TABLESPACE_COMMENT", 2048, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0,
+ SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+};
+
+
ST_FIELD_INFO keycache_fields_info[]=
{
{"KEY_CACHE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
@@ -7876,7 +8628,7 @@ ST_SCHEMA_TABLE schema_tables[]=
0, make_old_format, 0, -1, -1, 0, 0},
#endif
{"FILES", files_fields_info, create_schema_table,
- fill_schema_files, 0, 0, -1, -1, 0, 0},
+ hton_fill_schema_table, 0, 0, -1, -1, 0, 0},
{"GLOBAL_STATUS", variables_fields_info, create_schema_table,
fill_status, make_old_format, 0, 0, -1, 0, 0},
{"GLOBAL_VARIABLES", variables_fields_info, create_schema_table,
@@ -7887,11 +8639,14 @@ ST_SCHEMA_TABLE schema_tables[]=
fill_key_cache_tables, make_old_format, 0, -1,-1, 0, 0},
{"KEY_COLUMN_USAGE", key_column_usage_fields_info, create_schema_table,
get_all_tables, 0, get_schema_key_column_usage_record, 4, 5, 0,
- OPEN_TABLE_ONLY},
+ OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY},
{"OPEN_TABLES", open_tables_fields_info, create_schema_table,
fill_open_tables, make_old_format, 0, -1, -1, 1, 0},
+ {"PARAMETERS", parameters_fields_info, create_schema_table,
+ fill_schema_proc, 0, 0, -1, -1, 0, 0},
{"PARTITIONS", partitions_fields_info, create_schema_table,
- get_all_tables, 0, get_schema_partitions_record, 1, 2, 0, OPEN_TABLE_ONLY},
+ get_all_tables, 0, get_schema_partitions_record, 1, 2, 0,
+ OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY},
{"PLUGINS", plugin_fields_info, create_schema_table,
fill_plugins, make_old_format, 0, -1, -1, 0, 0},
{"PROCESSLIST", processlist_fields_info, create_schema_table,
@@ -7901,8 +8656,8 @@ ST_SCHEMA_TABLE schema_tables[]=
NULL, -1, -1, false, 0},
{"REFERENTIAL_CONSTRAINTS", referential_constraints_fields_info,
create_schema_table, get_all_tables, 0, get_referential_constraints_record,
- 1, 9, 0, OPEN_TABLE_ONLY},
- {"ROUTINES", proc_fields_info, create_schema_table,
+ 1, 9, 0, OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY},
+ {"ROUTINES", proc_fields_info, create_schema_table,
fill_schema_proc, make_proc_old_format, 0, -1, -1, 0, 0},
{"SCHEMATA", schema_fields_info, create_schema_table,
fill_schema_schemata, make_schemata_old_format, 0, 1, -1, 0, 0},
@@ -7920,8 +8675,11 @@ ST_SCHEMA_TABLE schema_tables[]=
{"TABLES", tables_fields_info, create_schema_table,
get_all_tables, make_old_format, get_schema_tables_record, 1, 2, 0,
OPTIMIZE_I_S_TABLE},
+ {"TABLESPACES", tablespaces_fields_info, create_schema_table,
+ hton_fill_schema_table, 0, 0, -1, -1, 0, 0},
{"TABLE_CONSTRAINTS", table_constraints_fields_info, create_schema_table,
- get_all_tables, 0, get_schema_constraints_record, 3, 4, 0, OPEN_TABLE_ONLY},
+ get_all_tables, 0, get_schema_constraints_record, 3, 4, 0,
+ OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY},
{"TABLE_NAMES", table_names_fields_info, create_schema_table,
get_all_tables, make_table_names_old_format, 0, 1, 2, 1, OPTIMIZE_I_S_TABLE},
{"TABLE_PRIVILEGES", table_privileges_fields_info, create_schema_table,
@@ -7930,8 +8688,8 @@ ST_SCHEMA_TABLE schema_tables[]=
fill_schema_table_stats, make_old_format, 0, -1, -1, 0, 0},
{"TRIGGERS", triggers_fields_info, create_schema_table,
get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0,
- OPEN_TABLE_ONLY},
- {"USER_PRIVILEGES", user_privileges_fields_info, create_schema_table,
+ OPEN_TRIGGER_ONLY|OPTIMIZE_I_S_TABLE},
+ {"USER_PRIVILEGES", user_privileges_fields_info, create_schema_table,
fill_schema_user_privileges, 0, 0, -1, -1, 0, 0},
{"USER_STATISTICS", user_stats_fields_info, create_schema_table,
fill_schema_user_stats, make_old_format, 0, -1, -1, 0, 0},
@@ -7974,7 +8732,7 @@ int initialize_schema_table(st_plugin_int *plugin)
sql_print_error("Plugin '%s' init function returned error.",
plugin->name.str);
plugin->data= NULL;
- my_free(schema_table, MYF(0));
+ my_free(schema_table);
DBUG_RETURN(1);
}
@@ -8000,7 +8758,7 @@ int finalize_schema_table(st_plugin_int *plugin)
plugin->name.str));
}
}
- my_free(schema_table, MYF(0));
+ my_free(schema_table);
}
DBUG_RETURN(0);
}
@@ -8058,7 +8816,7 @@ static bool show_create_trigger_impl(THD *thd,
&trg_connection_cl_name,
&trg_db_cl_name);
- sys_var::make_set(thd, trg_sql_mode, &sql_mode_typelib, &trg_sql_mode_str);
+ sql_mode_string_representation(thd, trg_sql_mode, &trg_sql_mode_str);
/* Resolve trigger client character set. */
@@ -8094,7 +8852,7 @@ static bool show_create_trigger_impl(THD *thd,
fields.push_back(new Item_empty_string("Database Collation",
MY_CS_NAME_SIZE));
- if (p->send_fields(&fields, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ if (p->send_result_set_metadata(&fields, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
return TRUE;
/* Send data. */
@@ -8158,14 +8916,14 @@ static bool show_create_trigger_impl(THD *thd,
- do not update Lex::query_tables in add_table_to_list().
*/
-static TABLE_LIST *get_trigger_table_impl(
- THD *thd,
- const sp_name *trg_name)
+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;
+ TABLE_LIST *table;
build_trn_path(thd, trg_name, &trn_path);
@@ -8179,61 +8937,23 @@ static TABLE_LIST *get_trigger_table_impl(
return NULL;
/* We need to reset statement table list to be PS/SP friendly. */
-
- TABLE_LIST *table;
-
- if (!(table= (TABLE_LIST *)thd->calloc(sizeof(TABLE_LIST))))
- {
- my_error(ER_OUTOFMEMORY, MYF(0), static_cast<int>(sizeof(TABLE_LIST)));
+ if (!(table= (TABLE_LIST*) thd->alloc(sizeof(TABLE_LIST))))
return NULL;
- }
- table->db_length= trg_name->m_db.length;
- table->db= thd->strmake(trg_name->m_db.str, trg_name->m_db.length);
+ db= trg_name->m_db;
- table->table_name_length= tbl_name.length;
- table->table_name= thd->strmake(tbl_name.str, tbl_name.length);
+ db.str= thd->strmake(db.str, db.length);
+ tbl_name.str= thd->strmake(tbl_name.str, tbl_name.length);
- table->alias= thd->strmake(tbl_name.str, tbl_name.length);
+ if (db.str == NULL || tbl_name.str == NULL)
+ return NULL;
- table->lock_type= TL_IGNORE;
- table->cacheable_table= 0;
+ table->init_one_table(db.str, db.length, tbl_name.str, tbl_name.length,
+ tbl_name.str, TL_IGNORE);
return table;
}
-/**
- Read TRN and TRG files to obtain base table name for the specified
- trigger name and construct TABE_LIST object for the base table. Acquire
- LOCK_open when doing this.
-
- @param thd Thread context.
- @param trg_name Trigger name.
-
- @return TABLE_LIST object corresponding to the base table.
-*/
-
-static TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name)
-{
- /* Acquire LOCK_open (stop the server). */
-
- pthread_mutex_lock(&LOCK_open);
-
- /*
- Load base table name from the TRN-file and create TABLE_LIST object.
- */
-
- TABLE_LIST *lst= get_trigger_table_impl(thd, trg_name);
-
- /* Release LOCK_open (continue the server). */
-
- pthread_mutex_unlock(&LOCK_open);
-
- /* That's it. */
-
- return lst;
-}
-
/**
SHOW CREATE TRIGGER high-level implementation.
@@ -8249,46 +8969,50 @@ static TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name)
bool show_create_trigger(THD *thd, const sp_name *trg_name)
{
TABLE_LIST *lst= get_trigger_table(thd, trg_name);
+ uint num_tables; /* NOTE: unused, only to pass to open_tables(). */
+ Table_triggers_list *triggers;
+ int trigger_idx;
+ bool error= TRUE;
if (!lst)
return TRUE;
- if (check_table_access(thd, TRIGGER_ACL, lst, 1, TRUE))
+ if (check_table_access(thd, TRIGGER_ACL, lst, FALSE, 1, TRUE))
{
my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "TRIGGER");
return TRUE;
}
/*
- Open the table by name in order to load Table_triggers_list object.
-
- NOTE: there is race condition here -- the table can be dropped after
- LOCK_open is released. It will be fixed later by introducing
- acquire-shared-table-name-lock functionality.
+ Metadata locks taken during SHOW CREATE TRIGGER should be released when
+ the statement completes as it is an information statement.
*/
+ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
- uint num_tables; /* NOTE: unused, only to pass to open_tables(). */
-
- if (open_tables(thd, &lst, &num_tables, 0))
+ /*
+ Open the table by name in order to load Table_triggers_list object.
+ */
+ if (open_tables(thd, &lst, &num_tables,
+ MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL))
{
my_error(ER_TRG_CANT_OPEN_TABLE, MYF(0),
(const char *) trg_name->m_db.str,
(const char *) lst->table_name);
- return TRUE;
+ goto exit;
/* Perform closing actions and return error status. */
}
- Table_triggers_list *triggers= lst->table->triggers;
+ triggers= lst->table->triggers;
if (!triggers)
{
my_error(ER_TRG_DOES_NOT_EXIST, MYF(0));
- return TRUE;
+ goto exit;
}
- int trigger_idx= triggers->find_trigger_by_name(&trg_name->m_name);
+ trigger_idx= triggers->find_trigger_by_name(&trg_name->m_name);
if (trigger_idx < 0)
{
@@ -8296,14 +9020,164 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name)
(const char *) trg_name->m_db.str,
(const char *) lst->table_name);
- return TRUE;
+ goto exit;
}
- return show_create_trigger_impl(thd, triggers, trigger_idx);
+ error= show_create_trigger_impl(thd, triggers, trigger_idx);
/*
NOTE: if show_create_trigger_impl() failed, that means we could not
send data to the client. In this case we simply raise the error
status and client connection will be closed.
*/
+
+exit:
+ close_thread_tables(thd);
+ /* Release any metadata locks taken during SHOW CREATE TRIGGER. */
+ thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+ return error;
}
+
+class IS_internal_schema_access : public ACL_internal_schema_access
+{
+public:
+ IS_internal_schema_access()
+ {}
+
+ ~IS_internal_schema_access()
+ {}
+
+ ACL_internal_access_result check(ulong want_access,
+ ulong *save_priv) const;
+
+ const ACL_internal_table_access *lookup(const char *name) const;
+};
+
+ACL_internal_access_result
+IS_internal_schema_access::check(ulong want_access,
+ ulong *save_priv) const
+{
+ want_access &= ~SELECT_ACL;
+
+ /*
+ We don't allow any simple privileges but SELECT_ACL on
+ the information_schema database.
+ */
+ if (unlikely(want_access & DB_ACLS))
+ return ACL_INTERNAL_ACCESS_DENIED;
+
+ /* Always grant SELECT for the information schema. */
+ *save_priv|= SELECT_ACL;
+
+ return want_access ? ACL_INTERNAL_ACCESS_CHECK_GRANT :
+ ACL_INTERNAL_ACCESS_GRANTED;
+}
+
+const ACL_internal_table_access *
+IS_internal_schema_access::lookup(const char *name) const
+{
+ /* There are no per table rules for the information schema. */
+ return NULL;
+}
+
+static IS_internal_schema_access is_internal_schema_access;
+
+void initialize_information_schema_acl()
+{
+ ACL_internal_schema_registry::register_schema(&INFORMATION_SCHEMA_NAME,
+ &is_internal_schema_access);
+}
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+/*
+ Convert a string in character set in column character set format
+ to utf8 character set if possible, the utf8 character set string
+ will later possibly be converted to character set used by client.
+ Thus we attempt conversion from column character set to both
+ utf8 and to character set client.
+
+ Examples of strings that should fail conversion to utf8 are unassigned
+ characters as e.g. 0x81 in cp1250 (Windows character set for for countries
+ like Czech and Poland). Example of string that should fail conversion to
+ character set on client (e.g. if this is latin1) is 0x2020 (daggger) in
+ ucs2.
+
+ If the conversion fails we will as a fall back convert the string to
+ hex encoded format. The caller of the function can also ask for hex
+ encoded format of output string unconditionally.
+
+ SYNOPSIS
+ get_cs_converted_string_value()
+ thd Thread object
+ input_str Input string in cs character set
+ output_str Output string to be produced in utf8
+ cs Character set of input string
+ use_hex Use hex string unconditionally
+
+
+ RETURN VALUES
+ No return value
+*/
+
+static void get_cs_converted_string_value(THD *thd,
+ String *input_str,
+ String *output_str,
+ CHARSET_INFO *cs,
+ bool use_hex)
+{
+
+ output_str->length(0);
+ if (input_str->length() == 0)
+ {
+ output_str->append("''");
+ return;
+ }
+ if (!use_hex)
+ {
+ String try_val;
+ uint try_conv_error= 0;
+
+ try_val.copy(input_str->ptr(), input_str->length(), cs,
+ thd->variables.character_set_client, &try_conv_error);
+ if (!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)
+ {
+ append_unescaped(output_str, val.ptr(), val.length());
+ return;
+ }
+ }
+ /* We had a conversion error, use hex encoded string for safety */
+ }
+ {
+ const uchar *ptr;
+ uint i, len;
+ char buf[3];
+
+ output_str->append("_");
+ output_str->append(cs->csname);
+ output_str->append(" ");
+ output_str->append("0x");
+ len= input_str->length();
+ ptr= (uchar*)input_str->ptr();
+ for (i= 0; i < len; i++)
+ {
+ uint high, low;
+
+ high= (*ptr) >> 4;
+ low= (*ptr) & 0x0F;
+ buf[0]= _dig_vec_upper[high];
+ buf[1]= _dig_vec_upper[low];
+ buf[2]= 0;
+ output_str->append((const char*)buf);
+ ptr++;
+ }
+ }
+ return;
+}
+#endif
diff --git a/sql/sql_show.h b/sql/sql_show.h
index 92ca85e4a95..6e87f6097f0 100644
--- a/sql/sql_show.h
+++ b/sql/sql_show.h
@@ -1,5 +1,4 @@
-/*
- Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2005, 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
@@ -12,18 +11,29 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef SQL_SHOW_H
#define SQL_SHOW_H
+#include "sql_list.h" /* List */
+#include "handler.h" /* enum_schema_tables */
+#include "table.h" /* enum_schema_table_state */
+
/* Forward declarations */
+class JOIN;
class String;
class THD;
+class sp_name;
+struct TABLE_LIST;
struct st_ha_create_information;
+typedef class st_select_lex SELECT_LEX;
typedef st_ha_create_information HA_CREATE_INFO;
-struct TABLE_LIST;
+struct LEX;
+typedef struct st_mysql_show_var SHOW_VAR;
+typedef struct st_schema_table ST_SCHEMA_TABLE;
+struct TABLE;
+typedef struct system_status_var STATUS_VAR;
enum find_files_result {
FIND_FILES_OK,
@@ -31,6 +41,46 @@ enum find_files_result {
FIND_FILES_DIR
};
+/* Used by handlers to store things in schema tables */
+#define IS_FILES_FILE_ID 0
+#define IS_FILES_FILE_NAME 1
+#define IS_FILES_FILE_TYPE 2
+#define IS_FILES_TABLESPACE_NAME 3
+#define IS_FILES_TABLE_CATALOG 4
+#define IS_FILES_TABLE_SCHEMA 5
+#define IS_FILES_TABLE_NAME 6
+#define IS_FILES_LOGFILE_GROUP_NAME 7
+#define IS_FILES_LOGFILE_GROUP_NUMBER 8
+#define IS_FILES_ENGINE 9
+#define IS_FILES_FULLTEXT_KEYS 10
+#define IS_FILES_DELETED_ROWS 11
+#define IS_FILES_UPDATE_COUNT 12
+#define IS_FILES_FREE_EXTENTS 13
+#define IS_FILES_TOTAL_EXTENTS 14
+#define IS_FILES_EXTENT_SIZE 15
+#define IS_FILES_INITIAL_SIZE 16
+#define IS_FILES_MAXIMUM_SIZE 17
+#define IS_FILES_AUTOEXTEND_SIZE 18
+#define IS_FILES_CREATION_TIME 19
+#define IS_FILES_LAST_UPDATE_TIME 20
+#define IS_FILES_LAST_ACCESS_TIME 21
+#define IS_FILES_RECOVER_TIME 22
+#define IS_FILES_TRANSACTION_COUNTER 23
+#define IS_FILES_VERSION 24
+#define IS_FILES_ROW_FORMAT 25
+#define IS_FILES_TABLE_ROWS 26
+#define IS_FILES_AVG_ROW_LENGTH 27
+#define IS_FILES_DATA_LENGTH 28
+#define IS_FILES_MAX_DATA_LENGTH 29
+#define IS_FILES_INDEX_LENGTH 30
+#define IS_FILES_DATA_FREE 31
+#define IS_FILES_CREATE_TIME 32
+#define IS_FILES_UPDATE_TIME 33
+#define IS_FILES_CHECK_TIME 34
+#define IS_FILES_CHECKSUM 35
+#define IS_FILES_STATUS 36
+#define IS_FILES_EXTRA 37
+
find_files_result find_files(THD *thd, List<LEX_STRING> *files, const char *db,
const char *path, const char *wild, bool dir);
@@ -40,6 +90,47 @@ int view_store_create_info(THD *thd, TABLE_LIST *table, String *buff);
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);
+void mysqld_list_fields(THD *thd,TABLE_LIST *table, const char *wild);
+int mysqld_dump_create_info(THD *thd, TABLE_LIST *table_list, int fd);
+bool mysqld_show_create(THD *thd, TABLE_LIST *table_list);
+bool mysqld_show_create_db(THD *thd, char *dbname, HA_CREATE_INFO *create);
+
+void mysqld_list_processes(THD *thd,const char *user,bool verbose);
+int mysqld_show_status(THD *thd);
+int mysqld_show_variables(THD *thd,const char *wild);
+bool mysqld_show_storage_engines(THD *thd);
+bool mysqld_show_authors(THD *thd);
+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);
+void 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);
+int add_status_vars(SHOW_VAR *list);
+void remove_status_vars(SHOW_VAR *list);
+void init_status_vars();
+void free_status_vars();
+void reset_status_vars();
+bool show_create_trigger(THD *thd, const sp_name *trg_name);
+void view_store_options(THD *thd, TABLE_LIST *table, String *buff);
+
+void init_fill_schema_files_row(TABLE* table);
+bool schema_table_store_record(THD *thd, TABLE *table);
+void initialize_information_schema_acl();
+
+ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name);
+ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx);
+int make_schema_select(THD *thd, SELECT_LEX *sel,
+ enum enum_schema_tables schema_table_idx);
+int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list);
+bool get_schema_tables_result(JOIN *join,
+ enum enum_schema_table_state executed_place);
+enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table);
+
+/* These functions were under INNODB_COMPATIBILITY_HOOKS */
+int get_quote_char_for_identifier(THD *thd, const char *name, uint length);
/* Handle the ignored database directories list for SHOW/I_S. */
bool ignore_db_dirs_init();
diff --git a/sql/sql_signal.cc b/sql/sql_signal.cc
new file mode 100644
index 00000000000..e671ad9526f
--- /dev/null
+++ b/sql/sql_signal.cc
@@ -0,0 +1,513 @@
+/* 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-1301 USA */
+
+#include "sql_priv.h"
+#include "sp_head.h"
+#include "sp_pcontext.h"
+#include "sp_rcontext.h"
+#include "sql_signal.h"
+
+/*
+ The parser accepts any error code (desired)
+ The runtime internally supports any error code (desired)
+ The client server protocol is limited to 16 bits error codes (restriction),
+ and the value of 65535 is reserved for progress reporting.
+ Enforcing the 65534 limit in the runtime until the protocol can change.
+*/
+#define MAX_MYSQL_ERRNO 65534
+
+const LEX_STRING 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") }
+};
+
+const LEX_STRING Diag_statement_item_names[]=
+{
+ { C_STRING_WITH_LEN("NUMBER") },
+ { C_STRING_WITH_LEN("MORE") },
+ { C_STRING_WITH_LEN("COMMAND_FUNCTION") },
+ { C_STRING_WITH_LEN("COMMAND_FUNCTION_CODE") },
+ { C_STRING_WITH_LEN("DYNAMIC_FUNCTION") },
+ { C_STRING_WITH_LEN("DYNAMIC_FUNCTION_CODE") },
+ { C_STRING_WITH_LEN("ROW_COUNT") },
+ { C_STRING_WITH_LEN("TRANSACTIONS_COMMITTED") },
+ { C_STRING_WITH_LEN("TRANSACTIONS_ROLLED_BACK") },
+ { C_STRING_WITH_LEN("TRANSACTION_ACTIVE") }
+};
+
+
+Set_signal_information::Set_signal_information(
+ const Set_signal_information& set)
+{
+ memcpy(m_item, set.m_item, sizeof(m_item));
+}
+
+void Set_signal_information::clear()
+{
+ memset(m_item, 0, sizeof(m_item));
+}
+
+void Signal_common::assign_defaults(MYSQL_ERROR *cond,
+ bool set_level_code,
+ MYSQL_ERROR::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 Signal_common::eval_defaults(THD *thd, MYSQL_ERROR *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_cond_type::state);
+ sqlstate= m_cond->sqlstate;
+ 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,
+ MYSQL_ERROR::WARN_LEVEL_WARN, ER_SIGNAL_WARN);
+ }
+ else if ((sqlstate[0] == '0') && (sqlstate[1] == '2'))
+ {
+ /* SQLSTATE class "02": not found. */
+ assign_defaults(cond, set_defaults,
+ MYSQL_ERROR::WARN_LEVEL_ERROR, ER_SIGNAL_NOT_FOUND);
+ }
+ else
+ {
+ /* other SQLSTATE classes : error. */
+ assign_defaults(cond, set_defaults,
+ MYSQL_ERROR::WARN_LEVEL_ERROR, ER_SIGNAL_EXCEPTION);
+ }
+}
+
+static bool assign_fixed_string(MEM_ROOT *mem_root,
+ CHARSET_INFO *dst_cs,
+ size_t max_char,
+ String *dst,
+ const String* src)
+{
+ bool truncated;
+ size_t numchars;
+ CHARSET_INFO *src_cs;
+ const char* src_str;
+ const char* src_end;
+ size_t src_len;
+ size_t to_copy;
+ char* dst_str;
+ size_t dst_len;
+ size_t dst_copied;
+ uint32 dummy_offset;
+
+ src_str= src->ptr();
+ if (src_str == NULL)
+ {
+ dst->set((const char*) NULL, 0, dst_cs);
+ return false;
+ }
+
+ src_cs= src->charset();
+ src_len= src->length();
+ src_end= src_str + src_len;
+ numchars= src_cs->cset->numchars(src_cs, src_str, src_end);
+
+ if (numchars <= max_char)
+ {
+ to_copy= src->length();
+ truncated= false;
+ }
+ else
+ {
+ numchars= max_char;
+ to_copy= dst_cs->cset->charpos(dst_cs, src_str, src_end, numchars);
+ truncated= true;
+ }
+
+ if (String::needs_conversion(to_copy, src_cs, dst_cs, & dummy_offset))
+ {
+ dst_len= numchars * dst_cs->mbmaxlen;
+ dst_str= (char*) alloc_root(mem_root, dst_len + 1);
+ if (dst_str)
+ {
+ const char* well_formed_error_pos;
+ const char* cannot_convert_error_pos;
+ const char* from_end_pos;
+
+ dst_copied= well_formed_copy_nchars(dst_cs, dst_str, dst_len,
+ src_cs, src_str, src_len,
+ numchars,
+ & well_formed_error_pos,
+ & cannot_convert_error_pos,
+ & from_end_pos);
+ DBUG_ASSERT(dst_copied <= dst_len);
+ dst_len= dst_copied; /* In case the copy truncated the data */
+ dst_str[dst_copied]= '\0';
+ }
+ }
+ else
+ {
+ dst_len= to_copy;
+ dst_str= (char*) alloc_root(mem_root, dst_len + 1);
+ if (dst_str)
+ {
+ memcpy(dst_str, src_str, to_copy);
+ dst_str[to_copy]= '\0';
+ }
+ }
+ dst->set(dst_str, dst_len, dst_cs);
+
+ return truncated;
+}
+
+static int assign_condition_item(MEM_ROOT *mem_root, const char* name, THD *thd,
+ Item *set, String *ci)
+{
+ char str_buff[(64+1)*4]; /* Room for a null terminated UTF8 String 64 */
+ String str_value(str_buff, sizeof(str_buff), & my_charset_utf8_bin);
+ String *str;
+ bool truncated;
+
+ DBUG_ENTER("assign_condition_item");
+
+ if (set->is_null())
+ {
+ thd->raise_error_printf(ER_WRONG_VALUE_FOR_VAR, name, "NULL");
+ DBUG_RETURN(1);
+ }
+
+ str= set->val_str(& str_value);
+ truncated= assign_fixed_string(mem_root, & my_charset_utf8_bin, 64, ci, str);
+ if (truncated)
+ {
+ if (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
+ MODE_STRICT_ALL_TABLES))
+ {
+ thd->raise_error_printf(ER_COND_ITEM_TOO_LONG, name);
+ DBUG_RETURN(1);
+ }
+
+ thd->raise_warning_printf(WARN_COND_ITEM_TRUNCATED, name);
+ }
+
+ DBUG_RETURN(0);
+}
+
+
+int Signal_common::eval_signal_informations(THD *thd, MYSQL_ERROR *cond)
+{
+ struct cond_item_map
+ {
+ enum enum_diag_condition_item_name m_item;
+ String MYSQL_ERROR::*m_member;
+ };
+
+ static cond_item_map map[]=
+ {
+ { DIAG_CLASS_ORIGIN, & MYSQL_ERROR::m_class_origin },
+ { DIAG_SUBCLASS_ORIGIN, & MYSQL_ERROR::m_subclass_origin },
+ { DIAG_CONSTRAINT_CATALOG, & MYSQL_ERROR::m_constraint_catalog },
+ { DIAG_CONSTRAINT_SCHEMA, & MYSQL_ERROR::m_constraint_schema },
+ { DIAG_CONSTRAINT_NAME, & MYSQL_ERROR::m_constraint_name },
+ { DIAG_CATALOG_NAME, & MYSQL_ERROR::m_catalog_name },
+ { DIAG_SCHEMA_NAME, & MYSQL_ERROR::m_schema_name },
+ { DIAG_TABLE_NAME, & MYSQL_ERROR::m_table_name },
+ { DIAG_COLUMN_NAME, & MYSQL_ERROR::m_column_name },
+ { DIAG_CURSOR_NAME, & MYSQL_ERROR::m_cursor_name }
+ };
+
+ Item *set;
+ String str_value;
+ String *str;
+ int i;
+ uint j;
+ int result= 1;
+ enum enum_diag_condition_item_name item_enum;
+ String *member;
+ const LEX_STRING *name;
+
+ DBUG_ENTER("Signal_common::eval_signal_informations");
+
+ for (i= FIRST_DIAG_SET_PROPERTY;
+ 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;
+ }
+ }
+ }
+
+ /*
+ Generically assign all the UTF8 String 64 condition items
+ described in the map.
+ */
+ for (j= 0; j < array_elements(map); j++)
+ {
+ item_enum= map[j].m_item;
+ set= m_set_signal_information.m_item[item_enum];
+ if (set != NULL)
+ {
+ member= & (cond->* map[j].m_member);
+ name= & Diag_condition_item_names[item_enum];
+ if (assign_condition_item(cond->m_mem_root, name->str, thd, set, member))
+ goto end;
+ }
+ }
+
+ /*
+ Assign the remaining attributes.
+ */
+
+ set= m_set_signal_information.m_item[DIAG_MESSAGE_TEXT];
+ if (set != NULL)
+ {
+ if (set->is_null())
+ {
+ thd->raise_error_printf(ER_WRONG_VALUE_FOR_VAR,
+ "MESSAGE_TEXT", "NULL");
+ goto end;
+ }
+ /*
+ Enforce that SET MESSAGE_TEXT = <value> evaluates the value
+ as VARCHAR(128) 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,
+ & utf8_text, str);
+ if (truncated)
+ {
+ if (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
+ MODE_STRICT_ALL_TABLES))
+ {
+ thd->raise_error_printf(ER_COND_ITEM_TOO_LONG,
+ "MESSAGE_TEXT");
+ goto end;
+ }
+
+ thd->raise_warning_printf(WARN_COND_ITEM_TRUNCATED,
+ "MESSAGE_TEXT");
+ }
+
+ /*
+ See the comments
+ "Design notes about MYSQL_ERROR::m_message_text."
+ in file sql_error.cc
+ */
+ String converted_text;
+ converted_text.set_charset(error_message_charset_info);
+ converted_text.append(utf8_text.ptr(), utf8_text.length(),
+ utf8_text.charset());
+ cond->set_builtin_message_text(converted_text.c_ptr_safe());
+ }
+
+ set= m_set_signal_information.m_item[DIAG_MYSQL_ERRNO];
+ if (set != NULL)
+ {
+ if (set->is_null())
+ {
+ thd->raise_error_printf(ER_WRONG_VALUE_FOR_VAR,
+ "MYSQL_ERRNO", "NULL");
+ goto end;
+ }
+ longlong code= set->val_int();
+ if ((code <= 0) || (code > MAX_MYSQL_ERRNO))
+ {
+ str= set->val_str(& str_value);
+ thd->raise_error_printf(ER_WRONG_VALUE_FOR_VAR,
+ "MYSQL_ERRNO", str->c_ptr_safe());
+ goto end;
+ }
+ cond->m_sql_errno= (int) code;
+ }
+
+ /*
+ The various item->val_xxx() methods don't return an error code,
+ but flag thd in case of failure.
+ */
+ if (! thd->is_error())
+ result= 0;
+
+end:
+ for (i= FIRST_DIAG_SET_PROPERTY;
+ i <= LAST_DIAG_SET_PROPERTY;
+ i++)
+ {
+ set= m_set_signal_information.m_item[i];
+ if (set)
+ {
+ if (set->fixed)
+ set->cleanup();
+ }
+ }
+
+ DBUG_RETURN(result);
+}
+
+bool Signal_common::raise_condition(THD *thd, MYSQL_ERROR *cond)
+{
+ bool result= TRUE;
+
+ DBUG_ENTER("Signal_common::raise_condition");
+
+ DBUG_ASSERT(m_lex->query_tables == NULL);
+
+ eval_defaults(thd, cond);
+ if (eval_signal_informations(thd, cond))
+ DBUG_RETURN(result);
+
+ /* SIGNAL should not signal WARN_LEVEL_NOTE */
+ DBUG_ASSERT((cond->m_level == MYSQL_ERROR::WARN_LEVEL_WARN) ||
+ (cond->m_level == MYSQL_ERROR::WARN_LEVEL_ERROR));
+
+ MYSQL_ERROR *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);
+
+ if (cond->m_level == MYSQL_ERROR::WARN_LEVEL_WARN)
+ {
+ my_ok(thd);
+ result= FALSE;
+ }
+
+ DBUG_RETURN(result);
+}
+
+bool Signal_statement::execute(THD *thd)
+{
+ bool result= TRUE;
+ MYSQL_ERROR cond(thd->mem_root);
+
+ DBUG_ENTER("Signal_statement::execute");
+
+ /*
+ WL#2110 SIGNAL specification says:
+
+ When SIGNAL is executed, it has five effects, in the following order:
+
+ (1) First, the diagnostics area is completely cleared. So if the
+ SIGNAL is in a DECLARE HANDLER then any pending errors or warnings
+ are gone. So is 'row count'.
+
+ This has roots in the SQL standard specification for SIGNAL.
+ */
+
+ thd->stmt_da->reset_diagnostics_area();
+ thd->set_row_count_func(0);
+ thd->warning_info->clear_warning_info(thd->query_id);
+
+ result= raise_condition(thd, &cond);
+
+ DBUG_RETURN(result);
+}
+
+
+bool Resignal_statement::execute(THD *thd)
+{
+ Sql_condition_info *signaled;
+ int result= TRUE;
+
+ DBUG_ENTER("Resignal_statement::execute");
+
+ thd->warning_info->m_warn_id= thd->query_id;
+
+ if (! thd->spcont || ! (signaled= thd->spcont->raised_condition()))
+ {
+ thd->raise_error(ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER);
+ DBUG_RETURN(result);
+ }
+
+ MYSQL_ERROR signaled_err(thd->mem_root);
+ signaled_err.set(signaled->m_sql_errno,
+ signaled->m_sql_state,
+ signaled->m_level,
+ signaled->m_message);
+
+ if (m_cond == NULL)
+ {
+ /* RESIGNAL without signal_value */
+ result= raise_condition(thd, &signaled_err);
+ DBUG_RETURN(result);
+ }
+
+ /* RESIGNAL with signal_value */
+ result= raise_condition(thd, &signaled_err);
+
+ DBUG_RETURN(result);
+}
+
diff --git a/sql/sql_signal.h b/sql/sql_signal.h
new file mode 100644
index 00000000000..058457a3639
--- /dev/null
+++ b/sql/sql_signal.h
@@ -0,0 +1,153 @@
+/* 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 */
+
+#ifndef SQL_SIGNAL_H
+#define SQL_SIGNAL_H
+
+/**
+ Signal_common represents the common properties of the SIGNAL and RESIGNAL
+ statements.
+*/
+class Signal_common : public Sql_statement
+{
+protected:
+ /**
+ Constructor.
+ @param lex the LEX structure for this statement.
+ @param cond the condition signaled if any, or NULL.
+ @param set collection of signal condition item assignments.
+ */
+ Signal_common(LEX *lex,
+ const sp_cond_type_t *cond,
+ const Set_signal_information& set)
+ : Sql_statement(lex),
+ m_cond(cond),
+ m_set_signal_information(set)
+ {}
+
+ virtual ~Signal_common()
+ {}
+
+ /**
+ 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(MYSQL_ERROR *cond,
+ bool set_level_code,
+ MYSQL_ERROR::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, MYSQL_ERROR *cond);
+
+ /**
+ Evaluate each signal condition items for this statement.
+ @param thd the current thread.
+ @param cond the condition to update.
+ @return 0 on success.
+ */
+ int eval_signal_informations(THD *thd, MYSQL_ERROR *cond);
+
+ /**
+ Raise a SQL condition.
+ @param thd the current thread.
+ @param cond the condition to raise.
+ @return false on success.
+ */
+ bool raise_condition(THD *thd, MYSQL_ERROR *cond);
+
+ /**
+ The condition to signal or resignal.
+ This member is optional and can be NULL (RESIGNAL).
+ */
+ const sp_cond_type_t *m_cond;
+
+ /**
+ Collection of 'SET item = value' assignments in the
+ SIGNAL/RESIGNAL statement.
+ */
+ Set_signal_information m_set_signal_information;
+};
+
+/**
+ Signal_statement represents a SIGNAL statement.
+*/
+class Signal_statement : public Signal_common
+{
+public:
+ /**
+ Constructor, used to represent a SIGNAL statement.
+ @param lex the LEX structure for this statement.
+ @param cond the SQL condition to signal (required).
+ @param set the collection of signal informations to signal.
+ */
+ Signal_statement(LEX *lex,
+ const sp_cond_type_t *cond,
+ const Set_signal_information& set)
+ : Signal_common(lex, cond, set)
+ {}
+
+ virtual ~Signal_statement()
+ {}
+
+ /**
+ Execute a SIGNAL statement at runtime.
+ @param thd the current thread.
+ @return false on success.
+ */
+ virtual bool execute(THD *thd);
+};
+
+/**
+ Resignal_statement represents a RESIGNAL statement.
+*/
+class Resignal_statement : public Signal_common
+{
+public:
+ /**
+ Constructor, used to represent a RESIGNAL statement.
+ @param lex the LEX structure for this statement.
+ @param cond the SQL condition to resignal (optional, may be NULL).
+ @param set the collection of signal informations to resignal.
+ */
+ Resignal_statement(LEX *lex,
+ const sp_cond_type_t *cond,
+ const Set_signal_information& set)
+ : Signal_common(lex, cond, set)
+ {}
+
+ virtual ~Resignal_statement()
+ {}
+
+ /**
+ Execute a RESIGNAL statement at runtime.
+ @param thd the current thread.
+ @return 0 on success.
+ */
+ virtual bool execute(THD *thd);
+};
+
+#endif
+
diff --git a/sql/sql_sort.h b/sql/sql_sort.h
index 8169cd0406e..f1a3a2f9d8b 100644
--- a/sql/sql_sort.h
+++ b/sql/sql_sort.h
@@ -1,4 +1,7 @@
-/* Copyright (c) 2000, 2001, 2003, 2007 MySQL AB
+#ifndef SQL_SORT_INCLUDED
+#define SQL_SORT_INCLUDED
+
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,7 +14,19 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "my_global.h" /* uchar */
+#include "my_base.h" /* ha_rows */
+#include "my_sys.h" /* qsort2_cmp */
+#include "queues.h"
+
+typedef struct st_buffpek BUFFPEK;
+typedef struct st_sort_field SORT_FIELD;
+
+class Field;
+struct TABLE;
+
/* Defines used by filesort and uniques */
@@ -57,7 +72,7 @@ typedef struct st_sort_param {
uint addon_length; /* Length of added packed fields */
uint res_length; /* Length of records in final sorted file/buffer */
uint keys; /* Max keys / buffer */
- element_count min_dupl_count;
+ uint min_dupl_count;
ha_rows max_rows,examined_rows;
TABLE *sort_form; /* For quicker make_sortkey */
SORT_FIELD *local_sortorder;
@@ -87,3 +102,4 @@ int merge_index(SORTPARAM *param, uchar *sort_buffer,
void reuse_freed_buff(QUEUE *queue, BUFFPEK *reuse, uint key_length);
+#endif /* SQL_SORT_INCLUDED */
diff --git a/sql/sql_state.c b/sql/sql_state.c
index 728e1a4b4a8..2bfd61d6696 100644
--- a/sql/sql_state.c
+++ b/sql/sql_state.c
@@ -1,4 +1,5 @@
/* Copyright (C) 2000-2003 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
@@ -17,6 +18,7 @@
#include <my_global.h>
#include <mysqld_error.h>
+#include <my_base.h>
struct st_map_errno_to_sqlstate
{
@@ -27,6 +29,7 @@ struct st_map_errno_to_sqlstate
struct st_map_errno_to_sqlstate sqlstate_map[]=
{
+#include <handler_state.h>
#include <sql_state.h>
};
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index 23aa2d02d82..885f53ae36a 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -1,5 +1,4 @@
-/*
- Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +11,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* This file is originally from the mysql distribution. Coded by monty */
@@ -25,21 +23,20 @@
#include <my_sys.h>
#include <m_string.h>
#include <m_ctype.h>
-#ifdef HAVE_FCONVERT
-#include <floatingpoint.h>
-#endif
+#include <mysql_com.h>
+
#include "sql_string.h"
-#ifdef MYSQL_CLIENT
-#error Attempt to use server-side sql_string on client. Use client/sql_string.cc
-#endif
/*****************************************************************************
** String functions
*****************************************************************************/
-bool String::real_alloc(uint32 arg_length)
+bool String::real_alloc(uint32 length)
{
- arg_length=ALIGN_SIZE(arg_length+1);
+ uint32 arg_length= ALIGN_SIZE(length + 1);
+ DBUG_ASSERT(arg_length > length);
+ if (arg_length <= length)
+ return TRUE; /* Overflow */
str_length=0;
if (Alloced_length < arg_length)
{
@@ -81,12 +78,15 @@ bool String::real_alloc(uint32 arg_length)
@retval true An error occured when attempting to allocate memory.
*/
-bool String::realloc(uint32 alloc_length)
+bool String::realloc_raw(uint32 alloc_length)
{
if (Alloced_length <= alloc_length)
{
char *new_ptr;
uint32 len= ALIGN_SIZE(alloc_length+1);
+ DBUG_ASSERT(len > alloc_length);
+ if (len <= alloc_length)
+ return TRUE; /* Overflow */
if (alloced)
{
if (!(new_ptr= (char*) my_realloc(Ptr,len,MYF(MY_WME))))
@@ -106,7 +106,6 @@ bool String::realloc(uint32 alloc_length)
Ptr= new_ptr;
Alloced_length= len;
}
- Ptr[alloc_length]=0; // This make other funcs shorter
return FALSE;
}
@@ -126,83 +125,17 @@ bool String::set_real(double num,uint decimals, CHARSET_INFO *cs)
{
char buff[FLOATING_POINT_BUFFER];
uint dummy_errors;
+ size_t len;
str_charset=cs;
if (decimals >= NOT_FIXED_DEC)
{
- uint32 len= my_sprintf(buff,(buff, "%.15g",num));// Enough for a DATETIME
+ len= my_gcvt(num, MY_GCVT_ARG_DOUBLE, sizeof(buff) - 1, buff, NULL);
return copy(buff, len, &my_charset_latin1, cs, &dummy_errors);
}
-#ifdef HAVE_FCONVERT
- int decpt,sign;
- char *pos,*to;
-
- VOID(fconvert(num,(int) decimals,&decpt,&sign,buff+1));
- if (!my_isdigit(&my_charset_latin1, buff[1]))
- { // Nan or Inf
- pos=buff+1;
- if (sign)
- {
- buff[0]='-';
- pos=buff;
- }
- uint dummy_errors;
- return copy(pos,(uint32) strlen(pos), &my_charset_latin1, cs, &dummy_errors);
- }
- if (alloc((uint32) ((uint32) decpt+3+decimals)))
- return TRUE;
- to=Ptr;
- if (sign)
- *to++='-';
-
- pos=buff+1;
- if (decpt < 0)
- { /* value is < 0 */
- *to++='0';
- if (!decimals)
- goto end;
- *to++='.';
- if ((uint32) -decpt > decimals)
- decpt= - (int) decimals;
- decimals=(uint32) ((int) decimals+decpt);
- while (decpt++ < 0)
- *to++='0';
- }
- else if (decpt == 0)
- {
- *to++= '0';
- if (!decimals)
- goto end;
- *to++='.';
- }
- else
- {
- while (decpt-- > 0)
- *to++= *pos++;
- if (!decimals)
- goto end;
- *to++='.';
- }
- while (decimals--)
- *to++= *pos++;
-
-end:
- *to=0;
- str_length=(uint32) (to-Ptr);
- return FALSE;
-#else
-#ifdef HAVE_SNPRINTF
- buff[sizeof(buff)-1]=0; // Safety
- IF_DBUG(int num_chars= )
- snprintf(buff, sizeof(buff)-1, "%.*f",(int) decimals, num);
- DBUG_ASSERT(num_chars > 0);
- DBUG_ASSERT(num_chars < (int) sizeof(buff));
-#else
- sprintf(buff,"%.*f",(int) decimals,num);
-#endif
- return copy(buff,(uint32) strlen(buff), &my_charset_latin1, cs,
+ len= my_fcvt(num, decimals, buff, NULL);
+ return copy(buff, (uint32) len, &my_charset_latin1, cs,
&dummy_errors);
-#endif
}
@@ -290,6 +223,42 @@ bool String::needs_conversion(uint32 arg_length,
/*
+ Checks that the source string can just be copied to the destination string
+ without conversion.
+ Unlike needs_conversion it will require conversion on incoming binary data
+ to ensure the data are verified for vailidity first.
+
+ @param arg_length Length of string to copy.
+ @param from_cs Character set to copy from
+ @param to_cs Character set to copy to
+
+ @return conversion needed
+*/
+bool String::needs_conversion_on_storage(uint32 arg_length,
+ CHARSET_INFO *cs_from,
+ CHARSET_INFO *cs_to)
+{
+ uint32 offset;
+ return (needs_conversion(arg_length, cs_from, cs_to, &offset) ||
+ /* force conversion when storing a binary string */
+ (cs_from == &my_charset_bin &&
+ /* into a non-binary destination */
+ cs_to != &my_charset_bin &&
+ /* and any of the following is true :*/
+ (
+ /* it's a variable length encoding */
+ cs_to->mbminlen != cs_to->mbmaxlen ||
+ /* longer than 2 bytes : neither 1 byte nor ucs2 */
+ cs_to->mbminlen > 2 ||
+ /* and is not a multiple of the char byte size */
+ 0 != (arg_length % cs_to->mbmaxlen)
+ )
+ )
+ );
+}
+
+
+/*
Copy a multi-byte character sets with adding leading zeros.
SYNOPSIS
@@ -318,8 +287,8 @@ bool String::copy_aligned(const char *str,uint32 arg_length, uint32 offset,
CHARSET_INFO *cs)
{
/* How many bytes are in incomplete character */
- offset= cs->mbmaxlen - offset; /* How many zeros we should prepend */
- DBUG_ASSERT(offset && offset != cs->mbmaxlen);
+ offset= cs->mbminlen - offset; /* How many zeros we should prepend */
+ DBUG_ASSERT(offset && offset != cs->mbminlen);
uint32 aligned_length= arg_length + offset;
if (alloc(aligned_length))
@@ -503,6 +472,16 @@ bool String::append(const char *s)
}
+
+bool String::append_ulonglong(ulonglong val)
+{
+ if (realloc(str_length+MAX_BIGINT_WIDTH+2))
+ return TRUE;
+ char *end= (char*) longlong10_to_str(val, (char*) Ptr + str_length, 10);
+ str_length= end - Ptr;
+ return FALSE;
+}
+
/*
Append a string in the given charset to the string
with character set recoding
@@ -510,11 +489,25 @@ bool String::append(const char *s)
bool String::append(const char *s,uint32 arg_length, CHARSET_INFO *cs)
{
- uint32 dummy_offset;
+ uint32 offset;
- if (needs_conversion(arg_length, cs, str_charset, &dummy_offset))
+ if (needs_conversion(arg_length, cs, str_charset, &offset))
{
- uint32 add_length= arg_length / cs->mbminlen * str_charset->mbmaxlen;
+ uint32 add_length;
+ if ((cs == &my_charset_bin) && offset)
+ {
+ DBUG_ASSERT(str_charset->mbminlen > offset);
+ offset= str_charset->mbminlen - offset; // How many characters to pad
+ add_length= arg_length + offset;
+ if (realloc(str_length + add_length))
+ return TRUE;
+ bzero((char*) Ptr + str_length, offset);
+ memcpy(Ptr + str_length + offset, s, arg_length);
+ str_length+= add_length;
+ return FALSE;
+ }
+
+ add_length= arg_length / cs->mbminlen * str_charset->mbmaxlen;
uint dummy_errors;
if (realloc_with_extra_if_needed(str_length + add_length))
return TRUE;
@@ -531,22 +524,6 @@ bool String::append(const char *s,uint32 arg_length, CHARSET_INFO *cs)
return FALSE;
}
-
-#ifdef TO_BE_REMOVED
-bool String::append(FILE* file, uint32 arg_length, myf my_flags)
-{
- if (realloc_with_extra_if_needed(str_length+arg_length))
- return TRUE;
- if (my_fread(file, (uchar*) Ptr + str_length, arg_length, my_flags))
- {
- shrink(str_length);
- return TRUE;
- }
- str_length+=arg_length;
- return FALSE;
-}
-#endif
-
bool String::append(IO_CACHE* file, uint32 arg_length)
{
if (realloc_with_extra_if_needed(str_length+arg_length))
@@ -708,7 +685,8 @@ void String::qs_append(const char *str, uint32 len)
void String::qs_append(double d)
{
char *buff = Ptr + str_length;
- str_length+= my_sprintf(buff, (buff, "%.15g", d));
+ str_length+= my_gcvt(d, MY_GCVT_ARG_DOUBLE, FLOATING_POINT_BUFFER - 1, buff,
+ NULL);
}
void String::qs_append(double *d)
@@ -882,6 +860,65 @@ outp:
}
+/*
+ Optimized for quick copying of ASCII characters in the range 0x00..0x7F.
+*/
+uint32
+copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs,
+ const char *from, uint32 from_length, CHARSET_INFO *from_cs,
+ uint *errors)
+{
+ /*
+ If any of the character sets is not ASCII compatible,
+ immediately switch to slow mb_wc->wc_mb method.
+ */
+ if ((to_cs->state | from_cs->state) & MY_CS_NONASCII)
+ return copy_and_convert_extended(to, to_length, to_cs,
+ from, from_length, from_cs, errors);
+
+ uint32 length= min(to_length, from_length), length2= length;
+
+#if defined(__i386__) || defined(__x86_64__)
+ /*
+ Special loop for i386, it allows to refer to a
+ non-aligned memory block as UINT32, which makes
+ it possible to copy four bytes at once. This
+ gives about 10% performance improvement comparing
+ to byte-by-byte loop.
+ */
+ for ( ; length >= 4; length-= 4, from+= 4, to+= 4)
+ {
+ if ((*(uint32*)from) & 0x80808080)
+ break;
+ *((uint32*) to)= *((const uint32*) from);
+ }
+#endif
+
+ for (; ; *to++= *from++, length--)
+ {
+ if (!length)
+ {
+ *errors= 0;
+ return length2;
+ }
+ if (*((unsigned char*) from) > 0x7F) /* A non-ASCII character */
+ {
+ uint32 copied_length= length2 - length;
+ to_length-= copied_length;
+ from_length-= copied_length;
+ return copied_length + copy_and_convert_extended(to, to_length,
+ to_cs,
+ from, from_length,
+ from_cs,
+ errors);
+ }
+ }
+
+ DBUG_ASSERT(FALSE); // Should never get to here
+ return 0; // Make compiler happy
+}
+
+
/**
Copy string with HEX-encoding of "bad" characters.
@@ -944,64 +981,6 @@ my_copy_with_hex_escaping(CHARSET_INFO *cs,
return dst - dst0;
}
-/*
- Optimized for quick copying of ASCII characters in the range 0x00..0x7F.
-*/
-uint32
-copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs,
- const char *from, uint32 from_length, CHARSET_INFO *from_cs,
- uint *errors)
-{
- /*
- If any of the character sets is not ASCII compatible,
- immediately switch to slow mb_wc->wc_mb method.
- */
- if ((to_cs->state | from_cs->state) & MY_CS_NONASCII)
- return copy_and_convert_extended(to, to_length, to_cs,
- from, from_length, from_cs, errors);
-
- uint32 length= min(to_length, from_length), length2= length;
-
-#if defined(__i386__)
- /*
- Special loop for i386, it allows to refer to a
- non-aligned memory block as UINT32, which makes
- it possible to copy four bytes at once. This
- gives about 10% performance improvement comparing
- to byte-by-byte loop.
- */
- for ( ; length >= 4; length-= 4, from+= 4, to+= 4)
- {
- if ((*(uint32*)from) & 0x80808080)
- break;
- *((uint32*) to)= *((const uint32*) from);
- }
-#endif
-
- for (; ; *to++= *from++, length--)
- {
- if (!length)
- {
- *errors= 0;
- return length2;
- }
- if (*((unsigned char*) from) > 0x7F) /* A non-ASCII character */
- {
- uint32 copied_length= length2 - length;
- to_length-= copied_length;
- from_length-= copied_length;
- return copied_length + copy_and_convert_extended(to, to_length,
- to_cs,
- from, from_length,
- from_cs,
- errors);
- }
- }
-
- DBUG_ASSERT(FALSE); // Should never get to here
- return 0; // Make compiler happy
-}
-
/*
copy a string,
@@ -1079,6 +1058,24 @@ well_formed_copy_nchars(CHARSET_INFO *to_cs,
uint pad_length= to_cs->mbminlen - from_offset;
bzero(to, pad_length);
memmove(to + pad_length, from, from_offset);
+ /*
+ In some cases left zero-padding can create an incorrect character.
+ For example:
+ INSERT INTO t1 (utf32_column) VALUES (0x110000);
+ We'll pad the value to 0x00110000, which is a wrong UTF32 sequence!
+ The valid characters range is limited to 0x00000000..0x0010FFFF.
+
+ Make sure we didn't pad to an incorrect character.
+ */
+ if (to_cs->cset->well_formed_len(to_cs,
+ to, to + to_cs->mbminlen, 1,
+ &well_formed_error) !=
+ to_cs->mbminlen)
+ {
+ *from_end_pos= *well_formed_error_pos= from;
+ *cannot_convert_error_pos= NULL;
+ return 0;
+ }
nchars--;
from+= from_offset;
from_length-= from_offset;
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 1f24d7b8e3d..1fce3ae6c6f 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -1,6 +1,9 @@
+#ifndef SQL_STRING_INCLUDED
+#define SQL_STRING_INCLUDED
+
/*
Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2008, 2014, Monty Program Ab.
+ Copyright (c) 2008, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,27 +16,22 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* This file is originally from the mysql distribution. Coded by monty */
-#ifndef MYSQL_SQL_STRING_H_INCLUDED
-#define MYSQL_SQL_STRING_H_INCLUDED
-
#ifdef USE_PRAGMA_INTERFACE
#pragma interface /* gcc class implementation */
#endif
-#ifndef NOT_FIXED_DEC
-#define NOT_FIXED_DEC 31
-#endif
-
-#ifdef MYSQL_CLIENT
-#error Attempt to use server-side sql_string on client. Use client/sql_string.h
-#endif
+#include "m_ctype.h" /* my_charset_bin */
+#include "my_sys.h" /* alloc_root, my_free, my_realloc */
+#include "m_string.h" /* TRASH */
class String;
+typedef struct st_io_cache IO_CACHE;
+typedef struct st_mem_root MEM_ROOT;
+
int sortcmp(const String *a,const String *b, CHARSET_INFO *cs);
String *copy_if_not_alloced(String *a,String *b,uint32 arg_length);
uint32 copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs,
@@ -145,6 +143,11 @@ public:
(void) realloc(str_length);
return Ptr;
}
+ LEX_STRING lex_string() const
+ {
+ LEX_STRING lex_string = { (char*) ptr(), length() };
+ return lex_string;
+ }
void set(String &str,uint32 offset,uint32 arg_length)
{
@@ -229,7 +232,9 @@ public:
*/
inline void chop()
{
- Ptr[str_length--]= '\0';
+ str_length--;
+ Ptr[str_length]= '\0';
+ DBUG_ASSERT(strlen(Ptr) == str_length);
}
inline void free()
@@ -237,7 +242,7 @@ public:
if (alloced)
{
alloced=0;
- my_free(Ptr,MYF(0));
+ my_free(Ptr);
}
Alloced_length= extra_alloc= 0;
Ptr=0;
@@ -250,20 +255,32 @@ public:
return real_alloc(arg_length);
}
bool real_alloc(uint32 arg_length); // Empties old string
- bool realloc(uint32 arg_length);
+ bool realloc_raw(uint32 arg_length);
+ bool realloc(uint32 arg_length)
+ {
+ if (realloc_raw(arg_length))
+ return TRUE;
+ Ptr[arg_length]=0; // This make other funcs shorter
+ return FALSE;
+ }
bool realloc_with_extra(uint32 arg_length)
{
if (extra_alloc < 4096)
extra_alloc= extra_alloc*2+128;
- return realloc(arg_length + extra_alloc);
+ if (realloc_raw(arg_length + extra_alloc))
+ return TRUE;
+ Ptr[arg_length]=0; // This make other funcs shorter
+ return FALSE;
}
bool realloc_with_extra_if_needed(uint32 arg_length)
{
if (arg_length < Alloced_length)
+ {
+ Ptr[arg_length]=0; // behave as if realloc was called.
return 0;
+ }
return realloc_with_extra(arg_length);
}
-
// Shrink the buffer, but only if it is allocated on the heap.
inline void shrink(uint32 arg_length)
{
@@ -307,6 +324,9 @@ public:
static bool needs_conversion(uint32 arg_length,
CHARSET_INFO *cs_from, CHARSET_INFO *cs_to,
uint32 *offset);
+ static bool needs_conversion_on_storage(uint32 arg_length,
+ CHARSET_INFO *cs_from,
+ CHARSET_INFO *cs_to);
bool copy_aligned(const char *s, uint32 arg_length, uint32 offset,
CHARSET_INFO *cs);
bool set_or_copy_aligned(const char *s, uint32 arg_length, CHARSET_INFO *cs);
@@ -322,8 +342,13 @@ public:
}
bool append(const String &s);
bool append(const char *s);
- bool append(const char *s,uint32 arg_length);
- bool append(const char *s,uint32 arg_length, CHARSET_INFO *cs);
+ bool append(LEX_STRING *ls)
+ {
+ return append(ls->str, ls->length);
+ }
+ bool append(const char *s, uint32 arg_length);
+ bool append(const char *s, uint32 arg_length, CHARSET_INFO *cs);
+ bool append_ulonglong(ulonglong val);
bool append(IO_CACHE* file, uint32 arg_length);
bool append_with_prefill(const char *s, uint32 arg_length,
uint32 full_length, char fill_char);
@@ -345,6 +370,16 @@ public:
}
return 0;
}
+ bool append_hex(const char *src, uint32 srclen)
+ {
+ for (const char *end= src + srclen ; src != end ; src++)
+ {
+ if (append(_dig_vec_lower[((uchar) *src) >> 4]) ||
+ append(_dig_vec_lower[((uchar) *src) & 0x0F]))
+ return true;
+ }
+ return false;
+ }
bool fill(uint32 max_length,char fill);
void strip_sp();
friend int sortcmp(const String *a,const String *b, CHARSET_INFO *cs);
@@ -474,4 +509,4 @@ static inline bool check_if_only_end_space(CHARSET_INFO *cs,
return str+ cs->cset->scan(cs, str, end, MY_SEQ_SPACES) == end;
}
-#endif // MYSQL_SQL_STRING_H_INCLUDED
+#endif /* SQL_STRING_INCLUDED */
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 546bff574d1..9a0a64151d5 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -1,5 +1,6 @@
/*
- Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,23 +18,48 @@
/* drop and alter of tables */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "debug_sync.h"
+#include "sql_table.h"
+#include "sql_rename.h" // do_rename
+#include "sql_parse.h" // test_if_data_home_dir
+#include "sql_cache.h" // query_cache_*
+#include "sql_base.h" // open_table_uncached, lock_table_names
+#include "lock.h" // mysql_unlock_tables
+#include "strfunc.h" // find_type2, find_set
+#include "sql_view.h" // view_checksum
+#include "sql_truncate.h" // regenerate_locked_table
+#include "sql_partition.h" // mem_alloc_error,
+ // generate_partition_syntax,
+ // partition_info
+#include "sql_db.h" // load_db_opt_by_name
+#include "sql_time.h" // make_truncated_value_warning
+#include "records.h" // init_read_record, end_read_record
+#include "filesort.h" // filesort_free_buffers
+#include "sql_select.h" // setup_order,
+ // make_unireg_sortorder
+#include "sql_handler.h" // mysql_ha_rm_tables
+#include "discover.h" // readfrm
+#include "my_pthread.h" // pthread_mutex_t
+#include "log_event.h" // Query_log_event
#include <hash.h>
#include <myisam.h>
#include <my_dir.h>
#include "create_options.h"
#include "sp_head.h"
+#include "sp.h"
#include "sql_trigger.h"
+#include "sql_parse.h"
#include "sql_show.h"
-#include "debug_sync.h"
-#include "sql_handler.h"
+#include "transaction.h"
+#include "datadict.h" // dd_frm_type()
+#include "sql_audit.h"
#ifdef __WIN__
#include <io.h>
#endif
-int creating_table= 0; // How many mysql_create_table are running
-
const char *primary_key_name="PRIMARY";
static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end);
@@ -44,27 +70,11 @@ static int copy_data_between_tables(THD *thd, TABLE *,TABLE *,
enum enum_enable_or_disable, bool);
static bool prepare_blob_field(THD *thd, Create_field *sql_field);
-static bool check_engine(THD *, const char *, HA_CREATE_INFO *);
+static bool check_engine(THD *, const char *, const char *, HA_CREATE_INFO *);
static int mysql_prepare_create_table(THD *, HA_CREATE_INFO *, Alter_info *,
bool, uint *, handler *, KEY **, uint *,
int);
-static bool mysql_prepare_alter_table(THD *, TABLE *, HA_CREATE_INFO *,
- Alter_info *);
-static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list);
-
-#ifndef DBUG_OFF
-
-/* Wait until we get a 'mysql_kill' signal */
-
-static void wait_for_kill_signal(THD *thd)
-{
- while (thd->killed == 0)
- sleep(1);
- // Reset signal and continue as if nothing happend
- thd->killed= NOT_KILLED;
-}
-#endif
-
+static uint blob_length_by_type(enum_field_types type);
/**
@brief Helper function for explain_filename
@@ -155,6 +165,13 @@ static char* add_identifier(THD* thd, char *to_p, const char * end_p,
diagnostic, error etc. when it would be useful to know what a particular
file [and directory] means. Such as SHOW ENGINE STATUS, error messages etc.
+ Examples:
+
+ t1#P#p1 table t1 partition p1
+ t1#P#p1#SP#sp1 table t1 partition p1 subpartition sp1
+ t1#P#p1#SP#sp1#TMP# table t1 partition p1 subpartition sp1 temporary
+ t1#P#p1#SP#sp1#REN# table t1 partition p1 subpartition sp1 renamed
+
@param thd Thread handle
@param from Path name in my_charset_filename
Null terminated in my_charset_filename, normalized
@@ -182,7 +199,6 @@ uint explain_filename(THD* thd,
uint to_length,
enum_explain_filename_mode explain_mode)
{
- uint res= 0;
char *to_p= to;
char *end_p= to_p + to_length;
const char *db_name= NULL;
@@ -193,7 +209,8 @@ uint explain_filename(THD* thd,
int part_name_len= 0;
const char *subpart_name= NULL;
int subpart_name_len= 0;
- enum enum_file_name_type {NORMAL, TEMP, RENAMED} name_type= NORMAL;
+ uint part_type= NORMAL_PART_NAME;
+
const char *tmp_p;
DBUG_ENTER("explain_filename");
DBUG_PRINT("enter", ("from '%s'", from));
@@ -212,17 +229,18 @@ uint explain_filename(THD* thd,
table_name= tmp_p;
}
tmp_p= table_name;
- while (!res && (tmp_p= strchr(tmp_p, '#')))
+ /* Look if there are partition tokens in the table name. */
+ while ((tmp_p= strchr(tmp_p, '#')))
{
tmp_p++;
switch (tmp_p[0]) {
case 'P':
case 'p':
if (tmp_p[1] == '#')
+ {
part_name= tmp_p + 2;
- else
- res= 1;
- tmp_p+= 2;
+ tmp_p+= 2;
+ }
break;
case 'S':
case 's':
@@ -230,44 +248,34 @@ uint explain_filename(THD* thd,
{
part_name_len= tmp_p - part_name - 1;
subpart_name= tmp_p + 3;
+ tmp_p+= 3;
}
- else
- res= 2;
- tmp_p+= 3;
break;
case 'T':
case 't':
if ((tmp_p[1] == 'M' || tmp_p[1] == 'm') &&
(tmp_p[2] == 'P' || tmp_p[2] == 'p') &&
tmp_p[3] == '#' && !tmp_p[4])
- name_type= TEMP;
- else
- res= 3;
- tmp_p+= 4;
+ {
+ part_type= TEMP_PART_NAME;
+ tmp_p+= 4;
+ }
break;
case 'R':
case 'r':
if ((tmp_p[1] == 'E' || tmp_p[1] == 'e') &&
(tmp_p[2] == 'N' || tmp_p[2] == 'n') &&
tmp_p[3] == '#' && !tmp_p[4])
- name_type= RENAMED;
- else
- res= 4;
- tmp_p+= 4;
+ {
+ part_type= RENAMED_PART_NAME;
+ tmp_p+= 4;
+ }
break;
default:
- res= 5;
+ /* Not partition name part. */
+ ;
}
}
- if (res)
- {
- /* Better to give something back if we fail parsing, than nothing at all */
- DBUG_PRINT("info", ("Error in explain_filename: %u", res));
- sql_print_warning("Invalid (old?) table or database name '%s'", from);
- DBUG_RETURN(my_snprintf(to, to_length,
- "<result %u when explaining filename '%s'>",
- res, from));
- }
if (part_name)
{
table_name_len= part_name - table_name - 3;
@@ -275,7 +283,7 @@ uint explain_filename(THD* thd,
subpart_name_len= strlen(subpart_name);
else
part_name_len= strlen(part_name);
- if (name_type != NORMAL)
+ if (part_type != NORMAL_PART_NAME)
{
if (subpart_name)
subpart_name_len-= 5;
@@ -289,7 +297,8 @@ uint explain_filename(THD* thd,
{
if (explain_mode == EXPLAIN_ALL_VERBOSE)
{
- to_p= strnmov(to_p, ER(ER_DATABASE_NAME), end_p - to_p);
+ to_p= strnmov(to_p, ER_THD_OR_DEFAULT(thd, ER_DATABASE_NAME),
+ end_p - to_p);
*(to_p++)= ' ';
to_p= add_identifier(thd, to_p, end_p, db_name, db_name_len);
to_p= strnmov(to_p, ", ", end_p - to_p);
@@ -302,7 +311,7 @@ uint explain_filename(THD* thd,
}
if (explain_mode == EXPLAIN_ALL_VERBOSE)
{
- to_p= strnmov(to_p, ER(ER_TABLE_NAME), end_p - to_p);
+ to_p= strnmov(to_p, ER_THD_OR_DEFAULT(thd, ER_TABLE_NAME), end_p - to_p);
*(to_p++)= ' ';
to_p= add_identifier(thd, to_p, end_p, table_name, table_name_len);
}
@@ -316,21 +325,25 @@ uint explain_filename(THD* thd,
to_p= strnmov(to_p, " ", end_p - to_p);
else
to_p= strnmov(to_p, ", ", end_p - to_p);
- if (name_type != NORMAL)
+ if (part_type != NORMAL_PART_NAME)
{
- if (name_type == TEMP)
- to_p= strnmov(to_p, ER(ER_TEMPORARY_NAME), end_p - to_p);
+ if (part_type == TEMP_PART_NAME)
+ to_p= strnmov(to_p, ER_THD_OR_DEFAULT(thd, ER_TEMPORARY_NAME),
+ end_p - to_p);
else
- to_p= strnmov(to_p, ER(ER_RENAMED_NAME), end_p - to_p);
+ to_p= strnmov(to_p, ER_THD_OR_DEFAULT(thd, ER_RENAMED_NAME),
+ end_p - to_p);
to_p= strnmov(to_p, " ", end_p - to_p);
}
- to_p= strnmov(to_p, ER(ER_PARTITION_NAME), end_p - to_p);
+ to_p= strnmov(to_p, ER_THD_OR_DEFAULT(thd, ER_PARTITION_NAME),
+ end_p - to_p);
*(to_p++)= ' ';
to_p= add_identifier(thd, to_p, end_p, part_name, part_name_len);
if (subpart_name)
{
to_p= strnmov(to_p, ", ", end_p - to_p);
- to_p= strnmov(to_p, ER(ER_SUBPARTITION_NAME), end_p - to_p);
+ to_p= strnmov(to_p, ER_THD_OR_DEFAULT(thd, ER_SUBPARTITION_NAME),
+ end_p - to_p);
*(to_p++)= ' ';
to_p= add_identifier(thd, to_p, end_p, subpart_name, subpart_name_len);
}
@@ -355,14 +368,18 @@ 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, uint to_length
+#ifndef DBUG_OFF
+ , bool stay_quiet
+#endif /* DBUG_OFF */
+ )
{
uint errors;
size_t res;
DBUG_ENTER("filename_to_tablename");
DBUG_PRINT("enter", ("from '%s'", from));
- if (!memcmp(from, tmp_file_prefix, tmp_file_prefix_length))
+ if (!strncmp(from, tmp_file_prefix, tmp_file_prefix_length))
{
/* Temporary table name. */
res= (strnmov(to, from, to_length) - to);
@@ -375,7 +392,13 @@ uint filename_to_tablename(const char *from, char *to, uint to_length)
{
res= (strxnmov(to, to_length, MYSQL50_TABLE_NAME_PREFIX, from, NullS) -
to);
- sql_print_error("Invalid (old?) table or database name '%s'", from);
+#ifndef DBUG_OFF
+ if (!stay_quiet) {
+#endif /* DBUG_OFF */
+ sql_print_error("Invalid (old?) table or database name '%s'", from);
+#ifndef DBUG_OFF
+ }
+#endif /* DBUG_OFF */
/*
TODO: add a stored procedure for fix table and database names,
and mention its name in error log.
@@ -522,9 +545,9 @@ uint build_table_filename(char *buff, size_t bufflen, const char *db,
if (flags & FN_IS_TMP) // FN_FROM_IS_TMP | FN_TO_IS_TMP
strnmov(tbbuff, table_name, sizeof(tbbuff));
else
- VOID(tablename_to_filename(table_name, tbbuff, sizeof(tbbuff)));
+ (void) tablename_to_filename(table_name, tbbuff, sizeof(tbbuff));
- VOID(tablename_to_filename(db, dbbuff, sizeof(dbbuff)));
+ (void) tablename_to_filename(db, dbbuff, sizeof(dbbuff));
char *end = buff + bufflen;
/* Don't add FN_ROOTDIR if mysql_data_home already includes it */
@@ -628,7 +651,7 @@ struct st_global_ddl_log
st_global_ddl_log global_ddl_log;
-pthread_mutex_t LOCK_gdl;
+mysql_mutex_t LOCK_gdl;
#define DDL_LOG_ENTRY_TYPE_POS 0
#define DDL_LOG_ACTION_TYPE_POS 1
@@ -662,8 +685,8 @@ static bool read_ddl_log_file_entry(uchar *file_entry_buf,
DBUG_ENTER("read_ddl_log_file_entry");
DBUG_ASSERT(io_size >= size);
- if (my_pread(file_id, file_entry_buf, size, io_size * entry_no,
- MYF(MY_WME)) != size)
+ if (mysql_file_pread(file_id, file_entry_buf, size, io_size * entry_no,
+ MYF(MY_WME)) != size)
error= TRUE;
DBUG_RETURN(error);
}
@@ -691,8 +714,8 @@ static bool write_ddl_log_file_entry(uchar *file_entry_buf,
DBUG_ENTER("write_ddl_log_file_entry");
DBUG_ASSERT(io_size >= size);
- if (my_pwrite(file_id, file_entry_buf, size,
- io_size * entry_no, MYF(MY_WME)) != size)
+ if (mysql_file_pwrite(file_id, file_entry_buf, size,
+ io_size * entry_no, MYF(MY_WME)) != size)
error= TRUE;
DBUG_RETURN(error);
}
@@ -729,7 +752,7 @@ static bool write_ddl_log_header()
sql_print_error("Error writing ddl log header");
DBUG_RETURN(TRUE);
}
- VOID(sync_ddl_log());
+ (void) sync_ddl_log();
DBUG_RETURN(error);
}
@@ -772,8 +795,9 @@ static uint read_ddl_log_header()
DBUG_ASSERT(global_ddl_log.io_size <= IO_SIZE);
create_ddl_log_file_name(file_name);
- if ((global_ddl_log.file_id= my_open(file_name,
- O_RDWR | O_BINARY, MYF(0))) >= 0)
+ if ((global_ddl_log.file_id= mysql_file_open(key_file_global_ddl_log,
+ file_name,
+ O_RDWR | O_BINARY, MYF(0))) >= 0)
{
if (read_ddl_log_file_entry((uchar *) file_entry_buf, 0UL,
DDL_LOG_HEADER_SIZE))
@@ -797,7 +821,7 @@ static uint read_ddl_log_header()
global_ddl_log.first_free= NULL;
global_ddl_log.first_used= NULL;
global_ddl_log.num_entries= 0;
- VOID(pthread_mutex_init(&LOCK_gdl, MY_MUTEX_INIT_FAST));
+ mysql_mutex_init(key_LOCK_gdl, &LOCK_gdl, MY_MUTEX_INIT_FAST);
global_ddl_log.do_release= true;
DBUG_RETURN(entry_no);
}
@@ -860,10 +884,10 @@ static bool init_ddl_log()
global_ddl_log.io_size= IO_SIZE;
global_ddl_log.name_len= FN_LEN;
create_ddl_log_file_name(file_name);
- if ((global_ddl_log.file_id= my_create(file_name,
- CREATE_MODE,
- O_RDWR | O_TRUNC | O_BINARY,
- MYF(MY_WME))) < 0)
+ if ((global_ddl_log.file_id= mysql_file_create(key_file_global_ddl_log,
+ file_name, CREATE_MODE,
+ O_RDWR | O_TRUNC | O_BINARY,
+ MYF(MY_WME))) < 0)
{
/* Couldn't create ddl log file, this is serious error */
sql_print_error("Failed to open ddl log file");
@@ -872,7 +896,7 @@ static bool init_ddl_log()
global_ddl_log.inited= TRUE;
if (write_ddl_log_header())
{
- VOID(my_close(global_ddl_log.file_id, MYF(MY_WME)));
+ (void) mysql_file_close(global_ddl_log.file_id, MYF(MY_WME));
global_ddl_log.inited= FALSE;
DBUG_RETURN(TRUE);
}
@@ -949,14 +973,14 @@ 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= my_delete(to_path, MYF(MY_WME))))
+ if ((error= mysql_file_delete(key_file_frm, to_path, MYF(MY_WME))))
{
if (my_errno != ENOENT)
break;
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
strxmov(to_path, ddl_log_entry->name, par_ext, NullS);
- VOID(my_delete(to_path, MYF(MY_WME)));
+ (void) mysql_file_delete(key_file_partition, to_path, MYF(MY_WME));
#endif
}
else
@@ -969,7 +993,7 @@ static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
}
if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
break;
- VOID(sync_ddl_log());
+ (void) sync_ddl_log();
error= FALSE;
if (ddl_log_entry->action_type == DDL_LOG_DELETE_ACTION)
break;
@@ -988,12 +1012,12 @@ static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
{
strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
strxmov(from_path, ddl_log_entry->from_name, reg_ext, NullS);
- if (my_rename(from_path, to_path, MYF(MY_WME)))
+ if (mysql_file_rename(key_file_frm, from_path, to_path, MYF(MY_WME)))
break;
#ifdef WITH_PARTITION_STORAGE_ENGINE
strxmov(to_path, ddl_log_entry->name, par_ext, NullS);
strxmov(from_path, ddl_log_entry->from_name, par_ext, NullS);
- VOID(my_rename(from_path, to_path, MYF(MY_WME)));
+ (void) mysql_file_rename(key_file_partition, from_path, to_path, MYF(MY_WME));
#endif
}
else
@@ -1004,7 +1028,7 @@ static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
}
if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
break;
- VOID(sync_ddl_log());
+ (void) sync_ddl_log();
error= FALSE;
break;
}
@@ -1036,7 +1060,7 @@ static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry,
DDL_LOG_MEMORY_ENTRY *first_used= global_ddl_log.first_used;
DBUG_ENTER("get_free_ddl_log_entry");
- safe_mutex_assert_owner(&LOCK_gdl);
+ mysql_mutex_assert_owner(&LOCK_gdl);
if (global_ddl_log.first_free == NULL)
{
if (!(used_entry= (DDL_LOG_MEMORY_ENTRY*)my_malloc(
@@ -1060,6 +1084,7 @@ static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry,
*/
used_entry->next_log_entry= first_used;
used_entry->prev_log_entry= NULL;
+ used_entry->next_active_log_entry= NULL;
global_ddl_log.first_used= used_entry;
if (first_used)
first_used->prev_log_entry= used_entry;
@@ -1146,7 +1171,7 @@ bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
}
if (write_header && !error)
{
- VOID(sync_ddl_log());
+ (void) sync_ddl_log();
if (write_ddl_log_header())
error= TRUE;
}
@@ -1204,7 +1229,7 @@ bool write_execute_ddl_log_entry(uint first_entry,
any log entries before, we are only here to write the execute
entry to indicate it is done.
*/
- VOID(sync_ddl_log());
+ (void) sync_ddl_log();
file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_LOG_EXECUTE_CODE;
}
else
@@ -1225,7 +1250,7 @@ bool write_execute_ddl_log_entry(uint first_entry,
release_ddl_log_memory_entry(*active_entry);
DBUG_RETURN(TRUE);
}
- VOID(sync_ddl_log());
+ (void) sync_ddl_log();
if (write_header)
{
if (write_ddl_log_header())
@@ -1327,7 +1352,7 @@ bool sync_ddl_log()
{
DBUG_RETURN(TRUE);
}
- if (my_sync(global_ddl_log.file_id, MYF(0)))
+ if (mysql_file_sync(global_ddl_log.file_id, MYF(0)))
{
/* Write to error log */
sql_print_error("Failed to sync ddl log");
@@ -1352,7 +1377,7 @@ void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry)
DDL_LOG_MEMORY_ENTRY *next_log_entry= log_entry->next_log_entry;
DDL_LOG_MEMORY_ENTRY *prev_log_entry= log_entry->prev_log_entry;
DBUG_ENTER("release_ddl_log_memory_entry");
- safe_mutex_assert_owner(&LOCK_gdl);
+ mysql_mutex_assert_owner(&LOCK_gdl);
global_ddl_log.first_free= log_entry;
log_entry->next_log_entry= first_free;
@@ -1385,7 +1410,7 @@ bool execute_ddl_log_entry(THD *thd, uint first_entry)
uchar file_entry_buf[IO_SIZE];
DBUG_ENTER("execute_ddl_log_entry");
- pthread_mutex_lock(&LOCK_gdl);
+ mysql_mutex_lock(&LOCK_gdl);
do
{
if (read_ddl_log_file_entry(file_entry_buf, read_entry, IO_SIZE))
@@ -1408,7 +1433,7 @@ bool execute_ddl_log_entry(THD *thd, uint first_entry)
}
read_entry= ddl_log_entry.next_entry;
} while (read_entry);
- pthread_mutex_unlock(&LOCK_gdl);
+ mysql_mutex_unlock(&LOCK_gdl);
DBUG_RETURN(FALSE);
}
@@ -1426,7 +1451,7 @@ static void close_ddl_log()
DBUG_ENTER("close_ddl_log");
if (global_ddl_log.file_id >= 0)
{
- VOID(my_close(global_ddl_log.file_id, MYF(MY_WME)));
+ (void) mysql_file_close(global_ddl_log.file_id, MYF(MY_WME));
global_ddl_log.file_id= (File) -1;
}
DBUG_VOID_RETURN;
@@ -1496,10 +1521,10 @@ void execute_ddl_log_recovery()
}
close_ddl_log();
create_ddl_log_file_name(file_name);
- VOID(my_delete(file_name, MYF(0)));
+ (void) mysql_file_delete(key_file_global_ddl_log, file_name, MYF(0));
global_ddl_log.recovery_phase= FALSE;
delete thd;
- my_free(file_entry_buf, MYF(0));
+ my_free(file_entry_buf);
/* Remember that we don't have a THD */
my_pthread_setspecific_ptr(THR_THD, 0);
DBUG_VOID_RETURN;
@@ -1523,25 +1548,25 @@ void release_ddl_log()
if (!global_ddl_log.do_release)
DBUG_VOID_RETURN;
- pthread_mutex_lock(&LOCK_gdl);
+ mysql_mutex_lock(&LOCK_gdl);
free_list= global_ddl_log.first_free;
used_list= global_ddl_log.first_used;
while (used_list)
{
DDL_LOG_MEMORY_ENTRY *tmp= used_list->next_log_entry;
- my_free(used_list, MYF(0));
+ my_free(used_list);
used_list= tmp;
}
while (free_list)
{
DDL_LOG_MEMORY_ENTRY *tmp= free_list->next_log_entry;
- my_free(free_list, MYF(0));
+ my_free(free_list);
free_list= tmp;
}
close_ddl_log();
global_ddl_log.inited= 0;
- pthread_mutex_unlock(&LOCK_gdl);
- VOID(pthread_mutex_destroy(&LOCK_gdl));
+ mysql_mutex_unlock(&LOCK_gdl);
+ mysql_mutex_destroy(&LOCK_gdl);
global_ddl_log.do_release= false;
DBUG_VOID_RETURN;
}
@@ -1652,7 +1677,10 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
{
if (!(part_syntax_buf= generate_partition_syntax(part_info,
&syntax_len,
- TRUE, TRUE)))
+ TRUE, TRUE,
+ lpt->create_info,
+ lpt->alter_info,
+ NULL)))
{
DBUG_RETURN(TRUE);
}
@@ -1671,7 +1699,7 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
CHF_CREATE_FLAG,
lpt->create_info))
{
- my_delete(shadow_frm_name, MYF(0));
+ mysql_file_delete(key_file_frm, shadow_frm_name, MYF(0));
error= 1;
goto end;
}
@@ -1689,13 +1717,13 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
if (readfrm(shadow_path, &data, &length) ||
packfrm(data, length, &lpt->pack_frm_data, &lpt->pack_frm_len))
{
- my_free(data, MYF(MY_ALLOW_ZERO_PTR));
- my_free(lpt->pack_frm_data, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(data);
+ my_free(lpt->pack_frm_data);
mem_alloc_error(length);
error= 1;
goto end;
}
- error= my_delete(shadow_frm_name, MYF(MY_WME));
+ error= mysql_file_delete(key_file_frm, shadow_frm_name, MYF(MY_WME));
}
if (flags & WFRM_INSTALL_SHADOW)
{
@@ -1718,20 +1746,19 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
completing this we write a new phase to the log entry that will
deactivate it.
*/
- VOID(pthread_mutex_lock(&LOCK_open));
- if (my_delete(frm_name, MYF(MY_WME)) ||
+ if (mysql_file_delete(key_file_frm, frm_name, MYF(MY_WME)) ||
#ifdef WITH_PARTITION_STORAGE_ENGINE
lpt->table->file->ha_create_handler_files(path, shadow_path,
CHF_DELETE_FLAG, NULL) ||
deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos) ||
(sync_ddl_log(), FALSE) ||
-#endif
-#ifdef WITH_PARTITION_STORAGE_ENGINE
- my_rename(shadow_frm_name, frm_name, MYF(MY_WME)) ||
+ mysql_file_rename(key_file_frm,
+ shadow_frm_name, frm_name, MYF(MY_WME)) ||
lpt->table->file->ha_create_handler_files(path, shadow_path,
CHF_RENAME_FLAG, NULL))
#else
- my_rename(shadow_frm_name, frm_name, MYF(MY_WME)))
+ mysql_file_rename(key_file_frm,
+ shadow_frm_name, frm_name, MYF(MY_WME)))
#endif
{
error= 1;
@@ -1744,7 +1771,10 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
char *tmp_part_syntax_str;
if (!(part_syntax_buf= generate_partition_syntax(part_info,
&syntax_len,
- TRUE, TRUE)))
+ TRUE, TRUE,
+ lpt->create_info,
+ lpt->alter_info,
+ NULL)))
{
error= 1;
goto err;
@@ -1759,22 +1789,23 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
error= 1;
goto err;
}
- share->partition_info= tmp_part_syntax_str;
+ share->partition_info_str= tmp_part_syntax_str;
}
else
- memcpy((char*) share->partition_info, part_syntax_buf, syntax_len + 1);
- share->partition_info_len= part_info->part_info_len= syntax_len;
+ memcpy((char*) share->partition_info_str, part_syntax_buf,
+ syntax_len + 1);
+ share->partition_info_str_len= part_info->part_info_len= syntax_len;
part_info->part_info_string= part_syntax_buf;
}
#endif
err:
- VOID(pthread_mutex_unlock(&LOCK_open));
#ifdef WITH_PARTITION_STORAGE_ENGINE
deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos);
part_info->frm_log_entry= NULL;
- VOID(sync_ddl_log());
+ (void) sync_ddl_log();
#endif
+ ;
}
end:
@@ -1789,6 +1820,8 @@ end:
clear_error is clear_error to be called
query Query to log
query_length Length of query
+ is_trans if the event changes either
+ a trans or non-trans engine.
RETURN VALUES
NONE
@@ -1799,18 +1832,21 @@ end:
*/
int write_bin_log(THD *thd, bool clear_error,
- char const *query, ulong query_length)
+ char const *query, ulong query_length, bool is_trans)
{
int error= 0;
if (mysql_bin_log.is_open())
{
int errcode= 0;
+ thd_proc_info(thd, "Writing to binlog");
if (clear_error)
thd->clear_error();
else
errcode= query_error_code(thd, TRUE);
error= thd->binlog_query(THD::STMT_QUERY_TYPE,
- query, query_length, FALSE, FALSE, errcode);
+ query, query_length, is_trans, FALSE, FALSE,
+ errcode);
+ thd_proc_info(thd, 0);
}
return error;
}
@@ -1831,7 +1867,8 @@ int write_bin_log(THD *thd, bool clear_error,
If a table is in use, we will wait for all users to free the table
before dropping it
- Wait if global_read_lock (FLUSH TABLES WITH READ LOCK) is set.
+ Wait if global_read_lock (FLUSH TABLES WITH READ LOCK) is set, but
+ not if under LOCK TABLES.
RETURN
FALSE OK. In this case ok packet is sent to user
@@ -1842,123 +1879,253 @@ 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 error= FALSE, need_start_waiters= FALSE;
- Drop_table_error_handler err_handler(thd->get_internal_handler());
+ bool error;
+ Drop_table_error_handler err_handler;
+ TABLE_LIST *table;
+
DBUG_ENTER("mysql_rm_table");
- /* mark for close and remove all cached entries */
+ /* Disable drop of enabled log tables, must be done before name locking */
+ for (table= tables; table; table= table->next_local)
+ {
+ if (check_if_log_table(table->db_length, table->db,
+ table->table_name_length, table->table_name, true))
+ {
+ my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP");
+ DBUG_RETURN(true);
+ }
+ }
+
+ mysql_ha_rm_tables(thd, tables);
if (!drop_temporary)
{
- if ((error= wait_if_global_read_lock(thd, 0, 1)))
+ if (!thd->locked_tables_mode)
{
- my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), tables->table_name);
- DBUG_RETURN(TRUE);
+ if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout,
+ MYSQL_OPEN_SKIP_TEMPORARY))
+ DBUG_RETURN(true);
+ for (table= tables; table; table= table->next_local)
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name,
+ false);
}
else
- need_start_waiters= TRUE;
+ {
+ for (table= tables; table; table= table->next_local)
+ if (table->open_type != OT_BASE_ONLY &&
+ find_temporary_table(thd, table))
+ {
+ /*
+ A temporary table.
+
+ Don't try to find a corresponding MDL lock or assign it
+ to table->mdl_request.ticket. There can't be metadata
+ locks for temporary tables: they are local to the session.
+
+ Later in this function we release the MDL lock only if
+ table->mdl_requeset.ticket is not NULL. Thus here we
+ ensure that we won't release the metadata lock on the base
+ table locked with LOCK TABLES as a side effect of temporary
+ table drop.
+ */
+ DBUG_ASSERT(table->mdl_request.ticket == NULL);
+ }
+ else
+ {
+ /*
+ Not a temporary table.
+
+ Since 'tables' list can't contain duplicates (this is ensured
+ 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, false);
+ if (!table->table)
+ DBUG_RETURN(true);
+ table->mdl_request.ticket= table->table->mdl_ticket;
+ }
+ }
}
- /*
- Acquire LOCK_open after wait_if_global_read_lock(). If we would hold
- LOCK_open during wait_if_global_read_lock(), other threads could not
- close their tables. This would make a pretty deadlock.
- */
+ /* mark for close and remove all cached entries */
thd->push_internal_handler(&err_handler);
- error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
+ error= mysql_rm_table_no_locks(thd, tables, if_exists, drop_temporary,
+ false, false);
thd->pop_internal_handler();
-
- if (need_start_waiters)
- start_waiting_global_read_lock(thd);
-
if (error)
DBUG_RETURN(TRUE);
my_ok(thd);
DBUG_RETURN(FALSE);
}
-/*
- Execute the drop of a normal or temporary table
- SYNOPSIS
- mysql_rm_table_part2()
- thd Thread handler
- tables Tables to drop
- if_exists If set, don't give an error if table doesn't exists.
- In this case we give an warning of level 'NOTE'
- drop_temporary Only drop temporary tables
- drop_view Allow to delete VIEW .frm
- dont_log_query Don't write query to log files. This will also not
- generate warnings if the handler files doesn't exists
-
- TODO:
- When logging to the binary log, we should log
- tmp_tables and transactional tables as separate statements if we
- are in a transaction; This is needed to get these tables into the
- cached binary log that is only written on COMMIT.
-
- The current code only writes DROP statements that only uses temporary
- tables to the cache binary log. This should be ok on most cases, but
- not all.
+/**
+ Find the comment in the query.
+ That's auxiliary function to be used handling DROP TABLE [comment].
+
+ @param thd Thread handler
+ @param comment_pos How many characters to skip before the comment.
+ Can be either 9 for DROP TABLE or
+ 17 for DROP TABLE IF EXISTS
+ @param comment_start returns the beginning of the comment if found.
+
+ @retval 0 no comment found
+ @retval >0 the lenght of the comment found
- RETURN
- 0 ok
- 1 Error
- -1 Thread was killed
*/
+static uint32 comment_length(THD *thd, uint32 comment_pos,
+ const char **comment_start)
+{
+ /* We use uchar * here to make array indexing portable */
+ const uchar *query= (uchar*) thd->query();
+ const uchar *query_end= (uchar*) query + thd->query_length();
+ const uchar *const state_map= thd->charset()->state_map;
+
+ for (; query < query_end; query++)
+ {
+ if (state_map[static_cast<uchar>(*query)] == MY_LEX_SKIP)
+ continue;
+ if (comment_pos-- == 0)
+ break;
+ }
+ if (query > query_end - 3 /* comment can't be shorter than 4 */ ||
+ state_map[static_cast<uchar>(*query)] != MY_LEX_LONG_COMMENT || query[1] != '*')
+ return 0;
+
+ *comment_start= (char*) query;
+
+ for (query+= 3; query < query_end; query++)
+ {
+ if (query[-1] == '*' && query[0] == '/')
+ return (char*) query - *comment_start + 1;
+ }
+ return 0;
+}
-int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
- bool drop_temporary, bool drop_view,
- bool dont_log_query)
+
+/**
+ Execute the drop of a normal or temporary table.
+
+ @param thd Thread handler
+ @param tables Tables to drop
+ @param if_exists If set, don't give an error if table doesn't exists.
+ In this case we give an warning of level 'NOTE'
+ @param drop_temporary Only drop temporary tables
+ @param drop_view Allow to delete VIEW .frm
+ @param dont_log_query Don't write query to log files. This will also not
+ generate warnings if the handler files doesn't exists
+
+ @retval 0 ok
+ @retval 1 Error
+ @retval -1 Thread was killed
+
+ @note This function assumes that metadata locks have already been taken.
+ It is also assumed that the tables have been removed from TDC.
+
+ @todo When logging to the binary log, we should log
+ tmp_tables and transactional tables as separate statements if we
+ are in a transaction; This is needed to get these tables into the
+ cached binary log that is only written on COMMIT.
+ The current code only writes DROP statements that only uses temporary
+ tables to the cache binary log. This should be ok on most cases, but
+ not all.
+*/
+
+int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
+ bool drop_temporary, bool drop_view,
+ bool dont_log_query)
{
TABLE_LIST *table;
- char path[FN_REFLEN + 1], *alias;
- uint path_length;
+ char path[FN_REFLEN + 1], *alias= NULL;
+ uint path_length= 0;
String wrong_tables;
int error= 0;
int non_temp_tables_count= 0;
- bool some_tables_deleted=0, tmp_table_deleted=0, foreign_key_error=0;
+ bool foreign_key_error=0;
+ bool non_tmp_error= 0;
+ bool trans_tmp_table_deleted= 0, non_trans_tmp_table_deleted= 0;
+ bool non_tmp_table_deleted= 0;
+ bool is_drop_tmp_if_exists_added= 0;
String built_query;
- String built_tmp_query;
- DBUG_ENTER("mysql_rm_table_part2");
-
- LINT_INIT(alias);
- LINT_INIT(path_length);
+ String built_trans_tmp_query, built_non_trans_tmp_query;
+ DBUG_ENTER("mysql_rm_table_no_locks");
- if (thd->current_stmt_binlog_row_based && !dont_log_query)
+ /*
+ Prepares the drop statements that will be written into the binary
+ log as follows:
+
+ 1 - If we are not processing a "DROP TEMPORARY" it prepares a
+ "DROP".
+
+ 2 - A "DROP" may result in a "DROP TEMPORARY" but the opposite is
+ not true.
+
+ 3 - If the current format is row, the IF EXISTS token needs to be
+ appended because one does not know if CREATE TEMPORARY was previously
+ written to the binary log.
+
+ 4 - Add the IF_EXISTS token if necessary, i.e. if_exists is TRUE.
+
+ 5 - For temporary tables, there is a need to differentiate tables
+ in transactional and non-transactional storage engines. For that,
+ reason, two types of drop statements are prepared.
+
+ The need to different the type of tables when dropping a temporary
+ table stems from the fact that such drop does not commit an ongoing
+ transaction and changes to non-transactional tables must be written
+ ahead of the transaction in some circumstances.
+
+ 6- Slave SQL thread ignores all replicate-* filter rules
+ for temporary tables with 'IF EXISTS' clause. (See sql/sql_parse.cc:
+ mysql_execute_command() for details). These commands will be binlogged
+ as they are, even if the default database (from USE `db`) is not present
+ on the Slave. This can cause point in time recovery failures later
+ when user uses the slave's binlog to re-apply. Hence at the time of binary
+ logging, these commands will be written with fully qualified table names
+ and use `db` will be suppressed.
+ */
+ if (!dont_log_query)
{
- built_query.set_charset(system_charset_info);
- if (if_exists)
- built_query.append("DROP TABLE IF EXISTS ");
- else
- built_query.append("DROP TABLE ");
- }
+ if (!drop_temporary)
+ {
+ const char *comment_start;
+ uint32 comment_len;
- mysql_ha_rm_tables(thd, tables, FALSE);
+ built_query.set_charset(system_charset_info);
+ if (if_exists)
+ built_query.append("DROP TABLE IF EXISTS ");
+ else
+ built_query.append("DROP TABLE ");
- pthread_mutex_lock(&LOCK_open);
+ if ((comment_len= comment_length(thd, if_exists ? 17:9, &comment_start)))
+ {
+ built_query.append(comment_start, comment_len);
+ built_query.append(" ");
+ }
+ }
- /* Disable drop of enabled log tables, must be done before name locking */
- for (table= tables; table; table= table->next_local)
- {
- if (check_if_log_table(table->db_length, table->db,
- table->table_name_length, table->table_name, 1))
+ if (thd->is_current_stmt_binlog_format_row() || if_exists)
{
- my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP");
- pthread_mutex_unlock(&LOCK_open);
- DBUG_RETURN(1);
+ 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 ");
}
- }
-
- if (!drop_temporary && lock_table_names_exclusively(thd, tables))
- {
- pthread_mutex_unlock(&LOCK_open);
- DBUG_RETURN(1);
}
for (table= tables; table; table= table->next_local)
{
+ bool is_trans;
char *db=table->db;
size_t db_length= table->db_length;
handlerton *table_type;
@@ -1968,118 +2135,174 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
table->db, table->table_name, (long) table->table,
table->table ? (long) table->table->s : (long) -1));
- error= drop_temporary_table(thd, table);
-
- switch (error) {
- case 0:
- // removed temporary table
- tmp_table_deleted= 1;
- if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED &&
- thd->current_stmt_binlog_row_based)
- {
- if (built_tmp_query.is_empty())
- {
- built_tmp_query.set_charset(system_charset_info);
- built_tmp_query.append("DROP TEMPORARY TABLE IF EXISTS ");
- }
-
- if (thd->db == NULL || strcmp(db,thd->db) != 0)
- {
- append_identifier(thd, &built_tmp_query, db, db_length);
- built_tmp_query.append(".");
- }
- append_identifier(thd, &built_tmp_query, table->table_name,
- table->table_name_length);
- built_tmp_query.append(",");
- }
+ /*
+ If we are in locked tables mode and are dropping a temporary table,
+ the ticket should be NULL to ensure that we don't release a lock
+ on a base table later.
+ */
+ DBUG_ASSERT(!(thd->locked_tables_mode &&
+ table->open_type != OT_BASE_ONLY &&
+ find_temporary_table(thd, table) &&
+ table->mdl_request.ticket != NULL));
- continue;
- case -1:
- DBUG_ASSERT(thd->in_sub_stmt);
+ /*
+ drop_temporary_table may return one of the following error codes:
+ . 0 - a temporary table was successfully dropped.
+ . 1 - a temporary table was not found.
+ . -1 - a temporary table is used by an outer statement.
+ */
+ if (table->open_type == OT_BASE_ONLY)
error= 1;
- goto err_with_placeholders;
- default:
- // temporary table not found
- error= 0;
+ else if ((error= drop_temporary_table(thd, table, &is_trans)) == -1)
+ {
+ DBUG_ASSERT(thd->in_sub_stmt);
+ goto err;
}
- /*
- If row-based replication is used and the table is not a
- temporary table, we add the table name to the drop statement
- being built. The string always end in a comma and the comma
- will be chopped off before being written to the binary log.
- */
- if (!drop_temporary && thd->current_stmt_binlog_row_based && !dont_log_query)
+ if ((drop_temporary && if_exists) || !error)
{
- non_temp_tables_count++;
/*
- Don't write the database name if it is the current one (or if
- thd->db is NULL).
+ This handles the case of temporary tables. We have the following cases:
+
+ . "DROP TEMPORARY" was executed and a temporary table was affected
+ (i.e. drop_temporary && !error) or the if_exists was specified (i.e.
+ drop_temporary && if_exists).
+
+ . "DROP" was executed but a temporary table was affected (.i.e
+ !error).
*/
- if (thd->db == NULL || strcmp(db,thd->db) != 0)
+ if (!dont_log_query)
{
- append_identifier(thd, &built_query, db, db_length);
- built_query.append(".");
- }
-
- append_identifier(thd, &built_query, table->table_name,
- table->table_name_length);
- built_query.append(",");
- }
+ /*
+ If there is an error, we don't know the type of the engine
+ at this point. So, we keep it in the trx-cache.
+ */
+ is_trans= error ? TRUE : is_trans;
+ if (is_trans)
+ trans_tmp_table_deleted= TRUE;
+ else
+ non_trans_tmp_table_deleted= TRUE;
- if (!drop_temporary)
- {
- TABLE *locked_table;
- abort_locked_tables(thd, db, table->table_name);
- table->deleting= TRUE;
- remove_table_from_cache(thd, db, table->table_name,
- RTFC_WAIT_OTHER_THREAD_FLAG |
- RTFC_CHECK_KILLED_FLAG, FALSE);
+ String *built_ptr_query=
+ (is_trans ? &built_trans_tmp_query : &built_non_trans_tmp_query);
+ /*
+ Write the database name if it is not the current one or if
+ 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 )
+ {
+ append_identifier(thd, built_ptr_query, db, db_length);
+ built_ptr_query->append(".");
+ }
+ append_identifier(thd, built_ptr_query, table->table_name,
+ table->table_name_length);
+ built_ptr_query->append(",");
+ }
/*
- If the table was used in lock tables, remember it so that
- unlock_table_names can free it
+ This means that a temporary table was droped and as such there
+ is no need to proceed with the code that tries to drop a regular
+ table.
*/
- if ((locked_table= drop_locked_tables(thd, db, table->table_name)))
- table->table= locked_table;
+ if (!error) continue;
+ }
+ else if (!drop_temporary)
+ {
+ non_temp_tables_count++;
- if (thd->killed)
+ if (thd->locked_tables_mode)
{
- error= -1;
- goto err_with_placeholders;
+ if (wait_while_table_is_used(thd, table->table, HA_EXTRA_NOT_USED,
+ TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE))
+ {
+ error= -1;
+ goto err;
+ }
+ close_all_tables_for_name(thd, table->table->s,
+ HA_EXTRA_PREPARE_FOR_DROP);
+ table->table= 0;
}
+
+ /* Check that we have an exclusive lock on the table to be dropped. */
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db,
+ table->table_name,
+ MDL_EXCLUSIVE));
+
alias= (lower_case_table_names == 2) ? table->alias : table->table_name;
/* remove .frm file and engine files */
path_length= build_table_filename(path, sizeof(path) - 1, db, alias,
reg_ext,
table->internal_tmp_table ?
FN_IS_TMP : 0);
+
+ /*
+ This handles the case where a "DROP" was executed and a regular
+ table "may be" dropped as drop_temporary is FALSE and error is
+ TRUE. If the error was FALSE a temporary table was dropped and
+ regardless of the status of drop_tempoary a "DROP TEMPORARY"
+ must be used.
+ */
+ if (!dont_log_query)
+ {
+ /*
+ Note that unless if_exists is TRUE or a temporary table was deleted,
+ there is no means to know if the statement should be written to the
+ binary log. See further information on this variable in what follows.
+ */
+ 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)
+ {
+ append_identifier(thd, &built_query, db, db_length);
+ built_query.append(".");
+ }
+
+ append_identifier(thd, &built_query, table->table_name,
+ table->table_name_length);
+ built_query.append(",");
+ }
}
- DEBUG_SYNC(thd, "rm_table_part2_before_delete_table");
+ DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table");
+ DBUG_EXECUTE_IF("sleep_before_no_locks_delete_table",
+ my_sleep(100000););
+ error= 0;
if (drop_temporary ||
((access(path, F_OK) &&
ha_create_table_from_engine(thd, db, alias)) ||
(!drop_view &&
- mysql_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE)))
+ dd_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE)))
{
- /* Table was not found on disk and table can't be created from engine */
+ /*
+ One of the following cases happened:
+ . "DROP TEMPORARY" but a temporary table was not found.
+ . "DROP" but table was not found on disk and table can't be
+ created from engine.
+ . ./sql/datadict.cc +32 /Alfranio - TODO: We need to test this.
+ */
if (if_exists)
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
table->table_name);
else
+ {
+ non_tmp_error = (drop_temporary ? non_tmp_error : TRUE);
error= 1;
+ }
}
else
{
char *end;
/*
Cannot use the db_type from the table, since that might have changed
- while waiting for the exclusive name lock. We are under LOCK_open,
- so reading from the frm-file is safe.
+ while waiting for the exclusive name lock.
*/
if (frm_db_type == DB_TYPE_UNKNOWN)
{
- mysql_frm_type(thd, path, &frm_db_type);
+ dd_frm_type(thd, path, &frm_db_type);
DBUG_PRINT("info", ("frm_db_type %d from %s", frm_db_type, path));
}
table_type= ha_resolve_by_legacy_type(thd, frm_db_type);
@@ -2100,21 +2323,22 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
if (error == HA_ERR_ROW_IS_REFERENCED)
{
/* the table is referenced by a foreign key constraint */
- foreign_key_error=1;
+ foreign_key_error= 1;
}
if (!error || error == ENOENT || error == HA_ERR_NO_SUCH_TABLE)
{
int new_error;
/* Delete the table definition file */
strmov(end,reg_ext);
- if (!(new_error=my_delete(path,MYF(MY_WME))))
+ if (!(new_error= mysql_file_delete(key_file_frm, path, MYF(MY_WME))))
{
- some_tables_deleted=1;
+ non_tmp_table_deleted= TRUE;
new_error= Table_triggers_list::drop_all_triggers(thd, db,
table->table_name);
}
error|= new_error;
}
+ non_tmp_error= error ? TRUE : non_tmp_error;
}
if (error)
{
@@ -2122,17 +2346,23 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
wrong_tables.append(',');
wrong_tables.append(String(table->table_name,system_charset_info));
}
+ else
+ {
+ mysql_audit_drop_table(thd, table);
+ }
DBUG_PRINT("table", ("table: 0x%lx s: 0x%lx", (long) table->table,
table->table ? (long) table->table->s : (long) -1));
+
+ DBUG_EXECUTE_IF("bug43138",
+ my_printf_error(ER_BAD_TABLE_ERROR,
+ ER(ER_BAD_TABLE_ERROR), MYF(0),
+ table->table_name););
}
- /*
- It's safe to unlock LOCK_open: we have an exclusive lock
- on the table name.
- */
- pthread_mutex_unlock(&LOCK_open);
- DEBUG_SYNC(thd, "rm_table_part2_before_binlog");
- thd->thread_specific_used|= tmp_table_deleted;
+ DEBUG_SYNC(thd, "rm_table_no_locks_before_binlog");
+ thd->thread_specific_used|= (trans_tmp_table_deleted ||
+ non_trans_tmp_table_deleted);
error= 0;
+err:
if (wrong_tables.length())
{
if (!foreign_key_error)
@@ -2143,80 +2373,90 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
error= 1;
}
- if (some_tables_deleted || tmp_table_deleted || !error)
+ if (non_trans_tmp_table_deleted ||
+ trans_tmp_table_deleted || non_tmp_table_deleted)
{
query_cache_invalidate3(thd, tables, 0);
- if (!dont_log_query)
+ if (!dont_log_query && mysql_bin_log.is_open())
{
- if (!thd->current_stmt_binlog_row_based ||
- (non_temp_tables_count > 0 && !tmp_table_deleted))
+ if (non_trans_tmp_table_deleted)
{
- /*
- In this case, we are either using statement-based
- replication or using row-based replication but have only
- deleted one or more non-temporary tables (and no temporary
- tables). In this case, we can write the original query into
- the binary log.
- */
- error |= write_bin_log(thd, !error, thd->query(), thd->query_length());
+ /* Chop of the last comma */
+ built_non_trans_tmp_query.chop();
+ built_non_trans_tmp_query.append(" /* generated by server */");
+ 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);
}
- else if (thd->current_stmt_binlog_row_based &&
- tmp_table_deleted)
+ if (trans_tmp_table_deleted)
{
- if (non_temp_tables_count > 0)
- {
- /*
- In this case we have deleted both temporary and
- non-temporary tables, so:
- - since we have deleted a non-temporary table we have to
- binlog the statement, but
- - since we have deleted a temporary table we cannot binlog
- the statement (since the table may have not been created on the
- slave - check "if" branch below, this might cause the slave to
- stop).
-
- Instead, we write a built statement, only containing the
- non-temporary tables, to the binary log
- */
- built_query.chop(); // Chop of the last comma
+ /* Chop of the last comma */
+ built_trans_tmp_query.chop();
+ built_trans_tmp_query.append(" /* generated by server */");
+ 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);
+ }
+ if (non_tmp_table_deleted)
+ {
+ /* Chop of the last comma */
+ built_query.chop();
built_query.append(" /* generated by server */");
- error|= write_bin_log(thd, !error, built_query.ptr(), built_query.length());
- }
+ int error_code = (non_tmp_error ?
+ (foreign_key_error ? ER_ROW_IS_REFERENCED : ER_BAD_TABLE_ERROR) : 0);
+ error |= thd->binlog_query(THD::STMT_QUERY_TYPE,
+ built_query.ptr(),
+ built_query.length(),
+ TRUE, FALSE, FALSE,
+ error_code);
+ }
+ }
+ }
- /*
- One needs to always log any temporary table drop, if:
- 1. thread logging format is mixed mode; AND
- 2. current statement logging format is set to row.
- */
- if (thd->variables.binlog_format == BINLOG_FORMAT_MIXED)
+ if (!drop_temporary)
+ {
+ /*
+ Under LOCK TABLES we should release meta-data locks on the tables
+ which were dropped.
+
+ Leave LOCK TABLES mode if we managed to drop all tables which were
+ locked. Additional check for 'non_temp_tables_count' is to avoid
+ leaving LOCK TABLES mode if we have dropped only temporary tables.
+ */
+ if (thd->locked_tables_mode)
+ {
+ if (thd->lock && thd->lock->table_count == 0 && non_temp_tables_count > 0)
+ {
+ thd->locked_tables_list.unlock_locked_tables(thd);
+ goto end;
+ }
+ for (table= tables; table; table= table->next_local)
+ {
+ /* Drop locks for all successfully dropped tables. */
+ if (table->table == NULL && table->mdl_request.ticket)
{
/*
- In this case we have deleted some temporary tables but we are using
- row based logging for the statement. However, thread uses mixed mode
- format, thence we need to log the dropping as we cannot tell for
- sure whether the create was logged as statement previously or not, ie,
- before switching to row mode.
+ Under LOCK TABLES we may have several instances of table open
+ and locked and therefore have to remove several metadata lock
+ requests associated with them.
*/
- built_tmp_query.chop(); // Chop of the last comma
- built_tmp_query.append(" /* generated by server */");
- error|= write_bin_log(thd, !error, built_tmp_query.ptr(), built_tmp_query.length());
+ thd->mdl_context.release_all_locks_for_name(table->mdl_request.ticket);
}
}
-
- /*
- The remaining cases are:
- - no tables were deleted and
- - only temporary tables were deleted and row-based
- replication is used.
- In both these cases, nothing should be written to the binary
- log.
- */
}
+ /*
+ Rely on the caller to implicitly commit the transaction
+ and release metadata locks.
+ */
}
- pthread_mutex_lock(&LOCK_open);
-err_with_placeholders:
- unlock_table_names(thd, tables, (TABLE_LIST*) 0);
- pthread_mutex_unlock(&LOCK_open);
+
+end:
DBUG_RETURN(error);
}
@@ -2245,7 +2485,7 @@ bool quick_rm_table(handlerton *base,const char *db,
uint path_length= build_table_filename(path, sizeof(path) - 1,
db, table_name, reg_ext, flags);
- if (my_delete(path,MYF(0)))
+ if (mysql_file_delete(key_file_frm, path, MYF(0)))
error= 1; /* purecov: inspected */
path[path_length - reg_ext_length]= '\0'; // Remove reg_ext
if (!(flags & FRM_ONLY))
@@ -2338,17 +2578,19 @@ bool check_duplicates_in_interval(const char *set_or_name,
tmp.count--;
if (find_type2(&tmp, (const char*)*cur_value, *cur_length, cs))
{
+ THD *thd= current_thd;
+ ErrConvString err(*cur_value, *cur_length, cs);
if ((current_thd->variables.sql_mode &
(MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)))
{
my_error(ER_DUPLICATED_VALUE_IN_TYPE, MYF(0),
- name,*cur_value,set_or_name);
+ name, err.ptr(), set_or_name);
return 1;
}
- push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_DUPLICATED_VALUE_IN_TYPE,
- ER(ER_DUPLICATED_VALUE_IN_TYPE),
- name,*cur_value,set_or_name);
+ push_warning_printf(thd,MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_DUPLICATED_VALUE_IN_TYPE,
+ ER(ER_DUPLICATED_VALUE_IN_TYPE),
+ name, err.ptr(), set_or_name);
(*dup_val_count)++;
}
}
@@ -2559,10 +2801,6 @@ int prepare_create_field(Create_field *sql_field,
(sql_field->decimals << FIELDFLAG_DEC_SHIFT));
break;
}
- if (sql_field->flags & NOT_NULL_FLAG)
- DBUG_PRINT("info", ("1"));
- if (sql_field->vcol_info)
- DBUG_PRINT("info", ("2"));
if (!(sql_field->flags & NOT_NULL_FLAG) ||
(sql_field->vcol_info)) /* Make virtual columns allow NULL values */
sql_field->pack_flag|= FIELDFLAG_MAYBE_NULL;
@@ -2571,6 +2809,54 @@ int prepare_create_field(Create_field *sql_field,
DBUG_RETURN(0);
}
+
+/*
+ Get character set from field object generated by parser using
+ default values when not set.
+
+ SYNOPSIS
+ get_sql_field_charset()
+ sql_field The sql_field object
+ create_info Info generated by parser
+
+ RETURN VALUES
+ cs Character set
+*/
+
+CHARSET_INFO* get_sql_field_charset(Create_field *sql_field,
+ HA_CREATE_INFO *create_info)
+{
+ CHARSET_INFO *cs= sql_field->charset;
+
+ if (!cs)
+ cs= create_info->default_table_charset;
+ /*
+ table_charset is set only in ALTER TABLE t1 CONVERT TO CHARACTER SET csname
+ if we want change character set for all varchar/char columns.
+ But the table charset must not affect the BLOB fields, so don't
+ allow to change my_charset_bin to somethig else.
+ */
+ if (create_info->table_charset && cs != &my_charset_bin)
+ cs= create_info->table_charset;
+ return cs;
+}
+
+
+bool check_duplicate_warning(THD *thd, char *msg, ulong length)
+{
+ List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
+ MYSQL_ERROR *err;
+ while ((err= it++))
+ {
+ if (strncmp(msg, err->get_message_text(), length) == 0)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
/*
Preparation for table creation
@@ -2634,18 +2920,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
executing a prepared statement for the second time.
*/
sql_field->length= sql_field->char_length;
- if (!sql_field->charset)
- sql_field->charset= create_info->default_table_charset;
- /*
- table_charset is set in ALTER TABLE if we want change character set
- for all varchar/char columns.
- But the table charset must not affect the BLOB fields, so don't
- allow to change my_charset_bin to somethig else.
- */
- if (create_info->table_charset && sql_field->charset != &my_charset_bin)
- sql_field->charset= create_info->table_charset;
-
- save_cs= sql_field->charset;
+ save_cs= sql_field->charset= get_sql_field_charset(sql_field,
+ create_info);
if ((sql_field->flags & BINCMP_FLAG) &&
!(sql_field->charset= get_charset_by_csname(sql_field->charset->csname,
MY_CS_BINSORT,MYF(0))))
@@ -2711,7 +2987,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
sql_field->interval_list);
List_iterator<String> int_it(sql_field->interval_list);
String conv, *tmp;
- char comma_buf[2];
+ char comma_buf[4]; /* 4 bytes for utf32 */
int comma_length= cs->cset->wc_mb(cs, ',', (uchar*) comma_buf,
(uchar*) comma_buf +
sizeof(comma_buf));
@@ -2740,7 +3016,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
interval->type_lengths[i],
comma_buf, comma_length, NULL, 0))
{
- my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", tmp->ptr());
+ ErrConvString err(tmp->ptr(), tmp->length(), cs);
+ my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", err.ptr());
DBUG_RETURN(TRUE);
}
}
@@ -2961,6 +3238,15 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
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
+ from the select tables. This order may differ on master and slave. We
+ therefore mark it as unsafe.
+ */
+ if (select_field_count > 0 && auto_increment)
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_CREATE_SELECT_AUTOINC);
+
/* Create keys */
List_iterator<Key> key_iterator(alter_info->key_list);
@@ -2977,9 +3263,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
while ((key=key_iterator++))
{
- DBUG_PRINT("info", ("key name: '%s' type: %d", key->name ? key->name :
+ DBUG_PRINT("info", ("key name: '%s' type: %d", key->name.str ? key->name.str :
"(none)" , key->type));
- LEX_STRING key_name_str;
if (key->type == Key::FOREIGN_KEY)
{
fk_key_count++;
@@ -2990,7 +3275,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
fk_key->ref_columns.elements != fk_key->columns.elements)
{
my_error(ER_WRONG_FK_DEF, MYF(0),
- (fk_key->name ? fk_key->name : "foreign key without name"),
+ (fk_key->name.str ? fk_key->name.str :
+ "foreign key without name"),
ER(ER_KEY_REF_DO_NOT_MATCH_TABLE_REF));
DBUG_RETURN(TRUE);
}
@@ -3003,12 +3289,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
DBUG_RETURN(TRUE);
}
- key_name_str.str= (char*) key->name;
- key_name_str.length= key->name ? strlen(key->name) : 0;
- if (check_string_char_length(&key_name_str, "", NAME_CHAR_LEN,
+ if (check_string_char_length(&key->name, "", NAME_CHAR_LEN,
system_charset_info, 1))
{
- my_error(ER_TOO_LONG_IDENT, MYF(0), key->name);
+ my_error(ER_TOO_LONG_IDENT, MYF(0), key->name.str);
DBUG_RETURN(TRUE);
}
key_iterator2.rewind ();
@@ -3022,7 +3306,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
Then we do not need the generated shorter key.
*/
if ((key2->type != Key::FOREIGN_KEY &&
- key2->name != ignore_key &&
+ key2->name.str != ignore_key &&
!foreign_key_prefix(key, key2)))
{
/* TODO: issue warning message */
@@ -3030,10 +3314,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (!key2->generated ||
(key->generated && key->columns.elements <
key2->columns.elements))
- key->name= ignore_key;
+ key->name.str= ignore_key;
else
{
- key2->name= ignore_key;
+ key2->name.str= ignore_key;
key_parts-= key2->columns.elements;
(*key_count)--;
}
@@ -3041,14 +3325,14 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
}
}
}
- if (key->name != ignore_key)
+ if (key->name.str != ignore_key)
key_parts+=key->columns.elements;
else
(*key_count)--;
- if (key->name && !tmp_table && (key->type != Key::PRIMARY) &&
- !my_strcasecmp(system_charset_info,key->name,primary_key_name))
+ if (key->name.str && !tmp_table && (key->type != Key::PRIMARY) &&
+ !my_strcasecmp(system_charset_info, key->name.str, primary_key_name))
{
- my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
+ my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name.str);
DBUG_RETURN(TRUE);
}
}
@@ -3071,12 +3355,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
uint key_length=0;
Key_part_spec *column;
- if (key->name == ignore_key)
+ if (key->name.str == ignore_key)
{
/* ignore redundant keys */
do
key=key_iterator++;
- while (key && key->name == ignore_key);
+ while (key && key->name.str == ignore_key);
if (!key)
break;
}
@@ -3188,29 +3472,28 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
CHARSET_INFO *ft_key_charset=0; // for FULLTEXT
for (uint column_nr=0 ; (column=cols++) ; column_nr++)
{
- uint length;
Key_part_spec *dup_column;
it.rewind();
field=0;
while ((sql_field=it++) &&
my_strcasecmp(system_charset_info,
- column->field_name,
+ column->field_name.str,
sql_field->field_name))
field++;
if (!sql_field)
{
- my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name);
+ 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, dup_column->field_name))
+ column->field_name.str, dup_column->field_name.str))
{
my_printf_error(ER_DUP_FIELDNAME,
ER(ER_DUP_FIELDNAME),MYF(0),
- column->field_name);
+ column->field_name.str);
DBUG_RETURN(TRUE);
}
}
@@ -3224,7 +3507,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet
(ft_key_charset && sql_field->charset != ft_key_charset))
{
- my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name);
+ my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name.str);
DBUG_RETURN(-1);
}
ft_key_charset=sql_field->charset;
@@ -3260,15 +3543,15 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
{
if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS))
{
- my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name);
+ my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name.str);
DBUG_RETURN(TRUE);
}
if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type ==
Field::GEOM_POINT)
- column->length= 25;
+ column->length= MAX_LEN_GEOM_POINT_FIELD;
if (!column->length)
{
- my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name);
+ my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name.str);
DBUG_RETURN(TRUE);
}
}
@@ -3310,7 +3593,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
key_info->flags|= HA_NULL_PART_KEY;
if (!(file->ha_table_flags() & HA_NULL_IN_KEY))
{
- my_error(ER_NULL_COLUMN_IN_INDEX, MYF(0), column->field_name);
+ my_error(ER_NULL_COLUMN_IN_INDEX, MYF(0), column->field_name.str);
DBUG_RETURN(TRUE);
}
if (key->type == Key::SPATIAL)
@@ -3331,78 +3614,85 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
key_part_info->fieldnr= field;
key_part_info->offset= (uint16) sql_field->offset;
key_part_info->key_type=sql_field->pack_flag;
- length= sql_field->key_length;
+ uint key_part_length= sql_field->key_length;
if (column->length)
{
if (f_is_blob(sql_field->pack_flag))
{
- if ((length=column->length) > max_key_length ||
- length > file->max_key_part_length())
+ key_part_length= min(column->length,
+ blob_length_by_type(sql_field->sql_type)
+ * sql_field->charset->mbmaxlen);
+ if (key_part_length > max_key_length ||
+ key_part_length > file->max_key_part_length())
{
- length=min(max_key_length, file->max_key_part_length());
+ key_part_length= min(max_key_length, file->max_key_part_length());
if (key->type == Key::MULTIPLE)
{
/* not a critical problem */
- char warn_buff[MYSQL_ERRMSG_SIZE];
- my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_KEY),
- length);
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_TOO_LONG_KEY, warn_buff);
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_TOO_LONG_KEY, ER(ER_TOO_LONG_KEY),
+ key_part_length);
/* Align key length to multibyte char boundary */
- length-= length % sql_field->charset->mbmaxlen;
+ key_part_length-= key_part_length % sql_field->charset->mbmaxlen;
}
else
{
- my_error(ER_TOO_LONG_KEY,MYF(0),length);
+ my_error(ER_TOO_LONG_KEY, MYF(0), key_part_length);
DBUG_RETURN(TRUE);
}
}
}
+ // Catch invalid use of partial keys
else if (!f_is_geom(sql_field->pack_flag) &&
- (column->length > length ||
- !Field::type_can_have_key_part (sql_field->sql_type) ||
- ((f_is_packed(sql_field->pack_flag) ||
- ((file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS) &&
- (key_info->flags & HA_NOSAME))) &&
- column->length != length)))
- {
+ // is the key partial?
+ column->length != key_part_length &&
+ // 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) ||
+ // 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?
+ ((file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS) &&
+ // and is this a 'unique' key?
+ (key_info->flags & HA_NOSAME))))
+ {
my_message(ER_WRONG_SUB_KEY, ER(ER_WRONG_SUB_KEY), MYF(0));
DBUG_RETURN(TRUE);
}
else if (!(file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS))
- length=column->length;
+ key_part_length= column->length;
}
- else if (length == 0 && (sql_field->flags & NOT_NULL_FLAG))
+ else if (key_part_length == 0 && (sql_field->flags & NOT_NULL_FLAG))
{
- my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name);
- DBUG_RETURN(TRUE);
+ my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name.str);
+ DBUG_RETURN(TRUE);
}
- if (length > file->max_key_part_length() && key->type != Key::FULLTEXT)
+ if (key_part_length > file->max_key_part_length() &&
+ key->type != Key::FULLTEXT)
{
- length= file->max_key_part_length();
+ key_part_length= file->max_key_part_length();
if (key->type == Key::MULTIPLE)
{
/* not a critical problem */
- char warn_buff[MYSQL_ERRMSG_SIZE];
- my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_KEY),
- length);
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_TOO_LONG_KEY, warn_buff);
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_TOO_LONG_KEY, ER(ER_TOO_LONG_KEY),
+ key_part_length);
/* Align key length to multibyte char boundary */
- length-= length % sql_field->charset->mbmaxlen;
+ key_part_length-= key_part_length % sql_field->charset->mbmaxlen;
}
else
{
- my_error(ER_TOO_LONG_KEY,MYF(0),length);
+ my_error(ER_TOO_LONG_KEY, MYF(0), key_part_length);
DBUG_RETURN(TRUE);
}
}
- key_part_info->length=(uint16) length;
+ key_part_info->length= (uint16) key_part_length;
/* Use packed keys for long strings on the first column */
if (!((*db_options) & HA_OPTION_NO_PACK_KEYS) &&
!((create_info->table_options & HA_OPTION_NO_PACK_KEYS)) &&
- (length >= KEY_DEFAULT_PACK_LENGTH &&
+ (key_part_length >= KEY_DEFAULT_PACK_LENGTH &&
(sql_field->sql_type == MYSQL_TYPE_STRING ||
sql_field->sql_type == MYSQL_TYPE_VARCHAR ||
sql_field->pack_flag & FIELDFLAG_BLOB)))
@@ -3414,10 +3704,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
key_info->flags|= HA_PACK_KEY;
}
/* Check if the key segment is partial, set the key flag accordingly */
- if (length != sql_field->key_length)
+ if (key_part_length != sql_field->key_length)
key_info->flags|= HA_KEY_HAS_PART_KEY_SEG;
- key_length+=length;
+ key_length+= key_part_length;
key_part_info++;
/* Create the key name based on the first column (if not given) */
@@ -3434,7 +3724,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
key_name=primary_key_name;
primary_key=1;
}
- else if (!(key_name = key->name))
+ else if (!(key_name= key->name.str))
key_name=make_unique_key_name(sql_field->field_name,
*key_info_buffer, key_info);
if (check_if_keyname_exists(key_name, *key_info_buffer, key_info))
@@ -3458,6 +3748,40 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length);
DBUG_RETURN(TRUE);
}
+
+ uint tmp_len= system_charset_info->cset->charpos(system_charset_info,
+ key->key_create_info.comment.str,
+ key->key_create_info.comment.str +
+ key->key_create_info.comment.length,
+ INDEX_COMMENT_MAXLEN);
+
+ if (tmp_len < key->key_create_info.comment.length)
+ {
+ if ((thd->variables.sql_mode &
+ (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)))
+ {
+ my_error(ER_TOO_LONG_INDEX_COMMENT, MYF(0),
+ key_info->name, static_cast<ulong>(INDEX_COMMENT_MAXLEN));
+ DBUG_RETURN(-1);
+ }
+ char warn_buff[MYSQL_ERRMSG_SIZE];
+ my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_INDEX_COMMENT),
+ key_info->name, static_cast<ulong>(INDEX_COMMENT_MAXLEN));
+ /* do not push duplicate warnings */
+ if (!check_duplicate_warning(thd, warn_buff, strlen(warn_buff)))
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_TOO_LONG_INDEX_COMMENT, warn_buff);
+
+ key->key_create_info.comment.length= tmp_len;
+ }
+
+ key_info->comment.length= key->key_create_info.comment.length;
+ if (key_info->comment.length > 0)
+ {
+ key_info->flags|= HA_USES_COMMENT;
+ key_info->comment.str= key->key_create_info.comment.str;
+ }
+
key_info++;
}
if (!unique_key && !primary_key &&
@@ -3656,40 +3980,45 @@ void sp_prepare_create_field(THD *thd, Create_field *sql_field)
}
-/*
- Write CREATE TABLE binlog
-
- SYNOPSIS
- write_create_table_bin_log()
- thd Thread object
- create_info Create information
- internal_tmp_table Set to 1 if this is an internal temporary table
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+/**
+ Auxiliary function which allows to check if freshly created .FRM
+ file for table can be opened.
- DESCRIPTION
- This function only is called in mysql_create_table_no_lock and
- mysql_create_table
+ @retval FALSE - Success.
+ @retval TRUE - Failure.
+*/
- RETURN VALUES
- NONE
- */
-static inline int write_create_table_bin_log(THD *thd,
- const HA_CREATE_INFO *create_info,
- bool internal_tmp_table)
+static bool check_if_created_table_can_be_opened(THD *thd,
+ const char *path,
+ const char *db,
+ const char *table_name,
+ HA_CREATE_INFO *create_info,
+ handler *file)
{
+ TABLE table;
+ TABLE_SHARE share;
+ bool result;
+
/*
- Don't write statement if:
- - It is an internal temporary table,
- - Row-based logging is used and it we are creating a temporary table, or
- - The binary log is not open.
- Otherwise, the statement shall be binlogged.
- */
- if (!internal_tmp_table &&
- (!thd->current_stmt_binlog_row_based ||
- (thd->current_stmt_binlog_row_based &&
- !(create_info->options & HA_LEX_CREATE_TMP_TABLE))))
- return write_bin_log(thd, TRUE, thd->query(), thd->query_length());
- return 0;
+ It is impossible to open definition of partitioned table without .par file.
+ */
+ if (file->ha_create_handler_files(path, NULL, CHF_CREATE_FLAG, create_info))
+ return TRUE;
+
+ init_tmp_table_share(thd, &share, db, 0, table_name, path);
+
+ result= (open_table_def(thd, &share, 0) ||
+ open_table_from_share(thd, &share, "", 0, (uint) READ_ALL,
+ 0, &table, TRUE));
+ if (! result)
+ (void) closefrm(&table, 0);
+
+ free_table_share(&share);
+ (void) file->ha_create_handler_files(path, NULL, CHF_DELETE_FLAG, create_info);
+ return result;
}
+#endif
/**
@@ -3768,14 +4097,16 @@ bool check_table_file_presence(char *old_path,
internal_tmp_table Set to 1 if this is an internal temporary table
(From ALTER TABLE)
select_field_count
+ is_trans identifies the type of engine where the table
+ was created: either trans or non-trans.
DESCRIPTION
If one creates a temporary table, this is automatically opened
Note that this function assumes that caller already have taken
- name-lock on table being created or used some other way to ensure
- that concurrent operations won't intervene. mysql_create_table()
- is a wrapper that can be used for this.
+ exclusive metadata lock on table being created or used some other
+ way to ensure that concurrent operations won't intervene.
+ mysql_create_table() is a wrapper that can be used for this.
no_log is needed for the case of CREATE ... SELECT,
as the logging will be done later in sql_insert.cc
@@ -3792,7 +4123,8 @@ bool mysql_create_table_no_lock(THD *thd,
HA_CREATE_INFO *create_info,
Alter_info *alter_info,
bool internal_tmp_table,
- uint select_field_count)
+ uint select_field_count,
+ bool *is_trans)
{
char path[FN_REFLEN + 1];
uint path_length;
@@ -3813,10 +4145,14 @@ bool mysql_create_table_no_lock(THD *thd,
MYF(0));
DBUG_RETURN(TRUE);
}
- if (check_engine(thd, table_name, create_info))
+ if (check_engine(thd, db, table_name, create_info))
DBUG_RETURN(TRUE);
+
+ set_table_default_charset(thd, create_info, (char*) db);
+
db_options= create_info->table_options;
- if (create_info->row_type != ROW_TYPE_FIXED &&
+ if (!create_info->frm_only &&
+ create_info->row_type != ROW_TYPE_FIXED &&
create_info->row_type != ROW_TYPE_DEFAULT)
db_options|= HA_OPTION_PACK_RECORD;
alias= table_case_name(create_info, table_name);
@@ -3909,7 +4245,7 @@ bool mysql_create_table_no_lock(THD *thd,
ha_resolve_storage_engine_name(part_info->default_engine_type),
ha_resolve_storage_engine_name(create_info->db_type)));
if (part_info->check_partition_info(thd, &engine_type, file,
- create_info, TRUE))
+ create_info, FALSE))
goto err;
part_info->default_engine_type= engine_type;
@@ -3919,7 +4255,10 @@ bool mysql_create_table_no_lock(THD *thd,
*/
if (!(part_syntax_buf= generate_partition_syntax(part_info,
&syntax_len,
- TRUE, TRUE)))
+ TRUE, TRUE,
+ create_info,
+ alter_info,
+ NULL)))
goto err;
part_info->part_info_string= part_syntax_buf;
part_info->part_info_len= syntax_len;
@@ -3945,9 +4284,9 @@ bool mysql_create_table_no_lock(THD *thd,
creates a proper .par file. The current part_info object is
only used to create the frm-file and .par-file.
*/
- if (part_info->use_default_no_partitions &&
- part_info->no_parts &&
- (int)part_info->no_parts !=
+ if (part_info->use_default_num_partitions &&
+ part_info->num_parts &&
+ (int)part_info->num_parts !=
file->get_default_no_partitions(create_info))
{
uint i;
@@ -3958,13 +4297,13 @@ bool mysql_create_table_no_lock(THD *thd,
(part_it++)->part_state= PART_TO_BE_DROPPED;
}
else if (part_info->is_sub_partitioned() &&
- part_info->use_default_no_subpartitions &&
- part_info->no_subparts &&
- (int)part_info->no_subparts !=
+ part_info->use_default_num_subpartitions &&
+ part_info->num_subparts &&
+ (int)part_info->num_subparts !=
file->get_default_no_partitions(create_info))
{
DBUG_ASSERT(thd->lex->sql_command != SQLCOM_CREATE_TABLE);
- part_info->no_subparts= file->get_default_no_partitions(create_info);
+ part_info->num_subparts= file->get_default_no_partitions(create_info);
}
}
else if (create_info->db_type != engine_type)
@@ -3986,8 +4325,6 @@ bool mysql_create_table_no_lock(THD *thd,
}
#endif
- set_table_default_charset(thd, create_info, (char*) db);
-
if (mysql_prepare_create_table(thd, create_info, alter_info,
internal_tmp_table,
&db_options, file,
@@ -4012,14 +4349,7 @@ bool mysql_create_table_no_lock(THD *thd,
find_temporary_table(thd, db, table_name))
{
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
- {
- create_info->table_existed= 1; // Mark that table existed
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
- alias);
- error= write_create_table_bin_log(thd, create_info, internal_tmp_table);
- goto err;
- }
+ goto warn;
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
goto err;
}
@@ -4030,13 +4360,12 @@ bool mysql_create_table_no_lock(THD *thd,
if (file->ht != maria_hton)
#endif
if (create_info->transactional)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_ILLEGAL_HA_CREATE_OPTION,
ER(ER_ILLEGAL_HA_CREATE_OPTION),
file->engine_name()->str,
"TRANSACTIONAL=1");
- VOID(pthread_mutex_lock(&LOCK_open));
if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
{
if (check_table_file_presence(NULL, path, db, table_name, table_name,
@@ -4045,7 +4374,7 @@ bool mysql_create_table_no_lock(THD *thd,
{
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
goto warn;
- goto unlock_and_end;
+ goto err;
}
/*
We don't assert here, but check the result, because the table could be
@@ -4055,11 +4384,14 @@ bool mysql_create_table_no_lock(THD *thd,
Then she could create the table. This case is pretty obscure and
therefore we don't introduce a new error message only for it.
*/
+ mysql_mutex_lock(&LOCK_open);
if (get_cached_table_share(db, table_name))
{
+ mysql_mutex_unlock(&LOCK_open);
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
- goto unlock_and_end;
+ goto err;
}
+ mysql_mutex_unlock(&LOCK_open);
}
/*
@@ -4067,7 +4399,7 @@ bool mysql_create_table_no_lock(THD *thd,
exist in any storage engine. In such a case it should
be discovered and the error ER_TABLE_EXISTS_ERROR be returned
unless user specified CREATE TABLE IF EXISTS
- The LOCK_open mutex has been locked to make sure no
+ An exclusive metadata lock ensures that no
one else is attempting to discover the table. Since
it's not on disk as a frm file, no one could be using it!
*/
@@ -4088,33 +4420,60 @@ bool mysql_create_table_no_lock(THD *thd,
if (create_if_not_exists)
goto warn;
my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
- goto unlock_and_end;
+ goto err;
default:
DBUG_PRINT("info", ("error: %u from storage engine", retcode));
my_error(retcode, MYF(0),table_name);
- goto unlock_and_end;
+ goto err;
}
}
thd_proc_info(thd, "creating table");
- create_info->table_existed= 0; // Mark that table is created
#ifdef HAVE_READLINK
- if (test_if_data_home_dir(create_info->data_file_name))
{
- my_error(ER_WRONG_ARGUMENTS, MYF(0), "DATA DIRECTORY");
- goto unlock_and_end;
- }
- if (test_if_data_home_dir(create_info->index_file_name))
- {
- my_error(ER_WRONG_ARGUMENTS, MYF(0), "INDEX DIRECTORY");
- goto unlock_and_end;
+ size_t dirlen;
+ char dirpath[FN_REFLEN];
+
+ /*
+ data_file_name and index_file_name include the table name without
+ extension. Mostly this does not refer to an existing file. When
+ comparing data_file_name or index_file_name against the data
+ directory, we try to resolve all symbolic links. On some systems,
+ we use realpath(3) for the resolution. This returns ENOENT if the
+ resolved path does not refer to an existing file. my_realpath()
+ does then copy the requested path verbatim, without symlink
+ resolution. Thereafter the comparison can fail even if the
+ requested path is within the data directory. E.g. if symlinks to
+ another file system are used. To make realpath(3) return the
+ resolved path, we strip the table name and compare the directory
+ path only. If the directory doesn't exist either, table creation
+ will fail anyway.
+ */
+ if (create_info->data_file_name)
+ {
+ dirname_part(dirpath, create_info->data_file_name, &dirlen);
+ if (test_if_data_home_dir(dirpath))
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), "DATA DIRECTORY");
+ goto err;
+ }
+ }
+ if (create_info->index_file_name)
+ {
+ dirname_part(dirpath, create_info->index_file_name, &dirlen);
+ if (test_if_data_home_dir(dirpath))
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), "INDEX DIRECTORY");
+ goto err;
+ }
+ }
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (check_partition_dirs(thd->lex->part_info))
{
- goto unlock_and_end;
+ goto err;
}
#endif /* WITH_PARTITION_STORAGE_ENGINE */
@@ -4137,23 +4496,53 @@ bool mysql_create_table_no_lock(THD *thd,
if (rea_create_table(thd, path, db, table_name,
create_info, alter_info->create_list,
key_count, key_info_buffer, file))
- goto unlock_and_end;
+ goto err;
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
{
- /* Open table and put in temporary table list */
- if (!(open_temporary_table(thd, path, db, table_name, 1)))
+ /*
+ Open a table (skipping table cache) and add it into
+ THD::temporary_tables list.
+ */
+
+ TABLE *table= open_table_uncached(thd, path, db, table_name, TRUE);
+
+ if (!table)
{
(void) rm_temporary_table(create_info->db_type, path);
- goto unlock_and_end;
+ goto err;
}
+
+ if (is_trans != NULL)
+ *is_trans= table->file->has_transactions();
+
thd->thread_specific_used= TRUE;
}
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ else if (part_info && create_info->frm_only)
+ {
+ /*
+ For partitioned tables we can't find some problems with table
+ until table is opened. Therefore in order to disallow creation
+ of corrupted tables we have to try to open table as the part
+ of its creation process.
+ In cases when both .FRM and SE part of table are created table
+ is implicitly open in ha_create_table() call.
+ In cases when we create .FRM without SE part we have to open
+ table explicitly.
+ */
+ if (check_if_created_table_can_be_opened(thd, path, db, table_name,
+ create_info, file))
+ {
+ char frm_name[FN_REFLEN];
+ strxmov(frm_name, path, reg_ext, NullS);
+ (void) mysql_file_delete(key_file_frm, frm_name, MYF(0));
+ goto err;
+ }
+ }
+#endif
- error= write_create_table_bin_log(thd, create_info, internal_tmp_table);
-unlock_and_end:
- VOID(pthread_mutex_unlock(&LOCK_open));
-
+ error= FALSE;
err:
thd_proc_info(thd, "After create");
delete file;
@@ -4164,86 +4553,58 @@ warn:
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
alias);
- create_info->table_existed= 1; // Mark that table existed
- error= write_create_table_bin_log(thd, create_info, internal_tmp_table);
- goto unlock_and_end;
+ goto err;
}
-/*
- Database and name-locking aware wrapper for mysql_create_table_no_lock(),
+/**
+ Implementation of SQLCOM_CREATE_TABLE.
+
+ Take the metadata locks (including a shared lock on the affected
+ schema) and create the table. Is written to be called from
+ mysql_execute_command(), to which it delegates the common parts
+ with other commands (i.e. implicit commit before and after,
+ close of thread tables.
*/
-bool mysql_create_table(THD *thd, const char *db, const char *table_name,
+bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
HA_CREATE_INFO *create_info,
- Alter_info *alter_info,
- bool internal_tmp_table,
- uint select_field_count)
+ Alter_info *alter_info)
{
- TABLE *name_lock= 0;
bool result;
+ bool is_trans= FALSE;
DBUG_ENTER("mysql_create_table");
- /* Wait for any database locks */
- pthread_mutex_lock(&LOCK_lock_db);
- while (!thd->killed &&
- hash_search(&lock_db_cache,(uchar*) db, strlen(db)))
+ /*
+ Open or obtain an exclusive metadata lock on table being created.
+ */
+ if (open_and_lock_tables(thd, thd->lex->query_tables, FALSE, 0))
{
- wait_for_condition(thd, &LOCK_lock_db, &COND_refresh);
- pthread_mutex_lock(&LOCK_lock_db);
+ /* is_error() may be 0 if table existed and we generated a warning */
+ result= thd->is_error();
+ goto end;
}
- if (thd->killed)
- {
- pthread_mutex_unlock(&LOCK_lock_db);
- DBUG_RETURN(TRUE);
- }
- creating_table++;
- pthread_mutex_unlock(&LOCK_lock_db);
+ /* Got lock. */
+ DEBUG_SYNC(thd, "locked_table_name");
- if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
- {
- if (lock_table_name_if_not_cached(thd, db, table_name, &name_lock))
- {
- result= TRUE;
- goto unlock;
- }
- if (!name_lock)
- {
- if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
- {
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
- table_name);
- create_info->table_existed= 1;
- result= FALSE;
- write_create_table_bin_log(thd, create_info, internal_tmp_table);
- }
- else
- {
- my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
- result= TRUE;
- }
- goto unlock;
- }
- }
+ result= mysql_create_table_no_lock(thd, create_table->db,
+ create_table->table_name, create_info,
+ alter_info, FALSE, 0, &is_trans);
- result= mysql_create_table_no_lock(thd, db, table_name, create_info,
- alter_info,
- internal_tmp_table,
- select_field_count);
+ /*
+ Don't write statement if:
+ - Table creation has failed
+ - Row-based logging is used and we are creating a temporary table
+ Otherwise, the statement shall be binlogged.
+ */
+ if (!result &&
+ (!thd->is_current_stmt_binlog_format_row() ||
+ (thd->is_current_stmt_binlog_format_row() &&
+ !(create_info->options & HA_LEX_CREATE_TMP_TABLE))))
+ result= write_bin_log(thd, TRUE, thd->query(), thd->query_length(), is_trans);
-unlock:
- if (name_lock)
- {
- pthread_mutex_lock(&LOCK_open);
- unlink_open_table(thd, name_lock, FALSE);
- pthread_mutex_unlock(&LOCK_open);
- }
- pthread_mutex_lock(&LOCK_lock_db);
- if (!--creating_table && creating_database)
- pthread_cond_signal(&COND_refresh);
- pthread_mutex_unlock(&LOCK_lock_db);
+end:
DBUG_RETURN(result);
}
@@ -4307,6 +4668,8 @@ make_unique_key_name(const char *field_name,KEY *start,KEY *end)
FN_TO_IS_TMP new_name is temporary.
NO_FRM_RENAME Don't rename the FRM file
but only the table in the storage engine.
+ NO_FK_CHECKS Don't check FK constraints
+ during rename.
RETURN
FALSE OK
@@ -4325,9 +4688,14 @@ mysql_rename_table(handlerton *base, const char *old_db,
char tmp_name[SAFE_NAME_LEN+1];
handler *file;
int error=0;
+ ulonglong save_bits= thd->variables.option_bits;
DBUG_ENTER("mysql_rename_table");
DBUG_PRINT("enter", ("old: '%s'.'%s' new: '%s'.'%s'",
old_db, old_name, new_db, new_name));
+
+ // Temporarily disable foreign key checks
+ if (flags & NO_FK_CHECKS)
+ thd->variables.option_bits|= OPTION_NO_FOREIGN_KEY_CHECKS;
file= (base == NULL ? 0 :
get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base));
@@ -4373,1053 +4741,13 @@ mysql_rename_table(handlerton *base, const char *old_db,
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "ALTER TABLE");
else if (error)
my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error);
- DBUG_RETURN(error != 0);
-}
-
-
-/*
- Force all other threads to stop using the table
-
- SYNOPSIS
- wait_while_table_is_used()
- thd Thread handler
- table Table to remove from cache
- function HA_EXTRA_PREPARE_FOR_DROP if table is to be deleted
- HA_EXTRA_FORCE_REOPEN if table is not be used
- HA_EXTRA_PREPARE_FOR_RENAME if table is to be renamed
- NOTES
- When returning, the table will be unusable for other threads until
- the table is closed.
-
- PREREQUISITES
- Lock on LOCK_open
- Win32 clients must also have a WRITE LOCK on the table !
-*/
-
-void wait_while_table_is_used(THD *thd,TABLE *table,
- enum ha_extra_function function)
-{
- DBUG_ENTER("wait_while_table_is_used");
- DBUG_PRINT("enter", ("table: '%s' share: 0x%lx db_stat: %u version: %lu",
- table->s->table_name.str, (ulong) table->s,
- table->db_stat, table->s->version));
-
- safe_mutex_assert_owner(&LOCK_open);
+ else if (!(flags & FN_IS_TMP))
+ mysql_audit_rename_table(thd, old_db, old_name, new_db, new_name);
- /* Mark all tables that are in use as 'old' */
- mysql_lock_abort(thd, table, TRUE); /* end threads waiting on lock */
-
- /* Wait until all there are no other threads that has this table open */
- remove_table_from_cache(thd, table->s->db.str,
- table->s->table_name.str,
- RTFC_WAIT_OTHER_THREAD_FLAG, FALSE);
- /* extra() call must come only after all instances above are closed */
- if (function != HA_EXTRA_NOT_USED)
- VOID(table->file->extra(function));
- DBUG_VOID_RETURN;
-}
-
-/*
- Close a cached table
-
- SYNOPSIS
- close_cached_table()
- thd Thread handler
- table Table to remove from cache
-
- NOTES
- Function ends by signaling threads waiting for the table to try to
- reopen the table.
-
- PREREQUISITES
- Lock on LOCK_open
- Win32 clients must also have a WRITE LOCK on the table !
-*/
-
-void close_cached_table(THD *thd, TABLE *table)
-{
- DBUG_ENTER("close_cached_table");
-
- wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
- /* Close lock if this is not got with LOCK TABLES */
- if (thd->lock)
- {
- mysql_unlock_tables(thd, thd->lock);
- thd->lock=0; // Start locked threads
- }
- /* Close all copies of 'table'. This also frees all LOCK TABLES lock */
- unlink_open_table(thd, table, TRUE);
-
- /* When lock on LOCK_open is freed other threads can continue */
- broadcast_refresh();
- DBUG_VOID_RETURN;
-}
-
-static int send_check_errmsg(THD *thd, TABLE_LIST* table,
- const char* operator_name, const char* errmsg)
-
-{
- Protocol *protocol= thd->protocol;
- protocol->prepare_for_resend();
- protocol->store(table->alias, 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);
- thd->clear_error();
- if (protocol->write())
- return -1;
- return 1;
-}
-
-
-static int prepare_for_restore(THD* thd, TABLE_LIST* table,
- HA_CHECK_OPT *check_opt)
-{
- DBUG_ENTER("prepare_for_restore");
-
- if (table->table) // do not overwrite existing tables on restore
- {
- DBUG_RETURN(send_check_errmsg(thd, table, "restore",
- "table exists, will not overwrite on restore"
- ));
- }
- else
- {
- char* backup_dir= thd->lex->backup_dir;
- char src_path[FN_REFLEN], dst_path[FN_REFLEN + 1], uname[FN_REFLEN];
- char* table_name= table->table_name;
- char* db= table->db;
-
- VOID(tablename_to_filename(table->table_name, uname, sizeof(uname) - 1));
-
- if (fn_format_relative_to_data_home(src_path, uname, backup_dir, reg_ext))
- DBUG_RETURN(-1); // protect buffer overflow
-
- build_table_filename(dst_path, sizeof(dst_path) - 1,
- db, table_name, reg_ext, 0);
-
- if (lock_and_wait_for_table_name(thd,table))
- DBUG_RETURN(-1);
-
- if (my_copy(src_path, dst_path, MYF(MY_WME)))
- {
- pthread_mutex_lock(&LOCK_open);
- unlock_table_name(thd, table);
- pthread_mutex_unlock(&LOCK_open);
- DBUG_RETURN(send_check_errmsg(thd, table, "restore",
- "Failed copying .frm file"));
- }
- if (mysql_truncate(thd, table, 1))
- {
- pthread_mutex_lock(&LOCK_open);
- unlock_table_name(thd, table);
- pthread_mutex_unlock(&LOCK_open);
- DBUG_RETURN(send_check_errmsg(thd, table, "restore",
- "Failed generating table from .frm file"));
- }
- }
-
- /*
- Now we should be able to open the partially restored table
- to finish the restore in the handler later on
- */
- pthread_mutex_lock(&LOCK_open);
- if (reopen_name_locked_table(thd, table, TRUE))
- {
- unlock_table_name(thd, table);
- pthread_mutex_unlock(&LOCK_open);
- DBUG_RETURN(send_check_errmsg(thd, table, "restore",
- "Failed to open partially restored table"));
- }
- /* A MERGE table must not come here. */
- DBUG_ASSERT(!table->table || !table->table->child_l);
- pthread_mutex_unlock(&LOCK_open);
- DBUG_RETURN(0);
-}
-
-
-static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
- HA_CHECK_OPT *check_opt)
-{
- int error= 0;
- TABLE tmp_table, *table;
- TABLE_SHARE *share;
- char from[FN_REFLEN],tmp[FN_REFLEN+32];
- const char **ext;
- MY_STAT stat_info;
- DBUG_ENTER("prepare_for_repair");
-
- if (!(check_opt->sql_flags & TT_USEFRM))
- DBUG_RETURN(0);
-
- if (!(table= table_list->table)) /* if open_ltable failed */
- {
- char key[MAX_DBKEY_LENGTH];
- uint key_length;
-
- key_length= create_table_def_key(thd, key, table_list, 0);
- pthread_mutex_lock(&LOCK_open);
- if (!(share= (get_table_share(thd, table_list, key, key_length, 0,
- &error))))
- {
- pthread_mutex_unlock(&LOCK_open);
- DBUG_RETURN(0); // Can't open frm file
- }
-
- if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE))
- {
- release_table_share(share, RELEASE_NORMAL);
- pthread_mutex_unlock(&LOCK_open);
- DBUG_RETURN(0); // Out of memory
- }
- table= &tmp_table;
- pthread_mutex_unlock(&LOCK_open);
- }
-
- /*
- REPAIR TABLE ... USE_FRM for temporary tables makes little sense.
- */
- if (table->s->tmp_table)
- {
- error= send_check_errmsg(thd, table_list, "repair",
- "Cannot repair temporary table from .frm file");
- goto end;
- }
-
- /*
- User gave us USE_FRM which means that the header in the index file is
- trashed.
- In this case we will try to fix the table the following way:
- - Rename the data file to a temporary name
- - Truncate the table
- - Replace the new data file with the old one
- - Run a normal repair using the new index file and the old data file
- */
-
- if (table->s->frm_version != FRM_VER_TRUE_VARCHAR)
- {
- error= send_check_errmsg(thd, table_list, "repair",
- "Failed repairing incompatible .frm file");
- goto end;
- }
-
- /*
- 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
- is meta/index file extention. Second element - data file extention.
- */
- ext= table->file->bas_ext();
- if (!ext[0] || !ext[1])
- goto end; // No data file
-
- // Name of data file
- strxmov(from, table->s->normalized_path.str, ext[1], NullS);
- if (!my_stat(from, &stat_info, MYF(0)))
- goto end; // Can't use USE_FRM flag
-
- my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
- from, current_pid, thd->thread_id);
-
- /* If we could open the table, close it */
- if (table_list->table)
- {
- pthread_mutex_lock(&LOCK_open);
- close_cached_table(thd, table);
- pthread_mutex_unlock(&LOCK_open);
- }
- if (lock_and_wait_for_table_name(thd,table_list))
- {
- error= -1;
- goto end;
- }
- if (my_rename(from, tmp, MYF(MY_WME)))
- {
- pthread_mutex_lock(&LOCK_open);
- unlock_table_name(thd, table_list);
- pthread_mutex_unlock(&LOCK_open);
- error= send_check_errmsg(thd, table_list, "repair",
- "Failed renaming data file");
- goto end;
- }
- if (mysql_truncate(thd, table_list, 1))
- {
- pthread_mutex_lock(&LOCK_open);
- unlock_table_name(thd, table_list);
- pthread_mutex_unlock(&LOCK_open);
- error= send_check_errmsg(thd, table_list, "repair",
- "Failed generating table from .frm file");
- goto end;
- }
- if (my_rename(tmp, from, MYF(MY_WME)))
- {
- pthread_mutex_lock(&LOCK_open);
- unlock_table_name(thd, table_list);
- pthread_mutex_unlock(&LOCK_open);
- error= send_check_errmsg(thd, table_list, "repair",
- "Failed restoring .MYD file");
- goto end;
- }
-
- /*
- Now we should be able to open the partially repaired table
- to finish the repair in the handler later on.
- */
- pthread_mutex_lock(&LOCK_open);
- if (reopen_name_locked_table(thd, table_list, TRUE))
- {
- unlock_table_name(thd, table_list);
- pthread_mutex_unlock(&LOCK_open);
- error= send_check_errmsg(thd, table_list, "repair",
- "Failed to open partially repaired table");
- goto end;
- }
- pthread_mutex_unlock(&LOCK_open);
-
-end:
- if (table == &tmp_table)
- {
- pthread_mutex_lock(&LOCK_open);
- closefrm(table, 1); // Free allocated memory
- pthread_mutex_unlock(&LOCK_open);
- }
- DBUG_RETURN(error);
-}
-
+ // Restore options bits to the original value
+ thd->variables.option_bits= save_bits;
-
-/*
- RETURN VALUES
- FALSE Message sent to net (admin operation went ok)
- TRUE Message should be sent by caller
- (admin operation or network communication failed)
-*/
-static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
- HA_CHECK_OPT* check_opt,
- const char *operator_name,
- thr_lock_type lock_type,
- bool open_for_modify,
- bool no_warnings_for_error,
- uint extra_open_options,
- int (*prepare_func)(THD *, TABLE_LIST *,
- HA_CHECK_OPT *),
- int (handler::*operator_func)(THD *,
- HA_CHECK_OPT *),
- int (view_operator_func)(THD *, TABLE_LIST*))
-{
- TABLE_LIST *table;
- SELECT_LEX *select= &thd->lex->select_lex;
- List<Item> field_list;
- Item *item;
- Protocol *protocol= thd->protocol;
- LEX *lex= thd->lex;
- int result_code;
- bool need_repair_or_alter= 0;
- DBUG_ENTER("mysql_admin_table");
- DBUG_PRINT("enter", ("extra_open_options: %u", extra_open_options));
-
- if (end_active_trans(thd))
- DBUG_RETURN(1);
- field_list.push_back(item = new Item_empty_string("Table", NAME_CHAR_LEN*2));
- item->maybe_null = 1;
- field_list.push_back(item = new Item_empty_string("Op", 10));
- item->maybe_null = 1;
- field_list.push_back(item = new Item_empty_string("Msg_type", 10));
- item->maybe_null = 1;
- field_list.push_back(item = new Item_empty_string("Msg_text", 255));
- item->maybe_null = 1;
- if (protocol->send_fields(&field_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
- DBUG_RETURN(TRUE);
-
- mysql_ha_rm_tables(thd, tables, FALSE);
-
- for (table= tables; table; table= table->next_local)
- {
- char table_name[SAFE_NAME_LEN*2+2];
- char* db = table->db;
- bool fatal_error=0;
-
- DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name));
- strxmov(table_name, db, ".", table->table_name, NullS);
- table->lock_type= lock_type;
- /* open only one table from local list of command */
- {
- TABLE_LIST *save_next_global, *save_next_local;
- save_next_global= table->next_global;
- table->next_global= 0;
- save_next_local= table->next_local;
- table->next_local= 0;
- select->table_list.first= table;
- /*
- Time zone tables and SP tables can be add to lex->query_tables list,
- so it have to be prepared.
- TODO: Investigate if we can put extra tables into argument instead of
- using lex->query_tables
- */
- lex->query_tables= table;
- lex->query_tables_last= &table->next_global;
- lex->query_tables_own_last= 0;
- thd->no_warnings_for_error= no_warnings_for_error;
- if (view_operator_func == NULL)
- table->required_type=FRMTYPE_TABLE;
- if (lex->sql_command == SQLCOM_CHECK ||
- lex->sql_command == SQLCOM_REPAIR ||
- lex->sql_command == SQLCOM_ANALYZE ||
- lex->sql_command == SQLCOM_OPTIMIZE)
- thd->prepare_derived_at_open= TRUE;
- thd->open_options|= extra_open_options;
- open_and_lock_tables(thd, table);
- thd->open_options&= ~extra_open_options;
- thd->prepare_derived_at_open= FALSE;
- thd->no_warnings_for_error= 0;
- table->next_global= save_next_global;
- table->next_local= save_next_local;
-#ifdef WITH_PARTITION_STORAGE_ENGINE
- if (table->table)
- {
- /*
- Set up which partitions that should be processed
- if ALTER TABLE t ANALYZE/CHECK/OPTIMIZE/REPAIR PARTITION ..
- */
- Alter_info *alter_info= &lex->alter_info;
-
- if (alter_info->flags & ALTER_ADMIN_PARTITION)
- {
- if (!table->table->part_info)
- {
- my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
- DBUG_RETURN(TRUE);
- }
- uint no_parts_found;
- uint no_parts_opt= alter_info->partition_names.elements;
- no_parts_found= set_part_state(alter_info, table->table->part_info,
- PART_CHANGED);
- if (no_parts_found != no_parts_opt &&
- (!(alter_info->flags & ALTER_ALL_PARTITION)))
- {
- char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
- size_t length;
- DBUG_PRINT("admin", ("sending non existent partition error"));
- protocol->prepare_for_resend();
- protocol->store(table_name, system_charset_info);
- protocol->store(operator_name, system_charset_info);
- protocol->store(STRING_WITH_LEN("error"), system_charset_info);
- length= my_snprintf(buff, sizeof(buff),
- ER(ER_DROP_PARTITION_NON_EXISTENT),
- table_name);
- protocol->store(buff, length, system_charset_info);
- if(protocol->write())
- goto err;
- my_eof(thd);
- goto err;
- }
- }
- }
-#endif
- }
- DBUG_PRINT("admin", ("table: 0x%lx", (long) table->table));
-
- if (prepare_func)
- {
- DBUG_PRINT("admin", ("calling prepare_func"));
- switch ((*prepare_func)(thd, table, check_opt)) {
- case 1: // error, message written to net
- ha_autocommit_or_rollback(thd, 1);
- end_trans(thd, ROLLBACK);
- close_thread_tables(thd);
- DBUG_PRINT("admin", ("simple error, admin next table"));
- continue;
- case -1: // error, message could be written to net
- /* purecov: begin inspected */
- DBUG_PRINT("admin", ("severe error, stop"));
- goto err;
- /* purecov: end */
- default: // should be 0 otherwise
- DBUG_PRINT("admin", ("prepare_func succeeded"));
- ;
- }
- }
-
- /*
- CHECK TABLE command is only command where VIEW allowed here and this
- command use only temporary teble method for VIEWs resolving => there
- can't be VIEW tree substitition of join view => if opening table
- succeed then table->table will have real TABLE pointer as value (in
- case of join view substitution table->table can be 0, but here it is
- impossible)
- */
- if (!table->table)
- {
- DBUG_PRINT("admin", ("open table failed"));
- if (!thd->warn_list.elements)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
- ER_CHECK_NO_SUCH_TABLE, ER(ER_CHECK_NO_SUCH_TABLE));
- /* if it was a view will check md5 sum */
- if (table->view &&
- view_checksum(thd, table) == HA_ADMIN_WRONG_CHECKSUM)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
- ER_VIEW_CHECKSUM, ER(ER_VIEW_CHECKSUM));
- if (thd->main_da.is_error() &&
- (thd->main_da.sql_errno() == ER_NO_SUCH_TABLE ||
- thd->main_da.sql_errno() == ER_FILE_NOT_FOUND))
- /* A missing table is just issued as a failed command */
- result_code= HA_ADMIN_FAILED;
- else
- /* Default failure code is corrupt table */
- result_code= HA_ADMIN_CORRUPT;
- goto send_result;
- }
-
- if (table->view)
- {
- DBUG_PRINT("admin", ("calling view_operator_func"));
- result_code= (*view_operator_func)(thd, table);
- goto send_result;
- }
-
- if (table->schema_table)
- {
- result_code= HA_ADMIN_NOT_IMPLEMENTED;
- goto send_result;
- }
-
- if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
- {
- /* purecov: begin inspected */
- char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
- size_t length;
- DBUG_PRINT("admin", ("sending error message"));
- protocol->prepare_for_resend();
- protocol->store(table_name, system_charset_info);
- protocol->store(operator_name, system_charset_info);
- protocol->store(STRING_WITH_LEN("error"), system_charset_info);
- length= my_snprintf(buff, sizeof(buff), ER(ER_OPEN_AS_READONLY),
- table_name);
- protocol->store(buff, length, system_charset_info);
- ha_autocommit_or_rollback(thd, 0);
- end_trans(thd, COMMIT);
- close_thread_tables(thd);
- lex->reset_query_tables_list(FALSE);
- table->table=0; // For query cache
- if (protocol->write())
- goto err;
- thd->main_da.reset_diagnostics_area();
- continue;
- /* purecov: end */
- }
-
- /* Close all instances of the table to allow repair to rename files */
- if (lock_type == TL_WRITE && table->table->s->version)
- {
- DBUG_PRINT("admin", ("removing table from cache"));
- pthread_mutex_lock(&LOCK_open);
- const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
- "Waiting to get writelock");
- mysql_lock_abort(thd,table->table, TRUE);
- remove_table_from_cache(thd, table->table->s->db.str,
- table->table->s->table_name.str,
- RTFC_WAIT_OTHER_THREAD_FLAG |
- RTFC_CHECK_KILLED_FLAG, FALSE);
- thd->exit_cond(old_message);
- DBUG_EXECUTE_IF("wait_in_mysql_admin_table", wait_for_kill_signal(thd););
- if (thd->killed)
- goto err;
- /* Flush entries in the query cache involving this table. */
- query_cache_invalidate3(thd, table->table, 0);
- open_for_modify= 0;
- }
-
- if (table->table->s->crashed && operator_func == &handler::ha_check)
- {
- /* purecov: begin inspected */
- DBUG_PRINT("admin", ("sending crashed warning"));
- protocol->prepare_for_resend();
- protocol->store(table_name, system_charset_info);
- protocol->store(operator_name, system_charset_info);
- protocol->store(STRING_WITH_LEN("warning"), system_charset_info);
- protocol->store(STRING_WITH_LEN("Table is marked as crashed"),
- system_charset_info);
- if (protocol->write())
- goto err;
- /* purecov: end */
- }
-
- if (operator_func == &handler::ha_repair &&
- !(check_opt->sql_flags & TT_USEFRM))
- {
- handler *file= table->table->file;
- int check_old_types= file->check_old_types();
- int check_for_upgrade= file->ha_check_for_upgrade(check_opt);
-
- if (check_old_types == HA_ADMIN_NEEDS_ALTER ||
- check_for_upgrade == HA_ADMIN_NEEDS_ALTER)
- {
- /* We use extra_open_options to be able to open crashed tables */
- thd->open_options|= extra_open_options;
- result_code= admin_recreate_table(thd, table);
- thd->open_options&= ~extra_open_options;
- goto send_result;
- }
- if (check_old_types || check_for_upgrade)
- {
- /* If repair is not implemented for the engine, run ALTER TABLE */
- need_repair_or_alter= 1;
- }
- }
-
- DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name));
- result_code = (table->table->file->*operator_func)(thd, check_opt);
- DBUG_PRINT("admin", ("operator_func returned: %d", result_code));
-
- if (result_code == HA_ADMIN_NOT_IMPLEMENTED && need_repair_or_alter)
- {
- /*
- repair was not implemented and we need to upgrade the table
- to a new version so we recreate the table with ALTER TABLE
- */
- result_code= admin_recreate_table(thd, table);
- }
-send_result:
-
- lex->cleanup_after_one_table_open();
- thd->clear_error(); // these errors shouldn't get client
- {
- List_iterator_fast<MYSQL_ERROR> it(thd->warn_list);
- MYSQL_ERROR *err;
- while ((err= it++))
- {
- protocol->prepare_for_resend();
- protocol->store(table_name, system_charset_info);
- protocol->store((char*) operator_name, system_charset_info);
- protocol->store(warning_level_names[err->level].str,
- warning_level_names[err->level].length,
- system_charset_info);
- protocol->store(err->msg, system_charset_info);
- if (protocol->write())
- goto err;
- }
- mysql_reset_errors(thd, true);
- }
- protocol->prepare_for_resend();
- protocol->store(table_name, system_charset_info);
- protocol->store(operator_name, system_charset_info);
-
-send_result_message:
-
- DBUG_PRINT("info", ("result_code: %d", result_code));
- switch (result_code) {
- case HA_ADMIN_NOT_IMPLEMENTED:
- {
- char buf[MYSQL_ERRMSG_SIZE];
- size_t length=my_snprintf(buf, sizeof(buf),
- ER(ER_CHECK_NOT_IMPLEMENTED), operator_name);
- protocol->store(STRING_WITH_LEN("note"), system_charset_info);
- protocol->store(buf, length, system_charset_info);
- }
- break;
-
- case HA_ADMIN_NOT_BASE_TABLE:
- {
- char buf[MYSQL_ERRMSG_SIZE];
- size_t length= my_snprintf(buf, sizeof(buf),
- ER(ER_BAD_TABLE_ERROR), table_name);
- protocol->store(STRING_WITH_LEN("note"), system_charset_info);
- protocol->store(buf, length, system_charset_info);
- }
- break;
-
- case HA_ADMIN_OK:
- protocol->store(STRING_WITH_LEN("status"), system_charset_info);
- protocol->store(STRING_WITH_LEN("OK"), system_charset_info);
- break;
-
- case HA_ADMIN_FAILED:
- protocol->store(STRING_WITH_LEN("status"), system_charset_info);
- protocol->store(STRING_WITH_LEN("Operation failed"),
- system_charset_info);
- break;
-
- case HA_ADMIN_REJECT:
- protocol->store(STRING_WITH_LEN("status"), system_charset_info);
- protocol->store(STRING_WITH_LEN("Operation need committed state"),
- system_charset_info);
- open_for_modify= FALSE;
- break;
-
- case HA_ADMIN_ALREADY_DONE:
- protocol->store(STRING_WITH_LEN("status"), system_charset_info);
- protocol->store(STRING_WITH_LEN("Table is already up to date"),
- system_charset_info);
- break;
-
- case HA_ADMIN_CORRUPT:
- protocol->store(STRING_WITH_LEN("error"), system_charset_info);
- protocol->store(STRING_WITH_LEN("Corrupt"), system_charset_info);
- fatal_error=1;
- break;
-
- case HA_ADMIN_INVALID:
- protocol->store(STRING_WITH_LEN("error"), system_charset_info);
- protocol->store(STRING_WITH_LEN("Invalid argument"),
- system_charset_info);
- break;
-
- case HA_ADMIN_TRY_ALTER:
- {
- uint save_flags;
- Alter_info *alter_info= &lex->alter_info;
-
- /* Store the original value of alter_info->flags */
- save_flags= alter_info->flags;
- /*
- This is currently used only by InnoDB. ha_innobase::optimize() answers
- "try with alter", so here we close the table, do an ALTER TABLE,
- reopen the table and do ha_innobase::analyze() on it.
- We have to end the row, so analyze could return more rows.
- */
- protocol->store(STRING_WITH_LEN("note"), system_charset_info);
- if(alter_info->flags & ALTER_ADMIN_PARTITION)
- {
- protocol->store(STRING_WITH_LEN(
- "Table does not support optimize on partitions. All partitions "
- "will be rebuilt and analyzed."),system_charset_info);
- }
- else
- {
- protocol->store(STRING_WITH_LEN(
- "Table does not support optimize, doing recreate + analyze instead"),
- system_charset_info);
- }
- if (protocol->write())
- goto err;
- DBUG_PRINT("info", ("HA_ADMIN_TRY_ALTER, trying analyze..."));
- TABLE_LIST *save_next_local= table->next_local,
- *save_next_global= table->next_global;
- table->next_local= table->next_global= 0;
-
- result_code= admin_recreate_table(thd, table);
-
- ha_autocommit_or_rollback(thd, 0);
- close_thread_tables(thd);
- if (!result_code) // recreation went ok
- {
- /*
- Reset the ALTER_ADMIN_PARTITION bit in alter_info->flags
- to force analyze on all partitions.
- */
- alter_info->flags &= ~(ALTER_ADMIN_PARTITION);
- if ((table->table= open_ltable(thd, table, lock_type, 0)) &&
- ((result_code= table->table->file->ha_analyze(thd, check_opt)) > 0))
- result_code= 0; // analyze went ok
- alter_info->flags= save_flags;
- }
- /* Start a new row for the final status row */
- protocol->prepare_for_resend();
- protocol->store(table_name, system_charset_info);
- protocol->store(operator_name, system_charset_info);
- if (result_code) // either mysql_recreate_table or analyze failed
- {
- DBUG_ASSERT(thd->is_error());
- if (thd->is_error())
- {
- const char *err_msg= thd->main_da.message();
- if (!thd->vio_ok())
- {
- sql_print_error("%s", err_msg);
- }
- else
- {
- /* Hijack the row already in-progress. */
- protocol->store(STRING_WITH_LEN("error"), system_charset_info);
- protocol->store(err_msg, system_charset_info);
- if (protocol->write())
- goto err;
- /* Start off another row for HA_ADMIN_FAILED */
- protocol->prepare_for_resend();
- protocol->store(table_name, system_charset_info);
- protocol->store(operator_name, system_charset_info);
- }
- thd->clear_error();
- }
- }
- result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
- table->next_local= save_next_local;
- table->next_global= save_next_global;
- goto send_result_message;
- }
- case HA_ADMIN_WRONG_CHECKSUM:
- {
- protocol->store(STRING_WITH_LEN("note"), system_charset_info);
- protocol->store(ER(ER_VIEW_CHECKSUM), strlen(ER(ER_VIEW_CHECKSUM)),
- system_charset_info);
- break;
- }
-
- case HA_ADMIN_NEEDS_UPGRADE:
- case HA_ADMIN_NEEDS_ALTER:
- {
- char buf[MYSQL_ERRMSG_SIZE];
- size_t length;
-
- protocol->store(STRING_WITH_LEN("error"), system_charset_info);
- length=my_snprintf(buf, sizeof(buf), ER(ER_TABLE_NEEDS_UPGRADE),
- table->table_name);
- protocol->store(buf, length, system_charset_info);
- fatal_error=1;
- break;
- }
-
- default: // Probably HA_ADMIN_INTERNAL_ERROR
- {
- char buf[MYSQL_ERRMSG_SIZE];
- size_t length=my_snprintf(buf, sizeof(buf),
- "Unknown - internal error %d during operation",
- result_code);
- protocol->store(STRING_WITH_LEN("error"), system_charset_info);
- protocol->store(buf, length, system_charset_info);
- fatal_error=1;
- break;
- }
- }
- if (table->table)
- {
- if (fatal_error)
- table->table->s->version=0; // Force close of table
- else if (open_for_modify)
- {
- if (table->table->s->tmp_table)
- table->table->file->info(HA_STATUS_CONST);
- else
- {
- pthread_mutex_lock(&LOCK_open);
- remove_table_from_cache(thd, table->table->s->db.str,
- table->table->s->table_name.str,
- RTFC_NO_FLAG, FALSE);
- pthread_mutex_unlock(&LOCK_open);
- }
- /* May be something modified consequently we have to invalidate cache */
- query_cache_invalidate3(thd, table->table, 0);
- }
- }
- ha_autocommit_or_rollback(thd, 0);
- end_trans(thd, COMMIT);
- close_thread_tables(thd);
- table->table=0; // For query cache
- if (protocol->write())
- goto err;
- }
-
- my_eof(thd);
- DBUG_RETURN(FALSE);
-
-err:
- ha_autocommit_or_rollback(thd, 1);
- end_trans(thd, ROLLBACK);
- close_thread_tables(thd); // Shouldn't be needed
- if (table)
- table->table=0;
- DBUG_RETURN(TRUE);
-}
-
-
-bool mysql_backup_table(THD* thd, TABLE_LIST* table_list)
-{
- DBUG_ENTER("mysql_backup_table");
- WARN_DEPRECATED(thd, "6.0", "BACKUP TABLE",
- "MySQL Administrator (mysqldump, mysql)");
- DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
- "backup", TL_READ, 0, 0, 0, 0,
- &handler::ha_backup, 0));
-}
-
-
-bool mysql_restore_table(THD* thd, TABLE_LIST* table_list)
-{
- DBUG_ENTER("mysql_restore_table");
- WARN_DEPRECATED(thd, "6.0", "RESTORE TABLE",
- "MySQL Administrator (mysqldump, mysql)");
- DBUG_RETURN(mysql_admin_table(thd, table_list, 0,
- "restore", TL_WRITE, 1, 1, 0,
- &prepare_for_restore,
- &handler::ha_restore, 0));
-}
-
-
-bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
-{
- DBUG_ENTER("mysql_repair_table");
- DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
- "repair", TL_WRITE, 1,
- test(check_opt->sql_flags & TT_USEFRM),
- HA_OPEN_FOR_REPAIR,
- &prepare_for_repair,
- &handler::ha_repair, 0));
-}
-
-
-bool mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
-{
- DBUG_ENTER("mysql_optimize_table");
- DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
- "optimize", TL_WRITE, 1,0,0,0,
- &handler::ha_optimize, 0));
-}
-
-
-/*
- Assigned specified indexes for a table into key cache
-
- SYNOPSIS
- mysql_assign_to_keycache()
- thd Thread object
- tables Table list (one table only)
-
- RETURN VALUES
- FALSE ok
- TRUE error
-*/
-
-bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
- LEX_STRING *key_cache_name)
-{
- HA_CHECK_OPT check_opt;
- KEY_CACHE *key_cache;
- DBUG_ENTER("mysql_assign_to_keycache");
-
- check_opt.init();
- pthread_mutex_lock(&LOCK_global_system_variables);
- if (!(key_cache= get_key_cache(key_cache_name)))
- {
- pthread_mutex_unlock(&LOCK_global_system_variables);
- my_error(ER_UNKNOWN_KEY_CACHE, MYF(0), key_cache_name->str);
- DBUG_RETURN(TRUE);
- }
- pthread_mutex_unlock(&LOCK_global_system_variables);
- if (!key_cache->key_cache_inited)
- {
- my_error(ER_UNKNOWN_KEY_CACHE, MYF(0), key_cache_name->str);
- DBUG_RETURN(TRUE);
- }
- check_opt.key_cache= key_cache;
- DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
- "assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
- 0, 0, &handler::assign_to_keycache, 0));
-}
-
-
-/*
- Reassign all tables assigned to a key cache to another key cache
-
- SYNOPSIS
- reassign_keycache_tables()
- thd Thread object
- src_cache Reference to the key cache to clean up
- dest_cache New key cache
-
- NOTES
- This is called when one sets a key cache size to zero, in which
- case we have to move the tables associated to this key cache to
- the "default" one.
-
- One has to ensure that one never calls this function while
- some other thread is changing the key cache. This is assured by
- the caller setting src_cache->in_init before calling this function.
-
- We don't delete the old key cache as there may still be pointers pointing
- to it for a while after this function returns.
-
- RETURN VALUES
- 0 ok
-*/
-
-int reassign_keycache_tables(THD *thd, KEY_CACHE *src_cache,
- KEY_CACHE *dst_cache)
-{
- DBUG_ENTER("reassign_keycache_tables");
-
- DBUG_ASSERT(src_cache != dst_cache);
- DBUG_ASSERT(src_cache->in_init);
- src_cache->param_buff_size= 0; // Free key cache
- ha_resize_key_cache(src_cache);
- ha_change_key_cache(src_cache, dst_cache);
- DBUG_RETURN(0);
-}
-
-
-/*
- Preload specified indexes for a table into key cache
-
- SYNOPSIS
- mysql_preload_keys()
- thd Thread object
- tables Table list (one table only)
-
- RETURN VALUES
- FALSE ok
- TRUE error
-*/
-
-bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
-{
- DBUG_ENTER("mysql_preload_keys");
- /*
- We cannot allow concurrent inserts. The storage engine reads
- directly from the index file, bypassing the cache. It could read
- outdated information if parallel inserts into cache blocks happen.
- */
- DBUG_RETURN(mysql_admin_table(thd, tables, 0,
- "preload_keys", TL_READ_NO_INSERT, 0, 0, 0, 0,
- &handler::preload_keys, 0));
-}
-
-
-
-/**
- @brief Create frm file based on I_S table
-
- @param[in] thd thread handler
- @param[in] schema_table I_S table
- @param[in] dst_path path where frm should be created
- @param[in] create_info Create info
-
- @return Operation status
- @retval 0 success
- @retval 1 error
-*/
-
-
-bool mysql_create_like_schema_frm(THD* thd, TABLE_LIST* schema_table,
- char *dst_path, HA_CREATE_INFO *create_info)
-{
- HA_CREATE_INFO local_create_info;
- Alter_info alter_info;
- bool tmp_table= (create_info->options & HA_LEX_CREATE_TMP_TABLE);
- uint keys= schema_table->table->s->keys;
- uint db_options= 0;
- DBUG_ENTER("mysql_create_like_schema_frm");
-
- bzero((char*) &local_create_info, sizeof(local_create_info));
- local_create_info.db_type= schema_table->table->s->db_type();
- local_create_info.row_type= schema_table->table->s->row_type;
- local_create_info.default_table_charset=default_charset_info;
- alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
- schema_table->table->use_all_columns();
- if (mysql_prepare_alter_table(thd, schema_table->table,
- &local_create_info, &alter_info))
- DBUG_RETURN(1);
- if (mysql_prepare_create_table(thd, &local_create_info, &alter_info,
- tmp_table, &db_options,
- schema_table->table->file,
- &schema_table->table->s->key_info, &keys, 0))
- DBUG_RETURN(1);
- local_create_info.max_rows= 0;
- if (mysql_create_frm(thd, dst_path, NullS, NullS,
- &local_create_info, alter_info.create_list,
- keys, schema_table->table->s->key_info,
- schema_table->table->file))
- DBUG_RETURN(1);
- DBUG_RETURN(0);
+ DBUG_RETURN(error != 0);
}
@@ -5441,185 +4769,89 @@ bool mysql_create_like_schema_frm(THD* thd, TABLE_LIST* schema_table,
bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
HA_CREATE_INFO *create_info)
{
- TABLE *name_lock= 0;
- char src_path[FN_REFLEN], dst_path[FN_REFLEN + 1];
- uint dst_path_length;
- char *db= table->db;
- char *table_name= table->table_name;
- int err;
+ HA_CREATE_INFO local_create_info;
+ Alter_info local_alter_info;
bool res= TRUE;
+ bool is_trans= FALSE;
uint not_used;
-#ifdef WITH_PARTITION_STORAGE_ENGINE
- char tmp_path[FN_REFLEN];
-#endif
- char ts_name[FN_LEN + 1];
- myf flags= MY_DONT_OVERWRITE_FILE;
DBUG_ENTER("mysql_create_like_table");
/*
- By opening source table we guarantee that it exists and no concurrent
- DDL operation will mess with it. Later we also take an exclusive
- name-lock on target table name, which makes copying of .frm file,
- call to ha_create_table() and binlogging atomic against concurrent DML
- and DDL operations on target table. Thus by holding both these "locks"
- we ensure that our statement is properly isolated from all concurrent
- operations which matter.
+ We the open source table to get its description in HA_CREATE_INFO
+ and Alter_info objects. This also acquires a shared metadata lock
+ on this table which ensures that no concurrent DDL operation will
+ mess with it.
+ Also in case when we create non-temporary table open_tables()
+ call obtains an exclusive metadata lock on target table ensuring
+ that we can safely perform table creation.
+ Thus by holding both these locks we ensure that our statement is
+ properly isolated from all concurrent operations which matter.
*/
- if (open_tables(thd, &src_table, &not_used, 0))
- DBUG_RETURN(TRUE);
-
- /*
- For bug#25875, Newly created table through CREATE TABLE .. LIKE
- has no ndb_dd attributes;
- Add something to get possible tablespace info from src table,
- it can get valid tablespace name only for disk-base ndb table
- */
- if ((src_table->table->file->get_tablespace_name(thd, ts_name, FN_LEN)))
+ if (open_tables(thd, &thd->lex->query_tables, &not_used, 0))
{
- create_info->tablespace= ts_name;
- create_info->storage_media= HA_SM_DISK;
+ res= thd->is_error();
+ goto err;
}
+ src_table->table->use_all_columns();
- strxmov(src_path, src_table->table->s->path.str, reg_ext, NullS);
+ DEBUG_SYNC(thd, "create_table_like_after_open");
- DBUG_EXECUTE_IF("sleep_create_like_before_check_if_exists", my_sleep(6000000););
+ /* Fill HA_CREATE_INFO and Alter_info with description of source table. */
+ bzero((char*) &local_create_info, sizeof(local_create_info));
+ local_create_info.db_type= src_table->table->s->db_type();
+ local_create_info.row_type= src_table->table->s->row_type;
+ if (mysql_prepare_alter_table(thd, src_table->table, &local_create_info,
+ &local_alter_info))
+ goto err;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ /* Partition info is not handled by mysql_prepare_alter_table() call. */
+ if (src_table->table->part_info)
+ thd->work_part_info= src_table->table->part_info->get_clone();
+#endif
/*
- Check that destination tables does not exist. Note that its name
- was already checked when it was added to the table list.
- */
- if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
- {
- if (src_table->table->file->ht == partition_hton)
- {
- my_error(ER_PARTITION_NO_TEMPORARY, MYF(0));
- goto err;
- }
- if (find_temporary_table(thd, db, table_name))
- goto table_exists;
- dst_path_length= build_tmptable_filename(thd, dst_path, sizeof(dst_path));
- create_info->table_options|= HA_CREATE_DELAY_KEY_WRITE;
- }
- else
- {
- if (lock_table_name_if_not_cached(thd, db, table_name, &name_lock))
- goto err;
- if (!name_lock)
- goto table_exists;
- dst_path_length= build_table_filename(dst_path, sizeof(dst_path) - 1,
- db, table_name, reg_ext, 0);
- if (!access(dst_path, F_OK))
- goto table_exists;
- }
-
- DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000););
-
- if (opt_sync_frm && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
- flags|= MY_SYNC;
+ Adjust description of source table before using it for creation of
+ target table.
- /*
- Create a new table by copying from source table
- and sync the new table if the flag MY_SYNC is set
-
- Altough exclusive name-lock on target table protects us from concurrent
- DML and DDL operations on it we still want to wrap .FRM creation and call
- to ha_create_table() in critical section protected by LOCK_open in order
- to provide minimal atomicity against operations which disregard name-locks,
- like I_S implementation, for example. This is a temporary and should not
- be copied. Instead we should fix our code to always honor name-locks.
-
- Also some engines (e.g. NDB cluster) require that LOCK_open should be held
- during the call to ha_create_table(). See bug #28614 for more info.
+ Similarly to SHOW CREATE TABLE we ignore MAX_ROWS attribute of
+ temporary table which represents I_S table.
*/
- VOID(pthread_mutex_lock(&LOCK_open));
if (src_table->schema_table)
- {
- if (mysql_create_like_schema_frm(thd, src_table, dst_path, create_info))
- {
- VOID(pthread_mutex_unlock(&LOCK_open));
- goto err;
- }
- }
- else if (my_copy(src_path, dst_path, flags))
- {
- if (my_errno == ENOENT)
- my_error(ER_BAD_DB_ERROR,MYF(0),db);
- else
- my_error(ER_CANT_CREATE_FILE,MYF(0),dst_path,my_errno);
- VOID(pthread_mutex_unlock(&LOCK_open));
- goto err;
- }
-
+ local_create_info.max_rows= 0;
+ /* Set IF NOT EXISTS option as in the CREATE TABLE LIKE statement. */
+ local_create_info.options|= create_info->options&HA_LEX_CREATE_IF_NOT_EXISTS;
+ /* Replace type of source table with one specified in the statement. */
+ local_create_info.options&= ~HA_LEX_CREATE_TMP_TABLE;
+ local_create_info.options|= create_info->options & HA_LEX_CREATE_TMP_TABLE;
+ /* Reset auto-increment counter for the new table. */
+ local_create_info.auto_increment_value= 0;
/*
- As mysql_truncate don't work on a new table at this stage of
- creation, instead create the table directly (for both normal
- and temporary tables).
+ Do not inherit values of DATA and INDEX DIRECTORY options from
+ the original table. This is documented behavior.
*/
-#ifdef WITH_PARTITION_STORAGE_ENGINE
- /*
- For partitioned tables we need to copy the .par file as well since
- it is used in open_table_def to even be able to create a new handler.
- */
- if (src_table->table->file->ht == partition_hton)
- {
- fn_format(tmp_path, dst_path, reg_ext, ".par", MYF(MY_REPLACE_EXT));
- strmov(dst_path, tmp_path);
- fn_format(tmp_path, src_path, reg_ext, ".par", MYF(MY_REPLACE_EXT));
- strmov(src_path, tmp_path);
- my_copy(src_path, dst_path, MYF(MY_DONT_OVERWRITE_FILE));
- }
-#endif
-
- DBUG_EXECUTE_IF("sleep_create_like_before_ha_create", my_sleep(6000000););
-
- dst_path[dst_path_length - reg_ext_length]= '\0'; // Remove .frm
- if (thd->variables.keep_files_on_create)
- create_info->options|= HA_CREATE_KEEP_FILES;
- err= ha_create_table(thd, dst_path, db, table_name, create_info, 1);
- VOID(pthread_mutex_unlock(&LOCK_open));
+ local_create_info.data_file_name= local_create_info.index_file_name= NULL;
- if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
- {
- if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
- {
- (void) rm_temporary_table(create_info->db_type,
- dst_path); /* purecov: inspected */
- goto err; /* purecov: inspected */
- }
- thd->thread_specific_used= TRUE;
- }
- else if (err)
- {
- (void) quick_rm_table(create_info->db_type, db,
- table_name, 0); /* purecov: inspected */
- goto err; /* purecov: inspected */
- }
-
-goto binlog;
-
-table_exists:
- if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
- {
- char warn_buff[MYSQL_ERRMSG_SIZE];
- my_snprintf(warn_buff, sizeof(warn_buff),
- ER(ER_TABLE_EXISTS_ERROR), table_name);
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_TABLE_EXISTS_ERROR,warn_buff);
- }
- else
- {
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
+ if ((res= mysql_create_table_no_lock(thd, table->db, table->table_name,
+ &local_create_info, &local_alter_info,
+ FALSE, 0, &is_trans)))
goto err;
- }
-binlog:
- DBUG_EXECUTE_IF("sleep_create_like_before_binlogging", my_sleep(6000000););
+ /*
+ Ensure that we have an exclusive lock on target table if we are creating
+ non-temporary table.
+ */
+ DBUG_ASSERT((create_info->options & HA_LEX_CREATE_TMP_TABLE) ||
+ thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db,
+ table->table_name,
+ MDL_EXCLUSIVE));
+
+ DEBUG_SYNC(thd, "create_table_like_before_binlog");
/*
We have to write the query before we unlock the tables.
*/
- if (thd->current_stmt_binlog_row_based)
+ if (thd->is_current_stmt_binlog_format_row())
{
/*
Since temporary tables are not replicated under row-based
@@ -5643,36 +4875,39 @@ binlog:
char buf[2048];
String query(buf, sizeof(buf), system_charset_info);
query.length(0); // Have to zero it since constructor doesn't
+ Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN);
/*
- Here we open the destination table, on which we already have
- name-lock. This is needed for store_create_info() to work.
- The table will be closed by unlink_open_table() at the end
- of this function.
+ The condition avoids a crash as described in BUG#48506. Other
+ binlogging problems related to CREATE TABLE IF NOT EXISTS LIKE
+ when the existing object is a view will be solved by BUG 47442.
*/
- table->table= name_lock;
- VOID(pthread_mutex_lock(&LOCK_open));
- if (reopen_name_locked_table(thd, table, FALSE))
- {
- VOID(pthread_mutex_unlock(&LOCK_open));
- goto err;
- }
- VOID(pthread_mutex_unlock(&LOCK_open));
-
- /*
- The condition avoids a crash as described in BUG#48506. Other
- binlogging problems related to CREATE TABLE IF NOT EXISTS LIKE
- when the existing object is a view will be solved by BUG 47442.
- */
if (!table->view)
{
- IF_DBUG(int result=)
+ /*
+ Here we open the destination table, on which we already have
+ exclusive metadata lock. This is needed for store_create_info()
+ to work. The table will be closed by close_thread_table() at
+ the end of this branch.
+ */
+ if (open_table(thd, table, thd->mem_root, &ot_ctx))
+ goto err;
+
+ int result __attribute__((unused))=
store_create_info(thd, table, &query,
create_info, FALSE /* show_database */);
DBUG_ASSERT(result == 0); // store_create_info() always return 0
if (write_bin_log(thd, TRUE, query.ptr(), query.length()))
goto err;
+
+ DBUG_ASSERT(thd->open_tables == table->table);
+ /*
+ When opening the table, we ignored the locked tables
+ (MYSQL_OPEN_GET_NEW_TABLE). Now we can close the table without
+ risking to close some locked table.
+ */
+ close_thread_table(thd, &thd->open_tables);
}
}
else // Case 1
@@ -5683,45 +4918,14 @@ binlog:
Case 3 and 4 does nothing under RBR
*/
}
- else if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
+ else if (write_bin_log(thd, TRUE, thd->query(), thd->query_length(), is_trans))
goto err;
- res= FALSE;
-
err:
- if (name_lock)
- {
- pthread_mutex_lock(&LOCK_open);
- unlink_open_table(thd, name_lock, FALSE);
- pthread_mutex_unlock(&LOCK_open);
- }
DBUG_RETURN(res);
}
-bool mysql_analyze_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
-{
- thr_lock_type lock_type = TL_READ_NO_INSERT;
-
- DBUG_ENTER("mysql_analyze_table");
- DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
- "analyze", lock_type, 1, 0, 0, 0,
- &handler::ha_analyze, 0));
-}
-
-
-bool mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
-{
- thr_lock_type lock_type = TL_READ_NO_INSERT;
-
- DBUG_ENTER("mysql_check_table");
- DBUG_RETURN(mysql_admin_table(thd, tables, check_opt,
- "check", lock_type,
- 0, 0, HA_OPEN_FOR_REPAIR, 0,
- &handler::ha_check, &view_checksum));
-}
-
-
/* table_list should contain just one table */
static int
mysql_discard_or_import_tablespace(THD *thd,
@@ -5747,6 +4951,7 @@ mysql_discard_or_import_tablespace(THD *thd,
not complain when we lock the table
*/
thd->tablespace_op= TRUE;
+ table_list->mdl_request.set_type(MDL_SHARED_WRITE);
if (!(table=open_ltable(thd, table_list, TL_WRITE, 0)))
{
thd->tablespace_op=FALSE;
@@ -5767,17 +4972,16 @@ mysql_discard_or_import_tablespace(THD *thd,
query_cache_invalidate3(thd, table_list, 0);
/* The ALTER TABLE is always in its own transaction */
- error = ha_autocommit_or_rollback(thd, 0);
- if (end_active_trans(thd))
+ error= trans_commit_stmt(thd);
+ if (trans_commit_implicit(thd))
error=1;
if (error)
goto err;
error= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
err:
- ha_autocommit_or_rollback(thd, error);
thd->tablespace_op=FALSE;
-
+
if (error == 0)
{
my_ok(thd);
@@ -5785,7 +4989,7 @@ err:
}
table->file->print_error(error, MYF(0));
-
+
DBUG_RETURN(-1);
}
@@ -5794,7 +4998,7 @@ err:
@details Checks if any index is being modified (present as both DROP INDEX
and ADD INDEX) in the current ALTER TABLE statement. Needed for disabling
- online ALTER TABLE.
+ in-place ALTER TABLE.
@param table The table being altered
@param alter_info The ALTER TABLE structure
@@ -5812,7 +5016,7 @@ is_index_maintenance_unique (TABLE *table, Alter_info *alter_info)
while ((key= key_it++))
{
- if (key->name)
+ if (key->name.str)
{
Alter_drop *drop;
@@ -5820,7 +5024,7 @@ is_index_maintenance_unique (TABLE *table, Alter_info *alter_info)
while ((drop= drop_it++))
{
if (drop->type == Alter_drop::KEY &&
- !my_strcasecmp(system_charset_info, key->name, drop->name))
+ !my_strcasecmp(system_charset_info, key->name.str, drop->name))
return TRUE;
}
}
@@ -5831,7 +5035,7 @@ is_index_maintenance_unique (TABLE *table, Alter_info *alter_info)
/*
SYNOPSIS
- compare_tables()
+ mysql_compare_tables()
table The original table.
alter_info Alter options, fields and keys for the new
table.
@@ -5871,17 +5075,16 @@ is_index_maintenance_unique (TABLE *table, Alter_info *alter_info)
FALSE The tables are compatible; We only have to modify the .frm
*/
-static
bool
-compare_tables(TABLE *table,
- Alter_info *alter_info,
- HA_CREATE_INFO *create_info,
- uint order_num,
- enum_alter_table_change_level *need_copy_table,
- KEY **key_info_buffer,
- uint **index_drop_buffer, uint *index_drop_count,
- uint **index_add_buffer, uint *index_add_count,
- uint *candidate_key_count)
+mysql_compare_tables(TABLE *table,
+ Alter_info *alter_info,
+ HA_CREATE_INFO *create_info,
+ uint order_num,
+ enum_alter_table_change_level *need_copy_table,
+ KEY **key_info_buffer,
+ uint **index_drop_buffer, uint *index_drop_count,
+ uint **index_add_buffer, uint *index_add_count,
+ uint *candidate_key_count)
{
Field **f_ptr, *field;
uint changes= 0, tmp;
@@ -5898,7 +5101,7 @@ compare_tables(TABLE *table,
*/
bool varchar= create_info->varchar;
bool not_nullable= true;
- DBUG_ENTER("compare_tables");
+ DBUG_ENTER("mysql_compare_tables");
/*
Create a copy of alter_info.
@@ -5909,10 +5112,10 @@ compare_tables(TABLE *table,
mysql_prepare_create_table. Unfortunately,
mysql_prepare_create_table performs its transformations
"in-place", that is, modifies the argument. Since we would
- like to keep compare_tables() idempotent (not altering any
+ like to keep mysql_compare_tables() idempotent (not altering any
of the arguments) we create a copy of alter_info here and
pass it to mysql_prepare_create_table, then use the result
- to evaluate possibility of fast ALTER TABLE, and then
+ to evaluate possibility of in-place ALTER TABLE, and then
destroy the copy.
*/
Alter_info tmp_alter_info(*alter_info, thd->mem_root);
@@ -5956,9 +5159,9 @@ compare_tables(TABLE *table,
There was a bug prior to mysql-4.0.25. Number of null fields was
calculated incorrectly. As a result frm and data files gets out of
- sync after fast alter table. There is no way to determine by which
+ sync after in-place alter table. There is no way to determine by which
mysql version (in 4.0 and 4.1 branches) table was created, thus we
- disable fast alter table for all tables created by mysql versions
+ disable in-place alter table for all tables created by mysql versions
prior to 5.0 branch.
See BUG#6236.
*/
@@ -5989,7 +5192,7 @@ compare_tables(TABLE *table,
DBUG_RETURN(1);
/*
- Use transformed info to evaluate possibility of fast ALTER TABLE
+ Use transformed info to evaluate possibility of in-place ALTER TABLE
but use the preserved field to persist modifications.
*/
new_field_it.init(alter_info->create_list);
@@ -6260,6 +5463,34 @@ bool alter_table_manage_keys(TABLE *table, int indexes_were_disabled,
DBUG_RETURN(error);
}
+/**
+ maximum possible length for certain blob types.
+
+ @param[in] type Blob type (e.g. MYSQL_TYPE_TINY_BLOB)
+
+ @return
+ length
+*/
+
+static uint
+blob_length_by_type(enum_field_types type)
+{
+ switch (type)
+ {
+ case MYSQL_TYPE_TINY_BLOB:
+ return 255;
+ case MYSQL_TYPE_BLOB:
+ return 65535;
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ return 16777215;
+ case MYSQL_TYPE_LONG_BLOB:
+ return 4294967295U;
+ default:
+ DBUG_ASSERT(0); // we should never go here
+ return 0;
+ }
+}
+
/**
Prepare column and key definitions for CREATE TABLE in ALTER TABLE.
@@ -6271,7 +5502,7 @@ bool alter_table_manage_keys(TABLE *table, int indexes_were_disabled,
semantic checks.
This function is invoked when we know that we're going to
- perform ALTER TABLE via a temporary table -- i.e. fast ALTER TABLE
+ perform ALTER TABLE via a temporary table -- i.e. in-place ALTER TABLE
is not possible, perhaps because the ALTER statement contains
instructions that require change in table data, not only in
table definition or indexes.
@@ -6303,7 +5534,7 @@ bool alter_table_manage_keys(TABLE *table, int indexes_were_disabled,
@retval FALSE success
*/
-static bool
+bool
mysql_prepare_alter_table(THD *thd, TABLE *table,
HA_CREATE_INFO *create_info,
Alter_info *alter_info)
@@ -6349,17 +5580,6 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
if (!(used_fields & HA_CREATE_USED_TRANSACTIONAL))
create_info->transactional= table->s->transactional;
- if (!create_info->tablespace && create_info->storage_media != HA_SM_MEMORY)
- {
- char *tablespace= static_cast<char *>(thd->alloc(FN_LEN + 1));
- /*
- Regular alter table of disk stored table (no tablespace/storage change)
- Copy tablespace name
- */
- if (tablespace &&
- (table->file->get_tablespace_name(thd, tablespace, FN_LEN)))
- create_info->tablespace= tablespace;
- }
restore_record(table, s->default_values); // Empty record for DEFAULT
create_info->option_list= merge_engine_table_options(table->s->option_list,
@@ -6586,6 +5806,14 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
BLOBs may have cfield->length == 0, which is why we test it before
checking whether cfield->length < key_part_length (in chars).
+
+ In case of TEXTs we check the data type maximum length *in bytes*
+ to key part length measured *in characters* (i.e. key_part_length
+ devided to mbmaxlen). This is because it's OK to have:
+ CREATE TABLE t1 (a tinytext, key(a(254)) character set utf8);
+ In case of this example:
+ - 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) ||
@@ -6593,12 +5821,16 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
(key_info->flags & HA_SPATIAL) ||
(cfield->field->field_length == key_part_length &&
!f_is_blob(key_part->key_type)) ||
- (cfield->length && (cfield->length < key_part_length /
- key_part->field->charset()->mbmaxlen)))
+ (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)))
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));
}
if (key_parts.elements)
@@ -6613,6 +5845,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
key_create_info.block_size= key_info->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)
+ key_create_info.comment= key_info->comment;
if (key_info->flags & HA_SPATIAL)
key_type= Key::SPATIAL;
@@ -6628,7 +5862,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
else
key_type= Key::MULTIPLE;
- key= new Key(key_type, key_name,
+ key= new Key(key_type, key_name, strlen(key_name),
&key_create_info,
test(key_info->flags & HA_GENERATED_KEY),
key_parts, key_info->option_list);
@@ -6644,10 +5878,10 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
goto err;
if (key->type != Key::FOREIGN_KEY)
new_key_list.push_back(key);
- if (key->name &&
- !my_strcasecmp(system_charset_info,key->name,primary_key_name))
+ if (key->name.str &&
+ !my_strcasecmp(system_charset_info, key->name.str, primary_key_name))
{
- my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
+ my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name.str);
goto err;
}
}
@@ -6731,7 +5965,7 @@ err:
Important is the fact, that this function tries to do as little work as
possible, by finding out whether a intermediate table is needed to copy
data into and when finishing the altering to use it as the original table.
- For this reason the function compare_tables() is called, which decides
+ For this reason the function mysql_compare_tables() is called, which decides
based on all kind of data how similar are the new and the original
tables.
@@ -6747,7 +5981,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
uint order_num, ORDER *order, bool ignore,
bool require_online)
{
- TABLE *table, *new_table= 0, *name_lock= 0;
+ TABLE *table, *new_table= 0;
+ MDL_ticket *mdl_ticket;
+ MDL_request target_mdl_request;
int error= 0;
char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1];
char old_name_buff[FN_REFLEN + 1];
@@ -6757,11 +5993,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
char reg_path[FN_REFLEN+1];
ha_rows copied,deleted;
handlerton *old_db_type, *new_db_type, *save_old_db_type;
- legacy_db_type table_type;
- frm_type_enum frm_type;
enum_alter_table_change_level need_copy_table= ALTER_TABLE_METADATA_ONLY;
#ifdef WITH_PARTITION_STORAGE_ENGINE
- uint fast_alter_partition= 0;
+ TABLE *table_for_fast_alter_partition= NULL;
bool partition_changed= FALSE;
#endif
bool need_lock_for_indexes __attribute__((unused)) = TRUE;
@@ -6769,10 +6003,15 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
uint index_drop_count= 0;
uint *index_drop_buffer= NULL;
uint index_add_count= 0;
+ handler_add_index *add= NULL;
+ bool pending_inplace_add_index= false;
uint *index_add_buffer= NULL;
uint candidate_key_count= 0;
bool no_pk;
ulong explicit_used_fields= 0;
+ enum ha_extra_function extra_func= thd->locked_tables_mode
+ ? HA_EXTRA_NOT_USED
+ : HA_EXTRA_FORCE_REOPEN;
DBUG_ENTER("mysql_alter_table");
/*
@@ -6831,94 +6070,40 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
build_table_filename(reg_path, sizeof(reg_path) - 1, db, table_name, reg_ext, 0);
build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0);
- mysql_ha_rm_tables(thd, table_list, FALSE);
+ mysql_ha_rm_tables(thd, table_list);
/* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
if (alter_info->tablespace_op != NO_TABLESPACE_OP)
- /* Conditionally writes to binlog. */
- DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
- alter_info->tablespace_op));
- strxnmov(new_name_buff, sizeof (new_name_buff) - 1, mysql_data_home, "/", db,
- "/", table_name, reg_ext, NullS);
- (void) unpack_filename(new_name_buff, new_name_buff);
- /*
- If this is just a rename of a view, short cut to the
- following scenario: 1) lock LOCK_open 2) do a RENAME
- 2) unlock LOCK_open.
- This is a copy-paste added to make sure
- ALTER (sic:) TABLE .. RENAME works for views. ALTER VIEW is handled
- as an independent branch in mysql_execute_command. The need
- for a copy-paste arose because the main code flow of ALTER TABLE
- ... RENAME tries to use open_ltable, which does not work for views
- (open_ltable was never modified to merge table lists of child tables
- into the main table list, like open_tables does).
- This code is wrong and will be removed, please do not copy.
- */
- frm_type= mysql_frm_type(thd, new_name_buff, &table_type);
- /* Rename a view */
- /* Sic: there is a race here */
- if (frm_type == FRMTYPE_VIEW && !(alter_info->flags & ~ALTER_RENAME))
{
- /*
- The following branch handles "ALTER VIEW v1 /no arguments/;"
- This feature is not documented one.
- However, before "OPTIMIZE TABLE t1;" was implemented,
- ALTER TABLE with no alter_specifications was used to force-rebuild
- the table. That's why this grammar is allowed. That's why we ignore
- it for views. So just do nothing in such a case.
- */
- if (!new_name)
- {
- my_ok(thd);
- DBUG_RETURN(FALSE);
- }
+ mysql_audit_alter_table(thd, table_list);
- /*
- Avoid problems with a rename on a table that we have locked or
- if the user is trying to to do this in a transcation context
- */
+ /* Conditionally writes to binlog. */
+ bool ret= mysql_discard_or_import_tablespace(thd,table_list,
+ alter_info->tablespace_op);
+ DBUG_RETURN(ret);
+ }
- if (thd->locked_tables || thd->active_transaction())
- {
- my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
- ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
- DBUG_RETURN(TRUE);
- }
+ /*
+ 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;
- if (wait_if_global_read_lock(thd,0,1))
- DBUG_RETURN(TRUE);
- VOID(pthread_mutex_lock(&LOCK_open));
- if (lock_table_names(thd, table_list))
- {
- error= 1;
- goto view_err;
- }
-
- if (!do_rename(thd, table_list, new_db, new_name, new_name, 1))
- {
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query(), thd->query_length(),
- 0, FALSE, 0);
- if ((error= mysql_bin_log.write(&qinfo)))
- goto view_err_unlock;
- }
- my_ok(thd);
- }
+ Alter_table_prelocking_strategy alter_prelocking_strategy(alter_info);
-view_err_unlock:
- unlock_table_names(thd, table_list, (TABLE_LIST*) 0);
+ DEBUG_SYNC(thd, "alter_table_before_open_tables");
+ error= open_and_lock_tables(thd, table_list, FALSE, 0,
+ &alter_prelocking_strategy);
-view_err:
- pthread_mutex_unlock(&LOCK_open);
- start_waiting_global_read_lock(thd);
- DBUG_RETURN(error);
+ if (error)
+ {
+ DBUG_RETURN(TRUE);
}
- if (!(table= open_n_lock_single_table(thd, table_list, TL_WRITE_ALLOW_READ)))
- DBUG_RETURN(TRUE);
+ table= table_list->table;
table->use_all_columns();
+ mdl_ticket= table->mdl_ticket;
/*
Prohibit changing of the UNION list of a non-temporary MERGE table
@@ -6926,7 +6111,8 @@ view_err:
set of tables from the old table or to open a new TABLE object for
an extended list and verify that they belong to locked tables.
*/
- if (thd->locked_tables &&
+ if ((thd->locked_tables_mode == LTM_LOCK_TABLES ||
+ thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES) &&
(create_info->used_fields & HA_CREATE_USED_UNION) &&
(table->s->tmp_table == NO_TMP_TABLE))
{
@@ -6970,14 +6156,43 @@ view_err:
}
else
{
- if (lock_table_name_if_not_cached(thd, new_db, new_name, &name_lock))
- DBUG_RETURN(TRUE);
- if (!name_lock)
+ MDL_request_list mdl_requests;
+ MDL_request target_db_mdl_request;
+
+ target_mdl_request.init(MDL_key::TABLE, new_db, new_name,
+ MDL_EXCLUSIVE, MDL_TRANSACTION);
+ mdl_requests.push_front(&target_mdl_request);
+
+ /*
+ If we are moving the table to a different database, we also
+ need IX lock on the database name so that the target database
+ is protected by MDL while the table is moved.
+ */
+ if (new_db != db)
{
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
- DBUG_RETURN(TRUE);
+ target_db_mdl_request.init(MDL_key::SCHEMA, new_db, "",
+ MDL_INTENTION_EXCLUSIVE,
+ MDL_TRANSACTION);
+ mdl_requests.push_front(&target_db_mdl_request);
}
+ /*
+ Global intention exclusive lock must have been already acquired when
+ table to be altered was open, so there is no need to do it here.
+ */
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::GLOBAL,
+ "", "",
+ MDL_INTENTION_EXCLUSIVE));
+
+ if (thd->mdl_context.acquire_locks(&mdl_requests,
+ thd->variables.lock_wait_timeout))
+ DBUG_RETURN(TRUE);
+
+ DEBUG_SYNC(thd, "locked_table_name");
+ /*
+ Table maybe does not exist, but we got an exclusive lock
+ on the name, now we can safely try to find out for sure.
+ */
build_table_filename(new_name_buff, sizeof(new_name_buff) - 1,
new_db, new_name_buff, reg_ext, 0);
build_table_filename(old_name_buff, sizeof(old_name_buff) - 1,
@@ -7018,7 +6233,7 @@ view_err:
create_info->db_type= old_db_type;
}
- if (check_engine(thd, new_name, create_info))
+ if (check_engine(thd, new_db, new_name, create_info))
goto err;
new_db_type= create_info->db_type;
@@ -7058,6 +6273,9 @@ view_err:
my_error(ER_ILLEGAL_HA, MYF(0), table_name);
goto err;
}
+
+ if (table->s->tmp_table == NO_TMP_TABLE)
+ mysql_audit_alter_table(thd, table_list);
thd_proc_info(thd, "setup");
if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
@@ -7067,32 +6285,19 @@ view_err:
case LEAVE_AS_IS:
break;
case ENABLE:
- /*
- wait_while_table_is_used() ensures that table being altered is
- opened only by this thread and that TABLE::TABLE_SHARE::version
- of TABLE object corresponding to this table is 0.
- The latter guarantees that no DML statement will open this table
- until ALTER TABLE finishes (i.e. until close_thread_tables())
- while the fact that the table is still open gives us protection
- from concurrent DDL statements.
- */
- VOID(pthread_mutex_lock(&LOCK_open));
- wait_while_table_is_used(thd, table,
- thd->locked_tables ? HA_EXTRA_NOT_USED :
- HA_EXTRA_FORCE_REOPEN);
- VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_EXECUTE_IF("sleep_alter_enable_indexes", my_sleep(6000000););
+ if (wait_while_table_is_used(thd, table, extra_func,
+ TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE))
+ goto err;
+ DEBUG_SYNC(thd,"alter_table_enable_indexes");
error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
- /* COND_refresh will be signaled in close_thread_tables() */
+ table->s->allow_access_to_protected_table();
break;
case DISABLE:
- VOID(pthread_mutex_lock(&LOCK_open));
- wait_while_table_is_used(thd, table,
- thd->locked_tables ? HA_EXTRA_NOT_USED :
- HA_EXTRA_FORCE_REOPEN);
- VOID(pthread_mutex_unlock(&LOCK_open));
+ if (wait_while_table_is_used(thd, table, extra_func,
+ TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE))
+ goto err;
error=table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
- /* COND_refresh will be signaled in close_thread_tables() */
+ table->s->allow_access_to_protected_table();
break;
default:
DBUG_ASSERT(FALSE);
@@ -7107,61 +6312,49 @@ view_err:
table->alias.c_ptr());
}
- /*
- Unlike to the above case close_cached_table() below will remove ALL
- instances of TABLE from table cache (it will also remove table lock
- held by this thread). So to make actual table renaming and writing
- to binlog atomic we have to put them into the same critical section
- protected by LOCK_open mutex. This also removes gap for races between
- access() and mysql_rename_table() calls.
- */
-
if (!error && (new_name != table_name || new_db != db))
{
thd_proc_info(thd, "rename");
-
- /*
- Workaround InnoDB ending the transaction when the table instance
- is unlocked/closed (close_cached_table below), otherwise the trx
- state will differ between the server and storage engine layers.
- */
- ha_autocommit_or_rollback(thd, 0);
-
- VOID(pthread_mutex_lock(&LOCK_open));
/*
Then do a 'simple' rename of the table. First we need to close all
instances of 'source' table.
+ Note that if wait_while_table_is_used() returns error here (i.e. if
+ this thread was killed) then it must be that previous step of
+ simple rename did nothing and therefore we can safely return
+ without additional clean-up.
*/
- close_cached_table(thd, table);
+ if (wait_while_table_is_used(thd, table, extra_func,
+ TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE))
+ goto err;
+ close_all_tables_for_name(thd, table->s, HA_EXTRA_PREPARE_FOR_RENAME);
/*
Then, we want check once again that target table does not exist.
Actually the order of these two steps does not matter since
- earlier we took name-lock on the target table, so we do them
- in this particular order only to be consistent with 5.0, in which
- we don't take this name-lock and where this order really matters.
+ earlier we took exclusive metadata lock on the target table, so
+ we do them in this particular order only to be consistent with 5.0,
+ in which we don't take this lock and where this order really matters.
TODO: Investigate if we need this access() check at all.
*/
if (!access(new_name_buff,F_OK))
{
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
- error= -1;
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
+ error= -1;
}
else
{
- *fn_ext(new_name)=0;
- if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0))
- error= -1;
- else if (Table_triggers_list::change_table_name(thd, db, table_name,
+ *fn_ext(new_name)=0;
+ if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0))
+ error= -1;
+ else if (Table_triggers_list::change_table_name(thd, db,
+ alias, table_name,
new_db, new_alias))
{
- VOID(mysql_rename_table(old_db_type, new_db, new_alias, db,
- table_name, 0));
+ (void) mysql_rename_table(old_db_type, new_db, new_alias, db,
+ table_name, NO_FK_CHECKS);
error= -1;
}
}
}
- else
- VOID(pthread_mutex_lock(&LOCK_open));
if (error == HA_ERR_WRONG_COMMAND)
{
@@ -7182,11 +6375,22 @@ view_err:
table->file->print_error(error, MYF(0));
error= -1;
}
- if (name_lock)
- unlink_open_table(thd, name_lock, FALSE);
- VOID(pthread_mutex_unlock(&LOCK_open));
table_list->table= NULL; // For query cache
query_cache_invalidate3(thd, table_list, 0);
+
+ if ((thd->locked_tables_mode == LTM_LOCK_TABLES ||
+ thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES))
+ {
+ /*
+ Under LOCK TABLES we should adjust meta-data locks before finishing
+ statement. Otherwise we can rely on them being released
+ along with the implicit commit.
+ */
+ if (new_name != table_name || new_db != db)
+ thd->mdl_context.release_all_locks_for_name(mdl_ticket);
+ else
+ mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
+ }
DBUG_RETURN(error);
}
@@ -7194,7 +6398,9 @@ view_err:
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type,
- &partition_changed, &fast_alter_partition))
+ &partition_changed,
+ db, table_name, path,
+ &table_for_fast_alter_partition))
goto err;
#endif
/*
@@ -7233,15 +6439,15 @@ view_err:
need_copy_table= ALTER_TABLE_DATA_CHANGED;
else
{
- enum_alter_table_change_level need_copy_table_res=ALTER_TABLE_METADATA_ONLY;
+ enum_alter_table_change_level need_copy_table_res;
/* Check how much the tables differ. */
- if (compare_tables(table, alter_info,
- create_info, order_num,
- &need_copy_table_res,
- &key_info_buffer,
- &index_drop_buffer, &index_drop_count,
- &index_add_buffer, &index_add_count,
- &candidate_key_count))
+ if (mysql_compare_tables(table, alter_info,
+ create_info, order_num,
+ &need_copy_table_res,
+ &key_info_buffer,
+ &index_drop_buffer, &index_drop_count,
+ &index_add_buffer, &index_add_count,
+ &candidate_key_count))
goto err;
DBUG_EXECUTE_IF("alter_table_only_metadata_change", {
@@ -7256,7 +6462,7 @@ view_err:
}
/*
- If there are index changes only, try to do them online. "Index
+ If there are index changes only, try to do them in-place. "Index
changes only" means also that the handler for the table does not
change. The table is open and locked. The handler can be accessed.
*/
@@ -7264,8 +6470,8 @@ view_err:
{
int pk_changed= 0;
ulong alter_flags= 0;
- ulong needed_online_flags= 0;
- ulong needed_fast_flags= 0;
+ ulong needed_inplace_with_read_flags= 0;
+ ulong needed_inplace_flags= 0;
KEY *key;
uint *idx_p;
uint *idx_end_p;
@@ -7289,8 +6495,8 @@ view_err:
{
DBUG_PRINT("info", ("Dropping primary key"));
/* Primary key. */
- needed_online_flags|= HA_ONLINE_DROP_PK_INDEX;
- needed_fast_flags|= HA_ONLINE_DROP_PK_INDEX_NO_WRITES;
+ needed_inplace_with_read_flags|= HA_INPLACE_DROP_PK_INDEX_NO_WRITE;
+ needed_inplace_flags|= HA_INPLACE_DROP_PK_INDEX_NO_READ_WRITE;
pk_changed++;
candidate_key_count--;
}
@@ -7300,8 +6506,9 @@ view_err:
bool is_candidate_key= true;
/* Non-primary unique key. */
- needed_online_flags|= HA_ONLINE_DROP_UNIQUE_INDEX;
- needed_fast_flags|= HA_ONLINE_DROP_UNIQUE_INDEX_NO_WRITES;
+ needed_inplace_with_read_flags|=
+ HA_INPLACE_DROP_UNIQUE_INDEX_NO_WRITE;
+ needed_inplace_flags|= HA_INPLACE_DROP_UNIQUE_INDEX_NO_READ_WRITE;
/*
Check if all fields in key are declared
@@ -7320,12 +6527,13 @@ view_err:
else
{
/* Non-unique key. */
- needed_online_flags|= HA_ONLINE_DROP_INDEX;
- needed_fast_flags|= HA_ONLINE_DROP_INDEX_NO_WRITES;
+ needed_inplace_with_read_flags|= HA_INPLACE_DROP_INDEX_NO_WRITE;
+ needed_inplace_flags|= HA_INPLACE_DROP_INDEX_NO_READ_WRITE;
}
}
no_pk= ((table->s->primary_key == MAX_KEY) ||
- (needed_online_flags & HA_ONLINE_DROP_PK_INDEX));
+ (needed_inplace_with_read_flags &
+ HA_INPLACE_DROP_PK_INDEX_NO_WRITE));
/* Check added indexes. */
for (idx_p= index_add_buffer, idx_end_p= idx_p + index_add_count;
idx_p < idx_end_p;
@@ -7363,16 +6571,16 @@ view_err:
{
DBUG_PRINT("info", ("Adding primary key"));
/* Primary key. */
- needed_online_flags|= HA_ONLINE_ADD_PK_INDEX;
- needed_fast_flags|= HA_ONLINE_ADD_PK_INDEX_NO_WRITES;
+ needed_inplace_with_read_flags|= HA_INPLACE_ADD_PK_INDEX_NO_WRITE;
+ needed_inplace_flags|= HA_INPLACE_ADD_PK_INDEX_NO_READ_WRITE;
pk_changed++;
no_pk= false;
}
else
{
/* Non-primary unique key. */
- needed_online_flags|= HA_ONLINE_ADD_UNIQUE_INDEX;
- needed_fast_flags|= HA_ONLINE_ADD_UNIQUE_INDEX_NO_WRITES;
+ needed_inplace_with_read_flags|= HA_INPLACE_ADD_UNIQUE_INDEX_NO_WRITE;
+ needed_inplace_flags|= HA_INPLACE_ADD_UNIQUE_INDEX_NO_READ_WRITE;
if (ignore)
{
/*
@@ -7388,42 +6596,56 @@ view_err:
else
{
/* Non-unique key. */
- needed_online_flags|= HA_ONLINE_ADD_INDEX;
- needed_fast_flags|= HA_ONLINE_ADD_INDEX_NO_WRITES;
+ needed_inplace_with_read_flags|= HA_INPLACE_ADD_INDEX_NO_WRITE;
+ needed_inplace_flags|= HA_INPLACE_ADD_INDEX_NO_READ_WRITE;
}
}
- if ((candidate_key_count > 0) &&
- (needed_online_flags & HA_ONLINE_DROP_PK_INDEX))
+ if ((candidate_key_count > 0) &&
+ (needed_inplace_with_read_flags & HA_INPLACE_DROP_PK_INDEX_NO_WRITE))
{
/*
Dropped primary key when there is some other unique
not null key that should be converted to primary key
*/
- needed_online_flags|= HA_ONLINE_ADD_PK_INDEX;
- needed_fast_flags|= HA_ONLINE_ADD_PK_INDEX_NO_WRITES;
+ needed_inplace_with_read_flags|= HA_INPLACE_ADD_PK_INDEX_NO_WRITE;
+ needed_inplace_flags|= HA_INPLACE_ADD_PK_INDEX_NO_READ_WRITE;
pk_changed= 2;
}
- DBUG_PRINT("info", ("needed_online_flags: 0x%lx, needed_fast_flags: 0x%lx",
- needed_online_flags, needed_fast_flags));
+ DBUG_PRINT("info",
+ ("needed_inplace_with_read_flags: 0x%lx, needed_inplace_flags: 0x%lx",
+ needed_inplace_with_read_flags, needed_inplace_flags));
/*
- Online or fast add/drop index is possible only if
+ In-place add/drop index is possible only if
the primary key is not added and dropped in the same statement.
Otherwise we have to recreate the table.
need_copy_table is no-zero at this place.
+
+ Also, in-place is not possible if we add a primary key
+ and drop another key in the same statement. If the drop fails,
+ we will not be able to revert adding of primary key.
*/
if ( pk_changed < 2 )
{
- if ((alter_flags & needed_online_flags) == needed_online_flags)
+ if ((needed_inplace_with_read_flags & HA_INPLACE_ADD_PK_INDEX_NO_WRITE) &&
+ index_drop_count > 0)
+ {
+ /*
+ Do copy, not in-place ALTER.
+ Avoid setting ALTER_TABLE_METADATA_ONLY.
+ */
+ }
+ else if ((alter_flags & needed_inplace_with_read_flags) ==
+ needed_inplace_with_read_flags)
{
- /* All required online flags are present. */
+ /* All required in-place flags to allow concurrent reads are present. */
need_copy_table= ALTER_TABLE_METADATA_ONLY;
need_lock_for_indexes= FALSE;
}
- else if ((alter_flags & needed_fast_flags) == needed_fast_flags)
+ else if ((alter_flags & needed_inplace_flags) == needed_inplace_flags)
{
- /* All required fast flags are present. */
+ /* All required in-place flags are present. */
need_copy_table= ALTER_TABLE_METADATA_ONLY;
}
}
@@ -7440,13 +6662,12 @@ view_err:
create_info->frm_only= 1;
#ifdef WITH_PARTITION_STORAGE_ENGINE
- if (fast_alter_partition)
+ if (table_for_fast_alter_partition)
{
- DBUG_ASSERT(!name_lock);
DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info,
create_info, table_list,
db, table_name,
- fast_alter_partition));
+ table_for_fast_alter_partition));
}
#endif
@@ -7503,16 +6724,29 @@ view_err:
create_info->data_file_name=create_info->index_file_name=0;
DEBUG_SYNC(thd, "alter_table_before_create_table_no_lock");
+ DBUG_EXECUTE_IF("sleep_before_create_table_no_lock",
+ my_sleep(100000););
/*
Create a table with a temporary name.
- With create_info->frm_only == 1 this creates a .frm file only.
+ With create_info->frm_only == 1 this creates a .frm file only and
+ we keep the original row format.
We don't log the statement, it will be logged later.
*/
+ if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
+ {
+ DBUG_ASSERT(create_info->frm_only);
+ /* Ensure we keep the original table format */
+ create_info->table_options= ((create_info->table_options &
+ ~HA_OPTION_PACK_RECORD) |
+ (table->s->db_create_options &
+ HA_OPTION_PACK_RECORD));
+ }
tmp_disable_binlog(thd);
+ create_info->options|=HA_CREATE_TMP_ALTER;
error= mysql_create_table_no_lock(thd, new_db, tmp_name,
create_info,
alter_info,
- 1, 0);
+ 1, 0, NULL);
reenable_binlog(thd);
if (error)
goto err;
@@ -7523,13 +6757,16 @@ view_err:
{
if (table->s->tmp_table)
{
+ Open_table_context ot_ctx(thd, (MYSQL_OPEN_IGNORE_FLUSH |
+ MYSQL_OPEN_FOR_REPAIR |
+ MYSQL_LOCK_IGNORE_TIMEOUT));
TABLE_LIST tbl;
bzero((void*) &tbl, sizeof(tbl));
tbl.db= new_db;
tbl.table_name= tbl.alias= tmp_name;
/* Table is in thd->temporary_tables */
- new_table= open_table(thd, &tbl, thd->mem_root, (bool*) 0,
- MYSQL_LOCK_IGNORE_FLUSH);
+ (void) open_table(thd, &tbl, thd->mem_root, &ot_ctx);
+ new_table= tbl.table;
}
else
{
@@ -7537,11 +6774,11 @@ view_err:
/* table is a normal table: Create temporary table in same directory */
build_table_filename(path, sizeof(path) - 1, new_db, tmp_name, "",
FN_IS_TMP);
- /* Open our intermediate table */
- new_table=open_temporary_table(thd, path, new_db, tmp_name,0);
+ /* Open our intermediate table. */
+ new_table= open_table_uncached(thd, path, new_db, tmp_name, TRUE);
}
if (!new_table)
- goto err1;
+ goto err_new_table_cleanup;
/*
Note: In case of MERGE table, we do not attach children. We do not
copy data for MERGE tables. Only the children have data.
@@ -7556,7 +6793,7 @@ view_err:
!(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER)))
{
my_error(ER_CANT_DO_ONLINE, MYF(0), "ALTER");
- goto close_table_and_return_error;
+ goto err_new_table_cleanup;
}
}
@@ -7574,6 +6811,10 @@ view_err:
/* We don't want update TIMESTAMP fields during ALTER TABLE. */
new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
new_table->next_number_field=new_table->found_next_number_field;
+ DBUG_EXECUTE_IF("abort_copy_table", {
+ my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
+ goto err_new_table_cleanup;
+ });
error= copy_data_between_tables(thd, table, new_table,
alter_info->create_list, ignore,
order_num, order, &copied, &deleted,
@@ -7582,21 +6823,34 @@ view_err:
}
else
{
- VOID(pthread_mutex_lock(&LOCK_open));
- wait_while_table_is_used(thd, table,
- thd->locked_tables ? HA_EXTRA_NOT_USED :
- HA_EXTRA_FORCE_REOPEN);
- VOID(pthread_mutex_unlock(&LOCK_open));
+ /*
+ Ensure that we will upgrade the metadata lock if
+ handler::enable/disable_indexes() will be called.
+ */
+ if (alter_info->keys_onoff != LEAVE_AS_IS ||
+ table->file->indexes_are_disabled())
+ need_lock_for_indexes= true;
+ if (!table->s->tmp_table && need_lock_for_indexes &&
+ wait_while_table_is_used(thd, table, extra_func,
+ TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE))
+ goto err_new_table_cleanup;
thd_proc_info(thd, "manage keys");
+ DEBUG_SYNC(thd, "alter_table_manage_keys");
alter_table_manage_keys(table, table->file->indexes_are_disabled(),
alter_info->keys_onoff);
- error= ha_autocommit_or_rollback(thd, 0);
- if (end_active_trans(thd))
+ error= trans_commit_stmt(thd);
+ if (trans_commit_implicit(thd))
error= 1;
+ /*
+ If the table was locked, allow one to still run SHOW commands against it
+ */
+ if (table->s->protected_against_usage())
+ table->s->allow_access_to_protected_table();
}
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
+
if (error)
- goto close_table_and_return_error;
+ goto err_new_table_cleanup;
/* If we did not need to copy, we might still need to add/drop indexes. */
if (! new_table)
@@ -7629,27 +6883,65 @@ view_err:
key_part->field= table->field[key_part->fieldnr];
}
/* Add the indexes. */
- if ((error= table->file->add_index(table, key_info, index_add_count)))
+ if ((error= table->file->add_index(table, key_info, index_add_count,
+ &add)))
{
/* Only report error if handler has not already reported an error */
- if (!thd->main_da.is_error())
+ if (!thd->is_error())
{
/*
- Exchange the key_info for the error message. If we exchange
- key number by key name in the message later, we need correct info.
+ HACK HACK HACK
+ Prepare the list of keys for an error message.
+ It must match what the engine does internally in ::add_index().
+ Here we emulate what innobase_create_key_def() does.
+ Luckily, in 10.0 this will go away.
*/
KEY *save_key_info= table->key_info;
- table->key_info= key_info;
+ uint add_cnt= index_add_count, old_cnt= table->s->keys;
+ KEY *merged= (KEY*)thd->alloc((old_cnt + add_cnt) * sizeof(KEY));
+#define is_PK(K) (!my_strcasecmp(system_charset_info, (K)->name, "PRIMARY"))
+
+ if (is_PK(key_info))
+ {
+ merged[0]= key_info[0];
+ if (table->key_info && is_PK(table->key_info))
+ {
+ old_cnt--;
+ table->key_info++;
+ }
+ memcpy(merged + 1, table->key_info, old_cnt * sizeof(KEY));
+ memcpy(merged + old_cnt + 1, key_info + 1, (add_cnt - 1) * sizeof(KEY));
+ }
+ else
+ merged= key_info;
+
+ table->key_info= merged;
table->file->print_error(error, MYF(0));
table->key_info= save_key_info;
}
- goto err1;
+ goto err_new_table_cleanup;
}
+ pending_inplace_add_index= true;
}
/*end of if (index_add_count)*/
if (index_drop_count)
{
+ /* Currently we must finalize add index if we also drop indexes */
+ if (pending_inplace_add_index)
+ {
+ /* Committing index changes needs exclusive metadata lock. */
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE,
+ table_list->db,
+ table_list->table_name,
+ MDL_EXCLUSIVE));
+ if ((error= table->file->final_add_index(add, true)))
+ {
+ table->file->print_error(error, MYF(0));
+ goto err_new_table_cleanup;
+ }
+ pending_inplace_add_index= false;
+ }
/* The prepare_drop_index() method takes an array of key numbers. */
key_numbers= (uint*) thd->alloc(sizeof(uint) * index_drop_count);
keyno_p= key_numbers;
@@ -7662,18 +6954,39 @@ view_err:
Tell the handler to prepare for drop indexes.
This re-numbers the indexes to get rid of gaps.
*/
- if ((error= table->file->prepare_drop_index(table, key_numbers,
- index_drop_count)))
+ error= table->file->prepare_drop_index(table, key_numbers,
+ index_drop_count);
+ if (!error)
{
- table->file->print_error(error, MYF(0));
- goto err1;
+ /* Tell the handler to finally drop the indexes. */
+ error= table->file->final_drop_index(table);
}
- /* Tell the handler to finally drop the indexes. */
- if ((error= table->file->final_drop_index(table)))
+ if (error)
{
table->file->print_error(error, MYF(0));
- goto err1;
+ if (index_add_count) // Drop any new indexes added.
+ {
+ /*
+ Temporarily set table-key_info to include information about the
+ indexes added above that we now need to drop.
+ */
+ KEY *save_key_info= table->key_info;
+ table->key_info= key_info_buffer;
+ if ((error= table->file->prepare_drop_index(table, index_add_buffer,
+ index_add_count)))
+ table->file->print_error(error, MYF(0));
+ else if ((error= table->file->final_drop_index(table)))
+ table->file->print_error(error, MYF(0));
+ table->key_info= save_key_info;
+ }
+
+ /*
+ Mark this TABLE instance as stale to avoid
+ out-of-sync index information.
+ */
+ table->m_needs_reopen= true;
+ goto err_new_table_cleanup;
}
}
/*end of if (index_drop_count)*/
@@ -7685,8 +6998,8 @@ view_err:
/* Need to commit before a table is unlocked (NDB requirement). */
DBUG_PRINT("info", ("Committing before unlocking table"));
- if (ha_autocommit_or_rollback(thd, 0) || end_active_trans(thd))
- goto err1;
+ if (trans_commit_stmt(thd) || trans_commit_implicit(thd))
+ goto err_new_table_cleanup;
}
/*end of if (! new_table) for add/drop index*/
@@ -7694,56 +7007,69 @@ view_err:
if (table->s->tmp_table != NO_TMP_TABLE)
{
- /* We changed a temporary table */
+ /*
+ In-place operations are not supported for temporary tables, so
+ we don't have to call final_add_index() in this case. The assert
+ verifies that in-place add index has not been done.
+ */
+ DBUG_ASSERT(!pending_inplace_add_index);
/* Close lock if this is a transactional table */
if (thd->lock)
{
- mysql_unlock_tables(thd, thd->lock);
- thd->lock=0;
+ if (thd->locked_tables_mode != LTM_LOCK_TABLES &&
+ thd->locked_tables_mode != LTM_PRELOCKED_UNDER_LOCK_TABLES)
+ {
+ mysql_unlock_tables(thd, thd->lock);
+ thd->lock=0;
+ }
+ else
+ {
+ /*
+ If LOCK TABLES list is not empty and contains this table,
+ unlock the table and remove the table from this list.
+ */
+ mysql_lock_remove(thd, thd->lock, table);
+ }
}
- /*
- If LOCK TABLES list is not empty and contains this table,
- unlock the table and remove the table from this list.
- */
- mysql_lock_remove(thd, thd->locked_tables, table, FALSE);
/* Remove link to old table and rename the new one */
close_temporary_table(thd, table, 1, 1);
/* Should pass the 'new_name' as we store table name in the cache */
if (rename_temporary_table(thd, new_table, new_db, new_name))
- goto err1;
+ goto err_new_table_cleanup;
/* We don't replicate alter table statement on temporary tables */
- if (!thd->current_stmt_binlog_row_based &&
+ if (!thd->is_current_stmt_binlog_format_row() &&
write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
DBUG_RETURN(TRUE);
goto end_temporary;
}
+ /*
+ Close the intermediate table that will be the new table, but do
+ not delete it! Even altough MERGE tables do not have their children
+ attached here it is safe to call close_temporary_table().
+ */
if (new_table)
{
- /*
- Close the intermediate table that will be the new table.
- Note that MERGE tables do not have their children attached here.
- */
- intern_close_table(new_table);
- my_free(new_table,MYF(0));
+ close_temporary_table(thd, new_table, 1, 0);
+ new_table= 0;
}
DEBUG_SYNC(thd, "alter_table_before_rename_result_table");
- VOID(pthread_mutex_lock(&LOCK_open));
/*
Data is copied. Now we:
- 1) Wait until all other threads close old version of table.
+ 1) Wait until all other threads will stop using old version of table
+ by upgrading shared metadata lock to exclusive one.
2) Close instances of table open by this thread and replace them
- with exclusive name-locks.
+ with placeholders to simplify reopen process.
3) Rename the old table to a temp name, rename the new one to the
old name.
4) If we are under LOCK TABLES and don't do ALTER TABLE ... RENAME
we reopen new version of table.
5) Write statement to the binary log.
6) If we are under LOCK TABLES and do ALTER TABLE ... RENAME we
- remove name-locks from list of open tables and table cache.
- 7) If we are not not under LOCK TABLES we rely on close_thread_tables()
- call to remove name-locks from table cache and list of open table.
+ remove placeholders and release metadata locks.
+ 7) If we are not not under LOCK TABLES we rely on the caller
+ (mysql_execute_command()) to release metadata locks.
*/
thd_proc_info(thd, "rename result table");
@@ -7752,10 +7078,40 @@ view_err:
if (lower_case_table_names)
my_casedn_str(files_charset_info, old_name);
- wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME);
- close_data_files_and_morph_locks(thd, db, table_name);
+ if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME,
+ TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE))
+ {
+ if (pending_inplace_add_index)
+ {
+ pending_inplace_add_index= false;
+ table->file->final_add_index(add, false);
+ }
+ // Mark this TABLE instance as stale to avoid out-of-sync index information.
+ table->m_needs_reopen= true;
+ goto err_new_table_cleanup;
+ }
+ if (pending_inplace_add_index)
+ {
+ pending_inplace_add_index= false;
+ DBUG_EXECUTE_IF("alter_table_rollback_new_index", {
+ table->file->final_add_index(add, false);
+ my_error(ER_UNKNOWN_ERROR, MYF(0));
+ goto err_new_table_cleanup;
+ });
+ if ((error= table->file->final_add_index(add, true)))
+ {
+ table->file->print_error(error, MYF(0));
+ goto err_new_table_cleanup;
+ }
+ }
+
+ close_all_tables_for_name(thd, table->s,
+ new_name != table_name || new_db != db ?
+ HA_EXTRA_PREPARE_FOR_RENAME :
+ HA_EXTRA_NOT_USED);
error=0;
+ table_list->table= table= 0; /* Safety */
save_old_db_type= old_db_type;
/*
@@ -7765,7 +7121,7 @@ view_err:
it should become the actual table. Later, we will recycle the old table.
However, in case of ALTER TABLE RENAME there might be no intermediate
table. This is when the old and new tables are compatible, according to
- compare_table(). Then, we need one additional call to
+ mysql_compare_table(). Then, we need one additional call to
mysql_rename_table() with flag NO_FRM_RENAME, which does nothing else but
actual rename in the SE and the FRM is not touched. Note that, if the
table is renamed and the SE is also changed, then an intermediate table
@@ -7781,29 +7137,59 @@ view_err:
FN_TO_IS_TMP))
{
error=1;
- VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
+ (void) quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP);
}
else if (mysql_rename_table(new_db_type, new_db, tmp_name, new_db,
- new_alias, FN_FROM_IS_TMP) ||
- ((new_name != table_name || new_db != db) && // we also do rename
- (need_copy_table != ALTER_TABLE_METADATA_ONLY ||
- mysql_rename_table(save_old_db_type, db, table_name, new_db,
- new_alias, NO_FRM_RENAME)) &&
- Table_triggers_list::change_table_name(thd, db, table_name,
- new_db, new_alias)))
+ new_alias, FN_FROM_IS_TMP))
{
/* Try to get everything back. */
- error=1;
- VOID(quick_rm_table(new_db_type,new_db,new_alias, 0));
- VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
- VOID(mysql_rename_table(old_db_type, db, old_name, db, alias,
- FN_FROM_IS_TMP));
+ error= 1;
+ (void) quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP);
+ (void) mysql_rename_table(old_db_type, db, old_name, db, alias,
+ FN_FROM_IS_TMP | NO_FK_CHECKS);
+ }
+ else if (new_name != table_name || new_db != db)
+ {
+ if (need_copy_table == ALTER_TABLE_METADATA_ONLY &&
+ mysql_rename_table(save_old_db_type, db, table_name, new_db,
+ new_alias, NO_FRM_RENAME))
+ {
+ /* Try to get everything back. */
+ error= 1;
+ (void) quick_rm_table(new_db_type, new_db, new_alias, 0);
+ (void) mysql_rename_table(old_db_type, db, old_name, db, alias,
+ FN_FROM_IS_TMP | NO_FK_CHECKS);
+ }
+ else if (Table_triggers_list::change_table_name(thd, db, alias,
+ table_name, new_db,
+ new_alias))
+ {
+ /* Try to get everything back. */
+ error= 1;
+ (void) quick_rm_table(new_db_type, new_db, new_alias, 0);
+ (void) mysql_rename_table(old_db_type, db, old_name, db,
+ alias, FN_FROM_IS_TMP | NO_FK_CHECKS);
+ /*
+ If we were performing "fast"/in-place ALTER TABLE we also need
+ to restore old name of table in storage engine as a separate
+ step, as the above rename affects .FRM only.
+ */
+ if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
+ {
+ (void) mysql_rename_table(save_old_db_type, new_db, new_alias,
+ db, table_name,
+ NO_FRM_RENAME | NO_FK_CHECKS);
+ }
+ }
}
+ if (! error)
+ (void) quick_rm_table(old_db_type, db, old_name, FN_IS_TMP);
+
if (error)
{
/* This shouldn't happen. But let us play it safe. */
- goto err_with_placeholders;
+ goto err_with_mdl;
}
if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
@@ -7813,53 +7199,46 @@ view_err:
To do this we need to obtain a handler object for it.
NO need to tamper with MERGE tables. The real open is done later.
*/
- TABLE *t_table;
+ Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN);
+ TABLE_LIST temp_table_list;
+ TABLE_LIST *t_table_list;
if (new_name != table_name || new_db != db)
{
- table_list->alias= new_name;
- table_list->table_name= new_name;
- table_list->table_name_length= strlen(new_name);
- table_list->db= new_db;
- table_list->db_length= strlen(new_db);
- table_list->table= name_lock;
- if (reopen_name_locked_table(thd, table_list, FALSE))
- goto err_with_placeholders;
- t_table= table_list->table;
+ temp_table_list.init_one_table(new_db, strlen(new_db),
+ new_name, strlen(new_name),
+ new_name, TL_READ_NO_INSERT);
+ temp_table_list.mdl_request.ticket= target_mdl_request.ticket;
+ t_table_list= &temp_table_list;
}
else
{
- if (reopen_table(table))
- goto err_with_placeholders;
- t_table= table;
- }
- /* Tell the handler that a new frm file is in place. */
- if (t_table->file->ha_create_handler_files(path, NULL, CHF_INDEX_FLAG,
- create_info))
- goto err_with_placeholders;
- if (thd->locked_tables && new_name == table_name && new_db == db)
- {
/*
- We are going to reopen table down on the road, so we have to restore
- state of the TABLE object which we used for obtaining of handler
- object to make it suitable for reopening.
+ Under LOCK TABLES, we have a different mdl_lock_ticket
+ points to a different instance than the one set initially
+ to request the lock.
*/
- DBUG_ASSERT(t_table == table);
- table->open_placeholder= 1;
- close_handle_and_leave_table_as_lock(table);
+ table_list->mdl_request.ticket= mdl_ticket;
+ t_table_list= table_list;
+ }
+ if (open_table(thd, t_table_list, thd->mem_root, &ot_ctx))
+ {
+ goto err_with_mdl;
}
- }
- VOID(quick_rm_table(old_db_type, db, old_name, FN_IS_TMP));
+ /* Tell the handler that a new frm file is in place. */
+ error= t_table_list->table->file->ha_create_handler_files(path, NULL,
+ CHF_INDEX_FLAG,
+ create_info);
+
+ DBUG_ASSERT(thd->open_tables == t_table_list->table);
+ close_thread_table(thd, &thd->open_tables);
+ t_table_list->table= NULL;
- if (thd->locked_tables && new_name == table_name && new_db == db)
- {
- thd->in_lock_tables= 1;
- error= reopen_tables(thd, 1, 1);
- thd->in_lock_tables= 0;
if (error)
- goto err_with_placeholders;
+ goto err_with_mdl;
}
- VOID(pthread_mutex_unlock(&LOCK_open));
+ if (thd->locked_tables_list.reopen_tables(thd))
+ goto err_with_mdl;
thd_proc_info(thd, "end");
@@ -7871,7 +7250,7 @@ view_err:
db, table_name);
DBUG_ASSERT(!(mysql_bin_log.is_open() &&
- thd->current_stmt_binlog_row_based &&
+ thd->is_current_stmt_binlog_format_row() &&
(create_info->options & HA_LEX_CREATE_TMP_TABLE)));
if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
DBUG_RETURN(TRUE);
@@ -7879,58 +7258,46 @@ view_err:
table_list->table=0; // For query cache
query_cache_invalidate3(thd, table_list, 0);
- if (thd->locked_tables && (new_name != table_name || new_db != db))
+ if (thd->locked_tables_mode == LTM_LOCK_TABLES ||
+ thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)
{
- /*
- If are we under LOCK TABLES and did ALTER TABLE with RENAME we need
- to remove placeholders for the old table and for the target table
- from the list of open tables and table cache. If we are not under
- LOCK TABLES we can rely on close_thread_tables() doing this job.
- */
- pthread_mutex_lock(&LOCK_open);
- unlink_open_table(thd, table, FALSE);
- unlink_open_table(thd, name_lock, FALSE);
- pthread_mutex_unlock(&LOCK_open);
+ if ((new_name != table_name || new_db != db))
+ thd->mdl_context.release_all_locks_for_name(mdl_ticket);
+ else
+ mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
}
end_temporary:
my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
(ulong) (copied + deleted), (ulong) deleted,
- (ulong) thd->cuted_fields);
+ (ulong) thd->warning_info->statement_warn_count());
my_ok(thd, copied + deleted, 0L, tmp_name);
- thd->some_tables_deleted=0;
DBUG_RETURN(FALSE);
-close_table_and_return_error:
- if (new_table && table->s->tmp_table == NO_TMP_TABLE)
- {
- /* This is not a temporary table, so close it the normal way */
- new_table->s->deleting= TRUE;
- intern_close_table(new_table);
- my_free(new_table,MYF(0));
- new_table= 0; // This forces call to quick_rm_table() below
- }
-
-err1:
+err_new_table_cleanup:
if (new_table)
{
/* close_temporary_table() frees the new_table pointer. */
close_temporary_table(thd, new_table, 1, 1);
}
else
- VOID(quick_rm_table(new_db_type, new_db, tmp_name,
- create_info->frm_only
- ? FN_IS_TMP | FRM_ONLY
- : FN_IS_TMP));
+ (void) quick_rm_table(new_db_type, new_db, tmp_name,
+ create_info->frm_only ? FN_IS_TMP | FRM_ONLY : FN_IS_TMP);
err:
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ /* If prep_alter_part_table created an intermediate table, destroy it. */
+ if (table_for_fast_alter_partition)
+ close_temporary(table_for_fast_alter_partition, 1, 0);
+#endif /* WITH_PARTITION_STORAGE_ENGINE */
/*
No default value was provided for a DATE/DATETIME field, the
current sql_mode doesn't allow the '0000-00-00' value and
the table to be altered isn't empty.
Report error here.
*/
- if (alter_info->error_if_not_empty && thd->row_count)
+ if (alter_info->error_if_not_empty &&
+ thd->warning_info->current_row_for_warning())
{
const char *f_val= 0;
enum enum_mysql_timestamp_type t_type= MYSQL_TIMESTAMP_DATE;
@@ -7951,35 +7318,77 @@ err:
}
bool save_abort_on_warning= thd->abort_on_warning;
thd->abort_on_warning= TRUE;
- make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
f_val, strlength(f_val), t_type,
alter_info->datetime_field->field_name);
thd->abort_on_warning= save_abort_on_warning;
}
- if (name_lock)
- {
- pthread_mutex_lock(&LOCK_open);
- unlink_open_table(thd, name_lock, FALSE);
- pthread_mutex_unlock(&LOCK_open);
- }
+
DBUG_RETURN(TRUE);
-err_with_placeholders:
+err_with_mdl:
/*
- An error happened while we were holding exclusive name-lock on table
- being altered. To be safe under LOCK TABLES we should remove placeholders
- from list of open tables list and table cache.
+ An error happened while we were holding exclusive name metadata lock
+ on table being altered. To be safe under LOCK TABLES we should
+ remove all references to the altered table from the list of locked
+ tables and release the exclusive metadata lock.
*/
- unlink_open_table(thd, table, FALSE);
- if (name_lock)
- unlink_open_table(thd, name_lock, FALSE);
- VOID(pthread_mutex_unlock(&LOCK_open));
+ thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
+ thd->mdl_context.release_all_locks_for_name(mdl_ticket);
DBUG_RETURN(TRUE);
}
/* Copy all rows from one table to another */
+
+
+/**
+ Prepare the transaction for the alter table's copy phase.
+*/
+
+bool mysql_trans_prepare_alter_copy_data(THD *thd)
+{
+ DBUG_ENTER("mysql_trans_prepare_alter_copy_data");
+ /*
+ Turn off recovery logging since rollback of an alter table is to
+ delete the new table so there is no need to log the changes to it.
+
+ This needs to be done before external_lock.
+ */
+ if (ha_enable_transaction(thd, FALSE))
+ DBUG_RETURN(TRUE);
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Commit the copy phase of the alter table.
+*/
+
+bool mysql_trans_commit_alter_copy_data(THD *thd)
+{
+ bool error= FALSE;
+ DBUG_ENTER("mysql_trans_commit_alter_copy_data");
+
+ if (ha_enable_transaction(thd, TRUE))
+ DBUG_RETURN(TRUE);
+
+ /*
+ Ensure that the new table is saved properly to disk before installing
+ the new .frm.
+ And that InnoDB's internal latches are released, to avoid deadlock
+ when waiting on other instances of the table before rename (Bug#54747).
+ */
+ if (trans_commit_stmt(thd))
+ error= TRUE;
+ if (trans_commit_implicit(thd))
+ error= TRUE;
+
+ DBUG_RETURN(error);
+}
+
+
static int
copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
List<Create_field> &create,
@@ -8001,7 +7410,7 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
List<Item> all_fields;
ha_rows examined_rows;
bool auto_increment_field_copied= 0;
- ulong save_sql_mode= thd->variables.sql_mode;
+ ulonglong save_sql_mode= thd->variables.sql_mode;
ulonglong prev_insert_id, time_to_report_progress;
List_iterator<Create_field> it(create);
Create_field *def;
@@ -8010,13 +7419,7 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
/* Two or 3 stages; Sorting, copying data and update indexes */
thd_progress_init(thd, 2 + test(order));
- /*
- Turn off recovery logging since rollback of an alter table is to
- delete the new table so there is no need to log the changes to it.
-
- This needs to be done before external_lock
- */
- if (ha_enable_transaction(thd, FALSE))
+ if (mysql_trans_prepare_alter_copy_data(thd))
goto err;
errpos=1;
@@ -8106,7 +7509,7 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
errpos= 4;
if (ignore)
to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
- thd->row_count= 0;
+ thd->warning_info->reset_current_row_for_warning();
restore_record(to, s->default_values); // Create empty record
thd->progress.max_counter= from->file->records();
@@ -8122,7 +7525,6 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
}
if (from->vfield)
update_virtual_fields(thd, from);
- thd->row_count++;
if (++thd->progress.counter >= time_to_report_progress)
{
time_to_report_progress+= MY_HOW_OFTEN_TO_WRITE/10;
@@ -8173,12 +7575,14 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
(to->key_info[0].key_part[0].field->flags &
AUTO_INCREMENT_FLAG))
err_msg= ER(ER_DUP_ENTRY_AUTOINCREMENT_CASE);
- to->file->print_keydup_error(key_nr, err_msg);
+ to->file->print_keydup_error(key_nr, err_msg, MYF(0));
+ error= 1;
break;
}
}
to->file->print_error(error,MYF(0));
+ error= 1;
break;
}
to->file->restore_auto_increment(prev_insert_id);
@@ -8186,6 +7590,7 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
}
else
found_count++;
+ thd->warning_info->inc_current_row_for_warning();
}
err:
@@ -8198,7 +7603,10 @@ err:
thd_progress_next_stage(thd);
if (error > 0)
+ {
+ /* We are going to drop the temporary table */
to->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
+ }
if (errpos >= 3 && to->file->ha_end_bulk_insert() && error <= 0)
{
to->file->print_error(my_errno,MYF(0));
@@ -8206,17 +7614,8 @@ err:
}
to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
- if (errpos >= 1 && ha_enable_transaction(thd, TRUE))
+ if (errpos >= 1 && mysql_trans_commit_alter_copy_data(thd))
error= 1;
-
- /*
- Ensure that the new table is saved properly to disk so that we
- can do a rename
- */
- if (ha_autocommit_or_rollback(thd, 0))
- error=1;
- if (end_active_trans(thd))
- error=1;
thd->variables.sql_mode= save_sql_mode;
thd->abort_on_warning= 0;
@@ -8227,34 +7626,11 @@ err:
error=1;
if (error < 0 && to->file->extra(HA_EXTRA_PREPARE_FOR_RENAME))
error= 1;
+ thd_progress_end(thd);
DBUG_RETURN(error > 0 ? -1 : 0);
}
-/* Prepare, run and cleanup for mysql_recreate_table() */
-
-static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list)
-{
- bool result_code;
- DBUG_ENTER("admin_recreate_table");
-
- ha_autocommit_or_rollback(thd, 1);
- close_thread_tables(thd);
- tmp_disable_binlog(thd); // binlogging is done by caller if wanted
- result_code= mysql_recreate_table(thd, table_list);
- reenable_binlog(thd);
- /*
- mysql_recreate_table() can push OK or ERROR.
- Clear 'OK' status. If there is an error, keep it:
- we will store the error message in a result set row
- and then clear.
- */
- if (thd->main_da.is_ok())
- thd->main_da.reset_diagnostics_area();
- DBUG_RETURN(result_code);
-}
-
-
/*
Recreates tables by calling mysql_alter_table().
@@ -8278,6 +7654,12 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list)
uninitialized data. open_tables() could fail.
*/
table_list->table= NULL;
+ /* Same applies to MDL ticket. */
+ table_list->mdl_request.ticket= NULL;
+ /* Set lock type which is appropriate for ALTER TABLE. */
+ table_list->lock_type= TL_READ_NO_INSERT;
+ /* Same applies to MDL request. */
+ table_list->mdl_request.set_type(MDL_SHARED_NO_WRITE);
bzero((char*) &create_info, sizeof(create_info));
create_info.row_type=ROW_TYPE_NOT_USED;
@@ -8299,12 +7681,18 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
Protocol *protocol= thd->protocol;
DBUG_ENTER("mysql_checksum_table");
+ /*
+ CHECKSUM TABLE returns results and rollbacks statement transaction,
+ so it should not be used in stored function or trigger.
+ */
+ DBUG_ASSERT(! thd->in_sub_stmt);
+
field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
item->maybe_null= 1;
field_list.push_back(item= new Item_int("Checksum", (longlong) 1,
MY_INT64_NUM_DECIMAL_DIGITS));
item->maybe_null= 1;
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
@@ -8316,8 +7704,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
strxmov(table_name, table->db ,".", table->table_name, NullS);
- t= table->table= open_n_lock_single_table(thd, table, TL_READ);
- thd->clear_error(); // these errors shouldn't get client
+ t= table->table= open_n_lock_single_table(thd, table, TL_READ, 0);
protocol->prepare_for_resend();
protocol->store(table_name, system_charset_info);
@@ -8326,16 +7713,13 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
{
/* Table didn't exist */
protocol->store_null();
- thd->clear_error();
}
else
{
/* Call ->checksum() if the table checksum matches 'old_mode' settings */
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)))
+ (((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());
else if (check_opt->flags & T_QUICK)
protocol->store_null();
@@ -8385,8 +7769,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
{
Field *f= t->field[i];
- if (! thd->variables.old_mode &&
- f->is_real_null(0))
+ if (! thd->variables.old_mode && f->is_real_null(0))
continue;
/*
BLOB and VARCHAR have pointers in their field, we must convert
@@ -8417,10 +7800,29 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
t->file->ha_rnd_end();
}
}
- thd->clear_error();
+ trans_rollback_stmt(thd);
close_thread_tables(thd);
+ /*
+ Don't release metadata locks, this will be done at
+ statement end.
+ */
table->table=0; // For query cache
}
+
+ if (thd->transaction_rollback_request)
+ {
+ /*
+ If transaction rollback was requested we honor it. To do this we
+ abort statement and return error as not only CHECKSUM TABLE is
+ rolled back but the whole transaction in which it was used.
+ */
+ thd->protocol->remove_last_row();
+ goto err;
+ }
+
+ /* Hide errors from client. Return NULL for problematic tables instead. */
+ thd->clear_error();
+
if (protocol->write())
goto err;
}
@@ -8428,23 +7830,36 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
my_eof(thd);
DBUG_RETURN(FALSE);
- err:
- close_thread_tables(thd); // Shouldn't be needed
- if (table)
- table->table=0;
+err:
DBUG_RETURN(TRUE);
}
-static bool check_engine(THD *thd, const char *table_name,
- HA_CREATE_INFO *create_info)
+/**
+ @brief Check if the table can be created in the specified storage engine.
+
+ Checks if the storage engine is enabled and supports the given table
+ type (e.g. normal, temporary, system). May do engine substitution
+ if the requested engine is disabled.
+
+ @param thd Thread descriptor.
+ @param db_name Database name.
+ @param table_name Name of table to be created.
+ @param create_info Create info from parser, including engine.
+
+ @retval true Engine not available/supported, error has been reported.
+ @retval false Engine available/supported.
+*/
+static bool check_engine(THD *thd, const char *db_name,
+ const char *table_name, HA_CREATE_INFO *create_info)
{
+ DBUG_ENTER("check_engine");
handlerton **new_engine= &create_info->db_type;
handlerton *req_engine= *new_engine;
bool no_substitution=
test(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION);
if (!(*new_engine= ha_checktype(thd, ha_legacy_type(req_engine),
no_substitution, 1)))
- return TRUE;
+ DBUG_RETURN(true);
if (req_engine && req_engine != *new_engine)
{
@@ -8462,9 +7877,10 @@ static bool check_engine(THD *thd, const char *table_name,
my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0),
hton_name(*new_engine)->str, "TEMPORARY");
*new_engine= 0;
- return TRUE;
+ DBUG_RETURN(true);
}
*new_engine= myisam_hton;
}
- return FALSE;
+
+ DBUG_RETURN(false);
}
diff --git a/sql/sql_table.h b/sql/sql_table.h
new file mode 100644
index 00000000000..838d763cbc8
--- /dev/null
+++ b/sql/sql_table.h
@@ -0,0 +1,226 @@
+/* Copyright (c) 2006, 2013, 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 SQL_TABLE_INCLUDED
+#define SQL_TABLE_INCLUDED
+
+#include "my_global.h" /* my_bool */
+#include "my_sys.h" // pthread_mutex_t
+
+class Alter_info;
+class Create_field;
+struct TABLE_LIST;
+class THD;
+struct TABLE;
+struct handlerton;
+typedef struct st_ha_check_opt HA_CHECK_OPT;
+typedef struct st_ha_create_information HA_CREATE_INFO;
+typedef struct st_key KEY;
+typedef struct st_key_cache KEY_CACHE;
+typedef struct st_lock_param_type ALTER_PARTITION_PARAM_TYPE;
+typedef struct st_order ORDER;
+class Alter_table_change_level;
+
+enum ddl_log_entry_code
+{
+ /*
+ DDL_LOG_EXECUTE_CODE:
+ This is a code that indicates that this is a log entry to
+ be executed, from this entry a linked list of log entries
+ can be found and executed.
+ DDL_LOG_ENTRY_CODE:
+ An entry to be executed in a linked list from an execute log
+ entry.
+ DDL_IGNORE_LOG_ENTRY_CODE:
+ An entry that is to be ignored
+ */
+ DDL_LOG_EXECUTE_CODE = 'e',
+ DDL_LOG_ENTRY_CODE = 'l',
+ DDL_IGNORE_LOG_ENTRY_CODE = 'i'
+};
+
+enum ddl_log_action_code
+{
+ /*
+ The type of action that a DDL_LOG_ENTRY_CODE entry is to
+ perform.
+ DDL_LOG_DELETE_ACTION:
+ Delete an entity
+ DDL_LOG_RENAME_ACTION:
+ Rename an entity
+ DDL_LOG_REPLACE_ACTION:
+ Rename an entity after removing the previous entry with the
+ new name, that is replace this entry.
+ */
+ DDL_LOG_DELETE_ACTION = 'd',
+ DDL_LOG_RENAME_ACTION = 'r',
+ DDL_LOG_REPLACE_ACTION = 's'
+};
+
+
+typedef struct st_ddl_log_entry
+{
+ const char *name;
+ const char *from_name;
+ const char *handler_name;
+ uint next_entry;
+ uint entry_pos;
+ enum ddl_log_entry_code entry_type;
+ enum ddl_log_action_code action_type;
+ /*
+ Most actions have only one phase. REPLACE does however have two
+ phases. The first phase removes the file with the new name if
+ there was one there before and the second phase renames the
+ old name to the new name.
+ */
+ char phase;
+} DDL_LOG_ENTRY;
+
+typedef struct st_ddl_log_memory_entry
+{
+ uint entry_pos;
+ struct st_ddl_log_memory_entry *next_log_entry;
+ struct st_ddl_log_memory_entry *prev_log_entry;
+ struct st_ddl_log_memory_entry *next_active_log_entry;
+} DDL_LOG_MEMORY_ENTRY;
+
+
+enum enum_explain_filename_mode
+{
+ EXPLAIN_ALL_VERBOSE= 0,
+ EXPLAIN_PARTITIONS_VERBOSE,
+ EXPLAIN_PARTITIONS_AS_COMMENT
+};
+
+/* Maximum length of GEOM_POINT Field */
+#define MAX_LEN_GEOM_POINT_FIELD 25
+
+/* depends on errmsg.txt Database `db`, Table `t` ... */
+#define EXPLAIN_FILENAME_MAX_EXTRA_LENGTH 63
+
+#define WFRM_WRITE_SHADOW 1
+#define WFRM_INSTALL_SHADOW 2
+#define WFRM_PACK_FRM 4
+#define WFRM_KEEP_SHARE 8
+
+/* Flags for conversion functions. */
+#define FN_FROM_IS_TMP (1 << 0)
+#define FN_TO_IS_TMP (1 << 1)
+#define FN_IS_TMP (FN_FROM_IS_TMP | FN_TO_IS_TMP)
+#define NO_FRM_RENAME (1 << 2)
+#define FRM_ONLY (1 << 3)
+/** Don't check foreign key constraints while renaming table */
+#define NO_FK_CHECKS (1 << 4)
+
+uint filename_to_tablename(const char *from, char *to, uint to_length
+#ifndef DBUG_OFF
+ , bool stay_quiet = false
+#endif /* DBUG_OFF */
+ );
+uint tablename_to_filename(const char *from, char *to, uint to_length);
+uint check_n_cut_mysql50_prefix(const char *from, char *to, uint to_length);
+bool check_mysql50_prefix(const char *name);
+uint build_table_filename(char *buff, size_t bufflen, const char *db,
+ const char *table, const char *ext, uint flags);
+uint build_table_shadow_filename(char *buff, size_t bufflen,
+ ALTER_PARTITION_PARAM_TYPE *lpt);
+bool check_table_file_presence(char *old_path, char *path, const char *db,
+ const char *table_name, const char *alias,
+ bool issue_error);
+bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
+ HA_CREATE_INFO *create_info,
+ Alter_info *alter_info);
+bool mysql_create_table_no_lock(THD *thd, const char *db,
+ const char *table_name,
+ HA_CREATE_INFO *create_info,
+ Alter_info *alter_info,
+ bool tmp_table, uint select_field_count,
+ bool *is_trans);
+bool mysql_prepare_alter_table(THD *thd, TABLE *table,
+ HA_CREATE_INFO *create_info,
+ Alter_info *alter_info);
+bool mysql_trans_prepare_alter_copy_data(THD *thd);
+bool mysql_trans_commit_alter_copy_data(THD *thd);
+bool mysql_alter_table(THD *thd, char *new_db, char *new_name,
+ HA_CREATE_INFO *create_info,
+ TABLE_LIST *table_list,
+ Alter_info *alter_info,
+ uint order_num, ORDER *order, bool ignore,
+ bool require_online);
+bool mysql_compare_tables(TABLE *table,
+ Alter_info *alter_info,
+ HA_CREATE_INFO *create_info,
+ uint order_num,
+ Alter_table_change_level *need_copy_table,
+ KEY **key_info_buffer,
+ uint **index_drop_buffer, uint *index_drop_count,
+ uint **index_add_buffer, uint *index_add_count,
+ uint *candidate_key_count);
+bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list);
+bool mysql_create_like_table(THD *thd, TABLE_LIST *table,
+ TABLE_LIST *src_table,
+ HA_CREATE_INFO *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_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);
+int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
+ bool drop_temporary, bool drop_view,
+ bool log_query);
+bool quick_rm_table(handlerton *base,const char *db,
+ const char *table_name, uint flags);
+void close_cached_table(THD *thd, TABLE *table);
+void sp_prepare_create_field(THD *thd, Create_field *sql_field);
+int prepare_create_field(Create_field *sql_field,
+ uint *blob_columns,
+ int *timestamps, int *timestamps_with_niladic,
+ longlong table_flags);
+CHARSET_INFO* get_sql_field_charset(Create_field *sql_field,
+ HA_CREATE_INFO *create_info);
+bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags);
+int write_bin_log(THD *thd, bool clear_error,
+ char const *query, ulong query_length,
+ bool is_trans= FALSE);
+bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
+ DDL_LOG_MEMORY_ENTRY **active_entry);
+bool write_execute_ddl_log_entry(uint first_entry,
+ bool complete,
+ DDL_LOG_MEMORY_ENTRY **active_entry);
+bool deactivate_ddl_log_entry(uint entry_no);
+void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry);
+bool sync_ddl_log();
+void release_ddl_log();
+void execute_ddl_log_recovery();
+bool execute_ddl_log_entry(THD *thd, uint first_entry);
+bool check_duplicate_warning(THD *thd, char *msg, ulong length);
+
+/*
+ These prototypes where under INNODB_COMPATIBILITY_HOOKS.
+*/
+uint explain_filename(THD* thd, const char *from, char *to, uint to_length,
+ enum_explain_filename_mode explain_mode);
+
+
+extern MYSQL_PLUGIN_IMPORT const char *primary_key_name;
+extern mysql_mutex_t LOCK_gdl;
+
+#endif /* SQL_TABLE_INCLUDED */
diff --git a/sql/sql_tablespace.cc b/sql/sql_tablespace.cc
index d4a14dea8b5..3f6daf7a9ec 100644
--- a/sql/sql_tablespace.cc
+++ b/sql/sql_tablespace.cc
@@ -1,6 +1,4 @@
-/*
- Copyright (c) 2006, 2007 MySQL AB, 2009, 2010 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,12 +11,15 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* drop and alter of tablespaces */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "sql_tablespace.h"
+#include "sql_table.h" // write_bin_log
+#include "sql_class.h" // THD
int mysql_alter_tablespace(THD *thd, st_alter_tablespace *ts_info)
{
@@ -34,7 +35,7 @@ int mysql_alter_tablespace(THD *thd, st_alter_tablespace *ts_info)
{
hton= ha_default_handlerton(thd);
if (ts_info->storage_engine != 0)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_USING_OTHER_HANDLER,
ER(ER_WARN_USING_OTHER_HANDLER),
hton_name(hton)->str,
@@ -64,7 +65,7 @@ int mysql_alter_tablespace(THD *thd, st_alter_tablespace *ts_info)
}
else
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_ILLEGAL_HA_CREATE_OPTION,
ER(ER_ILLEGAL_HA_CREATE_OPTION),
hton_name(hton)->str,
diff --git a/sql/sql_tablespace.h b/sql/sql_tablespace.h
new file mode 100644
index 00000000000..ae77d15cbcb
--- /dev/null
+++ b/sql/sql_tablespace.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_TABLESPACE_INCLUDED
+#define SQL_TABLESPACE_INCLUDED
+
+class THD;
+class st_alter_tablespace;
+
+int mysql_alter_tablespace(THD* thd, st_alter_tablespace *ts_info);
+
+#endif /* SQL_TABLESPACE_INCLUDED */
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index 010cc2f52c6..ec426e39ee3 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -1,6 +1,4 @@
-/*
- Copyright (c) 2000-2008 MySQL AB, 2009, 2010 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,15 +11,19 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* Write some debug info */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "sql_test.h"
+#include "sql_base.h" // table_def_cache, table_cache_count, unused_tables
+#include "sql_show.h" // calc_sum_of_all_status
#include "sql_select.h"
+#include "keycaches.h"
#include <hash.h>
#include <thr_alarm.h>
#if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_H)
@@ -43,7 +45,6 @@ static const char *lock_descriptions[] =
/* TL_READ_HIGH_PRIORITY */ "High priority read lock",
/* TL_READ_NO_INSERT */ "Read lock without concurrent inserts",
/* TL_WRITE_ALLOW_WRITE */ "Write lock that allows other writers",
- /* TL_WRITE_ALLOW_READ */ "Write lock, but allow reading",
/* TL_WRITE_CONCURRENT_INSERT */ "Concurrent insert lock",
/* TL_WRITE_DELAYED */ "Lock used by delayed insert",
/* TL_WRITE_DEFAULT */ NULL,
@@ -58,45 +59,55 @@ static const char *lock_descriptions[] =
void
print_where(COND *cond,const char *info, enum_query_type query_type)
{
+ char buff[1024];
+ String str(buff,(uint32) sizeof(buff), system_charset_info);
+ str.length(0);
+ str.extra_allocation(1024);
if (cond)
- {
- char buff[1024];
- String str(buff,(uint32) sizeof(buff), system_charset_info);
- str.length(0);
- str.extra_allocation(1024);
cond->print(&str, query_type);
- str.append('\0');
- DBUG_LOCK_FILE;
- (void) fprintf(DBUG_FILE,"\nWHERE:(%s) ",info);
- (void) fputs(str.c_ptr_safe(),DBUG_FILE);
- (void) fputc('\n',DBUG_FILE);
- DBUG_UNLOCK_FILE;
- }
+
+ DBUG_LOCK_FILE;
+ (void) fprintf(DBUG_FILE,"\nWHERE:(%s) %p ", info, cond);
+ (void) fputs(str.c_ptr_safe(),DBUG_FILE);
+ (void) fputc('\n',DBUG_FILE);
+ DBUG_UNLOCK_FILE;
}
+
/* This is for debugging purposes */
-void print_cached_tables(void)
+static void print_cached_tables(void)
{
uint idx,count,unused;
- TABLE *start_link,*lnk;
+ TABLE_SHARE *share;
+ TABLE *start_link, *lnk, *entry;
compile_time_assert(TL_WRITE_ONLY+1 == array_elements(lock_descriptions));
/* purecov: begin tested */
- VOID(pthread_mutex_lock(&LOCK_open));
+ mysql_mutex_lock(&LOCK_open);
puts("DB Table Version Thread Open Lock");
- for (idx=unused=0 ; idx < open_cache.records ; idx++)
+ for (idx=unused=0 ; idx < table_def_cache.records ; idx++)
{
- TABLE *entry=(TABLE*) hash_element(&open_cache,idx);
- printf("%-14.14s %-32s%6ld%8ld%6d %s\n",
- entry->s->db.str, entry->s->table_name.str, entry->s->version,
- entry->in_use ? entry->in_use->thread_id : 0L,
- entry->db_stat ? 1 : 0,
- entry->in_use ? lock_descriptions[(int)entry->reginfo.lock_type] : "Not in use");
- if (!entry->in_use)
+ share= (TABLE_SHARE*) my_hash_element(&table_def_cache, idx);
+
+ I_P_List_iterator<TABLE, TABLE_share> it(share->used_tables);
+ while ((entry= it++))
+ {
+ printf("%-14.14s %-32s%6ld%8ld%6d %s\n",
+ entry->s->db.str, entry->s->table_name.str, entry->s->version,
+ entry->in_use->thread_id, entry->db_stat ? 1 : 0,
+ lock_descriptions[(int)entry->reginfo.lock_type]);
+ }
+ it.init(share->free_tables);
+ while ((entry= it++))
+ {
unused++;
+ printf("%-14.14s %-32s%6ld%8ld%6d %s\n",
+ entry->s->db.str, entry->s->table_name.str, entry->s->version,
+ 0L, entry->db_stat ? 1 : 0, "Not in use");
+ }
}
count=0;
if ((start_link=lnk=unused_tables))
@@ -108,19 +119,20 @@ void print_cached_tables(void)
printf("unused_links isn't linked properly\n");
return;
}
- } while (count++ < open_cache.records && (lnk=lnk->next) != start_link);
+ } while (count++ < cached_open_tables() && (lnk=lnk->next) != start_link);
if (lnk != start_link)
{
printf("Unused_links aren't connected\n");
}
}
if (count != unused)
- printf("Unused_links (%d) doesn't match open_cache: %d\n", count,unused);
+ printf("Unused_links (%d) doesn't match table_def_cache: %d\n", count,
+ unused);
printf("\nCurrent refresh version: %ld\n",refresh_version);
- if (hash_check(&open_cache))
- printf("Error: File hash table is corrupted\n");
+ if (my_hash_check(&table_def_cache))
+ printf("Error: Table definition hash table is corrupted\n");
fflush(stdout);
- VOID(pthread_mutex_unlock(&LOCK_open));
+ mysql_mutex_unlock(&LOCK_open);
/* purecov: end */
return;
}
@@ -157,9 +169,8 @@ void TEST_filesort(SORT_FIELD *sortorder,uint s_length)
out.append(str);
}
}
- out.append('\0'); // Purify doesn't like c_ptr()
DBUG_LOCK_FILE;
- VOID(fputs("\nInfo about FILESORT\n",DBUG_FILE));
+ (void) fputs("\nInfo about FILESORT\n",DBUG_FILE);
fprintf(DBUG_FILE,"Sortorder: %s\n",out.c_ptr_safe());
DBUG_UNLOCK_FILE;
DBUG_VOID_RETURN;
@@ -176,8 +187,7 @@ TEST_join(JOIN *join)
DBUG_ENTER("TEST_join");
DBUG_LOCK_FILE;
- VOID(fputs("\nInfo about JOIN\n",DBUG_FILE));
-
+ (void) fputs("\nInfo about JOIN\n",DBUG_FILE);
while ((jt_range= it++))
{
/*
@@ -221,7 +231,7 @@ TEST_join(JOIN *join)
tab->select->quick->dbug_dump(18, FALSE);
}
else
- VOID(fputs(" select used\n",DBUG_FILE));
+ (void)fputs(" select used\n",DBUG_FILE);
}
if (tab->ref.key_parts)
{
@@ -229,7 +239,7 @@ TEST_join(JOIN *join)
" refs: %s\n", ref_key_parts[i].c_ptr_safe());
}
}
- VOID(fputs("\n",DBUG_FILE));
+ (void)fputs("\n",DBUG_FILE);
}
DBUG_UNLOCK_FILE;
DBUG_VOID_RETURN;
@@ -255,7 +265,7 @@ void print_keyuse(KEYUSE *keyuse)
fieldname= "FT_KEYPART";
else
fieldname= key_info->key_part[keyuse->keypart].field->field_name;
- longlong2str(keyuse->used_tables, buf2, 16, 0);
+ ll2str(keyuse->used_tables, buf2, 16, 0);
DBUG_LOCK_FILE;
fprintf(DBUG_FILE, "KEYUSE: %s.%s=%s optimize: %u used_tables: %s "
"ref_table_rows: %lu keypart_map: %0lx\n",
@@ -399,6 +409,15 @@ void print_sjm(SJ_MATERIALIZATION_INFO *sjm)
}
/* purecov: end */
+/*
+ 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)=
+ &List<Item_equal>::elem;
+TABLE_LIST* (List<TABLE_LIST>:: *dbug_list_table_list_elem_ptr)(int) =
+ &List<TABLE_LIST>::elem;
+
#endif
typedef struct st_debug_lock
@@ -410,8 +429,14 @@ typedef struct st_debug_lock
enum thr_lock_type type;
} TABLE_LOCK_INFO;
-static int dl_compare(TABLE_LOCK_INFO *a,TABLE_LOCK_INFO *b)
+C_MODE_START
+static int dl_compare(const void *p1, const void *p2)
{
+ TABLE_LOCK_INFO *a, *b;
+
+ a= (TABLE_LOCK_INFO *) p1;
+ b= (TABLE_LOCK_INFO *) p2;
+
if (a->thread_id > b->thread_id)
return 1;
if (a->thread_id < b->thread_id)
@@ -422,6 +447,7 @@ static int dl_compare(TABLE_LOCK_INFO *a,TABLE_LOCK_INFO *b)
return -1;
return 1;
}
+C_MODE_END
static void push_locks_into_array(DYNAMIC_ARRAY *ar, THR_LOCK_DATA *data,
@@ -441,7 +467,7 @@ static void push_locks_into_array(DYNAMIC_ARRAY *ar, THR_LOCK_DATA *data,
table_lock_info.lock_text=text;
// lock_type is also obtainable from THR_LOCK_DATA
table_lock_info.type=table->reginfo.lock_type;
- VOID(push_dynamic(ar,(uchar*) &table_lock_info));
+ (void) push_dynamic(ar,(uchar*) &table_lock_info);
}
}
}
@@ -461,18 +487,19 @@ static void push_locks_into_array(DYNAMIC_ARRAY *ar, THR_LOCK_DATA *data,
function so that we can easily add this if we ever need this.
*/
-static void display_table_locks(void)
+static void display_table_locks(void)
{
LIST *list;
+ void *saved_base;
DYNAMIC_ARRAY saved_table_locks;
- VOID(my_init_dynamic_array(&saved_table_locks,sizeof(TABLE_LOCK_INFO),open_cache.records + 20,50));
- VOID(pthread_mutex_lock(&THR_LOCK_lock));
+ (void) my_init_dynamic_array(&saved_table_locks,sizeof(TABLE_LOCK_INFO), cached_open_tables() + 20,50);
+ mysql_mutex_lock(&THR_LOCK_lock);
for (list= thr_lock_thread_list; list; list= list_rest(list))
{
THR_LOCK *lock=(THR_LOCK*) list->data;
- VOID(pthread_mutex_lock(&lock->mutex));
+ mysql_mutex_lock(&lock->mutex);
push_locks_into_array(&saved_table_locks, lock->write.data, FALSE,
"Locked - write");
push_locks_into_array(&saved_table_locks, lock->write_wait.data, TRUE,
@@ -481,16 +508,20 @@ static void display_table_locks(void)
"Locked - read");
push_locks_into_array(&saved_table_locks, lock->read_wait.data, TRUE,
"Waiting - read");
- VOID(pthread_mutex_unlock(&lock->mutex));
+ mysql_mutex_unlock(&lock->mutex);
}
- VOID(pthread_mutex_unlock(&THR_LOCK_lock));
- if (!saved_table_locks.elements) goto end;
-
- qsort((uchar*) dynamic_element(&saved_table_locks,0,TABLE_LOCK_INFO *),saved_table_locks.elements,sizeof(TABLE_LOCK_INFO),(qsort_cmp) dl_compare);
+ mysql_mutex_unlock(&THR_LOCK_lock);
+
+ if (!saved_table_locks.elements)
+ goto end;
+
+ saved_base= dynamic_element(&saved_table_locks, 0, TABLE_LOCK_INFO *);
+ my_qsort(saved_base, saved_table_locks.elements, sizeof(TABLE_LOCK_INFO),
+ dl_compare);
freeze_size(&saved_table_locks);
puts("\nThread database.table_name Locked/Waiting Lock_type\n");
-
+
unsigned int i;
for (i=0 ; i < saved_table_locks.elements ; i++)
{
@@ -503,8 +534,9 @@ end:
delete_dynamic(&saved_table_locks);
}
-
-static int print_key_cache_status(const char *name, KEY_CACHE *key_cache)
+C_MODE_START
+static int print_key_cache_status(const char *name, KEY_CACHE *key_cache,
+ void *unused __attribute__((unused)))
{
char llbuff1[22];
char llbuff2[22];
@@ -533,9 +565,11 @@ writes: %10s\n\
r_requests: %10s\n\
reads: %10s\n\n",
name,
- (ulong) key_cache->param_buff_size, key_cache->param_block_size,
- key_cache->param_division_limit, key_cache->param_age_threshold,
- key_cache->param_partitions,
+ (ulong)key_cache->param_buff_size,
+ (ulong)key_cache->param_block_size,
+ (ulong)key_cache->param_division_limit,
+ (ulong)key_cache->param_age_threshold,
+ (ulong)key_cache->param_partitions,
(ulong)stats.blocks_used,
(ulong)stats.blocks_changed,
llstr(stats.write_requests,llbuff1),
@@ -545,6 +579,7 @@ reads: %10s\n\n",
}
return 0;
}
+C_MODE_END
void mysql_print_status()
@@ -554,7 +589,7 @@ void mysql_print_status()
calc_sum_of_all_status(&tmp);
printf("\nStatus information:\n\n");
- VOID(my_getwd(current_dir, sizeof(current_dir),MYF(0)));
+ (void) my_getwd(current_dir, sizeof(current_dir),MYF(0));
printf("Current dir: %s\n", current_dir);
printf("Running threads: %d Stack size: %ld\n", thread_count,
(long) my_thread_stack_size);
@@ -564,8 +599,8 @@ void mysql_print_status()
#endif
/* Print key cache status */
puts("\nKey caches:");
- process_key_caches(print_key_cache_status);
- pthread_mutex_lock(&LOCK_status);
+ process_key_caches(print_key_cache_status, 0);
+ mysql_mutex_lock(&LOCK_status);
printf("\nhandler status:\n\
read_key: %10lu\n\
read_next: %10lu\n\
@@ -581,7 +616,7 @@ update: %10lu\n",
tmp.ha_write_count,
tmp.ha_delete_count,
tmp.ha_update_count);
- pthread_mutex_unlock(&LOCK_status);
+ mysql_mutex_unlock(&LOCK_status);
printf("\nTable status:\n\
Opened tables: %10lu\n\
Open tables: %10lu\n\
@@ -592,8 +627,8 @@ Open streams: %10lu\n",
(ulong) my_file_opened,
(ulong) my_stream_opened);
- ALARM_INFO alarm_info;
#ifndef DONT_USE_THR_ALARM
+ ALARM_INFO alarm_info;
thr_alarm_info(&alarm_info);
printf("\nAlarm status:\n\
Active alarms: %u\n\
@@ -601,15 +636,10 @@ Max used alarms: %u\n\
Next alarm time: %lu\n",
alarm_info.active_alarms,
alarm_info.max_used_alarms,
- alarm_info.next_alarm_time);
+ (ulong)alarm_info.next_alarm_time);
#endif
display_table_locks();
fflush(stdout);
- my_checkmalloc();
- fprintf(stdout,"\nBegin safemalloc memory dump:\n"); // tag needed for test suite
- TERMINATE(stdout, 1); // Write malloc information
- fprintf(stdout,"\nEnd safemalloc memory dump.\n");
- fflush(stdout);
#ifdef HAVE_MALLINFO
struct mallinfo info= mallinfo();
printf("\nMemory status:\n\
diff --git a/sql/sql_test.h b/sql/sql_test.h
new file mode 100644
index 00000000000..3c1ee188eeb
--- /dev/null
+++ b/sql/sql_test.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_TEST_INCLUDED
+#define SQL_TEST_INCLUDED
+
+#include "mysqld.h"
+
+class JOIN;
+struct TABLE_LIST;
+typedef class Item COND;
+typedef class st_select_lex SELECT_LEX;
+typedef struct st_sort_field SORT_FIELD;
+
+#ifndef DBUG_OFF
+void print_where(COND *cond,const char *info, enum_query_type query_type);
+void TEST_filesort(SORT_FIELD *sortorder,uint s_length);
+void TEST_join(JOIN *join);
+void print_plan(JOIN* join,uint idx, double record_count, double read_time,
+ double current_read_time, const char *info);
+void print_keyuse_array(DYNAMIC_ARRAY *keyuse_array);
+void print_sjm(SJ_MATERIALIZATION_INFO *sjm);
+void dump_TABLE_LIST_graph(SELECT_LEX *select_lex, TABLE_LIST* tl);
+#endif
+void mysql_print_status();
+
+#endif /* SQL_TEST_INCLUDED */
diff --git a/sql/time.cc b/sql/sql_time.cc
index 2cb3befe5a2..c5c65391758 100644
--- a/sql/time.cc
+++ b/sql/sql_time.cc
@@ -1,7 +1,5 @@
-/*
- Copyright (c) 2000-2007 MySQL AB, 2009 Sun Microsystems, Inc.
- Copyright (c) 2009-2013 Monty Program Ab
- Use is subject to license terms.
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2013 Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -14,13 +12,16 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* Functions to handle date and time */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h" // REQUIRED by other includes
+#include "sql_time.h"
+#include "tztime.h" // struct Time_zone
+#include "sql_class.h" // THD, MODE_INVALID_DATES, MODE_NO_ZERO_DATE
#include <m_ctype.h>
@@ -214,13 +215,13 @@ ulong convert_month_to_period(ulong month)
bool
-check_date_with_warn(const MYSQL_TIME *ltime, uint fuzzy_date,
+check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date,
timestamp_type ts_type)
{
int dummy_warnings;
if (check_date(ltime, fuzzy_date, &dummy_warnings))
{
- Lazy_string_time str(ltime);
+ ErrConvTime str(ltime);
make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
&str, ts_type, 0);
return true;
@@ -233,7 +234,7 @@ bool
adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec)
{
MYSQL_TIME copy= *ltime;
- Lazy_string_time str(&copy);
+ ErrConvTime str(&copy);
int warnings= 0;
if (check_time_range(ltime, dec, &warnings))
return true;
@@ -244,6 +245,70 @@ adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec)
}
/*
+ Convert a string to 8-bit representation,
+ for use in str_to_time/str_to_date/str_to_date.
+
+ In the future to_ascii() can be extended to convert
+ non-ASCII digits to ASCII digits
+ (for example, ARABIC-INDIC, DEVANAGARI, BENGALI, and so on)
+ so DATE/TIME/DATETIME values understand digits in the
+ respected scripts.
+*/
+static uint
+to_ascii(CHARSET_INFO *cs,
+ const char *src, uint src_length,
+ char *dst, uint dst_length)
+
+{
+ int cnvres;
+ my_wc_t wc;
+ const char *srcend= src + src_length;
+ char *dst0= dst, *dstend= dst + dst_length - 1;
+ while (dst < dstend &&
+ (cnvres= (cs->cset->mb_wc)(cs, &wc,
+ (const uchar*) src,
+ (const uchar*) srcend)) > 0 &&
+ wc < 128)
+ {
+ src+= cnvres;
+ *dst++= static_cast<char>(wc);
+ }
+ *dst= '\0';
+ return dst - dst0;
+}
+
+
+/* Character set-aware version of str_to_time() */
+timestamp_type
+str_to_time(CHARSET_INFO *cs, const char *str,uint length,
+ MYSQL_TIME *l_time, ulonglong fuzzydate, int *warning)
+{
+ char cnv[32];
+ if ((cs->state & MY_CS_NONASCII) != 0)
+ {
+ length= to_ascii(cs, str, length, cnv, sizeof(cnv));
+ str= cnv;
+ }
+ return str_to_time(str, length, l_time, fuzzydate, warning);
+}
+
+
+/* Character set-aware version of str_to_datetime() */
+timestamp_type str_to_datetime(CHARSET_INFO *cs,
+ const char *str, uint length,
+ MYSQL_TIME *l_time, ulonglong flags, int *was_cut)
+{
+ char cnv[32];
+ if ((cs->state & MY_CS_NONASCII) != 0)
+ {
+ length= to_ascii(cs, str, length, cnv, sizeof(cnv));
+ str= cnv;
+ }
+ return str_to_datetime(str, length, l_time, flags, was_cut);
+}
+
+
+/*
Convert a timestamp string to a MYSQL_TIME value and produce a warning
if string was truncated during conversion.
@@ -252,20 +317,24 @@ adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec)
*/
timestamp_type
-str_to_datetime_with_warn(const char *str, uint length, MYSQL_TIME *l_time,
- ulong flags)
+str_to_datetime_with_warn(CHARSET_INFO *cs,
+ const char *str, uint length, MYSQL_TIME *l_time,
+ ulonglong flags)
{
int was_cut;
THD *thd= current_thd;
timestamp_type ts_type;
- ts_type= str_to_datetime(str, length, l_time,
+ ts_type= str_to_datetime(cs, str, length, l_time,
(flags | (sql_mode_for_dates(thd))),
&was_cut);
if (was_cut || ts_type <= MYSQL_TIMESTAMP_ERROR)
make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
str, length, flags & TIME_TIME_ONLY ?
MYSQL_TIMESTAMP_TIME : ts_type, NullS);
+ DBUG_EXECUTE_IF("str_to_datetime_warn",
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_YES, str););
return ts_type;
}
@@ -278,14 +347,14 @@ str_to_datetime_with_warn(const char *str, uint length, MYSQL_TIME *l_time,
@param sec_part microsecond part of the number
@param ltime converted value will be written here
@param fuzzydate conversion flags (TIME_INVALID_DATE, etc)
- @param str original number, as a Lazy_string. For the warning
+ @param str original number, as an ErrConv. For the warning
@param field_name field name or NULL if not a field. For the warning
@returns 0 for success, 1 for a failure
*/
static bool number_to_time_with_warn(bool neg, ulonglong nr, ulong sec_part,
- MYSQL_TIME *ltime, ulong fuzzydate,
- const Lazy_string *str,
+ MYSQL_TIME *ltime, ulonglong fuzzydate,
+ const ErrConv *str,
const char *field_name)
{
int was_cut;
@@ -327,9 +396,9 @@ static bool number_to_time_with_warn(bool neg, ulonglong nr, ulong sec_part,
bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime,
- ulong fuzzydate, const char *field_name)
+ ulonglong fuzzydate, const char *field_name)
{
- const Lazy_string_double str(value);
+ const ErrConvDouble str(value);
bool neg= value < 0;
if (neg)
@@ -346,9 +415,9 @@ bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime,
bool decimal_to_datetime_with_warn(const my_decimal *value, MYSQL_TIME *ltime,
- ulong fuzzydate, const char *field_name)
+ ulonglong fuzzydate, const char *field_name)
{
- const Lazy_string_decimal str(value);
+ const ErrConvDecimal str(value);
ulonglong nr;
ulong sec_part;
bool neg= my_decimal2seconds(value, &nr, &sec_part);
@@ -358,9 +427,9 @@ bool decimal_to_datetime_with_warn(const my_decimal *value, MYSQL_TIME *ltime,
bool int_to_datetime_with_warn(bool neg, ulonglong value, MYSQL_TIME *ltime,
- ulong fuzzydate, const char *field_name)
+ ulonglong fuzzydate, const char *field_name)
{
- const Lazy_string_num str(neg ? -value : value, !neg);
+ const ErrConvInteger str(neg ? -value : value, !neg);
return number_to_time_with_warn(neg, value, 0, ltime,
fuzzydate, &str, field_name);
}
@@ -393,32 +462,6 @@ my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error_code)
/*
- Convert a time string to a MYSQL_TIME struct and produce a warning
- if string was cut during conversion.
-
- NOTE
- See str_to_time() for more info.
-
- RETURN
- 0 ok
- 1 wrong time value
-*/
-
-bool
-str_to_time_with_warn(const char *str, uint length, MYSQL_TIME *l_time,
- ulong fuzzydate)
-{
- int warning;
- bool ret_val= (str_to_time(str, length, l_time, fuzzydate, &warning) ==
- MYSQL_TIMESTAMP_ERROR);
- if (ret_val || warning)
- make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- str, length, MYSQL_TIMESTAMP_TIME, NullS);
- return ret_val;
-}
-
-
-/*
Convert a system time structure to TIME
*/
@@ -797,16 +840,13 @@ const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
void make_truncated_value_warning(THD *thd,
MYSQL_ERROR::enum_warning_level level,
- const Lazy_string *sval,
+ const ErrConv *sval,
timestamp_type time_type,
const char *field_name)
{
char warn_buff[MYSQL_ERRMSG_SIZE];
const char *type_str;
CHARSET_INFO *cs= &my_charset_latin1;
- char buff[128];
- String str(buff,(uint32) sizeof(buff), system_charset_info);
- sval->copy_to(&str);
switch (time_type) {
case MYSQL_TIMESTAMP_DATE:
@@ -823,17 +863,17 @@ void make_truncated_value_warning(THD *thd,
if (field_name)
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
- type_str, str.c_ptr_safe(), field_name,
- (ulong) thd->row_count);
+ type_str, sval->ptr(), field_name,
+ (ulong) thd->warning_info->current_row_for_warning());
else
{
if (time_type > MYSQL_TIMESTAMP_ERROR)
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
ER(ER_TRUNCATED_WRONG_VALUE),
- type_str, str.c_ptr_safe());
+ type_str, sval->ptr());
else
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
- ER(ER_WRONG_VALUE), type_str, str.c_ptr_safe());
+ ER(ER_WRONG_VALUE), type_str, sval->ptr());
}
push_warning(thd, level,
ER_TRUNCATED_WRONG_VALUE, warn_buff);
@@ -1093,7 +1133,7 @@ bool time_to_datetime(MYSQL_TIME *ltime)
/**
Return a valid DATE or DATETIME value from an arbitrary MYSQL_TIME.
If ltime is TIME, it's first converted to DATETIME.
- If ts_type is DATE, yymmss is set to zero.
+ If ts_type is DATE, hhmmss is set to zero.
The date part of the result is checked against fuzzy_date.
@param ltime The value to convert.
@@ -1101,14 +1141,15 @@ bool time_to_datetime(MYSQL_TIME *ltime)
@param ts_type The type to convert to.
@return false on success, true of error (negative time).*/
bool
-make_date_with_warn(MYSQL_TIME *ltime, uint fuzzy_date, timestamp_type ts_type)
+make_date_with_warn(MYSQL_TIME *ltime, ulonglong fuzzy_date,
+ timestamp_type ts_type)
{
DBUG_ASSERT(ts_type == MYSQL_TIMESTAMP_DATE ||
ts_type == MYSQL_TIMESTAMP_DATETIME);
if (ltime->time_type == MYSQL_TIMESTAMP_TIME && time_to_datetime(ltime))
{
/* e.g. negative time */
- Lazy_string_time str(ltime);
+ ErrConvTime str(ltime);
make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
&str, ts_type, 0);
return true;
diff --git a/sql/sql_time.h b/sql/sql_time.h
new file mode 100644
index 00000000000..443d22c5419
--- /dev/null
+++ b/sql/sql_time.h
@@ -0,0 +1,132 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef 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" /* MYSQL_ERROR */
+#include "structs.h" /* INTERVAL */
+
+typedef enum enum_mysql_timestamp_type timestamp_type;
+typedef struct st_date_time_format DATE_TIME_FORMAT;
+typedef struct st_known_date_time_format KNOWN_DATE_TIME_FORMAT;
+
+/* Flags for calc_week() function. */
+#define WEEK_MONDAY_FIRST 1
+#define WEEK_YEAR 2
+#define WEEK_FIRST_WEEKDAY 4
+
+ulong convert_period_to_month(ulong period);
+ulong convert_month_to_period(ulong month);
+bool time_to_datetime(MYSQL_TIME *ltime);
+void time_to_daytime_interval(MYSQL_TIME *l_time);
+bool get_date_from_daynr(long daynr,uint *year, uint *month, uint *day);
+my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error_code);
+bool str_to_time_with_warn(CHARSET_INFO *cs, const char *str, uint length,
+ MYSQL_TIME *l_time, ulonglong fuzzydate);
+timestamp_type str_to_datetime_with_warn(CHARSET_INFO *cs, const char *str,
+ uint length, MYSQL_TIME *l_time,
+ ulonglong flags);
+bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime,
+ ulonglong fuzzydate,
+ const char *name);
+bool decimal_to_datetime_with_warn(const my_decimal *value, MYSQL_TIME *ltime,
+ ulonglong fuzzydate,
+ const char *name);
+bool int_to_datetime_with_warn(bool neg, ulonglong value, MYSQL_TIME *ltime,
+ ulonglong fuzzydate,
+ const char *name);
+
+void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
+ const ErrConv *str_val,
+ timestamp_type time_type,
+ const char *field_name);
+
+static inline void make_truncated_value_warning(THD *thd,
+ MYSQL_ERROR::enum_warning_level level, const char *str_val,
+ uint str_length, timestamp_type time_type,
+ const char *field_name)
+{
+ const ErrConvString str(str_val, str_length, &my_charset_bin);
+ make_truncated_value_warning(thd, level, &str, time_type, field_name);
+}
+
+extern DATE_TIME_FORMAT *date_time_format_make(timestamp_type format_type,
+ const char *format_str,
+ uint format_length);
+extern DATE_TIME_FORMAT *date_time_format_copy(THD *thd,
+ DATE_TIME_FORMAT *format);
+const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
+ timestamp_type type);
+/* MYSQL_TIME operations */
+bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type,
+ INTERVAL interval);
+bool calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign,
+ longlong *seconds_out, long *microseconds_out);
+int my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b);
+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);
+
+int calc_weekday(long daynr,bool sunday_first_day_of_week);
+bool parse_date_time_format(timestamp_type format_type,
+ const char *format, uint format_length,
+ DATE_TIME_FORMAT *date_time_format);
+/* Character set-aware version of str_to_time() */
+timestamp_type str_to_time(CHARSET_INFO *cs, const char *str,uint length,
+ MYSQL_TIME *l_time, ulonglong fuzzydate, int *warning);
+/* Character set-aware version of str_to_datetime() */
+timestamp_type str_to_datetime(CHARSET_INFO *cs,
+ const char *str, uint length,
+ MYSQL_TIME *l_time, ulonglong flags, int *was_cut);
+
+/* convenience wrapper */
+inline bool parse_date_time_format(timestamp_type format_type,
+ DATE_TIME_FORMAT *date_time_format)
+{
+ return parse_date_time_format(format_type,
+ date_time_format->format.str,
+ date_time_format->format.length,
+ date_time_format);
+}
+
+
+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[];
+
+
+static inline bool
+non_zero_date(const MYSQL_TIME *ltime)
+{
+ return ltime->year || ltime->month || ltime->day;
+}
+static inline bool
+check_date(const MYSQL_TIME *ltime, ulonglong flags, int *was_cut)
+{
+ return check_date(ltime, non_zero_date(ltime), flags, was_cut);
+}
+bool check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date,
+ timestamp_type ts_type);
+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);
+
+#endif /* SQL_TIME_INCLUDED */
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index ef6959df2b3..b6915b708fa 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -12,16 +12,26 @@
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
-*/
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
#define MYSQL_LEX 1
-#include "mysql_priv.h"
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_priv.h"
+#include "unireg.h"
#include "sp_head.h"
#include "sql_trigger.h"
+#include "sql_parse.h" // parse_sql
#include "parse_file.h"
-#include "sql_handler.h"
+#include "sp.h"
+#include "sql_base.h" // find_temporary_table
+#include "sql_show.h" // append_definer, append_identifier
+#include "sql_table.h" // build_table_filename,
+ // check_n_cut_mysql50_prefix
+#include "sql_db.h" // get_default_db_collation
+#include "sql_acl.h" // *_ACL, is_acl_user
+#include "sql_handler.h" // mysql_ha_rm_tables
+#include "sp_cache.h" // sp_invalidate_cache
#include <mysys_err.h>
/*************************************************************************/
@@ -316,8 +326,12 @@ public:
Deprecated_trigger_syntax_handler() : m_trigger_name(NULL) {}
- virtual bool handle_error(uint sql_errno, const char *message,
- MYSQL_ERROR::enum_warning_level level, THD *thd)
+ virtual bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* message,
+ MYSQL_ERROR ** cond_hdl)
{
if (sql_errno != EE_OUTOFMEMORY &&
sql_errno != ER_OUT_OF_RESOURCES)
@@ -326,12 +340,11 @@ public:
m_trigger_name= &thd->lex->spname->m_name;
if (m_trigger_name)
my_snprintf(m_message, sizeof(m_message),
- "Trigger '%s' has an error in its body: '%s'",
+ ER(ER_ERROR_IN_TRIGGER_BODY),
m_trigger_name->str, message);
else
my_snprintf(m_message, sizeof(m_message),
- "Unknown trigger has an error in its body: '%s'",
- message);
+ ER(ER_ERROR_IN_UNKNOWN_TRIGGER_BODY), message);
return true;
}
return false;
@@ -377,8 +390,9 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
TABLE *table;
bool result= TRUE;
String stmt_query;
+ bool lock_upgrade_done= FALSE;
+ MDL_ticket *mdl_ticket= NULL;
Query_tables_list backup;
- bool need_start_waiting= FALSE;
DBUG_ENTER("mysql_create_or_drop_trigger");
@@ -427,19 +441,6 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
DBUG_RETURN(TRUE);
}
- /*
- We don't want perform our operations while global read lock is held
- so we have to wait until its end and then prevent it from occurring
- again until we are done, unless we are under lock tables. (Acquiring
- LOCK_open is not enough because global read lock is held without holding
- LOCK_open).
- */
- if (!thd->locked_tables &&
- !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
- DBUG_RETURN(TRUE);
-
- VOID(pthread_mutex_lock(&LOCK_open));
-
if (!create)
{
bool if_exists= thd->lex->drop_if_exists;
@@ -449,6 +450,20 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
destructive changes necessary to open the trigger's table.
*/
thd->lex->reset_n_backup_query_tables_list(&backup);
+ /*
+ Restore Query_tables_list::sql_command, which was
+ reset above, as the code that writes the query to the
+ binary log assumes that this value corresponds to the
+ statement that is being executed.
+ */
+ thd->lex->sql_command= backup.sql_command;
+
+ if (opt_readonly && !(thd->security_ctx->master_access & SUPER_ACL) &&
+ !thd->slave_thread)
+ {
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
+ goto end;
+ }
if (add_table_for_trigger(thd, thd->lex->spname, if_exists, & tables))
goto end;
@@ -479,7 +494,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last;
thd->lex->query_tables_own_last= 0;
- err_status= check_table_access(thd, TRIGGER_ACL, tables, 1, FALSE);
+ err_status= check_table_access(thd, TRIGGER_ACL, tables, FALSE, 1, FALSE);
thd->lex->query_tables_own_last= save_query_tables_own_last;
@@ -491,7 +506,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
DBUG_ASSERT(tables->next_global == 0);
/* We do not allow creation of triggers on temporary tables. */
- if (create && find_temporary_table(thd, tables->db, tables->table_name))
+ if (create && find_temporary_table(thd, tables))
{
my_error(ER_TRG_ON_VIEW_OR_TEMP_TABLE, MYF(0), tables->alias);
goto end;
@@ -499,31 +514,41 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
/* We also don't allow creation of triggers on views. */
tables->required_type= FRMTYPE_TABLE;
+ /*
+ Also prevent DROP TRIGGER from opening temporary table which might
+ shadow base table on which trigger to be dropped is defined.
+ */
+ tables->open_type= OT_BASE_ONLY;
/* Keep consistent with respect to other DDL statements */
- mysql_ha_rm_tables(thd, tables, TRUE);
+ mysql_ha_rm_tables(thd, tables);
- if (thd->locked_tables)
+ if (thd->locked_tables_mode)
{
- /* Table must be write locked */
- if (name_lock_locked_table(thd, tables))
+ /* Under LOCK TABLES we must only accept write locked tables. */
+ if (!(tables->table= find_table_for_mdl_upgrade(thd, tables->db,
+ tables->table_name,
+ FALSE)))
goto end;
}
else
{
- /* Grab the name lock and insert the placeholder*/
- if (lock_table_names(thd, tables))
+ tables->table= open_n_lock_single_table(thd, tables,
+ TL_READ_NO_INSERT, 0);
+ if (! tables->table)
goto end;
-
- /* Convert the placeholder to a real table */
- if (reopen_name_locked_table(thd, tables, TRUE))
- {
- unlock_table_name(thd, tables);
- goto end;
- }
+ tables->table->use_all_columns();
}
table= tables->table;
+ /* Later on we will need it to downgrade the lock */
+ mdl_ticket= table->mdl_ticket;
+
+ if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
+ goto end;
+
+ lock_upgrade_done= TRUE;
+
if (!table->triggers)
{
if (!create)
@@ -540,39 +565,42 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
table->triggers->create_trigger(thd, tables, &stmt_query):
table->triggers->drop_trigger(thd, tables, &stmt_query));
- /* Under LOCK TABLES we must reopen the table to activate the trigger. */
- if (!result && thd->locked_tables)
- {
- /* Make table suitable for reopening */
- close_data_files_and_morph_locks(thd, tables->db, tables->table_name);
- thd->in_lock_tables= 1;
- if (reopen_tables(thd, 1, 1))
- {
- /*
- Ignore reopen_tables errors for now. It's better not leave master/slave
- in a inconsistent state.
- */
- thd->clear_error();
- }
- thd->in_lock_tables= 0;
- }
+ if (result)
+ goto end;
-end:
+ close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED);
+ /*
+ Reopen the table if we were under LOCK TABLES.
+ Ignore the return value for now. It's better to
+ keep master/slave in consistent state.
+ */
+ if (thd->locked_tables_list.reopen_tables(thd))
+ thd->clear_error();
+ /*
+ Invalidate SP-cache. That's needed because triggers may change list of
+ pre-locking tables.
+ */
+ sp_cache_invalidate();
+
+end:
if (!result)
{
result= write_bin_log(thd, TRUE, stmt_query.ptr(), stmt_query.length());
}
- VOID(pthread_mutex_unlock(&LOCK_open));
+ /*
+ If we are under LOCK TABLES we should restore original state of
+ meta-data locks. Otherwise all locks will be released along
+ with the implicit commit.
+ */
+ if (thd->locked_tables_mode && tables && lock_upgrade_done)
+ mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
/* Restore the query table list. Used only for drop trigger. */
if (!create)
thd->lex->restore_backup_query_tables_list(&backup);
- if (need_start_waiting)
- start_waiting_global_read_lock(thd);
-
if (!result)
my_ok(thd);
@@ -680,10 +708,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
thd->security_ctx->priv_host)))
{
if (check_global_access(thd, SUPER_ACL))
- {
- my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
return TRUE;
- }
}
/*
@@ -864,7 +889,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
return 0;
err_with_cleanup:
- my_delete(trigname_buff, MYF(MY_WME));
+ mysql_file_delete(key_file_trn, trigname_buff, MYF(MY_WME));
return 1;
}
@@ -887,7 +912,7 @@ static bool rm_trigger_file(char *path, const char *db,
const char *table_name)
{
build_table_filename(path, FN_REFLEN-1, db, table_name, TRG_EXT, 0);
- return my_delete(path, MYF(MY_WME));
+ return mysql_file_delete(key_file_trg, path, MYF(MY_WME));
}
@@ -909,7 +934,7 @@ static bool rm_trigname_file(char *path, const char *db,
const char *trigger_name)
{
build_table_filename(path, FN_REFLEN - 1, db, trigger_name, TRN_EXT, 0);
- return my_delete(path, MYF(MY_WME));
+ return mysql_file_delete(key_file_trn, path, MYF(MY_WME));
}
@@ -1313,6 +1338,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
triggers->definitions_list.elements);
table->triggers= triggers;
+ status_var_increment(thd->status_var.feature_trigger);
/*
TODO: This could be avoided if there is no triggers
@@ -1328,7 +1354,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
List_iterator_fast<LEX_STRING> it_db_cl_name(triggers->db_cl_names);
LEX *old_lex= thd->lex, lex;
sp_rcontext *save_spcont= thd->spcont;
- ulong save_sql_mode= thd->variables.sql_mode;
+ ulonglong save_sql_mode= thd->variables.sql_mode;
LEX_STRING *on_table_name;
thd->lex= &lex;
@@ -1338,6 +1364,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
thd->reset_db((char*) db, strlen(db));
while ((trg_create_str= it++))
{
+ sp_head *sp;
trg_sql_mode= itm++;
LEX_STRING *trg_definer= it_definer++;
@@ -1424,8 +1451,11 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
int event= lex.trg_chistics.event;
int action_time= lex.trg_chistics.action_time;
- lex.sphead->set_creation_ctx(creation_ctx);
- triggers->bodies[event][action_time]= lex.sphead;
+ sp= triggers->bodies[event][action_time]= lex.sphead;
+ lex.sphead= NULL; /* Prevent double cleanup. */
+
+ sp->set_info(0, 0, &lex.sp_chistics, (ulong) *trg_sql_mode);
+ sp->set_creation_ctx(creation_ctx);
if (!trg_definer->length)
{
@@ -1438,29 +1468,28 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRG_NO_DEFINER, ER(ER_TRG_NO_DEFINER),
(const char*) db,
- (const char*) lex.sphead->m_name.str);
+ (const char*) sp->m_name.str);
/*
Set definer to the '' to correct displaying in the information
schema.
*/
- lex.sphead->set_definer((char*) "", 0);
+ sp->set_definer((char*) "", 0);
/*
Triggers without definer information are executed under the
authorization of the invoker.
*/
- lex.sphead->m_chistics->suid= SP_IS_NOT_SUID;
+ sp->m_chistics->suid= SP_IS_NOT_SUID;
}
else
- lex.sphead->set_definer(trg_definer->str, trg_definer->length);
+ sp->set_definer(trg_definer->str, trg_definer->length);
+
+ if (triggers->names_list.push_back(&sp->m_name, &table->mem_root))
+ goto err_with_lex_cleanup;
- if (triggers->names_list.push_back(&lex.sphead->m_name,
- &table->mem_root))
- goto err_with_lex_cleanup;
-
if (!(on_table_name= alloc_lex_string(&table->mem_root)))
goto err_with_lex_cleanup;
@@ -1698,7 +1727,7 @@ 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;
+ LEX_STRING tbl_name= null_lex_str;
DBUG_ENTER("add_table_for_trigger");
@@ -1726,7 +1755,8 @@ bool add_table_for_trigger(THD *thd,
DBUG_RETURN(TRUE);
*table= sp_add_to_query_tables(thd, lex, trg_name->m_db.str,
- tbl_name.str, TL_IGNORE);
+ tbl_name.str, TL_IGNORE,
+ MDL_SHARED_NO_WRITE);
DBUG_RETURN(*table ? FALSE : TRUE);
}
@@ -1739,9 +1769,6 @@ bool add_table_for_trigger(THD *thd,
@param db schema for table
@param name name for table
- @note
- The calling thread should hold the LOCK_open mutex;
-
@retval
False success
@retval
@@ -1756,7 +1783,7 @@ bool Table_triggers_list::drop_all_triggers(THD *thd, char *db, char *name)
DBUG_ENTER("drop_all_triggers");
bzero(&table, sizeof(table));
- init_alloc_root(&table.mem_root, 8192, 0);
+ init_sql_alloc(&table.mem_root, 8192, 0);
if (Table_triggers_list::check_n_load(thd, db, name, &table, 1))
{
@@ -1827,7 +1854,7 @@ Table_triggers_list::change_table_name_in_triggers(THD *thd,
{
char path_buff[FN_REFLEN];
LEX_STRING *def, *on_table_name, new_def;
- ulong save_sql_mode= thd->variables.sql_mode;
+ ulonglong save_sql_mode= thd->variables.sql_mode;
List_iterator_fast<LEX_STRING> it_def(definitions_list);
List_iterator_fast<LEX_STRING> it_on_table_name(on_table_names_list);
List_iterator_fast<ulonglong> it_mode(definition_modes_list);
@@ -1947,6 +1974,7 @@ Table_triggers_list::change_table_name_in_trignames(const char *old_db_name,
@param[in,out] thd Thread context
@param[in] db Old database of subject table
+ @param[in] old_alias Old alias of subject table
@param[in] old_table Old name of subject table
@param[in] new_db New database for subject table
@param[in] new_table New name of subject table
@@ -1956,13 +1984,14 @@ Table_triggers_list::change_table_name_in_trignames(const char *old_db_name,
i.e. it either will complete successfully, or will fail leaving files
in their initial state.
Also this method assumes that subject table is not renamed to itself.
- This method needs to be called under an exclusive table name lock.
+ This method needs to be called under an exclusive table metadata lock.
@retval FALSE Success
@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)
@@ -1974,22 +2003,17 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db,
DBUG_ENTER("change_table_name");
bzero(&table, sizeof(table));
- init_alloc_root(&table.mem_root, 8192, 0);
+ init_sql_alloc(&table.mem_root, 8192, 0);
/*
This method interfaces the mysql server code protected by
- either LOCK_open mutex or with an exclusive table name lock.
- In the future, only an exclusive table name lock will be enough.
+ an exclusive metadata lock.
*/
-#ifndef DBUG_OFF
- uchar key[MAX_DBKEY_LENGTH];
- uint key_length= create_table_def_key((char *)key, db, old_table);
- if (!is_table_name_exclusively_locked_by_this_thread(thd, key, key_length))
- safe_mutex_assert_owner(&LOCK_open);
-#endif
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, old_table,
+ MDL_EXCLUSIVE));
DBUG_ASSERT(my_strcasecmp(table_alias_charset, db, new_db) ||
- my_strcasecmp(table_alias_charset, old_table, new_table));
+ my_strcasecmp(table_alias_charset, old_alias, new_table));
if (Table_triggers_list::check_n_load(thd, db, old_table, &table, TRUE))
{
@@ -2003,7 +2027,7 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db,
result= 1;
goto end;
}
- LEX_STRING old_table_name= { (char *) old_table, strlen(old_table) };
+ 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
@@ -2097,6 +2121,8 @@ bool Table_triggers_list::process_triggers(THD *thd,
if (sp_trigger == NULL)
return FALSE;
+ status_var_increment(thd->status_var.executed_triggers);
+
if (old_row_is_record1)
{
old_field= record1_field;
@@ -2137,6 +2163,61 @@ bool Table_triggers_list::process_triggers(THD *thd,
/**
+ Add triggers for table to the set of routines used by statement.
+ Add tables used by them to statement table list. Do the same for
+ routines used by triggers.
+
+ @param thd Thread context.
+ @param prelocking_ctx Prelocking context of the statement.
+ @param table_list Table list element for table with trigger.
+
+ @retval FALSE Success.
+ @retval TRUE Failure.
+*/
+
+bool
+Table_triggers_list::
+add_tables_and_routines_for_triggers(THD *thd,
+ Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list)
+{
+ DBUG_ASSERT(static_cast<int>(table_list->lock_type) >=
+ static_cast<int>(TL_WRITE_ALLOW_WRITE));
+
+ for (int i= 0; i < (int)TRG_EVENT_MAX; i++)
+ {
+ if (table_list->trg_event_map &
+ static_cast<uint8>(1 << static_cast<int>(i)))
+ {
+ for (int j= 0; j < (int)TRG_ACTION_MAX; j++)
+ {
+ /* We can have only one trigger per action type currently */
+ sp_head *trigger= table_list->table->triggers->bodies[i][j];
+
+ if (trigger)
+ {
+ 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))
+ {
+ trigger->add_used_tables_to_table_list(thd,
+ &prelocking_ctx->query_tables_last,
+ table_list->belong_to_view);
+ sp_update_stmt_used_routines(thd, prelocking_ctx,
+ &trigger->m_sroutines,
+ table_list->belong_to_view);
+ trigger->propagate_attributes(prelocking_ctx);
+ }
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+
+/**
Mark fields of subject table which we read/set in its triggers
as such.
diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h
index d3808610ea8..47b1d19ae54 100644
--- a/sql/sql_trigger.h
+++ b/sql/sql_trigger.h
@@ -1,3 +1,6 @@
+#ifndef SQL_TRIGGER_INCLUDED
+#define SQL_TRIGGER_INCLUDED
+
/*
Copyright (c) 2004, 2011, Oracle and/or its affiliates.
@@ -12,8 +15,39 @@
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
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/* Forward declarations */
+
+class Item_trigger_field;
+class sp_head;
+class sp_name;
+class Query_tables_list;
+struct TABLE_LIST;
+class Query_tables_list;
+
+/** Event on which trigger is invoked. */
+enum trg_event_type
+{
+ TRG_EVENT_INSERT= 0,
+ TRG_EVENT_UPDATE= 1,
+ TRG_EVENT_DELETE= 2,
+ TRG_EVENT_MAX
+};
+
+#include "table.h" /* GRANT_INFO */
+
+/*
+ We need this two enums here instead of sql_lex.h because
+ at least one of them is used by Item_trigger_field interface.
+
+ Time when trigger is invoked (i.e. before or after row actually
+ inserted/updated/deleted).
*/
+enum trg_action_time_type
+{
+ TRG_ACTION_BEFORE= 0, TRG_ACTION_AFTER= 1, TRG_ACTION_MAX
+};
/**
@@ -106,8 +140,9 @@ public:
/* End of character ser context. */
- Table_triggers_list(TABLE *table_arg):
- record1_field(0), trigger_table(table_arg), m_has_unparseable_trigger(false)
+ Table_triggers_list(TABLE *table_arg)
+ :record1_field(0), trigger_table(table_arg),
+ m_has_unparseable_trigger(false)
{
bzero((char *)bodies, sizeof(bodies));
bzero((char *)trigger_fields, sizeof(trigger_fields));
@@ -145,6 +180,7 @@ public:
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);
@@ -166,8 +202,10 @@ public:
void set_parse_error_message(char *error_message);
friend class Item_trigger_field;
- friend int sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
- TABLE_LIST *table);
+
+ bool add_tables_and_routines_for_triggers(THD *thd,
+ Query_tables_list *prelocking_ctx,
+ TABLE_LIST *table_list);
private:
bool prepare_record1_accessors(TABLE *table);
@@ -208,4 +246,9 @@ bool load_table_name_for_trigger(THD *thd,
const sp_name *trg_name,
const LEX_STRING *trn_path,
LEX_STRING *tbl_name);
+bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create);
+
+extern const char * const TRG_EXT;
+extern const char * const TRN_EXT;
+#endif /* SQL_TRIGGER_INCLUDED */
diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc
new file mode 100644
index 00000000000..92fc4205656
--- /dev/null
+++ b/sql/sql_truncate.cc
@@ -0,0 +1,555 @@
+/* Copyright (c) 2010, 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 "debug_sync.h" // DEBUG_SYNC
+#include "table.h" // TABLE, FOREIGN_KEY_INFO
+#include "sql_class.h" // THD
+#include "sql_base.h" // open_and_lock_tables
+#include "sql_table.h" // write_bin_log
+#include "sql_handler.h" // mysql_ha_rm_tables
+#include "datadict.h" // dd_recreate_table()
+#include "lock.h" // MYSQL_OPEN_TEMPORARY_ONLY
+#include "sql_acl.h" // DROP_ACL
+#include "sql_parse.h" // check_one_table_access()
+#include "sql_truncate.h"
+#include "sql_show.h" //append_identifier()
+
+
+/**
+ Append a list of field names to a string.
+
+ @param str The string.
+ @param fields The list of field names.
+
+ @return TRUE on failure, FALSE otherwise.
+*/
+
+static bool fk_info_append_fields(THD *thd, String *str,
+ List<LEX_STRING> *fields)
+{
+ bool res= FALSE;
+ LEX_STRING *field;
+ List_iterator_fast<LEX_STRING> it(*fields);
+
+ while ((field= it++))
+ {
+ res|= append_identifier(thd, str, field->str, field->length);
+ res|= str->append(", ");
+ }
+
+ str->chop();
+ str->chop();
+
+ return res;
+}
+
+
+/**
+ Generate a foreign key description suitable for a error message.
+
+ @param thd Thread context.
+ @param fk_info The foreign key information.
+
+ @return A human-readable string describing the foreign key.
+*/
+
+static const char *fk_info_str(THD *thd, FOREIGN_KEY_INFO *fk_info)
+{
+ bool res= FALSE;
+ char buffer[STRING_BUFFER_USUAL_SIZE*2];
+ String str(buffer, sizeof(buffer), system_charset_info);
+
+ str.length(0);
+
+ /*
+ `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|= str.append(".");
+ res|= append_identifier(thd, &str, fk_info->foreign_table->str,
+ fk_info->foreign_table->length);
+ res|= str.append(", CONSTRAINT ");
+ res|= append_identifier(thd, &str, fk_info->foreign_id->str,
+ fk_info->foreign_id->length);
+ 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|= str.append(".");
+ res|= append_identifier(thd, &str, fk_info->referenced_table->str,
+ fk_info->referenced_table->length);
+ res|= str.append(" (");
+ res|= fk_info_append_fields(thd, &str, &fk_info->referenced_fields);
+ res|= str.append(')');
+
+ return res ? NULL : thd->strmake(str.ptr(), str.length());
+}
+
+
+/**
+ Check and emit a fatal error if the table which is going to be
+ affected by TRUNCATE TABLE is a parent table in some non-self-
+ referencing foreign key.
+
+ @remark The intention is to allow truncate only for tables that
+ are not dependent on other tables.
+
+ @param thd Thread context.
+ @param table Table handle.
+
+ @retval FALSE This table is not parent in a non-self-referencing foreign
+ key. Statement can proceed.
+ @retval TRUE This table is parent in a non-self-referencing foreign key,
+ error was emitted.
+*/
+
+static bool
+fk_truncate_illegal_if_parent(THD *thd, TABLE *table)
+{
+ FOREIGN_KEY_INFO *fk_info;
+ List<FOREIGN_KEY_INFO> fk_list;
+ List_iterator_fast<FOREIGN_KEY_INFO> it;
+
+ /*
+ Bail out early if the table is not referenced by a foreign key.
+ In this case, the table could only be, if at all, a child table.
+ */
+ if (! table->file->referenced_by_foreign_key())
+ return FALSE;
+
+ /*
+ This table _is_ referenced by a foreign key. At this point, only
+ self-referencing keys are acceptable. For this reason, get the list
+ of foreign keys referencing this table in order to check the name
+ of the child (dependent) tables.
+ */
+ table->file->get_parent_foreign_key_list(thd, &fk_list);
+
+ /* Out of memory when building list. */
+ if (thd->is_error())
+ return TRUE;
+
+ it.init(fk_list);
+
+ /* Loop over the set of foreign keys for which this table is a parent. */
+ while ((fk_info= it++))
+ {
+ DBUG_ASSERT(!my_strcasecmp(system_charset_info,
+ fk_info->referenced_db->str,
+ table->s->db.str));
+
+ DBUG_ASSERT(!my_strcasecmp(system_charset_info,
+ fk_info->referenced_table->str,
+ table->s->table_name.str));
+
+ if (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))
+ break;
+ }
+
+ /* Table is parent in a non-self-referencing foreign key. */
+ if (fk_info)
+ {
+ my_error(ER_TRUNCATE_ILLEGAL_FK, MYF(0), fk_info_str(thd, fk_info));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/*
+ Open and truncate a locked table.
+
+ @param thd Thread context.
+ @param table_ref Table list element for the table to be truncated.
+ @param is_tmp_table True if element refers to a temp table.
+
+ @retval TRUNCATE_OK Truncate was successful and statement can be safely
+ binlogged.
+ @retval TRUNCATE_FAILED_BUT_BINLOG Truncate failed but still go ahead with
+ binlogging as in case of non transactional tables
+ partial truncation is possible.
+
+ @retval TRUNCATE_FAILED_SKIP_BINLOG Truncate was not successful hence donot
+ binlong the statement.
+*/
+
+enum Truncate_statement::truncate_result
+Truncate_statement::handler_truncate(THD *thd, TABLE_LIST *table_ref,
+ bool is_tmp_table)
+{
+ int error= 0;
+ uint flags;
+ DBUG_ENTER("Truncate_statement::handler_truncate");
+
+ /*
+ Can't recreate, the engine must mechanically delete all rows
+ in the table. Use open_and_lock_tables() to open a write cursor.
+ */
+
+ /* If it is a temporary table, no need to take locks. */
+ if (is_tmp_table)
+ flags= MYSQL_OPEN_TEMPORARY_ONLY;
+ else
+ {
+ /* We don't need to load triggers. */
+ DBUG_ASSERT(table_ref->trg_event_map == 0);
+ /*
+ Our metadata lock guarantees that no transaction is reading
+ 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;
+ /*
+ Ignore pending FLUSH TABLES since we don't want to release
+ the MDL lock taken above and otherwise there is no way to
+ wait for FLUSH TABLES in deadlock-free fashion.
+ */
+ flags= MYSQL_OPEN_IGNORE_FLUSH | MYSQL_OPEN_SKIP_TEMPORARY;
+ /*
+ Even though we have an MDL lock on the table here, we don't
+ pass MYSQL_OPEN_HAS_MDL_LOCK to open_and_lock_tables
+ since to truncate a MERGE table, we must open and lock
+ merge children, and on those we don't have an MDL lock.
+ Thus clear the ticket to satisfy MDL asserts.
+ */
+ table_ref->mdl_request.ticket= NULL;
+ }
+
+ /* Open the table as it will handle some required preparations. */
+ if (open_and_lock_tables(thd, table_ref, FALSE, flags))
+ DBUG_RETURN(TRUNCATE_FAILED_SKIP_BINLOG);
+
+ /* Whether to truncate regardless of foreign keys. */
+ if (! (thd->variables.option_bits & OPTION_NO_FOREIGN_KEY_CHECKS))
+ if (fk_truncate_illegal_if_parent(thd, table_ref->table))
+ DBUG_RETURN(TRUNCATE_FAILED_SKIP_BINLOG);
+
+ error= table_ref->table->file->ha_truncate();
+ if (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
+ inspite of errors.
+ */
+ if (error == HA_ERR_WRONG_COMMAND ||
+ table_ref->table->file->has_transactions())
+ DBUG_RETURN(TRUNCATE_FAILED_SKIP_BINLOG);
+ else
+ DBUG_RETURN(TRUNCATE_FAILED_BUT_BINLOG);
+ }
+ DBUG_RETURN(TRUNCATE_OK);
+}
+
+
+/*
+ Close and recreate a temporary table. In case of success,
+ write truncate statement into the binary log if in statement
+ mode.
+
+ @param thd Thread context.
+ @param table The temporary table.
+
+ @retval FALSE Success.
+ @retval TRUE Error.
+*/
+
+static bool recreate_temporary_table(THD *thd, TABLE *table)
+{
+ bool error= TRUE;
+ TABLE_SHARE *share= table->s;
+ HA_CREATE_INFO create_info;
+ handlerton *table_type= table->s->db_type();
+ DBUG_ENTER("recreate_temporary_table");
+
+ memset(&create_info, 0, sizeof(create_info));
+ create_info.options|= HA_LEX_CREATE_TMP_TABLE;
+
+ table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK);
+
+ /* Don't free share. */
+ close_temporary_table(thd, table, FALSE, FALSE);
+
+ /*
+ We must use share->normalized_path.str since for temporary tables it
+ differs from what dd_recreate_table() would generate based
+ on table and schema names.
+ */
+ ha_create_table(thd, share->normalized_path.str, share->db.str,
+ share->table_name.str, &create_info, 1);
+
+ if (open_table_uncached(thd, share->path.str, share->db.str,
+ share->table_name.str, TRUE))
+ {
+ error= FALSE;
+ thd->thread_specific_used= TRUE;
+ }
+ else
+ rm_temporary_table(table_type, share->path.str);
+
+ free_table_share(share);
+ my_free(table);
+
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Handle locking a base table for truncate.
+
+ @param[in] thd Thread context.
+ @param[in] table_ref Table list element for the table to
+ be truncated.
+ @param[out] hton_can_recreate Set to TRUE if table can be dropped
+ and recreated.
+
+ @retval FALSE Success.
+ @retval TRUE Error.
+*/
+
+bool Truncate_statement::lock_table(THD *thd, TABLE_LIST *table_ref,
+ bool *hton_can_recreate)
+{
+ TABLE *table= NULL;
+ DBUG_ENTER("Truncate_statement::lock_table");
+
+ /* Lock types are set in the parser. */
+ DBUG_ASSERT(table_ref->lock_type == TL_WRITE);
+ /* The handler truncate protocol dictates a exclusive lock. */
+ DBUG_ASSERT(table_ref->mdl_request.type == MDL_EXCLUSIVE);
+
+ /*
+ Before doing anything else, acquire a metadata lock on the table,
+ or ensure we have one. We don't use open_and_lock_tables()
+ right away because we want to be able to truncate (and recreate)
+ corrupted tables, those that we can't fully open.
+
+ MySQL manual documents that TRUNCATE can be used to repair a
+ damaged table, i.e. a table that can not be fully "opened".
+ In particular MySQL manual says: As long as the table format
+ file tbl_name.frm is valid, the table can be re-created as
+ an empty table with TRUNCATE TABLE, even if the data or index
+ files have become corrupted.
+ */
+ if (thd->locked_tables_mode)
+ {
+ if (!(table= find_table_for_mdl_upgrade(thd, table_ref->db,
+ table_ref->table_name, FALSE)))
+ DBUG_RETURN(TRUE);
+
+ *hton_can_recreate= ha_check_storage_engine_flag(table->s->db_type(),
+ HTON_CAN_RECREATE);
+ table_ref->mdl_request.ticket= table->mdl_ticket;
+ }
+ else
+ {
+ /* Acquire an exclusive lock. */
+ DBUG_ASSERT(table_ref->next_global == NULL);
+ if (lock_table_names(thd, table_ref, NULL,
+ thd->variables.lock_wait_timeout,
+ MYSQL_OPEN_SKIP_TEMPORARY))
+ DBUG_RETURN(TRUE);
+
+ if (dd_check_storage_engine_flag(thd, table_ref->db, table_ref->table_name,
+ HTON_CAN_RECREATE, hton_can_recreate))
+ DBUG_RETURN(TRUE);
+ }
+
+ /*
+ A storage engine can recreate or truncate the table only if there
+ are no references to it from anywhere, i.e. no cached TABLE in the
+ table cache.
+ */
+ if (thd->locked_tables_mode)
+ {
+ DEBUG_SYNC(thd, "upgrade_lock_for_truncate");
+ /* To remove the table from the cache we need an exclusive lock. */
+ if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DROP,
+ TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE))
+ DBUG_RETURN(TRUE);
+ m_ticket_downgrade= table->mdl_ticket;
+ /* Close if table is going to be recreated. */
+ if (*hton_can_recreate)
+ close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED);
+ }
+ 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);
+ }
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Optimized delete of all rows by doing a full generate of the table.
+
+ @remark Will work even if the .MYI and .MYD files are destroyed.
+ In other words, it works as long as the .FRM is intact and
+ the engine supports re-create.
+
+ @param thd Thread context.
+ @param table_ref Table list element for the table to be truncated.
+
+ @retval FALSE Success.
+ @retval TRUE Error.
+*/
+
+bool Truncate_statement::truncate_table(THD *thd, TABLE_LIST *table_ref)
+{
+ int error;
+ TABLE *table;
+ bool binlog_stmt;
+ DBUG_ENTER("Truncate_statement::truncate_table");
+
+ /* Initialize, or reinitialize in case of reexecution (SP). */
+ m_ticket_downgrade= NULL;
+
+ /* Remove table from the HANDLER's hash. */
+ mysql_ha_rm_tables(thd, table_ref);
+
+ /* If it is a temporary table, no need to take locks. */
+ if ((table= find_temporary_table(thd, table_ref)))
+ {
+ /* In RBR, the statement is not binlogged if the table is temporary. */
+ binlog_stmt= !thd->is_current_stmt_binlog_format_row();
+
+ /* Note that a temporary table cannot be partitioned. */
+ if (ha_check_storage_engine_flag(table->s->db_type(), HTON_CAN_RECREATE))
+ {
+ if ((error= recreate_temporary_table(thd, table)))
+ binlog_stmt= FALSE; /* No need to binlog failed truncate-by-recreate. */
+
+ DBUG_ASSERT(! thd->transaction.stmt.modified_non_trans_table);
+ }
+ else
+ {
+ /*
+ The engine does not support truncate-by-recreate. Open the
+ table and invoke the handler truncate. In such a manner this
+ can in fact open several tables if it's a temporary MyISAMMRG
+ table.
+ */
+ error= handler_truncate(thd, table_ref, TRUE);
+ }
+
+ /*
+ No need to invalidate the query cache, queries with temporary
+ tables are not in the cache. No need to write to the binary
+ log a failed row-by-row delete even if under RBR as the table
+ might not exist on the slave.
+ */
+ }
+ else /* It's not a temporary table. */
+ {
+ bool hton_can_recreate;
+
+ if (lock_table(thd, table_ref, &hton_can_recreate))
+ DBUG_RETURN(TRUE);
+
+ if (hton_can_recreate)
+ {
+ /*
+ 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);
+
+ if (thd->locked_tables_mode && thd->locked_tables_list.reopen_tables(thd))
+ thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
+
+ /* No need to binlog a failed truncate-by-recreate. */
+ binlog_stmt= !error;
+ }
+ else
+ {
+ /*
+ The engine does not support truncate-by-recreate.
+ Attempt to use the handler truncate method.
+ */
+ error= handler_truncate(thd, table_ref, FALSE);
+
+ /*
+ All effects of a TRUNCATE TABLE operation are committed even if
+ truncation fails in the case of non transactional tables. Thus, the
+ 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)
+ binlog_stmt= true;
+ else
+ binlog_stmt= false;
+ }
+
+ /*
+ If we tried to open a MERGE table and failed due to problems with the
+ children tables, the table will have been closed and table_ref->table
+ will be invalid. Reset the pointer here in any case as
+ query_cache_invalidate does not need a valid TABLE object.
+ */
+ table_ref->table= NULL;
+ query_cache_invalidate3(thd, table_ref, FALSE);
+ }
+
+ /* DDL is logged in statement format, regardless of binlog format. */
+ if (binlog_stmt)
+ error|= write_bin_log(thd, !error, thd->query(), thd->query_length());
+
+ /*
+ A locked table ticket was upgraded to a exclusive lock. After the
+ the query has been written to the binary log, downgrade the lock
+ to a shared one.
+ */
+ if (m_ticket_downgrade)
+ m_ticket_downgrade->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
+
+ DBUG_RETURN(error);
+}
+
+
+/**
+ Execute a TRUNCATE statement at runtime.
+
+ @param thd The current thread.
+
+ @return FALSE on success.
+*/
+
+bool Truncate_statement::execute(THD *thd)
+{
+ bool res= TRUE;
+ TABLE_LIST *first_table= thd->lex->select_lex.table_list.first;
+ DBUG_ENTER("Truncate_statement::execute");
+
+ if (check_one_table_access(thd, DROP_ACL, first_table))
+ DBUG_RETURN(res);
+
+ if (! (res= truncate_table(thd, first_table)))
+ my_ok(thd);
+
+ DBUG_RETURN(res);
+}
+
diff --git a/sql/sql_truncate.h b/sql/sql_truncate.h
new file mode 100644
index 00000000000..0280ecc4932
--- /dev/null
+++ b/sql/sql_truncate.h
@@ -0,0 +1,70 @@
+#ifndef SQL_TRUNCATE_INCLUDED
+#define SQL_TRUNCATE_INCLUDED
+/* Copyright (c) 2010, 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 */
+
+class THD;
+struct TABLE_LIST;
+
+/**
+ Truncate_statement represents the TRUNCATE statement.
+*/
+class Truncate_statement : public Sql_statement
+{
+private:
+ /* Set if a lock must be downgraded after truncate is done. */
+ MDL_ticket *m_ticket_downgrade;
+
+public:
+ /**
+ Constructor, used to represent a ALTER TABLE statement.
+ @param lex the LEX structure for this statement.
+ */
+ Truncate_statement(LEX *lex)
+ : Sql_statement(lex)
+ {}
+
+ virtual ~Truncate_statement()
+ {}
+
+ /**
+ Execute a TRUNCATE statement at runtime.
+ @param thd the current thread.
+ @return false on success.
+ */
+ bool execute(THD *thd);
+
+protected:
+ enum truncate_result{
+ TRUNCATE_OK=0,
+ TRUNCATE_FAILED_BUT_BINLOG,
+ TRUNCATE_FAILED_SKIP_BINLOG
+ };
+
+ /** Handle locking a base table for truncate. */
+ bool lock_table(THD *, TABLE_LIST *, bool *);
+
+ /** Truncate table via the handler method. */
+ enum truncate_result handler_truncate(THD *, TABLE_LIST *, bool);
+
+ /**
+ Optimized delete of all rows by doing a full regenerate of the table.
+ Depending on the storage engine, it can be accomplished through a
+ drop and recreate or via the handler truncate method.
+ */
+ bool truncate_table(THD *, TABLE_LIST *);
+};
+
+#endif
diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc
index 2906e69fc75..e5fac48a750 100644
--- a/sql/sql_udf.cc
+++ b/sql/sql_udf.cc
@@ -1,5 +1,4 @@
-/*
- Copyright (c) 2000, 2010, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +11,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* This implements 'user defined functions' */
@@ -33,8 +31,14 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "sql_base.h" // close_mysql_tables
+#include "sql_parse.h" // check_identifier_name
+#include "sql_table.h" // write_bin_log
+#include "records.h" // init_read_record, end_read_record
#include <my_pthread.h>
+#include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT
#ifdef HAVE_DLOPEN
extern "C"
@@ -46,7 +50,7 @@ extern "C"
static bool initialized = 0;
static MEM_ROOT mem;
static HASH udf_hash;
-static rw_lock_t THR_LOCK_udf;
+static mysql_rwlock_t THR_LOCK_udf;
static udf_func *add_udf(LEX_STRING *name, Item_result ret,
@@ -102,6 +106,26 @@ extern "C" uchar* get_hash_key(const uchar *buff, size_t *length,
return (uchar*) udf->name.str;
}
+#ifdef HAVE_PSI_INTERFACE
+static PSI_rwlock_key key_rwlock_THR_LOCK_udf;
+
+static PSI_rwlock_info all_udf_rwlocks[]=
+{
+ { &key_rwlock_THR_LOCK_udf, "THR_LOCK_udf", PSI_FLAG_GLOBAL}
+};
+
+static void init_udf_psi_keys(void)
+{
+ const char* category= "sql";
+ int count;
+
+ if (PSI_server == NULL)
+ return;
+
+ count= array_elements(all_udf_rwlocks);
+ PSI_server->register_rwlock(category, all_udf_rwlocks, count);
+}
+#endif
/*
Read all predeclared functions from mysql.func and accept all that
@@ -121,15 +145,19 @@ void udf_init()
if (initialized)
DBUG_VOID_RETURN;
- my_rwlock_init(&THR_LOCK_udf,NULL);
-
+#ifdef HAVE_PSI_INTERFACE
+ init_udf_psi_keys();
+#endif
+
+ mysql_rwlock_init(key_rwlock_THR_LOCK_udf, &THR_LOCK_udf);
+
init_sql_alloc(&mem, UDF_ALLOC_BLOCK_SIZE, 0);
THD *new_thd = new THD;
if (!new_thd ||
- hash_init(&udf_hash,system_charset_info,32,0,0,get_hash_key, NULL, 0))
+ my_hash_init(&udf_hash,system_charset_info,32,0,0,get_hash_key, NULL, 0))
{
sql_print_error("Can't allocate memory for udf structures");
- hash_free(&udf_hash);
+ my_hash_free(&udf_hash);
free_root(&mem,MYF(0));
delete new_thd;
DBUG_VOID_RETURN;
@@ -137,15 +165,11 @@ void udf_init()
initialized = 1;
new_thd->thread_stack= (char*) &new_thd;
new_thd->store_globals();
- lex_start(new_thd);
new_thd->set_db(db, sizeof(db)-1);
- bzero((uchar*) &tables,sizeof(tables));
- tables.alias= tables.table_name= (char*) "func";
- tables.lock_type = TL_READ;
- tables.db= db;
+ tables.init_one_table(db, sizeof(db)-1, "func", 4, "func", TL_READ);
- if (simple_open_n_lock_tables(new_thd, &tables))
+ if (open_and_lock_tables(new_thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{
DBUG_PRINT("error",("Can't open udf table"));
sql_print_error("Can't open the mysql.func table. Please "
@@ -203,6 +227,7 @@ void udf_init()
char dlpath[FN_REFLEN];
strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", tmp->dl,
NullS);
+ (void) unpack_filename(dlpath, dlpath);
if (!(dl= dlopen(dlpath, RTLD_NOW)))
{
/* Print warning to log */
@@ -227,10 +252,10 @@ void udf_init()
if (error > 0)
sql_print_error("Got unknown error: %d", my_errno);
end_read_record(&read_record_info);
- new_thd->version--; // Force close to free memory
+ table->m_needs_reopen= TRUE; // Force close to free memory
end:
- close_thread_tables(new_thd);
+ close_mysql_tables(new_thd);
delete new_thd;
/* Remember that we don't have a THD */
my_pthread_setspecific_ptr(THR_THD, 0);
@@ -244,25 +269,25 @@ void udf_free()
DBUG_ENTER("udf_free");
for (uint idx=0 ; idx < udf_hash.records ; idx++)
{
- udf_func *udf=(udf_func*) hash_element(&udf_hash,idx);
+ udf_func *udf=(udf_func*) my_hash_element(&udf_hash,idx);
if (udf->dlhandle) // Not closed before
{
/* Mark all versions using the same handler as closed */
for (uint j=idx+1 ; j < udf_hash.records ; j++)
{
- udf_func *tmp=(udf_func*) hash_element(&udf_hash,j);
+ udf_func *tmp=(udf_func*) my_hash_element(&udf_hash,j);
if (udf->dlhandle == tmp->dlhandle)
tmp->dlhandle=0; // Already closed
}
dlclose(udf->dlhandle);
}
}
- hash_free(&udf_hash);
+ my_hash_free(&udf_hash);
free_root(&mem,MYF(0));
if (initialized)
{
initialized= 0;
- rwlock_destroy(&THR_LOCK_udf);
+ mysql_rwlock_destroy(&THR_LOCK_udf);
}
DBUG_VOID_RETURN;
}
@@ -273,7 +298,7 @@ static void del_udf(udf_func *udf)
DBUG_ENTER("del_udf");
if (!--udf->usage_count)
{
- hash_delete(&udf_hash,(uchar*) udf);
+ my_hash_delete(&udf_hash,(uchar*) udf);
using_udf_functions=udf_hash.records != 0;
}
else
@@ -287,7 +312,7 @@ static void del_udf(udf_func *udf)
uint name_length=udf->name.length;
udf->name.str=(char*) "*";
udf->name.length=1;
- hash_update(&udf_hash,(uchar*) udf,(uchar*) name,name_length);
+ my_hash_update(&udf_hash,(uchar*) udf,(uchar*) name,name_length);
}
DBUG_VOID_RETURN;
}
@@ -300,19 +325,19 @@ void free_udf(udf_func *udf)
if (!initialized)
DBUG_VOID_RETURN;
- rw_wrlock(&THR_LOCK_udf);
+ mysql_rwlock_wrlock(&THR_LOCK_udf);
if (!--udf->usage_count)
{
/*
We come here when someone has deleted the udf function
while another thread still was using the udf
*/
- hash_delete(&udf_hash,(uchar*) udf);
+ my_hash_delete(&udf_hash,(uchar*) udf);
using_udf_functions=udf_hash.records != 0;
if (!find_udf_dl(udf->dl))
dlclose(udf->dlhandle);
}
- rw_unlock(&THR_LOCK_udf);
+ mysql_rwlock_unlock(&THR_LOCK_udf);
DBUG_VOID_RETURN;
}
@@ -327,21 +352,22 @@ udf_func *find_udf(const char *name,uint length,bool mark_used)
if (!initialized)
DBUG_RETURN(NULL);
+ DEBUG_SYNC(current_thd, "find_udf_before_lock");
/* TODO: This should be changed to reader locks someday! */
if (mark_used)
- rw_wrlock(&THR_LOCK_udf); /* Called during fix_fields */
+ mysql_rwlock_wrlock(&THR_LOCK_udf); /* Called during fix_fields */
else
- rw_rdlock(&THR_LOCK_udf); /* Called during parsing */
+ mysql_rwlock_rdlock(&THR_LOCK_udf); /* Called during parsing */
- if ((udf=(udf_func*) hash_search(&udf_hash,(uchar*) name,
- length ? length : (uint) strlen(name))))
+ if ((udf=(udf_func*) my_hash_search(&udf_hash,(uchar*) name,
+ length ? length : (uint) strlen(name))))
{
if (!udf->dlhandle)
udf=0; // Could not be opened
else if (mark_used)
udf->usage_count++;
}
- rw_unlock(&THR_LOCK_udf);
+ mysql_rwlock_unlock(&THR_LOCK_udf);
DBUG_RETURN(udf);
}
@@ -356,7 +382,7 @@ static void *find_udf_dl(const char *dl)
*/
for (uint idx=0 ; idx < udf_hash.records ; idx++)
{
- udf_func *udf=(udf_func*) hash_element(&udf_hash,idx);
+ udf_func *udf=(udf_func*) my_hash_element(&udf_hash,idx);
if (!strcmp(dl, udf->dl) && udf->dlhandle != NULL)
DBUG_RETURN(udf->dlhandle);
}
@@ -438,11 +464,16 @@ int mysql_create_function(THD *thd,udf_func *udf)
Turn off row binlogging of this statement and use statement-based
so that all supporting tables are updated for CREATE FUNCTION command.
*/
- save_binlog_row_based= thd->current_stmt_binlog_row_based;
- thd->clear_current_stmt_binlog_row_based();
+ if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
+ thd->clear_current_stmt_binlog_format_row();
- rw_wrlock(&THR_LOCK_udf);
- if ((hash_search(&udf_hash,(uchar*) udf->name.str, udf->name.length)))
+ tables.init_one_table(STRING_WITH_LEN("mysql"), STRING_WITH_LEN("func"),
+ "func", TL_WRITE);
+ table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT);
+
+ mysql_rwlock_wrlock(&THR_LOCK_udf);
+ DEBUG_SYNC(current_thd, "mysql_create_function_after_lock");
+ if ((my_hash_search(&udf_hash,(uchar*) udf->name.str, udf->name.length)))
{
my_error(ER_UDF_EXISTS, MYF(0), udf->name.str);
goto err;
@@ -451,6 +482,8 @@ int mysql_create_function(THD *thd,udf_func *udf)
{
char dlpath[FN_REFLEN];
strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", udf->dl, NullS);
+ (void) unpack_filename(dlpath, dlpath);
+
if (!(dl = dlopen(dlpath, RTLD_NOW)))
{
DBUG_PRINT("error",("dlopen of %s failed, error: %d (%s)",
@@ -483,11 +516,8 @@ int mysql_create_function(THD *thd,udf_func *udf)
/* create entry in mysql.func table */
- bzero((char*) &tables,sizeof(tables));
- tables.db= (char*) "mysql";
- tables.table_name= tables.alias= (char*) "func";
/* Allow creation of functions even if we can't open func table */
- if (!(table = open_ltable(thd, &tables, TL_WRITE, 0)))
+ if (!table)
goto err;
table->use_all_columns();
restore_record(table, s->default_values); // Default values for fields
@@ -504,25 +534,31 @@ int mysql_create_function(THD *thd,udf_func *udf)
del_udf(u_d);
goto err;
}
- rw_unlock(&THR_LOCK_udf);
+ mysql_rwlock_unlock(&THR_LOCK_udf);
/* Binlog the create function. */
if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
{
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(1);
}
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(0);
err:
if (new_dl)
dlclose(dl);
- rw_unlock(&THR_LOCK_udf);
+ mysql_rwlock_unlock(&THR_LOCK_udf);
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(1);
}
@@ -550,12 +586,17 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
Turn off row binlogging of this statement and use statement-based
so that all supporting tables are updated for DROP FUNCTION command.
*/
- save_binlog_row_based= thd->current_stmt_binlog_row_based;
- thd->clear_current_stmt_binlog_row_based();
+ if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
+ thd->clear_current_stmt_binlog_format_row();
- rw_wrlock(&THR_LOCK_udf);
- if (!(udf=(udf_func*) hash_search(&udf_hash,(uchar*) udf_name->str,
- (uint) udf_name->length)))
+ tables.init_one_table(STRING_WITH_LEN("mysql"), STRING_WITH_LEN("func"),
+ "func", TL_WRITE);
+ table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT);
+
+ mysql_rwlock_wrlock(&THR_LOCK_udf);
+ DEBUG_SYNC(current_thd, "mysql_drop_function_after_lock");
+ if (!(udf=(udf_func*) my_hash_search(&udf_hash,(uchar*) udf_name->str,
+ (uint) udf_name->length)))
{
my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str);
goto err;
@@ -570,10 +611,7 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
if (udf->dlhandle && !find_udf_dl(udf->dl))
dlclose(udf->dlhandle);
- bzero((char*) &tables,sizeof(tables));
- tables.db=(char*) "mysql";
- tables.table_name= tables.alias= (char*) "func";
- if (!(table = open_ltable(thd, &tables, TL_WRITE, 0)))
+ if (!table)
goto err;
table->use_all_columns();
table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin);
@@ -586,24 +624,31 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
if ((error = table->file->ha_delete_row(table->record[0])))
table->file->print_error(error, MYF(0));
}
- close_thread_tables(thd);
-
- rw_unlock(&THR_LOCK_udf);
+ mysql_rwlock_unlock(&THR_LOCK_udf);
- /* Binlog the drop function. */
+ /*
+ Binlog the drop function. Keep the table open and locked
+ while binlogging, to avoid binlog inconsistency.
+ */
if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
{
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(1);
}
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(0);
- err:
- rw_unlock(&THR_LOCK_udf);
+err:
+ mysql_rwlock_unlock(&THR_LOCK_udf);
/* Restore the state of binlog format */
- thd->current_stmt_binlog_row_based= save_binlog_row_based;
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(1);
}
diff --git a/sql/sql_udf.h b/sql/sql_udf.h
index 4f3d4cac04f..cdb15b9e0f5 100644
--- a/sql/sql_udf.h
+++ b/sql/sql_udf.h
@@ -1,4 +1,8 @@
-/* Copyright (c) 2000, 2001, 2003-2007 MySQL AB
+#ifndef SQL_UDF_INCLUDED
+#define SQL_UDF_INCLUDED
+
+/* Copyright (c) 2000, 2003-2007 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
@@ -11,7 +15,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* This file defines structures needed by udf functions */
@@ -140,3 +144,4 @@ 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);
#endif
+#endif /* SQL_UDF_INCLUDED */
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index f9fecae38c4..6e2e6e06ff7 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -1,4 +1,5 @@
-/* Copyright (c) 2000, 2011, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,8 +12,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
@@ -21,9 +21,13 @@
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "sql_union.h"
#include "sql_select.h"
#include "sql_cursor.h"
+#include "sql_base.h" // fill_record
+#include "filesort.h" // filesort_free_buffers
bool mysql_union(THD *thd, LEX *lex, select_result *result,
SELECT_LEX_UNIT *unit, ulong setup_tables_done_option)
@@ -33,8 +37,7 @@ bool mysql_union(THD *thd, LEX *lex, select_result *result,
if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK |
setup_tables_done_option)))
res= unit->exec();
- if (res || !thd->cursor || !thd->cursor->is_open())
- res|= unit->cleanup();
+ res|= unit->cleanup();
DBUG_RETURN(res);
}
@@ -57,6 +60,8 @@ int select_union::send_data(List<Item> &values)
unit->offset_limit_cnt--;
return 0;
}
+ if (thd->killed == ABORT_QUERY)
+ return 0;
if (table->no_rows_with_nulls)
table->null_catch_flags= CHECK_ROW_FOR_NULLS_TO_REJECT;
fill_record(thd, table->field, values, TRUE, FALSE);
@@ -79,13 +84,16 @@ int select_union::send_data(List<Item> &values)
*/
return -1;
}
+ bool is_duplicate= FALSE;
/* create_internal_tmp_table_from_heap will generate error if needed */
if (table->file->is_fatal_error(write_err, HA_CHECK_DUP) &&
create_internal_tmp_table_from_heap(thd, table,
tmp_table_param.start_recinfo,
&tmp_table_param.recinfo,
- write_err, 1))
+ write_err, 1, &is_duplicate))
return 1;
+ if (is_duplicate)
+ return -1;
}
return 0;
}
@@ -122,6 +130,7 @@ bool select_union::flush()
table_alias name of the temporary table
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
DESCRIPTION
Create a temporary table that is used to store the result of a UNION,
@@ -136,7 +145,8 @@ bool
select_union::create_result_table(THD *thd_arg, List<Item> *column_types,
bool is_union_distinct, ulonglong options,
const char *alias,
- bool bit_fields_as_long, bool create_table)
+ bool bit_fields_as_long, bool create_table,
+ bool keep_row_order)
{
DBUG_ASSERT(table == 0);
tmp_table_param.init();
@@ -145,8 +155,8 @@ select_union::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*) alias,
- !create_table)))
+ options, HA_POS_ERROR, alias,
+ !create_table, keep_row_order)))
return TRUE;
table->keys_in_use_for_query.clear_all();
@@ -289,7 +299,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
bool can_skip_order_by;
sl->options|= SELECT_NO_UNLOCK;
JOIN *join= new JOIN(thd_arg, sl->item_list,
- sl->options | thd_arg->options | additional_options,
+ sl->options | thd_arg->variables.option_bits | additional_options,
tmp_result);
/*
setup_tables_done_option should be set only for very first SELECT,
@@ -305,18 +315,6 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
can_skip_order_by= is_union_select && !(sl->braces && sl->explicit_limit);
- /*
- 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);
- }
- }
-
saved_error= join->prepare(&sl->ref_pointer_array,
sl->table_list.first,
sl->with_wild,
@@ -340,6 +338,18 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
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
*/
@@ -430,7 +440,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
}
- create_options= (first_sl->options | thd_arg->options |
+ create_options= (first_sl->options | thd_arg->variables.option_bits |
TMP_TABLE_ALL_COLUMNS);
/*
Force the temporary table to be a MyISAM table if we're going to use
@@ -483,7 +493,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
init_prepare_fake_select_lex(thd, TRUE);
/* Should be done only once (the only item_list per statement) */
DBUG_ASSERT(fake_select_lex->join == 0);
- if (!(fake_select_lex->join= new JOIN(thd, item_list, thd->options,
+ if (!(fake_select_lex->join= new JOIN(thd, item_list, thd->variables.option_bits,
result)))
{
fake_select_lex->table_list.empty();
@@ -531,6 +541,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
err:
thd_arg->lex->current_select= lex_select_save;
+ (void) cleanup();
DBUG_RETURN(TRUE);
}
@@ -638,7 +649,10 @@ bool st_select_lex_unit::exec()
{
ha_rows records_at_start= 0;
thd->lex->current_select= sl;
- fake_select_lex->uncacheable|= sl->uncacheable;
+ if (sl != &thd->lex->select_lex)
+ fake_select_lex->uncacheable|= sl->uncacheable;
+ else
+ fake_select_lex->uncacheable= 0;
{
set_limit(sl);
@@ -679,6 +693,11 @@ bool st_select_lex_unit::exec()
0);
if (!saved_error)
{
+ /*
+ Save the current examined row count locally and clear the global
+ counter, so that we can accumulate the number of evaluated rows for
+ the current query block.
+ */
examined_rows+= thd->examined_row_count;
thd->examined_row_count= 0;
if (union_result->flush())
@@ -712,6 +731,20 @@ bool st_select_lex_unit::exec()
add_rows+= (ulonglong) (thd->limit_found_rows - (ulonglong)
((table->file->stats.records - records_at_start)));
}
+ if (thd->killed == ABORT_QUERY)
+ {
+ /*
+ Stop execution of the remaining queries in the UNIONS, and produce
+ the current result.
+ */
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT,
+ ER(ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT),
+ thd->accessed_rows_and_keys,
+ thd->lex->limit_rows_examined->val_uint());
+ thd->reset_killed();
+ break;
+ }
}
}
@@ -720,6 +753,11 @@ bool st_select_lex_unit::exec()
{
List<Item_func_match> empty_list;
empty_list.empty();
+ /*
+ Disable LIMIT ROWS EXAMINED in order to produce the possibly incomplete
+ result of the UNION without interruption due to exceeding the limit.
+ */
+ thd->lex->limit_rows_examined_cnt= ULONGLONG_MAX;
if (!thd->is_fatal_error) // Check if EOM
{
@@ -740,7 +778,7 @@ bool st_select_lex_unit::exec()
fake_select_lex->options, result)))
{
fake_select_lex->table_list.empty();
- DBUG_RETURN(TRUE);
+ goto err;
}
fake_select_lex->join->no_const_tables= TRUE;
@@ -812,6 +850,8 @@ bool st_select_lex_unit::exec()
}
}
thd->lex->current_select= lex_select_save;
+err:
+ thd->lex->set_limit_rows_examined();
DBUG_RETURN(saved_error);
}
diff --git a/sql/sql_union.h b/sql/sql_union.h
new file mode 100644
index 00000000000..171f607fba7
--- /dev/null
+++ b/sql/sql_union.h
@@ -0,0 +1,31 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_UNION_INCLUDED
+#define SQL_UNION_INCLUDED
+
+#include "my_global.h" /* ulong */
+
+class THD;
+class select_result;
+struct LEX;
+
+typedef class st_select_lex_unit SELECT_LEX_UNIT;
+
+bool mysql_union(THD *thd, LEX *lex, select_result *result,
+ SELECT_LEX_UNIT *unit, ulong setup_tables_done_option);
+
+
+#endif /* SQL_UNION_INCLUDED */
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 2da806fc6bf..300769ef099 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -1,4 +1,5 @@
-/* Copyright (c) 2000, 2010, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2011, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,7 +12,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
@@ -19,11 +20,28 @@
Multi-table updates were introduced by Sinisa & Monty
*/
-#include "mysql_priv.h"
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_priv.h"
+#include "unireg.h" // REQUIRED: for other includes
+#include "sql_update.h"
+#include "sql_cache.h" // query_cache_*
+#include "sql_base.h" // close_tables_for_reopen
+#include "sql_parse.h" // cleanup_items
+#include "sql_partition.h" // partition_key_modified
#include "sql_select.h"
+#include "sql_view.h" // check_key_in_view
#include "sp_head.h"
#include "sql_trigger.h"
+#include "probes_mysql.h"
#include "debug_sync.h"
+#include "key.h" // is_key_used
+#include "sql_acl.h" // *_ACL, check_grant
+#include "records.h" // init_read_record,
+ // end_read_record
+#include "filesort.h" // filesort
+#include "sql_derived.h" // mysql_derived_prepare,
+ // mysql_handle_derived,
+ // mysql_derived_filling
/**
@@ -46,6 +64,40 @@ bool records_are_comparable(const TABLE *table) {
bool compare_record(const TABLE *table)
{
+ DBUG_ASSERT(records_are_comparable(table));
+
+ if ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) != 0)
+ {
+ /*
+ Storage engine may not have read all columns of the record. Fields
+ (including NULL bits) not in the write_set may not have been read and
+ can therefore not be compared.
+ */
+ for (Field **ptr= table->field ; *ptr != NULL; ptr++)
+ {
+ Field *field= *ptr;
+ if (bitmap_is_set(table->write_set, field->field_index))
+ {
+ if (field->real_maybe_null())
+ {
+ uchar null_byte_index= field->null_ptr - table->record[0];
+
+ if (((table->record[0][null_byte_index]) & field->null_bit) !=
+ ((table->record[1][null_byte_index]) & field->null_bit))
+ return TRUE;
+ }
+ if (field->cmp_binary_offset(table->s->rec_buff_length))
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+
+ /*
+ The storage engine has read all columns, so it's safe to compare all bits
+ including those not in the write_set. This is cheaper than the field-by-field
+ comparison done above.
+ */
if (table->s->can_cmp_whole_record)
return cmp_record(table,record[1]);
/* Compare null bits */
@@ -198,16 +250,18 @@ int mysql_update(THD *thd,
COND *conds,
uint order_num, ORDER *order,
ha_rows limit,
- enum enum_duplicates handle_duplicates, bool ignore)
+ enum enum_duplicates handle_duplicates, bool ignore,
+ ha_rows *found_return, ha_rows *updated_return)
{
bool using_limit= limit != HA_POS_ERROR;
- bool safe_update= test(thd->options & OPTION_SAFE_UPDATES);
- bool used_key_is_modified, transactional_table, will_batch;
+ bool safe_update= test(thd->variables.option_bits & OPTION_SAFE_UPDATES);
+ bool used_key_is_modified= FALSE, transactional_table, will_batch;
bool can_compare_record;
int res;
int error, loc_error;
- uint used_index= MAX_KEY, dup_key_found;
+ uint used_index, dup_key_found;
bool need_sort= TRUE;
+ bool reverse= FALSE;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
uint want_privilege;
#endif
@@ -218,36 +272,29 @@ int mysql_update(THD *thd,
SQL_SELECT *select;
READ_RECORD info;
SELECT_LEX *select_lex= &thd->lex->select_lex;
- bool need_reopen;
ulonglong id;
List<Item> all_fields;
killed_state killed_status= NOT_KILLED;
DBUG_ENTER("mysql_update");
- for ( ; ; )
- {
- if (open_tables(thd, &table_list, &table_count, 0))
- DBUG_RETURN(1);
+ if (open_tables(thd, &table_list, &table_count, 0))
+ DBUG_RETURN(1);
- //Prepare views so they are handled correctly.
- if (mysql_handle_derived(thd->lex, DT_INIT))
- DBUG_RETURN(1);
+ //Prepare views so they are handled correctly.
+ if (mysql_handle_derived(thd->lex, DT_INIT))
+ DBUG_RETURN(1);
- if (table_list->is_multitable())
- {
- DBUG_ASSERT(table_list->view != 0);
- DBUG_PRINT("info", ("Switch to multi-update"));
- /* pass counter value */
- thd->lex->table_count= table_count;
- /* convert to multiupdate */
- DBUG_RETURN(2);
- }
- if (!lock_tables(thd, table_list, table_count, &need_reopen))
- break;
- if (!need_reopen)
- DBUG_RETURN(1);
- close_tables_for_reopen(thd, &table_list);
+ if (table_list->is_multitable())
+ {
+ DBUG_ASSERT(table_list->view != 0);
+ DBUG_PRINT("info", ("Switch to multi-update"));
+ /* pass counter value */
+ thd->lex->table_count= table_count;
+ /* convert to multiupdate */
+ DBUG_RETURN(2);
}
+ if (lock_tables(thd, table_list, table_count, 0))
+ DBUG_RETURN(1);
if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT))
DBUG_RETURN(1);
@@ -321,7 +368,7 @@ int mysql_update(THD *thd,
}
/* Apply the IN=>EXISTS transformation to all subqueries and optimize them. */
- if (select_lex->optimize_unflattened_subqueries())
+ if (select_lex->optimize_unflattened_subqueries(false))
DBUG_RETURN(TRUE);
if (select_lex->inner_refs_list.elements &&
@@ -378,11 +425,7 @@ int mysql_update(THD *thd,
my_ok(thd); // No matching records
DBUG_RETURN(0);
}
- if (!select && limit != HA_POS_ERROR)
- {
- if ((used_index= get_index_for_order(table, order, limit)) != MAX_KEY)
- need_sort= FALSE;
- }
+
/* If running in safe sql mode, don't allow updates without keys */
if (table->quick_keys.is_clear_all())
{
@@ -398,43 +441,56 @@ int mysql_update(THD *thd,
table->mark_columns_needed_for_update();
- /* Check if we are modifying a key that we are used to search with */
-
- if (select && select->quick)
- {
- used_index= select->quick->index;
- used_key_is_modified= (!select->quick->unique_key_range() &&
- select->quick->is_keys_used(table->write_set));
+ table->update_const_key_parts(conds);
+ order= simple_remove_const(order, conds);
+
+ if (select && select->quick && select->quick->unique_key_range())
+ { // Single row select (always "ordered"): Ok to use with key field UPDATE
+ need_sort= FALSE;
+ used_index= MAX_KEY;
+ used_key_is_modified= FALSE;
}
else
{
- used_key_is_modified= 0;
- if (used_index == MAX_KEY) // no index for sort order
- used_index= table->file->key_used_on_scan;
- if (used_index != MAX_KEY)
- used_key_is_modified= is_key_used(table, used_index, table->write_set);
+ used_index= get_index_for_order(order, table, select, limit,
+ &need_sort, &reverse);
+ if (select && select->quick)
+ {
+ DBUG_ASSERT(need_sort || used_index == select->quick->index);
+ used_key_is_modified= (!select->quick->unique_key_range() &&
+ select->quick->is_keys_used(table->write_set));
+ }
+ else
+ {
+ if (need_sort)
+ { // Assign table scan index to check below for modified key fields:
+ used_index= table->file->key_used_on_scan;
+ }
+ if (used_index != MAX_KEY)
+ { // Check if we are modifying a key that we are used to search with:
+ used_key_is_modified= is_key_used(table, used_index, table->write_set);
+ }
+ }
}
-
-#ifdef WITH_PARTITION_STORAGE_ENGINE
if (used_key_is_modified || order ||
partition_key_modified(table, table->write_set))
-#else
- if (used_key_is_modified || order)
-#endif
{
/*
We can't update table directly; We must first search after all
matching rows before updating the table!
*/
+
+ // Verify that table->restore_column_maps_after_mark_index() will work
+ DBUG_ASSERT(table->read_set == &table->def_read_set);
+ DBUG_ASSERT(table->write_set == &table->def_write_set);
+
if (used_index < MAX_KEY && old_covering_keys.is_set(used_index))
table->add_read_columns_used_by_index(used_index);
else
- {
table->use_all_columns();
- }
- /* note: We avoid sorting avoid if we sort on the used index */
+ /* note: We avoid sorting if we sort on the used index */
if (order && (need_sort || used_key_is_modified))
{
/*
@@ -479,7 +535,10 @@ int mysql_update(THD *thd,
/* If quick select is used, initialize it before retrieving rows. */
if (select && select->quick && select->quick->reset())
+ {
+ close_cached_file(&tempfile);
goto err;
+ }
table->file->try_semi_consistent_read(1);
/*
@@ -499,7 +558,7 @@ int mysql_update(THD *thd,
goto err;
}
else
- init_read_record_idx(&info, thd, table, 1, used_index);
+ init_read_record_idx(&info, thd, table, 1, used_index, reverse);
thd_proc_info(thd, "Searching rows for update");
ha_rows tmp_limit= limit;
@@ -531,13 +590,18 @@ int mysql_update(THD *thd,
}
else
{
- table->file->unlock_row();
+ /*
+ Don't try unlocking the row if skip_record reported an error since in
+ this case the transaction might have been rolled back already.
+ */
if (error < 0)
{
/* Fatal error from select->skip_record() */
error= 1;
break;
}
+ else
+ table->file->unlock_row();
}
}
if (thd->killed && !error)
@@ -566,8 +630,11 @@ int mysql_update(THD *thd,
if (error >= 0)
goto err;
}
- if (table->key_read)
- table->restore_column_maps_after_mark_index();
+ /*
+ This restore bitmaps, works for add_read_columns_used_by_index() and
+ use_all_columns():
+ */
+ table->restore_column_maps_after_mark_index();
}
if (ignore)
@@ -710,11 +777,13 @@ int mysql_update(THD *thd,
If (ignore && error is ignorable) we don't have to
do anything; otherwise...
*/
+ myf flags= 0;
+
if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
- thd->fatal_error(); /* Other handler errors are fatal */
+ flags|= ME_FATALERROR; /* Other handler errors are fatal */
prepare_record_for_error_message(error, table);
- table->file->print_error(error,MYF(0));
+ table->file->print_error(error,MYF(flags));
error= 1;
break;
}
@@ -771,9 +840,18 @@ int mysql_update(THD *thd,
}
}
}
- else
+ /*
+ 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())
table->file->unlock_row();
- thd->row_count++;
+ else
+ {
+ error= 1;
+ break;
+ }
+ thd->warning_info->inc_current_row_for_warning();
if (thd->is_error())
{
error= 1;
@@ -811,9 +889,8 @@ int mysql_update(THD *thd,
*/
{
/* purecov: begin inspected */
- thd->fatal_error();
prepare_record_for_error_message(loc_error, table);
- table->file->print_error(loc_error,MYF(0));
+ table->file->print_error(loc_error,MYF(ME_FATALERROR));
error= 1;
/* purecov: end */
}
@@ -829,7 +906,7 @@ int mysql_update(THD *thd,
end_read_record(&info);
delete select;
thd_proc_info(thd, "end");
- VOID(table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY));
+ (void) table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
/*
Invalidate the table in the query cache if something changed.
@@ -839,6 +916,9 @@ int mysql_update(THD *thd,
{
query_cache_invalidate3(thd, table_list, 1);
}
+
+ if (thd->transaction.stmt.modified_non_trans_table)
+ thd->transaction.all.modified_non_trans_table= TRUE;
/*
error < 0 means really no error at all: we processed all rows until the
@@ -861,13 +941,11 @@ int mysql_update(THD *thd,
if (thd->binlog_query(THD::ROW_QUERY_TYPE,
thd->query(), thd->query_length(),
- transactional_table, FALSE, errcode))
+ transactional_table, FALSE, FALSE, errcode))
{
error=1; // Rollback update
}
}
- if (thd->transaction.stmt.modified_non_trans_table)
- thd->transaction.all.modified_non_trans_table= TRUE;
}
DBUG_ASSERT(transactional_table || !updated || thd->transaction.stmt.modified_non_trans_table);
free_underlaid_joins(thd, select_lex);
@@ -879,11 +957,11 @@ int mysql_update(THD *thd,
if (error < 0)
{
char buff[MYSQL_ERRMSG_SIZE];
- my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO), (ulong) found, (ulong) updated,
- (ulong) thd->cuted_fields);
- thd->row_count_func=
- (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
- my_ok(thd, (ulong) thd->row_count_func, id, buff);
+ my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO), (ulong) found,
+ (ulong) updated,
+ (ulong) thd->warning_info->statement_warn_count());
+ my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
+ id, buff);
DBUG_PRINT("info",("%ld records updated", (long) updated));
}
thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* calc cuted fields */
@@ -893,6 +971,8 @@ int mysql_update(THD *thd,
thd->lex->current_select->save_leaf_tables(thd);
thd->lex->current_select->first_cond_optimization= 0;
}
+ *found_return= found;
+ *updated_return= updated;
DBUG_RETURN((error >= 0 || thd->is_error()) ? 1 : 0);
err:
@@ -929,19 +1009,6 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
SELECT_LEX *select_lex= &thd->lex->select_lex;
DBUG_ENTER("mysql_prepare_update");
- /*
- Statement-based replication of UPDATE ... LIMIT is not safe as order of
- rows is not defined, so in mixed mode we go to row-based.
-
- Note that we may consider a statement as safe if ORDER BY primary_key
- is present. However it may confuse users to see very similiar statements
- replicated differently.
- */
- if (thd->lex->current_select->select_limit)
- {
- thd->lex->set_stmt_unsafe();
- thd->set_current_stmt_binlog_row_based_if_mixed();
- }
#ifndef NO_EMBEDDED_ACCESS_CHECKS
table_list->grant.want_privilege= table->grant.want_privilege=
(SELECT_ACL & ~table->grant.privilege);
@@ -950,6 +1017,13 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
thd->lex->allow_sum_func= 0;
+ /*
+ We do not call DT_MERGE_FOR_INSERT because it has no sense for simple
+ (not multi-) update
+ */
+ if (mysql_handle_derived(thd->lex, DT_PREPARE))
+ DBUG_RETURN(TRUE);
+
if (setup_tables_and_check_access(thd, &select_lex->context,
&select_lex->top_join_list,
table_list,
@@ -968,7 +1042,6 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
if ((duplicate= unique_table(thd, table_list, table_list->next_global, 0)))
{
update_non_unique_table_error(table_list, "UPDATE", duplicate);
- my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name);
DBUG_RETURN(TRUE);
}
}
@@ -997,6 +1070,111 @@ static table_map get_table_map(List<Item> *items)
return map;
}
+/**
+ If one row is updated through two different aliases and the first
+ update physically moves the row, the second update will error
+ because the row is no longer located where expected. This function
+ checks if the multiple-table update is about to do that and if so
+ returns with an error.
+
+ The following update operations physically moves rows:
+ 1) Update of a column in a clustered primary key
+ 2) Update of a column used to calculate which partition the row belongs to
+
+ This function returns with an error if both of the following are
+ true:
+
+ a) A table in the multiple-table update statement is updated
+ through multiple aliases (including views)
+ b) At least one of the updates on the table from a) may physically
+ moves the row. Note: Updating a column used to calculate which
+ partition a row belongs to does not necessarily mean that the
+ row is moved. The new value may or may not belong to the same
+ partition.
+
+ @param leaves First leaf table
+ @param tables_for_update Map of tables that are updated
+
+ @return
+ true if the update is unsafe, in which case an error message is also set,
+ false otherwise.
+*/
+static
+bool unsafe_key_update(List<TABLE_LIST> leaves, table_map tables_for_update)
+{
+ List_iterator_fast<TABLE_LIST> it(leaves), it2(leaves);
+ TABLE_LIST *tl, *tl2;
+
+ while ((tl= it++))
+ {
+ if (tl->table->map & tables_for_update)
+ {
+ TABLE *table1= tl->table;
+ bool primkey_clustered= (table1->file->primary_key_is_clustered() &&
+ table1->s->primary_key != MAX_KEY);
+
+ bool table_partitioned= false;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ table_partitioned= (table1->part_info != NULL);
+#endif
+
+ if (!table_partitioned && !primkey_clustered)
+ continue;
+
+ it2.rewind();
+ while ((tl2= it2++))
+ {
+ /*
+ Look at "next" tables only since all previous tables have
+ already been checked
+ */
+ TABLE *table2= tl2->table;
+ if (tl2 != tl &&
+ table2->map & tables_for_update && table1->s == table2->s)
+ {
+ // A table is updated through two aliases
+ if (table_partitioned &&
+ (partition_key_modified(table1, table1->write_set) ||
+ partition_key_modified(table2, table2->write_set)))
+ {
+ // Partitioned key is updated
+ my_error(ER_MULTI_UPDATE_KEY_CONFLICT, MYF(0),
+ tl->belong_to_view ? tl->belong_to_view->alias
+ : tl->alias,
+ tl2->belong_to_view ? tl2->belong_to_view->alias
+ : tl2->alias);
+ return true;
+ }
+
+ if (primkey_clustered)
+ {
+ // The primary key can cover multiple columns
+ KEY key_info= table1->key_info[table1->s->primary_key];
+ KEY_PART_INFO *key_part= key_info.key_part;
+ KEY_PART_INFO *key_part_end= key_part + key_info.key_parts;
+
+ for (;key_part != key_part_end; ++key_part)
+ {
+ if (bitmap_is_set(table1->write_set, key_part->fieldnr-1) ||
+ bitmap_is_set(table2->write_set, key_part->fieldnr-1))
+ {
+ // Clustered primary key is updated
+ my_error(ER_MULTI_UPDATE_KEY_CONFLICT, MYF(0),
+ tl->belong_to_view ? tl->belong_to_view->alias
+ : tl->alias,
+ tl2->belong_to_view ? tl2->belong_to_view->alias
+ : tl2->alias);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
/*
make update specific preparation and checks after opening tables
@@ -1024,19 +1202,24 @@ int mysql_multi_update_prepare(THD *thd)
count in open_tables()
*/
uint table_count= lex->table_count;
- const bool using_lock_tables= thd->locked_tables != 0;
+ const bool using_lock_tables= thd->locked_tables_mode != LTM_NONE;
bool original_multiupdate= (thd->lex->sql_command == SQLCOM_UPDATE_MULTI);
- bool need_reopen= FALSE;
DBUG_ENTER("mysql_multi_update_prepare");
/* following need for prepared statements, to run next time multi-update */
thd->lex->sql_command= SQLCOM_UPDATE_MULTI;
-reopen_tables:
+ /*
+ Open tables and create derived ones, but do not lock and fill them yet.
- /* open tables and create derived ones, but do not lock and fill them */
- if (((original_multiupdate || need_reopen) &&
- open_tables(thd, &table_list, &table_count, 0)) ||
+ During prepare phase acquire only S metadata locks instead of SW locks to
+ keep prepare of multi-UPDATE compatible with concurrent LOCK TABLES WRITE
+ and global read lock.
+ */
+ if ((original_multiupdate &&
+ open_tables(thd, &table_list, &table_count,
+ (thd->stmt_arena->is_stmt_prepare() ?
+ MYSQL_OPEN_FORCE_SHARED_MDL : 0))) ||
mysql_handle_derived(lex, DT_INIT))
DBUG_RETURN(TRUE);
/*
@@ -1080,6 +1263,9 @@ reopen_tables:
thd->table_map_for_update= tables_for_update= get_table_map(fields);
+ if (unsafe_key_update(lex->select_lex.leaf_tables, tables_for_update))
+ DBUG_RETURN(true);
+
/*
Setup timestamp handling and locking mode
*/
@@ -1115,12 +1301,17 @@ reopen_tables:
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
improve performance.
+ We don't downgrade metadata lock from SW to SR in this case as
+ there is no guarantee that the same ticket is not used by
+ another table instance used by this statement which is going to
+ be write-locked (for example, trigger to be invoked might try
+ to update this table).
*/
- tl->lock_type= read_lock_type_for_table(thd, lex, tl);
+ if (using_lock_tables)
+ tl->lock_type= read_lock_type_for_table(thd, lex, tl);
+ else
+ tl->set_lock_type(thd, read_lock_type_for_table(thd, lex, tl));
tl->updating= 0;
- /* Update TABLE::lock_type accordingly. */
- if (!tl->placeholder() && !using_lock_tables)
- tl->table->reginfo.lock_type= tl->lock_type;
}
}
for (tl= table_list; tl; tl= tl->next_local)
@@ -1129,10 +1320,11 @@ reopen_tables:
if (!tl->is_derived())
{
uint want_privilege= tl->updating ? UPDATE_ACL : SELECT_ACL;
- if (check_access(thd, want_privilege,
- tl->db, &tl->grant.privilege, 0, 0,
- test(tl->schema_table)) ||
- check_grant(thd, want_privilege, tl, 0, 1, 0))
+ if (check_access(thd, want_privilege, tl->db,
+ &tl->grant.privilege,
+ &tl->grant.m_internal,
+ 0, 0) ||
+ check_grant(thd, want_privilege, tl, FALSE, 1, FALSE))
DBUG_RETURN(TRUE);
}
}
@@ -1154,64 +1346,11 @@ reopen_tables:
/* now lock and fill tables */
if (!thd->stmt_arena->is_stmt_prepare() &&
- lock_tables(thd, table_list, table_count, &need_reopen))
+ lock_tables(thd, table_list, table_count, 0))
{
- if (!need_reopen)
- DBUG_RETURN(TRUE);
-
- DBUG_PRINT("info", ("lock_tables failed, reopening"));
-
- /*
- We have to reopen tables since some of them were altered or dropped
- during lock_tables() or something was done with their triggers.
- Let us do some cleanups to be able do setup_table() and setup_fields()
- once again.
- */
- List_iterator_fast<Item> it(*fields);
- Item *item;
- while ((item= it++))
- item->cleanup();
-
- /* We have to cleanup translation tables of views. */
- for (TABLE_LIST *tbl= table_list; tbl; tbl= tbl->next_global)
- tbl->cleanup_items();
-
- /*
- To not to hog memory (as a result of the
- unit->reinit_exec_mechanism() call below):
- */
- lex->unit.cleanup();
-
- for (SELECT_LEX *sl= lex->all_selects_list;
- sl;
- sl= sl->next_select_in_list())
- {
- SELECT_LEX_UNIT *unit= sl->master_unit();
- unit->reinit_exec_mechanism(); // reset unit->prepared flags
- /*
- Reset 'clean' flag back to force normal execution of
- unit->cleanup() in Prepared_statement::cleanup_stmt()
- (call to lex->unit.cleanup() above sets this flag to TRUE).
- */
- unit->unclean();
- }
- // Reset 'prepared' flags for all derived tables/views
- mysql_handle_list_of_derived(thd->lex, table_list, DT_REINIT);
-
- /*
- Also we need to cleanup Natural_join_column::table_field items.
- To not to traverse a join tree we will cleanup whole
- thd->free_list (in PS execution mode that list may not contain
- items from 'fields' list, so the cleanup above is necessary to.
- */
- cleanup_items(thd->free_list);
- cleanup_items(thd->stmt_arena->free_list);
- close_tables_for_reopen(thd, &table_list);
-
- DEBUG_SYNC(thd, "multi_update_reopen_tables");
-
- goto reopen_tables;
+ DBUG_RETURN(TRUE);
}
+ /* @todo: downgrade the metadata locks here. */
/*
Check that we are not using table that we are updating, but we should
@@ -1266,18 +1405,22 @@ bool mysql_multi_update(THD *thd,
List<Item> *values,
COND *conds,
ulonglong options,
- enum enum_duplicates handle_duplicates, bool ignore,
- SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex)
+ enum enum_duplicates handle_duplicates,
+ bool ignore,
+ SELECT_LEX_UNIT *unit,
+ SELECT_LEX *select_lex,
+ multi_update **result)
{
- multi_update *result;
bool res;
DBUG_ENTER("mysql_multi_update");
- if (!(result= new multi_update(table_list,
+ if (!(*result= new multi_update(table_list,
&thd->lex->select_lex.leaf_tables,
fields, values,
handle_duplicates, ignore)))
+ {
DBUG_RETURN(TRUE);
+ }
thd->abort_on_warning= test(thd->variables.sql_mode &
(MODE_STRICT_TRANS_TABLES |
@@ -1292,15 +1435,14 @@ bool mysql_multi_update(THD *thd,
(ORDER *)NULL,
options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
OPTION_SETUP_TABLES_DONE,
- result, unit, select_lex);
+ *result, unit, select_lex);
DBUG_PRINT("info",("res: %d report_error: %d", res, (int) thd->is_error()));
res|= thd->is_error();
if (unlikely(res))
- result->abort();
- delete result;
+ (*result)->abort_result_set();
thd->abort_on_warning= 0;
- DBUG_RETURN(FALSE);
+ DBUG_RETURN(res);
}
@@ -1569,7 +1711,7 @@ multi_update::initialize_tables(JOIN *join)
TABLE_LIST *table_ref;
DBUG_ENTER("initialize_tables");
- if ((thd->options & OPTION_SAFE_UPDATES) && error_if_full_join(join))
+ if ((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;
@@ -1694,13 +1836,14 @@ loop_end:
tmp_param->field_count=temp_fields.elements;
tmp_param->group_parts=1;
tmp_param->group_length= table->file->ref_length;
- if (!(tmp_tables[cnt]=create_tmp_table(thd,
- tmp_param,
- temp_fields,
- (ORDER*) &group, 0, 0,
- TMP_TABLE_ALL_COLUMNS,
- HA_POS_ERROR,
- (char *) "")))
+ /* 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, "");
+ thd->variables.big_tables= save_big_tables;
+ if (!tmp_tables[cnt])
DBUG_RETURN(1);
tmp_tables[cnt]->file->extra(HA_EXTRA_WRITE_CACHE);
}
@@ -1818,11 +1961,13 @@ int multi_update::send_data(List<Item> &not_used_values)
If (ignore && error == is ignorable) we don't have to
do anything; otherwise...
*/
+ myf flags= 0;
+
if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
- thd->fatal_error(); /* Other handler errors are fatal */
+ flags|= ME_FATALERROR; /* Other handler errors are fatal */
prepare_record_for_error_message(error, table);
- table->file->print_error(error,MYF(0));
+ table->file->print_error(error,MYF(flags));
DBUG_RETURN(1);
}
}
@@ -1836,10 +1981,10 @@ int multi_update::send_data(List<Item> &not_used_values)
/* non-transactional or transactional table got modified */
/* either multi_update class' flag is raised in its branch */
if (table->file->has_transactions())
- transactional_tables= 1;
+ transactional_tables= TRUE;
else
{
- trans_safe= 0;
+ trans_safe= FALSE;
thd->transaction.stmt.modified_non_trans_table= TRUE;
}
}
@@ -1887,7 +2032,7 @@ int multi_update::send_data(List<Item> &not_used_values)
create_internal_tmp_table_from_heap(thd, tmp_table,
tmp_table_param[offset].start_recinfo,
&tmp_table_param[offset].recinfo,
- error, 1))
+ error, 1, NULL))
{
do_update= 0;
DBUG_RETURN(1); // Not a table_is_full error
@@ -1907,7 +2052,7 @@ void multi_update::send_error(uint errcode,const char *err)
}
-void multi_update::abort()
+void multi_update::abort_result_set()
{
/* the error was handled or nothing deleted and no side effects return */
if (error_handled ||
@@ -1932,7 +2077,7 @@ void multi_update::abort()
todo/fixme: do_update() is never called with the arg 1.
should it change the signature to become argless?
*/
- VOID(do_updates());
+ (void) do_updates();
}
}
if (thd->transaction.stmt.modified_non_trans_table)
@@ -1951,8 +2096,8 @@ void multi_update::abort()
int errcode= query_error_code(thd, thd->killed == NOT_KILLED);
/* the error of binary logging is ignored */
(void)thd->binlog_query(THD::ROW_QUERY_TYPE,
- thd->query(), thd->query_length(),
- transactional_tables, FALSE, errcode);
+ thd->query(), thd->query_length(),
+ transactional_tables, FALSE, FALSE, errcode);
}
thd->transaction.all.modified_non_trans_table= TRUE;
}
@@ -2050,7 +2195,7 @@ int multi_update::do_updates()
{
if ((local_error=
tbl->file->ha_rnd_pos(tbl->record[0],
- (uchar*) tmp_table->field[field_num]->ptr)))
+ (uchar *) tmp_table->field[field_num]->ptr)))
{
err_table= tbl;
goto err;
@@ -2112,10 +2257,10 @@ int multi_update::do_updates()
if (updated != org_updated)
{
if (table->file->has_transactions())
- transactional_tables= 1;
+ transactional_tables= TRUE;
else
{
- trans_safe= 0; // Can't do safe rollback
+ trans_safe= FALSE; // Can't do safe rollback
thd->transaction.stmt.modified_non_trans_table= TRUE;
}
}
@@ -2130,25 +2275,29 @@ int multi_update::do_updates()
err:
{
- thd->fatal_error();
prepare_record_for_error_message(local_error, err_table);
- err_table->file->print_error(local_error,MYF(0));
+ err_table->file->print_error(local_error,MYF(ME_FATALERROR));
}
err2:
- (void) table->file->ha_rnd_end();
- (void) tmp_table->file->ha_rnd_end();
+ if (table->file->inited)
+ (void) table->file->ha_rnd_end();
+ if (tmp_table->file->inited)
+ (void) tmp_table->file->ha_rnd_end();
check_opt_it.rewind();
while (TABLE *tbl= check_opt_it++)
- tbl->file->ha_rnd_end();
+ {
+ if (tbl->file->inited)
+ (void) tbl->file->ha_rnd_end();
+ }
if (updated != org_updated)
{
if (table->file->has_transactions())
- transactional_tables= 1;
+ transactional_tables= TRUE;
else
{
- trans_safe= 0;
+ trans_safe= FALSE;
thd->transaction.stmt.modified_non_trans_table= TRUE;
}
}
@@ -2170,7 +2319,9 @@ bool multi_update::send_eof()
Does updates for the last n - 1 tables, returns 0 if ok;
error takes into account killed status gained in do_updates()
*/
- int local_error = (table_count) ? do_updates() : 0;
+ int local_error= thd->is_error();
+ if (!local_error)
+ local_error = (table_count) ? do_updates() : 0;
/*
if local_error is not set ON until after do_updates() then
later carried out killing should not affect binlogging.
@@ -2194,8 +2345,9 @@ bool multi_update::send_eof()
either from the query's list or via a stored routine: bug#13270,23333
*/
- DBUG_ASSERT(trans_safe || !updated ||
- thd->transaction.stmt.modified_non_trans_table);
+ if (thd->transaction.stmt.modified_non_trans_table)
+ thd->transaction.all.modified_non_trans_table= TRUE;
+
if (local_error == 0 || thd->transaction.stmt.modified_non_trans_table)
{
if (mysql_bin_log.is_open())
@@ -2207,14 +2359,15 @@ bool multi_update::send_eof()
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, errcode))
+ transactional_tables, FALSE, FALSE, errcode))
{
local_error= 1; // Rollback update
}
}
- if (thd->transaction.stmt.modified_non_trans_table)
- thd->transaction.all.modified_non_trans_table= TRUE;
}
+ DBUG_ASSERT(trans_safe || !updated ||
+ thd->transaction.stmt.modified_non_trans_table);
+
if (local_error != 0)
error_handled= TRUE; // to force early leave from ::send_error()
@@ -2230,8 +2383,7 @@ bool multi_update::send_eof()
thd->first_successful_insert_id_in_prev_stmt : 0;
my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO),
(ulong) found, (ulong) updated, (ulong) thd->cuted_fields);
- thd->row_count_func=
- (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
- ::my_ok(thd, (ulong) thd->row_count_func, id, buff);
+ ::my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
+ id, buff);
DBUG_RETURN(FALSE);
}
diff --git a/sql/sql_update.h b/sql/sql_update.h
new file mode 100644
index 00000000000..64029c5d634
--- /dev/null
+++ b/sql/sql_update.h
@@ -0,0 +1,44 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_UPDATE_INCLUDED
+#define SQL_UPDATE_INCLUDED
+
+#include "sql_class.h" /* enum_duplicates */
+
+class Item;
+struct TABLE_LIST;
+class THD;
+
+typedef class st_select_lex SELECT_LEX;
+typedef class st_select_lex_unit SELECT_LEX_UNIT;
+
+bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
+ Item **conds, uint order_num, ORDER *order);
+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 mysql_multi_update(THD *thd, TABLE_LIST *table_list,
+ List<Item> *fields, List<Item> *values,
+ COND *conds, ulonglong options,
+ enum enum_duplicates handle_duplicates, bool ignore,
+ SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex,
+ multi_update **result);
+bool records_are_comparable(const TABLE *table);
+bool compare_record(const TABLE *table);
+
+#endif /* SQL_UPDATE_INCLUDED */
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 1f9e6cf1c11..b711f05be02 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -1,4 +1,5 @@
/* Copyright (c) 2004, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2011, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -15,28 +16,30 @@
*/
#define MYSQL_LEX 1
-#include "mysql_priv.h"
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_priv.h"
+#include "unireg.h"
+#include "sql_view.h"
+#include "sql_base.h" // find_table_in_global_list, lock_table_names
+#include "sql_parse.h" // sql_parse
+#include "sql_cache.h" // query_cache_*
+#include "lock.h" // MYSQL_OPEN_SKIP_TEMPORARY
+#include "sql_show.h" // append_identifier
+#include "sql_table.h" // build_table_filename
+#include "sql_db.h" // mysql_opt_change_db, mysql_change_db
+#include "sql_acl.h" // *_ACL, check_grant
#include "sql_select.h"
#include "parse_file.h"
#include "sp_head.h"
#include "sp.h"
#include "sp_cache.h"
+#include "datadict.h" // dd_frm_type()
#define MD5_BUFF_LENGTH 33
const LEX_STRING view_type= { C_STRING_WITH_LEN("VIEW") };
-static int mysql_register_view(THD *thd, TABLE_LIST *view,
- enum_view_create_mode mode);
-
-const char *updatable_views_with_limit_names[]= { "NO", "YES", NullS };
-TYPELIB updatable_views_with_limit_typelib=
-{
- array_elements(updatable_views_with_limit_names)-1, "",
- updatable_views_with_limit_names,
- 0
-};
-
+static int mysql_register_view(THD *, TABLE_LIST *, enum_view_create_mode);
/*
Make a unique name for an anonymous view column
@@ -207,40 +210,17 @@ static void make_valid_column_names(List<Item> &item_list)
static bool
fill_defined_view_parts (THD *thd, TABLE_LIST *view)
{
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length;
LEX *lex= thd->lex;
- bool not_used;
TABLE_LIST decoy;
memcpy (&decoy, view, sizeof (TABLE_LIST));
+ key_length= create_table_def_key(thd, key, view, 0);
- /*
- Let's reset decoy.view before calling open_table(): when we start
- supporting ALTER VIEW in PS/SP that may save us from a crash.
- */
-
- decoy.view= NULL;
-
- /*
- open_table() will return NULL if 'decoy' is idenitifying a view *and*
- there is no TABLE object for that view in the table cache. However,
- decoy.view will be set to 1.
-
- If there is a TABLE-instance for the oject identified by 'decoy',
- open_table() will return that instance no matter if it is a table or
- a view.
-
- Thus, there is no need to check for the return value of open_table(),
- since the return value itself does not mean anything.
- */
-
- open_table(thd, &decoy, thd->mem_root, &not_used, OPEN_VIEW_NO_PARSE);
-
- if (!decoy.view)
- {
- /* It's a table. */
- my_error(ER_WRONG_OBJECT, MYF(0), view->db, view->table_name, "VIEW");
+ if (tdc_open_view(thd, &decoy, decoy.alias, key, key_length,
+ thd->mem_root, OPEN_VIEW_NO_PARSE))
return TRUE;
- }
if (!lex->definer)
{
@@ -296,13 +276,17 @@ 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, &view->grant.privilege,
- 0, 0, is_schema_db(view->db, view->db_length)) ||
- check_grant(thd, CREATE_VIEW_ACL, view, 0, 1, 0)) ||
+ if ((check_access(thd, CREATE_VIEW_ACL, view->db,
+ &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, &view->grant.privilege,
- 0, 0, is_schema_db(view->db, view->db_length)) ||
- check_grant(thd, DROP_ACL, view, 0, 1, 0))))
+ (check_access(thd, DROP_ACL, view->db,
+ &view->grant.privilege,
+ &view->grant.m_internal,
+ 0, 0) ||
+ check_grant(thd, DROP_ACL, view, FALSE, 1, FALSE))))
goto err;
for (sl= select_lex; sl; sl= sl->next_select())
@@ -351,8 +335,10 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view,
if (!tbl->table_in_first_from_clause)
{
if (check_access(thd, SELECT_ACL, tbl->db,
- &tbl->grant.privilege, 0, 0, test(tbl->schema_table)) ||
- check_grant(thd, SELECT_ACL, tbl, 0, 1, 0))
+ &tbl->grant.privilege,
+ &tbl->grant.m_internal,
+ 0, 0) ||
+ check_grant(thd, SELECT_ACL, tbl, FALSE, 1, FALSE))
goto err;
}
}
@@ -418,9 +404,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
TABLE_LIST *tables= lex->query_tables;
TABLE_LIST *tbl;
SELECT_LEX *select_lex= &lex->select_lex;
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
SELECT_LEX *sl;
-#endif
SELECT_LEX_UNIT *unit= &lex->unit;
bool res= FALSE;
DBUG_ENTER("mysql_create_view");
@@ -429,12 +413,59 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
DBUG_ASSERT(!lex->proc_list.first && !lex->result &&
!lex->param_list.elements);
+ /*
+ We can't allow taking exclusive meta-data locks of unlocked view under
+ LOCK TABLES since this might lead to deadlock. Since at the moment we
+ can't really lock view with LOCK TABLES we simply prohibit creation/
+ alteration of views under LOCK TABLES.
+ */
+
+ if (thd->locked_tables_mode)
+ {
+ my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
+ res= TRUE;
+ goto err;
+ }
+
+ if ((res= create_view_precheck(thd, tables, view, mode)))
+ goto err;
+
+ lex->link_first_table_back(view, link_to_local);
+ view->open_type= OT_BASE_ONLY;
+
+ if (open_and_lock_tables(thd, lex->query_tables, TRUE, 0))
+ {
+ view= lex->unlink_first_table(&link_to_local);
+ res= TRUE;
+ goto err;
+ }
+
+ view= lex->unlink_first_table(&link_to_local);
+
+ if (check_db_dir_existence(view->db))
+ {
+ my_error(ER_BAD_DB_ERROR, MYF(0), view->db);
+ res= TRUE;
+ goto err;
+ }
+
if (mode == VIEW_ALTER && fill_defined_view_parts(thd, view))
{
res= TRUE;
goto err;
}
+ if (lex->limit_rows_examined)
+ {
+ /*
+ LIMIT ROWS EXAMINED is not supported inside views to avoid complicated
+ side-effects and semantics of the clause.
+ */
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0), "LIMIT ROWS EXAMINED inside views");
+ res= TRUE;
+ goto err;
+ }
+
sp_cache_invalidate();
if (!lex->definer)
@@ -490,16 +521,6 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
}
}
#endif
-
- if ((res= create_view_precheck(thd, tables, view, mode)))
- goto err;
-
- if (open_and_lock_tables(thd, tables))
- {
- res= TRUE;
- goto err;
- }
-
/*
check that tables are not temporary and this VIEW do not used in query
(it is possible with ALTERing VIEW).
@@ -578,7 +599,8 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
}
/* Check if the auto generated column names are conforming. */
- make_valid_column_names(select_lex->item_list);
+ for (sl= select_lex; sl; sl= sl->next_select())
+ make_valid_column_names(sl->item_list);
if (check_duplicate_names(select_lex->item_list, 1))
{
@@ -644,15 +666,9 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
}
#endif
- if (wait_if_global_read_lock(thd, 0, 0))
- {
- res= TRUE;
- goto err;
- }
- VOID(pthread_mutex_lock(&LOCK_open));
res= mysql_register_view(thd, view, mode);
- if (mysql_bin_log.is_open())
+ if (!res && mysql_bin_log.is_open())
{
String buff;
const LEX_STRING command[3]=
@@ -692,14 +708,12 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
int errcode= query_error_code(thd, TRUE);
if (thd->binlog_query(THD::STMT_QUERY_TYPE,
- buff.ptr(), buff.length(), FALSE, FALSE, errcode))
+ buff.ptr(), buff.length(), FALSE, FALSE, FALSE, errcode))
res= TRUE;
}
- VOID(pthread_mutex_unlock(&LOCK_open));
if (mode != VIEW_CREATE_NEW)
query_cache_invalidate3(thd, view, 0);
- start_waiting_global_read_lock(thd);
if (res)
goto err;
@@ -844,7 +858,8 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
thd->variables.sql_mode&= ~MODE_ANSI_QUOTES;
lex->unit.print(&view_query, QT_VIEW_INTERNAL);
- lex->unit.print(&is_query, QT_IS);
+ lex->unit.print(&is_query,
+ enum_query_type(QT_TO_SYSTEM_CHARSET | QT_WITHOUT_INTRODUCERS));
thd->variables.sql_mode|= sql_mode;
}
@@ -1153,8 +1168,18 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
table->db, table->table_name);
get_default_definer(thd, &table->definer);
}
+
+ /*
+ Initialize view definition context by character set names loaded from
+ the view definition file. Use UTF8 character set if view definition
+ file is of old version and does not contain the character set names.
+ */
+ table->view_creation_ctx= View_creation_ctx::create(thd, table);
+
if (flags & OPEN_VIEW_NO_PARSE)
{
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
DBUG_RETURN(FALSE);
}
@@ -1166,16 +1191,23 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
table->view_db.length= table->db_length;
table->view_name.str= table->table_name;
table->view_name.length= table->table_name_length;
-
- /*TODO: md5 test here and warning if it is differ */
-
/*
- Initialize view definition context by character set names loaded from
- the view definition file. Use UTF8 character set if view definition
- file is of old version and does not contain the character set names.
+ We don't invalidate a prepared statement when a view changes,
+ or when someone creates a temporary table.
+ Instead, the view is inlined into the body of the statement
+ upon the first execution. Below, make sure that on
+ re-execution of a prepared statement we don't prefer
+ a temporary table to the view, if the view name was shadowed
+ with a temporary table with the same name.
+ This assignment ensures that on re-execution open_table() will
+ not try to call find_temporary_table() for this TABLE_LIST,
+ but will invoke open_table_from_share(), which will
+ eventually call this function.
*/
+ table->open_type= OT_BASE_ONLY;
+
+ /*TODO: md5 test here and warning if it is differ */
- table->view_creation_ctx= View_creation_ctx::create(thd, table);
/*
TODO: TABLE mem root should be used here when VIEW will be stored in
@@ -1211,7 +1243,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
view_select= &lex->select_lex;
view_select->select_number= ++thd->select_number;
- ulong saved_mode= thd->variables.sql_mode;
+ ulonglong saved_mode= thd->variables.sql_mode;
/* switch off modes which can prevent normal parsing of VIEW
- MODE_REAL_AS_FLOAT affect only CREATE TABLE parsing
+ MODE_PIPES_AS_CONCAT affect expression parsing
@@ -1300,8 +1332,10 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
DBUG_ASSERT(view_tables == NULL || view_tables->security_ctx == NULL);
- if (check_table_access(thd, SELECT_ACL, view_tables, UINT_MAX, TRUE) ||
- check_table_access(thd, SHOW_VIEW_ACL, &view_no_suid, UINT_MAX, TRUE))
+ if (check_table_access(thd, SELECT_ACL, view_tables,
+ FALSE, UINT_MAX, TRUE) ||
+ check_table_access(thd, SHOW_VIEW_ACL, &view_no_suid,
+ FALSE, UINT_MAX, TRUE))
{
my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0));
goto err;
@@ -1311,7 +1345,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
(old_lex->sql_command == SQLCOM_SHOW_CREATE) &&
!table->belong_to_view)
{
- if (check_table_access(thd, SHOW_VIEW_ACL, table, UINT_MAX, FALSE))
+ if (check_table_access(thd, SHOW_VIEW_ACL, table, FALSE, UINT_MAX, FALSE))
goto err;
}
@@ -1326,7 +1360,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
tbl;
tbl= (view_tables_tail= tbl)->next_global)
{
- tbl->skip_temporary= 1;
+ tbl->open_type= OT_BASE_ONLY;
tbl->belong_to_view= top_view;
tbl->referencing_view= table;
tbl->prelocking_placeholder= table->prelocking_placeholder;
@@ -1373,8 +1407,8 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
If the view's body needs row-based binlogging (e.g. the VIEW is created
from SELECT UUID()), the top statement also needs it.
*/
- if (lex->is_stmt_unsafe())
- old_lex->set_stmt_unsafe();
+ old_lex->set_stmt_unsafe_flags(lex->get_stmt_unsafe_flags());
+
view_is_mergeable= (table->algorithm != VIEW_ALGORITHM_TMPTABLE &&
lex->can_be_merged());
@@ -1396,7 +1430,11 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
anyway.
*/
for (tbl= view_main_select_tables; tbl; tbl= tbl->next_local)
+ {
tbl->lock_type= table->lock_type;
+ tbl->mdl_request.set_type((tbl->lock_type >= TL_WRITE_ALLOW_WRITE) ?
+ MDL_SHARED_WRITE : MDL_SHARED_READ);
+ }
/*
If the view is mergeable, we might want to
INSERT/UPDATE/DELETE into tables of this view. Preserve the
@@ -1436,7 +1474,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
objects of the view.
*/
if (!(table->view_sctx= (Security_context *)
- thd->stmt_arena->alloc(sizeof(Security_context))))
+ thd->stmt_arena->calloc(sizeof(Security_context))))
goto err;
security_ctx= table->view_sctx;
}
@@ -1613,16 +1651,30 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
bool something_wrong= FALSE;
DBUG_ENTER("mysql_drop_view");
- VOID(pthread_mutex_lock(&LOCK_open));
+ /*
+ We can't allow dropping of unlocked view under LOCK TABLES since this
+ might lead to deadlock. But since we can't really lock view with LOCK
+ TABLES we have to simply prohibit dropping of views.
+ */
+
+ if (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,
+ MYSQL_OPEN_SKIP_TEMPORARY))
+ DBUG_RETURN(TRUE);
+
for (view= views; view; view= view->next_local)
{
- TABLE_SHARE *share;
frm_type_enum type= FRMTYPE_ERROR;
build_table_filename(path, sizeof(path) - 1,
view->db, view->table_name, reg_ext, 0);
if (access(path, F_OK) ||
- FRMTYPE_VIEW != (type= mysql_frm_type(thd, path, &not_used)))
+ FRMTYPE_VIEW != (type= dd_frm_type(thd, path, &not_used)))
{
char name[FN_REFLEN];
my_snprintf(name, sizeof(name), "%s.%s", view->db, view->table_name);
@@ -1649,24 +1701,18 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
}
continue;
}
- if (my_delete(path, MYF(MY_WME)))
+ if (mysql_file_delete(key_file_frm, path, MYF(MY_WME)))
error= TRUE;
some_views_deleted= TRUE;
/*
- For a view, there is only one table_share object which should never
- be used outside of LOCK_open
+ For a view, there is a TABLE_SHARE object, but its
+ ref_count never goes above 1. Remove it from the table
+ definition cache, in case the view was cached.
*/
- if ((share= get_cached_table_share(view->db, view->table_name)))
- {
- DBUG_ASSERT(share->ref_count == 0);
- pthread_mutex_lock(&share->mutex);
- share->ref_count++;
- share->version= 0;
- pthread_mutex_unlock(&share->mutex);
- release_table_share(share, RELEASE_WAIT_FOR_DROP);
- }
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, view->db, view->table_name,
+ FALSE);
query_cache_invalidate3(thd, view, 0);
sp_cache_invalidate();
}
@@ -1691,8 +1737,6 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
something_wrong= 1;
}
- VOID(pthread_mutex_unlock(&LOCK_open));
-
if (something_wrong)
{
DBUG_RETURN(TRUE);
@@ -1703,55 +1747,6 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
/*
- Check type of .frm if we are not going to parse it
-
- SYNOPSIS
- mysql_frm_type()
- path path to file
-
- RETURN
- FRMTYPE_ERROR error
- FRMTYPE_TABLE table
- FRMTYPE_VIEW view
-*/
-
-frm_type_enum mysql_frm_type(THD *thd, char *path, enum legacy_db_type *dbt)
-{
- File file;
- uchar header[10]; //"TYPE=VIEW\n" it is 10 characters
- size_t error;
- DBUG_ENTER("mysql_frm_type");
-
- *dbt= DB_TYPE_UNKNOWN;
-
-
-
- if ((file= my_open(path, O_RDONLY | O_SHARE, MYF(0))) < 0)
- DBUG_RETURN(FRMTYPE_ERROR);
- error= my_read(file, (uchar*) header, sizeof(header), MYF(MY_NABP));
- my_close(file, MYF(MY_WME));
-
- if (error)
- DBUG_RETURN(FRMTYPE_ERROR);
- if (!strncmp((char*) header, "TYPE=VIEW\n", sizeof(header)))
- DBUG_RETURN(FRMTYPE_VIEW);
-
- /*
- This is just a check for DB_TYPE. We'll return default unknown type
- if the following test is true (arg #3). This should not have effect
- on return value from this function (default FRMTYPE_TABLE)
- */
- if (header[0] != (uchar) 254 || header[1] != 1 ||
- (header[2] != FRM_VER && header[2] != FRM_VER+1 &&
- (header[2] < FRM_VER+3 || header[2] > FRM_VER+4)))
- DBUG_RETURN(FRMTYPE_TABLE);
-
- *dbt= (enum legacy_db_type) (uint) *(header + 3);
- DBUG_RETURN(FRMTYPE_TABLE); // Is probably a .frm table
-}
-
-
-/*
check of key (primary or unique) presence in updatable view
SYNOPSIS
diff --git a/sql/sql_view.h b/sql/sql_view.h
index 421ab87b342..2e9c77252e8 100644
--- a/sql/sql_view.h
+++ b/sql/sql_view.h
@@ -1,6 +1,8 @@
+#ifndef SQL_VIEW_INCLUDED
+#define SQL_VIEW_INCLUDED
+
/* -*- C++ -*- */
-/* Copyright (c) 2004-2006, 2008 MySQL AB, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2004, 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
@@ -16,6 +18,16 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "sql_class.h" /* Required by sql_lex.h */
+#include "sql_lex.h" /* enum_view_create_mode, enum_drop_mode */
+
+/* Forward declarations */
+
+class File_parser;
+
+
+/* Function declarations */
+
bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view,
enum_view_create_mode mode);
@@ -31,8 +43,6 @@ bool check_key_in_view(THD *thd, TABLE_LIST * view);
bool insert_view_fields(THD *thd, List<Item> *list, TABLE_LIST *view);
-frm_type_enum mysql_frm_type(THD *thd, char *path, enum legacy_db_type *dbt);
-
int view_checksum(THD *thd, TABLE_LIST *view);
extern TYPELIB updatable_views_with_limit_typelib;
@@ -43,3 +53,6 @@ bool mysql_rename_view(THD *thd, const char *new_db, const char *new_name,
#define VIEW_ANY_ACL (SELECT_ACL | UPDATE_ACL | INSERT_ACL | DELETE_ACL)
+extern const LEX_STRING view_type;
+
+#endif /* SQL_VIEW_INCLUDED */
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index bc9dc7e7656..777cef517c0 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1,5 +1,6 @@
/*
- Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2000, 2014, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2014, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +13,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* sql_yacc.yy */
@@ -23,22 +23,23 @@
*/
%{
-/* thd is passed as an argument to yyparse(), and subsequently to yylex().
-** The type will be void*, so it must be cast to (THD*) when used.
-** Use the YYTHD macro for this.
-*/
-#define YYPARSE_PARAM yythd
-#define YYLEX_PARAM yythd
-#define YYTHD ((THD *)yythd)
-#define YYLIP (& YYTHD->m_parser_state->m_lip)
+#define YYLIP (& thd->m_parser_state->m_lip)
+#define YYPS (& thd->m_parser_state->m_yacc)
#define MYSQL_YACC
#define YYINITDEPTH 100
#define YYMAXDEPTH 3200 /* Because of 64K stack */
-#define Lex (YYTHD->lex)
+#define Lex (thd->lex)
#define Select Lex->current_select
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h" // REQUIRED: for other includes
+#include "sql_parse.h" /* comp_*_creator */
+#include "sql_table.h" /* primary_key_name */
+#include "sql_partition.h" /* mem_alloc_error, partition_info, HASH_PARTITION */
+#include "sql_acl.h" /* *_ACL */
+#include "password.h" /* my_make_scrambled_password_323, my_make_scrambled_password */
+#include "sql_class.h" /* Key_part_spec, enum_filetype, Diag_condition_item_name */
#include "slave.h"
#include "lex_symbol.h"
#include "item_create.h"
@@ -46,10 +47,17 @@
#include "sp_pcontext.h"
#include "sp_rcontext.h"
#include "sp.h"
+#include "sql_alter.h" // Alter_table*_statement
+#include "sql_truncate.h" // Truncate_statement
+#include "sql_admin.h" // Analyze/Check..._table_stmt
+#include "sql_partition_admin.h" // Alter_table_*_partition_stmt
+#include "sql_signal.h"
#include "event_parse_data.h"
#include "create_options.h"
#include <myisam.h>
#include <myisammrg.h>
+#include "keycaches.h"
+#include "set_var.h"
/* this is to get the bison compilation windows warnings out */
#ifdef _MSC_VER
@@ -59,15 +67,12 @@
int yylex(void *yylval, void *yythd);
-const LEX_STRING null_lex_str= {0,0};
-const LEX_STRING empty_lex_str= { (char*) "", 0 };
-
#define yyoverflow(A,B,C,D,E,F) \
{ \
ulong val= *(F); \
if (my_yyoverflow((B), (D), &val)) \
{ \
- yyerror((char*) (A)); \
+ yyerror(thd, (char*) (A)); \
return 2; \
} \
else \
@@ -79,7 +84,7 @@ const LEX_STRING empty_lex_str= { (char*) "", 0 };
#define MYSQL_YYABORT \
do \
{ \
- LEX::cleanup_lex_after_parse_error(YYTHD);\
+ LEX::cleanup_lex_after_parse_error(thd); \
YYABORT; \
} while (0)
@@ -136,10 +141,13 @@ void my_parse_error(const char *s)
Lex_input_stream *lip= & thd->m_parser_state->m_lip;
const char *yytext= lip->get_tok_start();
+ if (!yytext)
+ yytext= "";
+
/* Push an error into the error stack */
+ ErrConvString err(yytext, strlen(yytext), thd->variables.character_set_client);
my_printf_error(ER_PARSE_ERROR, ER(ER_PARSE_ERROR), MYF(0), s,
- (yytext ? yytext : ""),
- lip->yylineno);
+ err.ptr(), lip->yylineno);
}
/**
@@ -162,10 +170,8 @@ void my_parse_error(const char *s)
to abort from the parser.
*/
-void MYSQLerror(const char *s)
+void MYSQLerror(THD *thd, const char *s)
{
- THD *thd= current_thd;
-
/*
Restore the original LEX if it was replaced when parsing
a stored procedure. We must ensure that a parsing error
@@ -190,9 +196,9 @@ void turn_parser_debug_on()
The syntax to run with bison traces is as follows :
- Starting a server manually :
- mysqld --debug="d,parser_debug" ...
+ mysqld --debug-dbug="d,parser_debug" ...
- Running a test :
- mysql-test-run.pl --mysqld="--debug=d,parser_debug" ...
+ mysql-test-run.pl --mysqld="--debug-dbug=d,parser_debug" ...
The result will be in the process stderr (var/log/master.err)
*/
@@ -429,9 +435,16 @@ set_system_variable(THD *thd, struct sys_var_with_base *tmp,
LEX *lex= thd->lex;
/* No AUTOCOMMIT from a stored function or trigger. */
- if (lex->spcont && tmp->var == &sys_autocommit)
+ if (lex->spcont && tmp->var == Sys_autocommit_ptr)
lex->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);
+ return TRUE;
+ }
+
if (! (var= new set_var(var_type, tmp->var, &tmp->base_name, val)))
return TRUE;
@@ -600,12 +613,97 @@ Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal,
DBUG_RETURN(result);
}
+/**
+ @brief Creates a new SELECT_LEX for a UNION branch.
+
+ Sets up and initializes a SELECT_LEX structure for a query once the parser
+ discovers a UNION token. The current SELECT_LEX is pushed on the stack and
+ the new SELECT_LEX becomes the current one.
+
+ @param lex The parser state.
+
+ @param is_union_distinct True if the union preceding the new select statement
+ uses UNION DISTINCT.
+
+ @param is_top_level This should be @c TRUE if the newly created SELECT_LEX
+ is a non-nested statement.
+
+ @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)
+{
+ /*
+ 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)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), "UNION", "INTO");
+ return TRUE;
+ }
+ if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE)
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ return TRUE;
+ }
+ /* This counter shouldn't be incremented for UNION parts */
+ lex->nest_level--;
+ if (mysql_new_select(lex, 0))
+ return TRUE;
+ mysql_init_select(lex);
+ lex->current_select->linkage=UNION_TYPE;
+ if (is_union_distinct) /* UNION DISTINCT - remember position */
+ lex->current_select->master_unit()->union_distinct=
+ lex->current_select;
+ return FALSE;
+}
+
+/**
+ @brief Initializes a SELECT_LEX for a query within parentheses (aka
+ braces).
+
+ @return false if successful, true if an error was reported. In the latter
+ case parsing should stop.
+ */
+bool setup_select_in_parentheses(LEX *lex)
+{
+ SELECT_LEX * sel= lex->current_select;
+ if (sel->set_braces(1))
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ return TRUE;
+ }
+ if (sel->linkage == UNION_TYPE &&
+ !sel->master_unit()->first_select()->braces &&
+ sel->master_unit()->first_select()->linkage ==
+ UNION_TYPE)
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ return TRUE;
+ }
+ if (sel->linkage == UNION_TYPE &&
+ sel->olap != UNSPECIFIED_OLAP_TYPE &&
+ sel->master_unit()->fake_select_lex)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), "CUBE/ROLLUP", "ORDER BY");
+ return TRUE;
+ }
+ /* select in braces, can't contain global parameters */
+ if (sel->master_unit()->fake_select_lex)
+ sel->master_unit()->global_parameters=
+ sel->master_unit()->fake_select_lex;
+ 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_OPTION_UPDATING,
+ TL_READ_NO_INSERT,
+ MDL_SHARED_NO_WRITE))
return TRUE;
lex->alter_info.reset();
lex->alter_info.flags= ALTER_ADD_INDEX;
@@ -615,9 +713,9 @@ static bool add_create_index_prepare (LEX *lex, Table_ident *table)
return FALSE;
}
-
-static bool add_create_index (LEX *lex, Key::Keytype type, const char *name,
- KEY_CREATE_INFO *info= NULL, bool generated= 0)
+static bool add_create_index (LEX *lex, Key::Keytype type,
+ const LEX_STRING &name,
+ KEY_CREATE_INFO *info= NULL, bool generated= 0)
{
Key *key;
key= new Key(type, name, info ? info : &lex->key_create_info, generated,
@@ -639,6 +737,7 @@ static bool add_create_index (LEX *lex, Key::Keytype type, const char *name,
LEX_STRING lex_str;
LEX_STRING *lex_str_ptr;
LEX_SYMBOL symbol;
+ LEX_TYPE lex_type;
Table_ident *table;
char *simple_string;
Item *item;
@@ -670,12 +769,17 @@ static bool add_create_index (LEX *lex, Key::Keytype type, const char *name,
struct sp_cond_type *spcondtype;
struct { int vars, conds, hndlrs, curs; } spblock;
sp_name *spname;
- struct st_lex *lex;
+ LEX *lex;
sp_head *sphead;
struct p_elem_val *p_elem_value;
enum index_hint_type index_hint;
+ enum enum_filetype filetype;
+ enum Foreign_key::fk_option m_fk_option;
+ enum enum_yes_no_unknown m_yes_no_unk;
+ Diag_condition_item_name diag_condition_item_name;
DYNCALL_CREATE_DEF *dyncol_def;
List<DYNCALL_CREATE_DEF> *dyncol_def_list;
+ bool is_not_empty;
}
%{
@@ -683,11 +787,13 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%}
%pure_parser /* We have threads */
+%parse-param { THD *thd }
+%lex-param { THD *thd }
/*
- Currently there are 174 shift/reduce conflicts.
+ Currently there are 175 shift/reduce conflicts.
We should not introduce new conflicts any more.
*/
-%expect 174
+%expect 175
/*
Comments for TOKENS.
@@ -758,6 +864,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%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
@@ -767,6 +874,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token CHECKSUM_SYM
%token CHECK_SYM /* SQL-2003-R */
%token CIPHER_SYM
+%token CLASS_ORIGIN_SYM /* SQL-2003-N */
%token CLIENT_SYM
%token CLIENT_STATS_SYM
%token CLOSE_SYM /* SQL-2003-R */
@@ -782,6 +890,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token COLUMN_GET_SYM
%token COLUMN_LIST_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 */
@@ -789,10 +898,13 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token COMPLETION_SYM
%token COMPRESSED_SYM
%token CONCURRENT
-%token CONDITION_SYM /* SQL-2003-N */
+%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 */
@@ -806,6 +918,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token CURDATE /* MYSQL-FUNC */
%token CURRENT_USER /* SQL-2003-R */
%token CURSOR_SYM /* SQL-2003-R */
+%token CURSOR_NAME_SYM /* SQL-2003-N */
%token CURTIME /* MYSQL-FUNC */
%token DATABASE
%token DATABASES
@@ -859,12 +972,14 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token ENUM
%token EQ /* OPERATOR */
%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 EXAMINED_SYM
%token EXECUTE_SYM /* SQL-2003-R */
%token EXISTS /* SQL-2003-R */
%token EXIT_SYM
@@ -886,12 +1001,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token FOREIGN /* SQL-2003-R */
%token FOR_SYM /* SQL-2003-R */
%token FOUND_SYM /* SQL-2003-R */
-%token FRAC_SECOND_SYM
%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
@@ -908,6 +1023,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token HAVING /* SQL-2003-R */
%token HELP_SYM
%token HEX_NUM
+%token HEX_STRING
%token HIGH_PRIORITY
%token HOST_SYM
%token HOSTS_SYM
@@ -920,6 +1036,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token IDENT_QUOTED
%token IF
%token IGNORE_SYM
+%token IGNORE_SERVER_IDS_SYM
%token IMPORT
%token INDEXES
%token INDEX_SYM
@@ -927,7 +1044,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token INFILE
%token INITIAL_SIZE_SYM
%token INNER_SYM /* SQL-2003-R */
-%token INNOBASE_SYM
%token INOUT_SYM /* SQL-2003-R */
%token INSENSITIVE_SYM /* SQL-2003-R */
%token INSERT /* SQL-2003-R */
@@ -951,6 +1067,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%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
@@ -995,6 +1112,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token MASTER_SSL_VERIFY_SERVER_CERT_SYM
%token MASTER_SYM
%token MASTER_USER_SYM
+%token MASTER_HEARTBEAT_PERIOD_SYM
%token MATCH /* SQL-2003-R */
%token MAX_CONNECTIONS_PER_HOUR
%token MAX_QUERIES_PER_HOUR
@@ -1010,6 +1128,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%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
@@ -1026,6 +1145,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token MULTIPOINT
%token MULTIPOLYGON
%token MUTEX_SYM
+%token MYSQL_ERRNO_SYM
%token NAMES_SYM /* SQL-2003-N */
%token NAME_SYM /* SQL-2003-N */
%token NATIONAL_SYM /* SQL-2003-R */
@@ -1093,11 +1213,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token PREV_SYM
%token PRIMARY_SYM /* SQL-2003-R */
%token PRIVILEGES /* SQL-2003-N */
-%token PROCEDURE /* SQL-2003-R */
+%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
@@ -1115,6 +1236,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token REDUNDANT_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
@@ -1130,6 +1253,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token REPLICATION
%token REQUIRE_SYM
%token RESET_SYM
+%token RESIGNAL_SYM /* SQL-2003-R */
%token RESOURCES
%token RESTORE_SYM
%token RESTRICT
@@ -1147,6 +1271,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%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 */
@@ -1165,10 +1290,11 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%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 SLOW_SYM
+%token SLOW
%token SMALLINT /* SQL-2003-R */
%token SNAPSHOT_SYM
%token SOCKET_SYM
@@ -1200,6 +1326,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token STORAGE_SYM
%token STRAIGHT_JOIN
%token STRING_SYM
+%token SUBCLASS_ORIGIN_SYM /* SQL-2003-N */
%token SUBDATE_SYM
%token SUBJECT_SYM
%token SUBPARTITIONS_SYM
@@ -1217,6 +1344,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token TABLE_STATS_SYM
%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
@@ -1287,11 +1415,14 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token WHERE /* SQL-2003-R */
%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 XOR
%token YEAR_MONTH_SYM
%token YEAR_SYM /* SQL-2003-R */
@@ -1320,11 +1451,13 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%left INTERVAL_SYM
%type <lex_str>
- IDENT IDENT_QUOTED TEXT_STRING DECIMAL_NUM FLOAT_NUM NUM LONG_NUM HEX_NUM
+ IDENT IDENT_QUOTED TEXT_STRING DECIMAL_NUM FLOAT_NUM NUM LONG_NUM
+ HEX_NUM HEX_STRING hex_num_or_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
%type <lex_str_ptr>
opt_table_alias
@@ -1334,19 +1467,20 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
table_ident_opt_wild
%type <simple_string>
- remember_name remember_end opt_ident opt_db text_or_password
- opt_constraint constraint
+ remember_name remember_end opt_db text_or_password
%type <string>
text_string opt_gconcat_separator
+%type <lex_type> field_def
+
%type <num>
- type int_type real_type order_dir lock_option field_def
+ type type_with_opt_collate int_type real_type order_dir lock_option
udf_type if_exists opt_local opt_table_options table_options
table_option opt_if_not_exists opt_no_write_to_binlog
- delete_option opt_temporary all_or_any opt_distinct
+ opt_temporary all_or_any opt_distinct
opt_ignore_leaves fulltext_options spatial_type union_option
- start_transaction_opts opt_chain opt_release
+ start_transaction_opts
union_opt select_derived_init option_type2
opt_natural_language_mode opt_query_expansion
opt_ev_status opt_ev_on_completion ev_on_completion opt_ev_comment
@@ -1354,6 +1488,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
optional_flush_tables_arguments opt_dyncol_type dyncol_type
opt_time_precision kill_type kill_option int_num
+%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
@@ -1362,15 +1502,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <choice> choice
-%type <p_elem_value>
- part_bit_expr
-
%type <lock_type>
replace_lock_option opt_low_priority insert_lock_option load_data_lock
%type <item>
literal text_literal insert_ident order_ident
- simple_ident select_item2 expr opt_expr opt_else sum_expr in_sum_expr
+ simple_ident expr opt_expr opt_else sum_expr in_sum_expr
variable variable_aux bool_pri
predicate bit_expr
table_wild simple_expr udf_expr
@@ -1385,6 +1522,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
function_call_nonkeyword
function_call_generic
function_call_conflict kill_expr
+ signal_allowed_expr
%type <item_num>
NUM_literal
@@ -1412,12 +1550,11 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
join_table_list join_table
table_factor table_ref esc_table_ref
select_derived derived_table_list
+ select_derived_union
%type <date_time_type> date_time_type;
%type <interval> interval
-%type <interval_time_st> interval_time_st
-
%type <interval_time_st> interval_time_stamp
%type <db_type> storage_engines known_storage_engines
@@ -1447,8 +1584,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <variable> internal_variable_name
-%type <select_lex> subselect take_first_select
- get_select_lex
+%type <select_lex> subselect
+ get_select_lex query_specification
+ query_expression_body
%type <boolfunc2creator> comp_op
@@ -1462,17 +1600,19 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
show describe load alter optimize keycache preload flush
reset purge begin commit rollback savepoint release
slave master_def master_defs master_file_def slave_until_opts
- repair restore backup analyze check start checksum
+ repair analyze check start checksum
field_list field_list_item field_spec kill column_def key_def
- keycache_list assign_to_keycache preload_list preload_keys
+ 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
procedure_list procedure_list2 procedure_item
handler
opt_precision opt_ignore opt_column opt_restrict
grant revoke set lock unlock string_list field_options field_option
- field_opt_list opt_binary table_lock_list table_lock
- ref_list opt_on_delete opt_on_delete_list opt_on_delete_item use
+ field_opt_list opt_binary ascii unicode 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
opt_option opt_place
@@ -1480,6 +1620,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
opt_column_list grant_privileges grant_ident grant_list grant_option
object_privilege object_privilege_list user_list rename_list
clear_privileges flush_options flush_option
+ opt_with_read_lock flush_options_list
equal optional_braces
opt_mi_check_type opt_to mi_check_types normal_join
table_to_table_list table_to_table opt_table_list opt_as
@@ -1493,7 +1634,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
prepare prepare_src execute deallocate
statement sp_suid
sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa
- load_data opt_field_or_var_spec fields_or_vars opt_load_data_set_spec
+ opt_field_or_var_spec fields_or_vars opt_load_data_set_spec
view_replace_or_algorithm view_replace
view_algorithm view_or_trigger_or_sp_or_event
definer_tail no_definer_tail
@@ -1505,6 +1646,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
fulltext_key_opt spatial_key_opt fulltext_key_opts spatial_key_opts
keep_gcc_happy
key_using_alg
+ part_column_list
server_def server_options_list server_option
definer_opt no_definer definer
parse_vcol_expr vcol_opt_specifier vcol_opt_attribute
@@ -1522,17 +1664,24 @@ END_OF_INPUT
%type <NONE> case_stmt_specification simple_case_stmt searched_case_stmt
%type <num> sp_decl_idents sp_opt_inout sp_handler_type sp_hcond_list
-%type <spcondtype> sp_cond sp_hcond
+%type <spcondtype> sp_cond sp_hcond sqlstate signal_value opt_signal_value
%type <spblock> sp_decls sp_decl
%type <lex> sp_cursor_stmt
%type <spname> sp_name
%type <index_hint> index_hint_type
%type <num> index_hint_clause
+%type <filetype> data_or_xml
+
+%type <NONE> signal_stmt resignal_stmt
+%type <diag_condition_item_name> signal_condition_information_item_name
%type <NONE>
'-' '+' '*' '/' '%' '(' ')'
',' '!' '{' '}' '&' '|' AND_SYM OR_SYM OR_OR_SYM BETWEEN_SYM CASE_SYM
- THEN_SYM WHEN_SYM DIV_SYM MOD_SYM OR2_SYM AND_AND_SYM
+ THEN_SYM WHEN_SYM DIV_SYM MOD_SYM OR2_SYM AND_AND_SYM DELETE_SYM
+
+%type <is_not_empty> opt_union_order_or_limit
+
%%
/*
@@ -1559,7 +1708,6 @@ rule: <-- starts at col 1
query:
END_OF_INPUT
{
- THD *thd= YYTHD;
if (!thd->bootstrap &&
(!(thd->lex->select_lex.options & OPTION_FOUND_COMMENT)))
{
@@ -1573,8 +1721,8 @@ query:
{
Lex_input_stream *lip = YYLIP;
- if ((YYTHD->client_capabilities & CLIENT_MULTI_QUERIES) &&
- ! lip->stmt_prepare_mode &&
+ if ((thd->client_capabilities & CLIENT_MULTI_QUERIES) &&
+ lip->multi_statements &&
! lip->eof())
{
/*
@@ -1615,7 +1763,6 @@ verb_clause:
statement:
alter
| analyze
- | backup
| binlog_base64_event
| call
| change
@@ -1651,12 +1798,13 @@ statement:
| repair
| replace
| reset
- | restore
+ | resignal_stmt
| revoke
| rollback
| savepoint
| select
| set
+ | signal_stmt
| show
| slave
| start
@@ -1671,7 +1819,6 @@ statement:
deallocate:
deallocate_or_drop PREPARE_SYM ident
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
lex->sql_command= SQLCOM_DEALLOCATE_PREPARE;
lex->prepared_stmt_name= $3;
@@ -1686,7 +1833,6 @@ deallocate_or_drop:
prepare:
PREPARE_SYM ident FROM prepare_src
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
lex->sql_command= SQLCOM_PREPARE;
lex->prepared_stmt_name= $2;
@@ -1696,14 +1842,12 @@ prepare:
prepare_src:
TEXT_STRING_sys
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
lex->prepared_stmt_code= $1;
lex->prepared_stmt_code_is_varref= FALSE;
}
| '@' ident_or_text
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
lex->prepared_stmt_code= $2;
lex->prepared_stmt_code_is_varref= TRUE;
@@ -1713,7 +1857,6 @@ prepare_src:
execute:
EXECUTE_SYM ident
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
lex->sql_command= SQLCOM_EXECUTE;
lex->prepared_stmt_name= $2;
@@ -1766,9 +1909,7 @@ help:
change:
CHANGE MASTER_SYM TO_SYM
{
- LEX *lex = Lex;
- lex->sql_command = SQLCOM_CHANGE_MASTER;
- bzero((char*) &lex->mi, sizeof(lex->mi));
+ Lex->sql_command = SQLCOM_CHANGE_MASTER;
}
master_defs
{}
@@ -1803,7 +1944,7 @@ master_def:
| MASTER_SSL_SYM EQ ulong_num
{
Lex->mi.ssl= $3 ?
- LEX_MASTER_INFO::SSL_ENABLE : LEX_MASTER_INFO::SSL_DISABLE;
+ LEX_MASTER_INFO::LEX_MI_ENABLE : LEX_MASTER_INFO::LEX_MI_DISABLE;
}
| MASTER_SSL_CA_SYM EQ TEXT_STRING_sys
{
@@ -1828,11 +1969,60 @@ master_def:
| MASTER_SSL_VERIFY_SERVER_CERT_SYM EQ ulong_num
{
Lex->mi.ssl_verify_server_cert= $3 ?
- LEX_MASTER_INFO::SSL_ENABLE : LEX_MASTER_INFO::SSL_DISABLE;
+ LEX_MASTER_INFO::LEX_MI_ENABLE : LEX_MASTER_INFO::LEX_MI_DISABLE;
}
- | master_file_def
+
+ | MASTER_HEARTBEAT_PERIOD_SYM EQ 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)
+ {
+ const char format[]= "%d";
+ char buf[4*sizeof(SLAVE_MAX_HEARTBEAT_PERIOD) + sizeof(format)];
+ sprintf(buf, format, SLAVE_MAX_HEARTBEAT_PERIOD);
+ my_error(ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE, MYF(0), buf);
+ MYSQL_YYABORT;
+ }
+ if (Lex->mi.heartbeat_period > slave_net_timeout)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX,
+ ER(ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX));
+ }
+ if (Lex->mi.heartbeat_period < 0.001)
+ {
+ if (Lex->mi.heartbeat_period != 0.0)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN,
+ ER(ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN));
+ Lex->mi.heartbeat_period= 0.0;
+ }
+ Lex->mi.heartbeat_opt= LEX_MASTER_INFO::LEX_MI_DISABLE;
+ }
+ Lex->mi.heartbeat_opt= LEX_MASTER_INFO::LEX_MI_ENABLE;
+ }
+ | IGNORE_SERVER_IDS_SYM EQ '(' ignore_server_id_list ')'
+ {
+ Lex->mi.repl_ignore_server_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));
+ }
+
master_file_def:
MASTER_LOG_FILE_SYM EQ TEXT_STRING_sys
{
@@ -1871,13 +2061,18 @@ master_file_def:
create:
CREATE opt_table_options TABLE_SYM opt_if_not_exists table_ident
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
lex->sql_command= SQLCOM_CREATE_TABLE;
if (!lex->select_lex.add_table_to_list(thd, $5, NULL,
TL_OPTION_UPDATING,
- TL_WRITE))
+ TL_WRITE, MDL_EXCLUSIVE))
MYSQL_YYABORT;
+ /*
+ For CREATE TABLE, an non-existing table is not an error.
+ Instruct open_tables() to just take an MDL lock if the
+ table does not exist.
+ */
+ lex->query_tables->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
lex->alter_info.reset();
lex->col_list.empty();
lex->change=NullS;
@@ -1886,21 +2081,23 @@ create:
lex->create_info.default_table_charset= NULL;
lex->name.str= 0;
lex->name.length= 0;
+ lex->create_last_non_select_table= lex->last_table();
}
create2
{
- LEX *lex= YYTHD->lex;
+ LEX *lex= thd->lex;
lex->current_select= &lex->select_lex;
if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) &&
!lex->create_info.db_type)
{
- lex->create_info.db_type= ha_default_handlerton(YYTHD);
- push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_WARN,
+ lex->create_info.db_type= ha_default_handlerton(thd);
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_USING_OTHER_HANDLER,
ER(ER_WARN_USING_OTHER_HANDLER),
hton_name(lex->create_info.db_type)->str,
$5->table.str);
}
+ create_table_set_open_action_and_adjust_tables(lex);
}
| CREATE opt_unique INDEX_SYM ident key_alg ON table_ident
{
@@ -1909,7 +2106,7 @@ create:
}
'(' key_list ')' normal_key_options
{
- if (add_create_index(Lex, $2, $4.str))
+ if (add_create_index(Lex, $2, $4))
MYSQL_YYABORT;
}
| CREATE fulltext INDEX_SYM ident init_key_options ON
@@ -1920,7 +2117,7 @@ create:
}
'(' key_list ')' fulltext_key_options
{
- if (add_create_index(Lex, $2, $4.str))
+ if (add_create_index(Lex, $2, $4))
MYSQL_YYABORT;
}
| CREATE spatial INDEX_SYM ident init_key_options ON
@@ -1931,7 +2128,7 @@ create:
}
'(' key_list ')' spatial_key_options
{
- if (add_create_index(Lex, $2, $4.str))
+ if (add_create_index(Lex, $2, $4))
MYSQL_YYABORT;
}
| CREATE DATABASE opt_if_not_exists ident
@@ -2024,7 +2221,6 @@ server_option:
event_tail:
remember_name EVENT_SYM opt_if_not_exists sp_name
{
- THD *thd= YYTHD;
LEX *lex=Lex;
lex->stmt_definition_begin= $1;
@@ -2091,7 +2287,7 @@ opt_ev_status:
ev_starts:
/* empty */
{
- Item *item= new (YYTHD->mem_root) Item_func_now_local(0);
+ Item *item= new (thd->mem_root) Item_func_now_local(0);
if (item == NULL)
MYSQL_YYABORT;
Lex->event_parse_data->item_starts= item;
@@ -2141,7 +2337,6 @@ opt_ev_comment:
ev_sql_stmt:
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
Lex_input_stream *lip= YYLIP;
@@ -2184,7 +2379,6 @@ ev_sql_stmt:
}
ev_sql_stmt_inner
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
/* return back to the original memory root ASAP */
@@ -2243,11 +2437,10 @@ sp_name:
$$= new sp_name($1, $3, true);
if ($$ == NULL)
MYSQL_YYABORT;
- $$->init_qname(YYTHD);
+ $$->init_qname(thd);
}
| ident
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
LEX_STRING db;
if (check_routine_name(&$1))
@@ -2317,7 +2510,7 @@ call:
lex->sql_command= SQLCOM_CALL;
lex->spname= $2;
lex->value_list.empty();
- sp_add_used_routine(lex, YYTHD, $2, TYPE_ENUM_PROCEDURE);
+ sp_add_used_routine(lex, thd, $2, TYPE_ENUM_PROCEDURE);
}
opt_sp_cparam_list {}
;
@@ -2377,7 +2570,7 @@ sp_init_param:
;
sp_fdparam:
- ident sp_init_param type
+ ident sp_init_param type_with_opt_collate
{
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
@@ -2391,7 +2584,7 @@ sp_fdparam:
(enum enum_field_types)$3,
sp_param_in);
- if (lex->sphead->fill_field_definition(YYTHD, lex,
+ if (lex->sphead->fill_field_definition(thd, lex,
(enum enum_field_types) $3,
&spvar->field_def))
{
@@ -2414,7 +2607,7 @@ sp_pdparams:
;
sp_pdparam:
- sp_opt_inout sp_init_param ident type
+ sp_opt_inout sp_init_param ident type_with_opt_collate
{
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
@@ -2428,7 +2621,7 @@ sp_pdparam:
(enum enum_field_types)$4,
(sp_param_mode_t)$1);
- if (lex->sphead->fill_field_definition(YYTHD, lex,
+ if (lex->sphead->fill_field_definition(thd, lex,
(enum enum_field_types) $4,
&spvar->field_def))
{
@@ -2491,13 +2684,12 @@ sp_decl:
{
LEX *lex= Lex;
- lex->sphead->reset_lex(YYTHD);
+ lex->sphead->reset_lex(thd);
lex->spcont->declare_var_boundary($2);
}
- type
+ type_with_opt_collate
sp_opt_default
{
- THD *thd= YYTHD;
LEX *lex= Lex;
sp_pcontext *pctx= lex->spcont;
uint num_vars= pctx->context_var_count();
@@ -2523,7 +2715,7 @@ sp_decl:
spvar->type= var_type;
spvar->dflt= dflt_value_item;
- if (lex->sphead->fill_field_definition(YYTHD, lex, var_type,
+ if (lex->sphead->fill_field_definition(thd, lex, var_type,
&spvar->field_def))
{
MYSQL_YYABORT;
@@ -2547,7 +2739,7 @@ sp_decl:
}
pctx->declare_var_boundary(0);
- if (lex->sphead->restore_lex(YYTHD))
+ if (lex->sphead->restore_lex(thd))
MYSQL_YYABORT;
$$.vars= $2;
$$.conds= $$.hndlrs= $$.curs= 0;
@@ -2557,12 +2749,12 @@ sp_decl:
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
- if (spc->find_cond(&$2, TRUE))
- {
- my_error(ER_SP_DUP_COND, MYF(0), $2.str);
- MYSQL_YYABORT;
- }
- if(YYTHD->lex->spcont->push_cond(&$2, $5))
+ if (spc->find_cond(&$2, TRUE))
+ {
+ my_error(ER_SP_DUP_COND, MYF(0), $2.str);
+ MYSQL_YYABORT;
+ }
+ if(thd->lex->spcont->push_cond(&$2, $5))
MYSQL_YYABORT;
$$.vars= $$.hndlrs= $$.curs= 0;
$$.conds= 1;
@@ -2577,7 +2769,7 @@ sp_decl:
sp_pcontext *ctx= lex->spcont;
sp_instr_hpush_jump *i=
new sp_instr_hpush_jump(sp->instructions(), ctx, $2,
- ctx->current_var_count());
+ ctx->current_var_count());
if (i == NULL || sp->add_instr(i))
MYSQL_YYABORT;
@@ -2602,15 +2794,15 @@ sp_decl:
i= new sp_instr_hreturn(sp->instructions(), ctx,
ctx->current_var_count());
if (i == NULL ||
- sp->add_instr(i))
+ sp->add_instr(i))
MYSQL_YYABORT;
}
else
{ /* EXIT or UNDO handler, just jump to the end of the block */
i= new sp_instr_hreturn(sp->instructions(), ctx, 0);
if (i == NULL ||
- sp->add_instr(i) ||
- sp->push_backpatch(i, lex->spcont->last_label())) /* Block end */
+ sp->add_instr(i) ||
+ sp->push_backpatch(i, lex->spcont->last_label())) /* Block end */
MYSQL_YYABORT;
}
lex->sphead->backpatch(hlab);
@@ -2637,9 +2829,9 @@ sp_decl:
}
i= new sp_instr_cpush(sp->instructions(), ctx, $5,
ctx->current_cursor_count());
- if (i == NULL ||
+ if (i == NULL ||
sp->add_instr(i) ||
- ctx->push_cursor(&$2))
+ ctx->push_cursor(&$2))
MYSQL_YYABORT;
$$.vars= $$.conds= $$.hndlrs= 0;
$$.curs= 1;
@@ -2648,7 +2840,7 @@ sp_decl:
sp_cursor_stmt:
{
- Lex->sphead->reset_lex(YYTHD);
+ Lex->sphead->reset_lex(thd);
}
select
{
@@ -2664,7 +2856,7 @@ sp_cursor_stmt:
}
lex->sp_lex_in_use= TRUE;
$$= lex;
- if (lex->sphead->restore_lex(YYTHD))
+ if (lex->sphead->restore_lex(thd))
MYSQL_YYABORT;
}
;
@@ -2708,25 +2900,34 @@ sp_hcond_element:
sp_cond:
ulong_num
{ /* mysql errno */
- $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ if ($1 == 0)
+ {
+ my_error(ER_WRONG_VALUE, MYF(0), "CONDITION", "0");
+ MYSQL_YYABORT;
+ }
+ $$= (sp_cond_type_t *) thd->alloc(sizeof(sp_cond_type_t));
if ($$ == NULL)
MYSQL_YYABORT;
$$->type= sp_cond_type_t::number;
$$->mysqlerr= $1;
}
- | SQLSTATE_SYM opt_value TEXT_STRING_literal
+ | sqlstate
+ ;
+
+sqlstate:
+ SQLSTATE_SYM opt_value TEXT_STRING_literal
{ /* SQLSTATE */
if (!sp_cond_check(&$3))
{
my_error(ER_SP_BAD_SQLSTATE, MYF(0), $3.str);
MYSQL_YYABORT;
}
- $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ $$= (sp_cond_type_t *) thd->alloc(sizeof(sp_cond_type_t));
if ($$ == NULL)
MYSQL_YYABORT;
$$->type= sp_cond_type_t::state;
- memcpy($$->sqlstate, $3.str, 5);
- $$->sqlstate[5]= '\0';
+ memcpy($$->sqlstate, $3.str, SQLSTATE_LENGTH);
+ $$->sqlstate[SQLSTATE_LENGTH]= '\0';
}
;
@@ -2751,27 +2952,179 @@ sp_hcond:
}
| SQLWARNING_SYM /* SQLSTATEs 01??? */
{
- $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ $$= (sp_cond_type_t *) thd->alloc(sizeof(sp_cond_type_t));
if ($$ == NULL)
MYSQL_YYABORT;
$$->type= sp_cond_type_t::warning;
}
| not FOUND_SYM /* SQLSTATEs 02??? */
{
- $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ $$= (sp_cond_type_t *) thd->alloc(sizeof(sp_cond_type_t));
if ($$ == NULL)
MYSQL_YYABORT;
$$->type= sp_cond_type_t::notfound;
}
| SQLEXCEPTION_SYM /* All other SQLSTATEs */
{
- $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ $$= (sp_cond_type_t *) thd->alloc(sizeof(sp_cond_type_t));
if ($$ == NULL)
MYSQL_YYABORT;
$$->type= sp_cond_type_t::exception;
}
;
+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_stmt= new (thd->mem_root) Signal_statement(lex, $2,
+ state->m_set_signal_info);
+ if (lex->m_stmt == NULL)
+ MYSQL_YYABORT;
+ }
+ ;
+
+signal_value:
+ ident
+ {
+ LEX *lex= Lex;
+ sp_cond_type_t *cond;
+ if (lex->spcont == NULL)
+ {
+ /* SIGNAL foo cannot be used outside of stored programs */
+ my_error(ER_SP_COND_MISMATCH, MYF(0), $1.str);
+ MYSQL_YYABORT;
+ }
+ cond= lex->spcont->find_cond(&$1);
+ if (cond == NULL)
+ {
+ my_error(ER_SP_COND_MISMATCH, MYF(0), $1.str);
+ MYSQL_YYABORT;
+ }
+ if (cond->type != sp_cond_type_t::state)
+ {
+ my_error(ER_SIGNAL_BAD_CONDITION_TYPE, MYF(0));
+ MYSQL_YYABORT;
+ }
+ $$= 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 EQ 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 EQ signal_allowed_expr
+ {
+ 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)
+ {
+ my_error(ER_DUP_SIGNAL_SET, MYF(0),
+ Diag_condition_item_names[index].str);
+ MYSQL_YYABORT;
+ }
+ 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 (item->functype() == Item_func::SUSERVAR_FUNC)
+ {
+ /*
+ Don't allow the following syntax:
+ SIGNAL/RESIGNAL ...
+ SET <signal condition item name> = @foo := expr
+ */
+ my_parse_error(ER(ER_SYNTAX_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
+ {
+ LEX *lex= thd->lex;
+ Yacc_state *state= & thd->m_parser_state->m_yacc;
+
+ lex->sql_command= SQLCOM_RESIGNAL;
+ lex->m_stmt= new (thd->mem_root) Resignal_statement(lex, $2,
+ state->m_set_signal_info);
+ if (lex->m_stmt == NULL)
+ MYSQL_YYABORT;
+ }
+ ;
+
sp_decl_idents:
ident
{
@@ -2835,7 +3188,6 @@ sp_proc_stmt_if:
sp_proc_stmt_statement:
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
Lex_input_stream *lip= YYLIP;
@@ -2844,7 +3196,6 @@ sp_proc_stmt_statement:
}
statement
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
Lex_input_stream *lip= YYLIP;
sp_head *sp= lex->sphead;
@@ -2891,7 +3242,7 @@ sp_proc_stmt_statement:
sp_proc_stmt_return:
RETURN_SYM
- { Lex->sphead->reset_lex(YYTHD); }
+ { Lex->sphead->reset_lex(thd); }
expr
{
LEX *lex= Lex;
@@ -2909,11 +3260,11 @@ sp_proc_stmt_return:
i= new sp_instr_freturn(sp->instructions(), lex->spcont, $3,
sp->m_return_field_def.sql_type, lex);
if (i == NULL ||
- sp->add_instr(i))
+ sp->add_instr(i))
MYSQL_YYABORT;
sp->m_flags|= sp_head::HAS_RETURN;
}
- if (sp->restore_lex(YYTHD))
+ if (sp->restore_lex(thd))
MYSQL_YYABORT;
}
;
@@ -3140,7 +3491,7 @@ sp_fetch_list:
;
sp_if:
- { Lex->sphead->reset_lex(YYTHD); }
+ { Lex->sphead->reset_lex(thd); }
expr THEN_SYM
{
LEX *lex= Lex;
@@ -3150,11 +3501,11 @@ sp_if:
sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, ctx,
$2, lex);
if (i == NULL ||
- sp->push_backpatch(i, ctx->push_label((char *)"", 0)) ||
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0)) ||
sp->add_cont_backpatch(i) ||
sp->add_instr(i))
MYSQL_YYABORT;
- if (sp->restore_lex(YYTHD))
+ if (sp->restore_lex(thd))
MYSQL_YYABORT;
}
sp_proc_stmts1
@@ -3193,7 +3544,7 @@ simple_case_stmt:
{
LEX *lex= Lex;
case_stmt_action_case(lex);
- lex->sphead->reset_lex(YYTHD); /* For expr $3 */
+ lex->sphead->reset_lex(thd); /* For expr $3 */
}
expr
{
@@ -3202,7 +3553,7 @@ simple_case_stmt:
MYSQL_YYABORT;
/* For expr $3 */
- if (lex->sphead->restore_lex(YYTHD))
+ if (lex->sphead->restore_lex(thd))
MYSQL_YYABORT;
}
simple_when_clause_list
@@ -3244,7 +3595,7 @@ searched_when_clause_list:
simple_when_clause:
WHEN_SYM
{
- Lex->sphead->reset_lex(YYTHD); /* For expr $3 */
+ Lex->sphead->reset_lex(thd); /* For expr $3 */
}
expr
{
@@ -3254,7 +3605,7 @@ simple_when_clause:
if (case_stmt_action_when(lex, $3, true))
MYSQL_YYABORT;
/* For expr $3 */
- if (lex->sphead->restore_lex(YYTHD))
+ if (lex->sphead->restore_lex(thd))
MYSQL_YYABORT;
}
THEN_SYM
@@ -3269,7 +3620,7 @@ simple_when_clause:
searched_when_clause:
WHEN_SYM
{
- Lex->sphead->reset_lex(YYTHD); /* For expr $3 */
+ Lex->sphead->reset_lex(thd); /* For expr $3 */
}
expr
{
@@ -3277,7 +3628,7 @@ searched_when_clause:
if (case_stmt_action_when(lex, $3, false))
MYSQL_YYABORT;
/* For expr $3 */
- if (lex->sphead->restore_lex(YYTHD))
+ if (lex->sphead->restore_lex(thd))
MYSQL_YYABORT;
}
THEN_SYM
@@ -3439,9 +3790,9 @@ sp_unlabeled_control:
if (i == NULL ||
lex->sphead->add_instr(i))
MYSQL_YYABORT;
- }
+ }
| WHILE_SYM
- { Lex->sphead->reset_lex(YYTHD); }
+ { Lex->sphead->reset_lex(thd); }
expr DO_SYM
{
LEX *lex= Lex;
@@ -3450,12 +3801,12 @@ sp_unlabeled_control:
sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont,
$3, lex);
if (i == NULL ||
- /* Jumping forward */
+ /* Jumping forward */
sp->push_backpatch(i, lex->spcont->last_label()) ||
sp->new_cont_backpatch(i) ||
sp->add_instr(i))
MYSQL_YYABORT;
- if (sp->restore_lex(YYTHD))
+ if (sp->restore_lex(thd))
MYSQL_YYABORT;
}
sp_proc_stmts1 END WHILE_SYM
@@ -3470,7 +3821,7 @@ sp_unlabeled_control:
lex->sphead->do_cont_backpatch();
}
| REPEAT_SYM sp_proc_stmts1 UNTIL_SYM
- { Lex->sphead->reset_lex(YYTHD); }
+ { Lex->sphead->reset_lex(thd); }
expr END REPEAT_SYM
{
LEX *lex= Lex;
@@ -3482,7 +3833,7 @@ sp_unlabeled_control:
if (i == NULL ||
lex->sphead->add_instr(i))
MYSQL_YYABORT;
- if (lex->sphead->restore_lex(YYTHD))
+ if (lex->sphead->restore_lex(thd))
MYSQL_YYABORT;
/* We can shortcut the cont_backpatch here */
i->m_cont_dest= ip+1;
@@ -3847,8 +4198,8 @@ ts_wait:
;
size_number:
- real_ulong_num { $$= $1;}
- | IDENT
+ real_ulonglong_num { $$= $1;}
+ | IDENT_sys
{
ulonglong number;
uint text_shift_number= 0;
@@ -3901,17 +4252,17 @@ size_number:
create2:
'(' create2a {}
| opt_create_table_options
- opt_partitioning
+ opt_create_partitioning
create3 {}
| LIKE table_ident
{
- THD *thd= YYTHD;
TABLE_LIST *src_table;
LEX *lex= thd->lex;
lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE;
src_table= lex->select_lex.add_table_to_list(thd, $2, NULL, 0,
- TL_READ);
+ TL_READ,
+ MDL_SHARED_READ);
if (! src_table)
MYSQL_YYABORT;
/* CREATE TABLE ... LIKE is not allowed for views. */
@@ -3919,13 +4270,13 @@ create2:
}
| '(' LIKE table_ident ')'
{
- THD *thd= YYTHD;
TABLE_LIST *src_table;
LEX *lex= thd->lex;
lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE;
src_table= lex->select_lex.add_table_to_list(thd, $3, NULL, 0,
- TL_READ);
+ TL_READ,
+ MDL_SHARED_READ);
if (! src_table)
MYSQL_YYABORT;
/* CREATE TABLE ... LIKE is not allowed for views. */
@@ -3934,36 +4285,40 @@ create2:
;
create2a:
- field_list ')'
+ create_field_list ')'
{
Lex->create_info.option_list= NULL;
}
opt_create_table_options
- opt_partitioning
+ opt_create_partitioning
create3 {}
- | opt_partitioning
+ | opt_create_partitioning
create_select ')'
- {
- Select->set_braces(1);
- Lex->create_select_start_with_brace= TRUE;
- }
+ { Select->set_braces(1);}
union_opt {}
;
create3:
/* empty */ {}
| opt_duplicate opt_as create_select
- {
- Select->set_braces(0);
- Lex->create_select_start_with_brace= FALSE;
- }
+ { Select->set_braces(0);}
union_clause {}
| opt_duplicate opt_as '(' create_select ')'
+ { Select->set_braces(1);}
+ union_opt {}
+ ;
+
+opt_create_partitioning:
+ opt_partitioning
{
- Select->set_braces(1);
- Lex->create_select_start_with_brace= TRUE;
+ /*
+ 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;
}
- union_opt {}
;
/*
@@ -3996,17 +4351,9 @@ opt_partitioning:
;
partitioning:
- PARTITION_SYM
+ PARTITION_SYM have_partitioning
{
-#ifdef WITH_PARTITION_STORAGE_ENGINE
LEX *lex= Lex;
- LEX_STRING partition_name={C_STRING_WITH_LEN("partition")};
- if (!plugin_is_ready(&partition_name, MYSQL_STORAGE_ENGINE_PLUGIN))
- {
- my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
- "--skip-partition");
- MYSQL_YYABORT;
- }
lex->part_info= new partition_info();
if (!lex->part_info)
{
@@ -4017,14 +4364,27 @@ partitioning:
{
lex->alter_info.flags|= ALTER_PARTITION;
}
+ }
+ partition
+ ;
+
+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))
+ {
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
+ "--skip-partition");
+ MYSQL_YYABORT;
+ }
#else
- my_error(ER_FEATURE_DISABLED, MYF(0),
- "partitioning", "--with-partition");
+ my_error(ER_FEATURE_DISABLED, MYF(0), "partitioning",
+ "--with-plugin-partition");
MYSQL_YYABORT;
#endif
-
}
- partition
;
partition_entry:
@@ -4045,25 +4405,28 @@ partition_entry:
;
partition:
- BY part_type_def opt_no_parts opt_sub_part part_defs
+ BY part_type_def opt_num_parts opt_sub_part part_defs
;
part_type_def:
- opt_linear KEY_SYM '(' part_field_list ')'
+ opt_linear KEY_SYM opt_key_algo '(' part_field_list ')'
{
- LEX *lex= Lex;
- lex->part_info->list_of_part_fields= TRUE;
- lex->part_info->part_type= HASH_PARTITION;
+ 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
+ | RANGE_SYM part_func
{ Lex->part_info->part_type= RANGE_PARTITION; }
- part_func {}
- | LIST_SYM
+ | 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 part_column_list
{ Lex->part_info->part_type= LIST_PARTITION; }
- part_func {}
;
opt_linear:
@@ -4072,6 +4435,25 @@ opt_linear:
{ Lex->part_info->linear_hash_ind= TRUE;}
;
+opt_key_algo:
+ /* empty */
+ { Lex->part_info->key_algorithm= partition_info::KEY_ALGORITHM_NONE;}
+ | ALGORITHM_SYM EQ 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:
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ }
+ ;
+
part_field_list:
/* empty */ {}
| part_field_item_list {}
@@ -4085,59 +4467,66 @@ part_field_item_list:
part_field_item:
ident
{
- if (Lex->part_info->part_field_list.push_back($1.str))
+ partition_info *part_info= Lex->part_info;
+ part_info->num_columns++;
+ if (part_info->part_field_list.push_back($1.str))
{
mem_alloc_error(1);
MYSQL_YYABORT;
}
+ if (part_info->num_columns > MAX_REF_PARTS)
+ {
+ my_error(ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR, MYF(0),
+ "list of partition fields");
+ MYSQL_YYABORT;
+ }
+ }
+ ;
+
+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:
'(' remember_name part_func_expr remember_end ')'
{
- LEX *lex= Lex;
- uint expr_len= (uint)($4 - $2) - 1;
- lex->part_info->list_of_part_fields= FALSE;
- lex->part_info->part_expr= $3;
- char *func_string= (char*) sql_memdup($2+1, expr_len);
- if (func_string == NULL)
- MYSQL_YYABORT;
- lex->part_info->part_func_string= func_string;
- lex->part_info->part_func_len= expr_len;
+ partition_info *part_info= Lex->part_info;
+ if (part_info->set_part_expr($2+1, $3, $4, FALSE))
+ { MYSQL_YYABORT; }
+ part_info->num_columns= 1;
+ part_info->column_list= FALSE;
}
;
sub_part_func:
'(' remember_name part_func_expr remember_end ')'
{
- LEX *lex= Lex;
- uint expr_len= (uint)($4 - $2) - 1;
- lex->part_info->list_of_subpart_fields= FALSE;
- lex->part_info->subpart_expr= $3;
- char *func_string= (char*) sql_memdup($2+1, expr_len);
- if (func_string == NULL)
- MYSQL_YYABORT;
- lex->part_info->subpart_func_string= func_string;
- lex->part_info->subpart_func_len= expr_len;
+ if (Lex->part_info->set_part_expr($2+1, $3, $4, TRUE))
+ { MYSQL_YYABORT; }
}
;
-opt_no_parts:
+opt_num_parts:
/* empty */ {}
| PARTITIONS_SYM real_ulong_num
{
- uint no_parts= $2;
- LEX *lex= Lex;
- if (no_parts == 0)
+ uint num_parts= $2;
+ partition_info *part_info= Lex->part_info;
+ if (num_parts == 0)
{
my_error(ER_NO_PARTS_ERROR, MYF(0), "partitions");
MYSQL_YYABORT;
}
- lex->part_info->no_parts= no_parts;
- lex->part_info->use_default_no_partitions= FALSE;
+ part_info->num_parts= num_parts;
+ part_info->use_default_num_partitions= FALSE;
}
;
@@ -4145,15 +4534,15 @@ opt_sub_part:
/* empty */ {}
| SUBPARTITION_SYM BY opt_linear HASH_SYM sub_part_func
{ Lex->part_info->subpart_type= HASH_PARTITION; }
- opt_no_subparts {}
- | SUBPARTITION_SYM BY opt_linear KEY_SYM
+ opt_num_subparts {}
+ | SUBPARTITION_SYM BY opt_linear KEY_SYM opt_key_algo
'(' sub_part_field_list ')'
{
- LEX *lex= Lex;
- lex->part_info->subpart_type= HASH_PARTITION;
- lex->part_info->list_of_subpart_fields= TRUE;
+ partition_info *part_info= Lex->part_info;
+ part_info->subpart_type= HASH_PARTITION;
+ part_info->list_of_subpart_fields= TRUE;
}
- opt_no_subparts {}
+ opt_num_subparts {}
;
sub_part_field_list:
@@ -4164,11 +4553,18 @@ sub_part_field_list:
sub_part_field_item:
ident
{
- if (Lex->part_info->subpart_field_list.push_back($1.str))
+ partition_info *part_info= Lex->part_info;
+ if (part_info->subpart_field_list.push_back($1.str))
{
mem_alloc_error(1);
MYSQL_YYABORT;
}
+ if (part_info->subpart_field_list.elements > MAX_REF_PARTS)
+ {
+ my_error(ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR, MYF(0),
+ "list of subpartition fields");
+ MYSQL_YYABORT;
+ }
}
;
@@ -4188,33 +4584,46 @@ part_func_expr:
}
;
-opt_no_subparts:
+opt_num_subparts:
/* empty */ {}
| SUBPARTITIONS_SYM real_ulong_num
{
- uint no_parts= $2;
+ uint num_parts= $2;
LEX *lex= Lex;
- if (no_parts == 0)
+ if (num_parts == 0)
{
my_error(ER_NO_PARTS_ERROR, MYF(0), "subpartitions");
MYSQL_YYABORT;
}
- lex->part_info->no_subparts= no_parts;
- lex->part_info->use_default_no_subpartitions= FALSE;
+ 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 (part_info->part_type == RANGE_PARTITION)
+ {
+ my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0),
+ "RANGE");
+ MYSQL_YYABORT;
+ }
+ else if (part_info->part_type == LIST_PARTITION)
+ {
+ my_error(ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0),
+ "LIST");
+ MYSQL_YYABORT;
+ }
+ }
| '(' part_def_list ')'
{
- LEX *lex= Lex;
- partition_info *part_info= lex->part_info;
+ partition_info *part_info= Lex->part_info;
uint count_curr_parts= part_info->partitions.elements;
- if (part_info->no_parts != 0)
+ if (part_info->num_parts != 0)
{
- if (part_info->no_parts !=
+ if (part_info->num_parts !=
count_curr_parts)
{
my_parse_error(ER(ER_PARTITION_WRONG_NO_PART_ERROR));
@@ -4223,7 +4632,7 @@ part_defs:
}
else if (count_curr_parts > 0)
{
- part_info->no_parts= count_curr_parts;
+ part_info->num_parts= count_curr_parts;
}
part_info->count_curr_subparts= 0;
}
@@ -4237,8 +4646,7 @@ part_def_list:
part_definition:
PARTITION_SYM
{
- LEX *lex= Lex;
- partition_info *part_info= lex->part_info;
+ partition_info *part_info= Lex->part_info;
partition_element *p_elem= new partition_element();
if (!p_elem || part_info->partitions.push_back(p_elem))
@@ -4250,7 +4658,7 @@ part_definition:
part_info->curr_part_elem= p_elem;
part_info->current_partition= p_elem;
part_info->use_default_partitions= FALSE;
- part_info->use_default_no_partitions= FALSE;
+ part_info->use_default_num_partitions= FALSE;
}
part_name
opt_part_values
@@ -4262,8 +4670,7 @@ part_definition:
part_name:
ident
{
- LEX *lex= Lex;
- partition_info *part_info= lex->part_info;
+ partition_info *part_info= Lex->part_info;
partition_element *p_elem= part_info->curr_part_elem;
p_elem->partition_name= $1.str;
}
@@ -4273,15 +4680,16 @@ opt_part_values:
/* empty */
{
LEX *lex= Lex;
+ partition_info *part_info= lex->part_info;
if (! lex->is_partition_management())
{
- if (lex->part_info->part_type == RANGE_PARTITION)
+ if (part_info->part_type == RANGE_PARTITION)
{
my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0),
"RANGE", "LESS THAN");
MYSQL_YYABORT;
}
- if (lex->part_info->part_type == LIST_PARTITION)
+ if (part_info->part_type == LIST_PARTITION)
{
my_error(ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0),
"LIST", "IN");
@@ -4289,14 +4697,15 @@ opt_part_values:
}
}
else
- lex->part_info->part_type= HASH_PARTITION;
+ part_info->part_type= HASH_PARTITION;
}
- | VALUES LESS_SYM THAN_SYM part_func_max
+ | VALUES LESS_SYM THAN_SYM
{
LEX *lex= Lex;
+ partition_info *part_info= lex->part_info;
if (! lex->is_partition_management())
{
- if (Lex->part_info->part_type != RANGE_PARTITION)
+ if (part_info->part_type != RANGE_PARTITION)
{
my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0),
"RANGE", "LESS THAN");
@@ -4304,153 +4713,181 @@ opt_part_values:
}
}
else
- lex->part_info->part_type= RANGE_PARTITION;
+ part_info->part_type= RANGE_PARTITION;
}
- | VALUES IN_SYM '(' part_list_func ')'
+ part_func_max {}
+ | VALUES IN_SYM
{
LEX *lex= Lex;
+ partition_info *part_info= lex->part_info;
if (! lex->is_partition_management())
{
- if (Lex->part_info->part_type != LIST_PARTITION)
+ if (part_info->part_type != LIST_PARTITION)
{
my_error(ER_PARTITION_WRONG_VALUES_ERROR, MYF(0),
- "LIST", "IN");
+ "LIST", "IN");
MYSQL_YYABORT;
}
}
else
- lex->part_info->part_type= LIST_PARTITION;
+ part_info->part_type= LIST_PARTITION;
}
+ part_values_in {}
;
part_func_max:
- max_value_sym
+ MAX_VALUE_SYM
{
- LEX *lex= Lex;
- if (lex->part_info->defined_max_value)
+ partition_info *part_info= Lex->part_info;
+
+ if (part_info->num_columns &&
+ part_info->num_columns != 1U)
{
- my_parse_error(ER(ER_PARTITION_MAXVALUE_ERROR));
+ part_info->print_debug("Kilroy II", NULL);
+ my_parse_error(ER(ER_PARTITION_COLUMN_LIST_ERROR));
MYSQL_YYABORT;
}
- lex->part_info->defined_max_value= TRUE;
- lex->part_info->curr_part_elem->max_value= TRUE;
- lex->part_info->curr_part_elem->range_value= LONGLONG_MAX;
- }
- | part_range_func
- {
- if (Lex->part_info->defined_max_value)
+ else
+ part_info->num_columns= 1U;
+ if (part_info->init_column_part())
{
- my_parse_error(ER(ER_PARTITION_MAXVALUE_ERROR));
MYSQL_YYABORT;
}
- if (Lex->part_info->curr_part_elem->has_null_value)
+ if (part_info->add_max_value())
{
- my_parse_error(ER(ER_NULL_IN_VALUES_LESS_THAN));
MYSQL_YYABORT;
}
}
+ | part_value_item {}
;
-max_value_sym:
- MAX_VALUE_SYM
- | '(' MAX_VALUE_SYM ')'
- ;
-
-part_range_func:
- '(' part_bit_expr ')'
+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 (!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(ER(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 (part_info->reorganize_into_single_field_col_val())
+ {
+ MYSQL_YYABORT;
+ }
+ }
+ }
+ | '(' part_value_list ')'
{
partition_info *part_info= Lex->part_info;
- if (!($2->unsigned_flag))
- part_info->curr_part_elem->signed_flag= TRUE;
- part_info->curr_part_elem->range_value= $2->value;
+ if (part_info->num_columns < 2U)
+ {
+ my_parse_error(ER(ER_ROW_SINGLE_PARTITION_FIELD_ERROR));
+ MYSQL_YYABORT;
+ }
}
;
-part_list_func:
- part_list_item {}
- | part_list_func ',' part_list_item {}
+part_value_list:
+ part_value_item {}
+ | part_value_list ',' part_value_item {}
;
-part_list_item:
- part_bit_expr
+part_value_item:
+ '('
{
- part_elem_value *value_ptr= $1;
partition_info *part_info= Lex->part_info;
- if (!value_ptr->unsigned_flag)
- part_info->curr_part_elem->signed_flag= TRUE;
- if (!value_ptr->null_value &&
- part_info->curr_part_elem->
- list_val_list.push_back(value_ptr))
+ 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())
{
- mem_alloc_error(sizeof(part_elem_value));
MYSQL_YYABORT;
}
}
- ;
-
-part_bit_expr:
- bit_expr
+ part_value_item_list {}
+ ')'
{
- Item *part_expr= $1;
- THD *thd= YYTHD;
- LEX *lex= thd->lex;
- Name_resolution_context *context= &lex->current_select->context;
- TABLE_LIST *save_list= context->table_list;
- const char *save_where= thd->where;
-
- context->table_list= 0;
- thd->where= "partition function";
-
- part_elem_value *value_ptr=
- (part_elem_value*)sql_alloc(sizeof(part_elem_value));
- if (!value_ptr)
+ 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 (part_info->num_columns != part_info->curr_list_object)
{
- mem_alloc_error(sizeof(part_elem_value));
+ /*
+ 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);
+ my_parse_error(ER(ER_PARTITION_COLUMN_LIST_ERROR));
MYSQL_YYABORT;
}
- if (part_expr->walk(&Item::check_partition_func_processor, 0,
- NULL))
+ 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:
+ MAX_VALUE_SYM
+ {
+ partition_info *part_info= Lex->part_info;
+ if (part_info->part_type == LIST_PARTITION)
{
- my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
+ my_parse_error(ER(ER_MAXVALUE_IN_VALUES_IN));
MYSQL_YYABORT;
}
- if (part_expr->fix_fields(YYTHD, (Item**)0) ||
- ((context->table_list= save_list), FALSE) ||
- (!part_expr->const_item()) ||
- (!lex->safe_to_cache_query))
+ if (part_info->add_max_value())
{
- my_error(ER_NO_CONST_EXPR_IN_RANGE_OR_LIST_ERROR, MYF(0));
MYSQL_YYABORT;
}
- thd->where= save_where;
- value_ptr->value= part_expr->val_int();
- value_ptr->unsigned_flag= TRUE;
- if (!part_expr->unsigned_flag &&
- value_ptr->value < 0)
- value_ptr->unsigned_flag= FALSE;
- if ((value_ptr->null_value= part_expr->null_value))
+ }
+ | bit_expr
+ {
+ LEX *lex= Lex;
+ partition_info *part_info= lex->part_info;
+ Item *part_expr= $1;
+
+ if (!lex->safe_to_cache_query)
{
- if (Lex->part_info->curr_part_elem->has_null_value)
- {
- my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0));
- MYSQL_YYABORT;
- }
- Lex->part_info->curr_part_elem->has_null_value= TRUE;
+ my_parse_error(ER(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR));
+ MYSQL_YYABORT;
}
- else if (part_expr->result_type() != INT_RESULT)
+ if (part_info->add_column_list_value(thd, part_expr))
{
- my_parse_error(ER(ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR));
MYSQL_YYABORT;
}
- $$= value_ptr;
}
;
+
opt_sub_partition:
/* empty */
{
- if (Lex->part_info->no_subparts != 0 &&
- !Lex->part_info->use_default_subpartitions)
+ partition_info *part_info= Lex->part_info;
+ if (part_info->num_subparts != 0 &&
+ !part_info->use_default_subpartitions)
{
/*
We come here when we have defined subpartitions on the first
@@ -4462,11 +4899,10 @@ opt_sub_partition:
}
| '(' sub_part_list ')'
{
- LEX *lex= Lex;
- partition_info *part_info= lex->part_info;
- if (part_info->no_subparts != 0)
+ partition_info *part_info= Lex->part_info;
+ if (part_info->num_subparts != 0)
{
- if (part_info->no_subparts !=
+ if (part_info->num_subparts !=
part_info->count_curr_subparts)
{
my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR));
@@ -4480,7 +4916,7 @@ opt_sub_partition:
my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR));
MYSQL_YYABORT;
}
- part_info->no_subparts= part_info->count_curr_subparts;
+ part_info->num_subparts= part_info->count_curr_subparts;
}
part_info->count_curr_subparts= 0;
}
@@ -4494,8 +4930,7 @@ sub_part_list:
sub_part_definition:
SUBPARTITION_SYM
{
- LEX *lex= Lex;
- partition_info *part_info= lex->part_info;
+ partition_info *part_info= Lex->part_info;
partition_element *curr_part= part_info->current_partition;
partition_element *sub_p_elem= new partition_element(curr_part);
if (part_info->use_default_subpartitions &&
@@ -4523,7 +4958,7 @@ sub_part_definition:
}
part_info->curr_part_elem= sub_p_elem;
part_info->use_default_subpartitions= FALSE;
- part_info->use_default_no_subpartitions= FALSE;
+ part_info->use_default_num_subpartitions= FALSE;
part_info->count_curr_subparts++;
}
sub_name opt_part_options {}
@@ -4549,9 +4984,9 @@ opt_part_option:
{ Lex->part_info->curr_part_elem->tablespace_name= $3.str; }
| opt_storage ENGINE_SYM opt_equal storage_engines
{
- LEX *lex= Lex;
- lex->part_info->curr_part_elem->engine_type= $4;
- lex->part_info->default_engine_type= $4;
+ 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
{
@@ -4581,7 +5016,6 @@ create_select:
SELECT_SYM
{
LEX *lex=Lex;
- lex->lock_option= TL_READ_DEFAULT;
if (lex->sql_command == SQLCOM_INSERT)
lex->sql_command= SQLCOM_INSERT_SELECT;
else if (lex->sql_command == SQLCOM_REPLACE)
@@ -4593,19 +5027,6 @@ create_select:
lex->current_select->table_list.save_and_clear(&lex->save_list);
mysql_init_select(lex);
lex->current_select->parsing_place= SELECT_LIST;
-
- if (lex->sql_command == SQLCOM_CREATE_TABLE &&
- (lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS))
- {
- Lex_input_stream *lip= YYLIP;
-
- if (lex->spcont)
- lex->create_select_pos= lip->get_tok_start() -
- lex->sphead->m_tmp_query;
- else
- lex->create_select_pos= lip->get_tok_start() - lip->get_buf();
- lex->create_select_in_comment= (lip->in_comment == DISCARD_COMMENT);
- }
}
select_options select_item_list
{
@@ -4682,13 +5103,6 @@ create_table_option:
Lex->create_info.db_type= $3;
Lex->create_info.used_fields|= HA_CREATE_USED_ENGINE;
}
- | TYPE_SYM opt_equal storage_engines
- {
- Lex->create_info.db_type= $3;
- WARN_DEPRECATED(yythd, "6.0", "TYPE=storage_engine",
- "'ENGINE=storage_engine'");
- Lex->create_info.used_fields|= HA_CREATE_USED_ENGINE;
- }
| MAX_ROWS opt_equal ulonglong_num
{
Lex->create_info.max_rows= $3;
@@ -4765,17 +5179,30 @@ create_table_option:
Lex->create_info.row_type= $3;
Lex->create_info.used_fields|= HA_CREATE_USED_ROW_FORMAT;
}
- | UNION_SYM opt_equal '(' opt_table_list ')'
+ | UNION_SYM opt_equal
{
- /* Move the union list to the merge_list */
+ 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;
- TABLE_LIST *table_list= lex->select_lex.get_table_list();
lex->create_info.merge_list= lex->select_lex.table_list;
- lex->create_info.merge_list.elements--;
- lex->create_info.merge_list.first= table_list->next_local;
- lex->select_lex.table_list.elements=1;
- lex->select_lex.table_list.next= &(table_list->next_local);
- table_list->next_local= 0;
+ 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.first);
+ 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
@@ -4819,25 +5246,25 @@ create_table_option:
}
| IDENT_sys equal TEXT_STRING_sys
{
- new (YYTHD->mem_root)
+ new (thd->mem_root)
engine_option_value($1, $3, true, &Lex->create_info.option_list,
&Lex->option_list_last);
}
| IDENT_sys equal ident
{
- new (YYTHD->mem_root)
+ 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 (YYTHD->mem_root)
+ new (thd->mem_root)
engine_option_value($1, $3, &Lex->create_info.option_list,
- &Lex->option_list_last, YYTHD->mem_root);
+ &Lex->option_list_last, thd->mem_root);
}
| IDENT_sys equal DEFAULT
{
- new (YYTHD->mem_root)
+ new (thd->mem_root)
engine_option_value($1, &Lex->create_info.option_list,
&Lex->option_list_last);
}
@@ -4867,33 +5294,33 @@ default_collation:
HA_CREATE_INFO *cinfo= &Lex->create_info;
if ((cinfo->used_fields & HA_CREATE_USED_DEFAULT_CHARSET) &&
cinfo->default_table_charset && $4 &&
- !my_charset_same(cinfo->default_table_charset,$4))
- {
- my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0),
- $4->name, cinfo->default_table_charset->csname);
- MYSQL_YYABORT;
- }
- Lex->create_info.default_table_charset= $4;
- Lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET;
+ !($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
{
- plugin_ref plugin= ha_resolve_by_name(YYTHD, &$1);
+ plugin_ref plugin= ha_resolve_by_name(thd, &$1);
if (plugin)
$$= plugin_data(plugin, handlerton*);
else
{
- if (YYTHD->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION)
+ if (thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION)
{
my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str);
MYSQL_YYABORT;
}
$$= 0;
- push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_UNKNOWN_STORAGE_ENGINE,
ER(ER_UNKNOWN_STORAGE_ENGINE),
$1.str);
@@ -4905,7 +5332,7 @@ known_storage_engines:
ident_or_text
{
plugin_ref plugin;
- if ((plugin= ha_resolve_by_name(YYTHD, &$1)))
+ if ((plugin= ha_resolve_by_name(thd, &$1)))
$$= plugin_data(plugin, handlerton*);
else
{
@@ -4943,6 +5370,14 @@ udf_type:
| 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
@@ -4990,15 +5425,13 @@ key_def:
{ Lex->option_list= NULL; }
normal_key_options
{
- if (add_create_index (Lex, $2, $3 ? $3 : $1))
+ if (add_create_index (Lex, $2, $3.str ? $3 : $1))
MYSQL_YYABORT;
}
| opt_constraint FOREIGN KEY_SYM opt_ident '(' key_list ')' references
{
LEX *lex=Lex;
- const char *key_name= $1 ? $1 : $4;
- const char *fkey_name = $4 ? $4 : key_name;
- Key *key= new Foreign_key(fkey_name, lex->col_list,
+ Key *key= new Foreign_key($4.str ? $4 : $1, lex->col_list,
$8,
lex->ref_list,
lex->fk_delete_opt,
@@ -5008,16 +5441,12 @@ key_def:
MYSQL_YYABORT;
lex->alter_info.key_list.push_back(key);
lex->option_list= NULL;
- if (add_create_index (lex, Key::MULTIPLE, key_name,
+ if (add_create_index (lex, Key::MULTIPLE, $1.str ? $1 : $4,
&default_key_create_info, 1))
MYSQL_YYABORT;
/* Only used for ALTER TABLE. Ignored otherwise. */
lex->alter_info.flags|= ALTER_FOREIGN_KEY;
}
- | constraint opt_check_constraint
- {
- Lex->col_list.empty(); /* Alloced by sql_alloc */
- }
| opt_constraint check_constraint
{
Lex->col_list.empty(); /* Alloced by sql_alloc */
@@ -5030,11 +5459,11 @@ opt_check_constraint:
;
check_constraint:
- CHECK_SYM expr
+ CHECK_SYM '(' expr ')'
;
opt_constraint:
- /* empty */ { $$=(char*) 0; }
+ /* empty */ { $$= null_lex_str; }
| constraint { $$= $1; }
;
@@ -5057,11 +5486,11 @@ field_spec:
field_def
{
LEX *lex=Lex;
- if (add_field_to_list(lex->thd, &$1, (enum enum_field_types) $3,
- lex->length,lex->dec,lex->type,
+ if (add_field_to_list(lex->thd, &$1, $3.type,
+ $3.length, $3.dec, lex->type,
lex->default_value, lex->on_update_value,
&lex->comment,
- lex->change,&lex->interval_list,lex->charset,
+ lex->change, &lex->interval_list, $3.charset,
lex->uint_geom_type,
lex->vcol_info, lex->option_list))
MYSQL_YYABORT;
@@ -5069,13 +5498,15 @@ field_spec:
;
field_def:
- type opt_attribute {}
- | type opt_generated_always AS '(' virtual_column_func ')'
- vcol_opt_specifier
- vcol_opt_attribute
+ type opt_attribute
+ { $$.set($1, Lex->length, Lex->dec, Lex->charset); }
+ | type opt_generated_always AS
+ { $<lex_type>$.set($1, Lex->length, Lex->dec, Lex->charset); }
+ '(' virtual_column_func ')' vcol_opt_specifier vcol_opt_attribute
{
- $$= (enum enum_field_types)MYSQL_TYPE_VIRTUAL;
- Lex->vcol_info->set_field_type((enum enum_field_types) $1);
+ $$= $<lex_type>4;
+ Lex->vcol_info->set_field_type($$.type);
+ $$.type= (enum enum_field_types)MYSQL_TYPE_VIRTUAL;
}
;
@@ -5235,7 +5666,7 @@ type:
{
char buff[sizeof("YEAR()") + MY_INT64_NUM_DECIMAL_DIGITS + 1];
my_snprintf(buff, sizeof(buff), "YEAR(%lu)", length);
- push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_WARN_DEPRECATED_SYNTAX,
ER(ER_WARN_DEPRECATED_SYNTAX),
buff, "YEAR(4)");
@@ -5249,7 +5680,7 @@ type:
{ $$=MYSQL_TYPE_TIME; }
| TIMESTAMP opt_field_length
{
- if (YYTHD->variables.sql_mode & MODE_MAXDB)
+ if (thd->variables.sql_mode & MODE_MAXDB)
$$=MYSQL_TYPE_DATETIME;
else
{
@@ -5338,7 +5769,8 @@ spatial_type:
| GEOMETRYCOLLECTION { $$= Field::GEOM_GEOMETRYCOLLECTION; }
| POINT_SYM
{
- Lex->length= (char*)"25";
+ Lex->length= const_cast<char*>(STRINGIFY_ARG
+ (MAX_LEN_GEOM_POINT_FIELD));
$$= Field::GEOM_POINT;
}
| MULTIPOINT { $$= Field::GEOM_MULTIPOINT; }
@@ -5381,7 +5813,7 @@ int_type:
real_type:
REAL
{
- $$= YYTHD->variables.sql_mode & MODE_REAL_AS_FLOAT ?
+ $$= thd->variables.sql_mode & MODE_REAL_AS_FLOAT ?
MYSQL_TYPE_FLOAT : MYSQL_TYPE_DOUBLE;
}
| DOUBLE_SYM
@@ -5433,6 +5865,7 @@ field_length:
opt_field_length:
/* empty */ { Lex->length=(char*) 0; /* use default length */ }
| field_length { }
+ ;
opt_precision:
/* empty */ {}
@@ -5455,7 +5888,7 @@ attribute:
| DEFAULT now_or_signed_literal { Lex->default_value=$2; }
| ON UPDATE_SYM NOW_SYM optional_braces
{
- Item *item= new (YYTHD->mem_root) Item_func_now_local(6);
+ Item *item= new (thd->mem_root) Item_func_now_local(6);
if (item == NULL)
MYSQL_YYABORT;
Lex->on_update_value= item;
@@ -5501,33 +5934,55 @@ attribute:
}
| IDENT_sys equal TEXT_STRING_sys
{
- new (YYTHD->mem_root)
+ new (thd->mem_root)
engine_option_value($1, $3, true, &Lex->option_list,
&Lex->option_list_last);
}
| IDENT_sys equal ident
{
- new (YYTHD->mem_root)
+ new (thd->mem_root)
engine_option_value($1, $3, false, &Lex->option_list,
&Lex->option_list_last);
}
| IDENT_sys equal real_ulonglong_num
{
- new (YYTHD->mem_root)
+ new (thd->mem_root)
engine_option_value($1, $3, &Lex->option_list,
- &Lex->option_list_last, YYTHD->mem_root);
+ &Lex->option_list_last, thd->mem_root);
}
| IDENT_sys equal DEFAULT
{
- new (YYTHD->mem_root)
+ new (thd->mem_root)
engine_option_value($1, &Lex->option_list, &Lex->option_list_last);
}
;
+
+type_with_opt_collate:
+ type opt_collate
+ {
+ $$= $1;
+
+ if (Lex->charset) /* Lex->charset is scanned in "type" */
+ {
+ if (!(Lex->charset= merge_charset_and_collation(Lex->charset, $2)))
+ MYSQL_YYABORT;
+ }
+ else if ($2)
+ {
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0),
+ "COLLATE with no CHARACTER SET "
+ "in SP parameters, RETURNS, DECLARE");
+ MYSQL_YYABORT;
+ }
+ }
+ ;
+
+
now_or_signed_literal:
NOW_SYM optional_braces
{
- $$= new (YYTHD->mem_root) Item_func_now_local(6);
+ $$= new (thd->mem_root) Item_func_now_local(6);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -5535,6 +5990,11 @@ now_or_signed_literal:
{ $$=$1; }
;
+hex_num_or_string:
+ HEX_NUM {}
+ | HEX_STRING {}
+ ;
+
charset:
CHAR_SYM SET {}
| CHARSET {}
@@ -5606,11 +6066,21 @@ opt_default:
| DEFAULT {}
;
-opt_binary:
- /* empty */ { Lex->charset=NULL; }
- | ASCII_SYM opt_bin_mod { Lex->charset=&my_charset_latin1; }
- | BYTE_SYM { Lex->charset=&my_charset_bin; }
- | UNICODE_SYM opt_bin_mod
+
+ascii:
+ ASCII_SYM { Lex->charset= &my_charset_latin1; }
+ | BINARY ASCII_SYM
+ {
+ Lex->charset= &my_charset_latin1_bin;
+ }
+ | ASCII_SYM BINARY
+ {
+ Lex->charset= &my_charset_latin1_bin;
+ }
+ ;
+
+unicode:
+ UNICODE_SYM
{
if (!(Lex->charset=get_charset_by_csname("ucs2",
MY_CS_PRIMARY,MYF(0))))
@@ -5619,8 +6089,40 @@ opt_binary:
MYSQL_YYABORT;
}
}
+ | UNICODE_SYM BINARY
+ {
+ if (!(Lex->charset=get_charset_by_name("ucs2_bin", MYF(0))))
+ {
+ my_error(ER_UNKNOWN_COLLATION, MYF(0), "ucs2_bin");
+ MYSQL_YYABORT;
+ }
+ }
+ | BINARY UNICODE_SYM
+ {
+ if (!(Lex->charset=get_charset_by_name("ucs2_bin", MYF(0))))
+ {
+ my_error(ER_UNKNOWN_COLLATION, MYF(0), "ucs2_bin");
+ MYSQL_YYABORT;
+ }
+ }
+ ;
+
+opt_binary:
+ /* empty */ { Lex->charset=NULL; }
+ | ascii
+ | unicode
+ | BYTE_SYM { Lex->charset=&my_charset_bin; }
| charset charset_name opt_bin_mod { Lex->charset=$2; }
- | BINARY opt_bin_charset { Lex->type|= BINCMP_FLAG; }
+ | BINARY
+ {
+ Lex->charset= NULL;
+ Lex->type|= BINCMP_FLAG;
+ }
+ | BINARY charset charset_name
+ {
+ Lex->charset= $3;
+ Lex->type|= BINCMP_FLAG;
+ }
;
opt_bin_mod:
@@ -5628,20 +6130,6 @@ opt_bin_mod:
| BINARY { Lex->type|= BINCMP_FLAG; }
;
-opt_bin_charset:
- /* empty */ { Lex->charset= NULL; }
- | ASCII_SYM { Lex->charset=&my_charset_latin1; }
- | UNICODE_SYM
- {
- if (!(Lex->charset=get_charset_by_csname("ucs2",
- MY_CS_PRIMARY,MYF(0))))
- {
- my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), "ucs2");
- MYSQL_YYABORT;
- }
- }
- | charset charset_name { Lex->charset=$2; }
- ;
opt_primary:
/* empty */
@@ -5649,64 +6137,93 @@ opt_primary:
;
references:
- REFERENCES table_ident
- {
- LEX *lex=Lex;
- lex->fk_delete_opt= lex->fk_update_opt= lex->fk_match_option= 0;
- lex->ref_list.empty();
- }
+ REFERENCES
+ table_ident
opt_ref_list
+ opt_match_clause
+ opt_on_update_delete
{
$$=$2;
}
;
opt_ref_list:
- /* empty */ opt_on_delete {}
- | '(' ref_list ')' opt_on_delete {}
+ /* empty */
+ { Lex->ref_list.empty(); }
+ | '(' ref_list ')'
;
ref_list:
ref_list ',' ident
{
- Key_part_spec *key= new Key_part_spec($3.str);
+ Key_part_spec *key= new Key_part_spec($3, 0);
if (key == NULL)
MYSQL_YYABORT;
Lex->ref_list.push_back(key);
}
| ident
{
- Key_part_spec *key= new Key_part_spec($1.str);
+ Key_part_spec *key= new Key_part_spec($1, 0);
if (key == NULL)
MYSQL_YYABORT;
- Lex->ref_list.push_back(key);
+ LEX *lex= Lex;
+ lex->ref_list.empty();
+ lex->ref_list.push_back(key);
}
;
-opt_on_delete:
- /* empty */ {}
- | opt_on_delete_list {}
- ;
-
-opt_on_delete_list:
- opt_on_delete_list opt_on_delete_item {}
- | opt_on_delete_item {}
+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_delete_item:
- ON DELETE_SYM delete_option { Lex->fk_delete_opt= $3; }
- | ON UPDATE_SYM delete_option { Lex->fk_update_opt= $3; }
- | 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= Foreign_key::FK_OPTION_UNDEF;
+ lex->fk_delete_opt= Foreign_key::FK_OPTION_UNDEF;
+ }
+ | ON UPDATE_SYM delete_option
+ {
+ LEX *lex= Lex;
+ lex->fk_update_opt= $3;
+ lex->fk_delete_opt= Foreign_key::FK_OPTION_UNDEF;
+ }
+ | ON DELETE_SYM delete_option
+ {
+ LEX *lex= Lex;
+ lex->fk_update_opt= Foreign_key::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 { $$= (int) Foreign_key::FK_OPTION_RESTRICT; }
- | CASCADE { $$= (int) Foreign_key::FK_OPTION_CASCADE; }
- | SET NULL_SYM { $$= (int) Foreign_key::FK_OPTION_SET_NULL; }
- | NO_SYM ACTION { $$= (int) Foreign_key::FK_OPTION_NO_ACTION; }
- | SET DEFAULT { $$= (int) Foreign_key::FK_OPTION_DEFAULT; }
+ RESTRICT { $$= Foreign_key::FK_OPTION_RESTRICT; }
+ | CASCADE { $$= Foreign_key::FK_OPTION_CASCADE; }
+ | SET NULL_SYM { $$= Foreign_key::FK_OPTION_SET_NULL; }
+ | NO_SYM ACTION { $$= Foreign_key::FK_OPTION_NO_ACTION; }
+ | SET DEFAULT { $$= Foreign_key::FK_OPTION_DEFAULT; }
;
normal_key_type:
@@ -5811,27 +6328,28 @@ key_using_alg:
all_key_opt:
KEY_BLOCK_SIZE opt_equal ulong_num
{ Lex->key_create_info.block_size= $3; }
+ | COMMENT_SYM TEXT_STRING_sys { Lex->key_create_info.comment= $2; }
| IDENT_sys equal TEXT_STRING_sys
{
- new (YYTHD->mem_root)
+ new (thd->mem_root)
engine_option_value($1, $3, true, &Lex->option_list,
&Lex->option_list_last);
}
| IDENT_sys equal ident
{
- new (YYTHD->mem_root)
+ new (thd->mem_root)
engine_option_value($1, $3, false, &Lex->option_list,
&Lex->option_list_last);
}
| IDENT_sys equal real_ulonglong_num
{
- new (YYTHD->mem_root)
+ new (thd->mem_root)
engine_option_value($1, $3, &Lex->option_list,
- &Lex->option_list_last, YYTHD->mem_root);
+ &Lex->option_list_last, thd->mem_root);
}
| IDENT_sys equal DEFAULT
{
- new (YYTHD->mem_root)
+ new (thd->mem_root)
engine_option_value($1, &Lex->option_list, &Lex->option_list_last);
}
;
@@ -5873,7 +6391,7 @@ key_list:
key_part:
ident
{
- $$= new Key_part_spec($1.str);
+ $$= new Key_part_spec($1, 0);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -5884,15 +6402,15 @@ key_part:
{
my_error(ER_KEY_PART_0, MYF(0), $1.str);
}
- $$= new Key_part_spec($1.str,(uint) key_part_len);
+ $$= new Key_part_spec($1, (uint) key_part_len);
if ($$ == NULL)
MYSQL_YYABORT;
}
;
opt_ident:
- /* empty */ { $$=(char*) 0; /* Default length */ }
- | field_ident { $$=$1.str; }
+ /* empty */ { $$= null_lex_str; }
+ | field_ident { $$= $1; }
;
opt_component:
@@ -5911,16 +6429,16 @@ string_list:
alter:
ALTER alter_options TABLE_SYM table_ident
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
lex->name.str= 0;
lex->name.length= 0;
lex->sql_command= SQLCOM_ALTER_TABLE;
lex->duplicates= DUP_ERROR;
if (!lex->select_lex.add_table_to_list(thd, $4, NULL,
- TL_OPTION_UPDATING))
+ TL_OPTION_UPDATING,
+ TL_READ_NO_INSERT,
+ MDL_SHARED_NO_WRITE))
MYSQL_YYABORT;
- lex->alter_info.reset();
lex->col_list.empty();
lex->select_lex.init_order();
lex->select_lex.db= (lex->select_lex.table_list.first)->db;
@@ -5931,9 +6449,20 @@ alter:
lex->alter_info.reset();
lex->no_write_to_binlog= 0;
lex->create_info.storage_media= HA_SM_DEFAULT;
+ lex->create_last_non_select_table= lex->last_table();
+ DBUG_ASSERT(!lex->m_stmt);
}
alter_commands
- {}
+ {
+ LEX *lex= thd->lex;
+ if (!lex->m_stmt)
+ {
+ /* Create a generic ALTER TABLE statment. */
+ lex->m_stmt= new (thd->mem_root) Alter_table_statement(lex);
+ if (lex->m_stmt == NULL)
+ MYSQL_YYABORT;
+ }
+ }
| ALTER DATABASE ident_or_empty
{
Lex->create_info.default_table_charset= NULL;
@@ -5959,7 +6488,7 @@ alter:
lex->sql_command= SQLCOM_ALTER_DB_UPGRADE;
lex->name= $3;
}
- | ALTER PROCEDURE sp_name
+ | ALTER PROCEDURE_SYM sp_name
{
LEX *lex= Lex;
@@ -6037,7 +6566,7 @@ alter:
Event_parse_data.
*/
- if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD)))
+ if (!(Lex->event_parse_data= Event_parse_data::new_instance(thd)))
MYSQL_YYABORT;
Lex->event_parse_data->identifier= $4;
@@ -6152,38 +6681,50 @@ alter_commands:
| OPTIMIZE PARTITION_SYM opt_no_write_to_binlog
all_or_alt_part_name_list
{
- LEX *lex= Lex;
- lex->sql_command = SQLCOM_OPTIMIZE;
- lex->alter_info.flags|= ALTER_ADMIN_PARTITION;
+ LEX *lex= thd->lex;
lex->no_write_to_binlog= $3;
lex->check_opt.init();
+ DBUG_ASSERT(!lex->m_stmt);
+ lex->m_stmt= new (thd->mem_root)
+ Alter_table_optimize_partition_statement(lex);
+ if (lex->m_stmt == NULL)
+ MYSQL_YYABORT;
}
opt_no_write_to_binlog
| ANALYZE_SYM PARTITION_SYM opt_no_write_to_binlog
all_or_alt_part_name_list
{
- LEX *lex= Lex;
- lex->sql_command = SQLCOM_ANALYZE;
- lex->alter_info.flags|= ALTER_ADMIN_PARTITION;
+ LEX *lex= thd->lex;
lex->no_write_to_binlog= $3;
lex->check_opt.init();
+ DBUG_ASSERT(!lex->m_stmt);
+ lex->m_stmt= new (thd->mem_root)
+ Alter_table_analyze_partition_statement(lex);
+ if (lex->m_stmt == NULL)
+ MYSQL_YYABORT;
}
| CHECK_SYM PARTITION_SYM all_or_alt_part_name_list
{
- LEX *lex= Lex;
- lex->sql_command = SQLCOM_CHECK;
- lex->alter_info.flags|= ALTER_ADMIN_PARTITION;
+ LEX *lex= thd->lex;
lex->check_opt.init();
+ DBUG_ASSERT(!lex->m_stmt);
+ lex->m_stmt= new (thd->mem_root)
+ Alter_table_check_partition_statement(lex);
+ if (lex->m_stmt == NULL)
+ MYSQL_YYABORT;
}
opt_mi_check_type
| REPAIR PARTITION_SYM opt_no_write_to_binlog
all_or_alt_part_name_list
{
- LEX *lex= Lex;
- lex->sql_command = SQLCOM_REPAIR;
- lex->alter_info.flags|= ALTER_ADMIN_PARTITION;
+ LEX *lex= thd->lex;
lex->no_write_to_binlog= $3;
lex->check_opt.init();
+ DBUG_ASSERT(!lex->m_stmt);
+ lex->m_stmt= new (thd->mem_root)
+ Alter_table_repair_partition_statement(lex);
+ if (lex->m_stmt == NULL)
+ MYSQL_YYABORT;
}
opt_mi_repair_type
| COALESCE PARTITION_SYM opt_no_write_to_binlog real_ulong_num
@@ -6191,7 +6732,17 @@ alter_commands:
LEX *lex= Lex;
lex->alter_info.flags|= ALTER_COALESCE_PARTITION;
lex->no_write_to_binlog= $3;
- lex->alter_info.no_parts= $4;
+ 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_stmt);
+ lex->m_stmt= new (thd->mem_root)
+ Alter_table_truncate_partition_statement(lex);
+ if (lex->m_stmt == NULL)
+ MYSQL_YYABORT;
}
| reorg_partition_rule
;
@@ -6233,12 +6784,11 @@ add_part_extra:
| '(' part_def_list ')'
{
LEX *lex= Lex;
- lex->part_info->no_parts= lex->part_info->partitions.elements;
+ lex->part_info->num_parts= lex->part_info->partitions.elements;
}
| PARTITIONS_SYM real_ulong_num
{
- LEX *lex= Lex;
- lex->part_info->no_parts= $2;
+ Lex->part_info->num_parts= $2;
}
;
@@ -6268,8 +6818,8 @@ reorg_parts_rule:
}
INTO '(' part_def_list ')'
{
- LEX *lex= Lex;
- lex->part_info->no_parts= lex->part_info->partitions.elements;
+ partition_info *part_info= Lex->part_info;
+ part_info->num_parts= part_info->partitions.elements;
}
;
@@ -6308,12 +6858,16 @@ add_column:
;
alter_list_item:
- add_column column_def opt_place { }
+ add_column column_def opt_place
+ {
+ Lex->create_last_non_select_table= Lex->last_table();
+ }
| ADD key_def
{
+ Lex->create_last_non_select_table= Lex->last_table();
Lex->alter_info.flags|= ALTER_ADD_INDEX;
}
- | add_column '(' field_list ')'
+ | add_column '(' create_field_list ')'
{
Lex->alter_info.flags|= ALTER_ADD_COLUMN | ALTER_ADD_INDEX;
}
@@ -6325,6 +6879,9 @@ alter_list_item:
lex->option_list= NULL;
}
field_spec opt_place
+ {
+ Lex->create_last_non_select_table= Lex->last_table();
+ }
| MODIFY_SYM opt_column field_ident
{
LEX *lex=Lex;
@@ -6340,16 +6897,19 @@ alter_list_item:
{
LEX *lex=Lex;
if (add_field_to_list(lex->thd,&$3,
- (enum enum_field_types) $5,
- lex->length,lex->dec,lex->type,
+ $5.type,
+ $5.length, $5.dec, lex->type,
lex->default_value, lex->on_update_value,
&lex->comment,
- $3.str, &lex->interval_list, lex->charset,
+ $3.str, &lex->interval_list, $5.charset,
lex->uint_geom_type,
lex->vcol_info, lex->option_list))
MYSQL_YYABORT;
}
opt_place
+ {
+ Lex->create_last_non_select_table= Lex->last_table();
+ }
| DROP opt_column field_ident opt_restrict
{
LEX *lex=Lex;
@@ -6434,7 +6994,6 @@ alter_list_item:
{
if (!$4)
{
- THD *thd= YYTHD;
$4= thd->variables.collation_database;
}
$5= $5 ? $5 : $4;
@@ -6453,11 +7012,17 @@ alter_list_item:
}
| create_table_options_space_separated
{
- Lex->alter_info.flags|= ALTER_OPTIONS;
+ LEX *lex=Lex;
+ lex->alter_info.flags|= ALTER_OPTIONS;
+ if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) &&
+ !lex->create_info.db_type)
+ {
+ lex->create_info.used_fields&= ~HA_CREATE_USED_ENGINE;
+ }
}
| FORCE_SYM
{
- Lex->alter_info.flags|= ALTER_FORCE;
+ Lex->alter_info.flags|= ALTER_RECREATE;
}
| alter_order_clause
{
@@ -6524,8 +7089,6 @@ slave:
LEX *lex=Lex;
lex->sql_command = SQLCOM_SLAVE_START;
lex->type = 0;
- /* We'll use mi structure for UNTIL options */
- bzero((char*) &lex->mi, sizeof(lex->mi));
/* If you change this code don't forget to update SLAVE START too */
}
slave_until
@@ -6542,8 +7105,6 @@ slave:
LEX *lex=Lex;
lex->sql_command = SQLCOM_SLAVE_START;
lex->type = 0;
- /* We'll use mi structure for UNTIL options */
- bzero((char*) &lex->mi, sizeof(lex->mi));
}
slave_until
{}
@@ -6611,33 +7172,13 @@ slave_until_opts:
| slave_until_opts ',' master_file_def
;
-restore:
- RESTORE_SYM table_or_tables
- {
- Lex->sql_command = SQLCOM_RESTORE_TABLE;
- }
- table_list FROM TEXT_STRING_sys
- {
- Lex->backup_dir = $6.str;
- }
- ;
-
-backup:
- BACKUP_SYM table_or_tables
- {
- Lex->sql_command = SQLCOM_BACKUP_TABLE;
- }
- table_list TO_SYM TEXT_STRING_sys
- {
- Lex->backup_dir = $6.str;
- }
- ;
-
checksum:
CHECKSUM_SYM table_or_tables
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_CHECKSUM;
+ /* Will be overriden during execution. */
+ YYPS->m_lock_type= TL_UNLOCK;
}
table_list opt_checksum_type
{}
@@ -6657,9 +7198,17 @@ repair:
lex->no_write_to_binlog= $2;
lex->check_opt.init();
lex->alter_info.reset();
+ /* Will be overriden during execution. */
+ YYPS->m_lock_type= TL_UNLOCK;
}
table_list opt_mi_repair_type
- {}
+ {
+ LEX* lex= thd->lex;
+ DBUG_ASSERT(!lex->m_stmt);
+ lex->m_stmt= new (thd->mem_root) Repair_table_statement(lex);
+ if (lex->m_stmt == NULL)
+ MYSQL_YYABORT;
+ }
;
opt_mi_repair_type:
@@ -6686,9 +7235,17 @@ analyze:
lex->no_write_to_binlog= $2;
lex->check_opt.init();
lex->alter_info.reset();
+ /* Will be overriden during execution. */
+ YYPS->m_lock_type= TL_UNLOCK;
}
table_list
- {}
+ {
+ LEX* lex= thd->lex;
+ DBUG_ASSERT(!lex->m_stmt);
+ lex->m_stmt= new (thd->mem_root) Analyze_table_statement(lex);
+ if (lex->m_stmt == NULL)
+ MYSQL_YYABORT;
+ }
;
binlog_base64_event:
@@ -6712,9 +7269,17 @@ check:
lex->sql_command = SQLCOM_CHECK;
lex->check_opt.init();
lex->alter_info.reset();
+ /* Will be overriden during execution. */
+ YYPS->m_lock_type= TL_UNLOCK;
}
table_list opt_mi_check_type
- {}
+ {
+ LEX* lex= thd->lex;
+ DBUG_ASSERT(!lex->m_stmt);
+ lex->m_stmt= new (thd->mem_root) Check_table_statement(lex);
+ if (lex->m_stmt == NULL)
+ MYSQL_YYABORT;
+ }
;
opt_mi_check_type:
@@ -6744,9 +7309,17 @@ optimize:
lex->no_write_to_binlog= $2;
lex->check_opt.init();
lex->alter_info.reset();
+ /* Will be overriden during execution. */
+ YYPS->m_lock_type= TL_UNLOCK;
}
table_list
- {}
+ {
+ LEX* lex= thd->lex;
+ DBUG_ASSERT(!lex->m_stmt);
+ lex->m_stmt= new (thd->mem_root) Optimize_table_statement(lex);
+ if (lex->m_stmt == NULL)
+ MYSQL_YYABORT;
+ }
;
opt_no_write_to_binlog:
@@ -6792,22 +7365,31 @@ table_to_table:
LEX *lex=Lex;
SELECT_LEX *sl= lex->current_select;
if (!sl->add_table_to_list(lex->thd, $1,NULL,TL_OPTION_UPDATING,
- TL_IGNORE) ||
+ TL_IGNORE, MDL_EXCLUSIVE) ||
!sl->add_table_to_list(lex->thd, $3,NULL,TL_OPTION_UPDATING,
- TL_IGNORE))
+ TL_IGNORE, MDL_EXCLUSIVE))
MYSQL_YYABORT;
}
;
keycache:
- CACHE_SYM INDEX_SYM keycache_list IN_SYM key_cache_name
+ 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= $5;
+ lex->ident= $6;
}
;
+keycache_list_or_parts:
+ keycache_list
+ | assign_to_keycache_parts
+ ;
+
keycache_list:
assign_to_keycache
| keycache_list ',' assign_to_keycache
@@ -6816,7 +7398,18 @@ keycache_list:
assign_to_keycache:
table_ident cache_keys_spec
{
- if (!Select->add_table_to_list(YYTHD, $1, NULL, 0, TL_READ,
+ if (!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 (!Select->add_table_to_list(thd, $1, NULL, 0, TL_READ,
+ MDL_SHARED_READ,
Select->pop_index_hints()))
MYSQL_YYABORT;
}
@@ -6832,11 +7425,17 @@ preload:
{
LEX *lex=Lex;
lex->sql_command=SQLCOM_PRELOAD_KEYS;
+ lex->alter_info.reset();
}
- preload_list
+ preload_list_or_parts
{}
;
+preload_list_or_parts:
+ preload_keys_parts
+ | preload_list
+ ;
+
preload_list:
preload_keys
| preload_list ',' preload_keys
@@ -6845,15 +7444,34 @@ preload_list:
preload_keys:
table_ident cache_keys_spec opt_ignore_leaves
{
- if (!Select->add_table_to_list(YYTHD, $1, NULL, $3, TL_READ,
+ if (!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 (!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.flags|= ALTER_ADMIN_PARTITION;
+ }
+ '(' all_or_alt_part_name_list ')'
+ ;
+
cache_keys_spec:
{
- Lex->select_lex.alloc_index_hints(YYTHD);
+ Lex->select_lex.alloc_index_hints(thd);
Select->set_index_hint_type(INDEX_HINT_USE,
INDEX_HINT_MASK_ALL);
}
@@ -6893,37 +7511,22 @@ select_init:
select_paren:
SELECT_SYM select_part2
{
- LEX *lex= Lex;
- SELECT_LEX * sel= lex->current_select;
- if (sel->set_braces(1))
- {
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
- }
- if (sel->linkage == UNION_TYPE &&
- !sel->master_unit()->first_select()->braces &&
- sel->master_unit()->first_select()->linkage ==
- UNION_TYPE)
- {
- my_parse_error(ER(ER_SYNTAX_ERROR));
+ if (setup_select_in_parentheses(Lex))
MYSQL_YYABORT;
- }
- if (sel->linkage == UNION_TYPE &&
- sel->olap != UNSPECIFIED_OLAP_TYPE &&
- sel->master_unit()->fake_select_lex)
- {
- my_error(ER_WRONG_USAGE, MYF(0),
- "CUBE/ROLLUP", "ORDER BY");
- MYSQL_YYABORT;
- }
- /* select in braces, can't contain global parameters */
- if (sel->master_unit()->fake_select_lex)
- sel->master_unit()->global_parameters=
- sel->master_unit()->fake_select_lex;
}
| '(' select_paren ')'
;
+/* The equivalent of select_paren for nested queries. */
+select_paren_derived:
+ SELECT_SYM select_part2_derived
+ {
+ if (setup_select_in_parentheses(Lex))
+ MYSQL_YYABORT;
+ }
+ | '(' select_paren_derived ')'
+ ;
+
select_init2:
select_part2
{
@@ -6972,8 +7575,8 @@ select_from:
opt_order_clause opt_limit_clause procedure_clause
{
Select->context.table_list=
- Select->context.first_name_resolution_table=
- (TABLE_LIST *) Select->table_list.first;
+ Select->context.first_name_resolution_table=
+ Select->table_list.first;
}
| FROM DUAL_SYM where_clause opt_limit_clause
/* oracle compatibility: oracle always requires FROM clause,
@@ -7000,50 +7603,63 @@ select_option_list:
;
select_option:
- STRAIGHT_JOIN { Select->options|= SELECT_STRAIGHT_JOIN; }
- | HIGH_PRIORITY
+ query_expression_option
+ | SQL_NO_CACHE_SYM
{
- if (check_simple_select())
+ /*
+ 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)
+ {
+ my_error(ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_NO_CACHE");
MYSQL_YYABORT;
- Lex->lock_option= TL_READ_HIGH_PRIORITY;
- Lex->current_select->lock_option= TL_READ_HIGH_PRIORITY;
- }
- | DISTINCT { Select->options|= SELECT_DISTINCT; }
- | SQL_SMALL_RESULT { Select->options|= SELECT_SMALL_RESULT; }
- | SQL_BIG_RESULT { Select->options|= SELECT_BIG_RESULT; }
- | SQL_BUFFER_RESULT
- {
- if (check_simple_select())
+ }
+ else if (Lex->select_lex.sql_cache == SELECT_LEX::SQL_CACHE)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), "SQL_CACHE", "SQL_NO_CACHE");
MYSQL_YYABORT;
- Select->options|= OPTION_BUFFER_RESULT;
- }
- | SQL_CALC_FOUND_ROWS
- {
- if (check_simple_select())
+ }
+ else if (Lex->select_lex.sql_cache == SELECT_LEX::SQL_NO_CACHE)
+ {
+ my_error(ER_DUP_ARGUMENT, MYF(0), "SQL_NO_CACHE");
MYSQL_YYABORT;
- Select->options|= OPTION_FOUND_ROWS;
- }
- | SQL_NO_CACHE_SYM
- {
- Lex->safe_to_cache_query=0;
- Lex->select_lex.options&= ~OPTION_TO_QUERY_CACHE;
- Lex->select_lex.sql_cache= SELECT_LEX::SQL_NO_CACHE;
+ }
+ else
+ {
+ 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
{
- /*
- Honor this flag only if SQL_NO_CACHE wasn't specified AND
- we are parsing the outermost SELECT in the query.
- */
- if (Lex->select_lex.sql_cache != SELECT_LEX::SQL_NO_CACHE &&
- Lex->current_select == &Lex->select_lex)
+ /*
+ 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)
+ {
+ my_error(ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_CACHE");
+ MYSQL_YYABORT;
+ }
+ else if (Lex->select_lex.sql_cache == SELECT_LEX::SQL_NO_CACHE)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), "SQL_NO_CACHE", "SQL_CACHE");
+ MYSQL_YYABORT;
+ }
+ else if (Lex->select_lex.sql_cache == SELECT_LEX::SQL_CACHE)
+ {
+ my_error(ER_DUP_ARGUMENT, MYF(0), "SQL_CACHE");
+ MYSQL_YYABORT;
+ }
+ else
{
Lex->safe_to_cache_query=1;
Lex->select_lex.options|= OPTION_TO_QUERY_CACHE;
Lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE;
}
}
- | ALL { Select->options|= SELECT_ALL; }
;
select_lock_type:
@@ -7052,16 +7668,13 @@ select_lock_type:
{
LEX *lex=Lex;
lex->current_select->set_lock_for_tables(TL_WRITE);
- lex->current_select->lock_option= TL_WRITE;
lex->safe_to_cache_query=0;
- lex->protect_against_global_read_lock= TRUE;
}
| LOCK_SYM IN_SYM SHARE_SYM MODE_SYM
{
LEX *lex=Lex;
lex->current_select->
set_lock_for_tables(TL_READ_WITH_SHARED_LOCKS);
- lex->current_select->lock_option= TL_READ_WITH_SHARED_LOCKS;
lex->safe_to_cache_query=0;
}
;
@@ -7071,7 +7684,6 @@ select_item_list:
| select_item
| '*'
{
- THD *thd= YYTHD;
Item *item= new (thd->mem_root)
Item_field(&thd->lex->current_select->context,
NULL, NULL, "*");
@@ -7084,9 +7696,13 @@ select_item_list:
;
select_item:
- remember_name select_item2 remember_end select_alias
+ remember_name table_wild remember_end
+ {
+ if (add_item_to_list(thd, $2))
+ MYSQL_YYABORT;
+ }
+ | remember_name expr remember_end select_alias
{
- THD *thd= YYTHD;
DBUG_ASSERT($1 < $3);
if (add_item_to_list(thd, $2))
@@ -7121,11 +7737,6 @@ remember_end:
}
;
-select_item2:
- table_wild { $$=$1; /* table.* */ }
- | expr { $$=$1; }
- ;
-
select_alias:
/* empty */ { $$=null_lex_str;}
| AS ident { $$=$2; }
@@ -7193,7 +7804,7 @@ expr:
else
{
/* X OR Y */
- $$ = new (YYTHD->mem_root) Item_cond_or($1, $3);
+ $$ = new (thd->mem_root) Item_cond_or($1, $3);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -7201,7 +7812,7 @@ expr:
| expr XOR expr %prec XOR
{
/* XOR is a proprietary extension */
- $$ = new (YYTHD->mem_root) Item_func_xor($1, $3);
+ $$ = new (thd->mem_root) Item_func_xor($1, $3);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -7243,50 +7854,50 @@ expr:
else
{
/* X AND Y */
- $$ = new (YYTHD->mem_root) Item_cond_and($1, $3);
+ $$ = new (thd->mem_root) Item_cond_and($1, $3);
if ($$ == NULL)
MYSQL_YYABORT;
}
}
| NOT_SYM expr %prec NOT_SYM
{
- $$= negate_expression(YYTHD, $2);
+ $$= negate_expression(thd, $2);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bool_pri IS TRUE_SYM %prec IS
{
- $$= new (YYTHD->mem_root) Item_func_istrue($1);
+ $$= new (thd->mem_root) Item_func_istrue($1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bool_pri IS not TRUE_SYM %prec IS
{
- $$= new (YYTHD->mem_root) Item_func_isnottrue($1);
+ $$= new (thd->mem_root) Item_func_isnottrue($1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bool_pri IS FALSE_SYM %prec IS
{
- $$= new (YYTHD->mem_root) Item_func_isfalse($1);
+ $$= new (thd->mem_root) Item_func_isfalse($1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bool_pri IS not FALSE_SYM %prec IS
{
- $$= new (YYTHD->mem_root) Item_func_isnotfalse($1);
+ $$= new (thd->mem_root) Item_func_isnotfalse($1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bool_pri IS UNKNOWN_SYM %prec IS
{
- $$= new (YYTHD->mem_root) Item_func_isnull($1);
+ $$= new (thd->mem_root) Item_func_isnull($1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bool_pri IS not UNKNOWN_SYM %prec IS
{
- $$= new (YYTHD->mem_root) Item_func_isnotnull($1);
+ $$= new (thd->mem_root) Item_func_isnotnull($1);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -7296,19 +7907,19 @@ expr:
bool_pri:
bool_pri IS NULL_SYM %prec IS
{
- $$= new (YYTHD->mem_root) Item_func_isnull($1);
+ $$= new (thd->mem_root) Item_func_isnull($1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bool_pri IS not NULL_SYM %prec IS
{
- $$= new (YYTHD->mem_root) Item_func_isnotnull($1);
+ $$= new (thd->mem_root) Item_func_isnotnull($1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bool_pri EQUAL_SYM predicate %prec EQUAL_SYM
{
- $$= new (YYTHD->mem_root) Item_func_equal($1,$3);
+ $$= new (thd->mem_root) Item_func_equal($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -7330,13 +7941,12 @@ bool_pri:
predicate:
bit_expr IN_SYM '(' subselect ')'
{
- $$= new (YYTHD->mem_root) Item_in_subselect($1, $4);
+ $$= new (thd->mem_root) Item_in_subselect($1, $4);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr not IN_SYM '(' subselect ')'
{
- THD *thd= YYTHD;
Item *item= new (thd->mem_root) Item_in_subselect($1, $5);
if (item == NULL)
MYSQL_YYABORT;
@@ -7346,7 +7956,7 @@ predicate:
}
| bit_expr IN_SYM '(' expr ')'
{
- $$= handle_sql2003_note184_exception(YYTHD, $1, true, $4);
+ $$= handle_sql2003_note184_exception(thd, $1, true, $4);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -7354,13 +7964,13 @@ predicate:
{
$6->push_front($4);
$6->push_front($1);
- $$= new (YYTHD->mem_root) Item_func_in(*$6);
+ $$= new (thd->mem_root) Item_func_in(*$6);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr not IN_SYM '(' expr ')'
{
- $$= handle_sql2003_note184_exception(YYTHD, $1, false, $5);
+ $$= handle_sql2003_note184_exception(thd, $1, false, $5);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -7368,7 +7978,7 @@ predicate:
{
$7->push_front($5);
$7->push_front($1);
- Item_func_in *item = new (YYTHD->mem_root) Item_func_in(*$7);
+ Item_func_in *item = new (thd->mem_root) Item_func_in(*$7);
if (item == NULL)
MYSQL_YYABORT;
item->negate();
@@ -7376,14 +7986,14 @@ predicate:
}
| bit_expr BETWEEN_SYM bit_expr AND_SYM predicate
{
- $$= new (YYTHD->mem_root) Item_func_between($1,$3,$5);
+ $$= new (thd->mem_root) Item_func_between($1,$3,$5);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr not BETWEEN_SYM bit_expr AND_SYM predicate
{
Item_func_between *item;
- item= new (YYTHD->mem_root) Item_func_between($1,$4,$6);
+ item= new (thd->mem_root) Item_func_between($1,$4,$6);
if (item == NULL)
MYSQL_YYABORT;
item->negate();
@@ -7391,42 +8001,42 @@ predicate:
}
| bit_expr SOUNDS_SYM LIKE bit_expr
{
- Item *item1= new (YYTHD->mem_root) Item_func_soundex($1);
- Item *item4= new (YYTHD->mem_root) Item_func_soundex($4);
+ Item *item1= new (thd->mem_root) Item_func_soundex($1);
+ Item *item4= new (thd->mem_root) Item_func_soundex($4);
if ((item1 == NULL) || (item4 == NULL))
MYSQL_YYABORT;
- $$= new (YYTHD->mem_root) Item_func_eq(item1, item4);
+ $$= new (thd->mem_root) Item_func_eq(item1, item4);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr LIKE simple_expr opt_escape
{
- $$= new (YYTHD->mem_root) Item_func_like($1,$3,$4,Lex->escape_used);
+ $$= new (thd->mem_root) Item_func_like($1,$3,$4,Lex->escape_used);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr not LIKE simple_expr opt_escape
{
- Item *item= new (YYTHD->mem_root) Item_func_like($1,$4,$5,
+ Item *item= new (thd->mem_root) Item_func_like($1,$4,$5,
Lex->escape_used);
if (item == NULL)
MYSQL_YYABORT;
- $$= new (YYTHD->mem_root) Item_func_not(item);
+ $$= new (thd->mem_root) Item_func_not(item);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr REGEXP bit_expr
{
- $$= new (YYTHD->mem_root) Item_func_regex($1,$3);
+ $$= new (thd->mem_root) Item_func_regex($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr not REGEXP bit_expr
{
- Item *item= new (YYTHD->mem_root) Item_func_regex($1,$4);
+ Item *item= new (thd->mem_root) Item_func_regex($1,$4);
if (item == NULL)
MYSQL_YYABORT;
- $$= negate_expression(YYTHD, item);
+ $$= negate_expression(thd, item);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -7436,85 +8046,85 @@ predicate:
bit_expr:
bit_expr '|' bit_expr %prec '|'
{
- $$= new (YYTHD->mem_root) Item_func_bit_or($1,$3);
+ $$= new (thd->mem_root) Item_func_bit_or($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr '&' bit_expr %prec '&'
{
- $$= new (YYTHD->mem_root) Item_func_bit_and($1,$3);
+ $$= new (thd->mem_root) Item_func_bit_and($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr SHIFT_LEFT bit_expr %prec SHIFT_LEFT
{
- $$= new (YYTHD->mem_root) Item_func_shift_left($1,$3);
+ $$= new (thd->mem_root) Item_func_shift_left($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr SHIFT_RIGHT bit_expr %prec SHIFT_RIGHT
{
- $$= new (YYTHD->mem_root) Item_func_shift_right($1,$3);
+ $$= new (thd->mem_root) Item_func_shift_right($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr '+' bit_expr %prec '+'
{
- $$= new (YYTHD->mem_root) Item_func_plus($1,$3);
+ $$= new (thd->mem_root) Item_func_plus($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr '-' bit_expr %prec '-'
{
- $$= new (YYTHD->mem_root) Item_func_minus($1,$3);
+ $$= new (thd->mem_root) Item_func_minus($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr '+' INTERVAL_SYM expr interval %prec '+'
{
- $$= new (YYTHD->mem_root) Item_date_add_interval($1,$4,$5,0);
+ $$= new (thd->mem_root) Item_date_add_interval($1,$4,$5,0);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr '-' INTERVAL_SYM expr interval %prec '-'
{
- $$= new (YYTHD->mem_root) Item_date_add_interval($1,$4,$5,1);
+ $$= new (thd->mem_root) Item_date_add_interval($1,$4,$5,1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr '*' bit_expr %prec '*'
{
- $$= new (YYTHD->mem_root) Item_func_mul($1,$3);
+ $$= new (thd->mem_root) Item_func_mul($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr '/' bit_expr %prec '/'
{
- $$= new (YYTHD->mem_root) Item_func_div($1,$3);
+ $$= new (thd->mem_root) Item_func_div($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr '%' bit_expr %prec '%'
{
- $$= new (YYTHD->mem_root) Item_func_mod($1,$3);
+ $$= new (thd->mem_root) Item_func_mod($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr DIV_SYM bit_expr %prec DIV_SYM
{
- $$= new (YYTHD->mem_root) Item_func_int_div($1,$3);
+ $$= new (thd->mem_root) Item_func_int_div($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr MOD_SYM bit_expr %prec MOD_SYM
{
- $$= new (YYTHD->mem_root) Item_func_mod($1,$3);
+ $$= new (thd->mem_root) Item_func_mod($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr '^' bit_expr
{
- $$= new (YYTHD->mem_root) Item_func_bit_xor($1,$3);
+ $$= new (thd->mem_root) Item_func_bit_xor($1,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -7650,7 +8260,7 @@ dyncall_create_element:
{
LEX *lex= Lex;
$$= (DYNCALL_CREATE_DEF *)
- alloc_root(YYTHD->mem_root, sizeof(DYNCALL_CREATE_DEF));
+ alloc_root(thd->mem_root, sizeof(DYNCALL_CREATE_DEF));
if ($$ == NULL)
MYSQL_YYABORT;
$$->num= $1;
@@ -7670,7 +8280,7 @@ dyncall_create_element:
dyncall_create_list:
dyncall_create_element
{
- $$= new (YYTHD->mem_root) List<DYNCALL_CREATE_DEF>;
+ $$= new (thd->mem_root) List<DYNCALL_CREATE_DEF>;
if ($$ == NULL)
MYSQL_YYABORT;
$$->push_back($1);
@@ -7690,7 +8300,6 @@ simple_expr:
| function_call_conflict
| simple_expr COLLATE_SYM ident_or_text %prec NEG
{
- THD *thd= YYTHD;
Item *i1= new (thd->mem_root) Item_string($3.str,
$3.length,
thd->charset());
@@ -7706,7 +8315,7 @@ simple_expr:
| sum_expr
| simple_expr OR_OR_SYM simple_expr
{
- $$= new (YYTHD->mem_root) Item_func_concat($1, $3);
+ $$= new (thd->mem_root) Item_func_concat($1, $3);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -7716,25 +8325,25 @@ simple_expr:
}
| '-' simple_expr %prec NEG
{
- $$= new (YYTHD->mem_root) Item_func_neg($2);
+ $$= new (thd->mem_root) Item_func_neg($2);
if ($$ == NULL)
MYSQL_YYABORT;
}
| '~' simple_expr %prec NEG
{
- $$= new (YYTHD->mem_root) Item_func_bit_neg($2);
+ $$= new (thd->mem_root) Item_func_bit_neg($2);
if ($$ == NULL)
MYSQL_YYABORT;
}
| not2 simple_expr %prec NEG
{
- $$= negate_expression(YYTHD, $2);
+ $$= negate_expression(thd, $2);
if ($$ == NULL)
MYSQL_YYABORT;
}
| '(' subselect ')'
{
- $$= new (YYTHD->mem_root) Item_singlerow_subselect($2);
+ $$= new (thd->mem_root) Item_singlerow_subselect($2);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -7743,20 +8352,20 @@ simple_expr:
| '(' expr ',' expr_list ')'
{
$4->push_front($2);
- $$= new (YYTHD->mem_root) Item_row(*$4);
+ $$= new (thd->mem_root) Item_row(*$4);
if ($$ == NULL)
MYSQL_YYABORT;
}
| ROW_SYM '(' expr ',' expr_list ')'
{
$5->push_front($3);
- $$= new (YYTHD->mem_root) Item_row(*$5);
+ $$= new (thd->mem_root) Item_row(*$5);
if ($$ == NULL)
MYSQL_YYABORT;
}
| EXISTS '(' subselect ')'
{
- $$= new (YYTHD->mem_root) Item_exists_subselect($3);
+ $$= new (thd->mem_root) Item_exists_subselect($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -7765,7 +8374,7 @@ simple_expr:
| MATCH ident_list_arg AGAINST '(' bit_expr fulltext_options ')'
{
$2->push_front($5);
- Item_func_match *i1= new (YYTHD->mem_root) Item_func_match(*$2, $6);
+ Item_func_match *i1= new (thd->mem_root) Item_func_match(*$2, $6);
if (i1 == NULL)
MYSQL_YYABORT;
Select->add_ftfunc_to_list(i1);
@@ -7773,7 +8382,7 @@ simple_expr:
}
| BINARY simple_expr %prec NEG
{
- $$= create_func_cast(YYTHD, $2, ITEM_CAST_CHAR, NULL, NULL,
+ $$= create_func_cast(thd, $2, ITEM_CAST_CHAR, NULL, NULL,
&my_charset_bin);
if ($$ == NULL)
MYSQL_YYABORT;
@@ -7781,27 +8390,27 @@ simple_expr:
| CAST_SYM '(' expr AS cast_type ')'
{
LEX *lex= Lex;
- $$= create_func_cast(YYTHD, $3, $5, lex->length, lex->dec,
+ $$= create_func_cast(thd, $3, $5, lex->length, lex->dec,
lex->charset);
if ($$ == NULL)
MYSQL_YYABORT;
}
| CASE_SYM opt_expr when_list opt_else END
{
- $$= new (YYTHD->mem_root) Item_func_case(* $3, $2, $4 );
+ $$= new (thd->mem_root) Item_func_case(* $3, $2, $4 );
if ($$ == NULL)
MYSQL_YYABORT;
}
| CONVERT_SYM '(' expr ',' cast_type ')'
{
- $$= create_func_cast(YYTHD, $3, $5, Lex->length, Lex->dec,
+ $$= create_func_cast(thd, $3, $5, Lex->length, Lex->dec,
Lex->charset);
if ($$ == NULL)
MYSQL_YYABORT;
}
| CONVERT_SYM '(' expr USING charset_name ')'
{
- $$= new (YYTHD->mem_root) Item_func_conv_charset($3,$5);
+ $$= new (thd->mem_root) Item_func_conv_charset($3,$5);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -7814,14 +8423,14 @@ simple_expr:
my_error(ER_WRONG_COLUMN_NAME, MYF(0), il->my_name()->str);
MYSQL_YYABORT;
}
- $$= new (YYTHD->mem_root) Item_default_value(Lex->current_context(),
+ $$= new (thd->mem_root) Item_default_value(Lex->current_context(),
$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| VALUES '(' simple_ident_nospvar ')'
{
- $$= new (YYTHD->mem_root) Item_insert_value(Lex->current_context(),
+ $$= new (thd->mem_root) Item_insert_value(Lex->current_context(),
$3);
if ($$ == NULL)
MYSQL_YYABORT;
@@ -7829,7 +8438,7 @@ simple_expr:
| INTERVAL_SYM expr interval '+' expr %prec INTERVAL_SYM
/* we cannot put interval before - */
{
- $$= new (YYTHD->mem_root) Item_date_add_interval($5,$2,$3,0);
+ $$= new (thd->mem_root) Item_date_add_interval($5,$2,$3,0);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -7844,51 +8453,50 @@ simple_expr:
function_call_keyword:
CHAR_SYM '(' expr_list ')'
{
- $$= new (YYTHD->mem_root) Item_func_char(*$3);
+ $$= new (thd->mem_root) Item_func_char(*$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| CHAR_SYM '(' expr_list USING charset_name ')'
{
- $$= new (YYTHD->mem_root) Item_func_char(*$3, $5);
+ $$= new (thd->mem_root) Item_func_char(*$3, $5);
if ($$ == NULL)
MYSQL_YYABORT;
}
| CURRENT_USER optional_braces
{
- $$= new (YYTHD->mem_root) Item_func_current_user(Lex->current_context());
+ $$= new (thd->mem_root) Item_func_current_user(Lex->current_context());
if ($$ == NULL)
MYSQL_YYABORT;
- Lex->set_stmt_unsafe();
+ Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
Lex->safe_to_cache_query= 0;
}
| DATE_SYM '(' expr ')'
{
- $$= new (YYTHD->mem_root) Item_date_typecast($3);
+ $$= new (thd->mem_root) Item_date_typecast($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| DAY_SYM '(' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_dayofmonth($3);
+ $$= new (thd->mem_root) Item_func_dayofmonth($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| HOUR_SYM '(' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_hour($3);
+ $$= new (thd->mem_root) Item_func_hour($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| INSERT '(' expr ',' expr ',' expr ',' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_insert($3,$5,$7,$9);
+ $$= new (thd->mem_root) Item_func_insert($3,$5,$7,$9);
if ($$ == NULL)
MYSQL_YYABORT;
}
| INTERVAL_SYM '(' expr ',' expr ')' %prec INTERVAL_SYM
{
- THD *thd= YYTHD;
List<Item> *list= new (thd->mem_root) List<Item>;
if (list == NULL)
MYSQL_YYABORT;
@@ -7903,7 +8511,6 @@ function_call_keyword:
}
| INTERVAL_SYM '(' expr ',' expr ',' expr_list ')' %prec INTERVAL_SYM
{
- THD *thd= YYTHD;
$7->push_front($5);
$7->push_front($3);
Item_row *item= new (thd->mem_root) Item_row(*$7);
@@ -7915,111 +8522,111 @@ function_call_keyword:
}
| LEFT '(' expr ',' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_left($3,$5);
+ $$= new (thd->mem_root) Item_func_left($3,$5);
if ($$ == NULL)
MYSQL_YYABORT;
}
| MINUTE_SYM '(' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_minute($3);
+ $$= new (thd->mem_root) Item_func_minute($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| MONTH_SYM '(' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_month($3);
+ $$= new (thd->mem_root) Item_func_month($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| RIGHT '(' expr ',' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_right($3,$5);
+ $$= new (thd->mem_root) Item_func_right($3,$5);
if ($$ == NULL)
MYSQL_YYABORT;
}
| SECOND_SYM '(' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_second($3);
+ $$= new (thd->mem_root) Item_func_second($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| TIME_SYM '(' expr ')'
{
- $$= new (YYTHD->mem_root) Item_time_typecast($3, AUTO_SEC_PART_DIGITS);
+ $$= new (thd->mem_root) Item_time_typecast($3, AUTO_SEC_PART_DIGITS);
if ($$ == NULL)
MYSQL_YYABORT;
}
| TIMESTAMP '(' expr ')'
{
- $$= new (YYTHD->mem_root) Item_datetime_typecast($3, AUTO_SEC_PART_DIGITS);
+ $$= new (thd->mem_root) Item_datetime_typecast($3, AUTO_SEC_PART_DIGITS);
if ($$ == NULL)
MYSQL_YYABORT;
}
| TIMESTAMP '(' expr ',' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_add_time($3, $5, 1, 0);
+ $$= new (thd->mem_root) Item_func_add_time($3, $5, 1, 0);
if ($$ == NULL)
MYSQL_YYABORT;
}
| TRIM '(' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_trim($3);
+ $$= new (thd->mem_root) Item_func_trim($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| TRIM '(' LEADING expr FROM expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_ltrim($6,$4);
+ $$= new (thd->mem_root) Item_func_ltrim($6,$4);
if ($$ == NULL)
MYSQL_YYABORT;
}
| TRIM '(' TRAILING expr FROM expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_rtrim($6,$4);
+ $$= new (thd->mem_root) Item_func_rtrim($6,$4);
if ($$ == NULL)
MYSQL_YYABORT;
}
| TRIM '(' BOTH expr FROM expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_trim($6,$4);
+ $$= new (thd->mem_root) Item_func_trim($6,$4);
if ($$ == NULL)
MYSQL_YYABORT;
}
| TRIM '(' LEADING FROM expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_ltrim($5);
+ $$= new (thd->mem_root) Item_func_ltrim($5);
if ($$ == NULL)
MYSQL_YYABORT;
}
| TRIM '(' TRAILING FROM expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_rtrim($5);
+ $$= new (thd->mem_root) Item_func_rtrim($5);
if ($$ == NULL)
MYSQL_YYABORT;
}
| TRIM '(' BOTH FROM expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_trim($5);
+ $$= new (thd->mem_root) Item_func_trim($5);
if ($$ == NULL)
MYSQL_YYABORT;
}
| TRIM '(' expr FROM expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_trim($5,$3);
+ $$= new (thd->mem_root) Item_func_trim($5,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| USER '(' ')'
{
- $$= new (YYTHD->mem_root) Item_func_user();
+ $$= new (thd->mem_root) Item_func_user();
if ($$ == NULL)
MYSQL_YYABORT;
- Lex->set_stmt_unsafe();
+ Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
Lex->safe_to_cache_query=0;
}
| YEAR_SYM '(' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_year($3);
+ $$= new (thd->mem_root) Item_func_year($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -8040,27 +8647,27 @@ function_call_keyword:
function_call_nonkeyword:
ADDDATE_SYM '(' expr ',' expr ')'
{
- $$= new (YYTHD->mem_root) Item_date_add_interval($3, $5,
+ $$= new (thd->mem_root) Item_date_add_interval($3, $5,
INTERVAL_DAY, 0);
if ($$ == NULL)
MYSQL_YYABORT;
}
| ADDDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')'
{
- $$= new (YYTHD->mem_root) Item_date_add_interval($3, $6, $7, 0);
+ $$= new (thd->mem_root) Item_date_add_interval($3, $6, $7, 0);
if ($$ == NULL)
MYSQL_YYABORT;
}
| CURDATE optional_braces
{
- $$= new (YYTHD->mem_root) Item_func_curdate_local();
+ $$= new (thd->mem_root) Item_func_curdate_local();
if ($$ == NULL)
MYSQL_YYABORT;
Lex->safe_to_cache_query=0;
}
| CURTIME opt_time_precision
{
- $$= new (YYTHD->mem_root) Item_func_curtime_local($2);
+ $$= new (thd->mem_root) Item_func_curtime_local($2);
if ($$ == NULL)
MYSQL_YYABORT;
Lex->safe_to_cache_query=0;
@@ -8068,76 +8675,76 @@ function_call_nonkeyword:
| DATE_ADD_INTERVAL '(' expr ',' INTERVAL_SYM expr interval ')'
%prec INTERVAL_SYM
{
- $$= new (YYTHD->mem_root) Item_date_add_interval($3,$6,$7,0);
+ $$= new (thd->mem_root) Item_date_add_interval($3,$6,$7,0);
if ($$ == NULL)
MYSQL_YYABORT;
}
| DATE_SUB_INTERVAL '(' expr ',' INTERVAL_SYM expr interval ')'
%prec INTERVAL_SYM
{
- $$= new (YYTHD->mem_root) Item_date_add_interval($3,$6,$7,1);
+ $$= new (thd->mem_root) Item_date_add_interval($3,$6,$7,1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| EXTRACT_SYM '(' interval FROM expr ')'
{
- $$=new (YYTHD->mem_root) Item_extract( $3, $5);
+ $$=new (thd->mem_root) Item_extract( $3, $5);
if ($$ == NULL)
MYSQL_YYABORT;
}
| GET_FORMAT '(' date_time_type ',' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_get_format($3, $5);
+ $$= new (thd->mem_root) Item_func_get_format($3, $5);
if ($$ == NULL)
MYSQL_YYABORT;
}
| NOW_SYM opt_time_precision
{
- $$= new (YYTHD->mem_root) Item_func_now_local($2);
+ $$= new (thd->mem_root) Item_func_now_local($2);
if ($$ == NULL)
MYSQL_YYABORT;
Lex->safe_to_cache_query=0;
}
| POSITION_SYM '(' bit_expr IN_SYM expr ')'
{
- $$ = new (YYTHD->mem_root) Item_func_locate($5,$3);
+ $$ = new (thd->mem_root) Item_func_locate($5,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| SUBDATE_SYM '(' expr ',' expr ')'
{
- $$= new (YYTHD->mem_root) Item_date_add_interval($3, $5,
+ $$= new (thd->mem_root) Item_date_add_interval($3, $5,
INTERVAL_DAY, 1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| SUBDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')'
{
- $$= new (YYTHD->mem_root) Item_date_add_interval($3, $6, $7, 1);
+ $$= new (thd->mem_root) Item_date_add_interval($3, $6, $7, 1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| SUBSTRING '(' expr ',' expr ',' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_substr($3,$5,$7);
+ $$= new (thd->mem_root) Item_func_substr($3,$5,$7);
if ($$ == NULL)
MYSQL_YYABORT;
}
| SUBSTRING '(' expr ',' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_substr($3,$5);
+ $$= new (thd->mem_root) Item_func_substr($3,$5);
if ($$ == NULL)
MYSQL_YYABORT;
}
| SUBSTRING '(' expr FROM expr FOR_SYM expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_substr($3,$5,$7);
+ $$= new (thd->mem_root) Item_func_substr($3,$5,$7);
if ($$ == NULL)
MYSQL_YYABORT;
}
| SUBSTRING '(' expr FROM expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_substr($3,$5);
+ $$= new (thd->mem_root) Item_func_substr($3,$5);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -8150,44 +8757,44 @@ function_call_nonkeyword:
sysdate_is_now=1, because the slave may have
sysdate_is_now=0.
*/
- Lex->set_stmt_unsafe();
+ Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
if (global_system_variables.sysdate_is_now == 0)
- $$= new (YYTHD->mem_root) Item_func_sysdate_local($2);
+ $$= new (thd->mem_root) Item_func_sysdate_local($2);
else
- $$= new (YYTHD->mem_root) Item_func_now_local($2);
+ $$= new (thd->mem_root) Item_func_now_local($2);
if ($$ == NULL)
MYSQL_YYABORT;
Lex->safe_to_cache_query=0;
}
| TIMESTAMP_ADD '(' interval_time_stamp ',' expr ',' expr ')'
{
- $$= new (YYTHD->mem_root) Item_date_add_interval($7,$5,$3,0);
+ $$= new (thd->mem_root) Item_date_add_interval($7,$5,$3,0);
if ($$ == NULL)
MYSQL_YYABORT;
}
| TIMESTAMP_DIFF '(' interval_time_stamp ',' expr ',' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_timestamp_diff($5,$7,$3);
+ $$= new (thd->mem_root) Item_func_timestamp_diff($5,$7,$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| UTC_DATE_SYM optional_braces
{
- $$= new (YYTHD->mem_root) Item_func_curdate_utc();
+ $$= new (thd->mem_root) Item_func_curdate_utc();
if ($$ == NULL)
MYSQL_YYABORT;
Lex->safe_to_cache_query=0;
}
| UTC_TIME_SYM opt_time_precision
{
- $$= new (YYTHD->mem_root) Item_func_curtime_utc($2);
+ $$= new (thd->mem_root) Item_func_curtime_utc($2);
if ($$ == NULL)
MYSQL_YYABORT;
Lex->safe_to_cache_query=0;
}
| UTC_TIMESTAMP_SYM opt_time_precision
{
- $$= new (YYTHD->mem_root) Item_func_now_utc($2);
+ $$= new (thd->mem_root) Item_func_now_utc($2);
if ($$ == NULL)
MYSQL_YYABORT;
Lex->safe_to_cache_query=0;
@@ -8195,35 +8802,35 @@ function_call_nonkeyword:
|
COLUMN_ADD_SYM '(' expr ',' dyncall_create_list ')'
{
- $$= create_func_dyncol_add(YYTHD, $3, *$5);
+ $$= create_func_dyncol_add(thd, $3, *$5);
if ($$ == NULL)
MYSQL_YYABORT;
}
|
COLUMN_DELETE_SYM '(' expr ',' expr_list ')'
{
- $$= create_func_dyncol_delete(YYTHD, $3, *$5);
+ $$= create_func_dyncol_delete(thd, $3, *$5);
if ($$ == NULL)
MYSQL_YYABORT;
}
|
COLUMN_EXISTS_SYM '(' expr ',' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_dyncol_exists($3, $5);
+ $$= new (thd->mem_root) Item_func_dyncol_exists($3, $5);
if ($$ == NULL)
MYSQL_YYABORT;
}
|
COLUMN_LIST_SYM '(' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_dyncol_list($3);
+ $$= new (thd->mem_root) Item_func_dyncol_list($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
|
COLUMN_CREATE_SYM '(' dyncall_create_list ')'
{
- $$= create_func_dyncol_create(YYTHD, *$3);
+ $$= create_func_dyncol_create(thd, *$3);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -8231,7 +8838,7 @@ function_call_nonkeyword:
COLUMN_GET_SYM '(' expr ',' expr AS cast_type ')'
{
LEX *lex= Lex;
- $$= create_func_dyncol_get(YYTHD, $3, $5, $7,
+ $$= create_func_dyncol_get(thd, $3, $5, $7,
lex->length, lex->dec,
lex->charset);
if ($$ == NULL)
@@ -8247,62 +8854,67 @@ function_call_nonkeyword:
function_call_conflict:
ASCII_SYM '(' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_ascii($3);
+ $$= new (thd->mem_root) Item_func_ascii($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| CHARSET '(' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_charset($3);
+ $$= new (thd->mem_root) Item_func_charset($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| COALESCE '(' expr_list ')'
{
- $$= new (YYTHD->mem_root) Item_func_coalesce(* $3);
+ $$= new (thd->mem_root) Item_func_coalesce(* $3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| COLLATION_SYM '(' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_collation($3);
+ $$= new (thd->mem_root) Item_func_collation($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| DATABASE '(' ')'
{
- $$= new (YYTHD->mem_root) Item_func_database();
+ $$= new (thd->mem_root) Item_func_database();
if ($$ == NULL)
MYSQL_YYABORT;
Lex->safe_to_cache_query=0;
}
| IF '(' expr ',' expr ',' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_if($3,$5,$7);
+ $$= new (thd->mem_root) Item_func_if($3,$5,$7);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
+ | LAST_VALUE '(' expr_list ')'
+ {
+ $$= new (thd->mem_root) Item_func_last_value(* $3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| MICROSECOND_SYM '(' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_microsecond($3);
+ $$= new (thd->mem_root) Item_func_microsecond($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| MOD_SYM '(' expr ',' expr ')'
{
- $$ = new (YYTHD->mem_root) Item_func_mod($3, $5);
+ $$ = new (thd->mem_root) Item_func_mod($3, $5);
if ($$ == NULL)
MYSQL_YYABORT;
}
| OLD_PASSWORD '(' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_old_password($3);
+ $$= new (thd->mem_root) Item_func_old_password($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| PASSWORD '(' expr ')'
{
- THD *thd= YYTHD;
Item* i1;
if (thd->variables.old_passwords)
i1= new (thd->mem_root) Item_func_old_password($3);
@@ -8314,31 +8926,30 @@ function_call_conflict:
}
| QUARTER_SYM '(' expr ')'
{
- $$ = new (YYTHD->mem_root) Item_func_quarter($3);
+ $$ = new (thd->mem_root) Item_func_quarter($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| REPEAT_SYM '(' expr ',' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_repeat($3,$5);
+ $$= new (thd->mem_root) Item_func_repeat($3,$5);
if ($$ == NULL)
MYSQL_YYABORT;
}
| REPLACE '(' expr ',' expr ',' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_replace($3,$5,$7);
+ $$= new (thd->mem_root) Item_func_replace($3,$5,$7);
if ($$ == NULL)
MYSQL_YYABORT;
}
| TRUNCATE_SYM '(' expr ',' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_round($3,$5,1);
+ $$= new (thd->mem_root) Item_func_round($3,$5,1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| WEEK_SYM '(' expr ')'
{
- THD *thd= YYTHD;
Item *i1= new (thd->mem_root) Item_int((char*) "0",
thd->variables.default_week_format,
1);
@@ -8350,7 +8961,7 @@ function_call_conflict:
}
| WEEK_SYM '(' expr ',' expr ')'
{
- $$= new (YYTHD->mem_root) Item_func_week($3,$5);
+ $$= new (thd->mem_root) Item_func_week($3,$5);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -8372,52 +8983,52 @@ function_call_conflict:
geometry_function:
CONTAINS_SYM '(' expr ',' expr ')'
{
- $$= GEOM_NEW(YYTHD,
+ $$= GEOM_NEW(thd,
Item_func_spatial_rel($3, $5,
Item_func::SP_CONTAINS_FUNC));
}
| GEOMETRYCOLLECTION '(' expr_list ')'
{
- $$= GEOM_NEW(YYTHD,
+ $$= GEOM_NEW(thd,
Item_func_spatial_collection(* $3,
Geometry::wkb_geometrycollection,
Geometry::wkb_point));
}
| LINESTRING '(' expr_list ')'
{
- $$= GEOM_NEW(YYTHD,
+ $$= GEOM_NEW(thd,
Item_func_spatial_collection(* $3,
Geometry::wkb_linestring,
Geometry::wkb_point));
}
| MULTILINESTRING '(' expr_list ')'
{
- $$= GEOM_NEW(YYTHD,
+ $$= GEOM_NEW(thd,
Item_func_spatial_collection(* $3,
Geometry::wkb_multilinestring,
Geometry::wkb_linestring));
}
| MULTIPOINT '(' expr_list ')'
{
- $$= GEOM_NEW(YYTHD,
+ $$= GEOM_NEW(thd,
Item_func_spatial_collection(* $3,
Geometry::wkb_multipoint,
Geometry::wkb_point));
}
| MULTIPOLYGON '(' expr_list ')'
{
- $$= GEOM_NEW(YYTHD,
+ $$= GEOM_NEW(thd,
Item_func_spatial_collection(* $3,
Geometry::wkb_multipolygon,
Geometry::wkb_polygon));
}
| POINT_SYM '(' expr ',' expr ')'
{
- $$= GEOM_NEW(YYTHD, Item_func_point($3,$5));
+ $$= GEOM_NEW(thd, Item_func_point($3,$5));
}
| POLYGON '(' expr_list ')'
{
- $$= GEOM_NEW(YYTHD,
+ $$= GEOM_NEW(thd,
Item_func_spatial_collection(* $3,
Geometry::wkb_polygon,
Geometry::wkb_linestring));
@@ -8455,7 +9066,6 @@ function_call_generic:
}
opt_udf_expr_list ')'
{
- THD *thd= YYTHD;
Create_func *builder;
Item *item= NULL;
@@ -8509,7 +9119,6 @@ function_call_generic:
}
| ident '.' ident '(' opt_expr_list ')'
{
- THD *thd= YYTHD;
Create_qfunc *builder;
Item *item= NULL;
@@ -8573,7 +9182,7 @@ opt_udf_expr_list:
udf_expr_list:
udf_expr
{
- $$= new (YYTHD->mem_root) List<Item>;
+ $$= new (thd->mem_root) List<Item>;
if ($$ == NULL)
MYSQL_YYABORT;
$$->push_back($1);
@@ -8606,7 +9215,7 @@ udf_expr:
remember_name we may get quoted or escaped names.
*/
else if ($2->type() != Item::FIELD_ITEM)
- $2->set_name($1, (uint) ($3 - $1), YYTHD->charset());
+ $2->set_name($1, (uint) ($3 - $1), thd->charset());
$$= $2;
}
;
@@ -8614,46 +9223,46 @@ udf_expr:
sum_expr:
AVG_SYM '(' in_sum_expr ')'
{
- $$= new (YYTHD->mem_root) Item_sum_avg($3);
+ $$= new (thd->mem_root) Item_sum_avg($3, FALSE);
if ($$ == NULL)
MYSQL_YYABORT;
}
| AVG_SYM '(' DISTINCT in_sum_expr ')'
{
- $$= new (YYTHD->mem_root) Item_sum_avg_distinct($4);
+ $$= new (thd->mem_root) Item_sum_avg($4, TRUE);
if ($$ == NULL)
MYSQL_YYABORT;
}
| BIT_AND '(' in_sum_expr ')'
{
- $$= new (YYTHD->mem_root) Item_sum_and($3);
+ $$= new (thd->mem_root) Item_sum_and($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| BIT_OR '(' in_sum_expr ')'
{
- $$= new (YYTHD->mem_root) Item_sum_or($3);
+ $$= new (thd->mem_root) Item_sum_or($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| BIT_XOR '(' in_sum_expr ')'
{
- $$= new (YYTHD->mem_root) Item_sum_xor($3);
+ $$= new (thd->mem_root) Item_sum_xor($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| COUNT_SYM '(' opt_all '*' ')'
{
- Item *item= new (YYTHD->mem_root) Item_int((int32) 0L,1);
+ Item *item= new (thd->mem_root) Item_int((int32) 0L,1);
if (item == NULL)
MYSQL_YYABORT;
- $$= new (YYTHD->mem_root) Item_sum_count(item);
+ $$= new (thd->mem_root) Item_sum_count(item);
if ($$ == NULL)
MYSQL_YYABORT;
}
| COUNT_SYM '(' in_sum_expr ')'
{
- $$= new (YYTHD->mem_root) Item_sum_count($3);
+ $$= new (thd->mem_root) Item_sum_count($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -8663,13 +9272,13 @@ sum_expr:
{ Select->in_sum_expr--; }
')'
{
- $$= new (YYTHD->mem_root) Item_sum_count_distinct(* $5);
+ $$= new (thd->mem_root) Item_sum_count(* $5);
if ($$ == NULL)
MYSQL_YYABORT;
}
| MIN_SYM '(' in_sum_expr ')'
{
- $$= new (YYTHD->mem_root) Item_sum_min($3);
+ $$= new (thd->mem_root) Item_sum_min($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -8680,55 +9289,55 @@ sum_expr:
*/
| MIN_SYM '(' DISTINCT in_sum_expr ')'
{
- $$= new (YYTHD->mem_root) Item_sum_min($4);
+ $$= new (thd->mem_root) Item_sum_min($4);
if ($$ == NULL)
MYSQL_YYABORT;
}
| MAX_SYM '(' in_sum_expr ')'
{
- $$= new (YYTHD->mem_root) Item_sum_max($3);
+ $$= new (thd->mem_root) Item_sum_max($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| MAX_SYM '(' DISTINCT in_sum_expr ')'
{
- $$= new (YYTHD->mem_root) Item_sum_max($4);
+ $$= new (thd->mem_root) Item_sum_max($4);
if ($$ == NULL)
MYSQL_YYABORT;
}
| STD_SYM '(' in_sum_expr ')'
{
- $$= new (YYTHD->mem_root) Item_sum_std($3, 0);
+ $$= new (thd->mem_root) Item_sum_std($3, 0);
if ($$ == NULL)
MYSQL_YYABORT;
}
| VARIANCE_SYM '(' in_sum_expr ')'
{
- $$= new (YYTHD->mem_root) Item_sum_variance($3, 0);
+ $$= new (thd->mem_root) Item_sum_variance($3, 0);
if ($$ == NULL)
MYSQL_YYABORT;
}
| STDDEV_SAMP_SYM '(' in_sum_expr ')'
{
- $$= new (YYTHD->mem_root) Item_sum_std($3, 1);
+ $$= new (thd->mem_root) Item_sum_std($3, 1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| VAR_SAMP_SYM '(' in_sum_expr ')'
{
- $$= new (YYTHD->mem_root) Item_sum_variance($3, 1);
+ $$= new (thd->mem_root) Item_sum_variance($3, 1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| SUM_SYM '(' in_sum_expr ')'
{
- $$= new (YYTHD->mem_root) Item_sum_sum($3);
+ $$= new (thd->mem_root) Item_sum_sum($3, FALSE);
if ($$ == NULL)
MYSQL_YYABORT;
}
| SUM_SYM '(' DISTINCT in_sum_expr ')'
{
- $$= new (YYTHD->mem_root) Item_sum_sum_distinct($4);
+ $$= new (thd->mem_root) Item_sum_sum($4, TRUE);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -8740,7 +9349,7 @@ sum_expr:
{
SELECT_LEX *sel= Select;
sel->in_sum_expr--;
- $$= new (YYTHD->mem_root)
+ $$= new (thd->mem_root)
Item_func_group_concat(Lex->current_context(), $3, $5,
sel->gorder_list, $7);
if ($$ == NULL)
@@ -8769,7 +9378,7 @@ variable_aux:
ident_or_text SET_VAR expr
{
Item_func_set_user_var *item;
- $$= item= new (YYTHD->mem_root) Item_func_set_user_var($1, $3);
+ $$= item= new (thd->mem_root) Item_func_set_user_var($1, $3);
if ($$ == NULL)
MYSQL_YYABORT;
LEX *lex= Lex;
@@ -8778,7 +9387,7 @@ variable_aux:
}
| ident_or_text
{
- $$= new (YYTHD->mem_root) Item_func_get_user_var($1);
+ $$= new (thd->mem_root) Item_func_get_user_var($1);
if ($$ == NULL)
MYSQL_YYABORT;
LEX *lex= Lex;
@@ -8792,10 +9401,10 @@ variable_aux:
my_parse_error(ER(ER_SYNTAX_ERROR));
MYSQL_YYABORT;
}
- if (!($$= get_system_var(YYTHD, $2, $3, $4)))
+ if (!($$= get_system_var(thd, $2, $3, $4)))
MYSQL_YYABORT;
if (!((Item_func_get_system_var*) $$)->is_written_to_binlog())
- Lex->set_stmt_unsafe();
+ Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_VARIABLE);
}
;
@@ -8807,7 +9416,7 @@ opt_distinct:
opt_gconcat_separator:
/* empty */
{
- $$= new (YYTHD->mem_root) String(",", 1, &my_charset_latin1);
+ $$= new (thd->mem_root) String(",", 1, &my_charset_latin1);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -8834,9 +9443,9 @@ opt_gorder_clause:
gorder_list:
gorder_list ',' order_ident order_dir
- { if (add_gorder_to_list(YYTHD, $3,(bool) $4)) MYSQL_YYABORT; }
+ { if (add_gorder_to_list(thd, $3,(bool) $4)) MYSQL_YYABORT; }
| order_ident order_dir
- { if (add_gorder_to_list(YYTHD, $1,(bool) $2)) MYSQL_YYABORT; }
+ { if (add_gorder_to_list(thd, $1,(bool) $2)) MYSQL_YYABORT; }
;
in_sum_expr:
@@ -8902,7 +9511,7 @@ opt_expr_list:
expr_list:
expr
{
- $$= new (YYTHD->mem_root) List<Item>;
+ $$= new (thd->mem_root) List<Item>;
if ($$ == NULL)
MYSQL_YYABORT;
$$->push_back($1);
@@ -8922,7 +9531,7 @@ ident_list_arg:
ident_list:
simple_ident
{
- $$= new (YYTHD->mem_root) List<Item>;
+ $$= new (thd->mem_root) List<Item>;
if ($$ == NULL)
MYSQL_YYABORT;
$$->push_back($1);
@@ -8961,6 +9570,7 @@ when_list:
}
;
+/* Equivalent to <table reference> in the SQL:2003 standard. */
/* Warning - may return NULL in case of incomplete SELECT */
table_ref:
table_factor { $$=$1; }
@@ -8968,7 +9578,10 @@ table_ref:
{
LEX *lex= Lex;
if (!($$= lex->current_select->nest_last_join(lex->thd)))
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
MYSQL_YYABORT;
+ }
}
;
@@ -8988,6 +9601,7 @@ esc_table_ref:
| '{' 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; }
@@ -9020,7 +9634,7 @@ join_table:
{
MYSQL_YYABORT_UNLESS($1 && $3);
/* Change the current name resolution context to a local context. */
- if (push_new_name_resolution_context(YYTHD, $1, $3))
+ if (push_new_name_resolution_context(thd, $1, $3))
MYSQL_YYABORT;
Select->parsing_place= IN_ON;
}
@@ -9035,7 +9649,7 @@ join_table:
{
MYSQL_YYABORT_UNLESS($1 && $3);
/* Change the current name resolution context to a local context. */
- if (push_new_name_resolution_context(YYTHD, $1, $3))
+ if (push_new_name_resolution_context(thd, $1, $3))
MYSQL_YYABORT;
Select->parsing_place= IN_ON;
}
@@ -9065,7 +9679,7 @@ join_table:
{
MYSQL_YYABORT_UNLESS($1 && $5);
/* Change the current name resolution context to a local context. */
- if (push_new_name_resolution_context(YYTHD, $1, $5))
+ if (push_new_name_resolution_context(thd, $1, $5))
MYSQL_YYABORT;
Select->parsing_place= IN_ON;
}
@@ -9101,7 +9715,7 @@ join_table:
{
MYSQL_YYABORT_UNLESS($1 && $5);
/* Change the current name resolution context to a local context. */
- if (push_new_name_resolution_context(YYTHD, $1, $5))
+ if (push_new_name_resolution_context(thd, $1, $5))
MYSQL_YYABORT;
Select->parsing_place= IN_ON;
}
@@ -9141,6 +9755,13 @@ normal_join:
| CROSS JOIN_SYM {}
;
+/*
+ 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:
{
@@ -9149,9 +9770,10 @@ table_factor:
}
table_ident opt_table_alias opt_key_definition
{
- if (!($$= Select->add_table_to_list(YYTHD, $2, $3,
+ if (!($$= Select->add_table_to_list(thd, $2, $3,
Select->get_table_join_options(),
- Lex->lock_option,
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type,
Select->pop_index_hints())))
MYSQL_YYABORT;
Select->add_joined_table($$);
@@ -9178,12 +9800,29 @@ table_factor:
/* incomplete derived tables return NULL, we must be
nested in select_derived rule to be here. */
}
- | '(' get_select_lex select_derived union_opt ')' opt_table_alias
+ /*
+ 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>
+
+ The following rules have been flattened into query_expression_body
+ (since we have no <with clause>).
+
+ <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.
+ */
+ | '(' get_select_lex select_derived_union ')' 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 &&
+ if (!($3 || $5) && $2->embedding &&
!$2->embedding->nested_join->join_list.elements)
{
/* we have a derived table ($3 == NULL) but no alias,
@@ -9205,17 +9844,24 @@ table_factor:
if (ti == NULL)
MYSQL_YYABORT;
if (!($$= sel->add_table_to_list(lex->thd,
- ti, $6, 0,
- TL_READ)))
+ new Table_ident(unit), $5, 0,
+ TL_READ, MDL_SHARED_READ)))
MYSQL_YYABORT;
sel->add_joined_table($$);
lex->pop_context();
lex->nest_level--;
}
- else if ($4 || $6)
+ /*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)
{
- /* simple nested joins cannot have aliases or unions */
+ /*
+ Tables with or without joins within parentheses cannot
+ have aliases, and we ruled out derived tables above.
+ */
my_parse_error(ER(ER_SYNTAX_ERROR));
MYSQL_YYABORT;
}
@@ -9228,6 +9874,95 @@ table_factor:
}
;
+/*
+ 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 opt_union_order_or_limit
+ {
+ if ($1 && $2)
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ }
+ | select_derived_union
+ UNION_SYM
+ union_option
+ {
+ if (add_select_to_union_list(Lex, (bool)$3, FALSE))
+ MYSQL_YYABORT;
+ }
+ query_specification
+ {
+ /*
+ Remove from the name resolution context stack the context of the
+ last select in the union.
+ */
+ Lex->pop_context();
+ }
+ opt_union_order_or_limit
+ {
+ if ($1 != NULL)
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ }
+ ;
+
+/* The equivalent of select_init2 for nested queries. */
+select_init2_derived:
+ select_part2_derived
+ {
+ LEX *lex= Lex;
+ SELECT_LEX * sel= lex->current_select;
+ if (lex->current_select->set_braces(0))
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ if (sel->linkage == UNION_TYPE &&
+ sel->master_unit()->first_select()->braces)
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ }
+ ;
+
+/* 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;
+ }
+ opt_select_from select_lock_type
+ ;
+
/* handle contents of parentheses in join expression */
select_derived:
get_select_lex
@@ -9314,8 +10049,7 @@ opt_outer:
index_hint_clause:
/* empty */
{
- $$= global_system_variables.old_mode ?
- INDEX_HINT_MASK_JOIN : INDEX_HINT_MASK_ALL;
+ $$= 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; }
@@ -9347,7 +10081,7 @@ index_hints_list:
opt_index_hints_list:
/* empty */
- | { Select->alloc_index_hints(YYTHD); } index_hints_list
+ | { Select->alloc_index_hints(thd); } index_hints_list
;
opt_key_definition:
@@ -9356,15 +10090,15 @@ opt_key_definition:
;
opt_key_usage_list:
- /* empty */ { Select->add_index_hint(YYTHD, NULL, 0); }
+ /* empty */ { Select->add_index_hint(thd, NULL, 0); }
| key_usage_list {}
;
key_usage_element:
ident
- { Select->add_index_hint(YYTHD, $1.str, $1.length); }
+ { Select->add_index_hint(thd, $1.str, $1.length); }
| PRIMARY_SYM
- { Select->add_index_hint(YYTHD, (char *)"PRIMARY", 7); }
+ { Select->add_index_hint(thd, (char *)"PRIMARY", 7); }
;
key_usage_list:
@@ -9377,7 +10111,7 @@ using_list:
{
if (!($$= new List<String>))
MYSQL_YYABORT;
- String *s= new (YYTHD->mem_root) String((const char *) $1.str,
+ String *s= new (thd->mem_root) String((const char *) $1.str,
$1.length,
system_charset_info);
if (s == NULL)
@@ -9386,7 +10120,7 @@ using_list:
}
| using_list ',' ident
{
- String *s= new (YYTHD->mem_root) String((const char *) $3.str,
+ String *s= new (thd->mem_root) String((const char *) $3.str,
$3.length,
system_charset_info);
if (s == NULL)
@@ -9397,7 +10131,7 @@ using_list:
;
interval:
- interval_time_st {}
+ interval_time_stamp {}
| DAY_HOUR_SYM { $$=INTERVAL_DAY_HOUR; }
| DAY_MICROSECOND_SYM { $$=INTERVAL_DAY_MICROSECOND; }
| DAY_MINUTE_SYM { $$=INTERVAL_DAY_MINUTE; }
@@ -9412,26 +10146,6 @@ interval:
;
interval_time_stamp:
- interval_time_st {}
- | FRAC_SECOND_SYM {
- $$=INTERVAL_MICROSECOND;
- /*
- FRAC_SECOND was mistakenly implemented with
- a wrong resolution. According to the ODBC
- standard it should be nanoseconds, not
- microseconds. Changing it to nanoseconds
- in MySQL would mean making TIMESTAMPDIFF
- and TIMESTAMPADD to return DECIMAL, since
- the return value would be too big for BIGINT
- Hence we just deprecate the incorrect
- implementation without changing its
- resolution.
- */
- WARN_DEPRECATED(yythd, VER_CELOSIA, "FRAC_SECOND", "MICROSECOND");
- }
- ;
-
-interval_time_st:
DAY_SYM { $$=INTERVAL_DAY; }
| WEEK_SYM { $$=INTERVAL_WEEK; }
| HOUR_SYM { $$=INTERVAL_HOUR; }
@@ -9511,7 +10225,6 @@ opt_escape:
}
| /* empty */
{
- THD *thd= YYTHD;
Lex->escape_used= FALSE;
$$= ((thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) ?
new (thd->mem_root) Item_string("", 0, &my_charset_latin1) :
@@ -9532,15 +10245,22 @@ group_clause:
group_list:
group_list ',' order_ident order_dir
- { if (add_group_to_list(YYTHD, $3,(bool) $4)) MYSQL_YYABORT; }
+ { if (add_group_to_list(thd, $3,(bool) $4)) MYSQL_YYABORT; }
| order_ident order_dir
- { if (add_group_to_list(YYTHD, $1,(bool) $2)) MYSQL_YYABORT; }
+ { if (add_group_to_list(thd, $1,(bool) $2)) MYSQL_YYABORT; }
;
olap_opt:
/* empty */ {}
- | WITH CUBE_SYM
+ | 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 (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE)
{
@@ -9550,10 +10270,17 @@ olap_opt:
}
lex->current_select->olap= CUBE_TYPE;
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "CUBE");
- MYSQL_YYABORT; /* To be deleted in 5.1 */
+ MYSQL_YYABORT;
}
- | WITH ROLLUP_SYM
+ | 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 (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE)
{
@@ -9581,7 +10308,6 @@ alter_order_list:
alter_order_item:
simple_ident_nospvar order_dir
{
- THD *thd= YYTHD;
bool ascending= ($2 == 1) ? true : false;
if (add_order_to_list(thd, $1, ascending))
MYSQL_YYABORT;
@@ -9634,9 +10360,9 @@ order_clause:
order_list:
order_list ',' order_ident order_dir
- { if (add_order_to_list(YYTHD, $3,(bool) $4)) MYSQL_YYABORT; }
+ { if (add_order_to_list(thd, $3,(bool) $4)) MYSQL_YYABORT; }
| order_ident order_dir
- { if (add_order_to_list(YYTHD, $1,(bool) $2)) MYSQL_YYABORT; }
+ { if (add_order_to_list(thd, $1,(bool) $2)) MYSQL_YYABORT; }
;
order_dir:
@@ -9652,6 +10378,7 @@ opt_limit_clause_init:
SELECT_LEX *sel= lex->current_select;
sel->offset_limit= 0;
sel->select_limit= 0;
+ lex->limit_rows_examined= 0;
}
| limit_clause {}
;
@@ -9662,7 +10389,18 @@ opt_limit_clause:
;
limit_clause:
- LIMIT limit_options {}
+ LIMIT limit_options
+ {
+ Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
+ }
+ | LIMIT limit_options ROWS_SYM EXAMINED_SYM limit_rows_option
+ {
+ Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
+ }
+ | LIMIT ROWS_SYM EXAMINED_SYM limit_rows_option
+ {
+ Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
+ }
;
limit_options:
@@ -9690,30 +10428,70 @@ limit_options:
;
limit_option:
- param_marker
+ ident
+ {
+ Item_splocal *splocal;
+ LEX *lex= thd->lex;
+ Lex_input_stream *lip= & thd->m_parser_state->m_lip;
+ sp_variable_t *spv;
+ sp_pcontext *spc = lex->spcont;
+ if (spc && (spv = spc->find_variable(&$1)))
+ {
+ splocal= new (thd->mem_root)
+ Item_splocal($1, spv->offset, spv->type,
+ lip->get_tok_start() - lex->sphead->m_tmp_query,
+ lip->get_ptr() - lip->get_tok_start());
+ if (splocal == NULL)
+ MYSQL_YYABORT;
+#ifndef DBUG_OFF
+ splocal->m_sp= lex->sphead;
+#endif
+ lex->safe_to_cache_query=0;
+ }
+ else
+ {
+ my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str);
+ MYSQL_YYABORT;
+ }
+ if (splocal->type() != Item::INT_ITEM)
+ {
+ my_error(ER_WRONG_SPVAR_TYPE_IN_LIMIT, MYF(0));
+ MYSQL_YYABORT;
+ }
+ splocal->limit_clause_param= TRUE;
+ $$= splocal;
+ }
+ | param_marker
{
((Item_param *) $1)->limit_clause_param= TRUE;
}
| ULONGLONG_NUM
{
- $$= new (YYTHD->mem_root) Item_uint($1.str, $1.length);
+ $$= new (thd->mem_root) Item_uint($1.str, $1.length);
if ($$ == NULL)
MYSQL_YYABORT;
}
| LONG_NUM
{
- $$= new (YYTHD->mem_root) Item_uint($1.str, $1.length);
+ $$= new (thd->mem_root) Item_uint($1.str, $1.length);
if ($$ == NULL)
MYSQL_YYABORT;
}
| NUM
{
- $$= new (YYTHD->mem_root) Item_uint($1.str, $1.length);
+ $$= new (thd->mem_root) Item_uint($1.str, $1.length);
if ($$ == NULL)
MYSQL_YYABORT;
}
;
+limit_rows_option:
+ limit_option
+ {
+ LEX *lex=Lex;
+ lex->limit_rows_examined= $1;
+ }
+
delete_limit_clause:
/* empty */
{
@@ -9724,8 +10502,11 @@ delete_limit_clause:
{
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 { my_parse_error(ER(ER_SYNTAX_ERROR)); MYSQL_YYABORT; }
+ | LIMIT limit_option ROWS_SYM EXAMINED_SYM { my_parse_error(ER(ER_SYNTAX_ERROR)); MYSQL_YYABORT; }
;
int_num:
@@ -9784,7 +10565,7 @@ choice:
procedure_clause:
/* empty */
- | PROCEDURE ident /* Procedure name */
+ | PROCEDURE_SYM ident /* Procedure name */
{
LEX *lex=Lex;
@@ -9802,7 +10583,7 @@ procedure_clause:
lex->proc_list.elements=0;
lex->proc_list.first=0;
lex->proc_list.next= &lex->proc_list.first;
- Item_field *item= new (YYTHD->mem_root)
+ Item_field *item= new (thd->mem_root)
Item_field(&lex->current_select->context,
NULL, NULL, $2.str);
if (item == NULL)
@@ -9827,8 +10608,6 @@ procedure_list2:
procedure_item:
remember_name expr remember_end
{
- THD *thd= YYTHD;
-
if (add_proc_to_list(thd, $2))
MYSQL_YYABORT;
if (!$2->name)
@@ -9839,8 +10618,7 @@ procedure_item:
select_var_list_init:
{
LEX *lex=Lex;
- if (!lex->describe &&
- (!(lex->result= new select_dumpvar(lex->nest_level))))
+ if (!lex->describe && (!(lex->result= new select_dumpvar())))
MYSQL_YYABORT;
}
select_var_list
@@ -9921,7 +10699,7 @@ into_destination:
LEX *lex= Lex;
lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
if (!(lex->exchange= new sql_exchange($2.str, 0)) ||
- !(lex->result= new select_export(lex->exchange, lex->nest_level)))
+ !(lex->result= new select_export(lex->exchange)))
MYSQL_YYABORT;
}
opt_load_data_charset
@@ -9935,7 +10713,7 @@ into_destination:
lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
if (!(lex->exchange= new sql_exchange($2.str,1)))
MYSQL_YYABORT;
- if (!(lex->result= new select_dump(lex->exchange, lex->nest_level)))
+ if (!(lex->result= new select_dump(lex->exchange)))
MYSQL_YYABORT;
}
}
@@ -9967,13 +10745,17 @@ do:
*/
drop:
- DROP opt_temporary table_or_tables if_exists table_list opt_restrict
+ DROP opt_temporary table_or_tables if_exists
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_DROP_TABLE;
lex->drop_temporary= $2;
lex->drop_if_exists= $4;
+ YYPS->m_lock_type= TL_UNLOCK;
+ YYPS->m_mdl_type= MDL_EXCLUSIVE;
}
+ table_list opt_restrict
+ {}
| DROP INDEX_SYM ident ON table_ident {}
{
LEX *lex=Lex;
@@ -9985,7 +10767,9 @@ drop:
lex->alter_info.flags= ALTER_DROP_INDEX;
lex->alter_info.drop_list.push_back(ad);
if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL,
- TL_OPTION_UPDATING))
+ TL_OPTION_UPDATING,
+ TL_READ_NO_INSERT,
+ MDL_SHARED_NO_WRITE))
MYSQL_YYABORT;
}
| DROP DATABASE if_exists ident
@@ -9997,7 +10781,6 @@ drop:
}
| DROP FUNCTION_SYM if_exists ident '.' ident
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
sp_name *spname;
if ($4.str && check_db_name(&$4))
@@ -10020,7 +10803,6 @@ drop:
}
| DROP FUNCTION_SYM if_exists ident
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
LEX_STRING db= {0, 0};
sp_name *spname;
@@ -10039,7 +10821,7 @@ drop:
spname->init_qname(thd);
lex->spname= spname;
}
- | DROP PROCEDURE if_exists sp_name
+ | DROP PROCEDURE_SYM if_exists sp_name
{
LEX *lex=Lex;
if (lex->sphead)
@@ -10055,12 +10837,16 @@ drop:
{
Lex->sql_command = SQLCOM_DROP_USER;
}
- | DROP VIEW_SYM if_exists table_list opt_restrict
+ | DROP VIEW_SYM if_exists
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_DROP_VIEW;
lex->drop_if_exists= $3;
+ YYPS->m_lock_type= TL_UNLOCK;
+ YYPS->m_mdl_type= MDL_EXCLUSIVE;
}
+ table_list opt_restrict
+ {}
| DROP EVENT_SYM if_exists sp_name
{
Lex->drop_if_exists= $3;
@@ -10101,7 +10887,10 @@ table_list:
table_name:
table_ident
{
- if (!Select->add_table_to_list(YYTHD, $1, NULL, TL_OPTION_UPDATING))
+ if (!Select->add_table_to_list(thd, $1, NULL,
+ TL_OPTION_UPDATING,
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type))
MYSQL_YYABORT;
}
;
@@ -10114,9 +10903,10 @@ table_alias_ref_list:
table_alias_ref:
table_ident_opt_wild
{
- if (!Select->add_table_to_list(YYTHD, $1, NULL,
+ if (!Select->add_table_to_list(thd, $1, NULL,
TL_OPTION_UPDATING | TL_OPTION_ALIAS,
- Lex->lock_option ))
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type))
MYSQL_YYABORT;
}
;
@@ -10141,8 +10931,6 @@ insert:
lex->sql_command= SQLCOM_INSERT;
lex->duplicates= DUP_ERROR;
mysql_init_select(lex);
- /* for subselects */
- lex->lock_option= TL_READ_DEFAULT;
}
insert_lock_option
opt_ignore insert2
@@ -10182,13 +10970,27 @@ insert_lock_option:
$$= (Lex->sphead ? TL_WRITE_DEFAULT : TL_WRITE_CONCURRENT_INSERT);
}
| LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; }
- | DELAYED_SYM { $$= TL_WRITE_DELAYED; }
+ | 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;
+ $$= TL_WRITE_DELAYED;
+ }
| HIGH_PRIORITY { $$= TL_WRITE; }
;
replace_lock_option:
opt_low_priority { $$= $1; }
- | DELAYED_SYM { $$= TL_WRITE_DELAYED; }
+ | 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;
+ $$= TL_WRITE_DELAYED;
+ }
;
insert2:
@@ -10301,7 +11103,7 @@ expr_or_default:
expr { $$= $1;}
| DEFAULT
{
- $$= new (YYTHD->mem_root) Item_default_value(Lex->current_context());
+ $$= new (thd->mem_root) Item_default_value(Lex->current_context());
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -10321,7 +11123,6 @@ update:
LEX *lex= Lex;
mysql_init_select(lex);
lex->sql_command= SQLCOM_UPDATE;
- lex->lock_option= TL_UNLOCK; /* Will be set later */
lex->duplicates= DUP_ERROR;
}
opt_low_priority opt_ignore join_table_list
@@ -10355,7 +11156,7 @@ update_list:
update_elem:
simple_ident_nospvar equal expr_or_default
{
- if (add_item_to_list(YYTHD, $1) || add_value_to_list(YYTHD, $3))
+ if (add_item_to_list(thd, $1) || add_value_to_list(thd, $3))
MYSQL_YYABORT;
}
;
@@ -10388,66 +11189,81 @@ delete:
LEX *lex= Lex;
lex->sql_command= SQLCOM_DELETE;
mysql_init_select(lex);
- lex->lock_option= TL_WRITE_DEFAULT;
+ YYPS->m_lock_type= TL_WRITE_DEFAULT;
+ YYPS->m_mdl_type= MDL_SHARED_WRITE;
+
lex->ignore= 0;
lex->select_lex.init_order();
}
- opt_delete_options single_multi {}
+ opt_delete_options single_multi
;
single_multi:
FROM table_ident
{
- if (!Select->add_table_to_list(YYTHD, $2, NULL, TL_OPTION_UPDATING,
- Lex->lock_option))
+ if (!Select->add_table_to_list(thd, $2, NULL, TL_OPTION_UPDATING,
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type))
MYSQL_YYABORT;
+ YYPS->m_lock_type= TL_READ_DEFAULT;
+ YYPS->m_mdl_type= MDL_SHARED_READ;
}
where_clause opt_order_clause
delete_limit_clause {}
| table_wild_list
- { mysql_init_multi_delete(Lex); }
+ {
+ mysql_init_multi_delete(Lex);
+ YYPS->m_lock_type= TL_READ_DEFAULT;
+ YYPS->m_mdl_type= MDL_SHARED_READ;
+ }
FROM join_table_list where_clause
- {
+ {
if (multi_delete_set_locks_and_link_aux_tables(Lex))
MYSQL_YYABORT;
}
| FROM table_alias_ref_list
- { mysql_init_multi_delete(Lex); }
+ {
+ mysql_init_multi_delete(Lex);
+ YYPS->m_lock_type= TL_READ_DEFAULT;
+ YYPS->m_mdl_type= MDL_SHARED_READ;
+ }
USING join_table_list where_clause
- {
+ {
if (multi_delete_set_locks_and_link_aux_tables(Lex))
MYSQL_YYABORT;
}
;
table_wild_list:
- table_wild_one {}
- | table_wild_list ',' table_wild_one {}
+ table_wild_one
+ | table_wild_list ',' table_wild_one
;
table_wild_one:
- ident opt_wild opt_table_alias
+ ident opt_wild
{
Table_ident *ti= new Table_ident($1);
if (ti == NULL)
MYSQL_YYABORT;
- if (!Select->add_table_to_list(YYTHD,
+ if (!Select->add_table_to_list(thd,
ti,
- $3,
+ NULL,
TL_OPTION_UPDATING | TL_OPTION_ALIAS,
- Lex->lock_option))
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type))
MYSQL_YYABORT;
}
- | ident '.' ident opt_wild opt_table_alias
+ | ident '.' ident opt_wild
{
- Table_ident *ti= new Table_ident(YYTHD, $1, $3, 0);
+ Table_ident *ti= new Table_ident(thd, $1, $3, 0);
if (ti == NULL)
MYSQL_YYABORT;
- if (!Select->add_table_to_list(YYTHD,
+ if (!Select->add_table_to_list(thd,
ti,
- $5,
+ NULL,
TL_OPTION_UPDATING | TL_OPTION_ALIAS,
- Lex->lock_option))
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type))
MYSQL_YYABORT;
}
;
@@ -10464,18 +11280,29 @@ opt_delete_options:
opt_delete_option:
QUICK { Select->options|= OPTION_QUICK; }
- | LOW_PRIORITY { Lex->lock_option= TL_WRITE_LOW_PRIORITY; }
+ | LOW_PRIORITY { YYPS->m_lock_type= TL_WRITE_LOW_PRIORITY; }
| IGNORE_SYM { Lex->ignore= 1; }
;
truncate:
- TRUNCATE_SYM opt_table_sym table_name
+ TRUNCATE_SYM opt_table_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;
+ }
+ table_name
+ {
+ LEX* lex= thd->lex;
+ DBUG_ASSERT(!lex->m_stmt);
+ lex->m_stmt= new (thd->mem_root) Truncate_statement(lex);
+ if (lex->m_stmt == NULL)
+ MYSQL_YYABORT;
}
;
@@ -10549,13 +11376,14 @@ show:
{
LEX *lex=Lex;
lex->wild=0;
- lex->lock_option= TL_READ;
mysql_init_select(lex);
lex->current_select->parsing_place= SELECT_LIST;
bzero((char*) &lex->create_info,sizeof(lex->create_info));
}
show_param
- {}
+ {
+ Select->parsing_place= NO_MATTER;
+ }
;
show_param:
@@ -10563,7 +11391,7 @@ show_param:
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_DATABASES;
- if (prepare_schema_table(YYTHD, lex, 0, SCH_SCHEMATA))
+ if (prepare_schema_table(thd, lex, 0, SCH_SCHEMATA))
MYSQL_YYABORT;
}
| opt_full TABLES opt_db wild_and_where
@@ -10571,7 +11399,7 @@ show_param:
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_TABLES;
lex->select_lex.db= $3;
- if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLE_NAMES))
+ if (prepare_schema_table(thd, lex, 0, SCH_TABLE_NAMES))
MYSQL_YYABORT;
}
| opt_full TRIGGERS_SYM opt_db wild_and_where
@@ -10579,7 +11407,7 @@ show_param:
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_TRIGGERS;
lex->select_lex.db= $3;
- if (prepare_schema_table(YYTHD, lex, 0, SCH_TRIGGERS))
+ if (prepare_schema_table(thd, lex, 0, SCH_TRIGGERS))
MYSQL_YYABORT;
}
| EVENTS_SYM opt_db wild_and_where
@@ -10587,7 +11415,7 @@ show_param:
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_EVENTS;
lex->select_lex.db= $2;
- if (prepare_schema_table(YYTHD, lex, 0, SCH_EVENTS))
+ if (prepare_schema_table(thd, lex, 0, SCH_EVENTS))
MYSQL_YYABORT;
}
| TABLE_SYM STATUS_SYM opt_db wild_and_where
@@ -10595,7 +11423,7 @@ show_param:
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_TABLE_STATUS;
lex->select_lex.db= $3;
- if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLES))
+ if (prepare_schema_table(thd, lex, 0, SCH_TABLES))
MYSQL_YYABORT;
}
| OPEN_SYM TABLES opt_db wild_and_where
@@ -10603,22 +11431,14 @@ show_param:
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_OPEN_TABLES;
lex->select_lex.db= $3;
- if (prepare_schema_table(YYTHD, lex, 0, SCH_OPEN_TABLES))
- MYSQL_YYABORT;
- }
- | opt_full PLUGIN_SYM
- {
- LEX *lex= Lex;
- WARN_DEPRECATED(yythd, "6.0", "SHOW PLUGIN", "'SHOW PLUGINS'");
- lex->sql_command= SQLCOM_SHOW_PLUGINS;
- if (prepare_schema_table(YYTHD, lex, 0, SCH_PLUGINS))
+ if (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(YYTHD, lex, 0, SCH_PLUGINS))
+ if (prepare_schema_table(thd, lex, 0, SCH_PLUGINS))
MYSQL_YYABORT;
}
| ENGINE_SYM known_storage_engines show_engine_param
@@ -10631,22 +11451,9 @@ show_param:
lex->sql_command= SQLCOM_SHOW_FIELDS;
if ($5)
$4->change_db($5);
- if (prepare_schema_table(YYTHD, lex, $4, SCH_COLUMNS))
+ if (prepare_schema_table(thd, lex, $4, SCH_COLUMNS))
MYSQL_YYABORT;
}
- | NEW_SYM MASTER_SYM FOR_SYM SLAVE
- WITH MASTER_LOG_FILE_SYM EQ
- TEXT_STRING_sys /* $8 */
- AND_SYM MASTER_LOG_POS_SYM EQ
- ulonglong_num /* $12 */
- AND_SYM MASTER_SERVER_ID_SYM EQ
- ulong_num /* $16 */
- {
- Lex->sql_command = SQLCOM_SHOW_NEW_MASTER;
- Lex->mi.log_file_name = $8.str;
- Lex->mi.pos = $12;
- Lex->mi.server_id = $16;
- }
| master_or_binary LOGS_SYM
{
Lex->sql_command = SQLCOM_SHOW_BINLOGS;
@@ -10660,44 +11467,44 @@ show_param:
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_BINLOG_EVENTS;
} opt_limit_clause_init
+ | RELAYLOG_SYM EVENTS_SYM binlog_in binlog_from
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_RELAYLOG_EVENTS;
+ } opt_limit_clause_init
| keys_or_index from_or_in table_ident opt_db where_clause
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_KEYS;
if ($4)
$3->change_db($4);
- if (prepare_schema_table(YYTHD, lex, $3, SCH_STATISTICS))
- MYSQL_YYABORT;
- }
- | COLUMN_SYM TYPES_SYM
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_SHOW_COLUMN_TYPES;
- }
- | TABLE_SYM TYPES_SYM
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_SHOW_STORAGE_ENGINES;
- WARN_DEPRECATED(yythd, "6.0", "SHOW TABLE TYPES", "'SHOW [STORAGE] ENGINES'");
- if (prepare_schema_table(YYTHD, lex, 0, SCH_ENGINES))
+ if (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(YYTHD, lex, 0, SCH_ENGINES))
+ if (prepare_schema_table(thd, lex, 0, SCH_ENGINES))
MYSQL_YYABORT;
}
| AUTHORS_SYM
{
LEX *lex=Lex;
lex->sql_command= SQLCOM_SHOW_AUTHORS;
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT,
+ ER(ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT),
+ "SHOW AUTHORS");
}
| CONTRIBUTORS_SYM
{
LEX *lex=Lex;
lex->sql_command= SQLCOM_SHOW_CONTRIBUTORS;
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT,
+ ER(ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT),
+ "SHOW CONTRIBUTORS");
}
| PRIVILEGES
{
@@ -10718,7 +11525,7 @@ show_param:
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_PROFILE;
- if (prepare_schema_table(YYTHD, lex, NULL, SCH_PROFILES) != 0)
+ if (prepare_schema_table(thd, lex, NULL, SCH_PROFILES) != 0)
YYABORT;
}
| opt_var_type STATUS_SYM wild_and_where
@@ -10726,33 +11533,9 @@ show_param:
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_STATUS;
lex->option_type= $1;
- if (prepare_schema_table(YYTHD, lex, 0, SCH_STATUS))
+ if (prepare_schema_table(thd, lex, 0, SCH_STATUS))
MYSQL_YYABORT;
}
- | INNOBASE_SYM STATUS_SYM
- {
- LEX *lex= Lex;
- lex->sql_command = SQLCOM_SHOW_ENGINE_STATUS;
- if (!(lex->create_info.db_type=
- ha_resolve_by_legacy_type(YYTHD, DB_TYPE_INNODB)))
- {
- my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), "InnoDB");
- MYSQL_YYABORT;
- }
- WARN_DEPRECATED(yythd, "6.0", "SHOW INNODB STATUS", "'SHOW ENGINE INNODB STATUS'");
- }
- | MUTEX_SYM STATUS_SYM
- {
- LEX *lex= Lex;
- lex->sql_command = SQLCOM_SHOW_ENGINE_MUTEX;
- if (!(lex->create_info.db_type=
- ha_resolve_by_legacy_type(YYTHD, DB_TYPE_INNODB)))
- {
- my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), "InnoDB");
- MYSQL_YYABORT;
- }
- WARN_DEPRECATED(yythd, "6.0", "SHOW MUTEX STATUS", "'SHOW ENGINE INNODB MUTEX'");
- }
| opt_full PROCESSLIST_SYM
{ Lex->sql_command= SQLCOM_SHOW_PROCESSLIST;}
| opt_var_type VARIABLES wild_and_where
@@ -10760,21 +11543,21 @@ show_param:
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_VARIABLES;
lex->option_type= $1;
- if (prepare_schema_table(YYTHD, lex, 0, SCH_VARIABLES))
+ if (prepare_schema_table(thd, lex, 0, SCH_VARIABLES))
MYSQL_YYABORT;
}
| charset wild_and_where
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_CHARSETS;
- if (prepare_schema_table(YYTHD, lex, 0, SCH_CHARSETS))
+ if (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(YYTHD, lex, 0, SCH_COLLATIONS))
+ if (prepare_schema_table(thd, lex, 0, SCH_COLLATIONS))
MYSQL_YYABORT;
}
| GRANTS
@@ -10804,7 +11587,7 @@ show_param:
{
LEX *lex= Lex;
lex->sql_command = SQLCOM_SHOW_CREATE;
- if (!lex->select_lex.add_table_to_list(YYTHD, $3, NULL,0))
+ if (!lex->select_lex.add_table_to_list(thd, $3, NULL,0))
MYSQL_YYABORT;
lex->only_view= 0;
lex->create_info.storage_media= HA_SM_DEFAULT;
@@ -10813,7 +11596,7 @@ show_param:
{
LEX *lex= Lex;
lex->sql_command = SQLCOM_SHOW_CREATE;
- if (!lex->select_lex.add_table_to_list(YYTHD, $3, NULL, 0))
+ if (!lex->select_lex.add_table_to_list(thd, $3, NULL, 0))
MYSQL_YYABORT;
lex->only_view= 1;
}
@@ -10829,31 +11612,31 @@ show_param:
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_CLIENT_STATS;
- if (prepare_schema_table(YYTHD, lex, 0, SCH_CLIENT_STATS))
+ if (prepare_schema_table(thd, lex, 0, SCH_CLIENT_STATS))
MYSQL_YYABORT;
}
| USER_STATS_SYM
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_USER_STATS;
- if (prepare_schema_table(YYTHD, lex, 0, SCH_USER_STATS))
+ if (prepare_schema_table(thd, lex, 0, SCH_USER_STATS))
MYSQL_YYABORT;
}
| TABLE_STATS_SYM
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_TABLE_STATS;
- if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLE_STATS))
+ if (prepare_schema_table(thd, lex, 0, SCH_TABLE_STATS))
MYSQL_YYABORT;
}
| INDEX_STATS_SYM
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_INDEX_STATS;
- if (prepare_schema_table(YYTHD, lex, 0, SCH_INDEX_STATS))
+ if (prepare_schema_table(thd, lex, 0, SCH_INDEX_STATS))
MYSQL_YYABORT;
}
- | CREATE PROCEDURE sp_name
+ | CREATE PROCEDURE_SYM sp_name
{
LEX *lex= Lex;
@@ -10873,39 +11656,29 @@ show_param:
lex->sql_command= SQLCOM_SHOW_CREATE_TRIGGER;
lex->spname= $3;
}
- | PROCEDURE STATUS_SYM wild_and_where
+ | PROCEDURE_SYM STATUS_SYM wild_and_where
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_STATUS_PROC;
- if (prepare_schema_table(YYTHD, lex, 0, SCH_PROCEDURES))
+ if (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(YYTHD, lex, 0, SCH_PROCEDURES))
+ if (prepare_schema_table(thd, lex, 0, SCH_PROCEDURES))
MYSQL_YYABORT;
}
- | PROCEDURE CODE_SYM sp_name
+ | PROCEDURE_SYM CODE_SYM sp_name
{
-#ifdef DBUG_OFF
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
-#else
Lex->sql_command= SQLCOM_SHOW_PROC_CODE;
Lex->spname= $3;
-#endif
}
| FUNCTION_SYM CODE_SYM sp_name
{
-#ifdef DBUG_OFF
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
-#else
Lex->sql_command= SQLCOM_SHOW_FUNC_CODE;
Lex->spname= $3;
-#endif
}
| CREATE EVENT_SYM sp_name
{
@@ -10962,7 +11735,7 @@ wild_and_where:
/* empty */
| LIKE TEXT_STRING_sys
{
- Lex->wild= new (YYTHD->mem_root) String($2.str, $2.length,
+ Lex->wild= new (thd->mem_root) String($2.str, $2.length,
system_charset_info);
if (Lex->wild == NULL)
MYSQL_YYABORT;
@@ -10980,16 +11753,18 @@ describe:
describe_command table_ident
{
LEX *lex= Lex;
- lex->lock_option= TL_READ;
mysql_init_select(lex);
lex->current_select->parsing_place= SELECT_LIST;
lex->sql_command= SQLCOM_SHOW_FIELDS;
lex->select_lex.db= 0;
lex->verbose= 0;
- if (prepare_schema_table(YYTHD, lex, $2, SCH_COLUMNS))
+ if (prepare_schema_table(thd, lex, $2, SCH_COLUMNS))
MYSQL_YYABORT;
}
- opt_describe_column {}
+ opt_describe_column
+ {
+ Select->parsing_place= NO_MATTER;
+ }
| describe_command opt_extended_describe
{ Lex->describe|= DESCRIBE_NORMAL; }
select
@@ -11015,7 +11790,7 @@ opt_describe_column:
| text_string { Lex->wild= $1; }
| ident
{
- Lex->wild= new (YYTHD->mem_root) String((const char*) $1.str,
+ Lex->wild= new (thd->mem_root) String((const char*) $1.str,
$1.length,
system_charset_info);
if (Lex->wild == NULL)
@@ -11039,16 +11814,55 @@ flush:
;
flush_options:
- flush_options ',' flush_option
+ 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_with_read_lock {}
+ | flush_options_list
+ ;
+
+opt_with_read_lock:
+ /* empty */ {}
+ | WITH READ_SYM LOCK_SYM optional_flush_tables_arguments
+ {
+ TABLE_LIST *tables= Lex->query_tables;
+ Lex->type|= REFRESH_READ_LOCK | $4;
+ for (; tables; tables= tables->next_global)
+ {
+ tables->mdl_request.set_type(MDL_SHARED_NO_WRITE);
+ tables->required_type= FRMTYPE_TABLE; /* Don't try to flush views. */
+ tables->open_type= OT_BASE_ONLY; /* Ignore temporary tables. */
+ }
+ }
+ ;
+
+flush_options_list:
+ flush_options_list ',' flush_option
| flush_option
+ {}
;
flush_option:
- table_or_tables
- { Lex->type|= REFRESH_TABLES; }
- opt_table_list {}
- | TABLES WITH READ_SYM LOCK_SYM optional_flush_tables_arguments
- { Lex->type|= REFRESH_TABLES | REFRESH_READ_LOCK | $5; }
+ 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
+ { Lex->type|= REFRESH_BINARY_LOG; }
+ | RELAY LOGS_SYM
+ { Lex->type|= REFRESH_RELAY_LOG; }
| QUERY_SYM CACHE_SYM
{ Lex->type|= REFRESH_QUERY_CACHE_FREE; }
| HOSTS_SYM
@@ -11060,9 +11874,10 @@ flush_option:
| STATUS_SYM
{ Lex->type|= REFRESH_STATUS; }
| SLAVE
- { Lex->type|= REFRESH_SLAVE; }
- | SLOW_SYM QUERY_SYM LOGS_SYM
- { Lex->type |= REFRESH_SLOW_QUERY_LOG; }
+ {
+ Lex->type|= REFRESH_SLAVE;
+ Lex->reset_slave_info.all= false;
+ }
| CLIENT_STATS_SYM
{ Lex->type|= REFRESH_CLIENT_STATS; }
| USER_STATS_SYM
@@ -11105,10 +11920,16 @@ reset_options:
reset_option:
SLAVE { Lex->type|= REFRESH_SLAVE; }
+ slave_reset_options { }
| MASTER_SYM { Lex->type|= REFRESH_MASTER; }
| 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; }
+ ;
+
purge:
PURGE
{
@@ -11192,69 +12013,48 @@ use:
/* import, export of files */
load:
- LOAD DATA_SYM
+ LOAD data_or_xml
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
if (lex->sphead)
{
- my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD DATA");
+ my_error(ER_SP_BADSTATEMENT, MYF(0),
+ $2 == FILETYPE_CSV ? "LOAD DATA" : "LOAD XML");
MYSQL_YYABORT;
}
}
- load_data
- {}
- | LOAD TABLE_SYM table_ident FROM MASTER_SYM
- {
- LEX *lex=Lex;
- WARN_DEPRECATED(yythd, "6.0", "LOAD TABLE FROM MASTER",
- "MySQL Administrator (mysqldump, mysql)");
- if (lex->sphead)
- {
- my_error(ER_SP_BADSTATEMENT, MYF(0), "LOAD TABLE");
- MYSQL_YYABORT;
- }
- lex->sql_command = SQLCOM_LOAD_MASTER_TABLE;
- if (!Select->add_table_to_list(YYTHD, $3, NULL, TL_OPTION_UPDATING))
- MYSQL_YYABORT;
- }
- ;
-
-load_data:
load_data_lock opt_local INFILE TEXT_STRING_filesystem
{
LEX *lex=Lex;
lex->sql_command= SQLCOM_LOAD;
- lex->lock_option= $1;
- lex->local_file= $2;
+ lex->local_file= $5;
lex->duplicates= DUP_ERROR;
lex->ignore= 0;
- if (!(lex->exchange= new sql_exchange($4.str, 0)))
+ if (!(lex->exchange= new sql_exchange($7.str, 0, $2)))
MYSQL_YYABORT;
}
opt_duplicate INTO TABLE_SYM table_ident
{
LEX *lex=Lex;
- if (!Select->add_table_to_list(YYTHD, $9, NULL, TL_OPTION_UPDATING,
- lex->lock_option))
+ if (!Select->add_table_to_list(thd, $12, NULL, TL_OPTION_UPDATING,
+ $4, MDL_SHARED_WRITE))
MYSQL_YYABORT;
lex->field_list.empty();
lex->update_list.empty();
lex->value_list.empty();
}
opt_load_data_charset
- { Lex->exchange->cs= $11; }
+ { Lex->exchange->cs= $14; }
+ opt_xml_rows_identified_by
opt_field_term opt_line_term opt_ignore_lines opt_field_or_var_spec
opt_load_data_set_spec
{}
- | FROM MASTER_SYM
- {
- Lex->sql_command = SQLCOM_LOAD_MASTER_DATA;
- WARN_DEPRECATED(yythd, "6.0", "LOAD DATA FROM MASTER",
- "mysqldump or future "
- "BACKUP/RESTORE DATABASE facility");
- }
+ ;
+
+data_or_xml:
+ DATA_SYM { $$= FILETYPE_CSV; }
+ | XML_SYM { $$= FILETYPE_XML; }
;
opt_local:
@@ -11339,15 +12139,26 @@ line_term:
}
;
+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
+ | 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 ')' {}
@@ -11365,7 +12176,7 @@ field_or_var:
simple_ident_nospvar {$$= $1;}
| '@' ident_or_text
{
- $$= new (YYTHD->mem_root) Item_user_var_as_out_param($2);
+ $$= new (thd->mem_root) Item_user_var_as_out_param($2);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -11373,7 +12184,23 @@ field_or_var:
opt_load_data_set_spec:
/* empty */ {}
- | SET insert_update_list {}
+ | 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 (lex->update_list.push_back($1) ||
+ lex->value_list.push_back($4))
+ MYSQL_YYABORT;
+ $4->set_name_no_truncate($3, (uint) ($5 - $3), thd->charset());
+ }
;
/* Common definitions */
@@ -11382,7 +12209,6 @@ text_literal:
TEXT_STRING
{
LEX_STRING tmp;
- THD *thd= YYTHD;
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 &&
@@ -11408,7 +12234,7 @@ text_literal:
uint repertoire= Lex->text_string_is_7bit ?
MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30;
DBUG_ASSERT(my_charset_is_ascii_based(national_charset_info));
- $$= new (YYTHD->mem_root) Item_string($1.str, $1.length,
+ $$= new (thd->mem_root) Item_string($1.str, $1.length,
national_charset_info,
DERIVATION_COERCIBLE,
repertoire);
@@ -11417,7 +12243,7 @@ text_literal:
}
| UNDERSCORE_CHARSET TEXT_STRING
{
- Item_string *str= new (YYTHD->mem_root) Item_string($2.str,
+ Item_string *str= new (thd->mem_root) Item_string($2.str,
$2.length, $1);
if (str == NULL)
MYSQL_YYABORT;
@@ -11436,7 +12262,7 @@ text_literal:
If the string has been pure ASCII so far,
check the new part.
*/
- CHARSET_INFO *cs= YYTHD->variables.collation_connection;
+ CHARSET_INFO *cs= thd->variables.collation_connection;
item->collation.repertoire|= my_string_repertoire(cs,
$2.str,
$2.length);
@@ -11447,15 +12273,15 @@ text_literal:
text_string:
TEXT_STRING_literal
{
- $$= new (YYTHD->mem_root) String($1.str,
+ $$= new (thd->mem_root) String($1.str,
$1.length,
- YYTHD->variables.collation_connection);
+ thd->variables.collation_connection);
if ($$ == NULL)
MYSQL_YYABORT;
}
| HEX_NUM
{
- Item *tmp= new (YYTHD->mem_root) Item_hex_string($1.str, $1.length);
+ Item *tmp= new (thd->mem_root) Item_hex_hybrid($1.str, $1.length);
if (tmp == NULL)
MYSQL_YYABORT;
/*
@@ -11465,9 +12291,17 @@ text_string:
tmp->quick_fix_field();
$$= tmp->val_str((String*) 0);
}
+ | HEX_STRING
+ {
+ Item *tmp= new (thd->mem_root) Item_hex_string($1.str, $1.length);
+ if (tmp == NULL)
+ MYSQL_YYABORT;
+ tmp->quick_fix_field();
+ $$= tmp->val_str((String*) 0);
+ }
| BIN_NUM
{
- Item *tmp= new (YYTHD->mem_root) Item_bin_string($1.str, $1.length);
+ Item *tmp= new (thd->mem_root) Item_bin_string($1.str, $1.length);
if (tmp == NULL)
MYSQL_YYABORT;
/*
@@ -11482,7 +12316,6 @@ text_string:
param_marker:
PARAM_MARKER
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
Lex_input_stream *lip= YYLIP;
Item_param *item;
@@ -11515,38 +12348,44 @@ literal:
| NUM_literal { $$ = $1; }
| NULL_SYM
{
- $$ = new (YYTHD->mem_root) Item_null();
+ $$ = new (thd->mem_root) Item_null();
if ($$ == NULL)
MYSQL_YYABORT;
YYLIP->next_state= MY_LEX_OPERATOR_OR_IDENT;
}
| FALSE_SYM
{
- $$= new (YYTHD->mem_root) Item_int((char*) "FALSE",0,1);
+ $$= new (thd->mem_root) Item_int((char*) "FALSE",0,1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| TRUE_SYM
{
- $$= new (YYTHD->mem_root) Item_int((char*) "TRUE",1,1);
+ $$= new (thd->mem_root) Item_int((char*) "TRUE",1,1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| HEX_NUM
{
- $$ = new (YYTHD->mem_root) Item_hex_string($1.str, $1.length);
+ $$ = new (thd->mem_root) Item_hex_hybrid($1.str, $1.length);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
+ | HEX_STRING
+ {
+ $$ = new (thd->mem_root) Item_hex_string($1.str, $1.length);
if ($$ == NULL)
MYSQL_YYABORT;
}
| BIN_NUM
{
- $$= new (YYTHD->mem_root) Item_bin_string($1.str, $1.length);
+ $$= new (thd->mem_root) Item_bin_string($1.str, $1.length);
if ($$ == NULL)
MYSQL_YYABORT;
}
- | UNDERSCORE_CHARSET HEX_NUM
+ | UNDERSCORE_CHARSET hex_num_or_string
{
- Item *tmp= new (YYTHD->mem_root) Item_hex_string($2.str, $2.length);
+ Item *tmp= new (thd->mem_root) Item_hex_string($2.str, $2.length);
if (tmp == NULL)
MYSQL_YYABORT;
/*
@@ -11557,7 +12396,7 @@ literal:
String *str= tmp->val_str((String*) 0);
Item_string *item_str;
- item_str= new (YYTHD->mem_root)
+ item_str= new (thd->mem_root)
Item_string(NULL, /* name will be set in select_item */
str ? str->ptr() : "",
str ? str->length() : 0,
@@ -11575,7 +12414,7 @@ literal:
}
| UNDERSCORE_CHARSET BIN_NUM
{
- Item *tmp= new (YYTHD->mem_root) Item_bin_string($2.str, $2.length);
+ Item *tmp= new (thd->mem_root) Item_bin_string($2.str, $2.length);
if (tmp == NULL)
MYSQL_YYABORT;
/*
@@ -11586,7 +12425,7 @@ literal:
String *str= tmp->val_str((String*) 0);
Item_string *item_str;
- item_str= new (YYTHD->mem_root)
+ item_str= new (thd->mem_root)
Item_string(NULL, /* name will be set in select_item */
str ? str->ptr() : "",
str ? str->length() : 0,
@@ -11610,7 +12449,7 @@ NUM_literal:
NUM
{
int error;
- $$= new (YYTHD->mem_root)
+ $$= new (thd->mem_root)
Item_int($1.str,
(longlong) my_strtoll10($1.str, NULL, &error),
$1.length);
@@ -11620,7 +12459,7 @@ NUM_literal:
| LONG_NUM
{
int error;
- $$= new (YYTHD->mem_root)
+ $$= new (thd->mem_root)
Item_int($1.str,
(longlong) my_strtoll10($1.str, NULL, &error),
$1.length);
@@ -11629,23 +12468,23 @@ NUM_literal:
}
| ULONGLONG_NUM
{
- $$= new (YYTHD->mem_root) Item_uint($1.str, $1.length);
+ $$= new (thd->mem_root) Item_uint($1.str, $1.length);
if ($$ == NULL)
MYSQL_YYABORT;
}
| DECIMAL_NUM
{
- $$= new (YYTHD->mem_root) Item_decimal($1.str, $1.length,
- YYTHD->charset());
- if (($$ == NULL) || (YYTHD->is_error()))
+ $$= new (thd->mem_root) Item_decimal($1.str, $1.length,
+ thd->charset());
+ if (($$ == NULL) || (thd->is_error()))
{
MYSQL_YYABORT;
}
}
| FLOAT_NUM
{
- $$= new (YYTHD->mem_root) Item_float($1.str, $1.length);
- if (($$ == NULL) || (YYTHD->is_error()))
+ $$= new (thd->mem_root) Item_float($1.str, $1.length);
+ if (($$ == NULL) || (thd->is_error()))
{
MYSQL_YYABORT;
}
@@ -11665,7 +12504,7 @@ table_wild:
ident '.' '*'
{
SELECT_LEX *sel= Select;
- $$= new (YYTHD->mem_root) Item_field(Lex->current_context(),
+ $$= new (thd->mem_root) Item_field(Lex->current_context(),
NullS, $1.str, "*");
if ($$ == NULL)
MYSQL_YYABORT;
@@ -11673,7 +12512,6 @@ table_wild:
}
| ident '.' ident '.' '*'
{
- THD *thd= YYTHD;
SELECT_LEX *sel= Select;
const char* schema= thd->client_capabilities & CLIENT_NO_SCHEMA ?
NullS : $1.str;
@@ -11693,7 +12531,6 @@ order_ident:
simple_ident:
ident
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
Lex_input_stream *lip= YYLIP;
sp_variable_t *spv;
@@ -11744,7 +12581,6 @@ simple_ident:
simple_ident_nospvar:
ident
{
- THD *thd= YYTHD;
SELECT_LEX *sel=Select;
if ((sel->parsing_place != IN_HAVING) ||
(sel->get_in_sum_expr() > 0))
@@ -11766,7 +12602,6 @@ simple_ident_nospvar:
simple_ident_q:
ident '.' ident
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
/*
@@ -11845,7 +12680,6 @@ simple_ident_q:
}
| '.' ident '.' ident
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
SELECT_LEX *sel= lex->current_select;
if (sel->no_table_names_allowed)
@@ -11870,7 +12704,6 @@ simple_ident_q:
}
| ident '.' ident '.' ident
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
SELECT_LEX *sel= lex->current_select;
const char* schema= (thd->client_capabilities & CLIENT_NO_SCHEMA ?
@@ -11938,7 +12771,7 @@ table_ident:
}
| ident '.' ident
{
- $$= new Table_ident(YYTHD, $1,$3,0);
+ $$= new Table_ident(thd, $1,$3,0);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -11960,7 +12793,7 @@ table_ident_opt_wild:
}
| ident '.' ident opt_wild
{
- $$= new Table_ident(YYTHD, $1,$3,0);
+ $$= new Table_ident(thd, $1,$3,0);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -11970,7 +12803,7 @@ table_ident_nodb:
ident
{
LEX_STRING db={(char*) any_db,3};
- $$= new Table_ident(YYTHD, db,$1,0);
+ $$= new Table_ident(thd, db,$1,0);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -11980,8 +12813,6 @@ IDENT_sys:
IDENT { $$= $1; }
| IDENT_QUOTED
{
- THD *thd= YYTHD;
-
if (thd->charset_is_system_charset)
{
CHARSET_INFO *cs= system_charset_info;
@@ -11991,8 +12822,9 @@ IDENT_sys:
$1.length, &dummy_error);
if (wlen < $1.length)
{
+ ErrConvString err($1.str, $1.length, &my_charset_bin);
my_error(ER_INVALID_CHARACTER_STRING, MYF(0),
- cs->csname, $1.str + wlen);
+ cs->csname, err.ptr());
MYSQL_YYABORT;
}
$$= $1;
@@ -12009,8 +12841,6 @@ IDENT_sys:
TEXT_STRING_sys:
TEXT_STRING
{
- THD *thd= YYTHD;
-
if (thd->charset_is_system_charset)
$$= $1;
else
@@ -12025,8 +12855,6 @@ TEXT_STRING_sys:
TEXT_STRING_literal:
TEXT_STRING
{
- THD *thd= YYTHD;
-
if (thd->charset_is_collation_connection)
$$= $1;
else
@@ -12041,8 +12869,6 @@ TEXT_STRING_literal:
TEXT_STRING_filesystem:
TEXT_STRING
{
- THD *thd= YYTHD;
-
if (thd->charset_is_character_set_filesystem)
$$= $1;
else
@@ -12059,7 +12885,6 @@ ident:
IDENT_sys { $$=$1; }
| keyword
{
- THD *thd= YYTHD;
$$.str= thd->strmake($1.str, $1.length);
if ($$.str == NULL)
MYSQL_YYABORT;
@@ -12071,7 +12896,6 @@ label_ident:
IDENT_sys { $$=$1; }
| keyword_sp
{
- THD *thd= YYTHD;
$$.str= thd->strmake($1.str, $1.length);
if ($$.str == NULL)
MYSQL_YYABORT;
@@ -12088,7 +12912,6 @@ ident_or_text:
user:
ident_or_text
{
- THD *thd= YYTHD;
if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
MYSQL_YYABORT;
$$->user = $1;
@@ -12099,13 +12922,12 @@ user:
$$->auth= empty_lex_str;
if (check_string_char_length(&$$->user, ER(ER_USERNAME),
- USERNAME_CHAR_LENGTH,
+ username_char_length,
system_charset_info, 0))
MYSQL_YYABORT;
}
| ident_or_text '@' ident_or_text
{
- THD *thd= YYTHD;
if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
MYSQL_YYABORT;
$$->user = $1; $$->host=$3;
@@ -12114,7 +12936,7 @@ user:
$$->auth= empty_lex_str;
if (check_string_char_length(&$$->user, ER(ER_USERNAME),
- USERNAME_CHAR_LENGTH,
+ username_char_length,
system_charset_info, 0) ||
check_host_name(&$$->host))
MYSQL_YYABORT;
@@ -12127,7 +12949,7 @@ user:
}
| CURRENT_USER optional_braces
{
- if (!($$=(LEX_USER*) YYTHD->alloc(sizeof(st_lex_user))))
+ if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
MYSQL_YYABORT;
/*
empty LEX_USER means current_user and
@@ -12162,6 +12984,7 @@ keyword:
| DEALLOCATE_SYM {}
| DO_SYM {}
| END {}
+ | EXAMINED_SYM {}
| EXECUTE_SYM {}
| FLUSH_SYM {}
| HANDLER_SYM {}
@@ -12171,6 +12994,7 @@ keyword:
| LANGUAGE_SYM {}
| NO_SYM {}
| OPEN_SYM {}
+ | OPTION {}
| OPTIONS_SYM {}
| OWNER_SYM {}
| PARSER_SYM {}
@@ -12227,14 +13051,17 @@ keyword_sp:
| BOOLEAN_SYM {}
| BTREE_SYM {}
| CASCADED {}
+ | CATALOG_NAME_SYM {}
| CHAIN_SYM {}
| CHANGED {}
| CIPHER_SYM {}
| CLIENT_STATS_SYM {}
| CLIENT_SYM {}
+ | CLASS_ORIGIN_SYM {}
| COALESCE {}
| CODE_SYM {}
| COLLATION_SYM {}
+ | COLUMN_NAME_SYM {}
| COLUMNS {}
| COMMITTED_SYM {}
| COMPACT_SYM {}
@@ -12243,10 +13070,14 @@ keyword_sp:
| CONCURRENT {}
| CONNECTION_SYM {}
| CONSISTENT_SYM {}
+ | CONSTRAINT_CATALOG_SYM {}
+ | CONSTRAINT_SCHEMA_SYM {}
+ | CONSTRAINT_NAME_SYM {}
| CONTEXT_SYM {}
| CONTRIBUTORS_SYM {}
| CPU_SYM {}
| CUBE_SYM {}
+ | CURSOR_NAME_SYM {}
| DATA_SYM {}
| DATAFILE_SYM {}
| DATETIME {}
@@ -12266,6 +13097,7 @@ keyword_sp:
| ENUM {}
| ENGINE_SYM {}
| ENGINES_SYM {}
+ | ERROR_SYM {}
| ERRORS {}
| ESCAPE_SYM {}
| EVENT_SYM {}
@@ -12282,7 +13114,7 @@ keyword_sp:
| FILE_SYM {}
| FIRST_SYM {}
| FIXED_SYM {}
- | FRAC_SECOND_SYM {}
+ | GENERAL {}
| GENERATED_SYM {}
| GEOMETRY_SYM {}
| GEOMETRYCOLLECTION {}
@@ -12294,6 +13126,7 @@ keyword_sp:
| HOSTS_SYM {}
| HOUR_SYM {}
| IDENTIFIED_SYM {}
+ | IGNORE_SERVER_IDS_SYM {}
| INDEX_STATS_SYM {}
| INVOKER_SYM {}
| IMPORT {}
@@ -12303,9 +13136,9 @@ keyword_sp:
| IPC_SYM {}
| ISOLATION {}
| ISSUER_SYM {}
- | INNOBASE_SYM {}
| INSERT_METHOD {}
| KEY_BLOCK_SIZE {}
+ | LAST_VALUE {}
| LAST_SYM {}
| LEAVES {}
| LESS_SYM {}
@@ -12318,6 +13151,7 @@ keyword_sp:
| LOGS_SYM {}
| MAX_ROWS {}
| MASTER_SYM {}
+ | MASTER_HEARTBEAT_PERIOD_SYM {}
| MASTER_HOST_SYM {}
| MASTER_PORT_SYM {}
| MASTER_LOG_FILE_SYM {}
@@ -12337,10 +13171,10 @@ keyword_sp:
| MAX_SIZE_SYM {}
| MAX_UPDATES_PER_HOUR {}
| MAX_USER_CONNECTIONS_SYM {}
- | MAX_VALUE_SYM {}
| MEDIUM_SYM {}
| MEMORY_SYM {}
| MERGE_SYM {}
+ | MESSAGE_TEXT_SYM {}
| MICROSECOND_SYM {}
| MIGRATE_SYM {}
| MINUTE_SYM {}
@@ -12352,6 +13186,7 @@ keyword_sp:
| MULTIPOINT {}
| MULTIPOLYGON {}
| MUTEX_SYM {}
+ | MYSQL_ERRNO_SYM {}
| NAME_SYM {}
| NAMES_SYM {}
| NATIONAL_SYM {}
@@ -12387,6 +13222,7 @@ keyword_sp:
| PROCESSLIST_SYM {}
| PROFILE_SYM {}
| PROFILES_SYM {}
+ | PROXY_SYM {}
| QUARTER_SYM {}
| QUERY_SYM {}
| QUICK {}
@@ -12396,6 +13232,8 @@ keyword_sp:
| REDO_BUFFER_SIZE_SYM {}
| REDOFILE_SYM {}
| REDUNDANT_SYM {}
+ | RELAY {}
+ | RELAYLOG_SYM {}
| RELAY_LOG_FILE_SYM {}
| RELAY_LOG_POS_SYM {}
| RELAY_THREAD {}
@@ -12413,6 +13251,7 @@ keyword_sp:
| ROW_SYM {}
| RTREE_SYM {}
| SCHEDULE_SYM {}
+ | SCHEMA_NAME_SYM {}
| SECOND_SYM {}
| SERIAL_SYM {}
| SERIALIZABLE_SYM {}
@@ -12420,7 +13259,7 @@ keyword_sp:
| SIMPLE_SYM {}
| SHARE_SYM {}
| SHUTDOWN {}
- | SLOW_SYM {}
+ | SLOW {}
| SNAPSHOT_SYM {}
| SOFT_SYM {}
| SOUNDS_SYM {}
@@ -12433,6 +13272,7 @@ keyword_sp:
| STATUS_SYM {}
| STORAGE_SYM {}
| STRING_SYM {}
+ | SUBCLASS_ORIGIN_SYM {}
| SUBDATE_SYM {}
| SUBJECT_SYM {}
| SUBPARTITION_SYM {}
@@ -12441,6 +13281,7 @@ keyword_sp:
| SUSPEND_SYM {}
| SWAPS_SYM {}
| SWITCHES_SYM {}
+ | TABLE_NAME_SYM {}
| TABLE_STATS_SYM {}
| TABLES {}
| TABLE_CHECKSUM_SYM {}
@@ -12478,6 +13319,7 @@ keyword_sp:
| WEEK_SYM {}
| WORK_SYM {}
| X509_SYM {}
+ | XML_SYM {}
| YEAR_SYM {}
| VIA_SYM {}
;
@@ -12511,7 +13353,6 @@ option_value_list:
option_type_value:
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
Lex_input_stream *lip= YYLIP;
@@ -12542,7 +13383,6 @@ option_type_value:
}
ext_option_value
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
Lex_input_stream *lip= YYLIP;
@@ -12625,7 +13465,6 @@ ext_option_value:
sys_option_value:
option_type internal_variable_name equal set_expr_or_default
{
- THD *thd= YYTHD;
LEX *lex= Lex;
LEX_STRING *name= &$2.base_name;
@@ -12637,7 +13476,7 @@ sys_option_value:
my_parse_error(ER(ER_SYNTAX_ERROR));
MYSQL_YYABORT;
}
- if (set_trigger_new_row(YYTHD, name, $4))
+ if (set_trigger_new_row(thd, name, $4))
MYSQL_YYABORT;
}
else if ($2.var)
@@ -12667,7 +13506,6 @@ sys_option_value:
}
| option_type TRANSACTION_SYM ISOLATION LEVEL_SYM isolation_types
{
- THD *thd= YYTHD;
LEX *lex=Lex;
lex->option_type= $1;
Item *item= new (thd->mem_root) Item_int((int32) $5);
@@ -12687,7 +13525,7 @@ option_value:
'@' ident_or_text equal expr
{
Item_func_set_user_var *item;
- item= new (YYTHD->mem_root) Item_func_set_user_var($2, $4);
+ item= new (thd->mem_root) Item_func_set_user_var($2, $4);
if (item == NULL)
MYSQL_YYABORT;
set_var_user *var= new set_var_user(item);
@@ -12697,7 +13535,6 @@ option_value:
}
| '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default
{
- THD *thd= YYTHD;
struct sys_var_with_base tmp= $4;
/* Lookup if necessary: must be a system variable. */
if (tmp.var == NULL)
@@ -12710,7 +13547,6 @@ option_value:
}
| charset old_or_new_charset_name_or_default
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
CHARSET_INFO *cs2;
cs2= $2 ? $2: global_system_variables.character_set_client;
@@ -12758,7 +13594,6 @@ option_value:
}
| PASSWORD equal text_or_password
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
LEX_USER *user;
sp_pcontext *spc= lex->spcont;
@@ -12774,7 +13609,7 @@ option_value:
if (!(user=(LEX_USER*) thd->alloc(sizeof(LEX_USER))))
MYSQL_YYABORT;
user->host=null_lex_str;
- user->user.str=thd->security_ctx->priv_user;
+ user->user.str=thd->security_ctx->user;
set_var_password *var= new set_var_password(user, $3);
if (var == NULL)
MYSQL_YYABORT;
@@ -12798,7 +13633,6 @@ option_value:
internal_variable_name:
ident
{
- THD *thd= YYTHD;
sp_pcontext *spc= thd->lex->spcont;
sp_variable_t *spv;
@@ -12857,7 +13691,7 @@ internal_variable_name:
}
else
{
- sys_var *tmp=find_sys_var(YYTHD, $3.str, $3.length);
+ sys_var *tmp=find_sys_var(thd, $3.str, $3.length);
if (!tmp)
MYSQL_YYABORT;
if (!tmp->is_struct())
@@ -12868,7 +13702,7 @@ internal_variable_name:
}
| DEFAULT '.' ident
{
- sys_var *tmp=find_sys_var(YYTHD, $3.str, $3.length);
+ sys_var *tmp=find_sys_var(thd, $3.str, $3.length);
if (!tmp)
MYSQL_YYABORT;
if (!tmp->is_struct())
@@ -12890,16 +13724,16 @@ text_or_password:
TEXT_STRING { $$=$1.str;}
| PASSWORD '(' TEXT_STRING ')'
{
- $$= $3.length ? YYTHD->variables.old_passwords ?
- Item_func_old_password::alloc(YYTHD, $3.str, $3.length) :
- Item_func_password::alloc(YYTHD, $3.str, $3.length) :
+ $$= $3.length ? thd->variables.old_passwords ?
+ Item_func_old_password::alloc(thd, $3.str, $3.length) :
+ Item_func_password::alloc(thd, $3.str, $3.length) :
$3.str;
if ($$ == NULL)
MYSQL_YYABORT;
}
| OLD_PASSWORD '(' TEXT_STRING ')'
{
- $$= $3.length ? Item_func_old_password::alloc(YYTHD, $3.str,
+ $$= $3.length ? Item_func_old_password::alloc(thd, $3.str,
$3.length) :
$3.str;
if ($$ == NULL)
@@ -12913,19 +13747,19 @@ set_expr_or_default:
| DEFAULT { $$=0; }
| ON
{
- $$=new (YYTHD->mem_root) Item_string("ON", 2, system_charset_info);
+ $$=new (thd->mem_root) Item_string("ON", 2, system_charset_info);
if ($$ == NULL)
MYSQL_YYABORT;
}
| ALL
{
- $$=new (YYTHD->mem_root) Item_string("ALL", 3, system_charset_info);
+ $$=new (thd->mem_root) Item_string("ALL", 3, system_charset_info);
if ($$ == NULL)
MYSQL_YYABORT;
}
| BINARY
{
- $$=new (YYTHD->mem_root) Item_string("binary", 6, system_charset_info);
+ $$=new (thd->mem_root) Item_string("binary", 6, system_charset_info);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -12963,11 +13797,14 @@ table_lock:
table_ident opt_table_alias lock_option
{
thr_lock_type lock_type= (thr_lock_type) $3;
- if (!Select->add_table_to_list(YYTHD, $1, $2, 0, lock_type))
+ bool lock_for_write= (lock_type >= TL_WRITE_ALLOW_WRITE);
+ if (!Select->add_table_to_list(thd, $1, $2, 0, lock_type,
+ (lock_for_write ?
+ lock_type == TL_WRITE_CONCURRENT_INSERT ?
+ MDL_SHARED_WRITE :
+ MDL_SHARED_NO_READ_WRITE :
+ MDL_SHARED_READ)))
MYSQL_YYABORT;
- /* If table is to be write locked, protect from a impending GRL. */
- if (lock_type >= TL_WRITE_ALLOW_WRITE)
- Lex->protect_against_global_read_lock= TRUE;
}
;
@@ -13039,17 +13876,25 @@ 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 (YYTHD->mem_root) Item_int((int32) 1);
+ Item *one= new (thd->mem_root) Item_int((int32) 1);
if (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(lex->thd, $2, 0, 0))
MYSQL_YYABORT;
}
handler_read_or_scan where_clause opt_limit_clause
{
Lex->expr_allows_subselect= TRUE;
+ /* Stored functions are not supported for HANDLER READ. */
+ if (Lex->uses_stored_routines())
+ {
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0),
+ "stored functions in HANDLER ... READ");
+ MYSQL_YYABORT;
+ }
}
;
@@ -13096,13 +13941,13 @@ revoke:
;
revoke_command:
- grant_privileges ON opt_table grant_ident FROM grant_list
+ grant_privileges ON opt_table grant_ident FROM user_list
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_REVOKE;
lex->type= 0;
}
- | grant_privileges ON FUNCTION_SYM grant_ident FROM grant_list
+ | grant_privileges ON FUNCTION_SYM grant_ident FROM user_list
{
LEX *lex= Lex;
if (lex->columns.elements)
@@ -13113,7 +13958,7 @@ revoke_command:
lex->sql_command= SQLCOM_REVOKE;
lex->type= TYPE_ENUM_FUNCTION;
}
- | grant_privileges ON PROCEDURE grant_ident FROM grant_list
+ | grant_privileges ON PROCEDURE_SYM grant_ident FROM user_list
{
LEX *lex= Lex;
if (lex->columns.elements)
@@ -13124,10 +13969,17 @@ revoke_command:
lex->sql_command= SQLCOM_REVOKE;
lex->type= TYPE_ENUM_PROCEDURE;
}
- | ALL opt_privileges ',' GRANT OPTION FROM grant_list
+ | ALL opt_privileges ',' GRANT OPTION FROM user_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;
+ }
;
grant:
@@ -13155,7 +14007,7 @@ grant_command:
lex->sql_command= SQLCOM_GRANT;
lex->type= TYPE_ENUM_FUNCTION;
}
- | grant_privileges ON PROCEDURE grant_ident TO_SYM grant_list
+ | grant_privileges ON PROCEDURE_SYM grant_ident TO_SYM grant_list
require_clause grant_options
{
LEX *lex= Lex;
@@ -13167,6 +14019,13 @@ grant_command:
lex->sql_command= SQLCOM_GRANT;
lex->type= TYPE_ENUM_PROCEDURE;
}
+ | 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;
+ }
;
opt_table:
@@ -13231,6 +14090,7 @@ object_privilege:
| CREATE USER { Lex->grant |= CREATE_USER_ACL; }
| EVENT_SYM { Lex->grant |= EVENT_ACL;}
| TRIGGER_SYM { Lex->grant |= TRIGGER_ACL; }
+ | CREATE TABLESPACE { Lex->grant |= CREATE_TABLESPACE_ACL; }
;
opt_and:
@@ -13355,16 +14215,19 @@ grant_list:
}
;
+via_or_with: VIA_SYM | WITH ;
+using_or_as: USING | AS ;
+
grant_user:
user IDENTIFIED_SYM BY TEXT_STRING
{
$$=$1; $1->password=$4;
if ($4.length)
{
- if (YYTHD->variables.old_passwords)
+ if (thd->variables.old_passwords)
{
char *buff=
- (char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1);
+ (char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1);
if (buff == NULL)
MYSQL_YYABORT;
my_make_scrambled_password_323(buff, $4.str, $4.length);
@@ -13374,7 +14237,7 @@ grant_user:
else
{
char *buff=
- (char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1);
+ (char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1);
if (buff == NULL)
MYSQL_YYABORT;
my_make_scrambled_password(buff, $4.str, $4.length);
@@ -13384,14 +14247,17 @@ grant_user:
}
}
| user IDENTIFIED_SYM BY PASSWORD TEXT_STRING
- { $$= $1; $1->password= $5; }
- | user IDENTIFIED_SYM VIA_SYM ident_or_text
+ {
+ $$= $1;
+ $1->password= $5;
+ }
+ | user IDENTIFIED_SYM via_or_with ident_or_text
{
$$= $1;
$1->plugin= $4;
$1->auth= empty_lex_str;
}
- | user IDENTIFIED_SYM VIA_SYM ident_or_text USING TEXT_STRING_sys
+ | user IDENTIFIED_SYM via_or_with ident_or_text using_or_as TEXT_STRING_sys
{
$$= $1;
$1->plugin= $4;
@@ -13418,7 +14284,7 @@ column_list:
column_list_id:
ident
{
- String *new_str = new (YYTHD->mem_root) String((const char*) $1.str,$1.length,system_charset_info);
+ String *new_str = new (thd->mem_root) String((const char*) $1.str,$1.length,system_charset_info);
if (new_str == NULL)
MYSQL_YYABORT;
List_iterator <LEX_COLUMN> iter(Lex->columns);
@@ -13468,6 +14334,11 @@ grant_options:
| 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 {}
@@ -13518,16 +14389,16 @@ opt_work:
opt_chain:
/* empty */
- { $$= (YYTHD->variables.completion_type == 1); }
- | AND_SYM NO_SYM CHAIN_SYM { $$=0; }
- | AND_SYM CHAIN_SYM { $$=1; }
+ { $$= TVL_UNKNOWN; }
+ | AND_SYM NO_SYM CHAIN_SYM { $$= TVL_NO; }
+ | AND_SYM CHAIN_SYM { $$= TVL_YES; }
;
opt_release:
/* empty */
- { $$= (YYTHD->variables.completion_type == 2); }
- | RELEASE_SYM { $$=1; }
- | NO_SYM RELEASE_SYM { $$=0; }
+ { $$= TVL_UNKNOWN; }
+ | RELEASE_SYM { $$= TVL_YES; }
+ | NO_SYM RELEASE_SYM { $$= TVL_NO; }
;
opt_savepoint:
@@ -13540,7 +14411,9 @@ commit:
{
LEX *lex=Lex;
lex->sql_command= SQLCOM_COMMIT;
- lex->tx_chain= $3;
+ /* Don't allow AND CHAIN RELEASE. */
+ MYSQL_YYABORT_UNLESS($3 != TVL_YES || $4 != TVL_YES);
+ lex->tx_chain= $3;
lex->tx_release= $4;
}
;
@@ -13550,7 +14423,9 @@ rollback:
{
LEX *lex=Lex;
lex->sql_command= SQLCOM_ROLLBACK;
- lex->tx_chain= $3;
+ /* 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
@@ -13593,33 +14468,8 @@ union_clause:
union_list:
UNION_SYM union_option
{
- LEX *lex=Lex;
- if (lex->result &&
- (lex->result->get_nest_level() == -1 ||
- lex->result->get_nest_level() == lex->nest_level))
- {
- /*
- Only the last SELECT can have INTO unless the INTO and UNION
- are at different nest levels. In version 5.1 and above, INTO
- will onle be allowed at top level.
- */
- my_error(ER_WRONG_USAGE, MYF(0), "UNION", "INTO");
- MYSQL_YYABORT;
- }
- if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE)
- {
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
- }
- /* This counter shouldn't be incremented for UNION parts */
- Lex->nest_level--;
- if (mysql_new_select(lex, 0))
+ if (add_select_to_union_list(Lex, (bool)$2, TRUE))
MYSQL_YYABORT;
- mysql_init_select(lex);
- lex->current_select->linkage=UNION_TYPE;
- if ($2) /* UNION DISTINCT - remember position */
- lex->current_select->master_unit()->union_distinct=
- lex->current_select;
}
select_init
{
@@ -13637,9 +14487,13 @@ union_opt:
| union_order_or_limit { $$= 1; }
;
+opt_union_order_or_limit:
+ /* Empty */{ $$= false; }
+ | union_order_or_limit { $$= true; }
+ ;
+
union_order_or_limit:
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
DBUG_ASSERT(lex->current_select->linkage != GLOBAL_OPTIONS_TYPE);
SELECT_LEX *sel= lex->current_select;
@@ -13655,7 +14509,6 @@ union_order_or_limit:
}
order_or_limit
{
- THD *thd= YYTHD;
thd->lex->current_select->no_table_names_allowed= 0;
thd->where= "";
}
@@ -13672,22 +14525,40 @@ union_option:
| ALL { $$=0; }
;
-take_first_select: /* empty */
- {
- $$= Lex->current_select->master_unit()->first_select();
- };
+query_specification:
+ SELECT_SYM select_init2_derived
+ {
+ $$= Lex->current_select->master_unit()->first_select();
+ }
+ | '(' select_paren_derived ')'
+ {
+ $$= Lex->current_select->master_unit()->first_select();
+ }
+ ;
+
+query_expression_body:
+ query_specification opt_union_order_or_limit
+ | query_expression_body
+ UNION_SYM union_option
+ {
+ if (add_select_to_union_list(Lex, (bool)$3, FALSE))
+ MYSQL_YYABORT;
+ }
+ query_specification
+ opt_union_order_or_limit
+ {
+ Lex->pop_context();
+ $$= $1;
+ }
+ ;
+/* Corresponds to <query expression> in the SQL:2003 standard. */
subselect:
- SELECT_SYM subselect_start select_init2 take_first_select
- subselect_end
- {
- $$= $4;
- }
- | '(' subselect_start select_paren take_first_select
- subselect_end ')'
- {
- $$= $4;
- };
+ subselect_start query_expression_body subselect_end
+ {
+ $$= $2;
+ }
+ ;
subselect_start:
{
@@ -13713,17 +14584,6 @@ subselect_start:
subselect_end:
{
LEX *lex=Lex;
- /*
- Set the required lock level for the tables associated with the
- current sub-select. This will overwrite previous lock options set
- using st_select_lex::add_table_to_list in any of the following
- rules: single_multi, table_wild_one, load_data, table_alias_ref,
- table_factor.
- The default lock level is TL_READ_DEFAULT but it can be modified
- with query options specific for a certain (sub-)SELECT.
- */
- lex->current_select->
- set_lock_for_tables(lex->current_select->lock_option);
lex->pop_context();
SELECT_LEX *child= lex->current_select;
@@ -13739,6 +14599,44 @@ subselect_end:
}
;
+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 (check_simple_select())
+ 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; }
+ | SQL_SMALL_RESULT { Select->options|= SELECT_SMALL_RESULT; }
+ | SQL_BIG_RESULT { Select->options|= SELECT_BIG_RESULT; }
+ | SQL_BUFFER_RESULT
+ {
+ if (check_simple_select())
+ MYSQL_YYABORT;
+ Select->options|= OPTION_BUFFER_RESULT;
+ }
+ | SQL_CALC_FOUND_ROWS
+ {
+ if (check_simple_select())
+ MYSQL_YYABORT;
+ Select->options|= OPTION_FOUND_ROWS;
+ }
+ | ALL { Select->options|= SELECT_ALL; }
+ ;
+
/**************************************************************************
CREATE VIEW | TRIGGER | PROCEDURE statements.
@@ -13792,14 +14690,14 @@ no_definer:
from older master servers (i.e. to create non-suid trigger in this
case).
*/
- YYTHD->lex->definer= 0;
+ thd->lex->definer= 0;
}
;
definer:
DEFINER_SYM EQ user
{
- YYTHD->lex->definer= get_current_user(YYTHD, $3);
+ thd->lex->definer= get_current_user(thd, $3);
}
;
@@ -13844,12 +14742,15 @@ view_suid:
view_tail:
view_suid VIEW_SYM table_ident
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
lex->sql_command= SQLCOM_CREATE_VIEW;
/* first table in list is target VIEW name */
- if (!lex->select_lex.add_table_to_list(thd, $3, NULL, TL_OPTION_UPDATING))
+ if (!lex->select_lex.add_table_to_list(thd, $3, 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
;
@@ -13884,7 +14785,6 @@ view_select:
}
view_select_aux view_check_option
{
- THD *thd= YYTHD;
LEX *lex= Lex;
uint len= YYLIP->get_cpp_ptr() - lex->create_view_select.str;
void *create_view_select= thd->memdup(lex->create_view_select.str, len);
@@ -13940,7 +14840,6 @@ trigger_tail:
EACH_SYM
ROW_SYM
{ /* $15 */
- THD *thd= YYTHD;
LEX *lex= thd->lex;
Lex_input_stream *lip= YYLIP;
sp_head *sp;
@@ -13974,8 +14873,8 @@ trigger_tail:
sp_head *sp= lex->sphead;
lex->sql_command= SQLCOM_CREATE_TRIGGER;
- sp->set_stmt_end(YYTHD);
- sp->restore_thd_mem_root(YYTHD);
+ sp->set_stmt_end(thd);
+ sp->restore_thd_mem_root(thd);
if (sp->is_not_allowed_in_function("trigger"))
MYSQL_YYABORT;
@@ -13985,10 +14884,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(YYTHD, $9,
+ if (!lex->select_lex.add_table_to_list(thd, $9,
(LEX_STRING*) 0,
TL_OPTION_UPDATING,
- TL_IGNORE))
+ TL_READ_NO_INSERT,
+ MDL_SHARED_NO_WRITE))
MYSQL_YYABORT;
}
;
@@ -14003,7 +14903,6 @@ udf_tail:
AGGREGATE_SYM remember_name FUNCTION_SYM ident
RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
if (is_native_function(thd, & $4))
{
@@ -14021,7 +14920,6 @@ udf_tail:
| remember_name FUNCTION_SYM ident
RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
if (is_native_function(thd, & $3))
{
@@ -14044,7 +14942,6 @@ sf_tail:
sp_name /* $3 */
'(' /* $4 */
{ /* $5 */
- THD *thd= YYTHD;
LEX *lex= thd->lex;
Lex_input_stream *lip= YYLIP;
sp_head *sp;
@@ -14087,7 +14984,7 @@ sf_tail:
lex->type= 0;
lex->vcol_info= 0;
}
- type /* $11 */
+ type_with_opt_collate /* $11 */
{ /* $12 */
LEX *lex= Lex;
sp_head *sp= lex->sphead;
@@ -14103,7 +15000,7 @@ sf_tail:
MYSQL_YYABORT;
}
- if (sp->fill_field_definition(YYTHD, lex,
+ if (sp->fill_field_definition(thd, lex,
(enum enum_field_types) $11,
&sp->m_return_field_def))
MYSQL_YYABORT;
@@ -14112,7 +15009,6 @@ sf_tail:
}
sp_c_chistics /* $13 */
{ /* $14 */
- THD *thd= YYTHD;
LEX *lex= thd->lex;
Lex_input_stream *lip= YYLIP;
@@ -14121,7 +15017,6 @@ sf_tail:
}
sp_proc_stmt /* $15 */
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
sp_head *sp= lex->sphead;
@@ -14175,7 +15070,7 @@ sf_tail:
;
sp_tail:
- PROCEDURE remember_name sp_name
+ PROCEDURE_SYM remember_name sp_name
{
LEX *lex= Lex;
sp_head *sp;
@@ -14192,10 +15087,10 @@ sp_tail:
sp= new sp_head();
if (sp == NULL)
MYSQL_YYABORT;
- sp->reset_thd_mem_root(YYTHD);
+ sp->reset_thd_mem_root(thd);
sp->init(lex);
sp->m_type= TYPE_ENUM_PROCEDURE;
- sp->init_sp_name(YYTHD, $3);
+ sp->init_sp_name(thd, $3);
lex->sphead= sp;
}
@@ -14210,7 +15105,6 @@ sp_tail:
sp_pdparam_list
')'
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
lex->sphead->m_param_end= YYLIP->get_cpp_tok_start();
@@ -14218,7 +15112,6 @@ sp_tail:
}
sp_c_chistics
{
- THD *thd= YYTHD;
LEX *lex= thd->lex;
lex->sphead->m_chistics= &lex->sp_chistics;
@@ -14229,9 +15122,9 @@ sp_tail:
LEX *lex= Lex;
sp_head *sp= lex->sphead;
- sp->set_stmt_end(YYTHD);
+ sp->set_stmt_end(thd);
lex->sql_command= SQLCOM_CREATE_PROCEDURE;
- sp->restore_thd_mem_root(YYTHD);
+ sp->restore_thd_mem_root(thd);
}
;
@@ -14268,21 +15161,21 @@ xid:
text_string
{
MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE);
- if (!(Lex->xid=(XID *)YYTHD->alloc(sizeof(XID))))
+ if (!(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 *)YYTHD->alloc(sizeof(XID))))
+ if (!(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 *)YYTHD->alloc(sizeof(XID))))
+ if (!(Lex->xid=(XID *)thd->alloc(sizeof(XID))))
MYSQL_YYABORT;
Lex->xid->set($5, $1->ptr(), $1->length(), $3->ptr(), $3->length());
}
@@ -14325,6 +15218,13 @@ install:
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_lex_str;
+ lex->ident= $3;
+ }
;
uninstall:
@@ -14334,6 +15234,13 @@ uninstall:
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_lex_str;
+ lex->ident= $3;
+ }
;
/* Avoid compiler warning from sql_yacc.cc where yyerrlab1 is not used */
diff --git a/sql/strfunc.cc b/sql/strfunc.cc
index 83da635d8c3..06bd92e0bc7 100644
--- a/sql/strfunc.cc
+++ b/sql/strfunc.cc
@@ -1,6 +1,4 @@
-/*
- Copyright (c) 2003-2007 MySQL AB, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,12 +11,17 @@
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
-*/
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
/* Some useful string utility functions used by the MySQL server */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "strfunc.h"
+#include "sql_class.h"
+#include "typelib.h" // TYPELIB
+#include "m_ctype.h" // my_charset_latin1
+#include "mysqld.h" // system_charset_info
/*
Return bitmap for strings used in a set
@@ -41,15 +44,14 @@
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, uint length, CHARSET_INFO *cs,
char **err_pos, uint *err_len, bool *set_warning)
{
CHARSET_INFO *strip= cs ? cs : &my_charset_latin1;
const char *end= str + strip->cset->lengthsp(strip, str, length);
ulonglong found= 0;
-
*err_pos= 0; // No error yet
+ *err_len= 0;
if (str != end)
{
const char *start= str;
@@ -76,18 +78,14 @@ ulonglong find_set(TYPELIB *lib, const char *str, uint length,
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)
+ if (!find && *err_len == 0) // report the first error with length > 0
{
- /* Report first error */
- if (!*err_pos)
- {
- *err_pos= (char*) start;
- *err_len= var_len;
- *set_warning= 1;
- }
+ *err_pos= (char*) start;
+ *err_len= var_len;
+ *set_warning= 1;
}
else
- found|= ((longlong) 1 << (find - 1));
+ found|= 1ULL << (find - 1);
if (pos >= end)
break;
start= pos + mblen;
@@ -96,214 +94,9 @@ ulonglong find_set(TYPELIB *lib, const char *str, uint length,
return found;
}
-
-static const char *on_off_default_names[]=
-{
- "off","on","default", NullS
-};
-
-static const unsigned int on_off_default_names_len[]=
-{
- sizeof("off") - 1,
- sizeof("on") - 1,
- sizeof("default") - 1
-};
-
-static TYPELIB on_off_default_typelib= {array_elements(on_off_default_names)-1,
- "", on_off_default_names,
- (unsigned int *)on_off_default_names_len};
-
-
-/*
- Parse a TYPELIB name from the buffer
-
- SYNOPSIS
- parse_name()
- lib Set of names to scan for.
- strpos INOUT Start of the buffer (updated to point to the next
- character after the name)
- end End of the buffer
- cs Charset used in the buffer
-
- DESCRIPTION
- Parse a TYPELIB name from the buffer. The buffer is assumed to contain
- one of the names specified in the TYPELIB, followed by comma, '=', or
- end of the buffer.
-
- RETURN
- 0 No matching name
- >0 Offset+1 in typelib for matched name
-*/
-
-static uint parse_name(TYPELIB *lib, const char **strpos, const char *end,
- CHARSET_INFO *cs)
-{
- const char *pos= *strpos;
- const char *start= pos;
-
- /* Find the length */
- if (cs && cs->mbminlen > 1)
- {
- int mblen= 0;
- for ( ; pos < end; pos+= mblen)
- {
- my_wc_t wc;
- if ((mblen= cs->cset->mb_wc(cs, &wc, (const uchar *) pos,
- (const uchar *) end)) < 1)
- mblen= 1; // Not to hang on a wrong multibyte sequence
- if (wc == (my_wc_t) '=' || wc == (my_wc_t) ',')
- break;
- }
- }
- else
- {
- for (; pos != end && *pos != '=' && *pos !=',' ; pos++)
- ;
- }
- uint var_len= (uint) (pos - start);
- /* Determine which flag it is */
- uint find= cs ? find_type2(lib, start, var_len, cs) :
- find_type(lib, start, var_len, (bool) 0);
- *strpos= pos;
- return find;
-}
-
-
-/* Read next character from the buffer in a charset-aware way */
-
-static my_wc_t get_next_char(const char **pos, const char *end, CHARSET_INFO *cs)
-{
- my_wc_t wc;
- if (*pos == end)
- return (my_wc_t)-1;
-
- if (cs && cs->mbminlen > 1)
- {
- int mblen;
- if ((mblen= cs->cset->mb_wc(cs, &wc, (const uchar *) *pos,
- (const uchar *) end)) < 1)
- mblen= 1; // Not to hang on a wrong multibyte sequence
- *pos += mblen;
- return wc;
- }
- else
- return *((*pos)++);
-}
-
-
-/*
- Parse and apply a set of flag assingments
-
- SYNOPSIS
- find_set_from_flags()
- lib Flag names
- default_name Number of "default" in the typelib
- cur_set Current set of flags (start from this state)
- default_set Default set of flags (use this for assign-default
- keyword and flag=default assignments)
- str String to be parsed
- length Length of the string
- cs String charset
- err_pos OUT If error, set to point to start of wrong set string
- NULL on success
- err_len OUT If error, set to the length of wrong set string
- set_warning OUT TRUE <=> Some string in set couldn't be used
-
- DESCRIPTION
- Parse a set of flag assignments, that is, parse a string in form:
-
- param_name1=value1,param_name2=value2,...
-
- where the names are specified in the TYPELIB, and each value can be
- either 'on','off', or 'default'. Setting the same name twice is not
- allowed.
-
- Besides param=val assignments, we support the "default" keyword (keyword
- #default_name in the typelib). It can be used one time, if specified it
- causes us to build the new set over the default_set rather than cur_set
- value.
-
- RETURN
- Parsed set value if (*errpos == NULL)
- Otherwise undefined
-*/
-
-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)
-{
- CHARSET_INFO *strip= cs ? cs : &my_charset_latin1;
- const char *end= str + strip->cset->lengthsp(strip, str, length);
- ulonglong flags_to_set= 0, flags_to_clear= 0;
- bool set_defaults= 0;
- *err_pos= 0; // No error yet
- if (str != end)
- {
- const char *start= str;
- for (;;)
- {
- const char *pos= start;
- uint flag_no, value;
-
- if (!(flag_no= parse_name(lib, &pos, end, cs)))
- goto err;
-
- if (flag_no == default_name)
- {
- /* Using 'default' twice isn't allowed. */
- if (set_defaults)
- goto err;
- set_defaults= TRUE;
- }
- else
- {
- ulonglong bit= ((longlong) 1 << (flag_no - 1));
- /* parse the '=on|off|default' */
- if ((flags_to_clear | flags_to_set) & bit ||
- get_next_char(&pos, end, cs) != '=' ||
- !(value= parse_name(&on_off_default_typelib, &pos, end, cs)))
- {
- goto err;
- }
-
- if (value == 1) // this is '=off'
- flags_to_clear|= bit;
- else if (value == 2) // this is '=on'
- flags_to_set|= bit;
- else // this is '=default'
- {
- if (default_set & bit)
- flags_to_set|= bit;
- else
- flags_to_clear|= bit;
- }
- }
- if (pos >= end)
- break;
-
- if (get_next_char(&pos, end, cs) != ',')
- goto err;
-
- start=pos;
- continue;
- err:
- *err_pos= (char*)start;
- *err_len= end - start;
- *set_warning= TRUE;
- break;
- }
- }
- ulonglong res= set_defaults? default_set : cur_set;
- res|= flags_to_set;
- res&= ~flags_to_clear;
- return res;
-}
-
-
/*
Function to find a string in a TYPELIB
- (Same format as mysys/typelib.c)
+ (similar to find_type() of mysys/typelib.c)
SYNOPSIS
find_type()
@@ -554,3 +347,60 @@ int find_string_in_array(LEX_STRING * const haystack, LEX_STRING * const needle,
}
return -1;
}
+
+
+char *set_to_string(THD *thd, LEX_STRING *result, ulonglong set,
+ const char *lib[])
+{
+ char buff[STRING_BUFFER_USUAL_SIZE*8];
+ String tmp(buff, sizeof(buff), &my_charset_latin1);
+ LEX_STRING unused;
+
+ if (!result)
+ result= &unused;
+
+ tmp.length(0);
+
+ for (uint i= 0; set; i++, set >>= 1)
+ if (set & 1) {
+ tmp.append(lib[i]);
+ tmp.append(',');
+ }
+
+ if (tmp.length())
+ {
+ result->str= thd->strmake(tmp.ptr(), tmp.length()-1);
+ result->length= tmp.length()-1;
+ }
+ else
+ {
+ result->str= const_cast<char*>("");
+ result->length= 0;
+ }
+ return result->str;
+}
+
+char *flagset_to_string(THD *thd, LEX_STRING *result, ulonglong set,
+ const char *lib[])
+{
+ char buff[STRING_BUFFER_USUAL_SIZE*8];
+ String tmp(buff, sizeof(buff), &my_charset_latin1);
+ LEX_STRING unused;
+
+ if (!result) result= &unused;
+
+ tmp.length(0);
+
+ // note that the last element is always "default", and it's ignored below
+ for (uint i= 0; lib[i+1]; i++, set >>= 1)
+ {
+ tmp.append(lib[i]);
+ tmp.append(set & 1 ? "=on," : "=off,");
+ }
+
+ result->str= thd->strmake(tmp.ptr(), tmp.length()-1);
+ result->length= tmp.length()-1;
+
+ return result->str;
+}
+
diff --git a/sql/strfunc.h b/sql/strfunc.h
new file mode 100644
index 00000000000..57c5427fcd0
--- /dev/null
+++ b/sql/strfunc.h
@@ -0,0 +1,49 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef 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,
+ 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,
+ bool part_match);
+uint find_type2(const TYPELIB *lib, const char *find, uint 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,
+ CHARSET_INFO * const cs);
+char *flagset_to_string(THD *thd, LEX_STRING *result, ulonglong set,
+ const char *lib[]);
+char *set_to_string(THD *thd, LEX_STRING *result, ulonglong set,
+ const char *lib[]);
+
+/*
+ These functions were protected by INNODB_COMPATIBILITY_HOOKS
+ */
+uint strconvert(CHARSET_INFO *from_cs, const char *from,
+ CHARSET_INFO *to_cs, char *to, uint to_length, uint *errors);
+
+#endif /* STRFUNC_INCLUDED */
diff --git a/sql/structs.h b/sql/structs.h
index c2bdf6db747..ae71819ae09 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -1,6 +1,7 @@
-/*
- Copyright (c) 2000-2008 MySQL AB, 2008-2010 Sun Microsystems, Inc.
- Use is subject to license terms.
+#ifndef STRUCTS_INCLUDED
+#define STRUCTS_INCLUDED
+
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,15 +14,24 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
/* The old structures from unireg */
-struct st_table;
+#include "sql_plugin.h" /* plugin_ref */
+#include "sql_const.h" /* MAX_REFLENGTH */
+#include "my_time.h" /* enum_mysql_timestamp_type */
+#include "thr_lock.h" /* thr_lock_type */
+#include "my_base.h" /* ha_rows, ha_key_alg */
+#include <mysql_com.h> /* USERNAME_LENGTH */
+
+struct TABLE;
class Field;
+class THD;
+
typedef struct st_date_time_format {
uchar positions[8];
char time_separator; /* Separator between hour and minute */
@@ -80,6 +90,9 @@ typedef struct st_key {
ulong flags; /* dupp key and pack flags */
uint key_parts; /* How many key_parts */
uint usable_key_parts; /* Should normally be = key_parts */
+ uint ext_key_parts; /* Number of key parts in extended key */
+ ulong ext_key_flags; /* Flags for extended key */
+ key_part_map ext_key_part_map; /* Bitmap of pk key parts in extension */
uint block_size;
uint name_length;
enum ha_key_alg algorithm;
@@ -105,7 +118,8 @@ typedef struct st_key {
union {
int bdb_return_if_eq;
} handler;
- struct st_table *table;
+ TABLE *table;
+ LEX_STRING comment;
/** reference to the list of options or NULL */
engine_option_value *option_list;
ha_index_option_struct *option_struct; /* structure with parsed options */
@@ -126,57 +140,6 @@ typedef struct st_reginfo { /* Extra info about reg */
} REGINFO;
-class SQL_SELECT;
-class THD;
-class handler;
-struct st_join_table;
-class Copy_field;
-
-/**
- A context for reading through a single table using a chosen access method:
- index read, scan, etc, use of cache, etc.
-
- Use by:
- READ_RECORD read_record;
- init_read_record(&read_record, ...);
- while (read_record.read_record())
- {
- ...
- }
- end_read_record();
-*/
-
-void rr_unlock_row(st_join_table *tab);
-
-struct READ_RECORD { /* Parameter to read_record */
- typedef int (*Read_func)(READ_RECORD*);
- typedef void (*Unlock_row_func)(st_join_table *);
- struct st_table *table; /* Head-form */
- // handler *file_;
- struct st_table **forms; /* head and ref forms */
-
- Read_func read_record;
- Unlock_row_func unlock_row;
- THD *thd;
- SQL_SELECT *select;
- uint cache_records;
- uint ref_length,struct_length,reclength,rec_cache_size,error_offset;
- uint index;
- 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;
- IO_CACHE *io_cache;
- bool print_error, ignore_not_found_rows;
- /*
- SJ-Materialization runtime may need to read fields from the materialized
- table and unpack them into original table fields:
- */
- Copy_field *copy_field;
- Copy_field *copy_field_end;
-};
-
-
/*
Originally MySQL used MYSQL_TIME structure inside server only, but since
4.1 it's exported to user in the new client API. Define aliases for
@@ -200,8 +163,6 @@ typedef struct st_known_date_time_format {
const char *time_format;
} KNOWN_DATE_TIME_FORMAT;
-enum SHOW_COMP_OPTION { SHOW_OPTION_YES, SHOW_OPTION_NO, SHOW_OPTION_DISABLED};
-
extern const char *show_comp_option_name[];
typedef int *(*update_var)(THD *, struct st_mysql_show_var *);
@@ -526,3 +487,5 @@ public:
Discrete_interval* get_tail() const { return tail; };
Discrete_interval* get_current() const { return current; };
};
+
+#endif /* STRUCTS_INCLUDED */
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
new file mode 100644
index 00000000000..5c2cabf7c2e
--- /dev/null
+++ b/sql/sys_vars.cc
@@ -0,0 +1,3935 @@
+/* Copyright (c) 2002, 2014, Oracle and/or its affiliates.
+ Copyright (c) 2012, 2014, SkySQL Ab.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ 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 */
+
+/*
+ How to add new variables:
+
+ 1. copy one of the existing variables, and edit the declaration.
+ 2. if you need special behavior on assignment or additional checks
+ use ON_CHECK and ON_UPDATE callbacks.
+ 3. *Don't* add new Sys_var classes or uncle Occam will come
+ with his razor to haunt you at nights
+
+ Note - all storage engine variables (for example myisam_whatever)
+ should go into the corresponding storage engine sources
+ (for example in storage/myisam/ha_myisam.cc) !
+*/
+
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_priv.h"
+#include "sql_class.h" // set_var.h: THD
+#include "sys_vars.h"
+
+#include "events.h"
+#include <thr_alarm.h>
+#include "slave.h"
+#include "rpl_mi.h"
+#include "transaction.h"
+#include "mysqld.h"
+#include "lock.h"
+#include "sql_time.h" // known_date_time_formats
+#include "sql_acl.h" // SUPER_ACL,
+ // mysql_user_table_is_in_short_password_format
+#include "derror.h" // read_texts
+#include "sql_base.h" // close_cached_tables
+#include <myisam.h>
+#include "log_slow.h"
+#include "debug_sync.h" // DEBUG_SYNC
+#include "sql_show.h"
+
+#include "log_event.h"
+#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
+#include "../storage/perfschema/pfs_server.h"
+#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
+#include "threadpool.h"
+
+/*
+ The rule for this file: everything should be 'static'. When a sys_var
+ variable or a function from this file is - in very rare cases - needed
+ elsewhere it should be explicitly declared 'export' here to show that it's
+ not a mistakenly forgotten 'static' keyword.
+*/
+#define export /* not static */
+
+#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
+
+static Sys_var_mybool Sys_pfs_enabled(
+ "performance_schema",
+ "Enable the performance schema.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_enabled),
+ CMD_LINE(OPT_ARG), DEFAULT(FALSE));
+
+static Sys_var_ulong Sys_pfs_events_waits_history_long_size(
+ "performance_schema_events_waits_history_long_size",
+ "Number of rows in EVENTS_WAITS_HISTORY_LONG.",
+ PARSED_EARLY READ_ONLY
+ GLOBAL_VAR(pfs_param.m_events_waits_history_long_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024),
+ DEFAULT(PFS_WAITS_HISTORY_LONG_SIZE), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_pfs_events_waits_history_size(
+ "performance_schema_events_waits_history_size",
+ "Number of rows per thread in EVENTS_WAITS_HISTORY.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_events_waits_history_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024),
+ DEFAULT(PFS_WAITS_HISTORY_SIZE), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_pfs_max_cond_classes(
+ "performance_schema_max_cond_classes",
+ "Maximum number of condition instruments.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_cond_class_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 256),
+ DEFAULT(PFS_MAX_COND_CLASS), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_pfs_max_cond_instances(
+ "performance_schema_max_cond_instances",
+ "Maximum number of instrumented condition objects.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_cond_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024),
+ DEFAULT(PFS_MAX_COND), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_pfs_max_file_classes(
+ "performance_schema_max_file_classes",
+ "Maximum number of file instruments.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_file_class_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 256),
+ DEFAULT(PFS_MAX_FILE_CLASS), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_pfs_max_file_handles(
+ "performance_schema_max_file_handles",
+ "Maximum number of opened instrumented files.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_file_handle_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024),
+ DEFAULT(PFS_MAX_FILE_HANDLE), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_pfs_max_file_instances(
+ "performance_schema_max_file_instances",
+ "Maximum number of instrumented files.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_file_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024),
+ DEFAULT(PFS_MAX_FILE), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_pfs_max_mutex_classes(
+ "performance_schema_max_mutex_classes",
+ "Maximum number of mutex instruments.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_mutex_class_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 256),
+ DEFAULT(PFS_MAX_MUTEX_CLASS), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_pfs_max_mutex_instances(
+ "performance_schema_max_mutex_instances",
+ "Maximum number of instrumented MUTEX objects.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_mutex_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 100*1024*1024),
+ DEFAULT(PFS_MAX_MUTEX), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_pfs_max_rwlock_classes(
+ "performance_schema_max_rwlock_classes",
+ "Maximum number of rwlock instruments.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_rwlock_class_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 256),
+ DEFAULT(PFS_MAX_RWLOCK_CLASS), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_pfs_max_rwlock_instances(
+ "performance_schema_max_rwlock_instances",
+ "Maximum number of instrumented RWLOCK objects.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_rwlock_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 100*1024*1024),
+ DEFAULT(PFS_MAX_RWLOCK), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_pfs_max_table_handles(
+ "performance_schema_max_table_handles",
+ "Maximum number of opened instrumented tables.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_table_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024),
+ DEFAULT(PFS_MAX_TABLE), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_pfs_max_table_instances(
+ "performance_schema_max_table_instances",
+ "Maximum number of instrumented tables.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_table_share_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024),
+ DEFAULT(PFS_MAX_TABLE_SHARE), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_pfs_max_thread_classes(
+ "performance_schema_max_thread_classes",
+ "Maximum number of thread instruments.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_thread_class_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 256),
+ DEFAULT(PFS_MAX_THREAD_CLASS), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_pfs_max_thread_instances(
+ "performance_schema_max_thread_instances",
+ "Maximum number of instrumented threads.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_thread_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024),
+ DEFAULT(PFS_MAX_THREAD), BLOCK_SIZE(1));
+
+#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
+
+static Sys_var_ulong Sys_auto_increment_increment(
+ "auto_increment_increment",
+ "Auto-increment columns are incremented by this",
+ SESSION_VAR(auto_increment_increment),
+ CMD_LINE(OPT_ARG),
+ VALID_RANGE(1, 65535), DEFAULT(1), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, IN_BINLOG);
+
+static Sys_var_ulong Sys_auto_increment_offset(
+ "auto_increment_offset",
+ "Offset added to Auto-increment columns. Used when "
+ "auto-increment-increment != 1",
+ SESSION_VAR(auto_increment_offset),
+ CMD_LINE(OPT_ARG),
+ VALID_RANGE(1, 65535), DEFAULT(1), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, IN_BINLOG);
+
+static Sys_var_mybool Sys_automatic_sp_privileges(
+ "automatic_sp_privileges",
+ "Creating and dropping stored procedures alters ACLs",
+ GLOBAL_VAR(sp_automatic_privileges),
+ CMD_LINE(OPT_ARG), DEFAULT(TRUE));
+
+static Sys_var_ulong Sys_back_log(
+ "back_log", "The number of outstanding connection requests "
+ "MySQL can have. This comes into play when the main MySQL thread "
+ "gets very many connection requests in a very short time",
+ READ_ONLY GLOBAL_VAR(back_log), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, 65535), DEFAULT(50), BLOCK_SIZE(1));
+
+static Sys_var_charptr Sys_basedir(
+ "basedir", "Path to installation directory. All paths are "
+ "usually resolved relative to this",
+ READ_ONLY GLOBAL_VAR(mysql_home_ptr), CMD_LINE(REQUIRED_ARG, 'b'),
+ IN_FS_CHARSET, DEFAULT(0));
+
+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. "
+ "If you often use transactions containing many statements, "
+ "you can increase this to get more performance",
+ GLOBAL_VAR(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_stmt_cache_size(
+ "binlog_stmt_cache_size", "The size of the statement cache for "
+ "updates to non-transactional engines for the binary log. "
+ "If you often use statements updating a great number of rows, "
+ "you can increase this to get more performance",
+ GLOBAL_VAR(binlog_stmt_cache_size),
+ CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(IO_SIZE, SIZE_T_MAX), DEFAULT(32768), BLOCK_SIZE(IO_SIZE));
+
+/*
+ Some variables like @sql_log_bin and @binlog_format change how/if binlogging
+ is done. We must not change them inside a running transaction or statement,
+ otherwise the event group eventually written to the binlog may become
+ incomplete or otherwise garbled.
+
+ This function does the appropriate check.
+
+ It returns true if an error is caused by incorrect usage, false if ok.
+*/
+static bool
+error_if_in_trans_or_substatement(THD *thd, int in_substatement_error,
+ int in_transaction_error)
+{
+ if (thd->in_sub_stmt)
+ {
+ my_error(in_substatement_error, MYF(0));
+ return true;
+ }
+
+ if (thd->in_active_multi_stmt_transaction())
+ {
+ my_error(in_transaction_error, MYF(0));
+ return true;
+ }
+
+ return false;
+}
+
+static bool check_has_super(sys_var *self, THD *thd, set_var *var)
+{
+ DBUG_ASSERT(self->scope() != sys_var::GLOBAL);// don't abuse check_has_super()
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (!(thd->security_ctx->master_access & SUPER_ACL))
+ {
+ my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
+ return true;
+ }
+#endif
+ return false;
+}
+static bool binlog_format_check(sys_var *self, THD *thd, set_var *var)
+{
+ if (check_has_super(self, thd, var))
+ return true;
+
+ if (var->type == OPT_GLOBAL)
+ return false;
+
+ /*
+ If RBR and open temporary tables, their CREATE TABLE may not be in the
+ binlog, so we can't toggle to SBR in this connection.
+
+ If binlog_format=MIXED, there are open temporary tables, and an unsafe
+ statement is executed, then subsequent statements are logged in row
+ format and hence changes to temporary tables may be lost. So we forbid
+ switching @@SESSION.binlog_format from MIXED to STATEMENT when there are
+ open temp tables and we are logging in row format.
+ */
+ if (thd->temporary_tables && var->type == OPT_SESSION &&
+ var->save_result.ulonglong_value == BINLOG_FORMAT_STMT &&
+ ((thd->variables.binlog_format == BINLOG_FORMAT_MIXED &&
+ thd->is_current_stmt_binlog_format_row()) ||
+ thd->variables.binlog_format == BINLOG_FORMAT_ROW))
+ {
+ my_error(ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR, MYF(0));
+ return true;
+ }
+
+ if (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;
+}
+
+static bool fix_binlog_format_after_update(sys_var *self, THD *thd,
+ enum_var_type type)
+{
+ if (type == OPT_SESSION)
+ thd->reset_current_stmt_binlog_format_row();
+ return false;
+}
+
+static Sys_var_enum Sys_binlog_format(
+ "binlog_format", "What form of binary logging the master will "
+ "use: either ROW for row-based binary logging, STATEMENT "
+ "for statement-based binary logging, or MIXED. MIXED is statement-"
+ "based binary logging except for those statements where only row-"
+ "based is correct: those which involve user-defined functions (i.e. "
+ "UDFs) or the UUID() function; for those, row-based binary logging is "
+ "automatically used. If NDBCLUSTER is enabled and binlog-format is "
+ "MIXED, the format switches to row-based and back implicitly per each "
+ "query accessing an NDBCLUSTER table",
+ SESSION_VAR(binlog_format), CMD_LINE(REQUIRED_ARG, OPT_BINLOG_FORMAT),
+ binlog_format_names, DEFAULT(BINLOG_FORMAT_STMT),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(binlog_format_check),
+ ON_UPDATE(fix_binlog_format_after_update));
+
+static bool binlog_direct_check(sys_var *self, THD *thd, set_var *var)
+{
+ if (check_has_super(self, thd, var))
+ return true;
+
+ if (var->type == OPT_GLOBAL)
+ return false;
+
+ if (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;
+}
+
+static Sys_var_mybool Sys_binlog_direct(
+ "binlog_direct_non_transactional_updates",
+ "Causes updates to non-transactional engines using statement format to "
+ "be written directly to binary log. Before using this option make sure "
+ "that there are no dependencies between transactional and "
+ "non-transactional tables such as in the statement INSERT INTO t_myisam "
+ "SELECT * FROM t_innodb; otherwise, slaves may diverge from the master.",
+ SESSION_VAR(binlog_direct_non_trans_update),
+ CMD_LINE(OPT_ARG), DEFAULT(FALSE),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(binlog_direct_check));
+
+static Sys_var_ulonglong Sys_bulk_insert_buff_size(
+ "bulk_insert_buffer_size", "Size of tree cache used in bulk "
+ "insert optimisation. Note that this is a limit per thread!",
+ SESSION_VAR(bulk_insert_buff_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, SIZE_T_MAX), DEFAULT(8192*1024), BLOCK_SIZE(1));
+
+static Sys_var_charptr Sys_character_sets_dir(
+ "character_sets_dir", "Directory where character sets are",
+ READ_ONLY GLOBAL_VAR(charsets_dir), CMD_LINE(REQUIRED_ARG),
+ IN_FS_CHARSET, DEFAULT(0));
+
+static bool check_not_null(sys_var *self, THD *thd, set_var *var)
+{
+ return var->value && var->value->is_null();
+}
+static bool check_charset(sys_var *self, THD *thd, set_var *var)
+{
+ if (!var->value)
+ return false;
+
+ char buff[STRING_BUFFER_USUAL_SIZE];
+ if (var->value->result_type() == STRING_RESULT)
+ {
+ String str(buff, sizeof(buff), system_charset_info), *res;
+ if (!(res=var->value->val_str(&str)))
+ var->save_result.ptr= NULL;
+ else if (!(var->save_result.ptr= get_charset_by_csname(res->c_ptr(),
+ MY_CS_PRIMARY,
+ MYF(0))) &&
+ !(var->save_result.ptr=get_old_charset_by_name(res->c_ptr())))
+ {
+ ErrConvString err(res);
+ my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), err.ptr());
+ return true;
+ }
+ }
+ else // INT_RESULT
+ {
+ int csno= (int)var->value->val_int();
+ if (!(var->save_result.ptr= get_charset(csno, MYF(0))))
+ {
+ my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), llstr(csno, buff));
+ return true;
+ }
+ }
+ return false;
+}
+static bool check_charset_not_null(sys_var *self, THD *thd, set_var *var)
+{
+ return check_charset(self, thd, var) || check_not_null(self, thd, var);
+}
+static Sys_var_struct Sys_character_set_system(
+ "character_set_system", "The character set used by the server "
+ "for storing identifiers",
+ READ_ONLY GLOBAL_VAR(system_charset_info), NO_CMD_LINE,
+ offsetof(CHARSET_INFO, csname), DEFAULT(0));
+
+static Sys_var_struct Sys_character_set_server(
+ "character_set_server", "The default character set",
+ SESSION_VAR(collation_server), NO_CMD_LINE,
+ offsetof(CHARSET_INFO, csname), DEFAULT(&default_charset_info),
+ NO_MUTEX_GUARD, IN_BINLOG, ON_CHECK(check_charset_not_null));
+
+static bool check_charset_db(sys_var *self, THD *thd, set_var *var)
+{
+ if (check_charset_not_null(self, thd, var))
+ return true;
+ if (!var->value) // = DEFAULT
+ var->save_result.ptr= thd->db_charset;
+ return false;
+}
+static Sys_var_struct Sys_character_set_database(
+ "character_set_database",
+ " The character set used by the default database",
+ SESSION_VAR(collation_database), NO_CMD_LINE,
+ offsetof(CHARSET_INFO, csname), DEFAULT(&default_charset_info),
+ NO_MUTEX_GUARD, IN_BINLOG, ON_CHECK(check_charset_db));
+
+static bool check_cs_client(sys_var *self, THD *thd, set_var *var)
+{
+ if (check_charset_not_null(self, thd, var))
+ return true;
+
+ // Currently, UCS-2 cannot be used as a client character set
+ if (((CHARSET_INFO *)(var->save_result.ptr))->mbminlen > 1)
+ return true;
+
+ return false;
+}
+static bool fix_thd_charset(sys_var *self, THD *thd, enum_var_type type)
+{
+ if (type == OPT_SESSION)
+ thd->update_charset();
+ return false;
+}
+static Sys_var_struct Sys_character_set_client(
+ "character_set_client", "The character set for statements "
+ "that arrive from the client",
+ SESSION_VAR(character_set_client), NO_CMD_LINE,
+ offsetof(CHARSET_INFO, csname), DEFAULT(&default_charset_info),
+ NO_MUTEX_GUARD, IN_BINLOG, ON_CHECK(check_cs_client),
+ ON_UPDATE(fix_thd_charset));
+
+static Sys_var_struct Sys_character_set_connection(
+ "character_set_connection", "The character set used for "
+ "literals that do not have a character set introducer and for "
+ "number-to-string conversion",
+ SESSION_VAR(collation_connection), NO_CMD_LINE,
+ offsetof(CHARSET_INFO, csname), DEFAULT(&default_charset_info),
+ NO_MUTEX_GUARD, IN_BINLOG, ON_CHECK(check_charset_not_null),
+ ON_UPDATE(fix_thd_charset));
+
+static Sys_var_struct Sys_character_set_results(
+ "character_set_results", "The character set used for returning "
+ "query results to the client",
+ SESSION_VAR(character_set_results), NO_CMD_LINE,
+ offsetof(CHARSET_INFO, csname), DEFAULT(&default_charset_info),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_charset));
+
+static Sys_var_struct Sys_character_set_filesystem(
+ "character_set_filesystem", "The filesystem character set",
+ SESSION_VAR(character_set_filesystem), NO_CMD_LINE,
+ offsetof(CHARSET_INFO, csname), DEFAULT(&character_set_filesystem),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_charset_not_null),
+ ON_UPDATE(fix_thd_charset));
+
+static const char *completion_type_names[]= {"NO_CHAIN", "CHAIN", "RELEASE", 0};
+static Sys_var_enum Sys_completion_type(
+ "completion_type", "The transaction completion type, one of "
+ "NO_CHAIN, CHAIN, RELEASE",
+ SESSION_VAR(completion_type), CMD_LINE(REQUIRED_ARG),
+ completion_type_names, DEFAULT(0));
+
+static bool check_collation_not_null(sys_var *self, THD *thd, set_var *var)
+{
+ if (!var->value)
+ return false;
+
+ char buff[STRING_BUFFER_USUAL_SIZE];
+ if (var->value->result_type() == STRING_RESULT)
+ {
+ String str(buff, sizeof(buff), system_charset_info), *res;
+ if (!(res= var->value->val_str(&str)))
+ var->save_result.ptr= NULL;
+ else if (!(var->save_result.ptr= get_charset_by_name(res->c_ptr(), MYF(0))))
+ {
+ ErrConvString err(res);
+ my_error(ER_UNKNOWN_COLLATION, MYF(0), err.ptr());
+ return true;
+ }
+ }
+ else // INT_RESULT
+ {
+ int csno= (int)var->value->val_int();
+ if (!(var->save_result.ptr= get_charset(csno, MYF(0))))
+ {
+ my_error(ER_UNKNOWN_COLLATION, MYF(0), llstr(csno, buff));
+ return true;
+ }
+ }
+ return check_not_null(self, thd, var);
+}
+static Sys_var_struct Sys_collation_connection(
+ "collation_connection", "The collation of the connection "
+ "character set",
+ SESSION_VAR(collation_connection), NO_CMD_LINE,
+ offsetof(CHARSET_INFO, name), DEFAULT(&default_charset_info),
+ NO_MUTEX_GUARD, IN_BINLOG, ON_CHECK(check_collation_not_null),
+ ON_UPDATE(fix_thd_charset));
+
+static bool check_collation_db(sys_var *self, THD *thd, set_var *var)
+{
+ if (check_collation_not_null(self, thd, var))
+ return true;
+ if (!var->value) // = DEFAULT
+ var->save_result.ptr= thd->db_charset;
+ return false;
+}
+static Sys_var_struct Sys_collation_database(
+ "collation_database", "The collation of the database "
+ "character set",
+ SESSION_VAR(collation_database), NO_CMD_LINE,
+ offsetof(CHARSET_INFO, name), DEFAULT(&default_charset_info),
+ NO_MUTEX_GUARD, IN_BINLOG, ON_CHECK(check_collation_db));
+
+static Sys_var_struct Sys_collation_server(
+ "collation_server", "The server default collation",
+ SESSION_VAR(collation_server), NO_CMD_LINE,
+ offsetof(CHARSET_INFO, name), DEFAULT(&default_charset_info),
+ NO_MUTEX_GUARD, IN_BINLOG, ON_CHECK(check_collation_not_null));
+
+static const char *concurrent_insert_names[]= {"NEVER", "AUTO", "ALWAYS", 0};
+static Sys_var_enum Sys_concurrent_insert(
+ "concurrent_insert", "Use concurrent insert with MyISAM. Possible "
+ "values are NEVER, AUTO, ALWAYS",
+ GLOBAL_VAR(myisam_concurrent_insert), CMD_LINE(OPT_ARG),
+ concurrent_insert_names, DEFAULT(1));
+
+static Sys_var_ulong Sys_connect_timeout(
+ "connect_timeout",
+ "The number of seconds the mysqld server is waiting for a connect "
+ "packet before responding with 'Bad handshake'",
+ GLOBAL_VAR(connect_timeout), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(2, LONG_TIMEOUT), DEFAULT(CONNECT_TIMEOUT), BLOCK_SIZE(1));
+
+static Sys_var_charptr Sys_datadir(
+ "datadir", "Path to the database root directory",
+ READ_ONLY GLOBAL_VAR(mysql_real_data_home_ptr),
+ CMD_LINE(REQUIRED_ARG, 'h'), IN_FS_CHARSET, DEFAULT(mysql_real_data_home));
+
+#ifndef DBUG_OFF
+static Sys_var_dbug Sys_dbug(
+ "debug", "Built-in DBUG debugger", sys_var::SESSION,
+ CMD_LINE(OPT_ARG, '#'), DEFAULT(""), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(check_has_super), ON_UPDATE(0),
+ DEPRECATED("'@@debug_dbug'"));
+
+static Sys_var_dbug Sys_debug_dbug(
+ "debug_dbug", "Built-in DBUG debugger", sys_var::SESSION,
+ CMD_LINE(OPT_ARG, '#'), DEFAULT(""), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(check_has_super));
+#endif
+
+/**
+ @todo
+ When updating myisam_delay_key_write, we should do a 'flush tables'
+ of all MyISAM tables to ensure that they are reopen with the
+ new attribute.
+*/
+export bool fix_delay_key_write(sys_var *self, THD *thd, enum_var_type type)
+{
+ switch (delay_key_write_options) {
+ case DELAY_KEY_WRITE_NONE:
+ myisam_delay_key_write=0;
+ ha_open_options&= ~HA_OPEN_DELAY_KEY_WRITE;
+ break;
+ case DELAY_KEY_WRITE_ON:
+ myisam_delay_key_write=1;
+ ha_open_options&= ~HA_OPEN_DELAY_KEY_WRITE;
+ break;
+ case DELAY_KEY_WRITE_ALL:
+ myisam_delay_key_write=1;
+ ha_open_options|= HA_OPEN_DELAY_KEY_WRITE;
+ break;
+ }
+#ifdef WITH_ARIA_STORAGE_ENGINE
+ maria_delay_key_write= myisam_delay_key_write;
+#endif
+ return false;
+}
+static const char *delay_key_write_names[]= { "OFF", "ON", "ALL", NullS };
+static Sys_var_enum Sys_delay_key_write(
+ "delay_key_write", "Type of DELAY_KEY_WRITE",
+ GLOBAL_VAR(delay_key_write_options), CMD_LINE(OPT_ARG),
+ delay_key_write_names, DEFAULT(DELAY_KEY_WRITE_ON),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_delay_key_write));
+
+static Sys_var_ulong Sys_delayed_insert_limit(
+ "delayed_insert_limit",
+ "After inserting delayed_insert_limit rows, the INSERT DELAYED "
+ "handler will check if there are any SELECT statements pending. "
+ "If so, it allows these to execute before continuing",
+ GLOBAL_VAR(delayed_insert_limit), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, UINT_MAX), DEFAULT(DELAYED_LIMIT), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_delayed_insert_timeout(
+ "delayed_insert_timeout",
+ "How long a INSERT DELAYED thread should wait for INSERT statements "
+ "before terminating",
+ GLOBAL_VAR(delayed_insert_timeout), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, LONG_TIMEOUT), DEFAULT(DELAYED_WAIT_TIMEOUT),
+ BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_delayed_queue_size(
+ "delayed_queue_size",
+ "What size queue (in rows) should be allocated for handling INSERT "
+ "DELAYED. If the queue becomes full, any client that does INSERT "
+ "DELAYED will wait until there is room in the queue again",
+ GLOBAL_VAR(delayed_queue_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, UINT_MAX), DEFAULT(DELAYED_QUEUE_SIZE), BLOCK_SIZE(1));
+
+#ifdef HAVE_EVENT_SCHEDULER
+static const char *event_scheduler_names[]= { "OFF", "ON", "DISABLED", NullS };
+static bool event_scheduler_check(sys_var *self, THD *thd, set_var *var)
+{
+ /* DISABLED is only accepted on the command line */
+ if (var->save_result.ulonglong_value == Events::EVENTS_DISABLED)
+ return true;
+ /*
+ If the scheduler was disabled because there are no/bad
+ system tables, produce a more meaningful error message
+ than ER_OPTION_PREVENTS_STATEMENT
+ */
+ if (Events::check_if_system_tables_error())
+ return true;
+ if (Events::opt_event_scheduler == Events::EVENTS_DISABLED)
+ {
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
+ "--event-scheduler=DISABLED or --skip-grant-tables");
+ return true;
+ }
+ return false;
+}
+static bool event_scheduler_update(sys_var *self, THD *thd, enum_var_type type)
+{
+ int err_no= 0;
+ uint opt_event_scheduler_value= Events::opt_event_scheduler;
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ /*
+ Events::start() is heavyweight. In particular it creates a new THD,
+ which takes LOCK_global_system_variables internally.
+ Thus we have to release it here.
+ We need to re-take it before returning, though.
+
+ Note that since we release LOCK_global_system_variables before calling
+ start/stop, there is a possibility that the server variable
+ can become out of sync with the real event scheduler state.
+
+ This can happen with two concurrent statments if the first gets
+ interrupted after start/stop but before retaking
+ LOCK_global_system_variables. However, this problem should be quite
+ rare and it's difficult to avoid it without opening up possibilities
+ for deadlocks. See bug#51160.
+ */
+ bool ret= opt_event_scheduler_value == Events::EVENTS_ON
+ ? Events::start(&err_no)
+ : Events::stop();
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ if (ret)
+ {
+ Events::opt_event_scheduler= Events::EVENTS_OFF;
+ my_error(ER_EVENT_SET_VAR_ERROR, MYF(0), err_no);
+ }
+ return ret;
+}
+
+static Sys_var_enum Sys_event_scheduler(
+ "event_scheduler", "Enable the event scheduler. Possible values are "
+ "ON, OFF, and DISABLED (keep the event scheduler completely "
+ "deactivated, it cannot be activated run-time)",
+ GLOBAL_VAR(Events::opt_event_scheduler), CMD_LINE(OPT_ARG),
+ event_scheduler_names, DEFAULT(Events::EVENTS_OFF),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(event_scheduler_check), ON_UPDATE(event_scheduler_update));
+#endif
+
+static Sys_var_ulong Sys_expire_logs_days(
+ "expire_logs_days",
+ "If non-zero, binary logs will be purged after expire_logs_days "
+ "days; possible purges happen at startup and at binary log rotation",
+ GLOBAL_VAR(expire_logs_days),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 99), DEFAULT(0), BLOCK_SIZE(1));
+
+static Sys_var_mybool Sys_flush(
+ "flush", "Flush MyISAM tables to disk between SQL commands",
+ GLOBAL_VAR(myisam_flush),
+ CMD_LINE(OPT_ARG), DEFAULT(FALSE));
+
+static Sys_var_ulong Sys_flush_time(
+ "flush_time",
+ "A dedicated thread is created to flush all tables at the "
+ "given interval",
+ GLOBAL_VAR(flush_time),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, LONG_TIMEOUT),
+ DEFAULT(0), BLOCK_SIZE(1));
+
+static bool check_ftb_syntax(sys_var *self, THD *thd, set_var *var)
+{
+ return ft_boolean_check_syntax_string((uchar*)
+ (var->save_result.string_value.str));
+}
+static bool query_cache_flush(sys_var *self, THD *thd, enum_var_type type)
+{
+#ifdef HAVE_QUERY_CACHE
+ query_cache.flush();
+#endif /* HAVE_QUERY_CACHE */
+ return false;
+}
+/// @todo make SESSION_VAR (usability enhancement and a fix for a race condition)
+static Sys_var_charptr Sys_ft_boolean_syntax(
+ "ft_boolean_syntax", "List of operators for "
+ "MATCH ... AGAINST ( ... IN BOOLEAN MODE)",
+ GLOBAL_VAR(ft_boolean_syntax),
+ CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET,
+ DEFAULT(DEFAULT_FTB_SYNTAX), NO_MUTEX_GUARD,
+ NOT_IN_BINLOG, ON_CHECK(check_ftb_syntax), ON_UPDATE(query_cache_flush));
+
+static Sys_var_ulong Sys_ft_max_word_len(
+ "ft_max_word_len",
+ "The maximum length of the word to be included in a FULLTEXT index. "
+ "Note: FULLTEXT indexes must be rebuilt after changing this variable",
+ READ_ONLY GLOBAL_VAR(ft_max_word_len), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(10, HA_FT_MAXCHARLEN), DEFAULT(HA_FT_MAXCHARLEN),
+ BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_ft_min_word_len(
+ "ft_min_word_len",
+ "The minimum length of the word to be included in a FULLTEXT index. "
+ "Note: FULLTEXT indexes must be rebuilt after changing this variable",
+ READ_ONLY GLOBAL_VAR(ft_min_word_len), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, HA_FT_MAXCHARLEN), DEFAULT(4), BLOCK_SIZE(1));
+
+/// @todo make it an updatable SESSION_VAR
+static Sys_var_ulong Sys_ft_query_expansion_limit(
+ "ft_query_expansion_limit",
+ "Number of best matches to use for query expansion",
+ READ_ONLY GLOBAL_VAR(ft_query_expansion_limit),
+ CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, 1000), DEFAULT(20), BLOCK_SIZE(1));
+
+static Sys_var_charptr Sys_ft_stopword_file(
+ "ft_stopword_file",
+ "Use stopwords from this file instead of built-in list",
+ READ_ONLY GLOBAL_VAR(ft_stopword_file), CMD_LINE(REQUIRED_ARG),
+ IN_FS_CHARSET, DEFAULT(0));
+
+static Sys_var_mybool Sys_ignore_builtin_innodb(
+ "ignore_builtin_innodb",
+ "Disable initialization of builtin InnoDB plugin",
+ READ_ONLY GLOBAL_VAR(opt_ignore_builtin_innodb),
+ CMD_LINE(OPT_ARG), DEFAULT(FALSE));
+
+static bool check_init_string(sys_var *self, THD *thd, set_var *var)
+{
+ if (var->save_result.string_value.str == 0)
+ {
+ var->save_result.string_value.str= const_cast<char*>("");
+ var->save_result.string_value.length= 0;
+ }
+ return false;
+}
+static PolyLock_rwlock PLock_sys_init_connect(&LOCK_sys_init_connect);
+static Sys_var_lexstring Sys_init_connect(
+ "init_connect", "Command(s) that are executed for each "
+ "new connection (unless the user has SUPER privilege)",
+ GLOBAL_VAR(opt_init_connect), CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET,
+ DEFAULT(""), &PLock_sys_init_connect, NOT_IN_BINLOG,
+ ON_CHECK(check_init_string));
+
+static Sys_var_charptr Sys_init_file(
+ "init_file", "Read SQL commands from this file at startup",
+ READ_ONLY GLOBAL_VAR(opt_init_file),
+#ifdef DISABLE_GRANT_OPTIONS
+ NO_CMD_LINE,
+#else
+ CMD_LINE(REQUIRED_ARG),
+#endif
+ IN_FS_CHARSET, DEFAULT(0));
+
+static PolyLock_rwlock PLock_sys_init_slave(&LOCK_sys_init_slave);
+static Sys_var_lexstring Sys_init_slave(
+ "init_slave", "Command(s) that are executed by a slave server "
+ "each time the SQL thread starts", GLOBAL_VAR(opt_init_slave),
+ CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET,
+ DEFAULT(""), &PLock_sys_init_slave,
+ NOT_IN_BINLOG, ON_CHECK(check_init_string));
+
+static Sys_var_ulong Sys_interactive_timeout(
+ "interactive_timeout",
+ "The number of seconds the server waits for activity on an interactive "
+ "connection before closing it",
+ SESSION_VAR(net_interactive_timeout),
+ CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, LONG_TIMEOUT), DEFAULT(NET_WAIT_TIMEOUT), BLOCK_SIZE(1));
+
+static Sys_var_ulonglong Sys_join_buffer_size(
+ "join_buffer_size",
+ "The size of the buffer that is used for joins",
+ SESSION_VAR(join_buff_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(128, SIZE_T_MAX), DEFAULT(128*1024), BLOCK_SIZE(128));
+
+static Sys_var_keycache Sys_key_buffer_size(
+ "key_buffer_size", "The size of the buffer used for "
+ "index blocks for MyISAM tables. Increase this to get better index "
+ "handling (for all reads and multiple writes) to as much as you can "
+ "afford",
+ KEYCACHE_VAR(param_buff_size),
+ CMD_LINE(REQUIRED_ARG, OPT_KEY_BUFFER_SIZE),
+ VALID_RANGE(0, SIZE_T_MAX), DEFAULT(KEY_CACHE_SIZE),
+ BLOCK_SIZE(IO_SIZE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(update_buffer_size));
+
+static Sys_var_keycache Sys_key_cache_block_size(
+ "key_cache_block_size", "The default size of key cache blocks",
+ KEYCACHE_VAR(param_block_size),
+ CMD_LINE(REQUIRED_ARG, OPT_KEY_CACHE_BLOCK_SIZE),
+ VALID_RANGE(512, 1024*16), DEFAULT(KEY_CACHE_BLOCK_SIZE),
+ BLOCK_SIZE(512), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(resize_keycache));
+
+static Sys_var_keycache Sys_key_cache_division_limit(
+ "key_cache_division_limit",
+ "The minimum percentage of warm blocks in key cache",
+ KEYCACHE_VAR(param_division_limit),
+ CMD_LINE(REQUIRED_ARG, OPT_KEY_CACHE_DIVISION_LIMIT),
+ VALID_RANGE(1, 100), DEFAULT(100),
+ BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(change_keycache_param));
+
+static Sys_var_keycache Sys_key_cache_age_threshold(
+ "key_cache_age_threshold", "This characterizes the number of "
+ "hits a hot block has to be untouched until it is considered aged "
+ "enough to be downgraded to a warm block. This specifies the "
+ "percentage ratio of that number of hits to the total number of "
+ "blocks in key cache",
+ KEYCACHE_VAR(param_age_threshold),
+ CMD_LINE(REQUIRED_ARG, OPT_KEY_CACHE_AGE_THRESHOLD),
+ VALID_RANGE(100, UINT_MAX), DEFAULT(300),
+ BLOCK_SIZE(100), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(change_keycache_param));
+
+static Sys_var_mybool Sys_large_files_support(
+ "large_files_support",
+ "Whether mysqld was compiled with options for large file support",
+ READ_ONLY GLOBAL_VAR(opt_large_files),
+ NO_CMD_LINE, DEFAULT(sizeof(my_off_t) > 4));
+
+static Sys_var_uint Sys_large_page_size(
+ "large_page_size",
+ "If large page support is enabled, this shows the size of memory pages",
+ READ_ONLY GLOBAL_VAR(opt_large_page_size), NO_CMD_LINE,
+ VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1));
+
+static Sys_var_mybool Sys_large_pages(
+ "large_pages", "Enable support for large pages",
+ READ_ONLY GLOBAL_VAR(opt_large_pages),
+ IF_WIN(NO_CMD_LINE, CMD_LINE(OPT_ARG)), DEFAULT(FALSE));
+
+static Sys_var_charptr Sys_language(
+ "lc_messages_dir", "Directory where error messages are",
+ READ_ONLY GLOBAL_VAR(lc_messages_dir_ptr), CMD_LINE(REQUIRED_ARG, 'L'),
+ IN_FS_CHARSET, DEFAULT(0));
+
+static Sys_var_mybool Sys_local_infile(
+ "local_infile", "Enable LOAD DATA LOCAL INFILE",
+ GLOBAL_VAR(opt_local_infile), CMD_LINE(OPT_ARG), DEFAULT(TRUE));
+
+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(LONG_TIMEOUT), BLOCK_SIZE(1));
+
+#ifdef HAVE_MLOCKALL
+static Sys_var_mybool Sys_locked_in_memory(
+ "locked_in_memory",
+ "Whether mysqld was locked in memory with --memlock",
+ READ_ONLY GLOBAL_VAR(locked_in_memory), NO_CMD_LINE, DEFAULT(FALSE));
+#endif
+
+/* this says NO_CMD_LINE, as command-line option takes a string, not a bool */
+static Sys_var_mybool Sys_log_bin(
+ "log_bin", "Whether the binary log is enabled",
+ READ_ONLY GLOBAL_VAR(opt_bin_log), NO_CMD_LINE, DEFAULT(FALSE));
+
+static Sys_var_mybool Sys_trust_function_creators(
+ "log_bin_trust_function_creators",
+ "If set to FALSE (the default), then when --log-bin is used, creation "
+ "of a stored function (or trigger) is allowed only to users having the "
+ "SUPER privilege and only if this stored function (trigger) may not "
+ "break binary logging. Note that if ALL connections to this server "
+ "ALWAYS use row-based binary logging, the security issues do not "
+ "exist and the binary logging cannot break, so you can safely set "
+ "this to TRUE",
+ GLOBAL_VAR(trust_function_creators),
+ CMD_LINE(OPT_ARG), DEFAULT(FALSE));
+
+static Sys_var_charptr Sys_log_error(
+ "log_error",
+ "Log errors to file (instead of stdout). If file name is not specified "
+ "then 'datadir'/'log-basename'.err or the 'pid-file' path with extension "
+ ".err is used",
+ READ_ONLY GLOBAL_VAR(log_error_file_ptr),
+ CMD_LINE(OPT_ARG, OPT_LOG_ERROR),
+ IN_FS_CHARSET, DEFAULT(disabled_my_option));
+
+static Sys_var_mybool 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));
+
+static Sys_var_ulong Sys_log_warnings(
+ "log_warnings",
+ "Log some not critical warnings to the general log file."
+ "Value can be between 0 and 11. Higher values mean more verbosity",
+ SESSION_VAR(log_warnings),
+ CMD_LINE(OPT_ARG, 'W'),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(1), BLOCK_SIZE(1));
+
+static bool update_cached_long_query_time(sys_var *self, THD *thd,
+ enum_var_type type)
+{
+ if (type == OPT_SESSION)
+ thd->variables.long_query_time=
+ double2ulonglong(thd->variables.long_query_time_double * 1e6);
+ else
+ global_system_variables.long_query_time=
+ double2ulonglong(global_system_variables.long_query_time_double * 1e6);
+ return false;
+}
+
+static Sys_var_double Sys_long_query_time(
+ "long_query_time",
+ "Log all queries that have taken more than long_query_time seconds "
+ "to execute to file. The argument will be treated as a decimal value "
+ "with microsecond precision",
+ SESSION_VAR(long_query_time_double),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, LONG_TIMEOUT), DEFAULT(10),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(update_cached_long_query_time));
+
+static bool fix_low_prio_updates(sys_var *self, THD *thd, enum_var_type type)
+{
+ if (type == OPT_SESSION)
+ thd->update_lock_default= (thd->variables.low_priority_updates ?
+ TL_WRITE_LOW_PRIORITY : TL_WRITE);
+ else
+ thr_upgraded_concurrent_insert_lock=
+ (global_system_variables.low_priority_updates ?
+ TL_WRITE_LOW_PRIORITY : TL_WRITE);
+ return false;
+}
+static Sys_var_mybool Sys_low_priority_updates(
+ "low_priority_updates",
+ "INSERT/DELETE/UPDATE has lower priority than selects",
+ SESSION_VAR(low_priority_updates),
+ CMD_LINE(OPT_ARG),
+ DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_low_prio_updates));
+
+#ifndef TO_BE_DELETED /* Alias for the low_priority_updates */
+static Sys_var_mybool Sys_sql_low_priority_updates(
+ "sql_low_priority_updates",
+ "INSERT/DELETE/UPDATE has lower priority than selects",
+ SESSION_VAR(low_priority_updates), NO_CMD_LINE,
+ DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_low_prio_updates));
+#endif
+
+static Sys_var_mybool Sys_lower_case_file_system(
+ "lower_case_file_system",
+ "Case sensitivity of file names on the file system where the "
+ "data directory is located",
+ READ_ONLY GLOBAL_VAR(lower_case_file_system), NO_CMD_LINE,
+ DEFAULT(FALSE));
+
+static Sys_var_uint Sys_lower_case_table_names(
+ "lower_case_table_names",
+ "If set to 1 table names are stored in lowercase on disk and table "
+ "names will be case-insensitive. Should be set to 2 if you are using "
+ "a case insensitive file system",
+ READ_ONLY GLOBAL_VAR(lower_case_table_names),
+ CMD_LINE(OPT_ARG, OPT_LOWER_CASE_TABLE_NAMES),
+ VALID_RANGE(0, 2),
+#ifdef FN_NO_CASE_SENSE
+ DEFAULT(1),
+#else
+ DEFAULT(0),
+#endif
+ BLOCK_SIZE(1));
+
+static bool session_readonly(sys_var *self, THD *thd, set_var *var)
+{
+ if (var->type == OPT_GLOBAL)
+ return false;
+ my_error(ER_VARIABLE_IS_READONLY, MYF(0), "SESSION",
+ self->name.str, "GLOBAL");
+ return true;
+}
+
+static bool check_max_allowed_packet(sys_var *self, THD *thd, set_var *var)
+{
+ longlong val;
+ if (session_readonly(self, thd, var))
+ return true;
+
+ val= var->save_result.ulonglong_value;
+ if (val < (longlong) global_system_variables.net_buffer_length)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ WARN_OPTION_BELOW_LIMIT, ER(WARN_OPTION_BELOW_LIMIT),
+ "max_allowed_packet", "net_buffer_length");
+ }
+ return false;
+}
+
+
+static Sys_var_ulong Sys_max_allowed_packet(
+ "max_allowed_packet",
+ "Max packet length to send to or receive from the server",
+ SESSION_VAR(max_allowed_packet), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1024, 1024*1024*1024), DEFAULT(1024*1024),
+ BLOCK_SIZE(1024), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(check_max_allowed_packet));
+
+static Sys_var_ulong Sys_slave_max_allowed_packet(
+ "slave_max_allowed_packet",
+ "The maximum packet length to sent successfully from the master to slave.",
+ GLOBAL_VAR(slave_max_allowed_packet), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1024, MAX_MAX_ALLOWED_PACKET),
+ DEFAULT(MAX_MAX_ALLOWED_PACKET),
+ BLOCK_SIZE(1024));
+
+static Sys_var_ulonglong Sys_max_binlog_cache_size(
+ "max_binlog_cache_size",
+ "Sets the total size of the transactional cache",
+ GLOBAL_VAR(max_binlog_cache_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(IO_SIZE, SIZE_T_MAX),
+ DEFAULT((SIZE_T_MAX/IO_SIZE)*IO_SIZE),
+ BLOCK_SIZE(IO_SIZE));
+
+static Sys_var_ulonglong Sys_max_binlog_stmt_cache_size(
+ "max_binlog_stmt_cache_size",
+ "Sets the total size of the statement cache",
+ GLOBAL_VAR(max_binlog_stmt_cache_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(IO_SIZE, SIZE_T_MAX),
+ DEFAULT((SIZE_T_MAX/IO_SIZE)*IO_SIZE),
+ BLOCK_SIZE(IO_SIZE));
+
+static bool fix_max_binlog_size(sys_var *self, THD *thd, enum_var_type type)
+{
+ mysql_bin_log.set_max_size(max_binlog_size);
+#ifdef HAVE_REPLICATION
+ if (!max_relay_log_size)
+ active_mi->rli.relay_log.set_max_size(max_binlog_size);
+#endif
+ return false;
+}
+static Sys_var_ulong Sys_max_binlog_size(
+ "max_binlog_size",
+ "Binary log will be rotated automatically when the size exceeds this "
+ "value. Will also apply to relay logs if max_relay_log_size is 0",
+ GLOBAL_VAR(max_binlog_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(IO_SIZE, 1024*1024L*1024L), DEFAULT(1024*1024L*1024L),
+ BLOCK_SIZE(IO_SIZE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_max_binlog_size));
+
+static bool fix_max_connections(sys_var *self, THD *thd, enum_var_type type)
+{
+#ifndef EMBEDDED_LIBRARY
+ resize_thr_alarm(max_connections + extra_max_connections +
+ global_system_variables.max_insert_delayed_threads + 10);
+#endif
+ return false;
+}
+
+// Default max_connections of 151 is larger than Apache's default max
+// children, to avoid "too many connections" error in a common setup
+static Sys_var_ulong Sys_max_connections(
+ "max_connections", "The number of simultaneous clients allowed",
+ GLOBAL_VAR(max_connections), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, 100000), DEFAULT(151), BLOCK_SIZE(1), NO_MUTEX_GUARD,
+ NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(fix_max_connections));
+
+static Sys_var_ulong Sys_max_connect_errors(
+ "max_connect_errors",
+ "If there is more than this number of interrupted connections from "
+ "a host this host will be blocked from further connections",
+ GLOBAL_VAR(max_connect_errors), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, UINT_MAX), DEFAULT(MAX_CONNECT_ERRORS),
+ BLOCK_SIZE(1));
+
+static bool check_max_delayed_threads(sys_var *self, THD *thd, set_var *var)
+{
+ return var->type != OPT_GLOBAL &&
+ var->save_result.ulonglong_value != 0 &&
+ var->save_result.ulonglong_value !=
+ global_system_variables.max_insert_delayed_threads;
+}
+
+// Alias for max_delayed_threads
+static Sys_var_ulong Sys_max_insert_delayed_threads(
+ "max_insert_delayed_threads",
+ "Don't start more than this number of threads to handle INSERT "
+ "DELAYED statements. If set to zero INSERT DELAYED will be not used",
+ SESSION_VAR(max_insert_delayed_threads),
+ NO_CMD_LINE, VALID_RANGE(0, 16384), DEFAULT(20),
+ BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(check_max_delayed_threads), ON_UPDATE(fix_max_connections));
+
+static Sys_var_ulong Sys_max_delayed_threads(
+ "max_delayed_threads",
+ "Don't start more than this number of threads to handle INSERT "
+ "DELAYED statements. If set to zero INSERT DELAYED will be not used",
+ SESSION_VAR(max_insert_delayed_threads),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 16384), DEFAULT(20),
+ BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(check_max_delayed_threads), ON_UPDATE(fix_max_connections));
+
+static Sys_var_ulong Sys_max_error_count(
+ "max_error_count",
+ "Max number of errors/warnings to store for a statement",
+ SESSION_VAR(max_error_count), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, 65535), DEFAULT(DEFAULT_ERROR_COUNT), BLOCK_SIZE(1));
+
+static Sys_var_ulonglong Sys_max_heap_table_size(
+ "max_heap_table_size",
+ "Don't allow creation of heap tables bigger than this",
+ SESSION_VAR(max_heap_table_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(16384, (ulonglong)~(intptr)0), DEFAULT(16*1024*1024),
+ BLOCK_SIZE(1024));
+
+static Sys_var_ulong Sys_metadata_locks_cache_size(
+ "metadata_locks_cache_size", "Size of unused metadata locks cache",
+ READ_ONLY GLOBAL_VAR(mdl_locks_cache_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, 1024*1024), DEFAULT(MDL_LOCKS_CACHE_SIZE_DEFAULT),
+ BLOCK_SIZE(1));
+
+/*
+ "pseudo_thread_id" variable used in the test suite to detect 32/64bit
+ systems. If you change it to something else then ulong then fix the tests
+ in mysql-test/include/have_32bit.inc and have_64bit.inc.
+*/
+static Sys_var_ulong Sys_pseudo_thread_id(
+ "pseudo_thread_id",
+ "This variable is for internal server use",
+ SESSION_ONLY(pseudo_thread_id),
+ NO_CMD_LINE, VALID_RANGE(0, ULONG_MAX), DEFAULT(0),
+ BLOCK_SIZE(1), NO_MUTEX_GUARD, IN_BINLOG,
+ ON_CHECK(check_has_super));
+
+static bool fix_max_join_size(sys_var *self, THD *thd, enum_var_type type)
+{
+ SV *sv= type == OPT_GLOBAL ? &global_system_variables : &thd->variables;
+ if (sv->max_join_size == HA_POS_ERROR)
+ sv->option_bits|= OPTION_BIG_SELECTS;
+ else
+ sv->option_bits&= ~OPTION_BIG_SELECTS;
+ return false;
+}
+static Sys_var_harows Sys_max_join_size(
+ "max_join_size",
+ "Joins that are probably going to read more than max_join_size "
+ "records return an error",
+ SESSION_VAR(max_join_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, HA_POS_ERROR), DEFAULT(HA_POS_ERROR), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_max_join_size));
+
+static Sys_var_ulong Sys_max_seeks_for_key(
+ "max_seeks_for_key",
+ "Limit assumed max number of seeks when looking up rows based on a key",
+ SESSION_VAR(max_seeks_for_key), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, UINT_MAX), DEFAULT(UINT_MAX), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_max_length_for_sort_data(
+ "max_length_for_sort_data",
+ "Max number of bytes in sorted records",
+ SESSION_VAR(max_length_for_sort_data), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(4, 8192*1024L), DEFAULT(1024), BLOCK_SIZE(1));
+
+static Sys_var_harows Sys_sql_max_join_size(
+ "sql_max_join_size", "Alias for max_join_size",
+ SESSION_VAR(max_join_size), NO_CMD_LINE,
+ VALID_RANGE(1, HA_POS_ERROR), DEFAULT(HA_POS_ERROR), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_max_join_size), DEPRECATED("'@@max_join_size'"));
+
+static Sys_var_ulong Sys_max_long_data_size(
+ "max_long_data_size",
+ "The maximum BLOB length to send to server from "
+ "mysql_send_long_data API. Deprecated option; "
+ "use max_allowed_packet instead.",
+ READ_ONLY GLOBAL_VAR(max_long_data_size),
+ CMD_LINE(REQUIRED_ARG, OPT_MAX_LONG_DATA_SIZE),
+ VALID_RANGE(1024, UINT_MAX32), DEFAULT(1024*1024),
+ BLOCK_SIZE(1));
+
+static PolyLock_mutex PLock_prepared_stmt_count(&LOCK_prepared_stmt_count);
+static Sys_var_ulong Sys_max_prepared_stmt_count(
+ "max_prepared_stmt_count",
+ "Maximum number of prepared statements in the server",
+ GLOBAL_VAR(max_prepared_stmt_count), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, 1024*1024), DEFAULT(16382), BLOCK_SIZE(1),
+ &PLock_prepared_stmt_count);
+
+static bool fix_max_relay_log_size(sys_var *self, THD *thd, enum_var_type type)
+{
+#ifdef HAVE_REPLICATION
+ active_mi->rli.relay_log.set_max_size(max_relay_log_size ?
+ max_relay_log_size: max_binlog_size);
+#endif
+ return false;
+}
+static Sys_var_ulong Sys_max_relay_log_size(
+ "max_relay_log_size",
+ "If non-zero: relay log will be rotated automatically when the "
+ "size exceeds this value; if zero: when the size "
+ "exceeds max_binlog_size",
+ GLOBAL_VAR(max_relay_log_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, 1024L*1024*1024), DEFAULT(0), BLOCK_SIZE(IO_SIZE),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_max_relay_log_size));
+
+static Sys_var_ulong Sys_max_sort_length(
+ "max_sort_length",
+ "The number of bytes to use when sorting BLOB or TEXT values (only "
+ "the first max_sort_length bytes of each value are used; the rest "
+ "are ignored)",
+ SESSION_VAR(max_sort_length), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(4, 8192*1024L), DEFAULT(1024), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_max_sp_recursion_depth(
+ "max_sp_recursion_depth",
+ "Maximum stored procedure recursion depth",
+ SESSION_VAR(max_sp_recursion_depth), CMD_LINE(OPT_ARG),
+ VALID_RANGE(0, 255), DEFAULT(0), BLOCK_SIZE(1));
+
+
+static bool if_checking_enabled(sys_var *self, THD *thd, set_var *var)
+{
+ if (session_readonly(self, thd, var))
+ return true;
+
+ if (!max_user_connections_checking)
+ {
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--max-user-connections=0");
+ return true;
+ }
+
+ return false;
+}
+// non-standard session_value_ptr() here
+static Sys_var_max_user_conn Sys_max_user_connections(
+ "max_user_connections",
+ "The maximum number of active connections for a single user "
+ "(0 = no limit)",
+ SESSION_VAR(max_user_connections), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(-1, INT_MAX), DEFAULT(0), BLOCK_SIZE(1), NO_MUTEX_GUARD,
+ NOT_IN_BINLOG, ON_CHECK(if_checking_enabled));
+
+static Sys_var_ulong Sys_max_tmp_tables(
+ "max_tmp_tables",
+ "Maximum number of temporary tables a client can keep open at a time",
+ SESSION_VAR(max_tmp_tables), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, UINT_MAX), DEFAULT(32), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_max_write_lock_count(
+ "max_write_lock_count",
+ "After this many write locks, allow some read locks to run in between",
+ GLOBAL_VAR(max_write_lock_count), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, UINT_MAX), DEFAULT(UINT_MAX), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_min_examined_row_limit(
+ "min_examined_row_limit",
+ "Don't write queries to slow log that examine fewer rows "
+ "than that",
+ SESSION_VAR(min_examined_row_limit), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1));
+
+#ifdef _WIN32
+static Sys_var_mybool Sys_named_pipe(
+ "named_pipe", "Enable the named pipe (NT)",
+ READ_ONLY GLOBAL_VAR(opt_enable_named_pipe), CMD_LINE(OPT_ARG),
+ DEFAULT(FALSE));
+#endif
+
+
+static bool check_net_buffer_length(sys_var *self, THD *thd, set_var *var)
+{
+ longlong val;
+ if (session_readonly(self, thd, var))
+ return true;
+
+ val= var->save_result.ulonglong_value;
+ if (val > (longlong) global_system_variables.max_allowed_packet)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ WARN_OPTION_BELOW_LIMIT, ER(WARN_OPTION_BELOW_LIMIT),
+ "max_allowed_packet", "net_buffer_length");
+ }
+ return false;
+}
+static Sys_var_ulong Sys_net_buffer_length(
+ "net_buffer_length",
+ "Buffer length for TCP/IP and socket communication",
+ SESSION_VAR(net_buffer_length), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1024, 1024*1024), DEFAULT(16384), BLOCK_SIZE(1024),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_net_buffer_length));
+
+static bool fix_net_read_timeout(sys_var *self, THD *thd, enum_var_type type)
+{
+ if (type != OPT_GLOBAL)
+ my_net_set_read_timeout(&thd->net, thd->variables.net_read_timeout);
+ return false;
+}
+static Sys_var_ulong Sys_net_read_timeout(
+ "net_read_timeout",
+ "Number of seconds to wait for more data from a connection before "
+ "aborting the read",
+ SESSION_VAR(net_read_timeout), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, LONG_TIMEOUT), DEFAULT(NET_READ_TIMEOUT), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_net_read_timeout));
+
+static bool fix_net_write_timeout(sys_var *self, THD *thd, enum_var_type type)
+{
+ if (type != OPT_GLOBAL)
+ my_net_set_write_timeout(&thd->net, thd->variables.net_write_timeout);
+ return false;
+}
+static Sys_var_ulong Sys_net_write_timeout(
+ "net_write_timeout",
+ "Number of seconds to wait for a block to be written to a connection "
+ "before aborting the write",
+ SESSION_VAR(net_write_timeout), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, LONG_TIMEOUT), DEFAULT(NET_WRITE_TIMEOUT), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_net_write_timeout));
+
+static bool fix_net_retry_count(sys_var *self, THD *thd, enum_var_type type)
+{
+ if (type != OPT_GLOBAL)
+ thd->net.retry_count=thd->variables.net_retry_count;
+ return false;
+}
+static Sys_var_ulong Sys_net_retry_count(
+ "net_retry_count",
+ "If a read on a communication port is interrupted, retry this "
+ "many times before giving up",
+ SESSION_VAR(net_retry_count), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, UINT_MAX), DEFAULT(MYSQLD_NET_RETRY_COUNT),
+ BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_net_retry_count));
+
+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 bool check_old_passwords(sys_var *self, THD *thd, set_var *var)
+{
+ return mysql_user_table_is_in_short_password_format;
+}
+static Sys_var_mybool Sys_old_passwords(
+ "old_passwords",
+ "Use old password encryption method (needed for 4.0 and older clients)",
+ SESSION_VAR(old_passwords), CMD_LINE(OPT_ARG), DEFAULT(FALSE),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_old_passwords));
+
+static Sys_var_ulong Sys_open_files_limit(
+ "open_files_limit",
+ "If this is not 0, then mysqld will use this value to reserve file "
+ "descriptors to use with setrlimit(). If this value is 0 then mysqld "
+ "will reserve max_connections*5 or max_connections + table_cache*2 "
+ "(whichever is larger) number of file descriptors",
+ READ_ONLY GLOBAL_VAR(open_files_limit), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, OS_FILE_LIMIT), DEFAULT(0), BLOCK_SIZE(1));
+
+/// @todo change to enum
+static Sys_var_ulong Sys_optimizer_prune_level(
+ "optimizer_prune_level",
+ "Controls the heuristic(s) applied during query optimization to prune "
+ "less-promising partial plans from the optimizer search space. "
+ "Meaning: 0 - do not apply any heuristic, thus perform exhaustive "
+ "search; 1 - prune plans based on number of retrieved rows",
+ SESSION_VAR(optimizer_prune_level), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, 1), DEFAULT(1), BLOCK_SIZE(1));
+
+/** Warns about deprecated value 63 */
+static bool fix_optimizer_search_depth(sys_var *self, THD *thd,
+ enum_var_type type)
+{
+ SV *sv= type == OPT_GLOBAL ? &global_system_variables : &thd->variables;
+ if (sv->optimizer_search_depth == MAX_TABLES+2)
+ WARN_DEPRECATED(thd, 6, 0, "optimizer-search-depth=63",
+ "a search depth less than 63");
+ return false;
+}
+
+static Sys_var_ulong Sys_optimizer_search_depth(
+ "optimizer_search_depth",
+ "Maximum depth of search performed by the query optimizer. Values "
+ "larger than the number of relations in a query result in better "
+ "query plans, but take longer to compile a query. Values smaller "
+ "than the number of tables in a relation result in faster "
+ "optimization, but may produce very bad query plans. If set to 0, "
+ "the system will automatically pick a reasonable value; if set to "
+ "63, the optimizer will switch to the original find_best search. "
+ "NOTE: The value 63 and its associated behaviour is deprecated",
+ SESSION_VAR(optimizer_search_depth), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, MAX_TABLES+2), DEFAULT(MAX_TABLES+1), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_optimizer_search_depth));
+
+/* this is used in the sigsegv handler */
+export const char *optimizer_switch_names[]=
+{
+ "index_merge","index_merge_union","index_merge_sort_union",
+ "index_merge_intersection","index_merge_sort_intersection",
+ "engine_condition_pushdown",
+ "index_condition_pushdown",
+ "derived_merge", "derived_with_keys",
+ "firstmatch","loosescan","materialization","in_to_exists","semijoin",
+ "partial_match_rowid_merge",
+ "partial_match_table_scan",
+ "subquery_cache",
+ "mrr",
+ "mrr_cost_based",
+ "mrr_sort_keys",
+ "outer_join_with_cache",
+ "semijoin_with_cache",
+ "join_cache_incremental",
+ "join_cache_hashed",
+ "join_cache_bka",
+ "optimize_join_buffer_size",
+ "table_elimination",
+ "extended_keys",
+ "default", NullS
+};
+/** propagates changes to @@engine_condition_pushdown */
+static bool fix_optimizer_switch(sys_var *self, THD *thd,
+ enum_var_type type)
+{
+ SV *sv= (type == OPT_GLOBAL) ? &global_system_variables : &thd->variables;
+ sv->engine_condition_pushdown=
+ test(sv->optimizer_switch & OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN);
+ return false;
+}
+static Sys_var_flagset Sys_optimizer_switch(
+ "optimizer_switch",
+ "optimizer_switch=option=val[,option=val...], where option is one of {"
+ "derived_merge, "
+ "derived_with_keys, "
+ "firstmatch, "
+ "in_to_exists, "
+ "engine_condition_pushdown, "
+ "index_condition_pushdown, "
+ "index_merge, "
+ "index_merge_intersection, "
+ "index_merge_sort_intersection, "
+ "index_merge_sort_union, "
+ "index_merge_union, "
+ "join_cache_bka, "
+ "join_cache_hashed, "
+ "join_cache_incremental, "
+ "loosescan, "
+ "materialization, "
+ "mrr, "
+ "mrr_cost_based, "
+ "mrr_sort_keys, "
+ "optimize_join_buffer_size, "
+ "outer_join_with_cache, "
+ "partial_match_rowid_merge, "
+ "partial_match_table_scan, "
+ "semijoin, "
+ "semijoin_with_cache, "
+ "subquery_cache, "
+ "table_elimination, "
+ "extended_keys "
+ "} and val is one of {on, off, default}",
+ SESSION_VAR(optimizer_switch), CMD_LINE(REQUIRED_ARG),
+ optimizer_switch_names, DEFAULT(OPTIMIZER_SWITCH_DEFAULT),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL),
+ ON_UPDATE(fix_optimizer_switch));
+
+static Sys_var_charptr Sys_pid_file(
+ "pid_file", "Pid file used by safe_mysqld",
+ READ_ONLY GLOBAL_VAR(pidfile_name_ptr), CMD_LINE(REQUIRED_ARG),
+ IN_FS_CHARSET, DEFAULT(0));
+
+static Sys_var_charptr Sys_plugin_dir(
+ "plugin_dir", "Directory for plugins",
+ READ_ONLY GLOBAL_VAR(opt_plugin_dir_ptr), CMD_LINE(REQUIRED_ARG),
+ IN_FS_CHARSET, DEFAULT(0));
+
+static Sys_var_uint Sys_port(
+ "port",
+ "Port number to use for connection or 0 to default to, "
+ "my.cnf, $MYSQL_TCP_PORT, "
+#if MYSQL_PORT_DEFAULT == 0
+ "/etc/services, "
+#endif
+ "built-in default (" STRINGIFY_ARG(MYSQL_PORT) "), whatever comes first",
+ READ_ONLY GLOBAL_VAR(mysqld_port), CMD_LINE(REQUIRED_ARG, 'P'),
+ VALID_RANGE(0, UINT_MAX32), DEFAULT(0), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_preload_buff_size(
+ "preload_buffer_size",
+ "The size of the buffer that is allocated when preloading indexes",
+ SESSION_VAR(preload_buff_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1024, 1024*1024*1024), DEFAULT(32768), BLOCK_SIZE(1));
+
+static Sys_var_uint Sys_protocol_version(
+ "protocol_version",
+ "The version of the client/server protocol used by the MySQL server",
+ READ_ONLY GLOBAL_VAR(protocol_version), NO_CMD_LINE,
+ VALID_RANGE(0, ~0), DEFAULT(PROTOCOL_VERSION), BLOCK_SIZE(1));
+
+static Sys_var_proxy_user Sys_proxy_user(
+ "proxy_user", "The proxy user account name used when logging in",
+ IN_SYSTEM_CHARSET);
+
+static Sys_var_external_user Sys_exterenal_user(
+ "external_user", "The external user account used when logging in",
+ IN_SYSTEM_CHARSET);
+
+static Sys_var_ulong Sys_read_buff_size(
+ "read_buffer_size",
+ "Each thread that does a sequential scan allocates a buffer of "
+ "this size for each table it scans. If you do many sequential scans, "
+ "you may want to increase this value",
+ SESSION_VAR(read_buff_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(IO_SIZE*2, INT_MAX32), DEFAULT(128*1024),
+ BLOCK_SIZE(IO_SIZE));
+
+static bool check_read_only(sys_var *self, THD *thd, set_var *var)
+{
+ /* Prevent self dead-lock */
+ if (thd->locked_tables_mode || thd->in_active_multi_stmt_transaction())
+ {
+ my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
+ return true;
+ }
+ return false;
+}
+static bool fix_read_only(sys_var *self, THD *thd, enum_var_type type)
+{
+ bool result= true;
+ my_bool new_read_only= read_only; // make a copy before releasing a mutex
+ DBUG_ENTER("sys_var_opt_readonly::update");
+
+ if (read_only == FALSE || read_only == opt_readonly)
+ {
+ opt_readonly= read_only;
+ DBUG_RETURN(false);
+ }
+
+ if (check_read_only(self, thd, 0)) // just in case
+ goto end;
+
+ if (thd->global_read_lock.is_acquired())
+ {
+ /*
+ This connection already holds the global read lock.
+ This can be the case with:
+ - FLUSH TABLES WITH READ LOCK
+ - SET GLOBAL READ_ONLY = 1
+ */
+ opt_readonly= read_only;
+ DBUG_RETURN(false);
+ }
+
+ /*
+ READ_ONLY=1 prevents write locks from being taken on tables and
+ blocks transactions from committing. We therefore should make sure
+ that no such events occur while setting the read_only variable.
+ This is a 2 step process:
+ [1] lock_global_read_lock()
+ Prevents connections from obtaining new write locks on
+ tables. Note that we can still have active rw transactions.
+ [2] make_global_read_lock_block_commit()
+ Prevents transactions from committing.
+ */
+
+ read_only= opt_readonly;
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+
+ if (thd->global_read_lock.lock_global_read_lock(thd))
+ goto end_with_mutex_unlock;
+
+ if ((result= thd->global_read_lock.make_global_read_lock_block_commit(thd)))
+ goto end_with_read_lock;
+
+ /* Change the opt_readonly system variable, safe because the lock is held */
+ opt_readonly= new_read_only;
+ result= false;
+
+ end_with_read_lock:
+ /* Release the lock */
+ thd->global_read_lock.unlock_global_read_lock(thd);
+ end_with_mutex_unlock:
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ end:
+ read_only= opt_readonly;
+ DBUG_RETURN(result);
+}
+
+
+/**
+ The read_only boolean is always equal to the opt_readonly boolean except
+ during fix_read_only(); when that function is entered, opt_readonly is
+ the pre-update value and read_only is the post-update value.
+ fix_read_only() compares them and runs needed operations for the
+ transition (especially when transitioning from false to true) and
+ synchronizes both booleans in the end.
+*/
+static Sys_var_mybool Sys_readonly(
+ "read_only",
+ "Make all non-temporary tables read-only, with the exception for "
+ "replication (slave) threads and users with the SUPER privilege",
+ GLOBAL_VAR(read_only), CMD_LINE(OPT_ARG), DEFAULT(FALSE),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(check_read_only), ON_UPDATE(fix_read_only));
+
+// Small lower limit to be able to test MRR
+static Sys_var_ulong Sys_read_rnd_buff_size(
+ "read_rnd_buffer_size",
+ "When reading rows in sorted order after a sort, the rows are read "
+ "through this buffer to avoid a disk seeks",
+ SESSION_VAR(read_rnd_buff_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, INT_MAX32), DEFAULT(256*1024), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_div_precincrement(
+ "div_precision_increment", "Precision of the result of '/' "
+ "operator will be increased on that value",
+ SESSION_VAR(div_precincrement), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, DECIMAL_MAX_SCALE), DEFAULT(4), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_rpl_recovery_rank(
+ "rpl_recovery_rank", "Unused, will be removed",
+ GLOBAL_VAR(rpl_recovery_rank), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, ULONG_MAX), DEFAULT(0), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0),
+ DEPRECATED(""));
+
+static Sys_var_ulong Sys_range_alloc_block_size(
+ "range_alloc_block_size",
+ "Allocation block size for storing ranges during optimization",
+ SESSION_VAR(range_alloc_block_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(RANGE_ALLOC_BLOCK_SIZE, UINT_MAX),
+ DEFAULT(RANGE_ALLOC_BLOCK_SIZE), BLOCK_SIZE(1024));
+
+static Sys_var_ulong Sys_multi_range_count(
+ "multi_range_count", "Ignored. Use mrr_buffer_size instead",
+ SESSION_VAR(multi_range_count), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, ULONG_MAX), DEFAULT(256), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0),
+ DEPRECATED("'@@mrr_buffer_size'"));
+
+static bool fix_thd_mem_root(sys_var *self, THD *thd, enum_var_type type)
+{
+ if (type != OPT_GLOBAL)
+ reset_root_defaults(thd->mem_root,
+ thd->variables.query_alloc_block_size,
+ thd->variables.query_prealloc_size);
+ return false;
+}
+static Sys_var_ulong Sys_query_alloc_block_size(
+ "query_alloc_block_size",
+ "Allocation block size for query parsing and execution",
+ SESSION_VAR(query_alloc_block_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1024, UINT_MAX), DEFAULT(QUERY_ALLOC_BLOCK_SIZE),
+ BLOCK_SIZE(1024), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_thd_mem_root));
+
+static Sys_var_ulong Sys_query_prealloc_size(
+ "query_prealloc_size",
+ "Persistent buffer for query parsing and execution",
+ SESSION_VAR(query_prealloc_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(QUERY_ALLOC_PREALLOC_SIZE, UINT_MAX),
+ DEFAULT(QUERY_ALLOC_PREALLOC_SIZE),
+ BLOCK_SIZE(1024), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_thd_mem_root));
+
+#ifdef HAVE_SMEM
+static Sys_var_mybool Sys_shared_memory(
+ "shared_memory", "Enable the shared memory",
+ READ_ONLY GLOBAL_VAR(opt_enable_shared_memory), CMD_LINE(OPT_ARG),
+ DEFAULT(FALSE));
+
+static Sys_var_charptr Sys_shared_memory_base_name(
+ "shared_memory_base_name", "Base name of shared memory",
+ READ_ONLY GLOBAL_VAR(shared_memory_base_name), CMD_LINE(REQUIRED_ARG),
+ IN_FS_CHARSET, DEFAULT(0));
+#endif
+
+// this has to be NO_CMD_LINE as the command-line option has a different name
+static Sys_var_mybool Sys_skip_external_locking(
+ "skip_external_locking", "Don't use system (external) locking",
+ READ_ONLY GLOBAL_VAR(my_disable_locking), NO_CMD_LINE, DEFAULT(TRUE));
+
+static Sys_var_mybool Sys_skip_networking(
+ "skip_networking", "Don't allow connection with TCP/IP",
+ READ_ONLY GLOBAL_VAR(opt_disable_networking), CMD_LINE(OPT_ARG),
+ DEFAULT(FALSE));
+
+static Sys_var_mybool Sys_skip_name_resolve(
+ "skip_name_resolve",
+ "Don't resolve hostnames. All hostnames are IP's or 'localhost'.",
+ READ_ONLY GLOBAL_VAR(opt_skip_name_resolve),
+ CMD_LINE(OPT_ARG, OPT_SKIP_RESOLVE),
+ DEFAULT(FALSE));
+
+static Sys_var_mybool Sys_skip_show_database(
+ "skip_show_database", "Don't allow 'SHOW DATABASE' commands",
+ READ_ONLY GLOBAL_VAR(opt_skip_show_db), CMD_LINE(OPT_ARG),
+ DEFAULT(FALSE));
+
+static Sys_var_charptr Sys_socket(
+ "socket", "Socket file to use for connection",
+ READ_ONLY GLOBAL_VAR(mysqld_unix_port), CMD_LINE(REQUIRED_ARG),
+ IN_FS_CHARSET, DEFAULT(0));
+
+/*
+ thread_concurrency is a no-op on all platforms since
+ MySQL 5.1. It will be removed in the context of
+ WL#5265
+*/
+static Sys_var_ulong Sys_thread_concurrency(
+ "thread_concurrency",
+ "Permits the application to give the threads system a hint for "
+ "the desired number of threads that should be run at the same time."
+ "This variable has no effect, and is deprecated. "
+ "It will be removed in a future release.",
+ READ_ONLY GLOBAL_VAR(concurrency),
+ CMD_LINE(REQUIRED_ARG, OPT_THREAD_CONCURRENCY),
+ VALID_RANGE(1, 512), DEFAULT(DEFAULT_CONCURRENCY), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0),
+ DEPRECATED(""));
+
+static Sys_var_ulonglong Sys_thread_stack(
+ "thread_stack", "The stack size for each thread",
+ READ_ONLY GLOBAL_VAR(my_thread_stack_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(128*1024, ULONGLONG_MAX), DEFAULT(DEFAULT_THREAD_STACK),
+ BLOCK_SIZE(1024));
+
+static Sys_var_charptr Sys_tmpdir(
+ "tmpdir", "Path for temporary files. Several paths may "
+ "be specified, separated by a "
+#if defined(__WIN__)
+ "semicolon (;)"
+#else
+ "colon (:)"
+#endif
+ ", in this case they are used in a round-robin fashion",
+ READ_ONLY GLOBAL_VAR(opt_mysql_tmpdir), CMD_LINE(REQUIRED_ARG, 't'),
+ IN_FS_CHARSET, DEFAULT(0));
+
+static bool fix_trans_mem_root(sys_var *self, THD *thd, enum_var_type type)
+{
+ if (type != OPT_GLOBAL)
+ reset_root_defaults(&thd->transaction.mem_root,
+ thd->variables.trans_alloc_block_size,
+ thd->variables.trans_prealloc_size);
+ return false;
+}
+static Sys_var_ulong Sys_trans_alloc_block_size(
+ "transaction_alloc_block_size",
+ "Allocation block size for transactions to be stored in binary log",
+ SESSION_VAR(trans_alloc_block_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1024, UINT_MAX), DEFAULT(QUERY_ALLOC_BLOCK_SIZE),
+ BLOCK_SIZE(1024), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_trans_mem_root));
+
+static Sys_var_ulong Sys_trans_prealloc_size(
+ "transaction_prealloc_size",
+ "Persistent buffer for transactions to be stored in binary log",
+ SESSION_VAR(trans_prealloc_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1024, UINT_MAX), DEFAULT(TRANS_ALLOC_PREALLOC_SIZE),
+ BLOCK_SIZE(1024), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_trans_mem_root));
+
+static const char *thread_handling_names[]=
+{
+ "one-thread-per-connection", "no-threads",
+#ifdef HAVE_POOL_OF_THREADS
+ "pool-of-threads",
+#endif
+ 0
+};
+
+#if defined (_WIN32) && defined (HAVE_POOL_OF_THREADS)
+/* Windows is using OS threadpool, so we're pretty sure it works well */
+#define DEFAULT_THREAD_HANDLING 2
+#else
+#define DEFAULT_THREAD_HANDLING 0
+#endif
+
+static Sys_var_enum Sys_thread_handling(
+ "thread_handling",
+ "Define threads usage for handling queries, one of "
+ "one-thread-per-connection, no-threads"
+#ifdef HAVE_POOL_OF_THREADS
+ ", pool-of-threads"
+#endif
+ , READ_ONLY GLOBAL_VAR(thread_handling), CMD_LINE(REQUIRED_ARG),
+ thread_handling_names,
+ DEFAULT(DEFAULT_THREAD_HANDLING)
+ );
+
+#ifdef HAVE_QUERY_CACHE
+static bool check_query_cache_size(sys_var *self, THD *thd, set_var *var)
+{
+ if (global_system_variables.query_cache_type == 0 &&
+ var->value && var->value->val_int() != 0)
+ {
+ my_error(ER_QUERY_CACHE_DISABLED, MYF(0));
+ return true;
+ }
+
+ return false;
+}
+static bool fix_query_cache_size(sys_var *self, THD *thd, enum_var_type type)
+{
+ ulong new_cache_size= query_cache.resize(query_cache_size);
+ /*
+ Note: query_cache_size is a global variable reflecting the
+ requested cache size. See also query_cache_size_arg
+ */
+ if (query_cache_size != new_cache_size)
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_QC_RESIZE, ER(ER_WARN_QC_RESIZE),
+ query_cache_size, new_cache_size);
+
+ query_cache_size= new_cache_size;
+ return false;
+}
+static Sys_var_ulonglong Sys_query_cache_size(
+ "query_cache_size",
+ "The memory allocated to store results from old queries",
+ GLOBAL_VAR(query_cache_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, ULONG_MAX), DEFAULT(0), BLOCK_SIZE(1024),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_query_cache_size),
+ ON_UPDATE(fix_query_cache_size));
+
+static Sys_var_ulong Sys_query_cache_limit(
+ "query_cache_limit",
+ "Don't cache results that are bigger than this",
+ GLOBAL_VAR(query_cache.query_cache_limit), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(1024*1024), BLOCK_SIZE(1));
+
+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);
+ return false;
+}
+static Sys_var_ulong Sys_query_cache_min_res_unit(
+ "query_cache_min_res_unit",
+ "The minimum size for blocks allocated by the query cache",
+ GLOBAL_VAR(query_cache_min_res_unit), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(QUERY_CACHE_MIN_RESULT_DATA_SIZE),
+ BLOCK_SIZE(8), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_qcache_min_res_unit));
+
+static const char *query_cache_type_names[]= { "OFF", "ON", "DEMAND", 0 };
+static bool check_query_cache_type(sys_var *self, THD *thd, set_var *var)
+{
+ if (query_cache.is_disable_in_progress())
+ {
+ my_error(ER_QUERY_CACHE_IS_DISABLED, MYF(0));
+ return true;
+ }
+ if (var->type != OPT_GLOBAL &&
+ global_system_variables.query_cache_type == 0 &&
+ var->value->val_int() != 0)
+ {
+ my_error(ER_QUERY_CACHE_IS_GLOBALY_DISABLED, MYF(0));
+ return true;
+ }
+
+ return false;
+}
+static bool fix_query_cache_type(sys_var *self, THD *thd, enum_var_type type)
+{
+ if (type != OPT_GLOBAL)
+ return false;
+
+ if (global_system_variables.query_cache_type != 0 &&
+ query_cache.is_disabled())
+ {
+ /* if disabling in progress variable will not be set */
+ DBUG_ASSERT(!query_cache.is_disable_in_progress());
+ /* Enable query cache because it was disabled */
+ fix_query_cache_size(0, thd, type);
+ }
+ else if (global_system_variables.query_cache_type == 0)
+ query_cache.disable_query_cache(thd);
+ return false;
+}
+static Sys_var_enum Sys_query_cache_type(
+ "query_cache_type",
+ "OFF = Don't cache or retrieve results. ON = Cache all results "
+ "except SELECT SQL_NO_CACHE ... queries. DEMAND = Cache only "
+ "SELECT SQL_CACHE ... queries",
+ SESSION_VAR(query_cache_type), CMD_LINE(REQUIRED_ARG),
+ query_cache_type_names, DEFAULT(1), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(check_query_cache_type),
+ ON_UPDATE(fix_query_cache_type));
+
+static Sys_var_mybool Sys_query_cache_wlock_invalidate(
+ "query_cache_wlock_invalidate",
+ "Invalidate queries in query cache on LOCK for write",
+ SESSION_VAR(query_cache_wlock_invalidate), CMD_LINE(OPT_ARG),
+ DEFAULT(FALSE));
+#endif /* HAVE_QUERY_CACHE */
+
+static Sys_var_mybool Sys_secure_auth(
+ "secure_auth",
+ "Disallow authentication for accounts that have old (pre-4.1) "
+ "passwords",
+ GLOBAL_VAR(opt_secure_auth), CMD_LINE(OPT_ARG),
+ DEFAULT(FALSE));
+
+static Sys_var_charptr Sys_secure_file_priv(
+ "secure_file_priv",
+ "Limit LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE() to files "
+ "within specified directory",
+ PREALLOCATED READ_ONLY GLOBAL_VAR(opt_secure_file_priv),
+ CMD_LINE(REQUIRED_ARG), IN_FS_CHARSET, DEFAULT(0));
+
+static bool fix_server_id(sys_var *self, THD *thd, enum_var_type type)
+{
+ server_id_supplied = 1;
+ thd->server_id= server_id;
+ return false;
+}
+static Sys_var_ulong Sys_server_id(
+ "server_id",
+ "Uniquely identifies the server instance in the community of "
+ "replication partners",
+ GLOBAL_VAR(server_id), CMD_LINE(REQUIRED_ARG, OPT_SERVER_ID),
+ VALID_RANGE(0, UINT_MAX32), DEFAULT(0), BLOCK_SIZE(1), NO_MUTEX_GUARD,
+ NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(fix_server_id));
+
+static Sys_var_mybool Sys_slave_compressed_protocol(
+ "slave_compressed_protocol",
+ "Use compression on master/slave protocol",
+ GLOBAL_VAR(opt_slave_compressed_protocol), CMD_LINE(OPT_ARG),
+ DEFAULT(FALSE));
+
+#ifdef HAVE_REPLICATION
+static const char *slave_exec_mode_names[]= {"STRICT", "IDEMPOTENT", 0};
+static Sys_var_enum Slave_exec_mode(
+ "slave_exec_mode",
+ "Modes for how replication events should be executed. Legal values "
+ "are STRICT (default) and IDEMPOTENT. In IDEMPOTENT mode, "
+ "replication will not stop for operations that are idempotent. "
+ "In STRICT mode, replication will stop on any unexpected difference "
+ "between the master and the slave",
+ GLOBAL_VAR(slave_exec_mode_options), CMD_LINE(REQUIRED_ARG),
+ slave_exec_mode_names, DEFAULT(SLAVE_EXEC_MODE_STRICT));
+
+static const char *slave_type_conversions_name[]= {"ALL_LOSSY", "ALL_NON_LOSSY", 0};
+static Sys_var_set Slave_type_conversions(
+ "slave_type_conversions",
+ "Set of slave type conversions that are enabled. Legal values are:"
+ " ALL_LOSSY to enable lossy conversions and"
+ " ALL_NON_LOSSY to enable non-lossy conversions."
+ " If the variable is assigned the empty set, no conversions are"
+ " allowed and it is expected that the types match exactly.",
+ GLOBAL_VAR(slave_type_conversions_options), CMD_LINE(REQUIRED_ARG),
+ slave_type_conversions_name,
+ DEFAULT(0));
+
+static Sys_var_mybool Sys_slave_sql_verify_checksum(
+ "slave_sql_verify_checksum",
+ "Force checksum verification of replication events after reading them "
+ "from relay log. Note: Events are always checksum-verified by slave on "
+ "receiving them from the network before writing them to the relay log",
+ GLOBAL_VAR(opt_slave_sql_verify_checksum), CMD_LINE(OPT_ARG),
+ DEFAULT(TRUE));
+
+static Sys_var_mybool Sys_master_verify_checksum(
+ "master_verify_checksum",
+ "Force checksum verification of logged events in the binary log before "
+ "sending them to slaves or printing them in the output of "
+ "SHOW BINLOG EVENTS",
+ GLOBAL_VAR(opt_master_verify_checksum), CMD_LINE(OPT_ARG),
+ DEFAULT(FALSE));
+
+/* These names must match RPL_SKIP_XXX #defines in slave.h. */
+static const char *replicate_events_marked_for_skip_names[]= {
+ "replicate", "filter_on_slave", "filter_on_master", 0
+};
+static bool
+replicate_events_marked_for_skip_check(sys_var *self, THD *thd,
+ set_var *var)
+{
+ int thread_mask;
+ DBUG_ENTER("sys_var_replicate_events_marked_for_skip_check");
+
+ /* Slave threads must be stopped to change the variable. */
+ mysql_mutex_lock(&LOCK_active_mi);
+ lock_slave_threads(active_mi);
+ init_thread_mask(&thread_mask, active_mi, 0 /*not inverse*/);
+ unlock_slave_threads(active_mi);
+ mysql_mutex_unlock(&LOCK_active_mi);
+
+ if (thread_mask) // We refuse if any slave thread is running
+ {
+ my_error(ER_SLAVE_MUST_STOP, MYF(0));
+ DBUG_RETURN(true);
+ }
+ DBUG_RETURN(false);
+}
+bool
+Sys_var_replicate_events_marked_for_skip::global_update(THD *thd, set_var *var)
+{
+ bool result;
+ int thread_mask;
+ DBUG_ENTER("Sys_var_replicate_events_marked_for_skip::global_update");
+
+ /* Slave threads must be stopped to change the variable. */
+ mysql_mutex_lock(&LOCK_active_mi);
+ lock_slave_threads(active_mi);
+ init_thread_mask(&thread_mask, active_mi, 0 /*not inverse*/);
+ if (thread_mask) // We refuse if any slave thread is running
+ {
+ my_error(ER_SLAVE_MUST_STOP, MYF(0));
+ result= true;
+ }
+ else
+ result= Sys_var_enum::global_update(thd, var);
+
+ unlock_slave_threads(active_mi);
+ mysql_mutex_unlock(&LOCK_active_mi);
+ DBUG_RETURN(result);
+}
+static Sys_var_replicate_events_marked_for_skip Replicate_events_marked_for_skip
+ ("replicate_events_marked_for_skip",
+ "Whether the slave should replicate events that were created with "
+ "@@skip_replication=1 on the master. Default REPLICATE (no events are "
+ "skipped). Other values are FILTER_ON_SLAVE (events will be sent by the "
+ "master but ignored by the slave) and FILTER_ON_MASTER (events marked with "
+ "@@skip_replication=1 will be filtered on the master and never be sent to "
+ "the slave).",
+ GLOBAL_VAR(opt_replicate_events_marked_for_skip), CMD_LINE(REQUIRED_ARG),
+ replicate_events_marked_for_skip_names, DEFAULT(RPL_SKIP_REPLICATE),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(replicate_events_marked_for_skip_check));
+#endif
+
+
+static Sys_var_ulong Sys_slow_launch_time(
+ "slow_launch_time",
+ "If creating the thread takes longer than this value (in seconds), "
+ "the Slow_launch_threads counter will be incremented",
+ GLOBAL_VAR(slow_launch_time), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, LONG_TIMEOUT), DEFAULT(2), BLOCK_SIZE(1));
+
+static Sys_var_ulonglong Sys_sort_buffer(
+ "sort_buffer_size",
+ "Each thread that needs to do a sort allocates a buffer of this size",
+ SESSION_VAR(sortbuff_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(MIN_SORT_MEMORY, SIZE_T_MAX), DEFAULT(MAX_SORT_MEMORY),
+ BLOCK_SIZE(1));
+
+export ulonglong expand_sql_mode(ulonglong sql_mode)
+{
+ if (sql_mode & MODE_ANSI)
+ {
+ /*
+ Note that we dont set
+ MODE_NO_KEY_OPTIONS | MODE_NO_TABLE_OPTIONS | MODE_NO_FIELD_OPTIONS
+ to allow one to get full use of MySQL in this mode.
+
+ MODE_ONLY_FULL_GROUP_BY was removed from ANSI mode because it is
+ currently overly restrictive (see BUG#8510).
+ */
+ sql_mode|= (MODE_REAL_AS_FLOAT | MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES |
+ MODE_IGNORE_SPACE);
+ }
+ if (sql_mode & MODE_ORACLE)
+ 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);
+ if (sql_mode & MODE_MSSQL)
+ sql_mode|= (MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES |
+ MODE_IGNORE_SPACE |
+ MODE_NO_KEY_OPTIONS | MODE_NO_TABLE_OPTIONS |
+ MODE_NO_FIELD_OPTIONS);
+ if (sql_mode & MODE_POSTGRESQL)
+ sql_mode|= (MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES |
+ MODE_IGNORE_SPACE |
+ MODE_NO_KEY_OPTIONS | MODE_NO_TABLE_OPTIONS |
+ MODE_NO_FIELD_OPTIONS);
+ if (sql_mode & MODE_DB2)
+ sql_mode|= (MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES |
+ MODE_IGNORE_SPACE |
+ MODE_NO_KEY_OPTIONS | MODE_NO_TABLE_OPTIONS |
+ MODE_NO_FIELD_OPTIONS);
+ if (sql_mode & MODE_MAXDB)
+ 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);
+ if (sql_mode & MODE_MYSQL40)
+ sql_mode|= MODE_HIGH_NOT_PRECEDENCE;
+ if (sql_mode & MODE_MYSQL323)
+ sql_mode|= MODE_HIGH_NOT_PRECEDENCE;
+ if (sql_mode & MODE_TRADITIONAL)
+ sql_mode|= (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES |
+ MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE |
+ MODE_ERROR_FOR_DIVISION_BY_ZERO | MODE_NO_AUTO_CREATE_USER |
+ MODE_NO_ENGINE_SUBSTITUTION);
+ return sql_mode;
+}
+static bool check_sql_mode(sys_var *self, THD *thd, set_var *var)
+{
+ var->save_result.ulonglong_value=
+ expand_sql_mode(var->save_result.ulonglong_value);
+ return false;
+}
+static bool fix_sql_mode(sys_var *self, THD *thd, enum_var_type type)
+{
+ if (type != OPT_GLOBAL)
+ {
+ /* Update thd->server_status */
+ if (thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES)
+ thd->server_status|= SERVER_STATUS_NO_BACKSLASH_ESCAPES;
+ else
+ thd->server_status&= ~SERVER_STATUS_NO_BACKSLASH_ESCAPES;
+ }
+ return false;
+}
+/*
+ WARNING: When adding new SQL modes don't forget to update the
+ tables definitions that stores it's value (ie: mysql.event, mysql.proc)
+*/
+static const char *sql_mode_names[]=
+{
+ "REAL_AS_FLOAT", "PIPES_AS_CONCAT", "ANSI_QUOTES", "IGNORE_SPACE",
+ "IGNORE_BAD_TABLE_OPTIONS",
+ "ONLY_FULL_GROUP_BY", "NO_UNSIGNED_SUBTRACTION", "NO_DIR_IN_CREATE",
+ "POSTGRESQL", "ORACLE", "MSSQL", "DB2", "MAXDB", "NO_KEY_OPTIONS",
+ "NO_TABLE_OPTIONS", "NO_FIELD_OPTIONS", "MYSQL323", "MYSQL40", "ANSI",
+ "NO_AUTO_VALUE_ON_ZERO", "NO_BACKSLASH_ESCAPES", "STRICT_TRANS_TABLES",
+ "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",
+ 0
+};
+export bool sql_mode_string_representation(THD *thd, ulonglong sql_mode,
+ LEX_STRING *ls)
+{
+ set_to_string(thd, ls, sql_mode, sql_mode_names);
+ return ls->str == 0;
+}
+/*
+ sql_mode should *not* be IN_BINLOG: even though it is written to the binlog,
+ the slave ignores the MODE_NO_DIR_IN_CREATE variable, so slave's value
+ differs from master's (see log_event.cc: Query_log_event::do_apply_event()).
+*/
+static Sys_var_set Sys_sql_mode(
+ "sql_mode",
+ "Syntax: sql-mode=mode[,mode[,mode...]]. See the manual for the "
+ "complete list of valid sql modes",
+ SESSION_VAR(sql_mode), CMD_LINE(REQUIRED_ARG),
+ sql_mode_names, DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(check_sql_mode), ON_UPDATE(fix_sql_mode));
+
+static const char *old_mode_names[]=
+{
+ "NO_DUP_KEY_WARNINGS_WITH_IGNORE", "NO_PROGRESS_INFO",
+ 0
+};
+
+export bool old_mode_string_representation(THD *thd, ulonglong sql_mode,
+ LEX_STRING *ls)
+{
+ set_to_string(thd, ls, sql_mode, old_mode_names);
+ return ls->str == 0;
+}
+/*
+ sql_mode should *not* be IN_BINLOG as the slave can't remember this
+ anyway on restart.
+*/
+static Sys_var_set Sys_old_behavior(
+ "old_mode",
+ "Used to emulate old behavior from earlier MariaDB or MySQL versions. "
+ "Syntax: old_mode=mode[,mode[,mode...]]. "
+ "See the manual for the complete list of valid old modes",
+ SESSION_VAR(old_behavior), CMD_LINE(REQUIRED_ARG),
+ old_mode_names, DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG);
+
+#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
+#define SSL_OPT(X) CMD_LINE(REQUIRED_ARG,X)
+#else
+#define SSL_OPT(X) NO_CMD_LINE
+#endif
+
+static Sys_var_charptr Sys_ssl_ca(
+ "ssl_ca",
+ "CA file in PEM format (check OpenSSL docs, implies --ssl)",
+ READ_ONLY GLOBAL_VAR(opt_ssl_ca), SSL_OPT(OPT_SSL_CA),
+ IN_FS_CHARSET, DEFAULT(0));
+
+static Sys_var_charptr Sys_ssl_capath(
+ "ssl_capath",
+ "CA directory (check OpenSSL docs, implies --ssl)",
+ READ_ONLY GLOBAL_VAR(opt_ssl_capath), SSL_OPT(OPT_SSL_CAPATH),
+ IN_FS_CHARSET, DEFAULT(0));
+
+static Sys_var_charptr Sys_ssl_cert(
+ "ssl_cert", "X509 cert in PEM format (implies --ssl)",
+ READ_ONLY GLOBAL_VAR(opt_ssl_cert), SSL_OPT(OPT_SSL_CERT),
+ IN_FS_CHARSET, DEFAULT(0));
+
+static Sys_var_charptr Sys_ssl_cipher(
+ "ssl_cipher", "SSL cipher to use (implies --ssl)",
+ READ_ONLY GLOBAL_VAR(opt_ssl_cipher), SSL_OPT(OPT_SSL_CIPHER),
+ IN_FS_CHARSET, DEFAULT(0));
+
+static Sys_var_charptr Sys_ssl_key(
+ "ssl_key", "X509 key in PEM format (implies --ssl)",
+ READ_ONLY GLOBAL_VAR(opt_ssl_key), SSL_OPT(OPT_SSL_KEY),
+ IN_FS_CHARSET, DEFAULT(0));
+
+// why ENUM and not BOOL ?
+static const char *updatable_views_with_limit_names[]= {"NO", "YES", 0};
+static Sys_var_enum Sys_updatable_views_with_limit(
+ "updatable_views_with_limit",
+ "YES = Don't issue an error message (warning only) if a VIEW without "
+ "presence of a key of the underlying table is used in queries with a "
+ "LIMIT clause for updating. NO = Prohibit update of a VIEW, which "
+ "does not contain a key of the underlying table and the query uses "
+ "a LIMIT clause (usually get from GUI tools)",
+ SESSION_VAR(updatable_views_with_limit), CMD_LINE(REQUIRED_ARG),
+ updatable_views_with_limit_names, DEFAULT(TRUE));
+
+static Sys_var_mybool Sys_sync_frm(
+ "sync_frm", "Sync .frm files to disk on creation",
+ GLOBAL_VAR(opt_sync_frm), CMD_LINE(OPT_ARG),
+ DEFAULT(TRUE));
+
+static char *system_time_zone_ptr;
+static Sys_var_charptr Sys_system_time_zone(
+ "system_time_zone", "The server system time zone",
+ READ_ONLY GLOBAL_VAR(system_time_zone_ptr), NO_CMD_LINE,
+ IN_SYSTEM_CHARSET, DEFAULT(system_time_zone));
+
+static Sys_var_ulong Sys_table_def_size(
+ "table_definition_cache",
+ "The number of cached table definitions",
+ GLOBAL_VAR(table_def_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(TABLE_DEF_CACHE_MIN, 512*1024),
+ DEFAULT(TABLE_DEF_CACHE_DEFAULT), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_table_cache_size(
+ "table_open_cache", "The number of cached open tables",
+ GLOBAL_VAR(table_cache_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, 512*1024), DEFAULT(TABLE_OPEN_CACHE_DEFAULT),
+ BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_thread_cache_size(
+ "thread_cache_size",
+ "How many threads we should keep in a cache for reuse",
+ GLOBAL_VAR(thread_cache_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, 16384), DEFAULT(0), BLOCK_SIZE(1));
+
+#ifdef HAVE_POOL_OF_THREADS
+static bool fix_tp_max_threads(sys_var *, THD *, enum_var_type)
+{
+#ifdef _WIN32
+ tp_set_max_threads(threadpool_max_threads);
+#endif
+ return false;
+}
+
+
+#ifdef _WIN32
+static bool fix_tp_min_threads(sys_var *, THD *, enum_var_type)
+{
+ tp_set_min_threads(threadpool_min_threads);
+ return false;
+}
+#endif
+
+
+#ifndef _WIN32
+static bool check_threadpool_size(sys_var *self, THD *thd, set_var *var)
+{
+ ulonglong v= var->save_result.ulonglong_value;
+ if (v > threadpool_max_size)
+ {
+ var->save_result.ulonglong_value= threadpool_max_size;
+ return throw_bounds_warning(thd, self->name.str, true, true, v);
+ }
+ return false;
+}
+
+
+static bool fix_threadpool_size(sys_var*, THD*, enum_var_type)
+{
+ tp_set_threadpool_size(threadpool_size);
+ return false;
+}
+
+
+static bool fix_threadpool_stall_limit(sys_var*, THD*, enum_var_type)
+{
+ tp_set_threadpool_stall_limit(threadpool_stall_limit);
+ return false;
+}
+#endif
+
+#ifdef _WIN32
+static Sys_var_uint Sys_threadpool_min_threads(
+ "thread_pool_min_threads",
+ "Minimum number of threads in the thread pool.",
+ GLOBAL_VAR(threadpool_min_threads), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, 256), DEFAULT(1), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_tp_min_threads)
+ );
+#else
+static Sys_var_uint Sys_threadpool_idle_thread_timeout(
+ "thread_pool_idle_timeout",
+ "Timeout in seconds for an idle thread in the thread pool."
+ "Worker thread will be shut down after timeout",
+ GLOBAL_VAR(threadpool_idle_timeout), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, UINT_MAX), DEFAULT(60), BLOCK_SIZE(1)
+);
+static Sys_var_uint Sys_threadpool_oversubscribe(
+ "thread_pool_oversubscribe",
+ "How many additional active worker threads in a group are allowed.",
+ GLOBAL_VAR(threadpool_oversubscribe), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, 1000), DEFAULT(3), BLOCK_SIZE(1)
+);
+static Sys_var_uint Sys_threadpool_size(
+ "thread_pool_size",
+ "Number of thread groups in the pool. "
+ "This parameter is roughly equivalent to maximum number of concurrently "
+ "executing threads (threads in a waiting state do not count as executing).",
+ GLOBAL_VAR(threadpool_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, MAX_THREAD_GROUPS), DEFAULT(my_getncpus()), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_threadpool_size),
+ ON_UPDATE(fix_threadpool_size)
+);
+static Sys_var_uint Sys_threadpool_stall_limit(
+ "thread_pool_stall_limit",
+ "Maximum query execution time in milliseconds,"
+ "before an executing non-yielding thread is considered stalled."
+ "If a worker thread is stalled, additional worker thread "
+ "may be created to handle remaining clients.",
+ GLOBAL_VAR(threadpool_stall_limit), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(10, UINT_MAX), DEFAULT(500), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_threadpool_stall_limit)
+);
+#endif /* !WIN32 */
+static Sys_var_uint Sys_threadpool_max_threads(
+ "thread_pool_max_threads",
+ "Maximum allowed number of worker threads in the thread pool",
+ GLOBAL_VAR(threadpool_max_threads), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, 65536), DEFAULT(500), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_tp_max_threads)
+);
+#endif /* HAVE_POOL_OF_THREADS */
+
+/**
+ Can't change the 'next' tx_isolation if we are already in a
+ transaction.
+*/
+
+static bool check_tx_isolation(sys_var *self, THD *thd, set_var *var)
+{
+ if (var->type == OPT_DEFAULT && thd->in_active_multi_stmt_transaction())
+ {
+ DBUG_ASSERT(thd->in_multi_stmt_transaction_mode());
+ my_error(ER_CANT_CHANGE_TX_ISOLATION, MYF(0));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// NO_CMD_LINE - different name of the option
+static Sys_var_tx_isolation Sys_tx_isolation(
+ "tx_isolation", "Default transaction isolation level",
+ SESSION_VAR(tx_isolation), NO_CMD_LINE,
+ tx_isolation_names, DEFAULT(ISO_REPEATABLE_READ),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_tx_isolation));
+
+static Sys_var_ulonglong Sys_tmp_table_size(
+ "tmp_table_size",
+ "If an internal in-memory temporary table exceeds this size, MySQL "
+ "will automatically convert it to an on-disk MyISAM or Aria table",
+ SESSION_VAR(tmp_table_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1024, (ulonglong)~(intptr)0), DEFAULT(16*1024*1024),
+ BLOCK_SIZE(1));
+
+static Sys_var_mybool Sys_timed_mutexes(
+ "timed_mutexes",
+ "Specify whether to time mutexes. Deprecated, has no effect.",
+ GLOBAL_VAR(timed_mutexes), CMD_LINE(OPT_ARG), DEFAULT(0),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL), ON_UPDATE(NULL),
+ DEPRECATED(""));
+
+static char *server_version_ptr;
+static Sys_var_charptr Sys_version(
+ "version", "Server version",
+ READ_ONLY GLOBAL_VAR(server_version_ptr), NO_CMD_LINE,
+ IN_SYSTEM_CHARSET, DEFAULT(server_version));
+
+static char *server_version_comment_ptr;
+static Sys_var_charptr Sys_version_comment(
+ "version_comment", "version_comment",
+ READ_ONLY GLOBAL_VAR(server_version_comment_ptr), NO_CMD_LINE,
+ IN_SYSTEM_CHARSET, DEFAULT(MYSQL_COMPILATION_COMMENT));
+
+static char *server_version_compile_machine_ptr;
+static Sys_var_charptr Sys_version_compile_machine(
+ "version_compile_machine", "version_compile_machine",
+ READ_ONLY GLOBAL_VAR(server_version_compile_machine_ptr), NO_CMD_LINE,
+ IN_SYSTEM_CHARSET, DEFAULT(MACHINE_TYPE));
+
+static char *server_version_compile_os_ptr;
+static Sys_var_charptr Sys_version_compile_os(
+ "version_compile_os", "version_compile_os",
+ READ_ONLY GLOBAL_VAR(server_version_compile_os_ptr), NO_CMD_LINE,
+ IN_SYSTEM_CHARSET, DEFAULT(SYSTEM_TYPE));
+
+static Sys_var_ulong Sys_net_wait_timeout(
+ "wait_timeout",
+ "The number of seconds the server waits for activity on a "
+ "connection before closing it",
+ SESSION_VAR(net_wait_timeout), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT)),
+ DEFAULT(NET_WAIT_TIMEOUT), BLOCK_SIZE(1));
+
+/** propagates changes to the relevant flag of @@optimizer_switch */
+static bool fix_engine_condition_pushdown(sys_var *self, THD *thd,
+ enum_var_type type)
+{
+ SV *sv= (type == OPT_GLOBAL) ? &global_system_variables : &thd->variables;
+ if (sv->engine_condition_pushdown)
+ sv->optimizer_switch|= OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN;
+ else
+ sv->optimizer_switch&= ~OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN;
+ return false;
+}
+static Sys_var_mybool Sys_engine_condition_pushdown(
+ "engine_condition_pushdown",
+ "Push supported query conditions to the storage engine."
+ " Deprecated, use --optimizer-switch instead.",
+ SESSION_VAR(engine_condition_pushdown),
+ CMD_LINE(OPT_ARG, OPT_ENGINE_CONDITION_PUSHDOWN),
+ DEFAULT(TRUE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL),
+ ON_UPDATE(fix_engine_condition_pushdown),
+ DEPRECATED("'@@optimizer_switch'"));
+
+static Sys_var_plugin Sys_default_storage_engine(
+ "default_storage_engine", "The default storage engine for new tables",
+ SESSION_VAR(table_plugin), NO_CMD_LINE,
+ MYSQL_STORAGE_ENGINE_PLUGIN, DEFAULT(&default_storage_engine),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_not_null));
+
+// Alias for @@default_storage_engine
+static Sys_var_plugin Sys_storage_engine(
+ "storage_engine", "Alias for @@default_storage_engine. Deprecated",
+ SESSION_VAR(table_plugin), NO_CMD_LINE,
+ MYSQL_STORAGE_ENGINE_PLUGIN, DEFAULT(&default_storage_engine),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_not_null));
+
+#if defined(ENABLED_DEBUG_SYNC)
+/*
+ Variable can be set for the session only.
+
+ This could be changed later. Then we need to have a global array of
+ actions in addition to the thread local ones. SET GLOBAL would
+ manage the global array, SET [SESSION] the local array. A sync point
+ would need to look for a local and a global action. Setting and
+ executing of global actions need to be protected by a mutex.
+
+ The purpose of global actions could be to allow synchronizing with
+ connectionless threads that cannot execute SET statements.
+*/
+static Sys_var_debug_sync Sys_debug_sync(
+ "debug_sync", "Debug Sync Facility",
+ sys_var::ONLY_SESSION, NO_CMD_LINE,
+ DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_has_super));
+#endif /* defined(ENABLED_DEBUG_SYNC) */
+
+/**
+ "time_format" "date_format" "datetime_format"
+
+ the following three variables are unused, and the source of confusion
+ (bug reports like "I've changed date_format, but date format hasn't changed.
+ I've made them read-only, to alleviate the situation somewhat.
+
+ @todo make them NO_CMD_LINE ?
+*/
+static Sys_var_charptr Sys_date_format(
+ "date_format", "The DATE format (ignored)",
+ READ_ONLY GLOBAL_VAR(global_date_format.format.str),
+ CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET,
+ DEFAULT(known_date_time_formats[ISO_FORMAT].date_format));
+
+static Sys_var_charptr Sys_datetime_format(
+ "datetime_format", "The DATETIME format (ignored)",
+ READ_ONLY GLOBAL_VAR(global_datetime_format.format.str),
+ CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET,
+ DEFAULT(known_date_time_formats[ISO_FORMAT].datetime_format));
+
+static Sys_var_charptr Sys_time_format(
+ "time_format", "The TIME format (ignored)",
+ READ_ONLY GLOBAL_VAR(global_time_format.format.str),
+ CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET,
+ DEFAULT(known_date_time_formats[ISO_FORMAT].time_format));
+
+static bool fix_autocommit(sys_var *self, THD *thd, enum_var_type type)
+{
+ if (type == OPT_GLOBAL)
+ {
+ if (global_system_variables.option_bits & OPTION_AUTOCOMMIT)
+ global_system_variables.option_bits&= ~OPTION_NOT_AUTOCOMMIT;
+ else
+ global_system_variables.option_bits|= OPTION_NOT_AUTOCOMMIT;
+ return false;
+ }
+
+ if (thd->variables.option_bits & OPTION_AUTOCOMMIT &&
+ thd->variables.option_bits & OPTION_NOT_AUTOCOMMIT)
+ { // activating autocommit
+
+ if (trans_commit_stmt(thd) || trans_commit(thd))
+ {
+ thd->variables.option_bits&= ~OPTION_AUTOCOMMIT;
+ return true;
+ }
+ /*
+ Don't close thread tables or release metadata locks: if we do so, we
+ risk releasing locks/closing tables of expressions used to assign
+ other variables, as in:
+ set @var=my_stored_function1(), @@autocommit=1, @var2=(select max(a)
+ from my_table), ...
+ The locks will be released at statement end anyway, as SET
+ statement that assigns autocommit is marked to commit
+ transaction implicitly at the end (@sa stmt_causes_implicitcommit()).
+ */
+ thd->variables.option_bits&=
+ ~(OPTION_BEGIN | OPTION_KEEP_LOG | OPTION_NOT_AUTOCOMMIT);
+ thd->transaction.all.modified_non_trans_table= false;
+ thd->server_status|= SERVER_STATUS_AUTOCOMMIT;
+ return false;
+ }
+
+ if (!(thd->variables.option_bits & OPTION_AUTOCOMMIT) &&
+ !(thd->variables.option_bits & OPTION_NOT_AUTOCOMMIT))
+ { // disabling autocommit
+
+ thd->transaction.all.modified_non_trans_table= false;
+ thd->server_status&= ~SERVER_STATUS_AUTOCOMMIT;
+ thd->variables.option_bits|= OPTION_NOT_AUTOCOMMIT;
+ return false;
+ }
+
+ return false; // autocommit value wasn't changed
+}
+static Sys_var_bit Sys_autocommit(
+ "autocommit", "autocommit",
+ SESSION_VAR(option_bits), NO_CMD_LINE, OPTION_AUTOCOMMIT, DEFAULT(TRUE),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(fix_autocommit));
+export sys_var *Sys_autocommit_ptr= &Sys_autocommit; // for sql_yacc.yy
+
+static Sys_var_mybool Sys_big_tables(
+ "big_tables", "Allow big result sets by saving all "
+ "temporary sets on file (Solves most 'table full' errors)",
+ SESSION_VAR(big_tables), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
+
+#ifndef TO_BE_DELETED /* Alias for big_tables */
+static Sys_var_mybool Sys_sql_big_tables(
+ "sql_big_tables", "alias for big_tables",
+ SESSION_VAR(big_tables), NO_CMD_LINE, DEFAULT(FALSE));
+#endif
+
+static Sys_var_bit Sys_big_selects(
+ "sql_big_selects", "sql_big_selects",
+ SESSION_VAR(option_bits), NO_CMD_LINE, OPTION_BIG_SELECTS,
+ DEFAULT(FALSE));
+
+static Sys_var_bit Sys_log_off(
+ "sql_log_off", "sql_log_off",
+ SESSION_VAR(option_bits), NO_CMD_LINE, OPTION_LOG_OFF,
+ DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_has_super));
+
+/**
+ This function sets the session variable thd->variables.sql_log_bin
+ to reflect changes to @@session.sql_log_bin.
+
+ @param[IN] self A pointer to the sys_var, i.e. Sys_log_binlog.
+ @param[IN] type The type either session or global.
+
+ @return @c FALSE.
+*/
+static bool fix_sql_log_bin_after_update(sys_var *self, THD *thd,
+ enum_var_type type)
+{
+ if (type == OPT_SESSION)
+ {
+ if (thd->variables.sql_log_bin)
+ thd->variables.option_bits |= OPTION_BIN_LOG;
+ else
+ thd->variables.option_bits &= ~OPTION_BIN_LOG;
+ }
+ return FALSE;
+}
+
+/**
+ This function checks if the sql_log_bin can be changed,
+ what is possible if:
+ - the user is a super user;
+ - the set is not called from within a function/trigger;
+ - there is no on-going transaction.
+
+ @param[IN] self A pointer to the sys_var, i.e. Sys_log_binlog.
+ @param[IN] var A pointer to the set_var created by the parser.
+
+ @return @c FALSE if the change is allowed, otherwise @c TRUE.
+*/
+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)
+ return FALSE;
+
+ if (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;
+}
+
+static Sys_var_mybool Sys_log_binlog(
+ "sql_log_bin", "sql_log_bin",
+ SESSION_VAR(sql_log_bin), NO_CMD_LINE,
+ DEFAULT(TRUE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_sql_log_bin),
+ ON_UPDATE(fix_sql_log_bin_after_update));
+
+static Sys_var_bit Sys_sql_warnings(
+ "sql_warnings", "sql_warnings",
+ SESSION_VAR(option_bits), NO_CMD_LINE, OPTION_WARNINGS,
+ DEFAULT(FALSE));
+
+static Sys_var_bit Sys_sql_notes(
+ "sql_notes", "sql_notes",
+ SESSION_VAR(option_bits), NO_CMD_LINE, OPTION_SQL_NOTES,
+ DEFAULT(TRUE));
+
+static Sys_var_bit Sys_auto_is_null(
+ "sql_auto_is_null", "sql_auto_is_null",
+ SESSION_VAR(option_bits), NO_CMD_LINE, OPTION_AUTO_IS_NULL,
+ DEFAULT(FALSE), NO_MUTEX_GUARD, IN_BINLOG);
+
+static Sys_var_bit Sys_safe_updates(
+ "sql_safe_updates", "sql_safe_updates",
+ SESSION_VAR(option_bits), NO_CMD_LINE, OPTION_SAFE_UPDATES,
+ DEFAULT(FALSE));
+
+static Sys_var_bit Sys_buffer_results(
+ "sql_buffer_result", "sql_buffer_result",
+ SESSION_VAR(option_bits), NO_CMD_LINE, OPTION_BUFFER_RESULT,
+ DEFAULT(FALSE));
+
+static Sys_var_bit Sys_quote_show_create(
+ "sql_quote_show_create", "sql_quote_show_create",
+ SESSION_VAR(option_bits), NO_CMD_LINE, OPTION_QUOTE_SHOW_CREATE,
+ DEFAULT(TRUE));
+
+static Sys_var_bit Sys_foreign_key_checks(
+ "foreign_key_checks", "foreign_key_checks",
+ SESSION_VAR(option_bits), NO_CMD_LINE,
+ REVERSE(OPTION_NO_FOREIGN_KEY_CHECKS),
+ DEFAULT(TRUE), NO_MUTEX_GUARD, IN_BINLOG);
+
+static Sys_var_bit Sys_unique_checks(
+ "unique_checks", "unique_checks",
+ SESSION_VAR(option_bits), NO_CMD_LINE,
+ REVERSE(OPTION_RELAXED_UNIQUE_CHECKS),
+ DEFAULT(TRUE), NO_MUTEX_GUARD, IN_BINLOG);
+
+#ifdef ENABLED_PROFILING
+static Sys_var_bit Sys_profiling(
+ "profiling", "profiling",
+ SESSION_VAR(option_bits), NO_CMD_LINE, OPTION_PROFILING,
+ DEFAULT(FALSE));
+
+static Sys_var_ulong Sys_profiling_history_size(
+ "profiling_history_size", "Limit of query profiling memory",
+ SESSION_VAR(profiling_history_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, 100), DEFAULT(15), BLOCK_SIZE(1));
+#endif
+
+/*
+ When this is set by a connection, binlogged events will be marked with a
+ corresponding flag. The slave can be configured to not replicate events
+ so marked.
+ In the binlog dump thread on the master, this variable is re-used for a
+ related purpose: The slave sets this flag when connecting to the master to
+ request that the master filter out (ie. not send) any events with the flag
+ set, thus saving network traffic on events that would be ignored by the
+ slave anyway.
+*/
+static bool check_skip_replication(sys_var *self, THD *thd, set_var *var)
+{
+ /*
+ We must not change @@skip_replication in the middle of a transaction or
+ statement, as that could result in only part of the transaction / statement
+ being replicated.
+ (This would be particularly serious if we were to replicate eg.
+ 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))
+ return 1;
+
+ return 0;
+}
+
+static Sys_var_bit Sys_skip_replication(
+ "skip_replication", "skip_replication",
+ SESSION_ONLY(option_bits), NO_CMD_LINE, OPTION_SKIP_REPLICATION,
+ DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(check_skip_replication));
+
+static Sys_var_harows Sys_select_limit(
+ "sql_select_limit",
+ "The maximum number of rows to return from SELECT statements",
+ SESSION_VAR(select_limit), NO_CMD_LINE,
+ VALID_RANGE(0, HA_POS_ERROR), DEFAULT(HA_POS_ERROR), BLOCK_SIZE(1));
+
+static bool update_timestamp(THD *thd, set_var *var)
+{
+ if (var->value)
+ {
+ my_hrtime_t hrtime = { hrtime_from_time(var->save_result.double_value) };
+ thd->set_time(hrtime);
+ }
+ else // SET timestamp=DEFAULT
+ thd->user_time.val= 0;
+ return false;
+}
+static double read_timestamp(THD *thd)
+{
+ return thd->start_time +
+ thd->start_time_sec_part/(double)TIME_SECOND_PART_FACTOR;
+}
+static Sys_var_session_special_double Sys_timestamp(
+ "timestamp", "Set the time for this client",
+ sys_var::ONLY_SESSION, NO_CMD_LINE,
+ VALID_RANGE(0, TIMESTAMP_MAX_VALUE),
+ NO_MUTEX_GUARD, IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(update_timestamp), ON_READ(read_timestamp));
+
+static bool update_last_insert_id(THD *thd, set_var *var)
+{
+ if (!var->value)
+ {
+ my_error(ER_NO_DEFAULT, MYF(0), var->var->name.str);
+ return true;
+ }
+ thd->first_successful_insert_id_in_prev_stmt=
+ var->save_result.ulonglong_value;
+ return false;
+}
+static ulonglong read_last_insert_id(THD *thd)
+{
+ return (ulonglong) thd->read_first_successful_insert_id_in_prev_stmt();
+}
+static Sys_var_session_special Sys_last_insert_id(
+ "last_insert_id", "The value to be returned from LAST_INSERT_ID()",
+ sys_var::ONLY_SESSION, NO_CMD_LINE,
+ VALID_RANGE(0, ULONGLONG_MAX), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(update_last_insert_id), ON_READ(read_last_insert_id));
+
+// alias for last_insert_id(), Sybase-style
+static Sys_var_session_special Sys_identity(
+ "identity", "Synonym for the last_insert_id variable",
+ sys_var::ONLY_SESSION, NO_CMD_LINE,
+ VALID_RANGE(0, ULONGLONG_MAX), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(update_last_insert_id), ON_READ(read_last_insert_id));
+
+/*
+ insert_id should *not* be marked as written to the binlog (i.e., it
+ should *not* be IN_BINLOG), because we want any statement that
+ refers to insert_id explicitly to be unsafe. (By "explicitly", we
+ mean using @@session.insert_id, whereas insert_id is used
+ "implicitly" when NULL value is inserted into an auto_increment
+ column).
+
+ We want statements referring explicitly to @@session.insert_id to be
+ unsafe, because insert_id is modified internally by the slave sql
+ thread when NULL values are inserted in an AUTO_INCREMENT column.
+ This modification interfers with the value of the
+ @@session.insert_id variable if @@session.insert_id is referred
+ explicitly by an insert statement (as is seen by executing "SET
+ @@session.insert_id=0; CREATE TABLE t (a INT, b INT KEY
+ AUTO_INCREMENT); INSERT INTO t(a) VALUES (@@session.insert_id);" in
+ statement-based logging mode: t will be different on master and
+ slave).
+*/
+static bool update_insert_id(THD *thd, set_var *var)
+{
+ if (!var->value)
+ {
+ my_error(ER_NO_DEFAULT, MYF(0), var->var->name.str);
+ return true;
+ }
+ thd->force_one_auto_inc_interval(var->save_result.ulonglong_value);
+ return false;
+}
+
+static ulonglong read_insert_id(THD *thd)
+{
+ return thd->auto_inc_intervals_forced.minimum();
+}
+static Sys_var_session_special Sys_insert_id(
+ "insert_id", "The value to be used by the following INSERT "
+ "or ALTER TABLE statement when inserting an AUTO_INCREMENT value",
+ sys_var::ONLY_SESSION, NO_CMD_LINE,
+ VALID_RANGE(0, ULONGLONG_MAX), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(update_insert_id), ON_READ(read_insert_id));
+
+static bool update_rand_seed1(THD *thd, set_var *var)
+{
+ if (!var->value)
+ {
+ my_error(ER_NO_DEFAULT, MYF(0), var->var->name.str);
+ return true;
+ }
+ thd->rand.seed1= (ulong) var->save_result.ulonglong_value;
+ return false;
+}
+static ulonglong read_rand_seed(THD *thd)
+{
+ return 0;
+}
+static Sys_var_session_special Sys_rand_seed1(
+ "rand_seed1", "Sets the internal state of the RAND() "
+ "generator for replication purposes",
+ sys_var::ONLY_SESSION, NO_CMD_LINE,
+ VALID_RANGE(0, ULONG_MAX), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(update_rand_seed1), ON_READ(read_rand_seed));
+
+static bool update_rand_seed2(THD *thd, set_var *var)
+{
+ if (!var->value)
+ {
+ my_error(ER_NO_DEFAULT, MYF(0), var->var->name.str);
+ return true;
+ }
+ thd->rand.seed2= (ulong) var->save_result.ulonglong_value;
+ return false;
+}
+static Sys_var_session_special Sys_rand_seed2(
+ "rand_seed2", "Sets the internal state of the RAND() "
+ "generator for replication purposes",
+ sys_var::ONLY_SESSION, NO_CMD_LINE,
+ VALID_RANGE(0, ULONG_MAX), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(update_rand_seed2), ON_READ(read_rand_seed));
+
+static ulonglong read_error_count(THD *thd)
+{
+ return thd->warning_info->error_count();
+}
+// this really belongs to the SHOW STATUS
+static Sys_var_session_special Sys_error_count(
+ "error_count", "The number of errors that resulted from the "
+ "last statement that generated messages",
+ READ_ONLY sys_var::ONLY_SESSION, NO_CMD_LINE,
+ VALID_RANGE(0, ULONGLONG_MAX), BLOCK_SIZE(1), NO_MUTEX_GUARD,
+ NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0), ON_READ(read_error_count));
+
+static ulonglong read_warning_count(THD *thd)
+{
+ return thd->warning_info->warn_count();
+}
+// this really belongs to the SHOW STATUS
+static Sys_var_session_special Sys_warning_count(
+ "warning_count", "The number of errors, warnings, and notes "
+ "that resulted from the last statement that generated messages",
+ READ_ONLY sys_var::ONLY_SESSION, NO_CMD_LINE,
+ VALID_RANGE(0, ULONGLONG_MAX), BLOCK_SIZE(1), NO_MUTEX_GUARD,
+ NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0), ON_READ(read_warning_count));
+
+static Sys_var_ulong Sys_default_week_format(
+ "default_week_format",
+ "The default week format used by WEEK() functions",
+ SESSION_VAR(default_week_format), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, 7), DEFAULT(0), BLOCK_SIZE(1));
+
+static Sys_var_ulonglong Sys_group_concat_max_len(
+ "group_concat_max_len",
+ "The maximum length of the result of function GROUP_CONCAT()",
+ SESSION_VAR(group_concat_max_len), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(4, SIZE_T_MAX), DEFAULT(1024), BLOCK_SIZE(1));
+
+static char *glob_hostname_ptr;
+static Sys_var_charptr Sys_hostname(
+ "hostname", "Server host name",
+ READ_ONLY GLOBAL_VAR(glob_hostname_ptr), NO_CMD_LINE,
+ IN_FS_CHARSET, DEFAULT(glob_hostname));
+
+#ifndef EMBEDDED_LIBRARY
+static Sys_var_charptr Sys_repl_report_host(
+ "report_host",
+ "Hostname or IP of the slave to be reported to the master during "
+ "slave registration. Will appear in the output of SHOW SLAVE HOSTS. "
+ "Leave unset if you do not want the slave to register itself with the "
+ "master. Note that it is not sufficient for the master to simply read "
+ "the IP of the slave off the socket once the slave connects. Due to "
+ "NAT and other routing issues, that IP may not be valid for connecting "
+ "to the slave from the master or other hosts",
+ READ_ONLY GLOBAL_VAR(report_host), CMD_LINE(REQUIRED_ARG),
+ IN_FS_CHARSET, DEFAULT(0));
+
+static Sys_var_charptr Sys_repl_report_user(
+ "report_user",
+ "The account user name of the slave to be reported to the master "
+ "during slave registration",
+ READ_ONLY GLOBAL_VAR(report_user), CMD_LINE(REQUIRED_ARG),
+ IN_FS_CHARSET, DEFAULT(0));
+
+static Sys_var_charptr Sys_repl_report_password(
+ "report_password",
+ "The account password of the slave to be reported to the master "
+ "during slave registration",
+ READ_ONLY GLOBAL_VAR(report_password), CMD_LINE(REQUIRED_ARG),
+ IN_FS_CHARSET, DEFAULT(0));
+
+static Sys_var_uint Sys_repl_report_port(
+ "report_port",
+ "Port for connecting to slave reported to the master during slave "
+ "registration. Set it only if the slave is listening on a non-default "
+ "port or if you have a special tunnel from the master or other clients "
+ "to the slave. If not sure, leave this option unset",
+ READ_ONLY GLOBAL_VAR(report_port), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1));
+#endif
+
+static Sys_var_mybool Sys_keep_files_on_create(
+ "keep_files_on_create",
+ "Don't overwrite stale .MYD and .MYI even if no directory is specified",
+ SESSION_VAR(keep_files_on_create), CMD_LINE(OPT_ARG),
+ DEFAULT(FALSE));
+
+static char *license;
+static Sys_var_charptr Sys_license(
+ "license", "The type of license the server has",
+ READ_ONLY GLOBAL_VAR(license), NO_CMD_LINE, IN_SYSTEM_CHARSET,
+ DEFAULT(STRINGIFY_ARG(LICENSE)));
+
+static bool check_log_path(sys_var *self, THD *thd, set_var *var)
+{
+ if (!var->value)
+ return false; // DEFAULT is ok
+
+ if (!var->save_result.string_value.str)
+ return true;
+
+ if (var->save_result.string_value.length > FN_REFLEN)
+ { // path is too long
+ my_error(ER_PATH_LENGTH, MYF(0), self->name.str);
+ return true;
+ }
+
+ char path[FN_REFLEN];
+ size_t path_length= unpack_filename(path, var->save_result.string_value.str);
+
+ if (!path_length)
+ return true;
+
+ MY_STAT f_stat;
+
+ if (my_stat(path, &f_stat, MYF(0)))
+ {
+ if (!MY_S_ISREG(f_stat.st_mode) || !(f_stat.st_mode & MY_S_IWRITE))
+ return true; // not a regular writable file
+ return false;
+ }
+
+ (void) dirname_part(path, var->save_result.string_value.str, &path_length);
+
+ if (var->save_result.string_value.length - path_length >= FN_LEN)
+ { // filename is too long
+ my_error(ER_PATH_LENGTH, MYF(0), self->name.str);
+ return true;
+ }
+
+ if (!path_length) // no path is good path (remember, relative to datadir)
+ return false;
+
+ if (my_access(path, (F_OK|W_OK)))
+ return true; // directory is not writable
+
+ return false;
+}
+static bool fix_log(char** logname, const char* default_logname,
+ const char*ext, bool enabled, void (*reopen)(char*))
+{
+ if (!*logname) // SET ... = DEFAULT
+ {
+ make_default_log_name(logname, ext, false);
+ if (!*logname)
+ return true;
+ }
+ logger.lock_exclusive();
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ if (enabled)
+ reopen(*logname);
+ logger.unlock();
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ return false;
+}
+static void reopen_general_log(char* name)
+{
+ logger.get_log_file_handler()->close(0);
+ logger.get_log_file_handler()->open_query_log(name);
+}
+static bool fix_general_log_file(sys_var *self, THD *thd, enum_var_type type)
+{
+ return fix_log(&opt_logname, opt_log_basename, ".log", opt_log,
+ reopen_general_log);
+}
+static Sys_var_charptr Sys_general_log_path(
+ "general_log_file", "Log connections and queries to given file",
+ PREALLOCATED GLOBAL_VAR(opt_logname), CMD_LINE(REQUIRED_ARG),
+ IN_FS_CHARSET, DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(check_log_path), ON_UPDATE(fix_general_log_file));
+
+static void reopen_slow_log(char* name)
+{
+ logger.get_slow_log_file_handler()->close(0);
+ logger.get_slow_log_file_handler()->open_slow_log(name);
+}
+static bool fix_slow_log_file(sys_var *self, THD *thd, enum_var_type type)
+{
+ return fix_log(&opt_slow_logname, opt_log_basename, "-slow.log",
+ opt_slow_log, reopen_slow_log);
+}
+static Sys_var_charptr Sys_slow_log_path(
+ "slow_query_log_file", "Log slow queries to given log file. "
+ "Defaults logging to 'hostname'-slow.log. Must be enabled to activate "
+ "other slow log options",
+ PREALLOCATED GLOBAL_VAR(opt_slow_logname), CMD_LINE(REQUIRED_ARG),
+ IN_FS_CHARSET, DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(check_log_path), ON_UPDATE(fix_slow_log_file));
+
+/// @todo deprecate these four legacy have_PLUGIN variables and use I_S instead
+export SHOW_COMP_OPTION have_csv, have_innodb= SHOW_OPTION_DISABLED;
+export SHOW_COMP_OPTION have_ndbcluster, have_partitioning;
+static Sys_var_have Sys_have_csv(
+ "have_csv", "have_csv",
+ READ_ONLY GLOBAL_VAR(have_csv), NO_CMD_LINE);
+
+static Sys_var_have Sys_have_innodb(
+ "have_innodb", "have_innodb",
+ READ_ONLY GLOBAL_VAR(have_innodb), NO_CMD_LINE);
+
+static Sys_var_have Sys_have_ndbcluster(
+ "have_ndbcluster", "have_ndbcluster",
+ READ_ONLY GLOBAL_VAR(have_ndbcluster), NO_CMD_LINE);
+
+static Sys_var_have Sys_have_partition_db(
+ "have_partitioning", "have_partitioning",
+ READ_ONLY GLOBAL_VAR(have_partitioning), NO_CMD_LINE);
+
+static Sys_var_have Sys_have_compress(
+ "have_compress", "have_compress",
+ READ_ONLY GLOBAL_VAR(have_compress), NO_CMD_LINE);
+
+static Sys_var_have Sys_have_crypt(
+ "have_crypt", "have_crypt",
+ READ_ONLY GLOBAL_VAR(have_crypt), NO_CMD_LINE);
+
+static Sys_var_have Sys_have_dlopen(
+ "have_dynamic_loading", "have_dynamic_loading",
+ READ_ONLY GLOBAL_VAR(have_dlopen), NO_CMD_LINE);
+
+static Sys_var_have Sys_have_geometry(
+ "have_geometry", "have_geometry",
+ READ_ONLY GLOBAL_VAR(have_geometry), NO_CMD_LINE);
+
+static Sys_var_have Sys_have_openssl(
+ "have_openssl", "have_openssl",
+ READ_ONLY GLOBAL_VAR(have_ssl), NO_CMD_LINE);
+
+static Sys_var_have Sys_have_profiling(
+ "have_profiling", "have_profiling",
+ READ_ONLY GLOBAL_VAR(have_profiling), NO_CMD_LINE);
+
+static Sys_var_have Sys_have_query_cache(
+ "have_query_cache", "have_query_cache",
+ READ_ONLY GLOBAL_VAR(have_query_cache), NO_CMD_LINE);
+
+static Sys_var_have Sys_have_rtree_keys(
+ "have_rtree_keys", "have_rtree_keys",
+ READ_ONLY GLOBAL_VAR(have_rtree_keys), NO_CMD_LINE);
+
+static Sys_var_have Sys_have_ssl(
+ "have_ssl", "have_ssl",
+ READ_ONLY GLOBAL_VAR(have_ssl), NO_CMD_LINE);
+
+static Sys_var_have Sys_have_symlink(
+ "have_symlink", "have_symlink",
+ READ_ONLY GLOBAL_VAR(have_symlink), NO_CMD_LINE);
+
+static bool fix_log_state(sys_var *self, THD *thd, enum_var_type type);
+static Sys_var_mybool Sys_general_log(
+ "general_log", "Log connections and queries to a table or log file. "
+ "Defaults logging to a file 'hostname'.log or a table mysql.general_log"
+ "if --log-output=TABLE is used",
+ GLOBAL_VAR(opt_log), CMD_LINE(OPT_ARG),
+ DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_log_state));
+
+// Synonym of "general_log" for consistency with SHOW VARIABLES output
+static Sys_var_mybool Sys_log(
+ "log", "Alias for --general-log. Deprecated",
+ GLOBAL_VAR(opt_log), NO_CMD_LINE,
+ DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_log_state), DEPRECATED("'@@general_log'"));
+
+static Sys_var_mybool Sys_slow_query_log(
+ "slow_query_log",
+ "Log slow queries to a table or log file. Defaults logging to a file "
+ "'hostname'-slow.log or a table mysql.slow_log if --log-output=TABLE is "
+ "used. Must be enabled to activate other slow log options",
+ GLOBAL_VAR(opt_slow_log), CMD_LINE(OPT_ARG),
+ DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_log_state));
+
+/* Synonym of "slow_query_log" for consistency with SHOW VARIABLES output */
+static Sys_var_mybool Sys_log_slow(
+ "log_slow_queries",
+ "Alias for --slow-query-log. Deprecated",
+ GLOBAL_VAR(opt_slow_log), NO_CMD_LINE,
+ DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_log_state), DEPRECATED("'@@slow_query_log'"));
+
+static bool fix_log_state(sys_var *self, THD *thd, enum_var_type type)
+{
+ bool res;
+ my_bool *UNINIT_VAR(newvalptr), newval, UNINIT_VAR(oldval);
+ uint UNINIT_VAR(log_type);
+
+ if (self == &Sys_general_log || self == &Sys_log)
+ {
+ newvalptr= &opt_log;
+ oldval= logger.get_log_file_handler()->is_open();
+ log_type= QUERY_LOG_GENERAL;
+ }
+ else if (self == &Sys_slow_query_log || self == &Sys_log_slow)
+ {
+ newvalptr= &opt_slow_log;
+ oldval= logger.get_slow_log_file_handler()->is_open();
+ log_type= QUERY_LOG_SLOW;
+ }
+ else
+ DBUG_ASSERT(FALSE);
+
+ newval= *newvalptr;
+ if (oldval == newval)
+ return false;
+
+ *newvalptr= oldval; // [de]activate_log_handler works that way (sigh)
+
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ if (!newval)
+ {
+ logger.deactivate_log_handler(thd, log_type);
+ res= false;
+ }
+ else
+ res= logger.activate_log_handler(thd, log_type);
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ return res;
+}
+
+static bool check_not_empty_set(sys_var *self, THD *thd, set_var *var)
+{
+ return var->save_result.ulonglong_value == 0;
+}
+static bool fix_log_output(sys_var *self, THD *thd, enum_var_type type)
+{
+ logger.lock_exclusive();
+ logger.init_slow_log(log_output_options);
+ logger.init_general_log(log_output_options);
+ logger.unlock();
+ return false;
+}
+
+static const char *log_output_names[] = { "NONE", "FILE", "TABLE", NULL};
+
+static Sys_var_set Sys_log_output(
+ "log_output", "Syntax: log-output=value[,value...], "
+ "where \"value\" could be TABLE, FILE or NONE",
+ GLOBAL_VAR(log_output_options), CMD_LINE(REQUIRED_ARG),
+ log_output_names, DEFAULT(LOG_FILE), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(check_not_empty_set), ON_UPDATE(fix_log_output));
+
+#ifdef HAVE_REPLICATION
+static Sys_var_mybool Sys_log_slave_updates(
+ "log_slave_updates", "Tells the slave to log the updates from "
+ "the slave thread to the binary log. You will need to turn it on if "
+ "you plan to daisy-chain the slaves",
+ READ_ONLY GLOBAL_VAR(opt_log_slave_updates), CMD_LINE(OPT_ARG),
+ DEFAULT(0));
+
+static Sys_var_charptr Sys_relay_log(
+ "relay_log", "The location and name to use for relay logs",
+ READ_ONLY GLOBAL_VAR(opt_relay_logname), CMD_LINE(REQUIRED_ARG),
+ IN_FS_CHARSET, DEFAULT(0));
+
+static Sys_var_charptr Sys_relay_log_index(
+ "relay_log_index", "The location and name to use for the file "
+ "that keeps a list of the last relay logs",
+ READ_ONLY GLOBAL_VAR(opt_relaylog_index_name), CMD_LINE(REQUIRED_ARG),
+ IN_FS_CHARSET, DEFAULT(0));
+
+static Sys_var_charptr Sys_relay_log_info_file(
+ "relay_log_info_file", "The location and name of the file that "
+ "remembers where the SQL replication thread is in the relay logs",
+ READ_ONLY GLOBAL_VAR(relay_log_info_file), CMD_LINE(REQUIRED_ARG),
+ IN_FS_CHARSET, DEFAULT(0));
+
+static Sys_var_mybool Sys_relay_log_purge(
+ "relay_log_purge", "if disabled - do not purge relay logs. "
+ "if enabled - purge them as soon as they are no more needed",
+ GLOBAL_VAR(relay_log_purge), CMD_LINE(OPT_ARG), DEFAULT(TRUE));
+
+static Sys_var_mybool Sys_relay_log_recovery(
+ "relay_log_recovery", "Enables automatic relay log recovery "
+ "right after the database startup, which means that the IO Thread "
+ "starts re-fetching from the master right after the last transaction "
+ "processed",
+ GLOBAL_VAR(relay_log_recovery), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
+
+bool Sys_var_rpl_filter::do_check(THD *thd, set_var *var)
+{
+ bool status;
+
+ /*
+ We must not be holding LOCK_global_system_variables here, otherwise we can
+ deadlock with THD::init() which is invoked from within the slave threads
+ with opposite locking order.
+ */
+ mysql_mutex_assert_not_owner(&LOCK_global_system_variables);
+
+ mysql_mutex_lock(&LOCK_active_mi);
+ mysql_mutex_lock(&active_mi->rli.run_lock);
+
+ status= active_mi->rli.slave_running;
+
+ mysql_mutex_unlock(&active_mi->rli.run_lock);
+ mysql_mutex_unlock(&LOCK_active_mi);
+
+ if (status)
+ my_error(ER_SLAVE_MUST_STOP, MYF(0));
+ else
+ status= Sys_var_charptr::do_string_check(thd, var, charset(thd));
+
+ return status;
+}
+
+void Sys_var_rpl_filter::lock(void)
+{
+ /*
+ Starting a slave thread causes the new thread to attempt to
+ acquire LOCK_global_system_variables (in THD::init) while
+ LOCK_active_mi is being held by the thread that initiated
+ the process. In order to not violate the lock order, unlock
+ LOCK_global_system_variables before grabbing LOCK_active_mi.
+ */
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+
+ mysql_mutex_lock(&LOCK_active_mi);
+ mysql_mutex_lock(&active_mi->rli.run_lock);
+}
+
+void Sys_var_rpl_filter::unlock(void)
+{
+ mysql_mutex_unlock(&active_mi->rli.run_lock);
+ mysql_mutex_unlock(&LOCK_active_mi);
+
+ mysql_mutex_lock(&LOCK_global_system_variables);
+}
+
+bool Sys_var_rpl_filter::global_update(THD *thd, set_var *var)
+{
+ bool slave_running, status= false;
+
+ lock();
+
+ if (! (slave_running= active_mi->rli.slave_running))
+ status= set_filter_value(var->save_result.string_value.str);
+
+ if (slave_running)
+ my_error(ER_SLAVE_MUST_STOP, MYF(0));
+
+ unlock();
+
+ return slave_running || status;
+}
+
+bool Sys_var_rpl_filter::set_filter_value(const char *value)
+{
+ bool status= true;
+
+ switch (opt_id) {
+ case OPT_REPLICATE_DO_DB:
+ status= rpl_filter->set_do_db(value);
+ break;
+ case OPT_REPLICATE_DO_TABLE:
+ status= rpl_filter->set_do_table(value);
+ break;
+ case OPT_REPLICATE_IGNORE_DB:
+ status= rpl_filter->set_ignore_db(value);
+ break;
+ case OPT_REPLICATE_IGNORE_TABLE:
+ status= rpl_filter->set_ignore_table(value);
+ break;
+ case OPT_REPLICATE_WILD_DO_TABLE:
+ status= rpl_filter->set_wild_do_table(value);
+ break;
+ case OPT_REPLICATE_WILD_IGNORE_TABLE:
+ status= rpl_filter->set_wild_ignore_table(value);
+ break;
+ }
+
+ return status;
+}
+
+uchar *Sys_var_rpl_filter::global_value_ptr(THD *thd, LEX_STRING *base)
+{
+ char buf[256];
+ String tmp(buf, sizeof(buf), &my_charset_bin);
+
+ tmp.length(0);
+
+ lock();
+
+ switch (opt_id) {
+ case OPT_REPLICATE_DO_DB:
+ rpl_filter->get_do_db(&tmp);
+ break;
+ case OPT_REPLICATE_DO_TABLE:
+ rpl_filter->get_do_table(&tmp);
+ break;
+ case OPT_REPLICATE_IGNORE_DB:
+ rpl_filter->get_ignore_db(&tmp);
+ break;
+ case OPT_REPLICATE_IGNORE_TABLE:
+ rpl_filter->get_ignore_table(&tmp);
+ break;
+ case OPT_REPLICATE_WILD_DO_TABLE:
+ rpl_filter->get_wild_do_table(&tmp);
+ break;
+ case OPT_REPLICATE_WILD_IGNORE_TABLE:
+ rpl_filter->get_wild_ignore_table(&tmp);
+ break;
+ }
+
+ unlock();
+
+ return (uchar *) thd->strmake(tmp.ptr(), tmp.length());
+}
+
+static Sys_var_rpl_filter Sys_replicate_do_db(
+ "replicate_do_db", OPT_REPLICATE_DO_DB,
+ "Tell the slave to restrict replication to updates of tables "
+ "whose names appear in the comma-separated list. For "
+ "statement-based replication, only the default database (that "
+ "is, the one selected by USE) is considered, not any explicitly "
+ "mentioned tables in the query. For row-based replication, the "
+ "actual names of table(s) being updated are checked.");
+
+static Sys_var_rpl_filter Sys_replicate_do_table(
+ "replicate_do_table", OPT_REPLICATE_DO_TABLE,
+ "Tells the slave to restrict replication to tables in the "
+ "comma-separated list.");
+
+static Sys_var_rpl_filter Sys_replicate_ignore_db(
+ "replicate_ignore_db", OPT_REPLICATE_IGNORE_DB,
+ "Tell the slave to restrict replication to updates of tables "
+ "whose names do not appear in the comma-separated list. For "
+ "statement-based replication, only the default database (that "
+ "is, the one selected by USE) is considered, not any explicitly "
+ "mentioned tables in the query. For row-based replication, the "
+ "actual names of table(s) being updated are checked.");
+
+static Sys_var_rpl_filter Sys_replicate_ignore_table(
+ "replicate_ignore_table", OPT_REPLICATE_IGNORE_TABLE,
+ "Tells the slave thread not to replicate any statement that "
+ "updates the specified table, even if any other tables might be "
+ "updated by the same statement.");
+
+static Sys_var_rpl_filter Sys_replicate_wild_do_table(
+ "replicate_wild_do_table", OPT_REPLICATE_WILD_DO_TABLE,
+ "Tells the slave thread to restrict replication to statements "
+ "where any of the updated tables match the specified database "
+ "and table name patterns.");
+
+static Sys_var_rpl_filter Sys_replicate_wild_ignore_table(
+ "replicate_wild_ignore_table", OPT_REPLICATE_WILD_IGNORE_TABLE,
+ "Tells the slave thread to not replicate to the tables that "
+ "match the given wildcard pattern.");
+
+static Sys_var_charptr Sys_slave_load_tmpdir(
+ "slave_load_tmpdir", "The location where the slave should put "
+ "its temporary files when replicating a LOAD DATA INFILE command",
+ READ_ONLY GLOBAL_VAR(slave_load_tmpdir), CMD_LINE(REQUIRED_ARG),
+ IN_FS_CHARSET, DEFAULT(0));
+
+static bool fix_slave_net_timeout(sys_var *self, THD *thd, enum_var_type type)
+{
+ DEBUG_SYNC(thd, "fix_slave_net_timeout");
+
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ mysql_mutex_lock(&LOCK_active_mi);
+ DBUG_PRINT("info", ("slave_net_timeout=%u mi->heartbeat_period=%.3f",
+ slave_net_timeout,
+ (active_mi? active_mi->heartbeat_period : 0.0)));
+ if (active_mi && slave_net_timeout < active_mi->heartbeat_period)
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX,
+ ER(ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX));
+ mysql_mutex_unlock(&LOCK_active_mi);
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ return false;
+}
+static Sys_var_uint Sys_slave_net_timeout(
+ "slave_net_timeout", "Number of seconds to wait for more data "
+ "from a master/slave connection before aborting the read",
+ GLOBAL_VAR(slave_net_timeout), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, LONG_TIMEOUT), DEFAULT(SLAVE_NET_TIMEOUT), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_slave_net_timeout));
+
+static bool check_slave_skip_counter(sys_var *self, THD *thd, set_var *var)
+{
+ bool result= false;
+ mysql_mutex_lock(&LOCK_active_mi);
+ mysql_mutex_lock(&active_mi->rli.run_lock);
+ if (active_mi->rli.slave_running)
+ {
+ my_message(ER_SLAVE_MUST_STOP, ER(ER_SLAVE_MUST_STOP), MYF(0));
+ result= true;
+ }
+ mysql_mutex_unlock(&active_mi->rli.run_lock);
+ mysql_mutex_unlock(&LOCK_active_mi);
+ return result;
+}
+static bool fix_slave_skip_counter(sys_var *self, THD *thd, enum_var_type type)
+{
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ mysql_mutex_lock(&LOCK_active_mi);
+ mysql_mutex_lock(&active_mi->rli.run_lock);
+ /*
+ The following test should normally never be true as we test this
+ in the check function; To be safe against multiple
+ SQL_SLAVE_SKIP_COUNTER request, we do the check anyway
+ */
+ if (!active_mi->rli.slave_running)
+ {
+ mysql_mutex_lock(&active_mi->rli.data_lock);
+ active_mi->rli.slave_skip_counter= sql_slave_skip_counter;
+ mysql_mutex_unlock(&active_mi->rli.data_lock);
+ }
+ mysql_mutex_unlock(&active_mi->rli.run_lock);
+ mysql_mutex_unlock(&LOCK_active_mi);
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ return 0;
+}
+static Sys_var_uint Sys_slave_skip_counter(
+ "sql_slave_skip_counter", "sql_slave_skip_counter",
+ GLOBAL_VAR(sql_slave_skip_counter), NO_CMD_LINE,
+ VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_slave_skip_counter),
+ ON_UPDATE(fix_slave_skip_counter));
+
+static Sys_var_charptr Sys_slave_skip_errors(
+ "slave_skip_errors", "Tells the slave thread to continue "
+ "replication when a query event returns an error from the "
+ "provided list",
+ READ_ONLY GLOBAL_VAR(opt_slave_skip_errors), CMD_LINE(REQUIRED_ARG),
+ IN_SYSTEM_CHARSET, DEFAULT(0));
+
+static Sys_var_ulonglong Sys_relay_log_space_limit(
+ "relay_log_space_limit", "Maximum space to use for all relay logs",
+ READ_ONLY GLOBAL_VAR(relay_log_space_limit), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, ULONGLONG_MAX), DEFAULT(0), BLOCK_SIZE(1));
+
+static Sys_var_uint Sys_sync_relaylog_period(
+ "sync_relay_log", "Synchronously flush relay log to disk after "
+ "every #th event. Use 0 (default) to disable synchronous flushing",
+ GLOBAL_VAR(sync_relaylog_period), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1));
+
+static Sys_var_uint Sys_sync_relayloginfo_period(
+ "sync_relay_log_info", "Synchronously flush relay log info "
+ "to disk after every #th transaction. Use 0 (default) to disable "
+ "synchronous flushing",
+ GLOBAL_VAR(sync_relayloginfo_period), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1));
+#endif
+
+static Sys_var_uint Sys_sync_binlog_period(
+ "sync_binlog", "Synchronously flush binary log to disk after "
+ "every #th event. Use 0 (default) to disable synchronous flushing",
+ GLOBAL_VAR(sync_binlog_period), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1));
+
+static Sys_var_uint Sys_sync_masterinfo_period(
+ "sync_master_info", "Synchronously flush master info to disk "
+ "after every #th event. Use 0 (default) to disable synchronous flushing",
+ GLOBAL_VAR(sync_masterinfo_period), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1));
+
+#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",
+ GLOBAL_VAR(slave_trans_retries), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(10), BLOCK_SIZE(1));
+#endif
+
+static bool check_locale(sys_var *self, THD *thd, set_var *var)
+{
+ if (!var->value)
+ return false;
+
+ MY_LOCALE *locale;
+ char buff[STRING_BUFFER_USUAL_SIZE];
+ if (var->value->result_type() == INT_RESULT)
+ {
+ int lcno= (int)var->value->val_int();
+ if (!(locale= my_locale_by_number(lcno)))
+ {
+ my_error(ER_UNKNOWN_LOCALE, MYF(0), llstr(lcno, buff));
+ return true;
+ }
+ if (check_not_null(self, thd, var))
+ return true;
+ }
+ else // STRING_RESULT
+ {
+ String str(buff, sizeof(buff), system_charset_info), *res;
+ if (!(res=var->value->val_str(&str)))
+ return true;
+ else if (!(locale= my_locale_by_name(res->c_ptr_safe())))
+ {
+ ErrConvString err(res);
+ my_error(ER_UNKNOWN_LOCALE, MYF(0), err.ptr());
+ return true;
+ }
+ }
+
+ var->save_result.ptr= locale;
+
+ if (!locale->errmsgs->errmsgs)
+ {
+ bool res;
+ mysql_mutex_lock(&LOCK_error_messages);
+ res= (!locale->errmsgs->errmsgs &&
+ read_texts(ERRMSG_FILE, locale->errmsgs->language,
+ &locale->errmsgs->errmsgs,
+ ER_ERROR_LAST - ER_ERROR_FIRST + 1));
+ mysql_mutex_unlock(&LOCK_error_messages);
+ if (res)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+ "Can't process error message file for locale '%s'",
+ locale->name);
+ return true;
+ }
+ }
+ status_var_increment(thd->status_var.feature_locale);
+ return false;
+}
+
+static Sys_var_struct Sys_lc_messages(
+ "lc_messages", "Set the language used for the error messages",
+ SESSION_VAR(lc_messages), NO_CMD_LINE,
+ my_offsetof(MY_LOCALE, name), DEFAULT(&my_default_lc_messages),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_locale));
+
+static Sys_var_struct Sys_lc_time_names(
+ "lc_time_names", "Set the language used for the month "
+ "names and the days of the week",
+ SESSION_VAR(lc_time_names), NO_CMD_LINE,
+ my_offsetof(MY_LOCALE, name), DEFAULT(&my_default_lc_time_names),
+ NO_MUTEX_GUARD, IN_BINLOG, ON_CHECK(check_locale));
+
+static Sys_var_tz Sys_time_zone(
+ "time_zone", "time_zone",
+ SESSION_VAR(time_zone), NO_CMD_LINE,
+ DEFAULT(&default_tz), NO_MUTEX_GUARD, IN_BINLOG);
+
+static Sys_var_charptr Sys_ignore_db_dirs(
+ "ignore_db_dirs",
+ "Specifies a directory to add to the ignore list when collecting "
+ "database names from the datadir. Put a blank argument to reset "
+ "the list accumulated so far.",
+ READ_ONLY GLOBAL_VAR(opt_ignore_db_dirs),
+ CMD_LINE(REQUIRED_ARG, OPT_IGNORE_DB_DIRECTORY),
+ IN_FS_CHARSET, DEFAULT(0));
+
+static Sys_var_ulong Sys_sp_cache_size(
+ "stored_program_cache",
+ "The soft upper limit for number of cached stored routines for "
+ "one connection.",
+ GLOBAL_VAR(stored_program_cache_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(256, 512 * 1024), DEFAULT(256), BLOCK_SIZE(1));
+
+export const char *plugin_maturity_names[]=
+{ "unknown", "experimental", "alpha", "beta", "gamma", "stable", 0 };
+static Sys_var_enum Sys_plugin_maturity(
+ "plugin_maturity",
+ "The lowest desirable plugin maturity "
+ "(unknown, experimental, alpha, beta, gamma, or stable). "
+ "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));
+
+static Sys_var_ulong Sys_deadlock_search_depth_short(
+ "deadlock_search_depth_short",
+ "Short search depth for the two-step deadlock detection",
+ SESSION_VAR(wt_deadlock_search_depth_short), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, 32), DEFAULT(4), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_deadlock_search_depth_long(
+ "deadlock_search_depth_long",
+ "Long search depth for the two-step deadlock detection",
+ SESSION_VAR(wt_deadlock_search_depth_long), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, 33), DEFAULT(15), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_deadlock_timeout_depth_short(
+ "deadlock_timeout_short",
+ "Short timeout for the two-step deadlock detection (in microseconds)",
+ SESSION_VAR(wt_timeout_short), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(10000), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_deadlock_timeout_depth_long(
+ "deadlock_timeout_long",
+ "Long timeout for the two-step deadlock detection (in microseconds)",
+ SESSION_VAR(wt_timeout_long), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(50000000), BLOCK_SIZE(1));
+
+#ifndef DBUG_OFF
+static Sys_var_ulong Sys_debug_crc_break(
+ "debug_crc_break",
+ "Call my_debug_put_break_here() if crc matches this number (for debug)",
+ GLOBAL_VAR(my_crc_dbug_check), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, ULONG_MAX), DEFAULT(0), BLOCK_SIZE(1));
+#endif
+
+static Sys_var_uint Sys_extra_port(
+ "extra_port",
+ "Extra port number to use for tcp connections in a "
+ "one-thread-per-connection manner. 0 means don't use another port",
+ READ_ONLY GLOBAL_VAR(mysqld_extra_port), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, UINT_MAX32), DEFAULT(0), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_extra_max_connections(
+ "extra_max_connections", "The number of connections on extra-port",
+ GLOBAL_VAR(extra_max_connections), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, 100000), DEFAULT(1), BLOCK_SIZE(1), NO_MUTEX_GUARD,
+ NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(fix_max_connections));
+
+#ifdef SAFE_MUTEX
+static Sys_var_mybool Sys_mutex_deadlock_detector(
+ "mutex_deadlock_detector", "Enable checking of wrong mutex usage",
+ READ_ONLY GLOBAL_VAR(safe_mutex_deadlock_detector),
+ CMD_LINE(OPT_ARG), DEFAULT(TRUE));
+#endif
+
+static Sys_var_keycache Sys_key_cache_segments(
+ "key_cache_segments", "The number of segments in a key cache",
+ KEYCACHE_VAR(param_partitions),
+ CMD_LINE(REQUIRED_ARG, OPT_KEY_CACHE_PARTITIONS),
+ VALID_RANGE(0, MAX_KEY_CACHE_PARTITIONS),
+ DEFAULT(DEFAULT_KEY_CACHE_PARTITIONS),
+ BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ 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
+};
+static Sys_var_set Sys_log_slow_filter(
+ "log_slow_filter",
+ "Log only certain types of queries. Multiple "
+ "flags can be specified, separated by commas. Valid values are admin, "
+ "slave, filesort, filesort_on_disk, full_join, full_scan, query_cache, "
+ "query_cache_miss, tmp_table, tmp_table_on_disk",
+ SESSION_VAR(log_slow_filter), CMD_LINE(REQUIRED_ARG),
+ log_slow_filter_names,
+ DEFAULT(MAX_SET(array_elements(log_slow_filter_names)-1)));
+
+static Sys_var_ulong Sys_log_slow_rate_limit(
+ "log_slow_rate_limit",
+ "Write to slow log every #th slow query. Set to 1 to log everything. "
+ "Increase it to reduce the size of the slow or the performance impact "
+ "of slow logging",
+ SESSION_VAR(log_slow_rate_limit), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, UINT_MAX), DEFAULT(1), BLOCK_SIZE(1));
+
+static const char *log_slow_verbosity_names[]= { "innodb", "query_plan", 0 };
+static Sys_var_set Sys_log_slow_verbosity(
+ "log_slow_verbosity",
+ "log-slow-verbosity=[value[,value ...]] where value is one of "
+ "'innodb', 'query_plan'",
+ SESSION_VAR(log_slow_verbosity), CMD_LINE(REQUIRED_ARG),
+ log_slow_verbosity_names, DEFAULT(LOG_SLOW_VERBOSITY_INIT));
+
+static Sys_var_ulong Sys_join_cache_level(
+ "join_cache_level",
+ "Controls what join operations can be executed with join buffers. Odd "
+ "numbers are used for plain join buffers while even numbers are used "
+ "for linked buffers",
+ SESSION_VAR(join_cache_level), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, 8), DEFAULT(2), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_mrr_buffer_size(
+ "mrr_buffer_size",
+ "Size of buffer to use when using MRR with range access",
+ SESSION_VAR(mrr_buff_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(IO_SIZE*2, INT_MAX32), DEFAULT(256*1024), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_rowid_merge_buff_size(
+ "rowid_merge_buff_size",
+ "The size of the buffers used [NOT] IN evaluation via partial matching",
+ SESSION_VAR(rowid_merge_buff_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, ((ulonglong)~(intptr)0)/2), DEFAULT(8*1024*1024),
+ BLOCK_SIZE(1));
+
+static Sys_var_mybool Sys_userstat(
+ "userstat",
+ "Enables statistics gathering for USER_STATISTICS, CLIENT_STATISTICS, "
+ "INDEX_STATISTICS and TABLE_STATISTICS tables in the INFORMATION_SCHEMA",
+ GLOBAL_VAR(opt_userstat_running),
+ CMD_LINE(OPT_ARG), DEFAULT(FALSE));
+
+static Sys_var_mybool Sys_binlog_annotate_row_events(
+ "binlog_annotate_row_events",
+ "Tells the master to annotate RBR events with the statement that "
+ "caused these events",
+ SESSION_VAR(binlog_annotate_row_events), CMD_LINE(OPT_ARG),
+ DEFAULT(FALSE));
+
+#ifdef HAVE_REPLICATION
+static Sys_var_mybool Sys_replicate_annotate_row_events(
+ "replicate_annotate_row_events",
+ "Tells the slave to write annotate rows events recieved from the master "
+ "to its own binary log. Ignored if log_slave_updates is not set",
+ READ_ONLY GLOBAL_VAR(opt_replicate_annotate_row_events),
+ CMD_LINE(OPT_ARG), DEFAULT(0));
+#endif
+
+static Sys_var_ulonglong Sys_join_buffer_space_limit(
+ "join_buffer_space_limit",
+ "The limit of the space for all join buffers used by a query",
+ SESSION_VAR(join_buff_space_limit), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(2048, ULONGLONG_MAX), DEFAULT(16*128*1024),
+ BLOCK_SIZE(2048));
+
+static Sys_var_ulong Sys_progress_report_time(
+ "progress_report_time",
+ "Seconds between sending progress reports to the client for "
+ "time-consuming statements. Set to 0 to disable progress reporting.",
+ SESSION_VAR(progress_report_time), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(5), BLOCK_SIZE(1));
+
+static Sys_var_mybool Sys_no_thread_alarm(
+ "debug_no_thread_alarm",
+ "Disable system thread alarm calls. Disabling it may be useful "
+ "in debugging or testing, never do it in production",
+ READ_ONLY GLOBAL_VAR(my_disable_thr_alarm), CMD_LINE(OPT_ARG),
+ DEFAULT(FALSE));
+
+static Sys_var_mybool Sys_query_cache_strip_comments(
+ "query_cache_strip_comments",
+ "Strip all comments from a query before storing it "
+ "in the query cache",
+ SESSION_VAR(query_cache_strip_comments), CMD_LINE(OPT_ARG),
+ DEFAULT(FALSE));
+
+static ulonglong in_transaction(THD *thd)
+{
+ return test(thd->in_active_multi_stmt_transaction());
+}
+static Sys_var_session_special Sys_in_transaction(
+ "in_transaction", "Whether there is an active transaction",
+ READ_ONLY sys_var::ONLY_SESSION, NO_CMD_LINE,
+ VALID_RANGE(0, 1), BLOCK_SIZE(1), NO_MUTEX_GUARD,
+ NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0), ON_READ(in_transaction));
+
+#ifndef DBUG_OFF
+static Sys_var_ulong Sys_debug_binlog_fsync_sleep(
+ "debug_binlog_fsync_sleep",
+ "Extra sleep (in microseconds) to add to binlog fsync(), for debugging",
+ GLOBAL_VAR(opt_binlog_dbug_fsync_sleep),
+ CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1));
+#endif
+
+static Sys_var_harows Sys_expensive_subquery_limit(
+ "expensive_subquery_limit",
+ "The maximum number of rows a subquery may examine in order to be "
+ "executed during optimization and used for constant optimization",
+ SESSION_VAR(expensive_subquery_limit), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, HA_POS_ERROR), DEFAULT(100), BLOCK_SIZE(1));
+
+static bool check_pseudo_slave_mode(sys_var *self, THD *thd, set_var *var)
+{
+ longlong previous_val= thd->variables.pseudo_slave_mode;
+ longlong val= (longlong) var->save_result.ulonglong_value;
+ bool rli_fake= false;
+
+#ifndef EMBEDDED_LIBRARY
+ rli_fake= thd->rli_fake ? true : false;
+#endif
+
+ if (rli_fake)
+ {
+ if (!val)
+ {
+#ifndef EMBEDDED_LIBRARY
+ delete thd->rli_fake;
+ thd->rli_fake= NULL;
+#endif
+ }
+ else if (previous_val && val)
+ goto ineffective;
+ else if (!previous_val && val)
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WRONG_VALUE_FOR_VAR,
+ "'pseudo_slave_mode' is already ON.");
+ }
+ else
+ {
+ if (!previous_val && !val)
+ goto ineffective;
+ else if (previous_val && !val)
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WRONG_VALUE_FOR_VAR,
+ "Slave applier execution mode not active, "
+ "statement ineffective.");
+ }
+ goto end;
+
+ineffective:
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WRONG_VALUE_FOR_VAR,
+ "'pseudo_slave_mode' change was ineffective.");
+
+end:
+ return FALSE;
+}
+static Sys_var_mybool Sys_pseudo_slave_mode(
+ "pseudo_slave_mode",
+ "SET pseudo_slave_mode= 0,1 are commands that mysqlbinlog "
+ "adds to beginning and end of binary log dumps. While zero "
+ "value indeed disables, the actual enabling of the slave "
+ "applier execution mode is done implicitly when a "
+ "Format_description_event is sent through the session.",
+ SESSION_ONLY(pseudo_slave_mode), NO_CMD_LINE, DEFAULT(FALSE),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_pseudo_slave_mode));
+
diff --git a/sql/sys_vars.h b/sql/sys_vars.h
new file mode 100644
index 00000000000..3cbd24f1c89
--- /dev/null
+++ b/sql/sys_vars.h
@@ -0,0 +1,1883 @@
+/* Copyright (c) 2002, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2013, Monty Program Ab.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/**
+ @file
+ "private" interface to sys_var - server configuration variables.
+
+ This header is included only by the file that contains declarations
+ of sys_var variables (sys_vars.cc).
+*/
+
+#include "sys_vars_shared.h"
+#include <my_getopt.h>
+#include <my_bit.h>
+#include <my_dir.h>
+#include "keycaches.h"
+#include "strfunc.h"
+#include "tztime.h" // my_tz_find, my_tz_SYSTEM, struct Time_zone
+
+/*
+ a set of mostly trivial (as in f(X)=X) defines below to make system variable
+ declarations more readable
+*/
+#define VALID_RANGE(X,Y) X,Y
+#define DEFAULT(X) X
+#define BLOCK_SIZE(X) X
+#define GLOBAL_VAR(X) sys_var::GLOBAL, (((char*)&(X))-(char*)&global_system_variables), sizeof(X)
+#define SESSION_VAR(X) sys_var::SESSION, offsetof(SV, X), sizeof(((SV *)0)->X)
+#define SESSION_ONLY(X) sys_var::ONLY_SESSION, offsetof(SV, X), sizeof(((SV *)0)->X)
+#define NO_CMD_LINE CMD_LINE(NO_ARG, -1)
+/*
+ the define below means that there's no *second* mutex guard,
+ LOCK_global_system_variables always guards all system variables
+*/
+#define NO_MUTEX_GUARD ((PolyLock*)0)
+#define IN_BINLOG sys_var::SESSION_VARIABLE_IN_BINLOG
+#define NOT_IN_BINLOG sys_var::VARIABLE_NOT_IN_BINLOG
+#define ON_READ(X) X
+#define ON_CHECK(X) X
+#define ON_UPDATE(X) X
+#define READ_ONLY sys_var::READONLY+
+// this means that Sys_var_charptr initial value was malloc()ed
+#define PREALLOCATED sys_var::ALLOCATED+
+#define PARSED_EARLY sys_var::PARSE_EARLY+
+/*
+ Sys_var_bit meaning is reversed, like in
+ @@foreign_key_checks <-> OPTION_NO_FOREIGN_KEY_CHECKS
+*/
+#define REVERSE(X) ~(X)
+#define DEPRECATED(X) X
+
+#define session_var(THD, TYPE) (*(TYPE*)session_var_ptr(THD))
+#define global_var(TYPE) (*(TYPE*)global_var_ptr())
+
+#if SIZEOF_OFF_T > 4 && defined(BIG_TABLES)
+#define GET_HA_ROWS GET_ULL
+#else
+#define GET_HA_ROWS GET_ULONG
+#endif
+
+/*
+ special assert for sysvars. Tells the name of the variable,
+ and fails even in non-debug builds.
+
+ It is supposed to be used *only* in Sys_var* constructors,
+ and has name_arg hard-coded to prevent incorrect usage.
+*/
+#define SYSVAR_ASSERT(X) \
+ while(!(X)) \
+ { \
+ fprintf(stderr, "Sysvar '%s' failed '%s'\n", name_arg, #X); \
+ DBUG_ABORT(); \
+ exit(255); \
+ }
+
+enum charset_enum {IN_SYSTEM_CHARSET, IN_FS_CHARSET};
+
+static const char *bool_values[3]= {"OFF", "ON", 0};
+TYPELIB bool_typelib={ array_elements(bool_values)-1, "", bool_values, 0 };
+
+/**
+ A small wrapper class to pass getopt arguments as a pair
+ to the Sys_var_* constructors. It improves type safety and helps
+ to catch errors in the argument order.
+*/
+struct CMD_LINE
+{
+ int id;
+ enum get_opt_arg_type arg_type;
+ CMD_LINE(enum get_opt_arg_type getopt_arg_type, int getopt_id=0)
+ : id(getopt_id), arg_type(getopt_arg_type) {}
+};
+
+/**
+ Sys_var_integer template is used to generate Sys_var_* classes
+ for variables that represent the value as an integer number.
+ They are Sys_var_uint, Sys_var_ulong, Sys_var_harows, Sys_var_ulonglong,
+ Sys_var_int.
+
+ An integer variable has a minimal and maximal values, and a "block_size"
+ (any valid value of the variable must be divisible by the block_size).
+
+ Class specific constructor arguments: min, max, block_size
+ Backing store: int, uint, ulong, ha_rows, ulonglong, depending on the class
+*/
+template <typename T, ulong ARGT, enum enum_mysql_show_type SHOWT>
+class Sys_var_integer: public sys_var
+{
+public:
+ Sys_var_integer(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt,
+ T min_val, T max_val, T def_val, uint block_size, 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, SHOWT, def_val, lock, binlog_status_arg,
+ on_check_func, on_update_func, substitute)
+ {
+ option.var_type= ARGT;
+ option.min_value= min_val;
+ option.max_value= max_val;
+ option.block_size= block_size;
+ option.u_max_value= (uchar**)max_var_ptr();
+ if (max_var_ptr())
+ *max_var_ptr()= max_val;
+
+ global_var(T)= def_val;
+ SYSVAR_ASSERT(size == sizeof(T));
+ SYSVAR_ASSERT(min_val < max_val);
+ SYSVAR_ASSERT(min_val <= def_val);
+ SYSVAR_ASSERT(max_val >= def_val);
+ SYSVAR_ASSERT(block_size > 0);
+ SYSVAR_ASSERT(def_val % block_size == 0);
+ }
+ bool do_check(THD *thd, set_var *var)
+ {
+ my_bool fixed= FALSE, unused;
+ longlong v= var->value->val_int();
+
+ if ((ARGT == GET_HA_ROWS) || (ARGT == GET_UINT) ||
+ (ARGT == GET_ULONG) || (ARGT == GET_ULL))
+ {
+ ulonglong uv;
+
+ /*
+ if the value is signed and negative,
+ and a variable is unsigned, it is set to zero
+ */
+ if ((fixed= (!var->value->unsigned_flag && v < 0)))
+ uv= 0;
+ else
+ uv= v;
+
+ var->save_result.ulonglong_value=
+ getopt_ull_limit_value(uv, &option, &unused);
+
+ if (max_var_ptr() && (T)var->save_result.ulonglong_value > *max_var_ptr())
+ var->save_result.ulonglong_value= *max_var_ptr();
+
+ fixed= fixed || var->save_result.ulonglong_value != uv;
+ }
+ else
+ {
+ /*
+ if the value is unsigned and has the highest bit set
+ and a variable is signed, it is set to max signed value
+ */
+ if ((fixed= (var->value->unsigned_flag && v < 0)))
+ v= LONGLONG_MAX;
+
+ var->save_result.longlong_value=
+ getopt_ll_limit_value(v, &option, &unused);
+
+ if (max_var_ptr() && (T)var->save_result.longlong_value > *max_var_ptr())
+ var->save_result.longlong_value= *max_var_ptr();
+
+ fixed= fixed || var->save_result.longlong_value != v;
+ }
+ return throw_bounds_warning(thd, name.str, fixed,
+ var->value->unsigned_flag, v);
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ session_var(thd, T)= static_cast<T>(var->save_result.ulonglong_value);
+ return false;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ global_var(T)= static_cast<T>(var->save_result.ulonglong_value);
+ return false;
+ }
+ bool check_update_type(Item_result type)
+ { return type != INT_RESULT; }
+ void session_save_default(THD *thd, set_var *var)
+ { var->save_result.ulonglong_value= (ulonglong)*(T*)global_value_ptr(thd, 0); }
+ void global_save_default(THD *thd, set_var *var)
+ { var->save_result.ulonglong_value= option.def_value; }
+ private:
+ T *max_var_ptr()
+ {
+ return scope() == SESSION ? (T*)(((uchar*)&max_system_variables) + offset)
+ : 0;
+ }
+};
+
+typedef Sys_var_integer<int, GET_INT, SHOW_SINT> Sys_var_int;
+typedef Sys_var_integer<uint, GET_UINT, SHOW_UINT> Sys_var_uint;
+typedef Sys_var_integer<ulong, GET_ULONG, SHOW_ULONG> Sys_var_ulong;
+typedef Sys_var_integer<ha_rows, GET_HA_ROWS, SHOW_HA_ROWS> Sys_var_harows;
+typedef Sys_var_integer<ulonglong, GET_ULL, SHOW_ULONGLONG> Sys_var_ulonglong;
+
+/**
+ Helper class for variables that take values from a TYPELIB
+*/
+class Sys_var_typelib: public sys_var
+{
+protected:
+ TYPELIB typelib;
+public:
+ Sys_var_typelib(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off,
+ CMD_LINE getopt,
+ SHOW_TYPE show_val_type_arg, const char *values[],
+ ulonglong def_val, PolyLock *lock,
+ enum binlog_status_enum binlog_status_arg,
+ on_check_function on_check_func, on_update_function on_update_func,
+ const char *substitute)
+ : sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
+ getopt.arg_type, show_val_type_arg, def_val, lock,
+ binlog_status_arg, on_check_func,
+ on_update_func, substitute)
+ {
+ for (typelib.count= 0; values[typelib.count]; typelib.count++) /*no-op */;
+ typelib.name="";
+ typelib.type_names= values;
+ typelib.type_lengths= 0; // only used by Fields_enum and Field_set
+ option.typelib= &typelib;
+ }
+ bool do_check(THD *thd, set_var *var) // works for enums and my_bool
+ {
+ char buff[STRING_BUFFER_USUAL_SIZE];
+ String str(buff, sizeof(buff), system_charset_info), *res;
+
+ if (var->value->result_type() == STRING_RESULT)
+ {
+ if (!(res=var->value->val_str(&str)))
+ return true;
+ else
+ if (!(var->save_result.ulonglong_value=
+ find_type(&typelib, res->ptr(), res->length(), false)))
+ return true;
+ else
+ var->save_result.ulonglong_value--;
+ }
+ else
+ {
+ longlong tmp=var->value->val_int();
+ if (tmp < 0 || tmp >= typelib.count)
+ return true;
+ else
+ var->save_result.ulonglong_value= tmp;
+ }
+
+ return false;
+ }
+ bool check_update_type(Item_result type)
+ { return type != INT_RESULT && type != STRING_RESULT; }
+};
+
+/**
+ The class for ENUM variables - variables that take one value from a fixed
+ list of values.
+
+ Class specific constructor arguments:
+ char* values[] - 0-terminated list of strings of valid values
+
+ Backing store: uint
+
+ @note
+ Do *not* use "enum FOO" variables as a backing store, there is no
+ guarantee that sizeof(enum FOO) == sizeof(uint), there is no guarantee
+ even that sizeof(enum FOO) == sizeof(enum BAR)
+*/
+class Sys_var_enum: public Sys_var_typelib
+{
+public:
+ Sys_var_enum(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, 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_typelib(name_arg, comment, flag_args, off, getopt,
+ SHOW_CHAR, values, def_val, lock,
+ binlog_status_arg, on_check_func, on_update_func,
+ substitute)
+ {
+ option.var_type= GET_ENUM;
+ global_var(ulong)= def_val;
+ SYSVAR_ASSERT(def_val < typelib.count);
+ SYSVAR_ASSERT(size == sizeof(ulong));
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ session_var(thd, ulong)= static_cast<ulong>(var->save_result.ulonglong_value);
+ return false;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ global_var(ulong)= static_cast<ulong>(var->save_result.ulonglong_value);
+ return false;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ { var->save_result.ulonglong_value= global_var(ulong); }
+ void global_save_default(THD *thd, set_var *var)
+ { var->save_result.ulonglong_value= option.def_value; }
+ uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ { return (uchar*)typelib.type_names[session_var(thd, ulong)]; }
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base)
+ { return (uchar*)typelib.type_names[global_var(ulong)]; }
+};
+
+/**
+ The class for boolean variables - a variant of ENUM variables
+ with the fixed list of values of { OFF , ON }
+
+ Backing store: my_bool
+*/
+class Sys_var_mybool: public Sys_var_typelib
+{
+public:
+ Sys_var_mybool(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt,
+ my_bool def_val, PolyLock *lock=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_typelib(name_arg, comment, flag_args, off, getopt,
+ SHOW_MY_BOOL, bool_values, def_val, lock,
+ binlog_status_arg, on_check_func, on_update_func,
+ substitute)
+ {
+ option.var_type= GET_BOOL;
+ global_var(my_bool)= def_val;
+ SYSVAR_ASSERT(def_val < 2);
+ SYSVAR_ASSERT(getopt.arg_type == OPT_ARG || getopt.id == -1);
+ SYSVAR_ASSERT(size == sizeof(my_bool));
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ session_var(thd, my_bool)= var->save_result.ulonglong_value != 0;
+ return false;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ global_var(my_bool)= var->save_result.ulonglong_value != 0;
+ return false;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ { var->save_result.ulonglong_value= (ulonglong)*(my_bool *)global_value_ptr(thd, 0); }
+ void global_save_default(THD *thd, set_var *var)
+ { var->save_result.ulonglong_value= option.def_value; }
+};
+
+/**
+ The class for string variables. The string can be in character_set_filesystem
+ or in character_set_system. The string can be allocated with my_malloc()
+ or not. The state of the initial value is specified in the constructor,
+ after that it's managed automatically. The value of NULL is supported.
+
+ Class specific constructor arguments:
+ enum charset_enum is_os_charset_arg
+
+ Backing store: char*
+
+ @note
+ This class supports only GLOBAL variables, because THD on destruction
+ does not destroy individual members of SV, there's no way to free
+ allocated string variables for every thread.
+*/
+class Sys_var_charptr: public sys_var
+{
+public:
+ Sys_var_charptr(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt,
+ enum charset_enum is_os_charset_arg,
+ const char *def_val, PolyLock *lock=0,
+ enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
+ on_check_function on_check_func=0,
+ on_update_function on_update_func=0,
+ const char *substitute=0)
+ : sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
+ getopt.arg_type, SHOW_CHAR_PTR, (intptr)def_val,
+ lock, binlog_status_arg, on_check_func, on_update_func,
+ substitute)
+ {
+ is_os_charset= is_os_charset_arg == IN_FS_CHARSET;
+ /*
+ use GET_STR_ALLOC - if ALLOCATED it must be *always* allocated,
+ otherwise (GET_STR) you'll never know whether to free it or not.
+ (think of an exit because of an error right after my_getopt)
+ */
+ option.var_type= (flags & ALLOCATED) ? GET_STR_ALLOC : GET_STR;
+ global_var(const char*)= def_val;
+ SYSVAR_ASSERT(scope() == GLOBAL);
+ SYSVAR_ASSERT(size == sizeof(char *));
+ }
+ void cleanup()
+ {
+ if (flags & ALLOCATED)
+ {
+ my_free(global_var(char*));
+ global_var(char *)= NULL;
+ }
+ flags&= ~ALLOCATED;
+ }
+ static bool do_string_check(THD *thd, set_var *var, CHARSET_INFO *charset)
+ {
+ char buff[STRING_BUFFER_USUAL_SIZE], buff2[STRING_BUFFER_USUAL_SIZE];
+ String str(buff, sizeof(buff), charset);
+ String str2(buff2, sizeof(buff2), charset), *res;
+
+ if (!(res=var->value->val_str(&str)))
+ var->save_result.string_value.str= 0;
+ else
+ {
+ uint32 unused;
+ if (String::needs_conversion(res->length(), res->charset(),
+ charset, &unused))
+ {
+ uint errors;
+ str2.copy(res->ptr(), res->length(), res->charset(), charset,
+ &errors);
+ res=&str2;
+
+ }
+ var->save_result.string_value.str= thd->strmake(res->ptr(), res->length());
+ var->save_result.string_value.length= res->length();
+ }
+
+ return false;
+ }
+ bool do_check(THD *thd, set_var *var)
+ { return do_string_check(thd, var, charset(thd)); }
+ bool session_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(FALSE);
+ return true;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ char *new_val, *ptr= var->save_result.string_value.str;
+ size_t len=var->save_result.string_value.length;
+ if (ptr)
+ {
+ new_val= (char*)my_memdup(ptr, len+1, MYF(MY_WME));
+ if (!new_val) return true;
+ new_val[len]=0;
+ }
+ else
+ new_val= 0;
+ if (flags & ALLOCATED)
+ my_free(global_var(char*));
+ flags|= ALLOCATED;
+ global_var(char*)= new_val;
+ return false;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ { DBUG_ASSERT(FALSE); }
+ void global_save_default(THD *thd, set_var *var)
+ {
+ char *ptr= (char*)(intptr)option.def_value;
+ var->save_result.string_value.str= ptr;
+ var->save_result.string_value.length= ptr ? strlen(ptr) : 0;
+ }
+ bool check_update_type(Item_result type)
+ { return type != STRING_RESULT; }
+};
+
+
+class Sys_var_proxy_user: public sys_var
+{
+public:
+ Sys_var_proxy_user(const char *name_arg,
+ const char *comment, enum charset_enum is_os_charset_arg)
+ : sys_var(&all_sys_vars, name_arg, comment,
+ sys_var::READONLY+sys_var::ONLY_SESSION, 0, -1,
+ NO_ARG, SHOW_CHAR, 0, NULL, VARIABLE_NOT_IN_BINLOG,
+ NULL, NULL, NULL)
+ {
+ is_os_charset= is_os_charset_arg == IN_FS_CHARSET;
+ option.var_type= GET_STR;
+ }
+ bool do_check(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(FALSE);
+ return true;
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(FALSE);
+ return true;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(FALSE);
+ return false;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ { DBUG_ASSERT(FALSE); }
+ void global_save_default(THD *thd, set_var *var)
+ { DBUG_ASSERT(FALSE); }
+ bool check_update_type(Item_result type)
+ { return true; }
+protected:
+ virtual uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ return thd->security_ctx->proxy_user[0] ?
+ (uchar *) &(thd->security_ctx->proxy_user[0]) : NULL;
+ }
+};
+
+class Sys_var_external_user : public Sys_var_proxy_user
+{
+public:
+ Sys_var_external_user(const char *name_arg, const char *comment_arg,
+ enum charset_enum is_os_charset_arg)
+ : Sys_var_proxy_user (name_arg, comment_arg, is_os_charset_arg)
+ {}
+
+protected:
+ virtual uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ return (uchar*)thd->security_ctx->external_user;
+ }
+};
+
+class Sys_var_rpl_filter: public sys_var
+{
+private:
+ int opt_id;
+
+public:
+ Sys_var_rpl_filter(const char *name, int getopt_id, const char *comment)
+ : sys_var(&all_sys_vars, name, comment, sys_var::GLOBAL, 0, -1,
+ NO_ARG, SHOW_CHAR, 0, NULL, VARIABLE_NOT_IN_BINLOG,
+ NULL, NULL, NULL), opt_id(getopt_id)
+ {
+ option.var_type= GET_STR;
+ }
+
+ bool check_update_type(Item_result type)
+ { return type != STRING_RESULT; }
+
+ bool do_check(THD *thd, set_var *var);
+
+ void session_save_default(THD *thd, set_var *var)
+ { DBUG_ASSERT(FALSE); }
+
+ void global_save_default(THD *thd, set_var *var)
+ { DBUG_ASSERT(FALSE); }
+
+ bool session_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(FALSE);
+ return true;
+ }
+
+ bool global_update(THD *thd, set_var *var);
+
+protected:
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base);
+ bool set_filter_value(const char *value);
+ void lock(void);
+ void unlock(void);
+};
+
+/**
+ The class for string variables. Useful for strings that aren't necessarily
+ \0-terminated. Otherwise the same as Sys_var_charptr.
+
+ Class specific constructor arguments:
+ enum charset_enum is_os_charset_arg
+
+ Backing store: LEX_STRING
+
+ @note
+ Behaves exactly as Sys_var_charptr, only the backing store is different.
+*/
+class Sys_var_lexstring: public Sys_var_charptr
+{
+public:
+ Sys_var_lexstring(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt,
+ enum charset_enum is_os_charset_arg,
+ const char *def_val, PolyLock *lock=0,
+ enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
+ on_check_function on_check_func=0,
+ on_update_function on_update_func=0,
+ const char *substitute=0)
+ : Sys_var_charptr(name_arg, comment, flag_args, off, sizeof(char*),
+ getopt, is_os_charset_arg, def_val, lock, binlog_status_arg,
+ on_check_func, on_update_func, substitute)
+ {
+ global_var(LEX_STRING).length= strlen(def_val);
+ SYSVAR_ASSERT(size == sizeof(LEX_STRING));
+ *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;
+ return false;
+ }
+};
+
+#ifndef DBUG_OFF
+/**
+ @@session.dbug and @@global.dbug variables.
+
+ @@dbug variable differs from other variables in one aspect:
+ if its value is not assigned in the session, it "points" to the global
+ value, and so when the global value is changed, the change
+ immediately takes effect in the session.
+
+ This semantics is intentional, to be able to debug one session from
+ another.
+*/
+class Sys_var_dbug: public sys_var
+{
+public:
+ Sys_var_dbug(const char *name_arg,
+ const char *comment, int flag_args,
+ CMD_LINE getopt,
+ const char *def_val, PolyLock *lock=0,
+ enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
+ on_check_function on_check_func=0,
+ on_update_function on_update_func=0,
+ const char *substitute=0)
+ : sys_var(&all_sys_vars, name_arg, comment, flag_args, 0, 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_NO_ARG; }
+ bool do_check(THD *thd, set_var *var)
+ {
+ char buff[STRING_BUFFER_USUAL_SIZE];
+ String str(buff, sizeof(buff), system_charset_info), *res;
+
+ if (!(res=var->value->val_str(&str)))
+ var->save_result.string_value.str= const_cast<char*>("");
+ else
+ var->save_result.string_value.str= thd->strmake(res->ptr(), res->length());
+ return false;
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ const char *val= var->save_result.string_value.str;
+ if (!var->value)
+ DBUG_POP();
+ else
+ DBUG_SET(val);
+ return false;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ const char *val= var->save_result.string_value.str;
+ DBUG_SET_INITIAL(val);
+ return false;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ { }
+ void global_save_default(THD *thd, set_var *var)
+ {
+ char *ptr= (char*)(intptr)option.def_value;
+ var->save_result.string_value.str= ptr;
+ }
+ uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ char buf[256];
+ DBUG_EXPLAIN(buf, sizeof(buf));
+ return (uchar*) thd->strdup(buf);
+ }
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ char buf[256];
+ DBUG_EXPLAIN_INITIAL(buf, sizeof(buf));
+ return (uchar*) thd->strdup(buf);
+ }
+ bool check_update_type(Item_result type)
+ { return type != STRING_RESULT; }
+};
+#endif
+
+#define KEYCACHE_VAR(X) GLOBAL_VAR(dflt_key_cache_var.X)
+#define keycache_var_ptr(KC, OFF) (((uchar*)(KC))+(OFF))
+#define keycache_var(KC, OFF) (*(ulonglong*)keycache_var_ptr(KC, OFF))
+typedef bool (*keycache_update_function)(THD *, KEY_CACHE *, ptrdiff_t, ulonglong);
+
+/**
+ The class for keycache_* variables. Supports structured names,
+ keycache_name.variable_name.
+
+ Class specific constructor arguments:
+ everything derived from Sys_var_ulonglong
+
+ Backing store: ulonglong
+
+ @note these variables can be only GLOBAL
+*/
+class Sys_var_keycache: public Sys_var_ulonglong
+{
+ keycache_update_function keycache_update;
+public:
+ Sys_var_keycache(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt,
+ ulonglong min_val, ulonglong max_val, ulonglong def_val,
+ uint block_size, PolyLock *lock,
+ enum binlog_status_enum binlog_status_arg,
+ on_check_function on_check_func,
+ keycache_update_function on_update_func,
+ const char *substitute=0)
+ : Sys_var_ulonglong(name_arg, comment, flag_args, off, size,
+ getopt, min_val, max_val, def_val,
+ block_size, lock, binlog_status_arg, on_check_func, 0,
+ substitute),
+ keycache_update(on_update_func)
+ {
+ option.var_type|= GET_ASK_ADDR;
+ option.value= (uchar**)1; // crash me, please
+ // fix an offset from global_system_variables to be an offset in KEY_CACHE
+ offset= global_var_ptr() - (uchar*)dflt_key_cache;
+ SYSVAR_ASSERT(scope() == GLOBAL);
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ ulonglong new_value= var->save_result.ulonglong_value;
+ LEX_STRING *base_name= &var->base;
+ KEY_CACHE *key_cache;
+
+ /* If no basename, assume it's for the key cache named 'default' */
+ if (!base_name->length)
+ base_name= &default_key_cache_base;
+
+ key_cache= get_key_cache(base_name);
+
+ if (!key_cache)
+ { // Key cache didn't exists */
+ if (!new_value) // Tried to delete cache
+ return false; // Ok, nothing to do
+ if (!(key_cache= create_key_cache(base_name->str, base_name->length)))
+ return true;
+ }
+
+ /**
+ Abort if some other thread is changing the key cache
+ @todo This should be changed so that we wait until the previous
+ assignment is done and then do the new assign
+ */
+ if (key_cache->in_init)
+ return true;
+
+ return keycache_update(thd, key_cache, offset, new_value);
+ }
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ KEY_CACHE *key_cache= get_key_cache(base);
+ if (!key_cache)
+ key_cache= &zero_key_cache;
+ return keycache_var_ptr(key_cache, offset);
+ }
+};
+
+static bool update_buffer_size(THD *thd, KEY_CACHE *key_cache,
+ ptrdiff_t offset, ulonglong new_value)
+{
+ bool error= false;
+ DBUG_ASSERT(offset == offsetof(KEY_CACHE, param_buff_size));
+
+ if (new_value == 0)
+ {
+ if (key_cache == dflt_key_cache)
+ {
+ my_error(ER_WARN_CANT_DROP_DEFAULT_KEYCACHE, MYF(0));
+ return true;
+ }
+
+ if (key_cache->key_cache_inited) // If initied
+ {
+ /*
+ Move tables using this key cache to the default key cache
+ and clear the old key cache.
+ */
+ key_cache->in_init= 1;
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ key_cache->param_buff_size= 0;
+ ha_resize_key_cache(key_cache);
+ ha_change_key_cache(key_cache, dflt_key_cache);
+ /*
+ We don't delete the key cache as some running threads my still be in
+ the key cache code with a pointer to the deleted (empty) key cache
+ */
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ key_cache->in_init= 0;
+ }
+ return error;
+ }
+
+ key_cache->param_buff_size= new_value;
+
+ /* If key cache didn't exist initialize it, else resize it */
+ key_cache->in_init= 1;
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+
+ if (!key_cache->key_cache_inited)
+ error= ha_init_key_cache(0, key_cache, 0);
+ else
+ error= ha_resize_key_cache(key_cache);
+
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ key_cache->in_init= 0;
+
+ return error;
+}
+
+static bool update_keycache(THD *thd, KEY_CACHE *key_cache,
+ ptrdiff_t offset, ulonglong new_value,
+ int (*func)(KEY_CACHE *))
+{
+ bool error= false;
+ DBUG_ASSERT(offset != offsetof(KEY_CACHE, param_buff_size));
+
+ keycache_var(key_cache, offset)= new_value;
+
+ key_cache->in_init= 1;
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ error= func(key_cache);
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ key_cache->in_init= 0;
+
+ return error;
+}
+
+static bool resize_keycache(THD *thd, KEY_CACHE *key_cache,
+ ptrdiff_t offset, ulonglong new_value)
+{
+ return update_keycache(thd, key_cache, offset, new_value,
+ ha_resize_key_cache);
+}
+
+static bool change_keycache_param(THD *thd, KEY_CACHE *key_cache,
+ ptrdiff_t offset, ulonglong new_value)
+{
+ return update_keycache(thd, key_cache, offset, new_value,
+ ha_change_key_cache_param);
+}
+
+static bool repartition_keycache(THD *thd, KEY_CACHE *key_cache,
+ ptrdiff_t offset, ulonglong new_value)
+{
+ return update_keycache(thd, key_cache, offset, new_value,
+ ha_repartition_key_cache);
+}
+
+
+/**
+ The class for floating point variables
+
+ Class specific constructor arguments: min, max
+
+ Backing store: double
+*/
+class Sys_var_double: public sys_var
+{
+public:
+ Sys_var_double(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt,
+ double min_val, double max_val, double 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_DOUBLE,
+ (longlong) getopt_double2ulonglong(def_val),
+ lock, binlog_status_arg, on_check_func, on_update_func,
+ substitute)
+ {
+ option.var_type= GET_DOUBLE;
+ option.min_value= (longlong) getopt_double2ulonglong(min_val);
+ option.max_value= (longlong) getopt_double2ulonglong(max_val);
+ global_var(double)= (double)option.def_value;
+ SYSVAR_ASSERT(min_val < max_val);
+ SYSVAR_ASSERT(min_val <= def_val);
+ SYSVAR_ASSERT(max_val >= def_val);
+ SYSVAR_ASSERT(size == sizeof(double));
+ }
+ bool do_check(THD *thd, set_var *var)
+ {
+ my_bool fixed;
+ double v= var->value->val_real();
+ var->save_result.double_value= getopt_double_limit_value(v, &option, &fixed);
+
+ return throw_bounds_warning(thd, name.str, fixed, v);
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ session_var(thd, double)= var->save_result.double_value;
+ return false;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ global_var(double)= var->save_result.double_value;
+ return false;
+ }
+ bool check_update_type(Item_result type)
+ {
+ return type != INT_RESULT && type != REAL_RESULT && type != DECIMAL_RESULT;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ { var->save_result.double_value= global_var(double); }
+ void global_save_default(THD *thd, set_var *var)
+ { var->save_result.double_value= getopt_ulonglong2double(option.def_value); }
+};
+
+/**
+ The class for the @max_user_connections.
+ It's derived from Sys_var_uint, but non-standard session value
+ requires a new class.
+
+ Class specific constructor arguments:
+ everything derived from Sys_var_uint
+
+ Backing store: uint
+*/
+class Sys_var_max_user_conn: public Sys_var_int
+{
+public:
+ Sys_var_max_user_conn(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt,
+ int min_val, int max_val, int def_val,
+ uint block_size, 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_int(name_arg, comment, SESSION, off, size, getopt,
+ min_val, max_val, def_val, block_size,
+ lock, binlog_status_arg, on_check_func, on_update_func,
+ substitute)
+ { }
+ uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ if (thd->user_connect && thd->user_connect->user_resources.user_conn)
+ return (uchar*) &(thd->user_connect->user_resources.user_conn);
+ return global_value_ptr(thd, base);
+ }
+};
+
+// overflow-safe (1 << X)-1
+#define MAX_SET(X) ((((1UL << ((X)-1))-1) << 1) | 1)
+
+/**
+ The class for flagset variables - a variant of SET that allows in-place
+ editing (turning on/off individual bits). String representations looks like
+ a "flag=val,flag=val,...". Example: @@optimizer_switch
+
+ Class specific constructor arguments:
+ char* values[] - 0-terminated list of strings of valid values
+
+ Backing store: ulonglong
+
+ @note
+ the last value in the values[] array should
+ *always* be the string "default".
+*/
+class Sys_var_flagset: public Sys_var_typelib
+{
+public:
+ Sys_var_flagset(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt,
+ const char *values[], ulonglong 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_typelib(name_arg, comment, flag_args, off, getopt,
+ SHOW_CHAR, values, def_val, lock,
+ binlog_status_arg, on_check_func, on_update_func,
+ substitute)
+ {
+ option.var_type= GET_FLAGSET;
+ global_var(ulonglong)= def_val;
+ SYSVAR_ASSERT(typelib.count > 1);
+ SYSVAR_ASSERT(typelib.count <= 65);
+ SYSVAR_ASSERT(def_val < MAX_SET(typelib.count));
+ SYSVAR_ASSERT(strcmp(values[typelib.count-1], "default") == 0);
+ SYSVAR_ASSERT(size == sizeof(ulonglong));
+ }
+ bool do_check(THD *thd, set_var *var)
+ {
+ char buff[STRING_BUFFER_USUAL_SIZE];
+ String str(buff, sizeof(buff), system_charset_info), *res;
+ ulonglong default_value, current_value;
+ if (var->type == OPT_GLOBAL)
+ {
+ default_value= option.def_value;
+ current_value= global_var(ulonglong);
+ }
+ else
+ {
+ default_value= global_var(ulonglong);
+ current_value= session_var(thd, ulonglong);
+ }
+
+ if (var->value->result_type() == STRING_RESULT)
+ {
+ if (!(res=var->value->val_str(&str)))
+ return true;
+ else
+ {
+ char *error;
+ uint error_len;
+
+ var->save_result.ulonglong_value=
+ find_set_from_flags(&typelib,
+ typelib.count,
+ current_value,
+ default_value,
+ res->ptr(), res->length(),
+ &error, &error_len);
+ if (error)
+ {
+ ErrConvString err(error, error_len, res->charset());
+ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name.str, err.ptr());
+ return true;
+ }
+ }
+ }
+ else
+ {
+ longlong tmp=var->value->val_int();
+ if ((tmp < 0 && ! var->value->unsigned_flag)
+ || (ulonglong)tmp > MAX_SET(typelib.count))
+ return true;
+ else
+ var->save_result.ulonglong_value= tmp;
+ }
+
+ return false;
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ session_var(thd, ulonglong)= var->save_result.ulonglong_value;
+ return false;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ global_var(ulonglong)= var->save_result.ulonglong_value;
+ return false;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ { var->save_result.ulonglong_value= global_var(ulonglong); }
+ void global_save_default(THD *thd, set_var *var)
+ { var->save_result.ulonglong_value= option.def_value; }
+ uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ return (uchar*)flagset_to_string(thd, 0, session_var(thd, ulonglong),
+ typelib.type_names);
+ }
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ return (uchar*)flagset_to_string(thd, 0, global_var(ulonglong),
+ typelib.type_names);
+ }
+};
+
+/**
+ The class for SET variables - variables taking zero or more values
+ from the given list. Example: @@sql_mode
+
+ Class specific constructor arguments:
+ char* values[] - 0-terminated list of strings of valid values
+
+ Backing store: ulonglong
+*/
+class Sys_var_set: public Sys_var_typelib
+{
+public:
+ Sys_var_set(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt,
+ const char *values[], ulonglong 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_typelib(name_arg, comment, flag_args, off, getopt,
+ SHOW_CHAR, values, def_val, lock,
+ binlog_status_arg, on_check_func, on_update_func,
+ substitute)
+ {
+ option.var_type= GET_SET;
+ global_var(ulonglong)= def_val;
+ SYSVAR_ASSERT(typelib.count > 0);
+ SYSVAR_ASSERT(typelib.count <= 64);
+ SYSVAR_ASSERT(def_val <= MAX_SET(typelib.count));
+ SYSVAR_ASSERT(size == sizeof(ulonglong));
+ }
+ bool do_check(THD *thd, set_var *var)
+ {
+ char buff[STRING_BUFFER_USUAL_SIZE];
+ String str(buff, sizeof(buff), system_charset_info), *res;
+
+ if (var->value->result_type() == STRING_RESULT)
+ {
+ if (!(res=var->value->val_str(&str)))
+ return true;
+ else
+ {
+ char *error;
+ uint error_len;
+ bool not_used;
+
+ var->save_result.ulonglong_value=
+ find_set(&typelib, res->ptr(), res->length(), NULL,
+ &error, &error_len, &not_used);
+ /*
+ note, we only issue an error if error_len > 0.
+ That is even while empty (zero-length) values are considered
+ errors by find_set(), these errors are ignored here
+ */
+ if (error_len)
+ {
+ ErrConvString err(error, error_len, res->charset());
+ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name.str, err.ptr());
+ return true;
+ }
+ }
+ }
+ else
+ {
+ longlong tmp=var->value->val_int();
+ if ((tmp < 0 && ! var->value->unsigned_flag)
+ || (ulonglong)tmp > MAX_SET(typelib.count))
+ return true;
+ else
+ var->save_result.ulonglong_value= tmp;
+ }
+
+ return false;
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ session_var(thd, ulonglong)= var->save_result.ulonglong_value;
+ return false;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ global_var(ulonglong)= var->save_result.ulonglong_value;
+ return false;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ { var->save_result.ulonglong_value= global_var(ulonglong); }
+ void global_save_default(THD *thd, set_var *var)
+ { var->save_result.ulonglong_value= option.def_value; }
+ uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ return (uchar*)set_to_string(thd, 0, session_var(thd, ulonglong),
+ typelib.type_names);
+ }
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ return (uchar*)set_to_string(thd, 0, global_var(ulonglong),
+ typelib.type_names);
+ }
+};
+
+/**
+ The class for variables which value is a plugin.
+ Example: @@default_storage_engine
+
+ Class specific constructor arguments:
+ int plugin_type_arg (for example MYSQL_STORAGE_ENGINE_PLUGIN)
+
+ Backing store: plugin_ref
+
+ @note
+ these variables don't support command-line equivalents, any such
+ command-line options should be added manually to my_long_options in mysqld.cc
+*/
+class Sys_var_plugin: public sys_var
+{
+ int plugin_type;
+public:
+ Sys_var_plugin(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt,
+ int plugin_type_arg, 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),
+ plugin_type(plugin_type_arg)
+ {
+ option.var_type= GET_STR;
+ SYSVAR_ASSERT(size == sizeof(plugin_ref));
+ SYSVAR_ASSERT(getopt.id == -1); // 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;
+ if (!(res=var->value->val_str(&str)))
+ var->save_result.plugin= NULL;
+ else
+ {
+ const LEX_STRING pname= { const_cast<char*>(res->ptr()), res->length() };
+ plugin_ref plugin;
+
+ // special code for storage engines (e.g. to handle historical aliases)
+ if (plugin_type == MYSQL_STORAGE_ENGINE_PLUGIN)
+ plugin= ha_resolve_by_name(thd, &pname);
+ else
+ plugin= my_plugin_lock_by_name(thd, &pname, plugin_type);
+ if (!plugin)
+ {
+ // historically different error code
+ if (plugin_type == MYSQL_STORAGE_ENGINE_PLUGIN)
+ {
+ ErrConvString err(res);
+ my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), err.ptr());
+ }
+ return true;
+ }
+ var->save_result.plugin= plugin;
+ }
+ return false;
+ }
+ void do_update(plugin_ref *valptr, plugin_ref newval)
+ {
+ plugin_ref oldval= *valptr;
+ if (oldval != newval)
+ {
+ *valptr= my_plugin_lock(NULL, newval);
+ plugin_unlock(NULL, oldval);
+ }
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ do_update((plugin_ref*)session_var_ptr(thd),
+ var->save_result.plugin);
+ return false;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ do_update((plugin_ref*)global_var_ptr(),
+ var->save_result.plugin);
+ return false;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ {
+ plugin_ref plugin= global_var(plugin_ref);
+ var->save_result.plugin= my_plugin_lock(thd, plugin);
+ }
+ void global_save_default(THD *thd, set_var *var)
+ {
+ LEX_STRING pname;
+ char **default_value= reinterpret_cast<char**>(option.def_value);
+ pname.str= *default_value;
+ pname.length= strlen(pname.str);
+
+ plugin_ref plugin;
+ if (plugin_type == MYSQL_STORAGE_ENGINE_PLUGIN)
+ plugin= ha_resolve_by_name(thd, &pname);
+ else
+ plugin= my_plugin_lock_by_name(thd, &pname, plugin_type);
+ DBUG_ASSERT(plugin);
+
+ var->save_result.plugin= my_plugin_lock(thd, plugin);
+ }
+ bool check_update_type(Item_result type)
+ { return type != STRING_RESULT; }
+ uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ plugin_ref plugin= session_var(thd, plugin_ref);
+ return (uchar*)(plugin ? thd->strmake(plugin_name(plugin)->str,
+ plugin_name(plugin)->length) : 0);
+ }
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ plugin_ref plugin= global_var(plugin_ref);
+ return (uchar*)(plugin ? thd->strmake(plugin_name(plugin)->str,
+ plugin_name(plugin)->length) : 0);
+ }
+};
+
+#if defined(ENABLED_DEBUG_SYNC)
+/**
+ The class for @@debug_sync session-only variable
+*/
+class Sys_var_debug_sync :public sys_var
+{
+public:
+ Sys_var_debug_sync(const char *name_arg,
+ const char *comment, int flag_args,
+ CMD_LINE getopt,
+ const char *def_val, PolyLock *lock=0,
+ enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
+ on_check_function on_check_func=0,
+ on_update_function on_update_func=0,
+ const char *substitute=0)
+ : sys_var(&all_sys_vars, name_arg, comment, flag_args, 0, getopt.id,
+ getopt.arg_type, SHOW_CHAR, (intptr)def_val,
+ lock, binlog_status_arg, on_check_func, on_update_func,
+ substitute)
+ {
+ SYSVAR_ASSERT(scope() == ONLY_SESSION);
+ option.var_type= GET_NO_ARG;
+ }
+ bool do_check(THD *thd, set_var *var)
+ {
+ char buff[STRING_BUFFER_USUAL_SIZE];
+ String str(buff, sizeof(buff), system_charset_info), *res;
+
+ if (!(res=var->value->val_str(&str)))
+ var->save_result.string_value.str= const_cast<char*>("");
+ else
+ var->save_result.string_value.str= thd->strmake(res->ptr(), res->length());
+ return false;
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ extern bool debug_sync_update(THD *thd, char *val_str);
+ return debug_sync_update(thd, var->save_result.string_value.str);
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(FALSE);
+ return true;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ {
+ var->save_result.string_value.str= const_cast<char*>("");
+ var->save_result.string_value.length= 0;
+ }
+ void global_save_default(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(FALSE);
+ }
+ uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ extern uchar *debug_sync_value_ptr(THD *thd);
+ return debug_sync_value_ptr(thd);
+ }
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ DBUG_ASSERT(FALSE);
+ return 0;
+ }
+ bool check_update_type(Item_result type)
+ { return type != STRING_RESULT; }
+};
+#endif /* defined(ENABLED_DEBUG_SYNC) */
+
+/**
+ The class for bit variables - a variant of boolean that stores the value
+ in a bit.
+
+ Class specific constructor arguments:
+ ulonglong bitmask_arg - the mask for the bit to set in the ulonglong
+ backing store
+
+ Backing store: ulonglong
+
+ @note
+ This class supports the "reverse" semantics, when the value of the bit
+ being 0 corresponds to the value of variable being set. To activate it
+ use REVERSE(bitmask) instead of simply bitmask in the constructor.
+
+ @note
+ variables of this class cannot be set from the command line as
+ my_getopt does not support bits.
+*/
+class Sys_var_bit: public Sys_var_typelib
+{
+ ulonglong bitmask;
+ bool reverse_semantics;
+ void set(uchar *ptr, ulonglong value)
+ {
+ if ((value != 0) ^ reverse_semantics)
+ (*(ulonglong *)ptr)|= bitmask;
+ else
+ (*(ulonglong *)ptr)&= ~bitmask;
+ }
+public:
+ Sys_var_bit(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt,
+ ulonglong bitmask_arg, my_bool 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_typelib(name_arg, comment, flag_args, off, getopt,
+ SHOW_MY_BOOL, bool_values, def_val, lock,
+ binlog_status_arg, on_check_func, on_update_func,
+ substitute)
+ {
+ option.var_type= GET_BOOL;
+ reverse_semantics= my_count_bits(bitmask_arg) > 1;
+ bitmask= reverse_semantics ? ~bitmask_arg : bitmask_arg;
+ set(global_var_ptr(), def_val);
+ SYSVAR_ASSERT(def_val < 2);
+ SYSVAR_ASSERT(getopt.id == -1); // force NO_CMD_LINE
+ SYSVAR_ASSERT(size == sizeof(ulonglong));
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ set(session_var_ptr(thd), var->save_result.ulonglong_value);
+ return false;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ set(global_var_ptr(), var->save_result.ulonglong_value);
+ return false;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ { var->save_result.ulonglong_value= global_var(ulonglong) & bitmask; }
+ void global_save_default(THD *thd, set_var *var)
+ { var->save_result.ulonglong_value= option.def_value; }
+ uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ thd->sys_var_tmp.my_bool_value= reverse_semantics ^
+ ((session_var(thd, ulonglong) & bitmask) != 0);
+ return (uchar*) &thd->sys_var_tmp.my_bool_value;
+ }
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ thd->sys_var_tmp.my_bool_value= reverse_semantics ^
+ ((global_var(ulonglong) & bitmask) != 0);
+ return (uchar*) &thd->sys_var_tmp.my_bool_value;
+ }
+};
+
+/**
+ The class for variables that have a special meaning for a session,
+ such as @@timestamp or @@rnd_seed1, their values typically cannot be read
+ from SV structure, and a special "read" callback is provided.
+
+ Class specific constructor arguments:
+ everything derived from Sys_var_ulonglong
+ session_special_read_function read_func_arg
+
+ Backing store: ulonglong
+
+ @note
+ These variables are session-only, global or command-line equivalents
+ are not supported as they're generally meaningless.
+*/
+class Sys_var_session_special: public Sys_var_ulonglong
+{
+ typedef bool (*session_special_update_function)(THD *thd, set_var *var);
+ typedef ulonglong (*session_special_read_function)(THD *thd);
+
+ session_special_read_function read_func;
+ session_special_update_function update_func;
+public:
+ Sys_var_session_special(const char *name_arg,
+ const char *comment, int flag_args,
+ CMD_LINE getopt,
+ ulonglong min_val, ulonglong max_val, uint block_size,
+ PolyLock *lock, enum binlog_status_enum binlog_status_arg,
+ on_check_function on_check_func,
+ session_special_update_function update_func_arg,
+ session_special_read_function read_func_arg,
+ const char *substitute=0)
+ : Sys_var_ulonglong(name_arg, comment, flag_args, 0,
+ sizeof(ulonglong), getopt, min_val,
+ max_val, 0, block_size, lock, binlog_status_arg, on_check_func, 0,
+ substitute),
+ read_func(read_func_arg), update_func(update_func_arg)
+ {
+ SYSVAR_ASSERT(scope() == ONLY_SESSION);
+ SYSVAR_ASSERT(getopt.id == -1); // NO_CMD_LINE, because the offset is fake
+ }
+ bool session_update(THD *thd, set_var *var)
+ { return update_func(thd, var); }
+ bool global_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(FALSE);
+ return true;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ { var->value= 0; }
+ void global_save_default(THD *thd, set_var *var)
+ { DBUG_ASSERT(FALSE); }
+ uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ thd->sys_var_tmp.ulonglong_value= read_func(thd);
+ return (uchar*) &thd->sys_var_tmp.ulonglong_value;
+ }
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ DBUG_ASSERT(FALSE);
+ return 0;
+ }
+};
+
+
+class Sys_var_session_special_double: public Sys_var_double
+{
+ typedef bool (*session_special_update_function)(THD *thd, set_var *var);
+ typedef double (*session_special_read_function)(THD *thd);
+
+ session_special_read_function read_func;
+ session_special_update_function update_func;
+public:
+ Sys_var_session_special_double(const char *name_arg,
+ const char *comment, int flag_args,
+ CMD_LINE getopt,
+ double min_val, double max_val,
+ PolyLock *lock, enum binlog_status_enum binlog_status_arg,
+ on_check_function on_check_func,
+ session_special_update_function update_func_arg,
+ session_special_read_function read_func_arg,
+ const char *substitute=0)
+ : Sys_var_double(name_arg, comment, flag_args, 0,
+ sizeof(double), getopt, min_val,
+ max_val, 0, lock, binlog_status_arg, on_check_func, 0,
+ substitute),
+ read_func(read_func_arg), update_func(update_func_arg)
+ {
+ SYSVAR_ASSERT(scope() == ONLY_SESSION);
+ SYSVAR_ASSERT(getopt.id == -1); // NO_CMD_LINE, because the offset is fake
+ }
+ bool session_update(THD *thd, set_var *var)
+ { return update_func(thd, var); }
+ bool global_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(FALSE);
+ return true;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ { var->value= 0; }
+ void global_save_default(THD *thd, set_var *var)
+ { DBUG_ASSERT(FALSE); }
+ uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ thd->sys_var_tmp.double_value= read_func(thd);
+ return (uchar*) &thd->sys_var_tmp.double_value;
+ }
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ DBUG_ASSERT(FALSE);
+ return 0;
+ }
+};
+
+
+/**
+ The class for read-only variables that show whether a particular
+ feature is supported by the server. Example: have_compression
+
+ Backing store: enum SHOW_COMP_OPTION
+
+ @note
+ These variables are necessarily read-only, only global, and have no
+ command-line equivalent.
+*/
+class Sys_var_have: public sys_var
+{
+public:
+ Sys_var_have(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt,
+ 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, 0,
+ lock, binlog_status_arg, on_check_func, on_update_func,
+ substitute)
+ {
+ SYSVAR_ASSERT(scope() == GLOBAL);
+ SYSVAR_ASSERT(getopt.id == -1);
+ SYSVAR_ASSERT(lock == 0);
+ SYSVAR_ASSERT(binlog_status_arg == VARIABLE_NOT_IN_BINLOG);
+ SYSVAR_ASSERT(is_readonly());
+ SYSVAR_ASSERT(on_update == 0);
+ SYSVAR_ASSERT(size == sizeof(enum SHOW_COMP_OPTION));
+ }
+ bool do_check(THD *thd, set_var *var) {
+ DBUG_ASSERT(FALSE);
+ return true;
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(FALSE);
+ return true;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(FALSE);
+ return true;
+ }
+ void session_save_default(THD *thd, set_var *var) { }
+ void global_save_default(THD *thd, set_var *var) { }
+ uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ DBUG_ASSERT(FALSE);
+ return 0;
+ }
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ return (uchar*)show_comp_option_name[global_var(enum SHOW_COMP_OPTION)];
+ }
+ bool check_update_type(Item_result type) { return false; }
+};
+
+/**
+ Generic class for variables for storing entities that are internally
+ represented as structures, have names, and possibly can be referred to by
+ numbers. Examples: character sets, collations, locales,
+
+ Class specific constructor arguments:
+ ptrdiff_t name_offset - offset of the 'name' field in the structure
+
+ Backing store: void*
+
+ @note
+ As every such a structure requires special treatment from my_getopt,
+ these variables don't support command-line equivalents, any such
+ command-line options should be added manually to my_long_options in mysqld.cc
+*/
+class Sys_var_struct: public sys_var
+{
+ ptrdiff_t name_offset; // offset to the 'name' property in the structure
+public:
+ Sys_var_struct(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt,
+ ptrdiff_t name_off, void *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),
+ name_offset(name_off)
+ {
+ option.var_type= GET_STR;
+ /*
+ struct variables are special on the command line - often (e.g. for
+ charsets) the name cannot be immediately resolved, but only after all
+ options (in particular, basedir) are parsed.
+
+ thus all struct command-line options should be added manually
+ to my_long_options in mysqld.cc
+ */
+ SYSVAR_ASSERT(getopt.id == -1);
+ SYSVAR_ASSERT(size == sizeof(void *));
+ }
+ bool do_check(THD *thd, set_var *var)
+ { return false; }
+ bool session_update(THD *thd, set_var *var)
+ {
+ session_var(thd, const void*)= var->save_result.ptr;
+ return false;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ global_var(const void*)= var->save_result.ptr;
+ return false;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ { var->save_result.ptr= global_var(void*); }
+ void global_save_default(THD *thd, set_var *var)
+ {
+ void **default_value= reinterpret_cast<void**>(option.def_value);
+ var->save_result.ptr= *default_value;
+ }
+ bool check_update_type(Item_result type)
+ { return type != INT_RESULT && type != STRING_RESULT; }
+ uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ uchar *ptr= session_var(thd, uchar*);
+ return ptr ? *(uchar**)(ptr+name_offset) : 0;
+ }
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ uchar *ptr= global_var(uchar*);
+ return ptr ? *(uchar**)(ptr+name_offset) : 0;
+ }
+};
+
+/**
+ The class for variables that store time zones
+
+ Backing store: Time_zone*
+
+ @note
+ Time zones cannot be supported directly by my_getopt, thus
+ these variables don't support command-line equivalents, any such
+ command-line options should be added manually to my_long_options in mysqld.cc
+*/
+class Sys_var_tz: public sys_var
+{
+public:
+ Sys_var_tz(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt,
+ Time_zone **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)
+ {
+ SYSVAR_ASSERT(getopt.id == -1);
+ SYSVAR_ASSERT(size == sizeof(Time_zone *));
+ }
+ bool do_check(THD *thd, set_var *var)
+ {
+ char buff[MAX_TIME_ZONE_NAME_LENGTH];
+ String str(buff, sizeof(buff), &my_charset_latin1);
+ String *res= var->value->val_str(&str);
+
+ if (!res)
+ return true;
+
+ if (!(var->save_result.time_zone= my_tz_find(thd, res)))
+ {
+ ErrConvString err(res);
+ my_error(ER_UNKNOWN_TIME_ZONE, MYF(0), err.ptr());
+ return true;
+ }
+ return false;
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ session_var(thd, Time_zone*)= var->save_result.time_zone;
+ return false;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ global_var(Time_zone*)= var->save_result.time_zone;
+ return false;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ {
+ var->save_result.time_zone= global_var(Time_zone*);
+ }
+ void global_save_default(THD *thd, set_var *var)
+ {
+ var->save_result.time_zone=
+ *(Time_zone**)(intptr)option.def_value;
+ }
+ uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ /*
+ This is an ugly fix for replication: we don't replicate properly queries
+ invoking system variables' values to update tables; but
+ CONVERT_TZ(,,@@session.time_zone) is so popular that we make it
+ replicable (i.e. we tell the binlog code to store the session
+ timezone). If it's the global value which was used we can't replicate
+ (binlog code stores session value only).
+ */
+ thd->time_zone_used= 1;
+ return (uchar *)(session_var(thd, Time_zone*)->get_name()->ptr());
+ }
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ return (uchar *)(global_var(Time_zone*)->get_name()->ptr());
+ }
+ bool check_update_type(Item_result type)
+ { return type != STRING_RESULT; }
+};
+
+/**
+ Special implementation for transaction isolation, that
+ distingushes between
+
+ SET GLOBAL TRANSACTION ISOLATION (stored in global_system_variables)
+ SET SESSION TRANSACTION ISOLATION (stored in thd->variables)
+ SET TRANSACTION ISOLATION (stored in thd->tx_isolation)
+
+ where the last statement sets isolation level for the next transaction only
+*/
+class Sys_var_tx_isolation: public Sys_var_enum
+{
+public:
+ Sys_var_tx_isolation(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, PolyLock *lock,
+ enum binlog_status_enum binlog_status_arg,
+ on_check_function on_check_func)
+ :Sys_var_enum(name_arg, comment, flag_args, off, size, getopt,
+ values, def_val, lock, binlog_status_arg, on_check_func)
+ {}
+ bool session_update(THD *thd, set_var *var)
+ {
+ if (var->type == OPT_SESSION && Sys_var_enum::session_update(thd, var))
+ return TRUE;
+ if (var->type == OPT_DEFAULT || !thd->in_active_multi_stmt_transaction())
+ thd->tx_isolation= (enum_tx_isolation) var->save_result.ulonglong_value;
+ return FALSE;
+ }
+};
+
+/*
+ Class for replicate_events_marked_for_skip.
+ We need a custom update function that ensures the slave is stopped when
+ the update is happening.
+*/
+class Sys_var_replicate_events_marked_for_skip: public Sys_var_enum
+{
+public:
+ Sys_var_replicate_events_marked_for_skip(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, PolyLock *lock,
+ enum binlog_status_enum binlog_status_arg,
+ on_check_function on_check_func)
+ :Sys_var_enum(name_arg, comment, flag_args, off, size, getopt,
+ values, def_val, lock, binlog_status_arg, on_check_func)
+ {}
+ bool global_update(THD *thd, set_var *var);
+};
+
+/****************************************************************************
+ Used templates
+****************************************************************************/
+
+#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
+template class List<set_var_base>;
+template class List_iterator_fast<set_var_base>;
+template class Sys_var_integer<int, GET_INT, SHOW_SINT>;
+template class Sys_var_integer<uint, GET_UINT, SHOW_INT>;
+template class Sys_var_integer<ulong, GET_ULONG, SHOW_LONG>;
+template class Sys_var_integer<ha_rows, GET_HA_ROWS, SHOW_HA_ROWS>;
+template class Sys_var_integer<ulonglong, GET_ULL, SHOW_LONGLONG>;
+#endif
+
diff --git a/sql/sys_vars_shared.h b/sql/sys_vars_shared.h
new file mode 100644
index 00000000000..ff050f63064
--- /dev/null
+++ b/sql/sys_vars_shared.h
@@ -0,0 +1,86 @@
+#ifndef SYS_VARS_SHARED_INCLUDED
+#define SYS_VARS_SHARED_INCLUDED
+
+/* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/**
+ @file
+ "protected" interface to sys_var - server configuration variables.
+
+ This header is included by files implementing support and utility
+ functions of sys_var's (set_var.cc) and files implementing
+ classes in the sys_var hierarchy (sql_plugin.cc)
+*/
+
+#include <sql_priv.h>
+#include "set_var.h"
+
+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_chain all_sys_vars;
+
+/** wrapper to hide a mutex and an rwlock under a common interface */
+class PolyLock
+{
+public:
+ virtual void rdlock()= 0;
+ virtual void wrlock()= 0;
+ virtual void unlock()= 0;
+ virtual ~PolyLock() {}
+};
+
+class PolyLock_mutex: public PolyLock
+{
+ mysql_mutex_t *mutex;
+public:
+ PolyLock_mutex(mysql_mutex_t *arg): mutex(arg) {}
+ void rdlock() { mysql_mutex_lock(mutex); }
+ void wrlock() { mysql_mutex_lock(mutex); }
+ void unlock() { mysql_mutex_unlock(mutex); }
+};
+
+class PolyLock_rwlock: public PolyLock
+{
+ mysql_rwlock_t *rwlock;
+public:
+ PolyLock_rwlock(mysql_rwlock_t *arg): rwlock(arg) {}
+ void rdlock() { mysql_rwlock_rdlock(rwlock); }
+ void wrlock() { mysql_rwlock_wrlock(rwlock); }
+ void unlock() { mysql_rwlock_unlock(rwlock); }
+};
+
+class AutoWLock
+{
+ PolyLock *lock;
+public:
+ AutoWLock(PolyLock *l) : lock(l) { if (lock) lock->wrlock(); }
+ ~AutoWLock() { if (lock) lock->unlock(); }
+};
+
+class AutoRLock
+{
+ PolyLock *lock;
+public:
+ AutoRLock(PolyLock *l) : lock(l) { if (lock) lock->rdlock(); }
+ ~AutoRLock() { if (lock) lock->unlock(); }
+};
+
+
+#endif /* SYS_VARS_SHARED_INCLUDED */
diff --git a/sql/table.cc b/sql/table.cc
index 47f70613ed4..4ce70987334 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -1,6 +1,5 @@
-/*
- Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 2008, 2014, Monty Program Ab.
+/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -13,23 +12,40 @@
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
-*/
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
/* Some general useful functions */
-#include "mysql_priv.h"
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_priv.h"
+#include "unireg.h" // REQUIRED: for other includes
+#include "table.h"
+#include "frm_crypt.h" // get_crypt_for_frm
+#include "key.h" // find_ref_key
+#include "sql_table.h" // build_table_filename,
+ // primary_key_name
#include "sql_trigger.h"
+#include "sql_parse.h" // free_items
+#include "strfunc.h" // unhex_type2
+#include "sql_partition.h" // mysql_unpack_partition,
+ // fix_partition_func, partition_info
+#include "sql_acl.h" // *_ACL, acl_getroot_no_password
+#include "sql_base.h" // release_table_share
#include "create_options.h"
#include <m_ctype.h>
#include "my_md5.h"
#include "my_bit.h"
#include "sql_select.h"
+#include "sql_derived.h"
+#include "mdl.h" // MDL_wait_for_graph_visitor
/* INFORMATION_SCHEMA name */
LEX_STRING INFORMATION_SCHEMA_NAME= {C_STRING_WITH_LEN("information_schema")};
+/* PERFORMANCE_SCHEMA name */
+LEX_STRING PERFORMANCE_SCHEMA_DB_NAME= {C_STRING_WITH_LEN("performance_schema")};
+
/* MYSQL_SCHEMA name */
LEX_STRING MYSQL_SCHEMA_NAME= {C_STRING_WITH_LEN("mysql")};
@@ -226,36 +242,34 @@ TABLE_CATEGORY get_table_category(const LEX_STRING *db, const LEX_STRING *name)
DBUG_ASSERT(db != NULL);
DBUG_ASSERT(name != NULL);
- if (is_schema_db(db->str, db->length))
- {
+ if (is_infoschema_db(db->str, db->length))
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))
+ return TABLE_CATEGORY_PERFORMANCE;
if ((db->length == MYSQL_SCHEMA_NAME.length) &&
(my_strcasecmp(system_charset_info,
- MYSQL_SCHEMA_NAME.str,
- db->str) == 0))
+ MYSQL_SCHEMA_NAME.str,
+ db->str) == 0))
{
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))
- {
- return TABLE_CATEGORY_PERFORMANCE;
- }
+ GENERAL_LOG_NAME.str,
+ name->str) == 0))
+ return TABLE_CATEGORY_LOG;
if ((name->length == SLOW_LOG_NAME.length) &&
(my_strcasecmp(system_charset_info,
- SLOW_LOG_NAME.str,
- name->str) == 0))
- {
- return TABLE_CATEGORY_PERFORMANCE;
- }
+ SLOW_LOG_NAME.str,
+ name->str) == 0))
+ return TABLE_CATEGORY_LOG;
}
return TABLE_CATEGORY_USER;
@@ -308,7 +322,7 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
share->normalized_path.str= share->path.str;
share->normalized_path.length= path_length;
- share->version= refresh_version;
+ share->set_refresh_version();
/*
Since alloc_table_share() can be called without any locking (for
@@ -320,9 +334,13 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
share->table_map_id= ~0UL;
share->cached_row_logging_check= -1;
+ share->used_tables.empty();
+ share->free_tables.empty();
+ share->m_flush_tickets.empty();
+
memcpy((char*) &share->mem_root, (char*) &mem_root, sizeof(mem_root));
- pthread_mutex_init(&share->mutex, MY_MUTEX_INIT_FAST);
- pthread_cond_init(&share->cond, NULL);
+ mysql_mutex_init(key_TABLE_SHARE_LOCK_ha_data,
+ &share->LOCK_ha_data, MY_MUTEX_INIT_FAST);
}
DBUG_RETURN(share);
}
@@ -381,70 +399,106 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
*/
share->table_map_id= (ulong) thd->query_id;
+ share->used_tables.empty();
+ share->free_tables.empty();
+ share->m_flush_tickets.empty();
+
DBUG_VOID_RETURN;
}
+/**
+ Release resources (plugins) used by the share and free its memory.
+ TABLE_SHARE is self-contained -- it's stored in its own MEM_ROOT.
+ Free this MEM_ROOT.
+*/
+
+void TABLE_SHARE::destroy()
+{
+ uint idx;
+ KEY *info_it;
+
+ /* The mutex is initialized only for shares that are part of the TDC */
+ if (tmp_table == NO_TMP_TABLE)
+ mysql_mutex_destroy(&LOCK_ha_data);
+ my_hash_free(&name_hash);
+
+ plugin_unlock(NULL, db_plugin);
+ db_plugin= NULL;
+
+ /* Release fulltext parsers */
+ info_it= key_info;
+ for (idx= keys; idx; idx--, info_it++)
+ {
+ if (info_it->flags & HA_USES_PARSER)
+ {
+ plugin_unlock(NULL, info_it->parser);
+ info_it->flags= 0;
+ }
+ }
+ if (ha_data_destroy)
+ {
+ ha_data_destroy(ha_data);
+ ha_data_destroy= NULL;
+ }
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (ha_part_data_destroy)
+ {
+ ha_part_data_destroy(ha_part_data);
+ ha_part_data_destroy= NULL;
+ }
+#endif /* WITH_PARTITION_STORAGE_ENGINE */
+ /*
+ Make a copy since the share is allocated in its own root,
+ and free_root() updates its argument after freeing the memory.
+ */
+ MEM_ROOT own_root= mem_root;
+ free_root(&own_root, MYF(0));
+}
+
/*
Free table share and memory used by it
SYNOPSIS
free_table_share()
share Table share
-
- NOTES
- share->mutex must be locked when we come here if it's not a temp table
*/
void free_table_share(TABLE_SHARE *share)
{
- MEM_ROOT mem_root;
- uint idx;
- KEY *key_info;
DBUG_ENTER("free_table_share");
DBUG_PRINT("enter", ("table: %s.%s", share->db.str, share->table_name.str));
DBUG_ASSERT(share->ref_count == 0);
- /*
- If someone is waiting for this to be deleted, inform it about this.
- Don't do a delete until we know that no one is refering to this anymore.
- */
- if (share->tmp_table == NO_TMP_TABLE)
+ if (share->m_flush_tickets.is_empty())
{
- /* share->mutex is locked in release_table_share() */
- while (share->waiting_on_cond)
- {
- pthread_cond_broadcast(&share->cond);
- pthread_cond_wait(&share->cond, &share->mutex);
- }
- /* No thread refers to this anymore */
- pthread_mutex_unlock(&share->mutex);
- pthread_mutex_destroy(&share->mutex);
- pthread_cond_destroy(&share->cond);
- }
- hash_free(&share->name_hash);
-
- plugin_unlock(NULL, share->db_plugin);
- share->db_plugin= NULL;
-
- /* Release fulltext parsers */
- key_info= share->key_info;
- for (idx= share->keys; idx; idx--, key_info++)
- {
- if (key_info->flags & HA_USES_PARSER)
- {
- plugin_unlock(NULL, key_info->parser);
- key_info->flags= 0;
- }
+ /*
+ No threads are waiting for this share to be flushed (the
+ share is not old, is for a temporary table, or just nobody
+ happens to be waiting for it). Destroy it.
+ */
+ share->destroy();
}
- if (share->ha_data_destroy)
+ else
{
- share->ha_data_destroy(share->ha_data);
- share->ha_data_destroy= NULL;
+ Wait_for_flush_list::Iterator it(share->m_flush_tickets);
+ Wait_for_flush *ticket;
+ /*
+ We're about to iterate over a list that is used
+ concurrently. Make sure this never happens without a lock.
+ */
+ mysql_mutex_assert_owner(&LOCK_open);
+
+ while ((ticket= it++))
+ (void) ticket->get_ctx()->m_wait.set_status(MDL_wait::GRANTED);
+ /*
+ If there are threads waiting for this share to be flushed,
+ the last one to receive the notification will destroy the
+ share. At this point the share is removed from the table
+ definition cache, so is OK to proceed here without waiting
+ for this thread to do the work.
+ */
}
- /* We must copy mem_root from share because share is allocated through it */
- memcpy((char*) &mem_root, (char*) &share->mem_root, sizeof(mem_root));
- free_root(&mem_root, MYF(0)); // Free's share
DBUG_VOID_RETURN;
}
@@ -552,7 +606,7 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags)
int error, table_type;
bool error_given;
File file;
- uchar head[288];
+ uchar head[64];
char path[FN_REFLEN];
MEM_ROOT **root_ptr, *old_root;
DBUG_ENTER("open_table_def");
@@ -563,7 +617,8 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags)
error_given= 0;
strxmov(path, share->normalized_path.str, reg_ext, NullS);
- if ((file= my_open(path, O_RDONLY | O_SHARE, MYF(0))) < 0)
+ if ((file= mysql_file_open(key_file_frm,
+ path, O_RDONLY | O_SHARE, MYF(0))) < 0)
{
/*
We don't try to open 5.0 unencoded name, if
@@ -574,7 +629,7 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags)
- non-encoded db or table name contain "#mysql50#" prefix.
This kind of tables must have been opened only by the
- my_open() above.
+ mysql_file_open() above.
*/
if (has_disabled_path_chars(share->table_name.str) ||
has_disabled_path_chars(share->db.str) ||
@@ -601,7 +656,8 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags)
so no need to check the old file name.
*/
if (length == share->normalized_path.length ||
- ((file= my_open(path, O_RDONLY | O_SHARE, MYF(0))) < 0))
+ ((file= mysql_file_open(key_file_frm,
+ path, O_RDONLY | O_SHARE, MYF(0))) < 0))
goto err_not_open;
/* Unencoded 5.0 table name found */
@@ -611,7 +667,7 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags)
}
error= 4;
- if (my_read(file, head, 64, MYF(MY_NABP)))
+ if (mysql_file_read(file, head, 64, MYF(MY_NABP)))
goto err;
if (head[0] == (uchar) 254 && head[1] == 1)
@@ -664,7 +720,7 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags)
thd->status_var.opened_shares++;
err:
- my_close(file, MYF(MY_WME));
+ mysql_file_close(file, MYF(MY_WME));
err_not_open:
if (error && !error_given)
@@ -677,6 +733,163 @@ err_not_open:
}
+static bool create_key_infos(uchar *strpos, uint keys, KEY *keyinfo,
+ uint new_frm_ver,
+ uint &ext_key_parts, TABLE_SHARE *share, uint len,
+ KEY *first_keyinfo, char* &keynames)
+{
+ uint i, j, n_length;
+ KEY_PART_INFO *key_part= NULL;
+ ulong *rec_per_key= NULL;
+ KEY_PART_INFO *first_key_part= NULL;
+ uint first_key_parts= 0;
+
+ if (!keys)
+ {
+ if (!(keyinfo = (KEY*) alloc_root(&share->mem_root, len)))
+ return 1;
+ bzero((char*) keyinfo, len);
+ key_part= reinterpret_cast<KEY_PART_INFO*> (keyinfo);
+ }
+
+ /*
+ If share->use_ext_keys is set to TRUE we assume that any key
+ can be extended by the components of the primary key whose
+ definition is read first from the frm file.
+ For each key only those fields of the assumed primary key are
+ added that are not included in the proper key definition.
+ If after all it turns out that there is no primary key the
+ added components are removed from each key.
+
+ When in the future we support others schemes of extending of
+ secondary keys with components of the primary key we'll have
+ to change the type of this flag for an enumeration type.
+ */
+
+ for (i=0 ; i < keys ; i++, keyinfo++)
+ {
+ if (new_frm_ver >= 3)
+ {
+ keyinfo->flags= (uint) uint2korr(strpos) ^ HA_NOSAME;
+ keyinfo->key_length= (uint) uint2korr(strpos+2);
+ keyinfo->key_parts= (uint) strpos[4];
+ keyinfo->algorithm= (enum ha_key_alg) strpos[5];
+ keyinfo->block_size= uint2korr(strpos+6);
+ strpos+=8;
+ }
+ else
+ {
+ keyinfo->flags= ((uint) strpos[0]) ^ HA_NOSAME;
+ keyinfo->key_length= (uint) uint2korr(strpos+1);
+ keyinfo->key_parts= (uint) strpos[3];
+ keyinfo->algorithm= HA_KEY_ALG_UNDEF;
+ strpos+=4;
+ }
+
+ if (i == 0)
+ {
+ ext_key_parts+= (share->use_ext_keys ? first_keyinfo->key_parts*(keys-1) : 0);
+ n_length=keys * sizeof(KEY) + ext_key_parts * sizeof(KEY_PART_INFO);
+ if (!(keyinfo= (KEY*) alloc_root(&share->mem_root,
+ n_length + len)))
+ return 1;
+ bzero((char*) keyinfo,n_length);
+ share->key_info= keyinfo;
+ key_part= reinterpret_cast<KEY_PART_INFO*> (keyinfo + keys);
+
+ if (!(rec_per_key= (ulong*) alloc_root(&share->mem_root,
+ sizeof(ulong) * ext_key_parts)))
+ return 1;
+ first_key_part= key_part;
+ first_key_parts= first_keyinfo->key_parts;
+ keyinfo->flags= first_keyinfo->flags;
+ keyinfo->key_length= first_keyinfo->key_length;
+ keyinfo->key_parts= first_keyinfo->key_parts;
+ keyinfo->algorithm= first_keyinfo->algorithm;
+ if (new_frm_ver >= 3)
+ keyinfo->block_size= first_keyinfo->block_size;
+ }
+
+ keyinfo->key_part= key_part;
+ keyinfo->rec_per_key= rec_per_key;
+ for (j=keyinfo->key_parts ; j-- ; key_part++)
+ {
+ *rec_per_key++=0;
+ key_part->fieldnr= (uint16) (uint2korr(strpos) & FIELD_NR_MASK);
+ key_part->offset= (uint) uint2korr(strpos+2)-1;
+ key_part->key_type= (uint) uint2korr(strpos+5);
+ // key_part->field= (Field*) 0; // Will be fixed later
+ if (new_frm_ver >= 1)
+ {
+ key_part->key_part_flag= *(strpos+4);
+ key_part->length= (uint) uint2korr(strpos+7);
+ strpos+=9;
+ }
+ else
+ {
+ key_part->length= *(strpos+4);
+ key_part->key_part_flag=0;
+ if (key_part->length > 128)
+ {
+ key_part->length&=127; /* purecov: inspected */
+ key_part->key_part_flag=HA_REVERSE_SORT; /* purecov: inspected */
+ }
+ strpos+=7;
+ }
+ key_part->store_length=key_part->length;
+ }
+ keyinfo->ext_key_parts= keyinfo->key_parts;
+ keyinfo->ext_key_flags= keyinfo->flags;
+ keyinfo->ext_key_part_map= 0;
+ if (share->use_ext_keys && i)
+ {
+ for (j= 0;
+ j < first_key_parts && keyinfo->ext_key_parts < MAX_REF_PARTS;
+ j++)
+ {
+ uint key_parts= keyinfo->key_parts;
+ KEY_PART_INFO* curr_key_part= keyinfo->key_part;
+ KEY_PART_INFO* curr_key_part_end= curr_key_part+key_parts;
+ for ( ; curr_key_part < curr_key_part_end; curr_key_part++)
+ {
+ if (curr_key_part->fieldnr == first_key_part[j].fieldnr)
+ break;
+ }
+ if (curr_key_part == curr_key_part_end)
+ {
+ *key_part++= first_key_part[j];
+ *rec_per_key++= 0;
+ keyinfo->ext_key_parts++;
+ keyinfo->ext_key_part_map|= 1 << j;
+ }
+ }
+ if (j == first_key_parts)
+ keyinfo->ext_key_flags= keyinfo->flags | HA_EXT_NOSAME;
+ }
+ share->ext_key_parts+= keyinfo->ext_key_parts;
+ }
+ keynames=(char*) key_part;
+ strpos+= (strmov(keynames, (char *) strpos) - keynames)+1;
+
+ //reading index comments
+ for (keyinfo= share->key_info, i=0; i < keys; i++, keyinfo++)
+ {
+ if (keyinfo->flags & HA_USES_COMMENT)
+ {
+ keyinfo->comment.length= uint2korr(strpos);
+ keyinfo->comment.str= strmake_root(&share->mem_root, (char*) strpos+2,
+ keyinfo->comment.length);
+ strpos+= 2 + keyinfo->comment.length;
+ }
+ DBUG_ASSERT(test(keyinfo->flags & HA_USES_COMMENT) ==
+ (keyinfo->comment.length > 0));
+ }
+
+ share->keys= keys; // do it *after* all key_info's are initialized
+
+ return 0;
+}
+
/*
Read data from a binary .frm file from MySQL 3.23 - 5.0 into TABLE_SHARE
*/
@@ -689,29 +902,35 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
uint interval_count, interval_parts, read_length, int_length;
uint db_create_options, keys, key_parts, n_length;
uint key_info_length, com_length, null_bit_pos;
- uint vcol_screen_length;
- uint extra_rec_buf_length, options_len;
- uint i,j;
+ uint extra_rec_buf_length;
+ uint i;
bool use_hash;
- char *keynames, *names, *comment_pos, *vcol_screen_pos;
+ char *keynames, *names, *comment_pos;
+ uchar forminfo[288];
uchar *record;
- uchar *disk_buff, *strpos, *null_flags, *null_pos, *options;
- uchar *buff= 0;
- ulong pos, record_offset, *rec_per_key, rec_buff_length;
+ uchar *disk_buff, *strpos, *null_flags, *null_pos;
+ ulong pos, record_offset;
+ ulong rec_buff_length;
handler *handler_file= 0;
KEY *keyinfo;
- KEY_PART_INFO *key_part;
+ KEY_PART_INFO *key_part= NULL;
SQL_CRYPT *crypted=0;
Field **field_ptr, *reg_field;
const char **interval_array;
enum legacy_db_type legacy_db_type;
my_bitmap_map *bitmaps;
bool null_bits_are_used;
+ uint vcol_screen_length, UNINIT_VAR(options_len);
+ char *vcol_screen_pos;
+ uchar *UNINIT_VAR(options);
+ uchar *extra_segment_buff= 0;
+ KEY first_keyinfo;
+ uint len;
+ uint ext_key_parts= 0;
+ keyinfo= &first_keyinfo;
+ share->ext_key_parts= 0;
DBUG_ENTER("open_binary_frm");
- LINT_INIT(options);
- LINT_INIT(options_len);
-
new_field_pack_flag= head[27];
new_frm_ver= (head[2] - FRM_VER);
field_pack_length= new_frm_ver < 2 ? 11 : 17;
@@ -722,6 +941,9 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
if (!(pos= get_form_pos(file, head)))
goto err; /* purecov: inspected */
+ mysql_file_seek(file,pos,MY_SEEK_SET,MYF(0));
+ if (mysql_file_read(file, forminfo,288,MYF(MY_NABP)))
+ goto err;
share->frm_version= head[2];
/*
Check if .frm file created by MySQL 5.0. In this case we want to
@@ -733,6 +955,11 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
share->frm_version= FRM_VER_TRUE_VARCHAR;
#ifdef WITH_PARTITION_STORAGE_ENGINE
+ /*
+ Yuck! Double-bad. Doesn't work with dynamic engine codes.
+ And doesn't lock the plugin. Fixed in 10.0.4
+ */
+ compile_time_assert(MYSQL_VERSION_ID < 100000);
if (*(head+61) &&
!(share->default_part_db_type=
ha_checktype(thd, (enum legacy_db_type) (uint) *(head+61), 1, 0)))
@@ -759,7 +986,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
share->transactional= (ha_choice) (head[39] & 3);
share->page_checksum= (ha_choice) ((head[39] >> 2) & 3);
share->row_type= (row_type) head[40];
- share->table_charset= get_charset((uint) head[38],MYF(0));
+ share->table_charset= get_charset((((uint) head[41]) << 8) +
+ (uint) head[38],MYF(0));
share->null_field_first= 1;
}
if (!share->table_charset)
@@ -784,86 +1012,24 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
/* Read keyinformation */
key_info_length= (uint) uint2korr(head+28);
- VOID(my_seek(file,(ulong) uint2korr(head+6),MY_SEEK_SET,MYF(0)));
+ mysql_file_seek(file, (ulong) uint2korr(head+6), MY_SEEK_SET, MYF(0));
if (read_string(file,(uchar**) &disk_buff,key_info_length))
goto err; /* purecov: inspected */
if (disk_buff[0] & 0x80)
{
- share->keys= keys= (disk_buff[1] << 7) | (disk_buff[0] & 0x7f);
+ keys= (disk_buff[1] << 7) | (disk_buff[0] & 0x7f);
share->key_parts= key_parts= uint2korr(disk_buff+2);
}
else
{
- share->keys= keys= disk_buff[0];
+ keys= disk_buff[0];
share->key_parts= key_parts= disk_buff[1];
}
share->keys_for_keyread.init(0);
share->keys_in_use.init(keys);
+ ext_key_parts= key_parts;
- n_length=keys*sizeof(KEY)+key_parts*sizeof(KEY_PART_INFO);
- if (!(keyinfo = (KEY*) alloc_root(&share->mem_root,
- n_length + uint2korr(disk_buff+4))))
- goto err; /* purecov: inspected */
- bzero((char*) keyinfo,n_length);
- share->key_info= keyinfo;
- key_part= my_reinterpret_cast(KEY_PART_INFO*) (keyinfo+keys);
- strpos=disk_buff+6;
-
- if (!(rec_per_key= (ulong*) alloc_root(&share->mem_root,
- sizeof(ulong)*key_parts)))
- goto err;
-
- for (i=0 ; i < keys ; i++, keyinfo++)
- {
- if (new_frm_ver >= 3)
- {
- keyinfo->flags= (uint) uint2korr(strpos) ^ HA_NOSAME;
- keyinfo->key_length= (uint) uint2korr(strpos+2);
- keyinfo->key_parts= (uint) strpos[4];
- keyinfo->algorithm= (enum ha_key_alg) strpos[5];
- keyinfo->block_size= uint2korr(strpos+6);
- strpos+=8;
- }
- else
- {
- keyinfo->flags= ((uint) strpos[0]) ^ HA_NOSAME;
- keyinfo->key_length= (uint) uint2korr(strpos+1);
- keyinfo->key_parts= (uint) strpos[3];
- keyinfo->algorithm= HA_KEY_ALG_UNDEF;
- strpos+=4;
- }
-
- keyinfo->key_part= key_part;
- keyinfo->rec_per_key= rec_per_key;
- for (j=keyinfo->key_parts ; j-- ; key_part++)
- {
- *rec_per_key++=0;
- key_part->fieldnr= (uint16) (uint2korr(strpos) & FIELD_NR_MASK);
- key_part->offset= (uint) uint2korr(strpos+2)-1;
- key_part->key_type= (uint) uint2korr(strpos+5);
- // key_part->field= (Field*) 0; // Will be fixed later
- if (new_frm_ver >= 1)
- {
- key_part->key_part_flag= *(strpos+4);
- key_part->length= (uint) uint2korr(strpos+7);
- strpos+=9;
- }
- else
- {
- key_part->length= *(strpos+4);
- key_part->key_part_flag=0;
- if (key_part->length > 128)
- {
- key_part->length&=127; /* purecov: inspected */
- key_part->key_part_flag=HA_REVERSE_SORT; /* purecov: inspected */
- }
- strpos+=7;
- }
- key_part->store_length=key_part->length;
- }
- }
- keynames=(char*) key_part;
- strpos+= (strmov(keynames, (char *) strpos) - keynames)+1;
+ len= (uint) uint2korr(disk_buff+4);
share->reclength = uint2korr((head+16));
share->stored_rec_length= share->reclength;
@@ -886,23 +1052,25 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
/* Read extra data segment */
uchar *next_chunk, *buff_end;
DBUG_PRINT("info", ("extra segment size is %u bytes", n_length));
- if (!(next_chunk= buff= (uchar*) my_malloc(n_length+1, MYF(MY_WME))))
+ if (!(extra_segment_buff= (uchar*) my_malloc(n_length + 1, MYF(MY_WME))))
goto err;
- if (my_pread(file, buff, n_length, record_offset + share->reclength,
- MYF(MY_NABP)))
+ next_chunk= extra_segment_buff;
+ if (mysql_file_pread(file, extra_segment_buff,
+ n_length, record_offset + share->reclength,
+ MYF(MY_NABP)))
{
- goto free_and_err;
+ goto err;
}
- share->connect_string.length= uint2korr(buff);
+ share->connect_string.length= uint2korr(next_chunk);
if (!(share->connect_string.str= strmake_root(&share->mem_root,
(char*) next_chunk + 2,
share->connect_string.
length)))
{
- goto free_and_err;
+ goto err;
}
next_chunk+= share->connect_string.length + 2;
- buff_end= buff + n_length;
+ buff_end= extra_segment_buff + n_length;
if (next_chunk + 2 < buff_end)
{
uint str_db_type_length= uint2korr(next_chunk);
@@ -919,7 +1087,10 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
plugin_data(tmp_plugin, handlerton *)))
{
/* bad file, legacy_db_type did not match the name */
- goto free_and_err;
+ sql_print_warning("%s.frm is inconsistent: engine typecode %d, engine name %s (%d)",
+ share->normalized_path.str, legacy_db_type,
+ plugin_name(tmp_plugin)->str,
+ ha_legacy_type(plugin_data(tmp_plugin, handlerton *)));
}
/*
tmp_plugin is locked with a local lock.
@@ -948,7 +1119,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
error= 8;
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
"--skip-partition");
- goto free_and_err;
+ goto err;
}
plugin_unlock(NULL, share->db_plugin);
share->db_plugin= ha_lock_engine(NULL, partition_hton);
@@ -961,35 +1132,43 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
{
/* purecov: begin inspected */
error= 8;
- name.str[name.length]= 0;
+ name.str[name.length]=0;
my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), name.str);
- goto free_and_err;
+ goto err;
/* purecov: end */
}
next_chunk+= str_db_type_length + 2;
}
+
+ share->set_use_ext_keys_flag(share->db_type()->flags & HTON_EXTENDED_KEYS);
+
+ if (create_key_infos(disk_buff + 6, keys, keyinfo, new_frm_ver,
+ ext_key_parts,
+ share, len, &first_keyinfo, keynames))
+ goto err;
+
if (next_chunk + 5 < buff_end)
{
- uint32 partition_info_len = uint4korr(next_chunk);
+ uint32 partition_info_str_len = uint4korr(next_chunk);
#ifdef WITH_PARTITION_STORAGE_ENGINE
if ((share->partition_info_buffer_size=
- share->partition_info_len= partition_info_len))
+ share->partition_info_str_len= partition_info_str_len))
{
- if (!(share->partition_info= (char*)
+ if (!(share->partition_info_str= (char*)
memdup_root(&share->mem_root, next_chunk + 4,
- partition_info_len + 1)))
+ partition_info_str_len + 1)))
{
- goto free_and_err;
+ goto err;
}
}
#else
- if (partition_info_len)
+ if (partition_info_str_len)
{
DBUG_PRINT("info", ("WITH_PARTITION_STORAGE_ENGINE is not defined"));
- goto free_and_err;
+ goto err;
}
#endif
- next_chunk+= 5 + partition_info_len;
+ next_chunk+= 5 + partition_info_str_len;
}
if (share->mysql_version >= 50110 && next_chunk < buff_end)
{
@@ -1009,7 +1188,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
{
DBUG_PRINT("error",
("fulltext key uses parser that is not defined in .frm"));
- goto free_and_err;
+ goto err;
}
parser_name.str= (char*) next_chunk;
parser_name.length= strlen((char*) next_chunk);
@@ -1019,11 +1198,31 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
if (! keyinfo->parser)
{
my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), parser_name.str);
- goto free_and_err;
+ goto err;
}
}
}
+
+ if (forminfo[46] == (uchar)255)
+ {
+ //reading long table comment
+ if (next_chunk + 2 > buff_end)
+ {
+ DBUG_PRINT("error",
+ ("long table comment is not defined in .frm"));
+ goto err;
+ }
+ share->comment.length = uint2korr(next_chunk);
+ if (! (share->comment.str= strmake_root(&share->mem_root,
+ (char*)next_chunk + 2, share->comment.length)))
+ {
+ goto err;
+ }
+ next_chunk+= 2 + share->comment.length;
+ }
+
DBUG_ASSERT(next_chunk <= buff_end);
+
if (share->db_create_options & HA_OPTION_TEXT_CREATE_OPTIONS)
{
/*
@@ -1036,6 +1235,14 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
}
DBUG_ASSERT(next_chunk <= buff_end);
}
+ else
+ {
+ if (create_key_infos(disk_buff + 6, keys, keyinfo, new_frm_ver,
+ ext_key_parts,
+ share, len, &first_keyinfo, keynames))
+ goto err;
+ }
+
share->key_block_size= uint2korr(head+62);
error=4;
@@ -1044,40 +1251,41 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
share->rec_buff_length= rec_buff_length;
if (!(record= (uchar *) alloc_root(&share->mem_root,
rec_buff_length)))
- goto free_and_err; /* purecov: inspected */
+ goto err; /* purecov: inspected */
share->default_values= record;
- if (my_pread(file, record, (size_t) share->reclength,
- record_offset, MYF(MY_NABP)))
- goto free_and_err; /* purecov: inspected */
+ if (mysql_file_pread(file, record, (size_t) share->reclength,
+ record_offset, MYF(MY_NABP)))
+ goto err; /* purecov: inspected */
- VOID(my_seek(file,pos,MY_SEEK_SET,MYF(0)));
- if (my_read(file, head,288,MYF(MY_NABP)))
- goto free_and_err;
+ mysql_file_seek(file, pos+288, MY_SEEK_SET, MYF(0));
#ifdef HAVE_CRYPTED_FRM
if (crypted)
{
- crypted->decode((char*) head+256,288-256);
- if (sint2korr(head+284) != 0) // Should be 0
- goto free_and_err; // Wrong password
+ crypted->decode((char*) forminfo+256,288-256);
+ if (sint2korr(forminfo+284) != 0) // Should be 0
+ goto err; // Wrong password
}
#endif
- share->fields= uint2korr(head+258);
- pos= uint2korr(head+260); /* Length of all screens */
- n_length= uint2korr(head+268);
- interval_count= uint2korr(head+270);
- interval_parts= uint2korr(head+272);
- int_length= uint2korr(head+274);
- share->null_fields= uint2korr(head+282);
- com_length= uint2korr(head+284);
- vcol_screen_length= uint2korr(head+286);
+ share->fields= uint2korr(forminfo+258);
+ pos= uint2korr(forminfo+260); /* Length of all screens */
+ n_length= uint2korr(forminfo+268);
+ interval_count= uint2korr(forminfo+270);
+ interval_parts= uint2korr(forminfo+272);
+ int_length= uint2korr(forminfo+274);
+ share->null_fields= uint2korr(forminfo+282);
+ com_length= uint2korr(forminfo+284);
+ vcol_screen_length= uint2korr(forminfo+286);
share->vfields= 0;
share->stored_fields= share->fields;
- share->comment.length= (int) (head[46]);
- share->comment.str= strmake_root(&share->mem_root, (char*) head+47,
- share->comment.length);
+ if (forminfo[46] != (uchar)255)
+ {
+ share->comment.length= (int) (forminfo[46]);
+ share->comment.str= strmake_root(&share->mem_root, (char*) forminfo+47,
+ share->comment.length);
+ }
- DBUG_PRINT("info",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d com_length: %d vcol_screen_length: %d", interval_count,interval_parts, share->keys,n_length,int_length, com_length, vcol_screen_length));
+ DBUG_PRINT("info",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d com_length: %d vcol_screen_length: %d", interval_count,interval_parts, keys,n_length,int_length, com_length, vcol_screen_length));
if (!(field_ptr = (Field **)
@@ -1088,14 +1296,14 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
keys+3)*sizeof(char *)+
(n_length+int_length+com_length+
vcol_screen_length)))))
- goto free_and_err; /* purecov: inspected */
+ goto err; /* purecov: inspected */
share->field= field_ptr;
read_length=(uint) (share->fields * field_pack_length +
pos+ (uint) (n_length+int_length+com_length+
vcol_screen_length));
if (read_string(file,(uchar**) &disk_buff,read_length))
- goto free_and_err; /* purecov: inspected */
+ goto err; /* purecov: inspected */
#ifdef HAVE_CRYPTED_FRM
if (crypted)
{
@@ -1122,7 +1330,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
fix_type_pointers(&interval_array, &share->fieldnames, 1, &names);
if (share->fieldnames.count != share->fields)
- goto free_and_err;
+ goto err;
fix_type_pointers(&interval_array, share->intervals, interval_count,
&names);
@@ -1136,7 +1344,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
uint count= (uint) (interval->count + 1) * sizeof(uint);
if (!(interval->type_lengths= (uint *) alloc_root(&share->mem_root,
count)))
- goto free_and_err;
+ goto err;
for (count= 0; count < interval->count; count++)
{
char *val= (char*) interval->type_names[count];
@@ -1152,7 +1360,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
/* Allocate handler */
if (!(handler_file= get_new_handler(share, thd->mem_root,
share->db_type())))
- goto free_and_err;
+ goto err;
record= share->default_values-1; /* Fieldstart = 1 */
null_bits_are_used= share->null_fields != 0;
@@ -1179,10 +1387,10 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
use_hash= share->fields >= MAX_FIELDS_BEFORE_HASH;
if (use_hash)
- use_hash= !hash_init(&share->name_hash,
- system_charset_info,
- share->fields,0,0,
- (hash_get_key) get_field_name,0,0);
+ use_hash= !my_hash_init(&share->name_hash,
+ system_charset_info,
+ share->fields,0,0,
+ (my_hash_get_key) get_field_name,0,0);
for (i=0 ; i < share->fields; i++, strpos+=field_pack_length, field_ptr++)
{
@@ -1215,18 +1423,19 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
charset= &my_charset_bin;
#else
error= 4; // unsupported field type
- goto free_and_err;
+ goto err;
#endif
}
else
{
- if (!strpos[14])
+ uint csid= strpos[14] + (((uint) strpos[11]) << 8);
+ if (!csid)
charset= &my_charset_bin;
- else if (!(charset=get_charset((uint) strpos[14], MYF(0))))
+ else if (!(charset= get_charset(csid, MYF(0))))
{
error= 5; // Unknown or unavailable charset
- errarg= (int) strpos[14];
- goto free_and_err;
+ errarg= (int) csid;
+ goto err;
}
}
@@ -1271,7 +1480,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
else if ((uint)vcol_screen_pos[0] != 1)
{
error= 4;
- goto free_and_err;
+ goto err;
}
fld_stored_in_db= (bool) (uint) vcol_screen_pos[2];
vcol_expr_length= vcol_info_length -
@@ -1281,7 +1490,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
vcol_screen_pos +
(uint) FRM_VCOL_HEADER_SIZE(opt_interval_id),
vcol_expr_length)))
- goto free_and_err;
+ goto err;
if (opt_interval_id)
interval_nr= (uint) vcol_screen_pos[3];
vcol_info->expr_str.length= vcol_expr_length;
@@ -1346,7 +1555,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
"Please do \"ALTER TABLE '%s' FORCE\" to fix it!",
share->fieldnames.type_names[i], share->table_name.str,
share->table_name.str);
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_CRASHED_ON_USAGE,
"Found incompatible DECIMAL field '%s' in %s; "
"Please do \"ALTER TABLE '%s' FORCE\" to fix it!",
@@ -1373,7 +1582,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
if (!reg_field) // Not supported field type
{
error= 4;
- goto free_and_err; /* purecov: inspected */
+ goto err; /* purecov: inspected */
}
reg_field->field_index= i;
@@ -1413,7 +1622,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
sent (OOM).
*/
error= 8;
- goto free_and_err;
+ goto err;
}
}
if (!reg_field->stored_in_db)
@@ -1431,13 +1640,40 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
/* Fix key->name and key_part->field */
if (key_parts)
{
- uint primary_key=(uint) (find_type((char*) primary_key_name,
- &share->keynames, 3) - 1);
+ uint add_first_key_parts= 0;
+ uint primary_key=(uint) (find_type(primary_key_name, &share->keynames,
+ FIND_TYPE_NO_PREFIX) - 1);
longlong ha_option= handler_file->ha_table_flags();
keyinfo= share->key_info;
- key_part= keyinfo->key_part;
- for (uint key=0 ; key < share->keys ; key++,keyinfo++)
+ if (share->use_ext_keys)
+ {
+ if (primary_key >= MAX_KEY)
+ {
+ add_first_key_parts= 0;
+ share->set_use_ext_keys_flag(FALSE);
+ }
+ else
+ {
+ add_first_key_parts= first_keyinfo.key_parts;
+ /*
+ Do not add components of the primary key starting from
+ the major component defined over the beginning of a field.
+ */
+ for (i= 0; i < first_keyinfo.key_parts; i++)
+ {
+ uint fieldnr= keyinfo[0].key_part[i].fieldnr;
+ if (share->field[fieldnr-1]->key_length() !=
+ keyinfo[0].key_part[i].length)
+ {
+ add_first_key_parts= i;
+ break;
+ }
+ }
+ }
+ }
+
+ for (uint key=0 ; key < keys ; key++,keyinfo++)
{
uint usable_parts= 0;
keyinfo->name=(char*) share->keynames.type_names[key];
@@ -1454,6 +1690,51 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
keyinfo->name_length+1);
}
+ if (ext_key_parts > share->key_parts && key)
+ {
+ KEY_PART_INFO *new_key_part= (keyinfo-1)->key_part +
+ (keyinfo-1)->ext_key_parts;
+
+ /*
+ Do not extend the key that contains a component
+ defined over the beginning of a field.
+ */
+ for (i= 0; i < keyinfo->key_parts; i++)
+ {
+ uint fieldnr= keyinfo->key_part[i].fieldnr;
+ if (share->field[fieldnr-1]->key_length() !=
+ keyinfo->key_part[i].length)
+ {
+ add_first_key_parts= 0;
+ break;
+ }
+ }
+
+ if (add_first_key_parts < keyinfo->ext_key_parts-keyinfo->key_parts)
+ {
+ share->ext_key_parts-= keyinfo->ext_key_parts;
+ key_part_map ext_key_part_map= keyinfo->ext_key_part_map;
+ keyinfo->ext_key_parts= keyinfo->key_parts;
+ keyinfo->ext_key_flags= keyinfo->flags;
+ keyinfo->ext_key_part_map= 0;
+ for (i= 0; i < add_first_key_parts; i++)
+ {
+ if (ext_key_part_map & 1<<i)
+ {
+ keyinfo->ext_key_part_map|= 1<<i;
+ keyinfo->ext_key_parts++;
+ }
+ }
+ share->ext_key_parts+= keyinfo->ext_key_parts;
+ }
+ if (new_key_part != keyinfo->key_part)
+ {
+ memmove(new_key_part, keyinfo->key_part,
+ sizeof(KEY_PART_INFO) * keyinfo->ext_key_parts);
+ keyinfo->key_part= new_key_part;
+ }
+ }
+
/* Fix fulltext keys for old .frm files */
if (share->key_info[key].flags & HA_FULLTEXT)
share->key_info[key].algorithm= HA_KEY_ALG_FULLTEXT;
@@ -1465,21 +1746,37 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
declare this as a primary key.
*/
primary_key=key;
+ key_part= keyinfo->key_part;
for (i=0 ; i < keyinfo->key_parts ;i++)
{
- uint fieldnr= key_part[i].fieldnr;
- if (!fieldnr ||
- share->field[fieldnr-1]->null_ptr ||
- share->field[fieldnr-1]->key_length() !=
- key_part[i].length)
+ DBUG_ASSERT(key_part[i].fieldnr > 0);
+ // Table field corresponding to the i'th key part.
+ Field *table_field= share->field[key_part[i].fieldnr - 1];
+
+ /*
+ If the key column is of NOT NULL BLOB type, then it
+ will definitly have key prefix. And if key part prefix size
+ is equal to the BLOB column max size, then we can promote
+ it to primary key.
+ */
+ if (!table_field->real_maybe_null() &&
+ table_field->type() == MYSQL_TYPE_BLOB &&
+ table_field->field_length == key_part[i].length)
+ continue;
+
+ if (table_field->real_maybe_null() ||
+ table_field->key_length() != key_part[i].length)
{
- primary_key=MAX_KEY; // Can't be used
+ primary_key= MAX_KEY; // Can't be used
break;
}
}
}
- for (i=0 ; i < keyinfo->key_parts ; key_part++,i++)
+ key_part= keyinfo->key_part;
+ uint key_parts= share->use_ext_keys ? keyinfo->ext_key_parts :
+ keyinfo->key_parts;
+ for (i=0; i < key_parts; key_part++, i++)
{
Field *field;
if (new_field_pack_flag <= 1)
@@ -1490,7 +1787,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
if (!key_part->fieldnr)
{
error= 4; // Wrong file
- goto free_and_err;
+ goto err;
}
field= key_part->field= share->field[key_part->fieldnr-1];
key_part->type= field->key_type();
@@ -1531,7 +1828,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
{
share->keys_for_keyread.set_bit(key);
field->part_of_key.set_bit(key);
- field->part_of_key_not_clustered.set_bit(key);
+ if (i < keyinfo->key_parts)
+ field->part_of_key_not_clustered.set_bit(key);
}
if (handler_file->index_flags(key, i, 1) & HA_READ_ORDER)
field->part_of_sortkey.set_bit(key);
@@ -1576,7 +1874,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
"Please do \"ALTER TABLE '%s' FORCE \" to fix it!",
share->table_name.str,
share->table_name.str);
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_CRASHED_ON_USAGE,
"Found wrong key definition in %s; "
"Please do \"ALTER TABLE '%s' FORCE\" to fix "
@@ -1638,7 +1936,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
}
else
share->primary_key= MAX_KEY;
- x_free((uchar*) disk_buff);
+ my_free(disk_buff);
disk_buff=0;
if (new_field_pack_flag <= 1)
{
@@ -1652,17 +1950,16 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
{
DBUG_ASSERT(options_len);
if (engine_table_options_frm_read(options, options_len, share))
- goto free_and_err;
+ goto err;
}
if (parse_engine_table_options(thd, handler_file->partition_ht(), share))
- goto free_and_err;
- my_free(buff, MYF(MY_ALLOW_ZERO_PTR));
+ goto err;
if (share->found_next_number_field)
{
reg_field= *share->found_next_number_field;
if ((int) (share->next_number_index= (uint)
- find_ref_key(share->key_info, share->keys,
+ find_ref_key(share->key_info, keys,
share->default_values, reg_field,
&share->next_number_key_offset,
&share->next_number_keypart)) < 0)
@@ -1714,25 +2011,32 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
delete handler_file;
#ifndef DBUG_OFF
if (use_hash)
- (void) hash_check(&share->name_hash);
+ (void) my_hash_check(&share->name_hash);
#endif
+ my_free(extra_segment_buff);
DBUG_RETURN (0);
- free_and_err:
- my_free(buff, MYF(MY_ALLOW_ZERO_PTR));
err:
share->error= error;
share->open_errno= my_errno;
share->errarg= errarg;
- x_free((uchar*) disk_buff);
+ my_free(disk_buff);
+ my_free(extra_segment_buff);
delete crypted;
delete handler_file;
- hash_free(&share->name_hash);
+ my_hash_free(&share->name_hash);
if (share->ha_data_destroy)
{
share->ha_data_destroy(share->ha_data);
share->ha_data_destroy= NULL;
}
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (share->ha_part_data_destroy)
+ {
+ share->ha_part_data_destroy(share->ha_part_data);
+ share->ha_data_destroy= NULL;
+ }
+#endif /* WITH_PARTITION_STORAGE_ENGINE */
open_table_error(share, error, share->open_errno, errarg);
DBUG_RETURN(error);
@@ -1797,75 +2101,27 @@ bool fix_vcol_expr(THD *thd,
{
Virtual_column_info *vcol_info= vcol_field->vcol_info;
Item* func_expr= vcol_info->expr_item;
- uint dir_length, home_dir_length;
bool result= TRUE;
TABLE_LIST tables;
- TABLE_LIST *save_table_list, *save_first_table, *save_last_table;
int error= 0;
- Name_resolution_context *context;
const char *save_where;
- char* db_name;
- char db_name_string[FN_REFLEN];
- bool save_use_only_table_context;
Field **ptr, *field;
enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
DBUG_ASSERT(func_expr);
DBUG_ENTER("fix_vcol_expr");
- /*
- Set-up the TABLE_LIST object to be a list with a single table
- Set the object to zero to create NULL pointers and set alias
- and real name to table name and get database name from file name.
- */
-
- bzero((void*)&tables, sizeof(TABLE_LIST));
- tables.alias= tables.table_name= (char*) table->s->table_name.str;
- tables.table= table;
- tables.next_local= 0;
- tables.next_name_resolution_table= 0;
- strmov(db_name_string, table->s->normalized_path.str);
- dir_length= dirname_length(db_name_string);
- db_name_string[dir_length - 1]= 0;
- home_dir_length= dirname_length(db_name_string);
- db_name= &db_name_string[home_dir_length];
- tables.db= db_name;
-
thd->mark_used_columns= MARK_COLUMNS_NONE;
- context= thd->lex->current_context();
- table->map= 1; //To ensure correct calculation of const item
- table->get_fields_in_item_tree= TRUE;
- save_table_list= context->table_list;
- save_first_table= context->first_name_resolution_table;
- save_last_table= context->last_name_resolution_table;
- context->table_list= &tables;
- context->first_name_resolution_table= &tables;
- context->last_name_resolution_table= NULL;
- func_expr->walk(&Item::change_context_processor, 0, (uchar*) context);
save_where= thd->where;
thd->where= "virtual column function";
- /* Save the context before fixing the fields*/
- save_use_only_table_context= thd->lex->use_only_table_context;
- thd->lex->use_only_table_context= TRUE;
/* Fix fields referenced to by the virtual column function */
if (!func_expr->fixed)
- {
- thd->lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VCOL_EXPR;
error= func_expr->fix_fields(thd, &vcol_info->expr_item);
- thd->lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_VCOL_EXPR;
- }
-
/* fix_fields could change the expression */
func_expr= vcol_info->expr_item;
/* Number of columns will be checked later */
- /* Restore the original context*/
- thd->lex->use_only_table_context= save_use_only_table_context;
- context->table_list= save_table_list;
- context->first_name_resolution_table= save_first_table;
- context->last_name_resolution_table= save_last_table;
-
if (unlikely(error))
{
DBUG_PRINT("info",
@@ -1971,6 +2227,8 @@ bool unpack_vcol_info_from_frm(THD *thd,
Query_arena backup_arena;
Query_arena *vcol_arena= 0;
Parser_state parser_state;
+ LEX *old_lex= thd->lex;
+ LEX lex;
DBUG_ENTER("unpack_vcol_info_from_frm");
DBUG_ASSERT(vcol_expr);
@@ -2018,7 +2276,7 @@ bool unpack_vcol_info_from_frm(THD *thd,
any new items created by fix_fields() are not reverted.
*/
Query_arena expr_arena(mem_root,
- Query_arena::CONVENTIONAL_EXECUTION);
+ Query_arena::STMT_CONVENTIONAL_EXECUTION);
if (!(vcol_arena= (Query_arena *) alloc_root(mem_root,
sizeof(Query_arena))))
goto err;
@@ -2028,6 +2286,9 @@ bool unpack_vcol_info_from_frm(THD *thd,
thd->set_n_backup_active_arena(vcol_arena, &backup_arena);
thd->stmt_arena= vcol_arena;
+ if (init_lex_with_single_table(thd, table, &lex))
+ goto err;
+
thd->lex->parse_vcol_expr= TRUE;
/*
@@ -2052,12 +2313,12 @@ bool unpack_vcol_info_from_frm(THD *thd,
err:
rc= TRUE;
- thd->lex->parse_vcol_expr= FALSE;
thd->free_items();
end:
thd->stmt_arena= backup_stmt_arena_ptr;
if (vcol_arena)
thd->restore_active_arena(vcol_arena, &backup_arena);
+ end_lex_with_single_table(thd, table, old_lex);
thd->variables.character_set_client= old_character_set_client;
DBUG_RETURN(rc);
@@ -2106,10 +2367,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
DBUG_PRINT("enter",("name: '%s.%s' form: 0x%lx", share->db.str,
share->table_name.str, (long) outparam));
- /* Parsing of partitioning information from .frm needs thd->lex set up. */
- DBUG_ASSERT(thd->lex->is_lex_started);
-
- thd->lex->context_analysis_only= 0; // not a view
+ thd->lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_VIEW; // not a view
error= 1;
bzero((char*) outparam, sizeof(*outparam));
@@ -2217,15 +2475,15 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
KEY *key_info, *key_info_end;
KEY_PART_INFO *key_part;
uint n_length;
- n_length= share->keys*sizeof(KEY) + share->key_parts*sizeof(KEY_PART_INFO);
+ n_length= share->keys*sizeof(KEY) + share->ext_key_parts*sizeof(KEY_PART_INFO);
if (!(key_info= (KEY*) alloc_root(&outparam->mem_root, n_length)))
goto err;
outparam->key_info= key_info;
- key_part= (my_reinterpret_cast(KEY_PART_INFO*) (key_info+share->keys));
-
+ key_part= (reinterpret_cast<KEY_PART_INFO*>(key_info+share->keys));
+
memcpy(key_info, share->key_info, sizeof(*key_info)*share->keys);
memcpy(key_part, share->key_info[0].key_part, (sizeof(*key_part) *
- share->key_parts));
+ share->ext_key_parts));
for (key_info_end= key_info + share->keys ;
key_info < key_info_end ;
@@ -2236,11 +2494,11 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
key_info->table= outparam;
key_info->key_part= key_part;
- for (key_part_end= key_part+ key_info->key_parts ;
- key_part < key_part_end ;
- key_part++)
+ key_part_end= key_part + (share->use_ext_keys ? key_info->ext_key_parts :
+ key_info->key_parts) ;
+ for ( ; key_part < key_part_end; key_part++)
{
- Field *field= key_part->field= outparam->field[key_part->fieldnr-1];
+ Field *field= key_part->field= outparam->field[key_part->fieldnr - 1];
if (field->key_length() != key_part->length &&
!(field->flags & BLOB_FLAG))
@@ -2254,6 +2512,8 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
field->field_length= key_part->length;
}
}
+ if (!share->use_ext_keys)
+ key_part+= key_info->ext_key_parts - key_info->key_parts;
}
}
@@ -2292,7 +2552,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
- if (share->partition_info_len && outparam->file)
+ if (share->partition_info_str_len && outparam->file)
{
/*
In this execution we must avoid calling thd->change_item_tree since
@@ -2307,16 +2567,15 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
Query_arena *backup_stmt_arena_ptr= thd->stmt_arena;
Query_arena backup_arena;
- Query_arena part_func_arena(&outparam->mem_root, Query_arena::INITIALIZED);
+ Query_arena part_func_arena(&outparam->mem_root,
+ Query_arena::STMT_INITIALIZED);
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,
- share->partition_info_len,
- share->part_state,
- share->part_state_len,
+ tmp= mysql_unpack_partition(thd, share->partition_info_str,
+ share->partition_info_str_len,
outparam, is_create_table,
share->default_part_db_type,
&work_part_info_used);
@@ -2511,7 +2770,7 @@ int closefrm(register TABLE *table, bool free_share)
if (free_share)
{
if (table->s->tmp_table == NO_TMP_TABLE)
- release_table_share(table->s, RELEASE_NORMAL);
+ release_table_share(table->s);
else
free_table_share(table->s);
}
@@ -2528,7 +2787,15 @@ void free_blobs(register TABLE *table)
for (ptr= table->s->blob_field, end=ptr + table->s->blob_fields ;
ptr != end ;
ptr++)
- ((Field_blob*) table->field[*ptr])->free();
+ {
+ /*
+ Reduced TABLE objects which are used by row-based replication for
+ type conversion might have some fields missing. Skip freeing BLOB
+ buffers for such missing fields.
+ */
+ if (table->field[*ptr])
+ ((Field_blob*) table->field[*ptr])->free();
+ }
}
@@ -2578,21 +2845,21 @@ static ulong get_form_pos(File file, uchar *head)
length= uint2korr(head+4);
- my_seek(file, 64L, MY_SEEK_SET, MYF(0));
+ mysql_file_seek(file, 64L, MY_SEEK_SET, MYF(0));
if (!(buf= (uchar*) my_malloc(length+names*4, MYF(MY_WME))))
DBUG_RETURN(0);
- if (my_read(file, buf, length+names*4, MYF(MY_NABP)))
+ if (mysql_file_read(file, buf, length+names*4, MYF(MY_NABP)))
{
- x_free(buf);
+ my_free(buf);
DBUG_RETURN(0);
}
pos= buf+length;
ret_value= uint4korr(pos);
- my_free(buf, MYF(0));
+ my_free(buf);
DBUG_RETURN(ret_value);
}
@@ -2609,11 +2876,11 @@ int read_string(File file, uchar**to, size_t length)
{
DBUG_ENTER("read_string");
- x_free(*to);
+ my_free(*to);
if (!(*to= (uchar*) my_malloc(length+1,MYF(MY_WME))) ||
- my_read(file, *to, length,MYF(MY_NABP)))
+ mysql_file_read(file, *to, length, MYF(MY_NABP)))
{
- x_free(*to); /* purecov: inspected */
+ my_free(*to); /* purecov: inspected */
*to= 0; /* purecov: inspected */
DBUG_RETURN(1); /* purecov: inspected */
}
@@ -2643,23 +2910,24 @@ ulong make_new_entry(File file, uchar *fileinfo, TYPELIB *formnames,
{ /* Expand file */
newpos+=IO_SIZE;
int4store(fileinfo+10,newpos);
- endpos=(ulong) my_seek(file,0L,MY_SEEK_END,MYF(0));/* Copy from file-end */
+ /* Copy from file-end */
+ endpos= (ulong) mysql_file_seek(file, 0L, MY_SEEK_END, MYF(0));
bufflength= (uint) (endpos & (IO_SIZE-1)); /* IO_SIZE is a power of 2 */
while (endpos > maxlength)
{
- VOID(my_seek(file,(ulong) (endpos-bufflength),MY_SEEK_SET,MYF(0)));
- if (my_read(file, buff, bufflength, MYF(MY_NABP+MY_WME)))
+ mysql_file_seek(file, (ulong) (endpos-bufflength), MY_SEEK_SET, MYF(0));
+ if (mysql_file_read(file, buff, bufflength, MYF(MY_NABP+MY_WME)))
DBUG_RETURN(0L);
- VOID(my_seek(file,(ulong) (endpos-bufflength+IO_SIZE),MY_SEEK_SET,
- MYF(0)));
- if ((my_write(file, buff,bufflength,MYF(MY_NABP+MY_WME))))
+ mysql_file_seek(file, (ulong) (endpos-bufflength+IO_SIZE), MY_SEEK_SET,
+ MYF(0));
+ if ((mysql_file_write(file, buff, bufflength, MYF(MY_NABP+MY_WME))))
DBUG_RETURN(0);
endpos-=bufflength; bufflength=IO_SIZE;
}
bzero(buff,IO_SIZE); /* Null new block */
- VOID(my_seek(file,(ulong) maxlength,MY_SEEK_SET,MYF(0)));
- if (my_write(file,buff,bufflength,MYF(MY_NABP+MY_WME)))
+ mysql_file_seek(file, (ulong) maxlength, MY_SEEK_SET, MYF(0));
+ if (mysql_file_write(file, buff, bufflength, MYF(MY_NABP+MY_WME)))
DBUG_RETURN(0L);
maxlength+=IO_SIZE; /* Fix old ref */
int2store(fileinfo+6,maxlength);
@@ -2674,20 +2942,21 @@ ulong make_new_entry(File file, uchar *fileinfo, TYPELIB *formnames,
if (n_length == 1 )
{ /* First name */
length++;
- VOID(strxmov((char*) buff,"/",newname,"/",NullS));
+ (void) strxmov((char*) buff,"/",newname,"/",NullS);
}
else
- VOID(strxmov((char*) buff,newname,"/",NullS)); /* purecov: inspected */
- VOID(my_seek(file,63L+(ulong) n_length,MY_SEEK_SET,MYF(0)));
- if (my_write(file, buff, (size_t) length+1,MYF(MY_NABP+MY_WME)) ||
- (names && my_write(file,(uchar*) (*formnames->type_names+n_length-1),
- names*4, MYF(MY_NABP+MY_WME))) ||
- my_write(file, fileinfo+10, 4,MYF(MY_NABP+MY_WME)))
+ (void) strxmov((char*) buff,newname,"/",NullS); /* purecov: inspected */
+ mysql_file_seek(file, 63L+(ulong) n_length, MY_SEEK_SET, MYF(0));
+ if (mysql_file_write(file, buff, (size_t) length+1, MYF(MY_NABP+MY_WME)) ||
+ (names && mysql_file_write(file,
+ (uchar*) (*formnames->type_names+n_length-1),
+ names*4, MYF(MY_NABP+MY_WME))) ||
+ mysql_file_write(file, fileinfo+10, 4, MYF(MY_NABP+MY_WME)))
DBUG_RETURN(0L); /* purecov: inspected */
int2store(fileinfo+8,names+1);
int2store(fileinfo+4,n_length+length);
- VOID(my_chsize(file, newpos, 0, MYF(MY_WME)));/* Append file with '\0' */
+ (void) mysql_file_chsize(file, newpos, 0, MYF(MY_WME));/* Append file with '\0' */
DBUG_RETURN(newpos);
} /* make_new_entry */
@@ -2957,12 +3226,14 @@ void append_unescaped(String *res, const char *pos, uint length)
File create_frm(THD *thd, const char *name, const char *db,
const char *table, uint reclength, uchar *fileinfo,
- HA_CREATE_INFO *create_info, uint keys)
+ HA_CREATE_INFO *create_info, uint keys, KEY *key_info)
{
register File file;
ulong length;
uchar fill[IO_SIZE];
int create_flags= O_RDWR | O_TRUNC;
+ ulong key_comment_total_bytes= 0;
+ uint i;
DBUG_ENTER("create_frm");
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
@@ -2974,10 +3245,10 @@ File create_frm(THD *thd, const char *name, const char *db,
if (create_info->min_rows > UINT_MAX32)
create_info->min_rows= UINT_MAX32;
- if ((file= my_create(name, CREATE_MODE, create_flags, MYF(0))) >= 0)
+ if ((file= mysql_file_create(key_file_frm,
+ name, CREATE_MODE, create_flags, MYF(0))) >= 0)
{
- ulong key_length, tmp_key_length;
- uint tmp;
+ uint key_length, tmp_key_length, tmp, csid;
bzero((char*) fileinfo,64);
/* header */
fileinfo[0]=(uchar) 254;
@@ -3000,7 +3271,17 @@ File create_frm(THD *thd, const char *name, const char *db,
1 byte for the NAMES_SEP_CHAR (after the last name)
9 extra bytes (padding for safety? alignment?)
*/
- key_length= keys * (8 + MAX_REF_PARTS * 9 + NAME_LEN + 1) + 16;
+ for (i= 0; i < keys; i++)
+ {
+ DBUG_ASSERT(test(key_info[i].flags & HA_USES_COMMENT) ==
+ (key_info[i].comment.length > 0));
+ if (key_info[i].flags & HA_USES_COMMENT)
+ key_comment_total_bytes += 2 + key_info[i].comment.length;
+ }
+
+ key_length= keys * (8 + MAX_REF_PARTS * 9 + NAME_LEN + 1) + 16
+ + key_comment_total_bytes;
+
length= next_io_size((ulong) (IO_SIZE+key_length+reclength+
create_info->extra_size));
int4store(fileinfo+10,length);
@@ -3017,13 +3298,14 @@ File create_frm(THD *thd, const char *name, const char *db,
fileinfo[32]=0; // No filename anymore
fileinfo[33]=5; // Mark for 5.0 frm file
int4store(fileinfo+34,create_info->avg_row_length);
- fileinfo[38]= (create_info->default_table_charset ?
- create_info->default_table_charset->number : 0);
+ csid= (create_info->default_table_charset ?
+ create_info->default_table_charset->number : 0);
+ fileinfo[38]= (uchar) csid;
fileinfo[39]= (uchar) ((uint) create_info->transactional |
((uint) create_info->page_checksum << 2));
fileinfo[40]= (uchar) create_info->row_type;
/* Next few bytes where for RAID support */
- fileinfo[41]= 0;
+ fileinfo[41]= (uchar) (csid >> 8);
fileinfo[42]= 0;
fileinfo[43]= 0;
fileinfo[44]= 0;
@@ -3041,11 +3323,11 @@ File create_frm(THD *thd, const char *name, const char *db,
bzero(fill,IO_SIZE);
for (; length > IO_SIZE ; length-= IO_SIZE)
{
- if (my_write(file,fill, IO_SIZE, MYF(MY_WME | MY_NABP)))
+ if (mysql_file_write(file, fill, IO_SIZE, MYF(MY_WME | MY_NABP)))
{
- VOID(my_close(file,MYF(0)));
- VOID(my_delete(name,MYF(0)));
- DBUG_RETURN(-1);
+ (void) mysql_file_close(file, MYF(0));
+ (void) mysql_file_delete(key_file_frm, name, MYF(0));
+ return(-1);
}
}
}
@@ -3084,9 +3366,9 @@ int
rename_file_ext(const char * from,const char * to,const char * ext)
{
char from_b[FN_REFLEN],to_b[FN_REFLEN];
- VOID(strxmov(from_b,from,ext,NullS));
- VOID(strxmov(to_b,to,ext,NullS));
- return (my_rename(from_b,to_b,MYF(MY_WME)));
+ (void) strxmov(from_b,from,ext,NullS);
+ (void) strxmov(to_b,to,ext,NullS);
+ return (mysql_file_rename(key_file_frm, from_b, to_b, MYF(MY_WME)));
}
@@ -3164,7 +3446,7 @@ uint calculate_key_len(TABLE *table, uint key, const uchar *buf,
KEY *key_info= table->s->key_info+key;
KEY_PART_INFO *key_part= key_info->key_part;
- KEY_PART_INFO *end_key_part= key_part + key_info->key_parts;
+ KEY_PART_INFO *end_key_part= key_part + table->actual_n_key_parts(key_info);
uint length= 0;
while (key_part < end_key_part && keypart_map)
@@ -3222,9 +3504,10 @@ bool check_db_name(LEX_STRING *org_name)
returns 1 on error
*/
-bool check_table_name(const char *name, uint length, bool check_for_path_chars)
+bool check_table_name(const char *name, size_t length, bool check_for_path_chars)
{
- uint name_length= 0; // name length in symbols
+ // name length in symbols
+ size_t name_length= 0;
const char *end= name+length;
@@ -3266,18 +3549,19 @@ bool check_table_name(const char *name, uint length, bool check_for_path_chars)
name_length++;
}
#if defined(USE_MB) && defined(USE_MB_IDENT)
- return (last_char_is_space || name_length > NAME_CHAR_LEN) ;
+ return last_char_is_space || (name_length > NAME_CHAR_LEN);
#else
- return 0;
+ return FALSE;
#endif
}
bool check_column_name(const char *name)
{
- uint name_length= 0; // name length in symbols
+ // name length in symbols
+ size_t name_length= 0;
bool last_char_is_space= TRUE;
-
+
while (*name)
{
#if defined(USE_MB) && defined(USE_MB_IDENT)
@@ -3295,14 +3579,14 @@ bool check_column_name(const char *name)
}
#else
last_char_is_space= *name==' ';
-#endif
- if (*name == NAMES_SEP_CHAR)
+ if (*name == '\377')
return 1;
+#endif
name++;
name_length++;
}
/* Error if empty or too long column name */
- return last_char_is_space || (uint) name_length > NAME_CHAR_LEN;
+ return last_char_is_space || (name_length > NAME_CHAR_LEN);
}
@@ -3347,7 +3631,8 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
report_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE,
ER(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE),
table->alias.c_ptr(), table_def->count, table->s->fields,
- (int) table->s->mysql_version, MYSQL_VERSION_ID);
+ static_cast<int>(table->s->mysql_version),
+ MYSQL_VERSION_ID);
DBUG_RETURN(TRUE);
}
else if (MYSQL_VERSION_ID == table->s->mysql_version)
@@ -3461,11 +3746,264 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
}
+/**
+ Traverse portion of wait-for graph which is reachable through edge
+ represented by this flush ticket in search for deadlocks.
+
+ @retval TRUE A deadlock is found. A victim is remembered
+ by the visitor.
+ @retval FALSE Success, no deadlocks.
+*/
+
+bool Wait_for_flush::accept_visitor(MDL_wait_for_graph_visitor *gvisitor)
+{
+ return m_share->visit_subgraph(this, gvisitor);
+}
+
+
+uint Wait_for_flush::get_deadlock_weight() const
+{
+ return m_deadlock_weight;
+}
+
+
+/**
+ Traverse portion of wait-for graph which is reachable through this
+ table share in search for deadlocks.
+
+ @param waiting_ticket Ticket representing wait for this share.
+ @param dvisitor Deadlock detection visitor.
+
+ @retval TRUE A deadlock is found. A victim is remembered
+ by the visitor.
+ @retval FALSE No deadlocks, it's OK to begin wait.
+*/
+
+bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush,
+ MDL_wait_for_graph_visitor *gvisitor)
+{
+ TABLE *table;
+ MDL_context *src_ctx= wait_for_flush->get_ctx();
+ bool result= TRUE;
+
+ /*
+ To protect used_tables list from being concurrently modified
+ while we are iterating through it we acquire LOCK_open.
+ This does not introduce deadlocks in the deadlock detector
+ because we won't try to acquire LOCK_open while
+ holding a write-lock on MDL_lock::m_rwlock.
+ */
+ if (gvisitor->m_lock_open_count++ == 0)
+ mysql_mutex_lock(&LOCK_open);
+
+ I_P_List_iterator <TABLE, TABLE_share> tables_it(used_tables);
+
+ /*
+ In case of multiple searches running in parallel, avoid going
+ over the same loop twice and shortcut the search.
+ Do it after taking the lock to weed out unnecessary races.
+ */
+ if (src_ctx->m_wait.get_status() != MDL_wait::EMPTY)
+ {
+ result= FALSE;
+ goto end;
+ }
+
+ if (gvisitor->enter_node(src_ctx))
+ goto end;
+
+ while ((table= tables_it++))
+ {
+ if (gvisitor->inspect_edge(&table->in_use->mdl_context))
+ {
+ goto end_leave_node;
+ }
+ }
+
+ tables_it.rewind();
+ while ((table= tables_it++))
+ {
+ if (table->in_use->mdl_context.visit_subgraph(gvisitor))
+ {
+ goto end_leave_node;
+ }
+ }
+
+ result= FALSE;
+
+end_leave_node:
+ gvisitor->leave_node(src_ctx);
+
+end:
+ if (gvisitor->m_lock_open_count-- == 1)
+ mysql_mutex_unlock(&LOCK_open);
+
+ return result;
+}
+
+
+/**
+ Wait until the subject share is removed from the table
+ definition cache and make sure it's destroyed.
+
+ @param mdl_context MDL context for thread which is going to wait.
+ @param abstime Timeout for waiting as absolute time value.
+ @param deadlock_weight Weight of this wait for deadlock detector.
+
+ @pre LOCK_open is write locked, the share is used (has
+ non-zero reference count), is marked for flush and
+ this connection does not reference the share.
+ LOCK_open will be unlocked temporarily during execution.
+
+ @retval FALSE - Success.
+ @retval TRUE - Error (OOM, deadlock, timeout, etc...).
+*/
+
+bool TABLE_SHARE::wait_for_old_version(THD *thd, struct timespec *abstime,
+ uint deadlock_weight)
+{
+ MDL_context *mdl_context= &thd->mdl_context;
+ Wait_for_flush ticket(mdl_context, this, deadlock_weight);
+ MDL_wait::enum_wait_status wait_status;
+
+ mysql_mutex_assert_owner(&LOCK_open);
+ /*
+ We should enter this method only when share's version is not
+ up to date and the share is referenced. Otherwise our
+ thread will never be woken up from wait.
+ */
+ DBUG_ASSERT(version != refresh_version && ref_count != 0);
+
+ m_flush_tickets.push_front(&ticket);
+
+ mdl_context->m_wait.reset_status();
+
+ mysql_mutex_unlock(&LOCK_open);
+
+ mdl_context->will_wait_for(&ticket);
+
+ mdl_context->find_deadlock();
+
+ wait_status= mdl_context->m_wait.timed_wait(thd, abstime, TRUE,
+ "Waiting for table flush");
+
+ mdl_context->done_waiting_for();
+
+ mysql_mutex_lock(&LOCK_open);
+
+ m_flush_tickets.remove(&ticket);
+
+ if (m_flush_tickets.is_empty() && ref_count == 0)
+ {
+ /*
+ If our thread was the last one using the share,
+ we must destroy it here.
+ */
+ destroy();
+ }
+
+ /*
+ In cases when our wait was aborted by KILL statement,
+ a deadlock or a timeout, the share might still be referenced,
+ so we don't delete it. Note, that we can't determine this
+ condition by checking wait_status alone, since, for example,
+ a timeout can happen after all references to the table share
+ were released, but before the share is removed from the
+ cache and we receive the notification. This is why
+ we first destroy the share, and then look at
+ wait_status.
+ */
+ switch (wait_status)
+ {
+ case MDL_wait::GRANTED:
+ return FALSE;
+ case MDL_wait::VICTIM:
+ my_error(ER_LOCK_DEADLOCK, MYF(0));
+ return TRUE;
+ case MDL_wait::TIMEOUT:
+ my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
+ return TRUE;
+ case MDL_wait::KILLED:
+ return TRUE;
+ default:
+ DBUG_ASSERT(0);
+ return TRUE;
+ }
+}
+
+
+/**
+ Initialize TABLE instance (newly created, or coming either from table
+ cache or THD::temporary_tables list) and prepare it for further use
+ during statement execution. Set the 'alias' attribute from the specified
+ TABLE_LIST element. Remember the TABLE_LIST element in the
+ TABLE::pos_in_table_list member.
+
+ @param thd Thread context.
+ @param tl TABLE_LIST element.
+*/
+
+void TABLE::init(THD *thd, TABLE_LIST *tl)
+{
+ DBUG_ASSERT(s->ref_count > 0 || s->tmp_table != NO_TMP_TABLE);
+
+ if (thd->lex->need_correct_ident())
+ alias_name_used= my_strcasecmp(table_alias_charset,
+ s->table_name.str,
+ tl->alias);
+ /* Fix alias if table name changes. */
+ if (strcmp(alias.c_ptr(), tl->alias))
+ alias.copy(tl->alias, strlen(tl->alias), alias.charset());
+
+ tablenr= thd->current_tablenr++;
+ used_fields= 0;
+ const_table= 0;
+ null_row= 0;
+ maybe_null= 0;
+ force_index= 0;
+ force_index_order= 0;
+ force_index_group= 0;
+ status= STATUS_NO_RECORD;
+ insert_values= 0;
+ fulltext_searched= 0;
+ file->ft_handler= 0;
+ reginfo.impossible_range= 0;
+ created= TRUE;
+
+ /* Catch wrong handling of the auto_increment_field_not_null. */
+ DBUG_ASSERT(!auto_increment_field_not_null);
+ auto_increment_field_not_null= FALSE;
+
+ if (timestamp_field)
+ timestamp_field_type= timestamp_field->get_auto_set_type();
+
+ pos_in_table_list= tl;
+
+ clear_column_bitmaps();
+
+ DBUG_ASSERT(key_read == 0);
+
+ /* mark the record[0] uninitialized */
+ TRASH(record[0], s->reclength);
+
+ /*
+ Initialize the null marker bits, to ensure that if we are doing a read
+ of only selected columns (like in keyread), all null markers are
+ initialized.
+ */
+ memset(record[0], 255, s->null_bytes);
+ memset(record[1], 255, s->null_bytes);
+
+ /* Tables may be reused in a sub statement. */
+ DBUG_ASSERT(!file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
+}
+
+
/*
Create Item_field for each column in the table.
SYNPOSIS
- st_table::fill_item_list()
+ TABLE::fill_item_list()
item_list a pointer to an empty list used to store items
DESCRIPTION
@@ -3478,7 +4016,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
1 out of memory
*/
-bool st_table::fill_item_list(List<Item> *item_list) const
+bool TABLE::fill_item_list(List<Item> *item_list) const
{
/*
All Item_field's created using a direct pointer to a field
@@ -3498,7 +4036,7 @@ bool st_table::fill_item_list(List<Item> *item_list) const
Fields of this table.
SYNPOSIS
- st_table::fill_item_list()
+ TABLE::fill_item_list()
item_list a non-empty list with Item_fields
DESCRIPTION
@@ -3508,7 +4046,7 @@ bool st_table::fill_item_list(List<Item> *item_list) const
is the same as the number of columns in the table.
*/
-void st_table::reset_item_list(List<Item> *item_list) const
+void TABLE::reset_item_list(List<Item> *item_list) const
{
List_iterator_fast<Item> it(*item_list);
for (Field **ptr= field; *ptr; ptr++)
@@ -3562,6 +4100,7 @@ bool TABLE_LIST::create_field_translation(THD *thd)
uint field_count= 0;
Query_arena *arena, backup;
bool res= FALSE;
+ DBUG_ENTER("TABLE_LIST::create_field_translation");
if (thd->stmt_arena->is_conventional() ||
thd->stmt_arena->is_stmt_prepare_or_first_sp_execute())
@@ -3582,7 +4121,7 @@ bool TABLE_LIST::create_field_translation(THD *thd)
if (field_translation)
{
/*
- Update items in the field translation aftet view have been prepared.
+ Update items in the field translation after view have been prepared.
It's needed because some items in the select list, like IN subselects,
might be substituted for optimized ones.
*/
@@ -3595,7 +4134,7 @@ bool TABLE_LIST::create_field_translation(THD *thd)
field_translation_updated= TRUE;
}
- return FALSE;
+ DBUG_RETURN(FALSE);
}
arena= thd->activate_stmt_arena_if_needed(&backup);
@@ -3613,17 +4152,20 @@ bool TABLE_LIST::create_field_translation(THD *thd)
while ((item= it++))
{
- transl[field_count].name= item->name;
+ DBUG_ASSERT(item->name && item->name[0]);
+ transl[field_count].name= thd->strdup(item->name);
transl[field_count++].item= item;
}
field_translation= transl;
field_translation_end= transl + field_count;
+ /* It's safe to cache this table for prepared statements */
+ cacheable_table= 1;
exit:
if (arena)
thd->restore_active_arena(arena, &backup);
- return res;
+ DBUG_RETURN(res);
}
@@ -3904,7 +4446,7 @@ bool TABLE_LIST::prep_check_option(THD *thd, uint8 check_opt_type)
There are currently two mechanisms at work that handle errors for views,
this one and a more general mechanism based on an Internal_error_handler,
see Show_create_error_handler. The latter handles errors encountered during
- execution of SHOW CREATE VIEW, while the machanism using this method is
+ execution of SHOW CREATE VIEW, while the mechanism using this method is
handles SELECT from views. The two methods should not clash.
@param[in,out] thd thread handler
@@ -3919,20 +4461,20 @@ void TABLE_LIST::hide_view_error(THD *thd)
/* Hide "Unknown column" or "Unknown function" error */
DBUG_ASSERT(thd->is_error());
- if (thd->main_da.sql_errno() == ER_BAD_FIELD_ERROR ||
- thd->main_da.sql_errno() == ER_SP_DOES_NOT_EXIST ||
- thd->main_da.sql_errno() == ER_FUNC_INEXISTENT_NAME_COLLISION ||
- thd->main_da.sql_errno() == ER_PROCACCESS_DENIED_ERROR ||
- thd->main_da.sql_errno() == ER_COLUMNACCESS_DENIED_ERROR ||
- thd->main_da.sql_errno() == ER_TABLEACCESS_DENIED_ERROR ||
- thd->main_da.sql_errno() == ER_TABLE_NOT_LOCKED ||
- thd->main_da.sql_errno() == ER_NO_SUCH_TABLE)
+ if (thd->stmt_da->sql_errno() == ER_BAD_FIELD_ERROR ||
+ thd->stmt_da->sql_errno() == ER_SP_DOES_NOT_EXIST ||
+ thd->stmt_da->sql_errno() == ER_FUNC_INEXISTENT_NAME_COLLISION ||
+ thd->stmt_da->sql_errno() == ER_PROCACCESS_DENIED_ERROR ||
+ thd->stmt_da->sql_errno() == ER_COLUMNACCESS_DENIED_ERROR ||
+ thd->stmt_da->sql_errno() == ER_TABLEACCESS_DENIED_ERROR ||
+ thd->stmt_da->sql_errno() == ER_TABLE_NOT_LOCKED ||
+ thd->stmt_da->sql_errno() == ER_NO_SUCH_TABLE)
{
TABLE_LIST *top= top_table();
thd->clear_error();
my_error(ER_VIEW_INVALID, MYF(0), top->view_db.str, top->view_name.str);
}
- else if (thd->main_da.sql_errno() == ER_NO_DEFAULT_FOR_FIELD)
+ else if (thd->stmt_da->sql_errno() == ER_NO_DEFAULT_FOR_FIELD)
{
TABLE_LIST *top= top_table();
thd->clear_error();
@@ -4014,7 +4556,7 @@ int TABLE_LIST::view_check_option(THD *thd, bool ignore_failure)
TABLE_LIST *main_view= top_table();
if (ignore_failure)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_VIEW_CHECK_FAILED, ER(ER_VIEW_CHECK_FAILED),
main_view->view_db.str, main_view->view_name.str);
return(VIEW_CHECK_SKIP);
@@ -4321,10 +4863,15 @@ bool TABLE_LIST::prepare_view_securety_context(THD *thd)
}
else
{
- my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
- thd->security_ctx->priv_user,
- thd->security_ctx->priv_host,
- (thd->password ? ER(ER_YES) : ER(ER_NO)));
+ if (thd->password == 2)
+ my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
+ thd->security_ctx->priv_user,
+ thd->security_ctx->priv_host);
+ else
+ my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
+ thd->security_ctx->priv_user,
+ thd->security_ctx->priv_host,
+ (thd->password ? ER(ER_YES) : ER(ER_NO)));
}
DBUG_RETURN(TRUE);
}
@@ -4574,14 +5121,15 @@ const char *Natural_join_column::db_name()
return table_ref->view_db.str;
/*
- Test that TABLE_LIST::db is the same as st_table_share::db to
+ 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) ||
(table_ref->schema_table &&
- table_ref->table->s->db.str[0] == 0) ||
+ is_infoschema_db(table_ref->table->s->db.str,
+ table_ref->table->s->db.length)) ||
table_ref->is_materialized_derived());
return table_ref->db;
}
@@ -4810,13 +5358,14 @@ const char *Field_iterator_table_ref::get_db_name()
return natural_join_it.column_ref()->db_name();
/*
- Test that TABLE_LIST::db is the same as st_table_share::db to
+ 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) ||
(table_ref->schema_table &&
- table_ref->table->s->db.str[0] == 0));
+ is_infoschema_db(table_ref->table->s->db.str,
+ table_ref->table->s->db.length)));
return table_ref->db;
}
@@ -4986,7 +5535,7 @@ Field_iterator_table_ref::get_natural_column_ref()
/* Reset all columns bitmaps */
-void st_table::clear_column_bitmaps()
+void TABLE::clear_column_bitmaps()
{
/*
Reset column read/write usage. It's identical to:
@@ -5008,9 +5557,9 @@ void st_table::clear_column_bitmaps()
key fields.
*/
-void st_table::prepare_for_position()
+void TABLE::prepare_for_position()
{
- DBUG_ENTER("st_table::prepare_for_position");
+ DBUG_ENTER("TABLE::prepare_for_position");
if ((file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) &&
s->primary_key < MAX_KEY)
@@ -5029,14 +5578,14 @@ void st_table::prepare_for_position()
NOTE:
This changes the bitmap to use the tmp bitmap
After this, you can't access any other columns in the table until
- bitmaps are reset, for example with st_table::clear_column_bitmaps()
- or st_table::restore_column_maps_after_mark_index()
+ bitmaps are reset, for example with TABLE::clear_column_bitmaps()
+ or TABLE::restore_column_maps_after_mark_index()
*/
-void st_table::mark_columns_used_by_index(uint index)
+void TABLE::mark_columns_used_by_index(uint index)
{
MY_BITMAP *bitmap= &tmp_set;
- DBUG_ENTER("st_table::mark_columns_used_by_index");
+ DBUG_ENTER("TABLE::mark_columns_used_by_index");
enable_keyread();
bitmap_clear_all(bitmap);
@@ -5054,10 +5603,10 @@ void st_table::mark_columns_used_by_index(uint index)
restore_column_maps_after_mark_index().
*/
-void st_table::add_read_columns_used_by_index(uint index)
+void TABLE::add_read_columns_used_by_index(uint index)
{
MY_BITMAP *bitmap= &tmp_set;
- DBUG_ENTER("st_table::add_read_columns_used_by_index");
+ DBUG_ENTER("TABLE::add_read_columns_used_by_index");
enable_keyread();
bitmap_copy(bitmap, read_set);
@@ -5078,9 +5627,9 @@ void st_table::add_read_columns_used_by_index(uint index)
when calling mark_columns_used_by_index
*/
-void st_table::restore_column_maps_after_mark_index()
+void TABLE::restore_column_maps_after_mark_index()
{
- DBUG_ENTER("st_table::restore_column_maps_after_mark_index");
+ DBUG_ENTER("TABLE::restore_column_maps_after_mark_index");
disable_keyread();
default_column_bitmaps();
@@ -5093,7 +5642,7 @@ void st_table::restore_column_maps_after_mark_index()
mark columns used by key, but don't reset other fields
*/
-void st_table::mark_columns_used_by_index_no_reset(uint index,
+void TABLE::mark_columns_used_by_index_no_reset(uint index,
MY_BITMAP *bitmap)
{
KEY_PART_INFO *key_part= key_info[index].key_part;
@@ -5119,7 +5668,7 @@ void st_table::mark_columns_used_by_index_no_reset(uint index,
always set and sometimes read.
*/
-void st_table::mark_auto_increment_column()
+void TABLE::mark_auto_increment_column()
{
DBUG_ASSERT(found_next_number_field);
/*
@@ -5152,7 +5701,7 @@ void st_table::mark_auto_increment_column()
retrieve the row again.
*/
-void st_table::mark_columns_needed_for_delete()
+void TABLE::mark_columns_needed_for_delete()
{
if (triggers)
triggers->mark_fields_used(TRG_EVENT_DELETE);
@@ -5202,7 +5751,7 @@ void st_table::mark_columns_needed_for_delete()
retrieve the row again.
*/
-void st_table::mark_columns_needed_for_update()
+void TABLE::mark_columns_needed_for_update()
{
DBUG_ENTER("mark_columns_needed_for_update");
if (triggers)
@@ -5247,7 +5796,7 @@ void st_table::mark_columns_needed_for_update()
as changed.
*/
-void st_table::mark_columns_needed_for_insert()
+void TABLE::mark_columns_needed_for_insert()
{
if (triggers)
{
@@ -5285,7 +5834,7 @@ void st_table::mark_columns_needed_for_insert()
FALSE otherwise
*/
-bool st_table::mark_virtual_col(Field *field)
+bool TABLE::mark_virtual_col(Field *field)
{
bool res;
DBUG_ASSERT(field->vcol_info);
@@ -5327,7 +5876,7 @@ bool st_table::mark_virtual_col(Field *field)
be added to read_set either.
*/
-void st_table::mark_virtual_columns_for_write(bool insert_fl)
+void TABLE::mark_virtual_columns_for_write(bool insert_fl)
{
Field **vfield_ptr, *tmp_vfield;
bool bitmap_updated= FALSE;
@@ -5426,6 +5975,7 @@ void TABLE::create_key_part_by_field(KEY *keyinfo,
keyinfo->key_length+= HA_KEY_NULL_LENGTH;
}
if (field->type() == MYSQL_TYPE_BLOB ||
+ field->type() == MYSQL_TYPE_GEOMETRY ||
field->real_type() == MYSQL_TYPE_VARCHAR)
{
key_part_info->store_length+= HA_KEY_BLOB_LENGTH;
@@ -5529,9 +6079,11 @@ bool TABLE::add_tmp_key(uint key, uint key_parts,
keyinfo= key_info + key;
keyinfo->key_part= key_part_info;
keyinfo->usable_key_parts= keyinfo->key_parts = key_parts;
+ keyinfo->ext_key_parts= keyinfo->key_parts;
keyinfo->key_length=0;
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
keyinfo->flags= HA_GENERATED_KEY;
+ keyinfo->ext_key_flags= keyinfo->flags;
if (unique)
keyinfo->flags|= HA_NOSAME;
sprintf(buf, "key%i", key);
@@ -5587,25 +6139,6 @@ void TABLE::use_index(int key_to_save)
s->keys= i;
}
-/**
- @brief Check if this is part of a MERGE table with attached children.
-
- @return status
- @retval TRUE children are attached
- @retval FALSE no MERGE part or children not attached
-
- @details
- A MERGE table consists of a parent TABLE and zero or more child
- TABLEs. Each of these TABLEs is called a part of a MERGE table.
-*/
-
-bool st_table::is_children_attached(void)
-{
- return((child_l && children_attached) ||
- (parent && parent->children_attached));
-}
-
-
/*
Return TRUE if the table is filled at execution phase
@@ -5613,13 +6146,60 @@ bool st_table::is_children_attached(void)
the table, like range analysis or constant table detection)
*/
-bool st_table::is_filled_at_execution()
+bool TABLE::is_filled_at_execution()
{
- return test(pos_in_table_list->jtbm_subselect ||
+ /*
+ pos_in_table_list == NULL for internal temporary tables because they
+ do not have a corresponding table reference. Such tables are filled
+ during execution.
+ */
+ return test(!pos_in_table_list ||
+ pos_in_table_list->jtbm_subselect ||
pos_in_table_list->is_active_sjm());
}
+/**
+ @brief
+ Get actual number of key components
+
+ @param keyinfo
+
+ @details
+ The function calculates actual number of key components, possibly including
+ components of extended keys, taken into consideration by the optimizer for the
+ key described by the parameter keyinfo.
+
+ @return number of considered key components
+*/
+
+uint TABLE::actual_n_key_parts(KEY *keyinfo)
+{
+ return optimizer_flag(in_use, OPTIMIZER_SWITCH_EXTENDED_KEYS) ?
+ keyinfo->ext_key_parts : keyinfo->key_parts;
+}
+
+
+/**
+ @brief
+ Get actual key flags for a table key
+
+ @param keyinfo
+
+ @details
+ The function finds out actual key flags taken into consideration by the
+ optimizer for the key described by the parameter keyinfo.
+
+ @return actual key flags
+*/
+
+ulong TABLE::actual_key_flags(KEY *keyinfo)
+{
+ return optimizer_flag(in_use, OPTIMIZER_SWITCH_EXTENDED_KEYS) ?
+ keyinfo->ext_key_flags : keyinfo->flags;
+}
+
+
/*
Cleanup this table for re-execution.
@@ -5648,6 +6228,8 @@ void TABLE_LIST::reinit_before_use(THD *thd)
}
while (parent_embedding &&
parent_embedding->nested_join->join_list.head() == embedded);
+
+ mdl_request.ticket= NULL;
}
@@ -5679,10 +6261,10 @@ Item_subselect *TABLE_LIST::containing_subselect()
DESCRIPTION
The parser collects the index hints for each table in a "tagged list"
(TABLE_LIST::index_hints). Using the information in this tagged list
- this function sets the members st_table::keys_in_use_for_query,
- st_table::keys_in_use_for_group_by, st_table::keys_in_use_for_order_by,
- st_table::force_index, st_table::force_index_order,
- st_table::force_index_group and st_table::covering_keys.
+ this function sets the members TABLE::keys_in_use_for_query,
+ TABLE::keys_in_use_for_group_by, TABLE::keys_in_use_for_order_by,
+ TABLE::force_index, TABLE::force_index_order,
+ TABLE::force_index_group and TABLE::covering_keys.
Current implementation of the runtime does not allow mixing FORCE INDEX
and USE INDEX, so this is checked here. Then the FORCE INDEX list
@@ -5871,6 +6453,77 @@ size_t max_row_length(TABLE *table, const uchar *data)
return length;
}
+
+/**
+ Helper function which allows to allocate metadata lock request
+ objects for all elements of table list.
+*/
+
+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->lock_type >= TL_WRITE_ALLOW_WRITE ?
+ MDL_SHARED_WRITE : MDL_SHARED_READ,
+ MDL_TRANSACTION);
+}
+
+
+/**
+ Update TABLE::const_key_parts for single table UPDATE/DELETE query
+
+ @param conds WHERE clause expression
+
+ @retval TRUE error (OOM)
+ @retval FALSE success
+
+ @note
+ Set const_key_parts bits if key fields are equal to constants in
+ the WHERE expression.
+*/
+
+bool TABLE::update_const_key_parts(COND *conds)
+{
+ bzero((char*) const_key_parts, sizeof(key_part_map) * s->keys);
+
+ if (conds == NULL)
+ return FALSE;
+
+ for (uint index= 0; index < s->keys; index++)
+ {
+ KEY_PART_INFO *keyinfo= key_info[index].key_part;
+ KEY_PART_INFO *keyinfo_end= keyinfo + key_info[index].key_parts;
+
+ for (key_part_map part_map= (key_part_map)1;
+ keyinfo < keyinfo_end;
+ keyinfo++, part_map<<= 1)
+ {
+ if (const_expression_in_where(conds, NULL, keyinfo->field))
+ const_key_parts[index]|= part_map;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ Test if the order list consists of simple field expressions
+
+ @param order Linked list of ORDER BY arguments
+
+ @return TRUE if @a order is empty or consist of simple field expressions
+*/
+
+bool is_simple_order(ORDER *order)
+{
+ for (ORDER *ord= order; ord; ord= ord->next)
+ {
+ if (ord->item[0]->real_item()->type() != Item::FIELD_ITEM)
+ return FALSE;
+ }
+ return TRUE;
+}
+
/*
@brief Compute values for virtual columns used in query
@@ -5962,7 +6615,7 @@ void TABLE_LIST::reset_const_table()
@return FALSE ok
*/
-bool TABLE_LIST::handle_derived(struct st_lex *lex, uint phases)
+bool TABLE_LIST::handle_derived(LEX *lex, uint phases)
{
SELECT_LEX_UNIT *unit= get_unit();
if (unit)
@@ -6223,6 +6876,35 @@ bool TABLE_LIST::change_refs_to_fields()
}
+void TABLE_LIST::set_lock_type(THD *thd, enum thr_lock_type lock)
+{
+ if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar *)&lock))
+ return;
+ /* we call it only when table is opened and it is "leaf" table*/
+ DBUG_ASSERT(table);
+ lock_type= lock;
+ /* table->file->get_table() can be 0 for derived tables */
+ if (table->file && table->file->get_table())
+ table->file->set_lock_type(lock);
+ if (is_merged_derived())
+ {
+ for (TABLE_LIST *table= get_single_select()->get_table_list();
+ table;
+ table= table->next_local)
+ {
+ table->set_lock_type(thd, lock);
+ }
+ }
+}
+
+uint TABLE_SHARE::actual_n_key_parts(THD *thd)
+{
+ return use_ext_keys &&
+ optimizer_flag(thd, OPTIMIZER_SWITCH_EXTENDED_KEYS) ?
+ ext_key_parts : key_parts;
+}
+
+
/*****************************************************************************
** Instansiate templates
*****************************************************************************/
diff --git a/sql/table.h b/sql/table.h
index 598e376409e..ac15e389f75 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -1,4 +1,7 @@
-/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+#ifndef TABLE_INCLUDED
+#define TABLE_INCLUDED
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,10 +14,21 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#ifndef SQL_TABLE_INCLUDED
-#define SQL_TABLE_INCLUDED
+#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 */
+
+#ifndef MYSQL_CLIENT
+
+#include "hash.h" /* HASH */
+#include "handler.h" /* row_type, ha_choice, handler */
+#include "mysql_com.h" /* enum_field_types */
+#include "thr_lock.h" /* thr_lock_type */
/* Structs that defines the TABLE */
@@ -27,6 +41,130 @@ class st_select_lex;
class partition_info;
class COND_EQUAL;
class Security_context;
+struct TABLE_LIST;
+class ACL_internal_schema_access;
+class ACL_internal_table_access;
+class Field;
+
+/*
+ Used to identify NESTED_JOIN structures within a join (applicable only to
+ structures that have not been simplified away and embed more the one
+ element)
+*/
+typedef ulonglong nested_join_map;
+
+
+#define tmp_file_prefix "#sql" /**< Prefix for tmp tables */
+#define tmp_file_prefix_length 4
+#define TMP_TABLE_KEY_EXTRA 8
+
+/**
+ Enumerate possible types of a table from re-execution
+ standpoint.
+ TABLE_LIST class has a member of this type.
+ At prepared statement prepare, this member is assigned a value
+ as of the current state of the database. Before (re-)execution
+ of a prepared statement, we check that the value recorded at
+ prepare matches the type of the object we obtained from the
+ table definition cache.
+
+ @sa check_and_update_table_version()
+ @sa Execute_observer
+ @sa Prepared_statement::reprepare()
+*/
+
+enum enum_table_ref_type
+{
+ /** Initial value set by the parser */
+ TABLE_REF_NULL= 0,
+ TABLE_REF_VIEW,
+ TABLE_REF_BASE_TABLE,
+ TABLE_REF_I_S_TABLE,
+ TABLE_REF_TMP_TABLE
+};
+
+
+/*************************************************************************/
+
+/**
+ Object_creation_ctx -- interface for creation context of database objects
+ (views, stored routines, events, triggers). Creation context -- is a set
+ of attributes, that should be fixed at the creation time and then be used
+ each time the object is parsed or executed.
+*/
+
+class Object_creation_ctx
+{
+public:
+ Object_creation_ctx *set_n_backup(THD *thd);
+
+ void restore_env(THD *thd, Object_creation_ctx *backup_ctx);
+
+protected:
+ Object_creation_ctx() {}
+ virtual Object_creation_ctx *create_backup_ctx(THD *thd) const = 0;
+
+ virtual void change_env(THD *thd) const = 0;
+
+public:
+ virtual ~Object_creation_ctx()
+ { }
+};
+
+/*************************************************************************/
+
+/**
+ Default_object_creation_ctx -- default implementation of
+ Object_creation_ctx.
+*/
+
+class Default_object_creation_ctx : public Object_creation_ctx
+{
+public:
+ CHARSET_INFO *get_client_cs()
+ {
+ return m_client_cs;
+ }
+
+ CHARSET_INFO *get_connection_cl()
+ {
+ return m_connection_cl;
+ }
+
+protected:
+ Default_object_creation_ctx(THD *thd);
+
+ Default_object_creation_ctx(CHARSET_INFO *client_cs,
+ CHARSET_INFO *connection_cl);
+
+protected:
+ virtual Object_creation_ctx *create_backup_ctx(THD *thd) const;
+
+ virtual void change_env(THD *thd) const;
+
+protected:
+ /**
+ client_cs stores the value of character_set_client session variable.
+ The only character set attribute is used.
+
+ Client character set is included into query context, because we save
+ query in the original character set, which is client character set. So,
+ in order to parse the query properly we have to switch client character
+ set on parsing.
+ */
+ CHARSET_INFO *m_client_cs;
+
+ /**
+ connection_cl stores the value of collation_connection session
+ variable. Both character set and collation attributes are used.
+
+ Connection collation is included into query context, becase it defines
+ the character set and collation of text literals in internal
+ representation of query (item-objects).
+ */
+ CHARSET_INFO *m_connection_cl;
+};
+
class Query_arena;
/*************************************************************************/
@@ -71,6 +209,25 @@ typedef struct st_order {
} ORDER;
/**
+ State information for internal tables grants.
+ This structure is part of the TABLE_LIST, and is updated
+ during the ACL check process.
+ @sa GRANT_INFO
+*/
+struct st_grant_internal_info
+{
+ /** True if the internal lookup by schema name was done. */
+ bool m_schema_lookup_done;
+ /** Cached internal schema access. */
+ const ACL_internal_schema_access *m_schema_access;
+ /** True if the internal lookup by table name was done. */
+ bool m_table_lookup_done;
+ /** Cached internal table access. */
+ const ACL_internal_table_access *m_table_access;
+};
+typedef struct st_grant_internal_info GRANT_INTERNAL_INFO;
+
+/**
@brief The current state of the privilege checking process for the current
user, SQL statement and SQL object.
@@ -131,6 +288,8 @@ typedef struct st_grant_info
check access rights to the underlying tables of a view.
*/
ulong orig_want_privilege;
+ /** The grant state for internal tables. */
+ GRANT_INTERNAL_INFO m_internal;
} GRANT_INFO;
enum tmp_table_type
@@ -138,23 +297,6 @@ enum tmp_table_type
NO_TMP_TABLE, NON_TRANSACTIONAL_TMP_TABLE, TRANSACTIONAL_TMP_TABLE,
INTERNAL_TMP_TABLE, SYSTEM_TMP_TABLE
};
-
-/** Event on which trigger is invoked. */
-enum trg_event_type
-{
- TRG_EVENT_INSERT= 0,
- TRG_EVENT_UPDATE= 1,
- TRG_EVENT_DELETE= 2,
- TRG_EVENT_MAX
-};
-
-enum frm_type_enum
-{
- FRMTYPE_ERROR= 0,
- FRMTYPE_TABLE,
- FRMTYPE_VIEW
-};
-
enum release_type { RELEASE_NORMAL, RELEASE_WAIT_FOR_DROP };
enum enum_vcol_update_mode
@@ -255,7 +397,7 @@ enum enum_table_category
- LOCK TABLE t FOR READ/WRITE
- FLUSH TABLES WITH READ LOCK
- SET GLOBAL READ_ONLY = ON
- as there is no point in locking explicitely
+ as there is no point in locking explicitly
an INFORMATION_SCHEMA table.
Nothing is directly written to information schema tables.
Note that this value is not used currently,
@@ -270,16 +412,16 @@ enum enum_table_category
TABLE_CATEGORY_INFORMATION=4,
/**
- Performance schema tables.
+ Log tables.
These tables are an interface provided by the system
- to inspect the system performance data.
+ to inspect the system logs.
These tables do *not* honor:
- LOCK TABLE t FOR READ/WRITE
- FLUSH TABLES WITH READ LOCK
- SET GLOBAL READ_ONLY = ON
- as there is no point in locking explicitely
- a PERFORMANCE_SCHEMA table.
- An example of PERFORMANCE_SCHEMA tables are:
+ as there is no point in locking explicitly
+ a LOG table.
+ An example of LOG tables are:
- mysql.slow_log
- mysql.general_log,
which *are* updated even when there is either
@@ -287,9 +429,31 @@ enum enum_table_category
User queries do not write directly to these tables
(there are exceptions for log tables).
The server implementation perform writes.
+ Log tables are cached in the table cache.
+ */
+ TABLE_CATEGORY_LOG=5,
+
+ /**
+ Performance schema tables.
+ These tables are an interface provided by the system
+ to inspect the system performance data.
+ These tables do *not* honor:
+ - LOCK TABLE t FOR READ/WRITE
+ - FLUSH TABLES WITH READ LOCK
+ - SET GLOBAL READ_ONLY = ON
+ as there is no point in locking explicitly
+ a PERFORMANCE_SCHEMA table.
+ An example of PERFORMANCE_SCHEMA tables are:
+ - performance_schema.*
+ which *are* updated (but not using the handler interface)
+ even when there is either
+ a GLOBAL READ LOCK or a GLOBAL READ_ONLY in effect.
+ User queries do not write directly to these tables
+ (there are exceptions for SETUP_* tables).
+ The server implementation perform writes.
Performance tables are cached in the table cache.
*/
- TABLE_CATEGORY_PERFORMANCE=5
+ TABLE_CATEGORY_PERFORMANCE=6
};
typedef enum enum_table_category TABLE_CATEGORY;
@@ -297,6 +461,10 @@ TABLE_CATEGORY get_table_category(const LEX_STRING *db,
const LEX_STRING *name);
+struct TABLE_share;
+
+extern ulong refresh_version;
+
typedef struct st_table_field_type
{
LEX_STRING name;
@@ -312,6 +480,19 @@ typedef struct st_table_field_def
} TABLE_FIELD_DEF;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+/**
+ Partition specific ha_data struct.
+*/
+typedef struct st_ha_data_partition
+{
+ bool auto_inc_initialized;
+ mysql_mutex_t LOCK_auto_inc; /**< protecting auto_inc val */
+ ulonglong next_auto_inc_val; /**< first non reserved value */
+} HA_DATA_PARTITION;
+#endif
+
+
class Table_check_intact
{
protected:
@@ -326,14 +507,53 @@ public:
};
-/*
+/**
+ Class representing the fact that some thread waits for table
+ share to be flushed. Is used to represent information about
+ such waits in MDL deadlock detector.
+*/
+
+class Wait_for_flush : public MDL_wait_for_subgraph
+{
+ MDL_context *m_ctx;
+ TABLE_SHARE *m_share;
+ uint m_deadlock_weight;
+public:
+ Wait_for_flush(MDL_context *ctx_arg, TABLE_SHARE *share_arg,
+ uint deadlock_weight_arg)
+ : m_ctx(ctx_arg), m_share(share_arg),
+ m_deadlock_weight(deadlock_weight_arg)
+ {}
+
+ MDL_context *get_ctx() const { return m_ctx; }
+
+ virtual bool accept_visitor(MDL_wait_for_graph_visitor *dvisitor);
+
+ virtual uint get_deadlock_weight() const;
+
+ /**
+ Pointers for participating in the list of waiters for table share.
+ */
+ Wait_for_flush *next_in_share;
+ Wait_for_flush **prev_in_share;
+};
+
+
+typedef I_P_List <Wait_for_flush,
+ I_P_List_adapter<Wait_for_flush,
+ &Wait_for_flush::next_in_share,
+ &Wait_for_flush::prev_in_share> >
+ Wait_for_flush_list;
+
+
+/**
This structure is shared between different table objects. There is one
instance of table share per one table in the database.
*/
-typedef struct st_table_share
+struct TABLE_SHARE
{
- st_table_share() {} /* Remove gcc warning */
+ TABLE_SHARE() {} /* Remove gcc warning */
/** Category of this table. */
TABLE_CATEGORY table_category;
@@ -344,13 +564,16 @@ typedef struct st_table_share
TYPELIB keynames; /* Pointers to keynames */
TYPELIB fieldnames; /* Pointer to fieldnames */
TYPELIB *intervals; /* pointer to interval info */
- pthread_mutex_t mutex; /* For locking the share */
- pthread_cond_t cond; /* To signal that share is ready */
- struct st_table_share *next, /* Link to unused shares */
- **prev;
-#ifdef NOT_YET
- struct st_table *open_tables; /* link to open tables */
-#endif
+ mysql_mutex_t LOCK_ha_data; /* To protect access to ha_data */
+ TABLE_SHARE *next, **prev; /* Link to unused shares */
+
+ /*
+ Doubly-linked (back-linked) lists of used and unused TABLE objects
+ for this share.
+ */
+ I_P_List <TABLE, TABLE_share> used_tables;
+ I_P_List <TABLE, TABLE_share> free_tables;
+
engine_option_value *option_list; /* text options for table */
ha_table_option_struct *option_struct; /* structure with parsed options */
@@ -391,9 +614,7 @@ typedef struct st_table_share
key_map keys_for_keyread;
ha_rows min_rows, max_rows; /* create information */
ulong avg_row_length; /* create information */
- ulong raid_chunksize;
ulong version, mysql_version;
- ulong timestamp_offset; /* Set to offset+1 of record */
ulong reclength; /* Recordlength */
/* Stored record length. No generated-only virtual fields are included */
ulong stored_rec_length;
@@ -406,13 +627,13 @@ typedef struct st_table_share
}
enum row_type row_type; /* How rows are stored */
enum tmp_table_type tmp_table;
+
/** Transactional or not. */
enum ha_choice transactional;
/** Per-page checksums or not. */
enum ha_choice page_checksum;
uint ref_count; /* How many TABLE objects uses this */
- uint open_count; /* Number of tables in open list */
uint blob_ptr_size; /* 4 or 8 */
uint key_block_size; /* create key_block_size, if used */
uint null_bytes, last_null_bit_pos;
@@ -426,6 +647,7 @@ typedef struct st_table_share
uint stored_fields;
uint rec_buff_length; /* Size of table->record[] buffer */
uint keys, key_parts;
+ uint ext_key_parts; /* Total number of key parts in extended keys */
uint max_key_length, max_unique_length, total_key_length;
uint uniques; /* Number of UNIQUE index */
uint null_fields; /* number of null fields */
@@ -435,10 +657,9 @@ typedef struct st_table_share
uint db_create_options; /* Create options from database */
uint db_options_in_use; /* Options in use */
uint db_record_offset; /* if HA_REC_IN_SEQ */
- uint raid_type, raid_chunks;
uint rowid_field_offset; /* Field_nr +1 to rowid field */
- /* Index of auto-updated TIMESTAMP field in field array */
- uint primary_key;
+ /* Primary key index number, used in TABLE::key_info[] */
+ uint primary_key;
uint next_number_index; /* autoincrement key number */
uint next_number_key_offset; /* autoinc keypart offset in a key */
uint next_number_keypart; /* autoinc keypart number in a key */
@@ -446,13 +667,12 @@ typedef struct st_table_share
uint column_bitmap_size;
uchar frm_version;
uint vfields; /* Number of computed (virtual) fields */
+ 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 crashed;
bool is_view;
- bool name_lock, replace_with_name_lock;
- bool waiting_on_cond; /* Protection against free */
bool deleting; /* going to delete this table */
bool can_cmp_whole_record;
ulong table_map_id; /* for row-based replication */
@@ -466,13 +686,11 @@ typedef struct st_table_share
int cached_row_logging_check;
#ifdef WITH_PARTITION_STORAGE_ENGINE
- /** @todo: Move into *ha_data for partitioning */
+ /* filled in when reading from frm */
bool auto_partitioned;
- char *partition_info;
- uint partition_info_len;
+ char *partition_info_str;
+ uint partition_info_str_len;
uint partition_info_buffer_size;
- const char *part_state;
- uint part_state_len;
handlerton *default_part_db_type;
#endif
@@ -490,8 +708,22 @@ typedef struct st_table_share
/** place to store storage engine specific data */
void *ha_data;
- void (*ha_data_destroy)(void *); /* An optional destructor for ha_data. */
+ void (*ha_data_destroy)(void *); /* An optional destructor for ha_data */
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ /** place to store partition specific data, LOCK_ha_data hold while init. */
+ HA_DATA_PARTITION *ha_part_data;
+ /* Destructor for ha_part_data */
+ void (*ha_part_data_destroy)(HA_DATA_PARTITION *);
+#endif
+ /** Instrumentation for this table share. */
+ PSI_table_share *m_psi;
+
+ /**
+ List of tickets representing threads waiting for the share to be flushed.
+ */
+ Wait_for_flush_list m_flush_tickets;
/*
Set share's table cache key and update its db and table name appropriately.
@@ -553,7 +785,7 @@ typedef struct st_table_share
inline bool require_write_privileges()
{
- return (table_category == TABLE_CATEGORY_PERFORMANCE);
+ return (table_category == TABLE_CATEGORY_LOG);
}
inline ulong get_table_def_version()
@@ -561,6 +793,42 @@ typedef struct st_table_share
return table_map_id;
}
+ /** Is this table share being expelled from the table definition cache? */
+ inline bool has_old_version() const
+ {
+ return version != refresh_version;
+ }
+ inline bool protected_against_usage() const
+ {
+ return version == 0;
+ }
+ inline void protect_against_usage()
+ {
+ version= 0;
+ }
+ /*
+ This is used only for the case of locked tables, as we want to
+ allow one to do SHOW commands on them even after ALTER or REPAIR
+ */
+ inline void allow_access_to_protected_table()
+ {
+ DBUG_ASSERT(version == 0);
+ version= 1;
+ }
+ /*
+ Remove from table definition cache at close.
+ Table can still be opened by SHOW
+ */
+ inline void remove_from_cache_at_close()
+ {
+ if (version != 0) /* Don't remove protection */
+ version= 1;
+ }
+ inline void set_refresh_version()
+ {
+ version= refresh_version;
+ }
+
/**
Convert unrelated members of TABLE_SHARE to one enum
representing its type.
@@ -660,10 +928,22 @@ typedef struct st_table_share
return (tmp_table == SYSTEM_TMP_TABLE || is_view) ? 0 : table_map_id;
}
-} TABLE_SHARE;
+ bool visit_subgraph(Wait_for_flush *waiting_ticket,
+ MDL_wait_for_graph_visitor *gvisitor);
+ bool wait_for_old_version(THD *thd, struct timespec *abstime,
+ uint deadlock_weight);
+ /** Release resources and free memory occupied by the table share. */
+ void destroy();
+
+ void set_use_ext_keys_flag(bool fl)
+ {
+ use_ext_keys= fl;
+ }
+
+ uint actual_n_key_parts(THD *thd);
+};
-extern ulong refresh_version;
/* Information for one open table */
enum index_hint_type
@@ -673,24 +953,32 @@ enum index_hint_type
INDEX_HINT_FORCE
};
+
#define CHECK_ROW_FOR_NULLS_TO_REJECT (1 << 0)
#define REJECT_ROW_DUE_TO_NULL_FIELDS (1 << 1)
-struct st_table {
- st_table() {} /* Remove gcc warning */
+/* Bitmap of table's fields */
+typedef Bitmap<MAX_FIELDS> Field_map;
+
+struct TABLE
+{
+ TABLE() {} /* Remove gcc warning */
TABLE_SHARE *s;
handler *file;
-#ifdef NOT_YET
- struct st_table *used_next, **used_prev; /* Link to used tables */
- struct st_table *open_next, **open_prev; /* Link to open tables */
-#endif
- struct st_table *next, *prev;
+ TABLE *next, *prev;
+
+private:
+ /**
+ Links for the lists of used/unused TABLE objects for this share.
+ Declared as private to avoid direct manipulation with those objects.
+ One should use methods of I_P_List template instead.
+ */
+ TABLE *share_next, **share_prev;
- /* For the below MERGE related members see top comment in ha_myisammrg.cc */
- struct st_table *parent; /* Set in MERGE child. Ptr to parent */
- TABLE_LIST *child_l; /* Set in MERGE parent. List of children */
- TABLE_LIST **child_last_l; /* Set in MERGE parent. End of list */
+ friend struct TABLE_share;
+
+public:
THD *in_use; /* Which thread uses this */
Field **field; /* Pointer to fields */
@@ -731,6 +1019,8 @@ struct st_table {
/* Table's triggers, 0 if there are no of them */
Table_triggers_list *triggers;
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;
ORDER *group;
String alias; /* alias or table name */
uchar *null_flags;
@@ -804,15 +1094,20 @@ struct st_table {
uint db_stat; /* mode of file as in handler.h */
/* number of select if it is derived table */
uint derived_select_number;
- int current_lock; /* Type of lock on table */
- bool copy_blobs; /* copy_blobs when storing */
-
/*
0 or JOIN_TYPE_{LEFT|RIGHT}. Currently this is only compared to 0.
If maybe_null !=0, this table is inner w.r.t. some outer join operation,
and null_row may be true.
*/
uint maybe_null;
+ int current_lock; /* Type of lock on table */
+ bool copy_blobs; /* copy_blobs when storing */
+ /*
+ Set if next_number_field is in the UPDATE fields of INSERT ... ON DUPLICATE
+ KEY UPDATE.
+ */
+ bool next_number_field_updated;
+
/*
If true, the current table row is considered to have all columns set to
NULL, including columns declared as "not null" (see maybe_null).
@@ -858,6 +1153,10 @@ struct st_table {
*/
bool force_index_group;
bool distinct,const_table,no_rows, used_for_duplicate_elimination;
+ /**
+ Forces DYNAMIC Aria row format for internal temporary tables.
+ */
+ bool keep_row_order;
/**
If set, the optimizer has found that row retrieval should access index
@@ -865,24 +1164,6 @@ struct st_table {
*/
bool key_read;
bool no_keyread;
- /*
- Placeholder for an open table which prevents other connections
- from taking name-locks on this table. Typically used with
- TABLE_SHARE::version member to take an exclusive name-lock on
- this table name -- a name lock that not only prevents other
- threads from opening the table, but also blocks other name
- locks. This is achieved by:
- - setting open_placeholder to 1 - this will block other name
- locks, as wait_for_locked_table_name will be forced to wait,
- see table_is_used for details.
- - setting version to 0 - this will force other threads to close
- the instance of this table and wait (this is the same approach
- as used for usual name locks).
- An exclusively name-locked table currently can have no handler
- object associated with it (db_stat is always 0), but please do
- not rely on that.
- */
- bool open_placeholder;
bool locked_by_logger;
bool no_replicate;
bool locked_by_name;
@@ -899,11 +1180,9 @@ struct st_table {
bool insert_or_update; /* Can be used by the handler */
bool alias_name_used; /* true if table_name is alias */
bool get_fields_in_item_tree; /* Signal to fix_field */
+ bool m_needs_reopen;
bool created; /* For tmp tables. TRUE <=> tmp table was actually created.*/
- /* If MERGE children attached to parent. See top comment in ha_myisammrg.cc */
- bool children_attached;
-
REGINFO reginfo; /* field connections */
MEM_ROOT mem_root;
GRANT_INFO grant;
@@ -920,8 +1199,10 @@ struct st_table {
partition_info *part_info; /* Partition related information */
bool no_partitions_used; /* If true, all partitions have been pruned away */
#endif
-
uint max_keys; /* Size of allocated key_info array. */
+ MDL_ticket *mdl_ticket;
+
+ 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 clear_column_bitmaps(void);
@@ -978,13 +1259,10 @@ struct st_table {
write_set= &def_write_set;
vcol_set= &def_vcol_set;
}
- /* Is table open or should be treated as such by name-locking? */
- inline bool is_name_opened() { return db_stat || open_placeholder; }
- /*
- Is this instance of the table should be reopen or represents a name-lock?
- */
- inline bool needs_reopen_or_name_lock()
- { return s->version != refresh_version; }
+ /** Should this instance of the table be reopened? */
+ inline bool needs_reopen()
+ { return !db_stat || m_needs_reopen; }
+
bool alloc_keys(uint key_count);
bool check_tmp_key(uint key, uint key_parts,
uint (*next_field_no) (uchar *), uchar *arg);
@@ -999,7 +1277,6 @@ struct st_table {
map= map_arg;
tablenr= tablenr_arg;
}
- bool is_children_attached(void);
inline void enable_keyread()
{
DBUG_ENTER("enable_keyread");
@@ -1024,8 +1301,31 @@ struct st_table {
}
DBUG_VOID_RETURN;
}
+
+ bool update_const_key_parts(COND *conds);
+ uint actual_n_key_parts(KEY *keyinfo);
+ ulong actual_key_flags(KEY *keyinfo);
};
+
+/**
+ Helper class which specifies which members of TABLE are used for
+ participation in the list of used/unused TABLE objects for the share.
+*/
+
+struct TABLE_share
+{
+ static inline TABLE **next_ptr(TABLE *l)
+ {
+ return &l->share_next;
+ }
+ static inline TABLE ***prev_ptr(TABLE *l)
+ {
+ return &l->share_prev;
+ }
+};
+
+
enum enum_schema_table_state
{
NOT_PROCESSED= 0,
@@ -1035,7 +1335,9 @@ enum enum_schema_table_state
typedef struct st_foreign_key_info
{
- LEX_STRING *forein_id;
+ LEX_STRING *foreign_id;
+ LEX_STRING *foreign_db;
+ LEX_STRING *foreign_table;
LEX_STRING *referenced_db;
LEX_STRING *referenced_table;
LEX_STRING *update_method;
@@ -1045,52 +1347,6 @@ typedef struct st_foreign_key_info
List<LEX_STRING> referenced_fields;
} FOREIGN_KEY_INFO;
-/*
- Make sure that the order of schema_tables and enum_schema_tables are the same.
-*/
-
-enum enum_schema_tables
-{
- SCH_CHARSETS= 0,
- SCH_CLIENT_STATS,
- SCH_COLLATIONS,
- SCH_COLLATION_CHARACTER_SET_APPLICABILITY,
- SCH_COLUMNS,
- SCH_COLUMN_PRIVILEGES,
- SCH_ENGINES,
- SCH_EVENTS,
- SCH_FILES,
- SCH_GLOBAL_STATUS,
- SCH_GLOBAL_VARIABLES,
- SCH_INDEX_STATS,
- SCH_KEY_CACHES,
- SCH_KEY_COLUMN_USAGE,
- SCH_OPEN_TABLES,
- SCH_PARTITIONS,
- SCH_PLUGINS,
- SCH_PROCESSLIST,
- SCH_PROFILES,
- SCH_REFERENTIAL_CONSTRAINTS,
- SCH_PROCEDURES,
- SCH_SCHEMATA,
- SCH_SCHEMA_PRIVILEGES,
- SCH_SESSION_STATUS,
- SCH_SESSION_VARIABLES,
- SCH_STATISTICS,
- SCH_STATUS,
- SCH_TABLES,
- SCH_TABLE_CONSTRAINTS,
- SCH_TABLE_NAMES,
- SCH_TABLE_PRIVILEGES,
- SCH_TABLE_STATS,
- SCH_TRIGGERS,
- SCH_USER_PRIVILEGES,
- SCH_USER_STATS,
- SCH_VARIABLES,
- SCH_VIEWS
-};
-
-
#define MY_I_S_MAYBE_NULL 1
#define MY_I_S_UNSIGNED 2
@@ -1223,7 +1479,6 @@ typedef struct st_schema_table
/** The threshold size a blob field buffer before it is freed */
#define MAX_TDC_BLOB_SIZE 65536
-struct st_lex;
class select_union;
class TMP_TABLE_PARAM;
@@ -1269,6 +1524,16 @@ public:
};
+/**
+ Type of table which can be open for an element of table list.
+*/
+
+enum enum_open_type
+{
+ OT_TEMPORARY_OR_BASE= 0, OT_TEMPORARY_ONLY, OT_BASE_ONLY
+};
+
+
class SJ_MATERIALIZATION_INFO;
class Index_hint;
class Item_in_subselect;
@@ -1309,8 +1574,8 @@ class Item_in_subselect;
4) jtbm semi-join (jtbm_subselect != NULL)
*/
+struct LEX;
class Index_hint;
-struct st_lex;
struct TABLE_LIST
{
TABLE_LIST() {} /* Remove gcc warning */
@@ -1320,13 +1585,23 @@ struct TABLE_LIST
simple_open_and_lock_tables
*/
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,
enum thr_lock_type lock_type_arg)
{
bzero((char*) this, sizeof(*this));
db= (char*) db_name_arg;
- table_name= alias= (char*) table_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);
lock_type= lock_type_arg;
+ mdl_request.init(MDL_key::TABLE, db, table_name,
+ (lock_type >= TL_WRITE_ALLOW_WRITE) ?
+ MDL_SHARED_WRITE : MDL_SHARED_READ,
+ MDL_TRANSACTION);
}
/*
@@ -1476,7 +1751,7 @@ struct TABLE_LIST
TMP_TABLE_PARAM *schema_table_param;
/* link to select_lex where this table was used */
st_select_lex *select_lex;
- st_lex *view; /* link on VIEW lex for merging */
+ LEX *view; /* link on VIEW lex for merging */
Field_translator *field_translation; /* array of VIEW fields */
/* pointer to element after last one in translation table above */
Field_translator *field_translation_end;
@@ -1578,10 +1853,16 @@ struct TABLE_LIST
struct st_nested_join *nested_join; /* if the element is a nested join */
TABLE_LIST *embedding; /* nested join containing the table */
List<TABLE_LIST> *join_list;/* join list the table belongs to */
+ bool lifted; /* set to true when the table is moved to
+ the upper level at the parsing stage */
bool cacheable_table; /* stop PS caching */
/* used in multi-upd/views privilege check */
bool table_in_first_from_clause;
- bool skip_temporary; /* this table shouldn't be temporary */
+ /**
+ Specifies which kind of table should be open for this element
+ of table list.
+ */
+ enum enum_open_type open_type;
/* TRUE if this merged view contain auto_increment field */
bool contain_auto_increment;
bool compact_view_format; /* Use compact format for SHOW CREATE VIEW */
@@ -1598,13 +1879,30 @@ struct TABLE_LIST
used for implicit LOCK TABLES only and won't be used in real statement.
*/
bool prelocking_placeholder;
- /*
- This TABLE_LIST object corresponds to the table to be created
- so it is possible that it does not exist (used in CREATE TABLE
- ... SELECT implementation).
+ /**
+ Indicates that if TABLE_LIST object corresponds to the table/view
+ which requires special handling.
*/
- bool create;
+ enum
+ {
+ /* Normal open. */
+ OPEN_NORMAL= 0,
+ /* Associate a table share only if the the table exists. */
+ OPEN_IF_EXISTS,
+ /* 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. */
bool internal_tmp_table;
+ /** TRUE if an alias for this table was specified in the SQL. */
+ bool is_alias;
+ /** TRUE if the table is referred to in the statement using a fully
+ qualified name (<db_name>.<table_name>).
+ */
+ bool is_fqtn;
+
bool deleting; /* going to delete this table */
/* TRUE <=> derived table should be filled right after optimization. */
@@ -1656,6 +1954,8 @@ struct TABLE_LIST
the parsed tree is created.
*/
uint8 trg_event_map;
+ /* TRUE <=> this table is a const one and was optimized away. */
+ bool optimized_away;
uint i_s_requested_object;
bool has_db_lookup_value;
@@ -1663,6 +1963,8 @@ struct TABLE_LIST
uint table_open_method;
enum enum_schema_table_state schema_table_state;
+ MDL_request mdl_request;
+
void calc_md5(char *buffer);
int view_check_option(THD *thd, bool ignore_failure);
bool create_field_translation(THD *thd);
@@ -1670,8 +1972,7 @@ struct TABLE_LIST
void cleanup_items();
bool placeholder()
{
- return (derived || view || schema_table || (create && !table->db_stat) ||
- !table);
+ return derived || view || schema_table || !table;
}
void print(THD *thd, table_map eliminated_tables, String *str,
enum_query_type query_type);
@@ -1715,26 +2016,12 @@ struct TABLE_LIST
Item_subselect *containing_subselect();
/*
- Compiles the tagged hints list and fills up st_table::keys_in_use_for_query,
- st_table::keys_in_use_for_group_by, st_table::keys_in_use_for_order_by,
- st_table::force_index and st_table::covering_keys.
+ Compiles the tagged hints list and fills up TABLE::keys_in_use_for_query,
+ TABLE::keys_in_use_for_group_by, TABLE::keys_in_use_for_order_by,
+ TABLE::force_index and TABLE::covering_keys.
*/
bool process_index_hints(TABLE *table);
- /* Access MERGE child def version. See top comment in ha_myisammrg.cc */
- inline ulong get_child_def_version()
- {
- return child_def_version;
- }
- inline void set_child_def_version(ulong version)
- {
- child_def_version= version;
- }
- inline void init_child_def_version()
- {
- child_def_version= ~0UL;
- }
-
/**
Compare the version of metadata from the previous execution
(if any) with values obtained from the current table
@@ -1757,9 +2044,14 @@ struct TABLE_LIST
*/
inline
void set_table_ref_id(TABLE_SHARE *s)
+ { set_table_ref_id(s->get_table_ref_type(), s->get_table_ref_version()); }
+
+ inline
+ void set_table_ref_id(enum_table_ref_type table_ref_type_arg,
+ ulong table_ref_version_arg)
{
- m_table_ref_type= s->get_table_ref_type();
- m_table_ref_version= s->get_table_ref_version();
+ m_table_ref_type= table_ref_type_arg;
+ m_table_ref_version= table_ref_version_arg;
}
/* Set of functions returning/setting state of a derived table/view. */
@@ -1820,7 +2112,7 @@ struct TABLE_LIST
derived_type|= DTYPE_MULTITABLE;
}
void reset_const_table();
- bool handle_derived(struct st_lex *lex, uint phases);
+ bool handle_derived(LEX *lex, uint phases);
/**
@brief True if this TABLE_LIST represents an anonymous derived table,
@@ -1861,6 +2153,7 @@ struct TABLE_LIST
}
return false;
}
+ void set_lock_type(THD* thd, enum thr_lock_type lock);
private:
bool prep_check_option(THD *thd, uint8 check_opt_type);
@@ -1871,13 +2164,6 @@ private:
#else
inline void set_check_merged() {}
#endif
- /*
- Cleanup for re-execution in a prepared statement or a stored
- procedure.
- */
-
- /* Remembered MERGE child def version. See top comment in ha_myisammrg.cc */
- ulong child_def_version;
/** See comments for set_metadata_id() */
enum enum_table_ref_type m_table_ref_type;
/** See comments for set_metadata_id() */
@@ -2137,4 +2423,90 @@ static inline void dbug_tmp_restore_column_maps(MY_BITMAP *read_set,
size_t max_row_length(TABLE *table, const uchar *data);
-#endif
+
+void init_mdl_requests(TABLE_LIST *table_list);
+
+int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
+ uint db_stat, uint prgflag, uint ha_open_flags,
+ TABLE *outparam, bool is_create_table);
+bool unpack_vcol_info_from_frm(THD *thd, MEM_ROOT *mem_root,
+ TABLE *table, Field *field,
+ LEX_STRING *vcol_expr, bool *error_reported);
+TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
+ uint key_length);
+void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
+ uint key_length,
+ const char *table_name, const char *path);
+void free_table_share(TABLE_SHARE *share);
+int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags);
+void open_table_error(TABLE_SHARE *share, int error, int db_errno, int errarg);
+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);
+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);
+
+int closefrm(TABLE *table, bool free_share);
+int read_string(File file, uchar* *to, size_t length);
+void free_blobs(TABLE *table);
+void free_field_buffers_larger_than(TABLE *table, uint32 size);
+int set_zone(int nr,int min_zone,int max_zone);
+ulong get_form_pos(File file, uchar *head, TYPELIB *save_names);
+ulong make_new_entry(File file,uchar *fileinfo,TYPELIB *formnames,
+ const char *newname);
+ulong next_io_size(ulong pos);
+void append_unescaped(String *res, const char *pos, uint length);
+File create_frm(THD *thd, const char *name, const char *db,
+ const char *table, uint reclength, uchar *fileinfo,
+ HA_CREATE_INFO *create_info, uint keys, KEY *key_info);
+char *fn_rext(char *name);
+
+/* performance schema */
+extern LEX_STRING PERFORMANCE_SCHEMA_DB_NAME;
+
+extern LEX_STRING GENERAL_LOG_NAME;
+extern LEX_STRING SLOW_LOG_NAME;
+
+/* information schema */
+extern LEX_STRING INFORMATION_SCHEMA_NAME;
+extern LEX_STRING 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));
+}
+
+inline bool is_infoschema_db(const char *name)
+{
+ return !my_strcasecmp(system_charset_info,
+ INFORMATION_SCHEMA_NAME.str, name);
+}
+
+TYPELIB *typelib(MEM_ROOT *mem_root, List<String> &strings);
+
+/**
+ return true if the table was created explicitly.
+*/
+inline bool is_user_table(TABLE * table)
+{
+ const char *name= table->s->table_name.str;
+ return strncmp(name, tmp_file_prefix, tmp_file_prefix_length);
+}
+
+inline void mark_as_null_row(TABLE *table)
+{
+ table->null_row=1;
+ table->status|=STATUS_NULL_ROW;
+ bfill(table->null_flags,table->s->null_bytes,255);
+}
+
+bool is_simple_order(ORDER *order);
+
+#endif /* MYSQL_CLIENT */
+
+#endif /* TABLE_INCLUDED */
diff --git a/sql/thr_malloc.cc b/sql/thr_malloc.cc
index 7afb8027a2c..cedcbefc26f 100644
--- a/sql/thr_malloc.cc
+++ b/sql/thr_malloc.cc
@@ -12,13 +12,15 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* Mallocs for used in threads */
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "thr_malloc.h"
+#include "sql_class.h"
extern "C" {
void sql_alloc_error_handler(void)
@@ -44,9 +46,10 @@ extern "C" {
returned in the error packet.
- SHOW ERROR/SHOW WARNINGS may be empty.
*/
- thd->main_da.set_error_status(thd,
- ER_OUT_OF_RESOURCES,
- ER(ER_OUT_OF_RESOURCES));
+ thd->stmt_da->set_error_status(thd,
+ ER_OUT_OF_RESOURCES,
+ ER(ER_OUT_OF_RESOURCES),
+ NULL);
}
}
@@ -113,10 +116,6 @@ void* sql_memdup(const void *ptr, size_t len)
return pos;
}
-void sql_element_free(void *ptr __attribute__((unused)))
-{} /* purecov: deadcode */
-
-
char *sql_strmake_with_convert(const char *str, size_t arg_length,
CHARSET_INFO *from_cs,
diff --git a/sql/thr_malloc.h b/sql/thr_malloc.h
new file mode 100644
index 00000000000..81b7d3cc238
--- /dev/null
+++ b/sql/thr_malloc.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef 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);
+void *sql_alloc(size_t);
+void *sql_calloc(size_t);
+char *sql_strdup(const char *str);
+char *sql_strmake(const char *str, size_t len);
+void *sql_memdup(const void * ptr, size_t size);
+char *sql_strmake_with_convert(const char *str, size_t arg_length,
+ CHARSET_INFO *from_cs,
+ size_t max_res_length,
+ CHARSET_INFO *to_cs, size_t *result_length);
+
+#endif /* THR_MALLOC_INCLUDED */
diff --git a/sql/threadpool.h b/sql/threadpool.h
new file mode 100644
index 00000000000..c080e5ba343
--- /dev/null
+++ b/sql/threadpool.h
@@ -0,0 +1,70 @@
+/* Copyright (C) 2012 Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#define MAX_THREAD_GROUPS 100000
+
+/* Threadpool parameters */
+extern uint threadpool_min_threads; /* Minimum threads in pool */
+extern uint threadpool_idle_timeout; /* Shutdown idle worker threads after this timeout */
+extern uint threadpool_size; /* Number of parallel executing threads */
+extern uint threadpool_max_size;
+extern uint threadpool_stall_limit; /* time interval in 10 ms units for stall checks*/
+extern uint threadpool_max_threads; /* Maximum threads in pool */
+extern uint threadpool_oversubscribe; /* Maximum active threads in group */
+
+
+
+/* Common thread pool routines, suitable for different implementations */
+extern void threadpool_remove_connection(THD *thd);
+extern int threadpool_process_request(THD *thd);
+extern int threadpool_add_connection(THD *thd);
+
+/*
+ Functions used by scheduler.
+ OS-specific implementations are in
+ threadpool_unix.cc or threadpool_win.cc
+*/
+extern bool tp_init();
+extern void tp_add_connection(THD*);
+extern void tp_wait_begin(THD *, int);
+extern void tp_wait_end(THD*);
+extern void tp_post_kill_notification(THD *thd);
+extern void tp_end(void);
+
+/* Used in SHOW for threadpool_idle_thread_count */
+extern int tp_get_idle_thread_count();
+
+/*
+ Threadpool statistics
+*/
+struct TP_STATISTICS
+{
+ /* Current number of worker thread. */
+ volatile int32 num_worker_threads;
+};
+
+extern TP_STATISTICS tp_stats;
+
+
+/* Functions to set threadpool parameters */
+extern void tp_set_min_threads(uint val);
+extern void tp_set_max_threads(uint val);
+extern void tp_set_threadpool_size(uint val);
+extern void tp_set_threadpool_stall_limit(uint val);
+
+/* Activate threadpool scheduler */
+extern void tp_scheduler(void);
+
+extern int show_threadpool_idle_threads(THD *thd, SHOW_VAR *var, char *buff);
diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc
new file mode 100644
index 00000000000..9e0cb07b86c
--- /dev/null
+++ b/sql/threadpool_common.cc
@@ -0,0 +1,282 @@
+/* Copyright (C) 2012 Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <my_global.h>
+#include <violite.h>
+#include <sql_priv.h>
+#include <sql_class.h>
+#include <my_pthread.h>
+#include <scheduler.h>
+#include <sql_connect.h>
+#include <sql_audit.h>
+#include <debug_sync.h>
+#include <threadpool.h>
+
+
+/* Threadpool parameters */
+
+uint threadpool_min_threads;
+uint threadpool_idle_timeout;
+uint threadpool_size;
+uint threadpool_max_size;
+uint threadpool_stall_limit;
+uint threadpool_max_threads;
+uint threadpool_oversubscribe;
+
+/* Stats */
+TP_STATISTICS tp_stats;
+
+
+extern "C" pthread_key(struct st_my_thread_var*, THR_KEY_mysys);
+extern bool do_command(THD*);
+
+/*
+ Worker threads contexts, and THD contexts.
+ =========================================
+
+ Both worker threads and connections have their sets of thread local variables
+ At the moment it is mysys_var (this has specific data for dbug, my_error and
+ similar goodies), and PSI per-client structure.
+
+ Whenever query is executed following needs to be done:
+
+ 1. Save worker thread context.
+ 2. Change TLS variables to connection specific ones using thread_attach(THD*).
+ This function does some additional work , e.g setting up
+ thread_stack/thread_ends_here pointers.
+ 3. Process query
+ 4. Restore worker thread context.
+
+ Connection login and termination follows similar schema w.r.t saving and
+ restoring contexts.
+
+ For both worker thread, and for the connection, mysys variables are created
+ using my_thread_init() and freed with my_thread_end().
+
+*/
+struct Worker_thread_context
+{
+ PSI_thread *psi_thread;
+ st_my_thread_var* mysys_var;
+
+ void save()
+ {
+#ifdef HAVE_PSI_INTERFACE
+ psi_thread= PSI_server?PSI_server->get_thread():0;
+#endif
+ mysys_var= (st_my_thread_var *)pthread_getspecific(THR_KEY_mysys);
+ }
+
+ void restore()
+ {
+#ifdef HAVE_PSI_INTERFACE
+ if (PSI_server)
+ PSI_server->set_thread(psi_thread);
+#endif
+ pthread_setspecific(THR_KEY_mysys,mysys_var);
+ pthread_setspecific(THR_THD, 0);
+ pthread_setspecific(THR_MALLOC, 0);
+ }
+};
+
+
+/*
+ Attach/associate the connection with the OS thread,
+*/
+static bool thread_attach(THD* thd)
+{
+ pthread_setspecific(THR_KEY_mysys,thd->mysys_var);
+ thd->thread_stack=(char*)&thd;
+ thd->store_globals();
+#ifdef HAVE_PSI_INTERFACE
+ if (PSI_server)
+ PSI_server->set_thread(thd->event_scheduler.m_psi);
+#endif
+ return 0;
+}
+
+
+int threadpool_add_connection(THD *thd)
+{
+ int retval=1;
+ Worker_thread_context worker_context;
+ worker_context.save();
+
+ /*
+ Create a new connection context: mysys_thread_var and PSI thread
+ Store them in THD.
+ */
+
+ pthread_setspecific(THR_KEY_mysys, 0);
+ my_thread_init();
+ thd->mysys_var= (st_my_thread_var *)pthread_getspecific(THR_KEY_mysys);
+ if (!thd->mysys_var)
+ {
+ /* Out of memory? */
+ worker_context.restore();
+ return 1;
+ }
+
+ /* Create new PSI thread for use with the THD. */
+#ifdef HAVE_PSI_INTERFACE
+ if (PSI_server)
+ {
+ thd->event_scheduler.m_psi =
+ PSI_server->new_thread(key_thread_one_connection, thd, thd->thread_id);
+ }
+#endif
+
+
+ /* Login. */
+ thread_attach(thd);
+ ulonglong now= microsecond_interval_timer();
+ thd->prior_thr_create_utime= now;
+ thd->start_utime= now;
+ thd->thr_create_utime= now;
+
+ if (!setup_connection_thread_globals(thd))
+ {
+ if (!login_connection(thd))
+ {
+ prepare_new_connection_state(thd);
+
+ /*
+ Check if THD is ok, as prepare_new_connection_state()
+ can fail, for example if init command failed.
+ */
+ if (thd_is_connection_alive(thd))
+ {
+ retval= 0;
+ thd->net.reading_or_writing= 1;
+ thd->skip_wait_timeout= true;
+ }
+ }
+ }
+ worker_context.restore();
+ return retval;
+}
+
+
+void threadpool_remove_connection(THD *thd)
+{
+
+ Worker_thread_context worker_context;
+ worker_context.save();
+
+ thread_attach(thd);
+ thd->net.reading_or_writing= 0;
+
+ end_connection(thd);
+ close_connection(thd, 0);
+
+ unlink_thd(thd);
+ mysql_cond_broadcast(&COND_thread_count);
+
+ /*
+ Free resources associated with this connection:
+ mysys thread_var and PSI thread.
+ */
+ my_thread_end();
+
+ worker_context.restore();
+}
+
+/**
+ Process a single client request or a single batch.
+*/
+int threadpool_process_request(THD *thd)
+{
+ int retval= 0;
+ Worker_thread_context worker_context;
+ worker_context.save();
+
+ thread_attach(thd);
+
+ if (thd->killed >= KILL_CONNECTION)
+ {
+ /*
+ killed flag was set by timeout handler
+ or KILL command. Return error.
+ */
+ retval= 1;
+ goto end;
+ }
+
+
+ /*
+ In the loop below, the flow is essentially the copy of thead-per-connections
+ logic, see do_handle_one_connection() in sql_connect.c
+
+ The goal is to execute a single query, thus the loop is normally executed
+ only once. However for SSL connections, it can be executed multiple times
+ (SSL can preread and cache incoming data, and vio->has_data() checks if it
+ was the case).
+ */
+ for(;;)
+ {
+ Vio *vio;
+ thd->net.reading_or_writing= 0;
+ mysql_audit_release(thd);
+
+ if ((retval= do_command(thd)) != 0)
+ goto end;
+
+ if (!thd_is_connection_alive(thd))
+ {
+ retval= 1;
+ goto end;
+ }
+
+ vio= thd->net.vio;
+ if (!vio->has_data(vio))
+ {
+ /* More info on this debug sync is in sql_parse.cc*/
+ DEBUG_SYNC(thd, "before_do_command_net_read");
+ thd->net.reading_or_writing= 1;
+ goto end;
+ }
+ }
+
+end:
+ worker_context.restore();
+ return retval;
+}
+
+
+static scheduler_functions tp_scheduler_functions=
+{
+ 0, // max_threads
+ NULL,
+ NULL,
+ tp_init, // init
+ NULL, // init_new_connection_thread
+ tp_add_connection, // add_connection
+ tp_wait_begin, // thd_wait_begin
+ tp_wait_end, // thd_wait_end
+ post_kill_notification, // post_kill_notification
+ NULL, // end_thread
+ tp_end // end
+};
+
+void pool_of_threads_scheduler(struct scheduler_functions *func,
+ ulong *arg_max_connections,
+ uint *arg_connection_count)
+{
+ *func = tp_scheduler_functions;
+ func->max_threads= threadpool_max_threads;
+ func->max_connections= arg_max_connections;
+ func->connection_count= arg_connection_count;
+ scheduler_init();
+}
diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc
new file mode 100644
index 00000000000..df1a05b3ebf
--- /dev/null
+++ b/sql/threadpool_unix.cc
@@ -0,0 +1,1680 @@
+/* Copyright (C) 2012 Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <my_global.h>
+#include <violite.h>
+#include <sql_priv.h>
+#include <sql_class.h>
+#include <my_pthread.h>
+#include <scheduler.h>
+#include <sql_connect.h>
+#include <mysqld.h>
+#include <debug_sync.h>
+#include <time.h>
+#include <sql_plist.h>
+#include <threadpool.h>
+#include <time.h>
+#ifdef __linux__
+#include <sys/epoll.h>
+typedef struct epoll_event native_event;
+#elif defined(HAVE_KQUEUE)
+#include <sys/event.h>
+typedef struct kevent native_event;
+#elif defined (__sun)
+#include <port.h>
+typedef port_event_t native_event;
+#else
+#error threadpool is not available on this platform
+#endif
+
+/** Maximum number of native events a listener can read in one go */
+#define MAX_EVENTS 1024
+
+/** Indicates that threadpool was initialized*/
+static bool threadpool_started= false;
+
+/*
+ Define PSI Keys for performance schema.
+ We have a mutex per group, worker threads, condition per worker thread,
+ and timer thread with its own mutex and condition.
+*/
+
+
+#ifdef HAVE_PSI_INTERFACE
+static PSI_mutex_key key_group_mutex;
+static PSI_mutex_key key_timer_mutex;
+static PSI_mutex_info mutex_list[]=
+{
+ { &key_group_mutex, "group_mutex", 0},
+ { &key_timer_mutex, "timer_mutex", PSI_FLAG_GLOBAL}
+};
+
+static PSI_cond_key key_worker_cond;
+static PSI_cond_key key_timer_cond;
+static PSI_cond_info cond_list[]=
+{
+ { &key_worker_cond, "worker_cond", 0},
+ { &key_timer_cond, "timer_cond", PSI_FLAG_GLOBAL}
+};
+
+static PSI_thread_key key_worker_thread;
+static PSI_thread_key key_timer_thread;
+static PSI_thread_info thread_list[] =
+{
+ {&key_worker_thread, "worker_thread", 0},
+ {&key_timer_thread, "timer_thread", PSI_FLAG_GLOBAL}
+};
+
+/* Macro to simplify performance schema registration */
+#define PSI_register(X) \
+ if(PSI_server) PSI_server->register_ ## X("threadpool", X ## _list, array_elements(X ## _list))
+#else
+#define PSI_register(X) /* no-op */
+#endif
+
+
+struct thread_group_t;
+
+/* Per-thread structure for workers */
+struct worker_thread_t
+{
+ ulonglong event_count; /* number of request handled by this thread */
+ thread_group_t* thread_group;
+ worker_thread_t *next_in_list;
+ worker_thread_t **prev_in_list;
+
+ mysql_cond_t cond;
+ bool woken;
+};
+
+typedef I_P_List<worker_thread_t, I_P_List_adapter<worker_thread_t,
+ &worker_thread_t::next_in_list,
+ &worker_thread_t::prev_in_list>
+ >
+worker_list_t;
+
+struct connection_t
+{
+
+ THD *thd;
+ thread_group_t *thread_group;
+ connection_t *next_in_queue;
+ connection_t **prev_in_queue;
+ ulonglong abs_wait_timeout;
+ bool logged_in;
+ bool bound_to_poll_descriptor;
+ bool waiting;
+};
+
+typedef I_P_List<connection_t,
+ I_P_List_adapter<connection_t,
+ &connection_t::next_in_queue,
+ &connection_t::prev_in_queue>,
+ I_P_List_null_counter,
+ I_P_List_fast_push_back<connection_t> >
+connection_queue_t;
+
+struct thread_group_t
+{
+ mysql_mutex_t mutex;
+ connection_queue_t queue;
+ worker_list_t waiting_threads;
+ worker_thread_t *listener;
+ pthread_attr_t *pthread_attr;
+ int pollfd;
+ int thread_count;
+ int active_thread_count;
+ int connection_count;
+ /* Stats for the deadlock detection timer routine.*/
+ int io_event_count;
+ int queue_event_count;
+ ulonglong last_thread_creation_time;
+ int shutdown_pipe[2];
+ bool shutdown;
+ bool stalled;
+
+} MY_ALIGNED(512);
+
+static thread_group_t *all_groups;
+static uint group_count;
+static int32 shutdown_group_count;
+
+/**
+ Used for printing "pool blocked" message, see
+ print_pool_blocked_message();
+*/
+static ulonglong pool_block_start;
+
+/* Global timer for all groups */
+struct pool_timer_t
+{
+ mysql_mutex_t mutex;
+ mysql_cond_t cond;
+ volatile uint64 current_microtime;
+ volatile uint64 next_timeout_check;
+ int tick_interval;
+ bool shutdown;
+};
+
+static pool_timer_t pool_timer;
+
+static void queue_put(thread_group_t *thread_group, connection_t *connection);
+static int wake_thread(thread_group_t *thread_group);
+static void handle_event(connection_t *connection);
+static int wake_or_create_thread(thread_group_t *thread_group);
+static int create_worker(thread_group_t *thread_group);
+static void *worker_main(void *param);
+static void check_stall(thread_group_t *thread_group);
+static void connection_abort(connection_t *connection);
+static void set_wait_timeout(connection_t *connection);
+static void set_next_timeout_check(ulonglong abstime);
+static void print_pool_blocked_message(bool);
+
+/**
+ Asynchronous network IO.
+
+ We use native edge-triggered network IO multiplexing facility.
+ This maps to different APIs on different Unixes.
+
+ Supported are currently Linux with epoll, Solaris with event ports,
+ OSX and BSD with kevent. All those API's are used with one-shot flags
+ (the event is signalled once client has written something into the socket,
+ then socket is removed from the "poll-set" until the command is finished,
+ and we need to re-arm/re-register socket)
+
+ No implementation for poll/select/AIO is currently provided.
+
+ The API closely resembles all of the above mentioned platform APIs
+ and consists of following functions.
+
+ - io_poll_create()
+ Creates an io_poll descriptor
+ On Linux: epoll_create()
+
+ - io_poll_associate_fd(int poll_fd, int fd, void *data)
+ Associate file descriptor with io poll descriptor
+ On Linux : epoll_ctl(..EPOLL_CTL_ADD))
+
+ - io_poll_disassociate_fd(int pollfd, int fd)
+ Associate file descriptor with io poll descriptor
+ On Linux: epoll_ctl(..EPOLL_CTL_DEL)
+
+
+ - io_poll_start_read(int poll_fd,int fd, void *data)
+ The same as io_poll_associate_fd(), but cannot be used before
+ io_poll_associate_fd() was called.
+ On Linux : epoll_ctl(..EPOLL_CTL_MOD)
+
+ - io_poll_wait (int pollfd, native_event *native_events, int maxevents,
+ int timeout_ms)
+
+ wait until one or more descriptors added with io_poll_associate_fd()
+ or io_poll_start_read() becomes readable. Data associated with
+ descriptors can be retrieved from native_events array, using
+ native_event_get_userdata() function.
+
+
+ On Linux: epoll_wait()
+*/
+
+#if defined (__linux__)
+#ifndef EPOLLRDHUP
+/* Early 2.6 kernel did not have EPOLLRDHUP */
+#define EPOLLRDHUP 0
+#endif
+static int io_poll_create()
+{
+ return epoll_create(1);
+}
+
+
+int io_poll_associate_fd(int pollfd, int fd, void *data)
+{
+ struct epoll_event ev;
+ ev.data.u64= 0; /* Keep valgrind happy */
+ ev.data.ptr= data;
+ ev.events= EPOLLIN|EPOLLET|EPOLLERR|EPOLLRDHUP|EPOLLONESHOT;
+ return epoll_ctl(pollfd, EPOLL_CTL_ADD, fd, &ev);
+}
+
+
+
+int io_poll_start_read(int pollfd, int fd, void *data)
+{
+ struct epoll_event ev;
+ ev.data.u64= 0; /* Keep valgrind happy */
+ ev.data.ptr= data;
+ ev.events= EPOLLIN|EPOLLET|EPOLLERR|EPOLLRDHUP|EPOLLONESHOT;
+ return epoll_ctl(pollfd, EPOLL_CTL_MOD, fd, &ev);
+}
+
+int io_poll_disassociate_fd(int pollfd, int fd)
+{
+ struct epoll_event ev;
+ return epoll_ctl(pollfd, EPOLL_CTL_DEL, fd, &ev);
+}
+
+
+/*
+ Wrapper around epoll_wait.
+ NOTE - in case of EINTR, it restarts with original timeout. Since we use
+ either infinite or 0 timeouts, this is not critical
+*/
+int io_poll_wait(int pollfd, native_event *native_events, int maxevents,
+ int timeout_ms)
+{
+ int ret;
+ do
+ {
+ ret = epoll_wait(pollfd, native_events, maxevents, timeout_ms);
+ }
+ while(ret == -1 && errno == EINTR);
+ return ret;
+}
+
+
+static void *native_event_get_userdata(native_event *event)
+{
+ return event->data.ptr;
+}
+
+#elif defined(HAVE_KQUEUE)
+
+/*
+ NetBSD is incompatible with other BSDs , last parameter in EV_SET macro
+ (udata, user data) needs to be intptr_t, whereas it needs to be void*
+ everywhere else.
+*/
+
+#ifdef __NetBSD__
+#define MY_EV_SET(a, b, c, d, e, f, g) EV_SET(a, b, c, d, e, f, (intptr_t)g)
+#else
+#define MY_EV_SET(a, b, c, d, e, f, g) EV_SET(a, b, c, d, e, f, g)
+#endif
+
+
+int io_poll_create()
+{
+ return kqueue();
+}
+
+int io_poll_start_read(int pollfd, int fd, void *data)
+{
+ struct kevent ke;
+ MY_EV_SET(&ke, fd, EVFILT_READ, EV_ADD|EV_ONESHOT,
+ 0, 0, data);
+ return kevent(pollfd, &ke, 1, 0, 0, 0);
+}
+
+
+int io_poll_associate_fd(int pollfd, int fd, void *data)
+{
+ struct kevent ke;
+ MY_EV_SET(&ke, fd, EVFILT_READ, EV_ADD|EV_ONESHOT,
+ 0, 0, data);
+ return io_poll_start_read(pollfd,fd, data);
+}
+
+
+int io_poll_disassociate_fd(int pollfd, int fd)
+{
+ struct kevent ke;
+ MY_EV_SET(&ke,fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
+ return kevent(pollfd, &ke, 1, 0, 0, 0);
+}
+
+
+int io_poll_wait(int pollfd, struct kevent *events, int maxevents, int timeout_ms)
+{
+ struct timespec ts;
+ int ret;
+ if (timeout_ms >= 0)
+ {
+ ts.tv_sec= timeout_ms/1000;
+ ts.tv_nsec= (timeout_ms%1000)*1000000;
+ }
+ do
+ {
+ ret= kevent(pollfd, 0, 0, events, maxevents,
+ (timeout_ms >= 0)?&ts:NULL);
+ }
+ while (ret == -1 && errno == EINTR);
+ return ret;
+}
+
+static void* native_event_get_userdata(native_event *event)
+{
+ return (void *)event->udata;
+}
+
+#elif defined (__sun)
+
+static int io_poll_create()
+{
+ return port_create();
+}
+
+int io_poll_start_read(int pollfd, int fd, void *data)
+{
+ return port_associate(pollfd, PORT_SOURCE_FD, fd, POLLIN, data);
+}
+
+static int io_poll_associate_fd(int pollfd, int fd, void *data)
+{
+ return io_poll_start_read(pollfd, fd, data);
+}
+
+int io_poll_disassociate_fd(int pollfd, int fd)
+{
+ return port_dissociate(pollfd, PORT_SOURCE_FD, fd);
+}
+
+int io_poll_wait(int pollfd, native_event *events, int maxevents, int timeout_ms)
+{
+ struct timespec ts;
+ int ret;
+ uint_t nget= 1;
+ if (timeout_ms >= 0)
+ {
+ ts.tv_sec= timeout_ms/1000;
+ ts.tv_nsec= (timeout_ms%1000)*1000000;
+ }
+ do
+ {
+ ret= port_getn(pollfd, events, maxevents, &nget,
+ (timeout_ms >= 0)?&ts:NULL);
+ }
+ while (ret == -1 && errno == EINTR);
+ DBUG_ASSERT(nget < INT_MAX);
+ return (int)nget;
+}
+
+static void* native_event_get_userdata(native_event *event)
+{
+ return event->portev_user;
+}
+#endif
+
+
+/* Dequeue element from a workqueue */
+
+static connection_t *queue_get(thread_group_t *thread_group)
+{
+ DBUG_ENTER("queue_get");
+ thread_group->queue_event_count++;
+ connection_t *c= thread_group->queue.front();
+ if (c)
+ {
+ thread_group->queue.remove(c);
+ }
+ DBUG_RETURN(c);
+}
+
+
+/*
+ Handle wait timeout :
+ Find connections that have been idle for too long and kill them.
+ Also, recalculate time when next timeout check should run.
+*/
+
+static void timeout_check(pool_timer_t *timer)
+{
+ DBUG_ENTER("timeout_check");
+
+ mysql_mutex_lock(&LOCK_thread_count);
+ I_List_iterator<THD> it(threads);
+
+ /* Reset next timeout check, it will be recalculated in the loop below */
+ my_atomic_fas64((volatile int64*)&timer->next_timeout_check, ULONGLONG_MAX);
+
+ THD *thd;
+ while ((thd=it++))
+ {
+ if (thd->net.reading_or_writing != 1)
+ continue;
+
+ connection_t *connection= (connection_t *)thd->event_scheduler.data;
+ if (!connection)
+ {
+ /*
+ Connection does not have scheduler data. This happens for example
+ if THD belongs to a different scheduler, that is listening to extra_port.
+ */
+ continue;
+ }
+
+ if(connection->abs_wait_timeout < timer->current_microtime)
+ {
+ /* Wait timeout exceeded, kill connection. */
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ thd->killed = KILL_CONNECTION;
+ post_kill_notification(thd);
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+ }
+ else
+ {
+ set_next_timeout_check(connection->abs_wait_timeout);
+ }
+ }
+ mysql_mutex_unlock(&LOCK_thread_count);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Timer thread.
+
+ Periodically, check if one of the thread groups is stalled. Stalls happen if
+ events are not being dequeued from the queue, or from the network, Primary
+ reason for stall can be a lengthy executing non-blocking request. It could
+ also happen that thread is waiting but wait_begin/wait_end is forgotten by
+ storage engine. Timer thread will create a new thread in group in case of
+ a stall.
+
+ Besides checking for stalls, timer thread is also responsible for terminating
+ clients that have been idle for longer than wait_timeout seconds.
+
+ TODO: Let the timer sleep for long time if there is no work to be done.
+ Currently it wakes up rather often on and idle server.
+*/
+
+static void* timer_thread(void *param)
+{
+ uint i;
+ pool_timer_t* timer=(pool_timer_t *)param;
+
+ my_thread_init();
+ DBUG_ENTER("timer_thread");
+ timer->next_timeout_check= ULONGLONG_MAX;
+ timer->current_microtime= microsecond_interval_timer();
+
+ for(;;)
+ {
+ struct timespec ts;
+ int err;
+
+ set_timespec_nsec(ts,timer->tick_interval*1000000);
+ mysql_mutex_lock(&timer->mutex);
+ err= mysql_cond_timedwait(&timer->cond, &timer->mutex, &ts);
+ if (timer->shutdown)
+ {
+ mysql_mutex_unlock(&timer->mutex);
+ break;
+ }
+ if (err == ETIMEDOUT)
+ {
+ timer->current_microtime= microsecond_interval_timer();
+
+ /* Check stalls in thread groups */
+ for (i= 0; i < threadpool_max_size; i++)
+ {
+ if(all_groups[i].connection_count)
+ check_stall(&all_groups[i]);
+ }
+
+ /* Check if any client exceeded wait_timeout */
+ if (timer->next_timeout_check <= timer->current_microtime)
+ timeout_check(timer);
+ }
+ mysql_mutex_unlock(&timer->mutex);
+ }
+
+ mysql_mutex_destroy(&timer->mutex);
+ my_thread_end();
+ return NULL;
+}
+
+
+
+void check_stall(thread_group_t *thread_group)
+{
+ if (mysql_mutex_trylock(&thread_group->mutex) != 0)
+ {
+ /* Something happens. Don't disturb */
+ return;
+ }
+
+ /*
+ Check if listener is present. If not, check whether any IO
+ events were dequeued since last time. If not, this means
+ listener is either in tight loop or thd_wait_begin()
+ was forgotten. Create a new worker(it will make itself listener).
+ */
+ if (!thread_group->listener && !thread_group->io_event_count)
+ {
+ wake_or_create_thread(thread_group);
+ mysql_mutex_unlock(&thread_group->mutex);
+ return;
+ }
+
+ /* Reset io event count */
+ thread_group->io_event_count= 0;
+
+ /*
+ Check whether requests from the workqueue are being dequeued.
+
+ The stall detection and resolution works as follows:
+
+ 1. There is a counter thread_group->queue_event_count for the number of
+ events removed from the queue. Timer resets the counter to 0 on each run.
+ 2. Timer determines stall if this counter remains 0 since last check
+ and the queue is not empty.
+ 3. Once timer determined a stall it sets thread_group->stalled flag and
+ wakes and idle worker (or creates a new one, subject to throttling).
+ 4. The stalled flag is reset, when an event is dequeued.
+
+ Q : Will this handling lead to an unbound growth of threads, if queue
+ stalls permanently?
+ A : No. If queue stalls permanently, it is an indication for many very long
+ simultaneous queries. The maximum number of simultanoues queries is
+ max_connections, further we have threadpool_max_threads limit, upon which no
+ worker threads are created. So in case there is a flood of very long
+ queries, threadpool would slowly approach thread-per-connection behavior.
+ NOTE:
+ If long queries never wait, creation of the new threads is done by timer,
+ so it is slower than in real thread-per-connection. However if long queries
+ do wait and indicate that via thd_wait_begin/end callbacks, thread creation
+ will be faster.
+ */
+ if (!thread_group->queue.is_empty() && !thread_group->queue_event_count)
+ {
+ thread_group->stalled= true;
+ wake_or_create_thread(thread_group);
+ }
+
+ /* Reset queue event count */
+ thread_group->queue_event_count= 0;
+
+ mysql_mutex_unlock(&thread_group->mutex);
+}
+
+
+static void start_timer(pool_timer_t* timer)
+{
+ pthread_t thread_id;
+ DBUG_ENTER("start_timer");
+ mysql_mutex_init(key_timer_mutex,&timer->mutex, NULL);
+ mysql_cond_init(key_timer_cond, &timer->cond, NULL);
+ timer->shutdown = false;
+ mysql_thread_create(key_timer_thread,&thread_id, NULL, timer_thread, timer);
+ DBUG_VOID_RETURN;
+}
+
+
+static void stop_timer(pool_timer_t *timer)
+{
+ DBUG_ENTER("stop_timer");
+ mysql_mutex_lock(&timer->mutex);
+ timer->shutdown = true;
+ mysql_cond_signal(&timer->cond);
+ mysql_mutex_unlock(&timer->mutex);
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Poll for socket events and distribute them to worker threads
+ In many case current thread will handle single event itself.
+
+ @return a ready connection, or NULL on shutdown
+*/
+static connection_t * listener(worker_thread_t *current_thread,
+ thread_group_t *thread_group)
+{
+ DBUG_ENTER("listener");
+ connection_t *retval= NULL;
+
+ for(;;)
+ {
+ native_event ev[MAX_EVENTS];
+ int cnt;
+
+ if (thread_group->shutdown)
+ break;
+
+ cnt = io_poll_wait(thread_group->pollfd, ev, MAX_EVENTS, -1);
+
+ if (cnt <=0)
+ {
+ DBUG_ASSERT(thread_group->shutdown);
+ break;
+ }
+
+ mysql_mutex_lock(&thread_group->mutex);
+
+ if (thread_group->shutdown)
+ {
+ mysql_mutex_unlock(&thread_group->mutex);
+ break;
+ }
+
+ thread_group->io_event_count += cnt;
+
+ /*
+ We got some network events and need to make decisions : whether
+ listener hould handle events and whether or not any wake worker
+ threads so they can handle events.
+
+ Q1 : Should listener handle an event itself, or put all events into
+ queue and let workers handle the events?
+
+ Solution :
+ Generally, listener that handles events itself is preferable. We do not
+ want listener thread to change its state from waiting to running too
+ often, Since listener has just woken from poll, it better uses its time
+ slice and does some work. Besides, not handling events means they go to
+ the queue, and often to wake another worker must wake up to handle the
+ event. This is not good, as we want to avoid wakeups.
+
+ The downside of listener that also handles queries is that we can
+ potentially leave thread group for long time not picking the new
+ network events. It is not a major problem, because this stall will be
+ detected sooner or later by the timer thread. Still, relying on timer
+ is not always good, because it may "tick" too slow (large timer_interval)
+
+ We use following strategy to solve this problem - if queue was not empty
+ we suspect flood of network events and listener stays, Otherwise, it
+ handles a query.
+
+
+ Q2: If queue is not empty, how many workers to wake?
+
+ Solution:
+ We generally try to keep one thread per group active (threads handling
+ queries are considered active, unless they stuck in inside some "wait")
+ Thus, we will wake only one worker, and only if there is not active
+ threads currently,and listener is not going to handle a query. When we
+ don't wake, we hope that currently active threads will finish fast and
+ handle the queue. If this does not happen, timer thread will detect stall
+ and wake a worker.
+
+ NOTE: Currently nothing is done to detect or prevent long queuing times.
+ A solutionc for the future would be to give up "one active thread per
+ group" principle, if events stay in the queue for too long, and just wake
+ more workers.
+ */
+
+ bool listener_picks_event= thread_group->queue.is_empty();
+
+ /*
+ If listener_picks_event is set, listener thread will handle first event,
+ and put the rest into the queue. If listener_pick_event is not set, all
+ events go to the queue.
+ */
+ for(int i=(listener_picks_event)?1:0; i < cnt ; i++)
+ {
+ connection_t *c= (connection_t *)native_event_get_userdata(&ev[i]);
+ thread_group->queue.push_back(c);
+ }
+
+ if (listener_picks_event)
+ {
+ /* Handle the first event. */
+ retval= (connection_t *)native_event_get_userdata(&ev[0]);
+ mysql_mutex_unlock(&thread_group->mutex);
+ break;
+ }
+
+ if(thread_group->active_thread_count==0)
+ {
+ /* We added some work items to queue, now wake a worker. */
+ if(wake_thread(thread_group))
+ {
+ /*
+ Wake failed, hence groups has no idle threads. Now check if there are
+ any threads in the group except listener.
+ */
+ if(thread_group->thread_count == 1)
+ {
+ /*
+ Currently there is no worker thread in the group, as indicated by
+ thread_count == 1 (this means listener is the only one thread in
+ the group).
+ The queue is not empty, and listener is not going to handle
+ events. In order to drain the queue, we create a worker here.
+ Alternatively, we could just rely on timer to detect stall, and
+ create thread, but waiting for timer would be an inefficient and
+ pointless delay.
+ */
+ create_worker(thread_group);
+ }
+ }
+ }
+ mysql_mutex_unlock(&thread_group->mutex);
+ }
+
+ DBUG_RETURN(retval);
+}
+
+/**
+ Adjust thread counters in group or global
+ whenever thread is created or is about to exit
+
+ @param thread_group
+ @param count - 1, when new thread is created
+ -1, when thread is about to exit
+*/
+
+static void add_thread_count(thread_group_t *thread_group, int32 count)
+{
+ thread_group->thread_count += count;
+ /* worker starts out and end in "active" state */
+ thread_group->active_thread_count += count;
+ my_atomic_add32(&tp_stats.num_worker_threads, count);
+}
+
+
+/**
+ Creates a new worker thread.
+ thread_mutex must be held when calling this function
+
+ NOTE: in rare cases, the number of threads can exceed
+ threadpool_max_threads, because we need at least 2 threads
+ per group to prevent deadlocks (one listener + one worker)
+*/
+
+static int create_worker(thread_group_t *thread_group)
+{
+ pthread_t thread_id;
+ bool max_threads_reached= false;
+ int err;
+
+ DBUG_ENTER("create_worker");
+ if (tp_stats.num_worker_threads >= (int)threadpool_max_threads
+ && thread_group->thread_count >= 2)
+ {
+ err= 1;
+ max_threads_reached= true;
+ goto end;
+ }
+
+
+ err= mysql_thread_create(key_worker_thread, &thread_id,
+ thread_group->pthread_attr, worker_main, thread_group);
+ if (!err)
+ {
+ thread_group->last_thread_creation_time=microsecond_interval_timer();
+ thread_created++;
+ add_thread_count(thread_group, 1);
+ }
+ else
+ {
+ my_errno= errno;
+ }
+
+end:
+ if (err)
+ print_pool_blocked_message(max_threads_reached);
+ else
+ pool_block_start= 0; /* Reset pool blocked timer, if it was set */
+
+ DBUG_RETURN(err);
+}
+
+
+/**
+ Calculate microseconds throttling delay for thread creation.
+
+ The value depends on how many threads are already in the group:
+ small number of threads means no delay, the more threads the larger
+ the delay.
+
+ The actual values were not calculated using any scientific methods.
+ They just look right, and behave well in practice.
+
+ TODO: Should throttling depend on thread_pool_stall_limit?
+*/
+static ulonglong microsecond_throttling_interval(thread_group_t *thread_group)
+{
+ int count= thread_group->thread_count;
+
+ if (count < 4)
+ return 0;
+
+ if (count < 8)
+ return 50*1000;
+
+ if(count < 16)
+ return 100*1000;
+
+ return 200*1000;
+}
+
+
+/**
+ Wakes a worker thread, or creates a new one.
+
+ Worker creation is throttled, so we avoid too many threads
+ to be created during the short time.
+*/
+static int wake_or_create_thread(thread_group_t *thread_group)
+{
+ DBUG_ENTER("wake_or_create_thread");
+
+ if (thread_group->shutdown)
+ DBUG_RETURN(0);
+
+ if (wake_thread(thread_group) == 0)
+ DBUG_RETURN(0);
+
+ if (thread_group->thread_count > thread_group->connection_count)
+ DBUG_RETURN(-1);
+
+
+ if (thread_group->active_thread_count == 0)
+ {
+ /*
+ We're better off creating a new thread here with no delay, either there
+ are no workers at all, or they all are all blocking and there was no
+ idle thread to wakeup. Smells like a potential deadlock or very slowly
+ executing requests, e.g sleeps or user locks.
+ */
+ DBUG_RETURN(create_worker(thread_group));
+ }
+
+ ulonglong now = microsecond_interval_timer();
+ ulonglong time_since_last_thread_created =
+ (now - thread_group->last_thread_creation_time);
+
+ /* Throttle thread creation. */
+ if (time_since_last_thread_created >
+ microsecond_throttling_interval(thread_group))
+ {
+ DBUG_RETURN(create_worker(thread_group));
+ }
+
+ DBUG_RETURN(-1);
+}
+
+
+
+int thread_group_init(thread_group_t *thread_group, pthread_attr_t* thread_attr)
+{
+ DBUG_ENTER("thread_group_init");
+ thread_group->pthread_attr = thread_attr;
+ mysql_mutex_init(key_group_mutex, &thread_group->mutex, NULL);
+ thread_group->pollfd= -1;
+ thread_group->shutdown_pipe[0]= -1;
+ thread_group->shutdown_pipe[1]= -1;
+ thread_group->queue.empty();
+ DBUG_RETURN(0);
+}
+
+
+void thread_group_destroy(thread_group_t *thread_group)
+{
+ mysql_mutex_destroy(&thread_group->mutex);
+ if (thread_group->pollfd != -1)
+ {
+ close(thread_group->pollfd);
+ thread_group->pollfd= -1;
+ }
+ for(int i=0; i < 2; i++)
+ {
+ if(thread_group->shutdown_pipe[i] != -1)
+ {
+ close(thread_group->shutdown_pipe[i]);
+ thread_group->shutdown_pipe[i]= -1;
+ }
+ }
+ if (my_atomic_add32(&shutdown_group_count, -1) == 1)
+ my_free(all_groups);
+}
+
+/**
+ Wake sleeping thread from waiting list
+*/
+
+static int wake_thread(thread_group_t *thread_group)
+{
+ DBUG_ENTER("wake_thread");
+ worker_thread_t *thread = thread_group->waiting_threads.front();
+ if(thread)
+ {
+ thread->woken= true;
+ thread_group->waiting_threads.remove(thread);
+ mysql_cond_signal(&thread->cond);
+ DBUG_RETURN(0);
+ }
+ DBUG_RETURN(1); /* no thread in waiter list => missed wakeup */
+}
+
+
+/**
+ Initiate shutdown for thread group.
+
+ The shutdown is asynchronous, we only care to wake all threads in here, so
+ they can finish. We do not wait here until threads terminate. Final cleanup
+ of the group (thread_group_destroy) will be done by the last exiting threads.
+*/
+
+static void thread_group_close(thread_group_t *thread_group)
+{
+ DBUG_ENTER("thread_group_close");
+
+ mysql_mutex_lock(&thread_group->mutex);
+ if (thread_group->thread_count == 0)
+ {
+ mysql_mutex_unlock(&thread_group->mutex);
+ thread_group_destroy(thread_group);
+ DBUG_VOID_RETURN;
+ }
+
+ thread_group->shutdown= true;
+ thread_group->listener= NULL;
+
+ if (pipe(thread_group->shutdown_pipe))
+ {
+ DBUG_VOID_RETURN;
+ }
+
+ /* Wake listener */
+ if (io_poll_associate_fd(thread_group->pollfd,
+ thread_group->shutdown_pipe[0], NULL))
+ {
+ DBUG_VOID_RETURN;
+ }
+ char c= 0;
+ if (write(thread_group->shutdown_pipe[1], &c, 1) < 0)
+ DBUG_VOID_RETURN;
+
+ /* Wake all workers. */
+ while(wake_thread(thread_group) == 0)
+ {
+ }
+
+ mysql_mutex_unlock(&thread_group->mutex);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Add work to the queue. Maybe wake a worker if they all sleep.
+
+ Currently, this function is only used when new connections need to
+ perform login (this is done in worker threads).
+
+*/
+
+static void queue_put(thread_group_t *thread_group, connection_t *connection)
+{
+ DBUG_ENTER("queue_put");
+
+ mysql_mutex_lock(&thread_group->mutex);
+ thread_group->queue.push_back(connection);
+
+ if (thread_group->active_thread_count == 0)
+ wake_or_create_thread(thread_group);
+
+ mysql_mutex_unlock(&thread_group->mutex);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Prevent too many threads executing at the same time,if the workload is
+ not CPU bound.
+*/
+
+static bool too_many_threads(thread_group_t *thread_group)
+{
+ return (thread_group->active_thread_count >= 1+(int)threadpool_oversubscribe
+ && !thread_group->stalled);
+}
+
+
+/**
+ Retrieve a connection with pending event.
+
+ Pending event in our case means that there is either a pending login request
+ (if connection is not yet logged in), or there are unread bytes on the socket.
+
+ If there are no pending events currently, thread will wait.
+ If timeout specified in abstime parameter passes, the function returns NULL.
+
+ @param current_thread - current worker thread
+ @param thread_group - current thread group
+ @param abstime - absolute wait timeout
+
+ @return
+ connection with pending event.
+ NULL is returned if timeout has expired,or on shutdown.
+*/
+
+connection_t *get_event(worker_thread_t *current_thread,
+ thread_group_t *thread_group, struct timespec *abstime)
+{
+ DBUG_ENTER("get_event");
+ connection_t *connection = NULL;
+ int err=0;
+
+ mysql_mutex_lock(&thread_group->mutex);
+ DBUG_ASSERT(thread_group->active_thread_count >= 0);
+
+ for(;;)
+ {
+ bool oversubscribed = too_many_threads(thread_group);
+ if (thread_group->shutdown)
+ break;
+
+ /* Check if queue is not empty */
+ if (!oversubscribed)
+ {
+ connection = queue_get(thread_group);
+ if(connection)
+ break;
+ }
+
+ /* If there is currently no listener in the group, become one. */
+ if(!thread_group->listener)
+ {
+ thread_group->listener= current_thread;
+ thread_group->active_thread_count--;
+ mysql_mutex_unlock(&thread_group->mutex);
+
+ connection = listener(current_thread, thread_group);
+
+ mysql_mutex_lock(&thread_group->mutex);
+ thread_group->active_thread_count++;
+ /* There is no listener anymore, it just returned. */
+ thread_group->listener= NULL;
+ break;
+ }
+
+ /*
+ Last thing we try before going to sleep is to
+ pick a single event via epoll, without waiting (timeout 0)
+ */
+ if (!oversubscribed)
+ {
+ native_event nev;
+ if (io_poll_wait(thread_group->pollfd,&nev,1, 0) == 1)
+ {
+ thread_group->io_event_count++;
+ connection = (connection_t *)native_event_get_userdata(&nev);
+ break;
+ }
+ }
+
+ /* And now, finally sleep */
+ current_thread->woken = false; /* wake() sets this to true */
+
+ /*
+ Add current thread to the head of the waiting list and wait.
+ It is important to add thread to the head rather than tail
+ as it ensures LIFO wakeup order (hot caches, working inactivity timeout)
+ */
+ thread_group->waiting_threads.push_front(current_thread);
+
+ thread_group->active_thread_count--;
+ if (abstime)
+ {
+ err = mysql_cond_timedwait(&current_thread->cond, &thread_group->mutex,
+ abstime);
+ }
+ else
+ {
+ err = mysql_cond_wait(&current_thread->cond, &thread_group->mutex);
+ }
+ thread_group->active_thread_count++;
+
+ if (!current_thread->woken)
+ {
+ /*
+ Thread was not signalled by wake(), it might be a spurious wakeup or
+ a timeout. Anyhow, we need to remove ourselves from the list now.
+ If thread was explicitly woken, than caller removed us from the list.
+ */
+ thread_group->waiting_threads.remove(current_thread);
+ }
+
+ if (err)
+ break;
+ }
+
+ thread_group->stalled= false;
+ mysql_mutex_unlock(&thread_group->mutex);
+
+ DBUG_RETURN(connection);
+}
+
+
+
+/**
+ Tells the pool that worker starts waiting on IO, lock, condition,
+ sleep() or similar.
+*/
+
+void wait_begin(thread_group_t *thread_group)
+{
+ DBUG_ENTER("wait_begin");
+ mysql_mutex_lock(&thread_group->mutex);
+ thread_group->active_thread_count--;
+
+ DBUG_ASSERT(thread_group->active_thread_count >=0);
+ DBUG_ASSERT(thread_group->connection_count > 0);
+
+ if ((thread_group->active_thread_count == 0) &&
+ (thread_group->queue.is_empty() || !thread_group->listener))
+ {
+ /*
+ Group might stall while this thread waits, thus wake
+ or create a worker to prevent stall.
+ */
+ wake_or_create_thread(thread_group);
+ }
+
+ mysql_mutex_unlock(&thread_group->mutex);
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Tells the pool has finished waiting.
+*/
+
+void wait_end(thread_group_t *thread_group)
+{
+ DBUG_ENTER("wait_end");
+ mysql_mutex_lock(&thread_group->mutex);
+ thread_group->active_thread_count++;
+ mysql_mutex_unlock(&thread_group->mutex);
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Allocate/initialize a new connection structure.
+*/
+
+connection_t *alloc_connection(THD *thd)
+{
+ DBUG_ENTER("alloc_connection");
+
+ connection_t* connection = (connection_t *)my_malloc(sizeof(connection_t),0);
+ if (connection)
+ {
+ connection->thd = thd;
+ connection->waiting= false;
+ connection->logged_in= false;
+ connection->bound_to_poll_descriptor= false;
+ connection->abs_wait_timeout= ULONGLONG_MAX;
+ }
+ DBUG_RETURN(connection);
+}
+
+
+
+/**
+ Add a new connection to thread pool..
+*/
+
+void tp_add_connection(THD *thd)
+{
+ DBUG_ENTER("tp_add_connection");
+
+ threads.append(thd);
+ mysql_mutex_unlock(&LOCK_thread_count);
+ connection_t *connection= alloc_connection(thd);
+ if (connection)
+ {
+ thd->event_scheduler.data= connection;
+
+ /* Assign connection to a group. */
+ thread_group_t *group=
+ &all_groups[thd->thread_id%group_count];
+
+ connection->thread_group=group;
+
+ mysql_mutex_lock(&group->mutex);
+ group->connection_count++;
+ mysql_mutex_unlock(&group->mutex);
+
+ /*
+ Add connection to the work queue.Actual logon
+ will be done by a worker thread.
+ */
+ queue_put(group, connection);
+ }
+ else
+ {
+ /* Allocation failed */
+ threadpool_remove_connection(thd);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Terminate connection.
+*/
+
+static void connection_abort(connection_t *connection)
+{
+ DBUG_ENTER("connection_abort");
+ thread_group_t *group= connection->thread_group;
+
+ threadpool_remove_connection(connection->thd);
+
+ mysql_mutex_lock(&group->mutex);
+ group->connection_count--;
+ mysql_mutex_unlock(&group->mutex);
+
+ my_free(connection);
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ MySQL scheduler callback: wait begin
+*/
+
+void tp_wait_begin(THD *thd, int type)
+{
+ DBUG_ENTER("tp_wait_begin");
+ DBUG_ASSERT(thd);
+ connection_t *connection = (connection_t *)thd->event_scheduler.data;
+ if (connection)
+ {
+ DBUG_ASSERT(!connection->waiting);
+ connection->waiting= true;
+ wait_begin(connection->thread_group);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ MySQL scheduler callback: wait end
+*/
+
+void tp_wait_end(THD *thd)
+{
+ DBUG_ENTER("tp_wait_end");
+ DBUG_ASSERT(thd);
+
+ connection_t *connection = (connection_t *)thd->event_scheduler.data;
+ if (connection)
+ {
+ DBUG_ASSERT(connection->waiting);
+ connection->waiting = false;
+ wait_end(connection->thread_group);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+static void set_next_timeout_check(ulonglong abstime)
+{
+ DBUG_ENTER("set_next_timeout_check");
+ while(abstime < pool_timer.next_timeout_check)
+ {
+ longlong old= (longlong)pool_timer.next_timeout_check;
+ my_atomic_cas64((volatile int64*)&pool_timer.next_timeout_check,
+ &old, abstime);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Set wait timeout for connection.
+*/
+
+static void set_wait_timeout(connection_t *c)
+{
+ DBUG_ENTER("set_wait_timeout");
+ /*
+ Calculate wait deadline for this connection.
+ Instead of using microsecond_interval_timer() which has a syscall
+ overhead, use pool_timer.current_microtime and take
+ into account that its value could be off by at most
+ one tick interval.
+ */
+
+ c->abs_wait_timeout= pool_timer.current_microtime +
+ 1000LL*pool_timer.tick_interval +
+ 1000000LL*c->thd->variables.net_wait_timeout;
+
+ set_next_timeout_check(c->abs_wait_timeout);
+ DBUG_VOID_RETURN;
+}
+
+
+
+/**
+ Handle a (rare) special case,where connection needs to
+ migrate to a different group because group_count has changed
+ after thread_pool_size setting.
+*/
+
+static int change_group(connection_t *c,
+ thread_group_t *old_group,
+ thread_group_t *new_group)
+{
+ int ret= 0;
+ int fd = c->thd->net.vio->sd;
+
+ DBUG_ASSERT(c->thread_group == old_group);
+
+ /* Remove connection from the old group. */
+ mysql_mutex_lock(&old_group->mutex);
+ if (c->bound_to_poll_descriptor)
+ {
+ io_poll_disassociate_fd(old_group->pollfd,fd);
+ c->bound_to_poll_descriptor= false;
+ }
+ c->thread_group->connection_count--;
+ mysql_mutex_unlock(&old_group->mutex);
+
+ /* Add connection to the new group. */
+ mysql_mutex_lock(&new_group->mutex);
+ c->thread_group= new_group;
+ new_group->connection_count++;
+ /* Ensure that there is a listener in the new group. */
+ if (!new_group->thread_count)
+ ret= create_worker(new_group);
+ mysql_mutex_unlock(&new_group->mutex);
+ return ret;
+}
+
+
+static int start_io(connection_t *connection)
+{
+ int fd = connection->thd->net.vio->sd;
+
+ /*
+ Usually, connection will stay in the same group for the entire
+ connection's life. However, we do allow group_count to
+ change at runtime, which means in rare cases when it changes is
+ connection should need to migrate to another group, this ensures
+ to ensure equal load between groups.
+
+ So we recalculate in which group the connection should be, based
+ on thread_id and current group count, and migrate if necessary.
+ */
+ thread_group_t *group =
+ &all_groups[connection->thd->thread_id%group_count];
+
+ if (group != connection->thread_group)
+ {
+ if (change_group(connection, connection->thread_group, group))
+ return -1;
+ }
+
+ /*
+ Bind to poll descriptor if not yet done.
+ */
+ if (!connection->bound_to_poll_descriptor)
+ {
+ connection->bound_to_poll_descriptor= true;
+ return io_poll_associate_fd(group->pollfd, fd, connection);
+ }
+
+ return io_poll_start_read(group->pollfd, fd, connection);
+}
+
+
+
+static void handle_event(connection_t *connection)
+{
+
+ DBUG_ENTER("handle_event");
+ int err;
+
+ if (!connection->logged_in)
+ {
+ err= threadpool_add_connection(connection->thd);
+ connection->logged_in= true;
+ }
+ else
+ {
+ err= threadpool_process_request(connection->thd);
+ }
+
+ if(err)
+ goto end;
+
+ set_wait_timeout(connection);
+ err= start_io(connection);
+
+end:
+ if (err)
+ connection_abort(connection);
+
+ DBUG_VOID_RETURN;
+}
+
+
+
+/**
+ Worker thread's main
+*/
+
+static void *worker_main(void *param)
+{
+
+ worker_thread_t this_thread;
+ pthread_detach_this_thread();
+ my_thread_init();
+
+ DBUG_ENTER("worker_main");
+
+ thread_group_t *thread_group = (thread_group_t *)param;
+
+ /* Init per-thread structure */
+ mysql_cond_init(key_worker_cond, &this_thread.cond, NULL);
+ this_thread.thread_group= thread_group;
+ this_thread.event_count=0;
+
+ /* Run event loop */
+ for(;;)
+ {
+ connection_t *connection;
+ struct timespec ts;
+ set_timespec(ts,threadpool_idle_timeout);
+ connection = get_event(&this_thread, thread_group, &ts);
+ if (!connection)
+ break;
+ this_thread.event_count++;
+ handle_event(connection);
+ }
+
+ /* Thread shutdown: cleanup per-worker-thread structure. */
+ mysql_cond_destroy(&this_thread.cond);
+
+ bool last_thread; /* last thread in group exits */
+ mysql_mutex_lock(&thread_group->mutex);
+ add_thread_count(thread_group, -1);
+ last_thread= ((thread_group->thread_count == 0) && thread_group->shutdown);
+ mysql_mutex_unlock(&thread_group->mutex);
+
+ /* Last thread in group exits and pool is terminating, destroy group.*/
+ if (last_thread)
+ thread_group_destroy(thread_group);
+
+ my_thread_end();
+ return NULL;
+}
+
+
+bool tp_init()
+{
+ DBUG_ENTER("tp_init");
+ threadpool_max_size= max(threadpool_size, 128);
+ all_groups= (thread_group_t *)
+ my_malloc(sizeof(thread_group_t) * threadpool_max_size, MYF(MY_WME|MY_ZEROFILL));
+ if (!all_groups)
+ {
+ threadpool_max_size= 0;
+ DBUG_RETURN(1);
+ }
+ threadpool_started= true;
+ scheduler_init();
+
+ for (uint i= 0; i < threadpool_max_size; i++)
+ {
+ thread_group_init(&all_groups[i], get_connection_attrib());
+ }
+ tp_set_threadpool_size(threadpool_size);
+ if(group_count == 0)
+ {
+ /* Something went wrong */
+ sql_print_error("Can't set threadpool size to %d",threadpool_size);
+ DBUG_RETURN(1);
+ }
+ PSI_register(mutex);
+ PSI_register(cond);
+ PSI_register(thread);
+
+ pool_timer.tick_interval= threadpool_stall_limit;
+ start_timer(&pool_timer);
+ DBUG_RETURN(0);
+}
+
+
+void tp_end()
+{
+ DBUG_ENTER("tp_end");
+
+ if (!threadpool_started)
+ DBUG_VOID_RETURN;
+
+ stop_timer(&pool_timer);
+ shutdown_group_count= threadpool_max_size;
+ for (uint i= 0; i < threadpool_max_size; i++)
+ {
+ thread_group_close(&all_groups[i]);
+ }
+ threadpool_started= false;
+ DBUG_VOID_RETURN;
+}
+
+
+/** Ensure that poll descriptors are created when threadpool_size changes */
+
+void tp_set_threadpool_size(uint size)
+{
+ bool success= true;
+ if (!threadpool_started)
+ return;
+
+ for(uint i=0; i< size; i++)
+ {
+ thread_group_t *group= &all_groups[i];
+ mysql_mutex_lock(&group->mutex);
+ if (group->pollfd == -1)
+ {
+ group->pollfd= io_poll_create();
+ success= (group->pollfd >= 0);
+ if(!success)
+ {
+ sql_print_error("io_poll_create() failed, errno=%d\n", errno);
+ break;
+ }
+ }
+ mysql_mutex_unlock(&all_groups[i].mutex);
+ if (!success)
+ {
+ group_count= i;
+ return;
+ }
+ }
+ group_count= size;
+}
+
+void tp_set_threadpool_stall_limit(uint limit)
+{
+ if (!threadpool_started)
+ return;
+ mysql_mutex_lock(&(pool_timer.mutex));
+ pool_timer.tick_interval= limit;
+ mysql_mutex_unlock(&(pool_timer.mutex));
+ mysql_cond_signal(&(pool_timer.cond));
+}
+
+
+/**
+ Calculate number of idle/waiting threads in the pool.
+
+ Sum idle threads over all groups.
+ Don't do any locking, it is not required for stats.
+*/
+
+int tp_get_idle_thread_count()
+{
+ int sum=0;
+ for (uint i= 0; i < threadpool_max_size && all_groups[i].pollfd >= 0; i++)
+ {
+ sum+= (all_groups[i].thread_count - all_groups[i].active_thread_count);
+ }
+ return sum;
+}
+
+
+/* Report threadpool problems */
+
+/**
+ Delay in microseconds, after which "pool blocked" message is printed.
+ (30 sec == 30 Mio usec)
+*/
+#define BLOCK_MSG_DELAY 30*1000000
+
+#define MAX_THREADS_REACHED_MSG \
+"Threadpool could not create additional thread to handle queries, because the \
+number of allowed threads was reached. Increasing 'thread_pool_max_threads' \
+parameter can help in this situation.\n \
+If 'extra_port' parameter is set, you can still connect to the database with \
+superuser account (it must be TCP connection using extra_port as TCP port) \
+and troubleshoot the situation. \
+A likely cause of pool blocks are clients that lock resources for long time. \
+'show processlist' or 'show engine innodb status' can give additional hints."
+
+#define CREATE_THREAD_ERROR_MSG "Can't create threads in threadpool (errno=%d)."
+
+/**
+ Write a message when blocking situation in threadpool occurs.
+ The message is written only when pool blocks for BLOCK_MSG_DELAY (30) seconds.
+ It will be just a single message for each blocking situation (to prevent
+ log flood).
+*/
+
+static void print_pool_blocked_message(bool max_threads_reached)
+{
+ ulonglong now;
+ static bool msg_written;
+
+ now= microsecond_interval_timer();
+ if (pool_block_start == 0)
+ {
+ pool_block_start= now;
+ msg_written = false;
+ return;
+ }
+
+ if (now > pool_block_start + BLOCK_MSG_DELAY && !msg_written)
+ {
+ if (max_threads_reached)
+ sql_print_error(MAX_THREADS_REACHED_MSG);
+ else
+ sql_print_error(CREATE_THREAD_ERROR_MSG, my_errno);
+
+ sql_print_information("Threadpool has been blocked for %u seconds\n",
+ (uint)((now- pool_block_start)/1000000));
+ /* avoid reperated messages for the same blocking situation */
+ msg_written= true;
+ }
+}
diff --git a/sql/threadpool_win.cc b/sql/threadpool_win.cc
new file mode 100644
index 00000000000..72e03da2453
--- /dev/null
+++ b/sql/threadpool_win.cc
@@ -0,0 +1,747 @@
+/* Copyright (C) 2012 Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifdef _WIN32_WINNT
+#undef _WIN32_WINNT
+#endif
+
+#define _WIN32_WINNT 0x0601
+
+#include <my_global.h>
+#include <violite.h>
+#include <sql_priv.h>
+#include <sql_class.h>
+#include <my_pthread.h>
+#include <scheduler.h>
+#include <sql_connect.h>
+#include <mysqld.h>
+#include <debug_sync.h>
+#include <threadpool.h>
+#include <windows.h>
+
+
+/*
+ Threadpool API is not available on XP. We still want to compile a single
+ version on Windows, but use the latest functionality if available.
+ We cannot use threadpool functionality directly, since executable won't
+ start on XP and loader will complain about missing symbols.
+
+ We solve using the usual way it is done on Windows, i.e with dynamic loading.
+ We'll need to load a lot of function, and make this less painful with the
+ WEAK_SYMBOL macro below
+*/
+
+/*
+ WEAK_SYMBOL(return_type, function_name, argument_type1,..,argument_typeN)
+
+ Declare and load function pointer from kernel32. The name of the static
+ variable that holds the function pointer is my_<original function name>
+ This should be combined with
+ #define <original function name> my_<original function name>
+ so that one could use Widows APIs transparently, without worrying whether
+ they are present in a particular version or not.
+
+ Of course, prior to use of any function there should be a check for correct
+ Windows version, or check whether function pointer is not NULL.
+*/
+#define WEAK_SYMBOL(return_type, function, ...) \
+ typedef return_type (WINAPI *pFN_##function)(__VA_ARGS__); \
+ static pFN_##function my_##function = (pFN_##function) \
+ (GetProcAddress(GetModuleHandle("kernel32"),#function))
+
+WEAK_SYMBOL(VOID, CancelThreadpoolIo, PTP_IO);
+#define CancelThreadpoolIo my_CancelThreadpoolIo
+
+WEAK_SYMBOL(VOID, CloseThreadpool, PTP_POOL);
+#define CloseThreadpool my_CloseThreadpool
+
+WEAK_SYMBOL(VOID, CloseThreadpoolIo, PTP_IO);
+#define CloseThreadpoolIo my_CloseThreadpoolIo
+
+WEAK_SYMBOL(VOID, CloseThreadpoolTimer,PTP_TIMER);
+#define CloseThreadpoolTimer my_CloseThreadpoolTimer
+
+WEAK_SYMBOL(VOID, CloseThreadpoolWait,PTP_WAIT);
+#define CloseThreadpoolWait my_CloseThreadpoolWait
+
+WEAK_SYMBOL(PTP_POOL, CreateThreadpool,PVOID);
+#define CreateThreadpool my_CreateThreadpool
+
+WEAK_SYMBOL(PTP_IO, CreateThreadpoolIo, HANDLE, PTP_WIN32_IO_CALLBACK, PVOID ,
+ PTP_CALLBACK_ENVIRON);
+#define CreateThreadpoolIo my_CreateThreadpoolIo
+
+WEAK_SYMBOL(PTP_TIMER, CreateThreadpoolTimer, PTP_TIMER_CALLBACK ,
+ PVOID pv, PTP_CALLBACK_ENVIRON pcbe);
+#define CreateThreadpoolTimer my_CreateThreadpoolTimer
+
+WEAK_SYMBOL(PTP_WAIT, CreateThreadpoolWait, PTP_WAIT_CALLBACK, PVOID,
+ PTP_CALLBACK_ENVIRON);
+#define CreateThreadpoolWait my_CreateThreadpoolWait
+
+WEAK_SYMBOL(VOID, DisassociateCurrentThreadFromCallback, PTP_CALLBACK_INSTANCE);
+#define DisassociateCurrentThreadFromCallback my_DisassociateCurrentThreadFromCallback
+
+WEAK_SYMBOL(DWORD, FlsAlloc, PFLS_CALLBACK_FUNCTION);
+#define FlsAlloc my_FlsAlloc
+
+WEAK_SYMBOL(PVOID, FlsGetValue, DWORD);
+#define FlsGetValue my_FlsGetValue
+
+WEAK_SYMBOL(BOOL, FlsSetValue, DWORD, PVOID);
+#define FlsSetValue my_FlsSetValue
+
+WEAK_SYMBOL(VOID, SetThreadpoolThreadMaximum, PTP_POOL, DWORD);
+#define SetThreadpoolThreadMaximum my_SetThreadpoolThreadMaximum
+
+WEAK_SYMBOL(BOOL, SetThreadpoolThreadMinimum, PTP_POOL, DWORD);
+#define SetThreadpoolThreadMinimum my_SetThreadpoolThreadMinimum
+
+WEAK_SYMBOL(VOID, SetThreadpoolTimer, PTP_TIMER, PFILETIME,DWORD,DWORD);
+#define SetThreadpoolTimer my_SetThreadpoolTimer
+
+WEAK_SYMBOL(VOID, SetThreadpoolWait, PTP_WAIT,HANDLE,PFILETIME);
+#define SetThreadpoolWait my_SetThreadpoolWait
+
+WEAK_SYMBOL(VOID, StartThreadpoolIo, PTP_IO);
+#define StartThreadpoolIo my_StartThreadpoolIo
+
+WEAK_SYMBOL(VOID, WaitForThreadpoolIoCallbacks,PTP_IO, BOOL);
+#define WaitForThreadpoolIoCallbacks my_WaitForThreadpoolIoCallbacks
+
+WEAK_SYMBOL(VOID, WaitForThreadpoolTimerCallbacks, PTP_TIMER, BOOL);
+#define WaitForThreadpoolTimerCallbacks my_WaitForThreadpoolTimerCallbacks
+
+WEAK_SYMBOL(VOID, WaitForThreadpoolWaitCallbacks, PTP_WAIT, BOOL);
+#define WaitForThreadpoolWaitCallbacks my_WaitForThreadpoolWaitCallbacks
+
+WEAK_SYMBOL(BOOL, SetFileCompletionNotificationModes, HANDLE, UCHAR);
+#define SetFileCompletionNotificationModes my_SetFileCompletionNotificationModes
+
+WEAK_SYMBOL(BOOL, TrySubmitThreadpoolCallback, PTP_SIMPLE_CALLBACK pfns,
+ PVOID pv,PTP_CALLBACK_ENVIRON pcbe);
+#define TrySubmitThreadpoolCallback my_TrySubmitThreadpoolCallback
+
+WEAK_SYMBOL(PTP_WORK, CreateThreadpoolWork, PTP_WORK_CALLBACK pfnwk, PVOID pv,
+ PTP_CALLBACK_ENVIRON pcbe);
+#define CreateThreadpoolWork my_CreateThreadpoolWork
+
+WEAK_SYMBOL(VOID, SubmitThreadpoolWork,PTP_WORK pwk);
+#define SubmitThreadpoolWork my_SubmitThreadpoolWork
+
+WEAK_SYMBOL(VOID, CloseThreadpoolWork, PTP_WORK pwk);
+#define CloseThreadpoolWork my_CloseThreadpoolWork
+
+WEAK_SYMBOL(BOOL, CallbackMayRunLong, PTP_CALLBACK_INSTANCE pci);
+#define CallbackMayRunLong my_CallbackMayRunLong
+
+#if _MSC_VER >= 1600
+/* Stack size manipulation available only on Win7+ /declarations in VS10 */
+WEAK_SYMBOL(BOOL, SetThreadpoolStackInformation, PTP_POOL,
+ PTP_POOL_STACK_INFORMATION);
+#define SetThreadpoolStackInformation my_SetThreadpoolStackInformation
+#else /* _MSC_VER < 1600 */
+#define SetThreadpoolCallbackPriority(env,prio)
+typedef enum _TP_CALLBACK_PRIORITY {
+ TP_CALLBACK_PRIORITY_HIGH,
+ TP_CALLBACK_PRIORITY_NORMAL,
+ TP_CALLBACK_PRIORITY_LOW,
+ TP_CALLBACK_PRIORITY_INVALID
+} TP_CALLBACK_PRIORITY;
+#endif
+
+
+/* Log a warning */
+static void tp_log_warning(const char *msg, const char *fct)
+{
+ sql_print_warning("Threadpool: %s. %s failed (last error %d)",msg, fct,
+ GetLastError());
+}
+
+
+PTP_POOL pool;
+DWORD fls;
+
+static bool skip_completion_port_on_success = false;
+
+/*
+ Threadpool callbacks.
+
+ io_completion_callback - handle client request
+ timer_callback - handle wait timeout (kill connection)
+ shm_read_callback, shm_close_callback - shared memory stuff
+ login_callback - user login (submitted as threadpool work)
+
+*/
+
+static void CALLBACK timer_callback(PTP_CALLBACK_INSTANCE instance,
+ PVOID context, PTP_TIMER timer);
+
+static void CALLBACK io_completion_callback(PTP_CALLBACK_INSTANCE instance,
+ PVOID context, PVOID overlapped, ULONG io_result, ULONG_PTR nbytes, PTP_IO io);
+
+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 check_thread_init();
+
+/* Get current time as Windows time */
+static ulonglong now()
+{
+ ulonglong current_time;
+ GetSystemTimeAsFileTime((PFILETIME)&current_time);
+ return current_time;
+}
+
+/*
+ Connection structure, encapsulates THD + structures for asynchronous
+ IO and pool.
+*/
+
+struct connection_t
+{
+ THD *thd;
+ HANDLE handle;
+ OVERLAPPED overlapped;
+ /* absolute time for wait timeout (as Windows time) */
+ volatile ulonglong timeout;
+ TP_CALLBACK_ENVIRON callback_environ;
+ PTP_IO io;
+ PTP_TIMER timer;
+ PTP_WAIT shm_read;
+ /* Callback instance, used to inform treadpool about long callbacks */
+ PTP_CALLBACK_INSTANCE callback_instance;
+ bool logged_in;
+};
+
+
+void init_connection(connection_t *connection)
+{
+ connection->logged_in = false;
+ connection->handle= 0;
+ connection->io= 0;
+ connection->shm_read= 0;
+ connection->timer= 0;
+ connection->logged_in = false;
+ connection->timeout= ULONGLONG_MAX;
+ connection->callback_instance= 0;
+ memset(&connection->overlapped, 0, sizeof(OVERLAPPED));
+ InitializeThreadpoolEnvironment(&connection->callback_environ);
+ SetThreadpoolCallbackPool(&connection->callback_environ, pool);
+ connection->thd = 0;
+}
+
+
+int init_io(connection_t *connection, THD *thd)
+{
+ connection->thd= thd;
+ Vio *vio = thd->net.vio;
+ switch(vio->type)
+ {
+ case VIO_TYPE_SSL:
+ case VIO_TYPE_TCPIP:
+ connection->handle= (HANDLE)vio->sd;
+ break;
+ case VIO_TYPE_NAMEDPIPE:
+ connection->handle= (HANDLE)vio->hPipe;
+ break;
+ case VIO_TYPE_SHARED_MEMORY:
+ connection->shm_read= CreateThreadpoolWait(shm_read_callback, connection,
+ &connection->callback_environ);
+ if (!connection->shm_read)
+ {
+ tp_log_warning("Allocation failed", "CreateThreadpoolWait");
+ return -1;
+ }
+ break;
+ default:
+ abort();
+ }
+
+ if (connection->handle)
+ {
+ /* Performance tweaks (s. MSDN documentation)*/
+ UCHAR flags= FILE_SKIP_SET_EVENT_ON_HANDLE;
+ if (skip_completion_port_on_success)
+ {
+ flags |= FILE_SKIP_COMPLETION_PORT_ON_SUCCESS;
+ }
+ (void)SetFileCompletionNotificationModes(connection->handle, flags);
+
+ /* Assign io completion callback */
+ connection->io= CreateThreadpoolIo(connection->handle,
+ io_completion_callback, connection, &connection->callback_environ);
+ if(!connection->io)
+ {
+ tp_log_warning("Allocation failed", "CreateThreadpoolWait");
+ return -1;
+ }
+ }
+ connection->timer= CreateThreadpoolTimer(timer_callback, connection,
+ &connection->callback_environ);
+ if (!connection->timer)
+ {
+ tp_log_warning("Allocation failed", "CreateThreadpoolWait");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ Start asynchronous read
+*/
+int start_io(connection_t *connection, PTP_CALLBACK_INSTANCE instance)
+{
+ /* Start async read */
+ DWORD num_bytes = 0;
+ static char c;
+ WSABUF buf;
+ buf.buf= &c;
+ buf.len= 0;
+ DWORD flags=0;
+ DWORD last_error= 0;
+
+ int retval;
+ Vio *vio= connection->thd->net.vio;
+
+ if (vio->type == VIO_TYPE_SHARED_MEMORY)
+ {
+ SetThreadpoolWait(connection->shm_read, vio->event_server_wrote, NULL);
+ return 0;
+ }
+ if (vio->type == VIO_CLOSED)
+ {
+ return -1;
+ }
+
+ DBUG_ASSERT(vio->type == VIO_TYPE_TCPIP ||
+ vio->type == VIO_TYPE_SSL ||
+ vio->type == VIO_TYPE_NAMEDPIPE);
+
+ OVERLAPPED *overlapped= &connection->overlapped;
+ PTP_IO io= connection->io;
+ StartThreadpoolIo(io);
+
+ if (vio->type == VIO_TYPE_TCPIP || vio->type == VIO_TYPE_SSL)
+ {
+ /* Start async io (sockets). */
+ if (WSARecv(vio->sd , &buf, 1, &num_bytes, &flags,
+ overlapped, NULL) == 0)
+ {
+ retval= last_error= 0;
+ }
+ else
+ {
+ retval= -1;
+ last_error= WSAGetLastError();
+ }
+ }
+ else
+ {
+ /* Start async io (named pipe) */
+ if (ReadFile(vio->hPipe, &c, 0, &num_bytes ,overlapped))
+ {
+ retval= last_error= 0;
+ }
+ else
+ {
+ retval= -1;
+ last_error= GetLastError();
+ }
+ }
+
+ if (retval == 0 || last_error == ERROR_MORE_DATA)
+ {
+ /*
+ IO successfully finished (synchronously).
+ If skip_completion_port_on_success is set, we need to handle it right
+ here, because completion callback would not be executed by the pool.
+ */
+ if(skip_completion_port_on_success)
+ {
+ CancelThreadpoolIo(io);
+ io_completion_callback(instance, connection, overlapped, last_error,
+ num_bytes, io);
+ }
+ return 0;
+ }
+
+ if(last_error == ERROR_IO_PENDING)
+ {
+ return 0;
+ }
+
+ /* Some error occured */
+ CancelThreadpoolIo(io);
+ return -1;
+}
+
+
+int login(connection_t *connection, PTP_CALLBACK_INSTANCE instance)
+{
+ if (threadpool_add_connection(connection->thd) == 0
+ && init_io(connection, connection->thd) == 0
+ && start_io(connection, instance) == 0)
+ {
+ return 0;
+ }
+ return -1;
+}
+
+/*
+ Recalculate wait timeout, maybe reset timer.
+*/
+void set_wait_timeout(connection_t *connection, ulonglong old_timeout)
+{
+ ulonglong new_timeout = now() +
+ 10000000LL*connection->thd->variables.net_wait_timeout;
+
+ if (new_timeout < old_timeout)
+ {
+ SetThreadpoolTimer(connection->timer, (PFILETIME) &new_timeout, 0, 1000);
+ }
+ connection->timeout = new_timeout;
+}
+
+
+/* Connection destructor */
+void destroy_connection(connection_t *connection, PTP_CALLBACK_INSTANCE instance)
+{
+ if (instance)
+ DisassociateCurrentThreadFromCallback(instance);
+ if (connection->io)
+ {
+ WaitForThreadpoolIoCallbacks(connection->io, TRUE);
+ CloseThreadpoolIo(connection->io);
+ }
+
+ if(connection->shm_read)
+ {
+ WaitForThreadpoolWaitCallbacks(connection->shm_read, TRUE);
+ CloseThreadpoolWait(connection->shm_read);
+ }
+
+ if(connection->timer)
+ {
+ SetThreadpoolTimer(connection->timer, 0, 0, 0);
+ WaitForThreadpoolTimerCallbacks(connection->timer, TRUE);
+ CloseThreadpoolTimer(connection->timer);
+ }
+
+ if (connection->thd)
+ {
+ threadpool_remove_connection(connection->thd);
+ }
+
+ DestroyThreadpoolEnvironment(&connection->callback_environ);
+}
+
+
+
+/*
+ This function should be called first whenever a callback is invoked in the
+ threadpool, does my_thread_init() if not yet done
+*/
+extern ulong thread_created;
+static void check_thread_init()
+{
+ if (FlsGetValue(fls) == NULL)
+ {
+ FlsSetValue(fls, (void *)1);
+ thread_created++;
+ InterlockedIncrement((volatile long *)&tp_stats.num_worker_threads);
+ }
+}
+
+
+/*
+ Decrement number of threads when a thread exits .
+ On Windows, FlsAlloc() provides the thread destruction callbacks.
+*/
+static VOID WINAPI thread_destructor(void *data)
+{
+ if(data)
+ {
+ InterlockedDecrement((volatile long *)&tp_stats.num_worker_threads);
+ }
+}
+
+
+/* Scheduler callback : init */
+bool tp_init(void)
+{
+ fls= FlsAlloc(thread_destructor);
+ pool= CreateThreadpool(NULL);
+ if(!pool)
+ {
+ sql_print_error("Can't create threadpool. "
+ "CreateThreadpool() failed with %d. Likely cause is memory pressure",
+ GetLastError());
+ exit(1);
+ }
+
+ if (threadpool_max_threads)
+ {
+ SetThreadpoolThreadMaximum(pool,threadpool_max_threads);
+ }
+
+ if (threadpool_min_threads)
+ {
+ if (!SetThreadpoolThreadMinimum(pool, threadpool_min_threads))
+ {
+ tp_log_warning( "Can't set threadpool minimum threads",
+ "SetThreadpoolThreadMinimum");
+ }
+ }
+
+ /*
+ Control stack size (OS must be Win7 or later, plus corresponding SDK)
+ */
+#if _MSC_VER >=1600
+ if (SetThreadpoolStackInformation)
+ {
+ TP_POOL_STACK_INFORMATION stackinfo;
+ stackinfo.StackCommit = 0;
+ stackinfo.StackReserve = (SIZE_T)my_thread_stack_size;
+ if (!SetThreadpoolStackInformation(pool, &stackinfo))
+ {
+ tp_log_warning("Can't set threadpool stack size",
+ "SetThreadpoolStackInformation");
+ }
+ }
+#endif
+
+ return 0;
+}
+
+
+/**
+ Scheduler callback : Destroy the scheduler.
+*/
+void tp_end(void)
+{
+ if(pool)
+ {
+ SetThreadpoolThreadMaximum(pool, 0);
+ CloseThreadpool(pool);
+ }
+}
+
+
+/*
+ Handle read completion/notification.
+*/
+static VOID CALLBACK io_completion_callback(PTP_CALLBACK_INSTANCE instance,
+ PVOID context, PVOID overlapped, ULONG io_result, ULONG_PTR nbytes, PTP_IO io)
+{
+ if(instance)
+ {
+ check_thread_init();
+ }
+
+ connection_t *connection = (connection_t*)context;
+
+ if (io_result != ERROR_SUCCESS)
+ goto error;
+
+ THD *thd= connection->thd;
+ ulonglong old_timeout = connection->timeout;
+ connection->timeout = ULONGLONG_MAX;
+ connection->callback_instance= instance;
+ if (threadpool_process_request(connection->thd))
+ goto error;
+
+ set_wait_timeout(connection, old_timeout);
+ if(start_io(connection, instance))
+ goto error;
+
+ return;
+
+error:
+ /* Some error has occured. */
+
+ destroy_connection(connection, instance);
+ free(connection);
+}
+
+
+/* Simple callback for login */
+static void CALLBACK login_callback(PTP_CALLBACK_INSTANCE instance,
+ PVOID context, PTP_WORK work)
+{
+ if(instance)
+ {
+ check_thread_init();
+ }
+
+ connection_t *connection =(connection_t *)context;
+ if (login(connection, instance) != 0)
+ {
+ destroy_connection(connection, instance);
+ free(connection);
+ }
+}
+
+/*
+ Timer callback.
+ Invoked when connection times out (wait_timeout)
+*/
+static VOID CALLBACK timer_callback(PTP_CALLBACK_INSTANCE instance,
+ PVOID parameter, PTP_TIMER timer)
+{
+ check_thread_init();
+
+ connection_t *con= (connection_t*)parameter;
+ ulonglong timeout= con->timeout;
+
+ if (timeout <= now())
+ {
+ con->thd->killed = KILL_CONNECTION;
+ if(con->thd->net.vio)
+ vio_shutdown(con->thd->net.vio, SD_BOTH);
+ }
+ else if(timeout != ULONGLONG_MAX)
+ {
+ /*
+ Reset timer.
+ There is a tiny possibility of a race condition, since the value of timeout
+ could have changed to smaller value in the thread doing io callback.
+
+ Given the relative unimportance of the wait timeout, we accept race
+ condition.
+ */
+ SetThreadpoolTimer(timer, (PFILETIME)&timeout, 0, 1000);
+ }
+}
+
+
+/*
+ Shared memory read callback.
+ Invoked when read event is set on connection.
+*/
+static void CALLBACK shm_read_callback(PTP_CALLBACK_INSTANCE instance,
+ PVOID context, PTP_WAIT wait,TP_WAIT_RESULT wait_result)
+{
+ connection_t *con= (connection_t *)context;
+ /* Disarm wait. */
+ SetThreadpoolWait(wait, NULL, NULL);
+
+ /*
+ This is an autoreset event, and one wakeup is eaten already by threadpool,
+ and the current state is "not set". Thus we need to reset the event again,
+ or vio_read will hang.
+ */
+ HANDLE h = con->thd->net.vio->event_server_wrote;
+ SetEvent(h);
+ io_completion_callback(instance, context, NULL, 0, 0 , 0);
+}
+
+
+/*
+ Notify the thread pool about a new connection.
+ NOTE: LOCK_thread_count is locked on entry. This function must unlock it.
+*/
+void tp_add_connection(THD *thd)
+{
+ threads.append(thd);
+ mysql_mutex_unlock(&LOCK_thread_count);
+
+ connection_t *con = (connection_t *)malloc(sizeof(connection_t));
+ if(!con)
+ {
+ tp_log_warning("Allocation failed", "tp_add_connection");
+ threadpool_remove_connection(thd);
+ return;
+ }
+
+ init_connection(con);
+ con->thd= thd;
+ thd->event_scheduler.data= con;
+
+ /* Try to login asynchronously, using threads in the pool */
+ PTP_WORK wrk = CreateThreadpoolWork(login_callback,con, &con->callback_environ);
+ if (wrk)
+ {
+ SubmitThreadpoolWork(wrk);
+ CloseThreadpoolWork(wrk);
+ }
+ else
+ {
+ /* Likely memory pressure */
+ login_callback(NULL, con, NULL); /* deletes connection if something goes wrong */
+ }
+}
+
+
+/**
+ Sets the number of idle threads the thread pool maintains in anticipation of new
+ requests.
+*/
+void tp_set_min_threads(uint val)
+{
+ if (pool)
+ SetThreadpoolThreadMinimum(pool, val);
+}
+
+void tp_set_max_threads(uint val)
+{
+ if (pool)
+ SetThreadpoolThreadMaximum(pool, val);
+}
+
+void tp_wait_begin(THD *thd, int type)
+{
+ DBUG_ASSERT(thd);
+
+ /*
+ Signal to the threadpool whenever callback can run long. Currently, binlog
+ waits are a good candidate, its waits are really long
+ */
+ if (type == THD_WAIT_BINLOG)
+ {
+ connection_t *connection= (connection_t *)thd->event_scheduler.data;
+ if(connection && connection->callback_instance)
+ {
+ CallbackMayRunLong(connection->callback_instance);
+ /*
+ Reset instance, to avoid calling CallbackMayRunLong twice within
+ the same callback (it is an error according to docs).
+ */
+ connection->callback_instance= 0;
+ }
+ }
+}
+
+void tp_wait_end(THD *thd)
+{
+ /* Do we need to do anything ? */
+}
+
+
+/**
+ Number of idle threads in pool.
+ This info is not available in Windows implementation,
+ thus function always returns 0.
+*/
+int tp_get_idle_thread_count()
+{
+ return 0;
+}
+
diff --git a/sql/transaction.cc b/sql/transaction.cc
new file mode 100644
index 00000000000..ae38e920a1d
--- /dev/null
+++ b/sql/transaction.cc
@@ -0,0 +1,835 @@
+/* Copyright (c) 2000, 2013, 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 Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+
+#ifdef USE_PRAGMA_IMPLEMENTATION
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "sql_priv.h"
+#include "transaction.h"
+#include "rpl_handler.h"
+#include "debug_sync.h" // DEBUG_SYNC
+
+/* Conditions under which the transaction state must not change. */
+static bool trans_check(THD *thd)
+{
+ enum xa_states xa_state= thd->transaction.xid_state.xa_state;
+ DBUG_ENTER("trans_check");
+
+ /*
+ Always commit statement transaction before manipulating with
+ the normal one.
+ */
+ DBUG_ASSERT(thd->transaction.stmt.is_empty());
+
+ if (unlikely(thd->in_sub_stmt))
+ my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
+ if (xa_state != XA_NOTR)
+ my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
+ else
+ DBUG_RETURN(FALSE);
+
+ DBUG_RETURN(TRUE);
+}
+
+
+/**
+ Mark a XA transaction as rollback-only if the RM unilaterally
+ rolled back the transaction branch.
+
+ @note If a rollback was requested by the RM, this function sets
+ the appropriate rollback error code and transits the state
+ to XA_ROLLBACK_ONLY.
+
+ @return TRUE if transaction was rolled back or if the transaction
+ state is XA_ROLLBACK_ONLY. FALSE otherwise.
+*/
+static bool xa_trans_rolled_back(XID_STATE *xid_state)
+{
+ if (xid_state->rm_error)
+ {
+ switch (xid_state->rm_error) {
+ case ER_LOCK_WAIT_TIMEOUT:
+ my_error(ER_XA_RBTIMEOUT, MYF(0));
+ break;
+ case ER_LOCK_DEADLOCK:
+ my_error(ER_XA_RBDEADLOCK, MYF(0));
+ break;
+ default:
+ my_error(ER_XA_RBROLLBACK, MYF(0));
+ }
+ xid_state->xa_state= XA_ROLLBACK_ONLY;
+ }
+
+ return (xid_state->xa_state == XA_ROLLBACK_ONLY);
+}
+
+
+/**
+ Rollback the active XA transaction.
+
+ @note Resets rm_error before calling ha_rollback(), so
+ the thd->transaction.xid structure gets reset
+ by ha_rollback() / THD::transaction::cleanup().
+
+ @return TRUE if the rollback failed, FALSE otherwise.
+*/
+
+static bool xa_trans_force_rollback(THD *thd)
+{
+ /*
+ We must reset rm_error before calling ha_rollback(),
+ so thd->transaction.xid structure gets reset
+ by ha_rollback()/THD::transaction::cleanup().
+ */
+ thd->transaction.xid_state.rm_error= 0;
+ if (ha_rollback_trans(thd, true))
+ {
+ my_error(ER_XAER_RMERR, MYF(0));
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ Begin a new transaction.
+
+ @note Beginning a transaction implicitly commits any current
+ transaction and releases existing locks.
+
+ @param thd Current thread
+ @param flags Transaction flags
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_begin(THD *thd, uint flags)
+{
+ int res= FALSE;
+ DBUG_ENTER("trans_begin");
+
+ if (trans_check(thd))
+ DBUG_RETURN(TRUE);
+
+ thd->locked_tables_list.unlock_locked_tables(thd);
+
+ DBUG_ASSERT(!thd->locked_tables_mode);
+
+ if (thd->in_multi_stmt_transaction_mode() ||
+ (thd->variables.option_bits & OPTION_TABLE_LOCK))
+ {
+ thd->variables.option_bits&= ~OPTION_TABLE_LOCK;
+ thd->server_status&= ~SERVER_STATUS_IN_TRANS;
+ res= test(ha_commit_trans(thd, TRUE));
+ }
+
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
+ thd->transaction.all.modified_non_trans_table= FALSE;
+
+ if (res)
+ DBUG_RETURN(TRUE);
+
+ /*
+ Release transactional metadata locks only after the
+ transaction has been committed.
+ */
+ thd->mdl_context.release_transactional_locks();
+
+ thd->variables.option_bits|= OPTION_BEGIN;
+ thd->server_status|= SERVER_STATUS_IN_TRANS;
+
+ if (flags & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT)
+ res= ha_start_consistent_snapshot(thd);
+
+ DBUG_RETURN(test(res));
+}
+
+
+/**
+ Commit the current transaction, making its changes permanent.
+
+ @param thd Current thread
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_commit(THD *thd)
+{
+ int res;
+ DBUG_ENTER("trans_commit");
+
+ if (trans_check(thd))
+ DBUG_RETURN(TRUE);
+
+ thd->server_status&= ~SERVER_STATUS_IN_TRANS;
+ res= ha_commit_trans(thd, TRUE);
+ if (res)
+ /*
+ if res is non-zero, then ha_commit_trans has rolled back the
+ transaction, so the hooks for rollback will be called.
+ */
+ RUN_HOOK(transaction, after_rollback, (thd, FALSE));
+ else
+ RUN_HOOK(transaction, after_commit, (thd, FALSE));
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
+ thd->transaction.all.modified_non_trans_table= FALSE;
+ thd->lex->start_transaction_opt= 0;
+
+ DBUG_RETURN(test(res));
+}
+
+
+/**
+ Implicitly commit the current transaction.
+
+ @note A implicit commit does not releases existing table locks.
+
+ @param thd Current thread
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_commit_implicit(THD *thd)
+{
+ bool res= FALSE;
+ DBUG_ENTER("trans_commit_implicit");
+
+ if (trans_check(thd))
+ DBUG_RETURN(TRUE);
+
+ if (thd->in_multi_stmt_transaction_mode() ||
+ (thd->variables.option_bits & OPTION_TABLE_LOCK))
+ {
+ /* Safety if one did "drop table" on locked tables */
+ if (!thd->locked_tables_mode)
+ thd->variables.option_bits&= ~OPTION_TABLE_LOCK;
+ thd->server_status&= ~SERVER_STATUS_IN_TRANS;
+ res= test(ha_commit_trans(thd, TRUE));
+ }
+
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
+ thd->transaction.all.modified_non_trans_table= FALSE;
+
+ /*
+ Upon implicit commit, reset the current transaction
+ isolation level. We do not care about
+ @@session.completion_type since it's documented
+ to not have any effect on implicit commit.
+ */
+ thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
+
+ DBUG_RETURN(res);
+}
+
+
+/**
+ Rollback the current transaction, canceling its changes.
+
+ @param thd Current thread
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_rollback(THD *thd)
+{
+ int res;
+ DBUG_ENTER("trans_rollback");
+
+ if (trans_check(thd))
+ DBUG_RETURN(TRUE);
+
+ thd->server_status&= ~SERVER_STATUS_IN_TRANS;
+ res= ha_rollback_trans(thd, TRUE);
+ RUN_HOOK(transaction, after_rollback, (thd, FALSE));
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
+ thd->transaction.all.modified_non_trans_table= FALSE;
+ thd->lex->start_transaction_opt= 0;
+
+ DBUG_RETURN(test(res));
+}
+
+
+/**
+ Implicitly rollback the current transaction, typically
+ after deadlock was discovered.
+
+ @param thd Current thread
+
+ @retval False Success
+ @retval True Failure
+
+ @note ha_rollback_low() which is indirectly called by this
+ function will mark XA transaction for rollback by
+ setting appropriate RM error status if there was
+ transaction rollback request.
+*/
+
+bool trans_rollback_implicit(THD *thd)
+{
+ int res;
+ DBUG_ENTER("trans_rollback_implict");
+
+ /*
+ Always commit/rollback statement transaction before manipulating
+ with the normal one.
+ Don't perform rollback in the middle of sub-statement, wait till
+ its end.
+ */
+ DBUG_ASSERT(thd->transaction.stmt.is_empty() && !thd->in_sub_stmt);
+
+ thd->server_status&= ~SERVER_STATUS_IN_TRANS;
+ DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
+ res= ha_rollback_trans(thd, true);
+ /*
+ We don't reset OPTION_BEGIN flag below to simulate implicit start
+ of new transacton in @@autocommit=1 mode. This is necessary to
+ preserve backward compatibility.
+ */
+ thd->variables.option_bits&= ~(OPTION_KEEP_LOG);
+ thd->transaction.all.modified_non_trans_table= false;
+
+ /* Rollback should clear transaction_rollback_request flag. */
+ DBUG_ASSERT(! thd->transaction_rollback_request);
+
+ DBUG_RETURN(test(res));
+}
+
+
+/**
+ Commit the single statement transaction.
+
+ @note Note that if the autocommit is on, then the following call
+ inside InnoDB will commit or rollback the whole transaction
+ (= the statement). The autocommit mechanism built into InnoDB
+ is based on counting locks, but if the user has used LOCK
+ TABLES then that mechanism does not know to do the commit.
+
+ @param thd Current thread
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_commit_stmt(THD *thd)
+{
+ DBUG_ENTER("trans_commit_stmt");
+ int res= FALSE;
+ /*
+ We currently don't invoke commit/rollback at end of
+ a sub-statement. In future, we perhaps should take
+ a savepoint for each nested statement, and release the
+ savepoint when statement has succeeded.
+ */
+ DBUG_ASSERT(! thd->in_sub_stmt);
+
+ if (thd->transaction.stmt.ha_list)
+ {
+ res= ha_commit_trans(thd, FALSE);
+ if (! thd->in_active_multi_stmt_transaction())
+ thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
+ }
+
+ if (res)
+ /*
+ if res is non-zero, then ha_commit_trans has rolled back the
+ transaction, so the hooks for rollback will be called.
+ */
+ RUN_HOOK(transaction, after_rollback, (thd, FALSE));
+ else
+ RUN_HOOK(transaction, after_commit, (thd, FALSE));
+
+ thd->transaction.stmt.reset();
+
+ DBUG_RETURN(test(res));
+}
+
+
+/**
+ Rollback the single statement transaction.
+
+ @param thd Current thread
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+bool trans_rollback_stmt(THD *thd)
+{
+ DBUG_ENTER("trans_rollback_stmt");
+
+ /*
+ We currently don't invoke commit/rollback at end of
+ a sub-statement. In future, we perhaps should take
+ a savepoint for each nested statement, and release the
+ savepoint when statement has succeeded.
+ */
+ DBUG_ASSERT(! thd->in_sub_stmt);
+
+ if (thd->transaction.stmt.ha_list)
+ {
+ ha_rollback_trans(thd, FALSE);
+ if (! thd->in_active_multi_stmt_transaction())
+ thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
+ }
+
+ RUN_HOOK(transaction, after_rollback, (thd, FALSE));
+
+ thd->transaction.stmt.reset();
+
+ DBUG_RETURN(FALSE);
+}
+
+/* Find a named savepoint in the current transaction. */
+static SAVEPOINT **
+find_savepoint(THD *thd, LEX_STRING name)
+{
+ SAVEPOINT **sv= &thd->transaction.savepoints;
+
+ while (*sv)
+ {
+ if (my_strnncoll(system_charset_info, (uchar *) name.str, name.length,
+ (uchar *) (*sv)->name, (*sv)->length) == 0)
+ break;
+ sv= &(*sv)->prev;
+ }
+
+ return sv;
+}
+
+
+/**
+ Set a named transaction savepoint.
+
+ @param thd Current thread
+ @param name Savepoint name
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_savepoint(THD *thd, LEX_STRING name)
+{
+ SAVEPOINT **sv, *newsv;
+ DBUG_ENTER("trans_savepoint");
+
+ if (!(thd->in_multi_stmt_transaction_mode() || thd->in_sub_stmt) ||
+ !opt_using_transactions)
+ DBUG_RETURN(FALSE);
+
+ enum xa_states xa_state= thd->transaction.xid_state.xa_state;
+ if (xa_state != XA_NOTR && xa_state != XA_ACTIVE)
+ {
+ my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
+ DBUG_RETURN(TRUE);
+ }
+
+ sv= find_savepoint(thd, name);
+
+ if (*sv) /* old savepoint of the same name exists */
+ {
+ newsv= *sv;
+ ha_release_savepoint(thd, *sv);
+ *sv= (*sv)->prev;
+ }
+ else if ((newsv= (SAVEPOINT *) alloc_root(&thd->transaction.mem_root,
+ savepoint_alloc_size)) == NULL)
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
+ newsv->name= strmake_root(&thd->transaction.mem_root, name.str, name.length);
+ newsv->length= 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))
+ DBUG_RETURN(TRUE);
+
+ newsv->prev= thd->transaction.savepoints;
+ thd->transaction.savepoints= newsv;
+
+ /*
+ Remember locks acquired before the savepoint was set.
+ They are used as a marker to only release locks acquired after
+ the setting of this savepoint.
+ Note: this works just fine if we're under LOCK TABLES,
+ since mdl_savepoint() is guaranteed to be beyond
+ the last locked table. This allows to release some
+ locks acquired during LOCK TABLES.
+ */
+ newsv->mdl_savepoint= thd->mdl_context.mdl_savepoint();
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Rollback a transaction to the named savepoint.
+
+ @note Modifications that the current transaction made to
+ rows after the savepoint was set are undone in the
+ rollback.
+
+ @note Savepoints that were set at a later time than the
+ named savepoint are deleted.
+
+ @param thd Current thread
+ @param name Savepoint name
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name)
+{
+ int res= FALSE;
+ SAVEPOINT *sv= *find_savepoint(thd, name);
+ DBUG_ENTER("trans_rollback_to_savepoint");
+
+ if (sv == NULL)
+ {
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", name.str);
+ DBUG_RETURN(TRUE);
+ }
+
+ enum xa_states xa_state= thd->transaction.xid_state.xa_state;
+ if (xa_state != XA_NOTR)
+ {
+ my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
+ DBUG_RETURN(TRUE);
+ }
+
+ if (ha_rollback_to_savepoint(thd, sv))
+ res= TRUE;
+ else if (((thd->variables.option_bits & OPTION_KEEP_LOG) ||
+ thd->transaction.all.modified_non_trans_table) &&
+ !thd->slave_thread)
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARNING_NOT_COMPLETE_ROLLBACK,
+ ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
+
+ thd->transaction.savepoints= sv;
+
+ /*
+ Release metadata locks that were acquired during this savepoint unit
+ unless binlogging is on. Releasing locks with binlogging on can break
+ replication as it allows other connections to drop these tables before
+ rollback to savepoint is written to the binlog.
+ */
+ bool binlog_on= mysql_bin_log.is_open() && thd->variables.sql_log_bin;
+ if (!res && !binlog_on)
+ thd->mdl_context.rollback_to_savepoint(sv->mdl_savepoint);
+
+ DBUG_RETURN(test(res));
+}
+
+
+/**
+ Remove the named savepoint from the set of savepoints of
+ the current transaction.
+
+ @note No commit or rollback occurs. It is an error if the
+ savepoint does not exist.
+
+ @param thd Current thread
+ @param name Savepoint name
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_release_savepoint(THD *thd, LEX_STRING name)
+{
+ int res= FALSE;
+ SAVEPOINT *sv= *find_savepoint(thd, name);
+ DBUG_ENTER("trans_release_savepoint");
+
+ if (sv == NULL)
+ {
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", name.str);
+ DBUG_RETURN(TRUE);
+ }
+
+ if (ha_release_savepoint(thd, sv))
+ res= TRUE;
+
+ thd->transaction.savepoints= sv->prev;
+
+ DBUG_RETURN(test(res));
+}
+
+
+/**
+ Starts an XA transaction with the given xid value.
+
+ @param thd Current thread
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_xa_start(THD *thd)
+{
+ enum xa_states xa_state= thd->transaction.xid_state.xa_state;
+ DBUG_ENTER("trans_xa_start");
+
+ if (xa_state == XA_IDLE && thd->lex->xa_opt == XA_RESUME)
+ {
+ bool not_equal= !thd->transaction.xid_state.xid.eq(thd->lex->xid);
+ if (not_equal)
+ my_error(ER_XAER_NOTA, MYF(0));
+ else
+ thd->transaction.xid_state.xa_state= XA_ACTIVE;
+ DBUG_RETURN(not_equal);
+ }
+
+ /* TODO: JOIN is not supported yet. */
+ if (thd->lex->xa_opt != XA_NONE)
+ my_error(ER_XAER_INVAL, MYF(0));
+ else if (xa_state != XA_NOTR)
+ my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
+ else if (thd->locked_tables_mode || thd->in_active_multi_stmt_transaction())
+ my_error(ER_XAER_OUTSIDE, MYF(0));
+ else if (!trans_begin(thd))
+ {
+ DBUG_ASSERT(thd->transaction.xid_state.xid.is_null());
+ thd->transaction.xid_state.xa_state= XA_ACTIVE;
+ thd->transaction.xid_state.rm_error= 0;
+ thd->transaction.xid_state.xid.set(thd->lex->xid);
+ if (xid_cache_insert(&thd->transaction.xid_state))
+ {
+ thd->transaction.xid_state.xa_state= XA_NOTR;
+ thd->transaction.xid_state.xid.null();
+ trans_rollback(thd);
+ DBUG_RETURN(true);
+ }
+ DBUG_RETURN(FALSE);
+ }
+
+ DBUG_RETURN(TRUE);
+}
+
+
+/**
+ Put a XA transaction in the IDLE state.
+
+ @param thd Current thread
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_xa_end(THD *thd)
+{
+ DBUG_ENTER("trans_xa_end");
+
+ /* TODO: SUSPEND and FOR MIGRATE are not supported yet. */
+ if (thd->lex->xa_opt != XA_NONE)
+ my_error(ER_XAER_INVAL, MYF(0));
+ else if (thd->transaction.xid_state.xa_state != XA_ACTIVE)
+ my_error(ER_XAER_RMFAIL, MYF(0),
+ xa_state_names[thd->transaction.xid_state.xa_state]);
+ else if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
+ my_error(ER_XAER_NOTA, MYF(0));
+ else if (!xa_trans_rolled_back(&thd->transaction.xid_state))
+ thd->transaction.xid_state.xa_state= XA_IDLE;
+
+ DBUG_RETURN(thd->is_error() ||
+ thd->transaction.xid_state.xa_state != XA_IDLE);
+}
+
+
+/**
+ Put a XA transaction in the PREPARED state.
+
+ @param thd Current thread
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_xa_prepare(THD *thd)
+{
+ DBUG_ENTER("trans_xa_prepare");
+
+ if (thd->transaction.xid_state.xa_state != XA_IDLE)
+ my_error(ER_XAER_RMFAIL, MYF(0),
+ xa_state_names[thd->transaction.xid_state.xa_state]);
+ else if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
+ my_error(ER_XAER_NOTA, MYF(0));
+ else if (ha_prepare(thd))
+ {
+ xid_cache_delete(&thd->transaction.xid_state);
+ thd->transaction.xid_state.xa_state= XA_NOTR;
+ my_error(ER_XA_RBROLLBACK, MYF(0));
+ }
+ else
+ thd->transaction.xid_state.xa_state= XA_PREPARED;
+
+ DBUG_RETURN(thd->is_error() ||
+ thd->transaction.xid_state.xa_state != XA_PREPARED);
+}
+
+
+/**
+ Commit and terminate the a XA transaction.
+
+ @param thd Current thread
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_xa_commit(THD *thd)
+{
+ bool res= TRUE;
+ enum xa_states xa_state= thd->transaction.xid_state.xa_state;
+ DBUG_ENTER("trans_xa_commit");
+
+ if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
+ {
+ /*
+ xid_state.in_thd is always true beside of xa recovery procedure.
+ Note, that there is no race condition here between xid_cache_search
+ and xid_cache_delete, since we always delete our own XID
+ (thd->lex->xid == thd->transaction.xid_state.xid).
+ The only case when thd->lex->xid != thd->transaction.xid_state.xid
+ and xid_state->in_thd == 0 is in the function
+ xa_cache_insert(XID, xa_states), which is called before starting
+ client connections, and thus is always single-threaded.
+ */
+ XID_STATE *xs= xid_cache_search(thd->lex->xid);
+ res= !xs || xs->in_thd;
+ if (res)
+ my_error(ER_XAER_NOTA, MYF(0));
+ else
+ {
+ res= xa_trans_rolled_back(xs);
+ ha_commit_or_rollback_by_xid(thd->lex->xid, !res);
+ xid_cache_delete(xs);
+ }
+ DBUG_RETURN(res);
+ }
+
+ if (xa_trans_rolled_back(&thd->transaction.xid_state))
+ {
+ xa_trans_force_rollback(thd);
+ res= thd->is_error();
+ }
+ else if (xa_state == XA_IDLE && thd->lex->xa_opt == XA_ONE_PHASE)
+ {
+ int r= ha_commit_trans(thd, TRUE);
+ if ((res= test(r)))
+ my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0));
+ }
+ else if (xa_state == XA_PREPARED && thd->lex->xa_opt == XA_NONE)
+ {
+ MDL_request mdl_request;
+
+ /*
+ Acquire metadata lock which will ensure that COMMIT is blocked
+ by active FLUSH TABLES WITH READ LOCK (and vice versa COMMIT in
+ progress blocks FTWRL).
+
+ We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
+ */
+ mdl_request.init(MDL_key::COMMIT, "", "", MDL_INTENTION_EXCLUSIVE,
+ MDL_TRANSACTION);
+
+ if (thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout))
+ {
+ ha_rollback_trans(thd, TRUE);
+ my_error(ER_XAER_RMERR, MYF(0));
+ }
+ else
+ {
+ DEBUG_SYNC(thd, "trans_xa_commit_after_acquire_commit_lock");
+
+ res= test(ha_commit_one_phase(thd, 1));
+ if (res)
+ my_error(ER_XAER_RMERR, MYF(0));
+ }
+ }
+ else
+ {
+ my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
+ DBUG_RETURN(TRUE);
+ }
+
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
+ thd->transaction.all.modified_non_trans_table= FALSE;
+ thd->server_status&= ~SERVER_STATUS_IN_TRANS;
+ xid_cache_delete(&thd->transaction.xid_state);
+ thd->transaction.xid_state.xa_state= XA_NOTR;
+
+ DBUG_RETURN(res);
+}
+
+
+/**
+ Roll back and terminate a XA transaction.
+
+ @param thd Current thread
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_xa_rollback(THD *thd)
+{
+ bool res= TRUE;
+ enum xa_states xa_state= thd->transaction.xid_state.xa_state;
+ DBUG_ENTER("trans_xa_rollback");
+
+ if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
+ {
+ XID_STATE *xs= xid_cache_search(thd->lex->xid);
+ if (!xs || xs->in_thd)
+ my_error(ER_XAER_NOTA, MYF(0));
+ else
+ {
+ xa_trans_rolled_back(xs);
+ ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
+ xid_cache_delete(xs);
+ }
+ DBUG_RETURN(thd->stmt_da->is_error());
+ }
+
+ if (xa_state != XA_IDLE && xa_state != XA_PREPARED && xa_state != XA_ROLLBACK_ONLY)
+ {
+ my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
+ DBUG_RETURN(TRUE);
+ }
+
+ res= xa_trans_force_rollback(thd);
+
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
+ thd->transaction.all.modified_non_trans_table= FALSE;
+ thd->server_status&= ~SERVER_STATUS_IN_TRANS;
+ xid_cache_delete(&thd->transaction.xid_state);
+ thd->transaction.xid_state.xa_state= XA_NOTR;
+
+ DBUG_RETURN(res);
+}
diff --git a/sql/transaction.h b/sql/transaction.h
new file mode 100644
index 00000000000..54b25f1de2a
--- /dev/null
+++ b/sql/transaction.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2008, 2013, 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 TRANSACTION_H
+#define TRANSACTION_H
+
+#ifdef USE_PRAGMA_INTERFACE
+#pragma interface /* gcc class implementation */
+#endif
+
+#include <my_global.h>
+#include <m_string.h>
+
+class THD;
+
+bool trans_begin(THD *thd, uint flags= 0);
+bool trans_commit(THD *thd);
+bool trans_commit_implicit(THD *thd);
+bool trans_rollback(THD *thd);
+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_xa_start(THD *thd);
+bool trans_xa_end(THD *thd);
+bool trans_xa_prepare(THD *thd);
+bool trans_xa_commit(THD *thd);
+bool trans_xa_rollback(THD *thd);
+
+#endif /* TRANSACTION_H */
diff --git a/sql/tzfile.h b/sql/tzfile.h
index fa459953f36..4feba612b36 100644
--- a/sql/tzfile.h
+++ b/sql/tzfile.h
@@ -1,4 +1,8 @@
-/* Copyright (c) 2004, 2007 MySQL AB
+#ifndef TZFILE_INCLUDED
+#define TZFILE_INCLUDED
+
+/* Copyright (c) 2004, 2006, 2007 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
@@ -11,7 +15,7 @@
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 */
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
This file is based on public domain code from ftp://elsie.ncih.nist.gov/
@@ -134,3 +138,5 @@ struct tzhead {
*/
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+
+#endif
diff --git a/sql/tztime.cc b/sql/tztime.cc
index 857646337d4..da23d6fc8c2 100644
--- a/sql/tztime.cc
+++ b/sql/tztime.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2004, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2004, 2010, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,8 +12,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
Most of the following code and structures were derived from
@@ -22,7 +21,7 @@
*/
/*
- We should not include mysql_priv.h in mysql_tzinfo_to_sql utility since
+ We should not include sql_priv.h in mysql_tzinfo_to_sql utility since
it creates unsolved link dependencies on some platforms.
*/
@@ -32,16 +31,26 @@
#include <my_global.h>
#if !defined(TZINFO2SQL) && !defined(TESTTIME)
-#include "mysql_priv.h"
+#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 "tzfile.h"
#include <m_string.h>
#include <my_dir.h>
+#include <mysql/psi/mysql_file.h>
+#include "lock.h" // MYSQL_LOCK_IGNORE_FLUSH,
+ // MYSQL_LOCK_IGNORE_TIMEOUT
/*
Now we don't use abbreviations in server but we will do this in future.
@@ -56,6 +65,8 @@
#endif /* !defined(DBUG_OFF) */
#endif /* defined(TZINFO2SQL) || defined(TESTTIME) */
+#define PROGRAM_VERSION "1.1"
+
/* Structure describing local time type (e.g. Moscow summer time (MSD)) */
typedef struct ttinfo
{
@@ -158,9 +169,9 @@ tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
uchar *p;
int read_from_file;
uint i;
- FILE *file;
+ MYSQL_FILE *file;
- if (!(file= my_fopen(name, O_RDONLY|O_BINARY, MYF(MY_WME))))
+ if (!(file= mysql_file_fopen(0, name, O_RDONLY|O_BINARY, MYF(MY_WME))))
return 1;
{
union
@@ -177,9 +188,9 @@ tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
uint ttisgmtcnt;
char *tzinfo_buf;
- read_from_file= my_fread(file, u.buf, sizeof(u.buf), MYF(MY_WME));
+ read_from_file= mysql_file_fread(file, u.buf, sizeof(u.buf), MYF(MY_WME));
- if (my_fclose(file, MYF(MY_WME)) != 0)
+ if (mysql_file_fclose(file, MYF(MY_WME)) != 0)
return 1;
if (read_from_file < (int)sizeof(struct tzhead))
@@ -1346,8 +1357,7 @@ Time_zone_offset::Time_zone_offset(long tz_offset_arg):
doesn't exists (falls into the spring time-gap).
RETURN VALUE
- Corresponding my_time_t value or 0 in case of error. In case of error
- *in_dst_time_gap is also set.
+ Corresponding my_time_t value or 0 in case of error.
*/
my_time_t
@@ -1446,7 +1456,7 @@ static MEM_ROOT tz_storage;
time zone in offset_tzs or creating if it didn't existed before in
tz_storage. So contention is low.
*/
-static pthread_mutex_t tz_LOCK;
+static mysql_mutex_t tz_LOCK;
static bool tz_inited= 0;
/*
@@ -1546,6 +1556,27 @@ tz_init_table_list(TABLE_LIST *tz_tabs)
}
}
+#ifdef HAVE_PSI_INTERFACE
+static PSI_mutex_key key_tz_LOCK;
+
+static PSI_mutex_info all_tz_mutexes[]=
+{
+ { & key_tz_LOCK, "tz_LOCK", PSI_FLAG_GLOBAL}
+};
+
+static void init_tz_psi_keys(void)
+{
+ const char* category= "sql";
+ int count;
+
+ if (PSI_server == NULL)
+ return;
+
+ count= array_elements(all_tz_mutexes);
+ PSI_server->register_mutex(category, all_tz_mutexes, count);
+}
+#endif /* HAVE_PSI_INTERFACE */
+
/*
Initialize time zone support infrastructure.
@@ -1577,7 +1608,6 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
{
THD *thd;
TABLE_LIST tz_tables[1+MY_TZ_TABLES_COUNT];
- Open_tables_state open_tables_state_backup;
TABLE *table;
Tz_names_entry *tmp_tzname;
my_bool return_val= 1;
@@ -1585,6 +1615,10 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
int res;
DBUG_ENTER("my_tz_init");
+#ifdef HAVE_PSI_INTERFACE
+ init_tz_psi_keys();
+#endif
+
/*
To be able to run this from boot, we allocate a temporary THD
*/
@@ -1592,24 +1626,23 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
DBUG_RETURN(1);
thd->thread_stack= (char*) &thd;
thd->store_globals();
- lex_start(thd);
/* Init all memory structures that require explicit destruction */
- if (hash_init(&tz_names, &my_charset_latin1, 20,
- 0, 0, (hash_get_key) my_tz_names_get_key, 0, 0))
+ if (my_hash_init(&tz_names, &my_charset_latin1, 20,
+ 0, 0, (my_hash_get_key) my_tz_names_get_key, 0, 0))
{
sql_print_error("Fatal error: OOM while initializing time zones");
goto end;
}
- if (hash_init(&offset_tzs, &my_charset_latin1, 26, 0, 0,
- (hash_get_key)my_offset_tzs_get_key, 0, 0))
+ if (my_hash_init(&offset_tzs, &my_charset_latin1, 26, 0, 0,
+ (my_hash_get_key)my_offset_tzs_get_key, 0, 0))
{
sql_print_error("Fatal error: OOM while initializing time zones");
- hash_free(&tz_names);
+ my_hash_free(&tz_names);
goto end;
}
- init_alloc_root(&tz_storage, 32 * 1024, 0);
- VOID(pthread_mutex_init(&tz_LOCK, MY_MUTEX_INIT_FAST));
+ init_sql_alloc(&tz_storage, 32 * 1024, 0);
+ mysql_mutex_init(key_tz_LOCK, &tz_LOCK, MY_MUTEX_INIT_FAST);
tz_inited= 1;
/* Add 'SYSTEM' time zone to tz_names hash */
@@ -1651,20 +1684,29 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
tz_init_table_list(tz_tables+1);
tz_tables[0].next_global= tz_tables[0].next_local= &tz_tables[1];
tz_tables[1].prev_global= &tz_tables[0].next_global;
+ init_mdl_requests(tz_tables);
/*
We need to open only mysql.time_zone_leap_second, but we try to
open all time zone tables to see if they exist.
*/
- if (open_system_tables_for_read(thd, tz_tables, &open_tables_state_backup))
+ if (open_and_lock_tables(thd, tz_tables, FALSE,
+ MYSQL_OPEN_IGNORE_FLUSH | MYSQL_LOCK_IGNORE_TIMEOUT))
{
sql_print_warning("Can't open and lock time zone table: %s "
- "trying to live without them", thd->main_da.message());
+ "trying to live without them", thd->stmt_da->message());
/* We will try emulate that everything is ok */
return_val= time_zone_tables_exist= 0;
goto end_with_setting_default_tz;
}
+ for (TABLE_LIST *tl= tz_tables; tl; tl= tl->next_global)
+ {
+ tl->table->use_all_columns();
+ /* Force close at the end of the function to free memory. */
+ tl->table->m_needs_reopen= TRUE;
+ }
+
/*
Now we are going to load leap seconds descriptions that are shared
between all time zones that use them. We are using index for getting
@@ -1680,14 +1722,11 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
}
table= tz_tables[0].table;
- /*
- It is OK to ignore ha_index_init()/ha_index_end() return values since
- mysql.time_zone* tables are MyISAM and these operations always succeed
- for MyISAM.
- */
- (void)table->file->ha_index_init(0, 1);
- table->use_all_columns();
+ if (table->file->ha_index_init(0, 1))
+ goto end_with_close;
+
+ table->use_all_columns();
tz_leapcnt= 0;
res= table->file->ha_index_first(table->record[0]);
@@ -1751,10 +1790,7 @@ end_with_setting_default_tz:
end_with_close:
if (time_zone_tables_exist)
- {
- thd->version--; /* Force close to free memory */
- close_system_tables(thd, &open_tables_state_backup);
- }
+ close_mysql_tables(thd);
end_with_cleanup:
@@ -1771,6 +1807,10 @@ end:
my_pthread_setspecific_ptr(THR_THD, 0);
my_pthread_setspecific_ptr(THR_MALLOC, 0);
}
+
+ default_tz= default_tz_name ? global_system_variables.time_zone
+ : my_tz_SYSTEM;
+
DBUG_RETURN(return_val);
}
@@ -1787,9 +1827,9 @@ void my_tz_free()
if (tz_inited)
{
tz_inited= 0;
- VOID(pthread_mutex_destroy(&tz_LOCK));
- hash_free(&offset_tzs);
- hash_free(&tz_names);
+ mysql_mutex_destroy(&tz_LOCK);
+ my_hash_free(&offset_tzs);
+ my_hash_free(&tz_names);
free_root(&tz_storage, MYF(0));
}
}
@@ -1859,12 +1899,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
tz_tables= tz_tables->next_local;
table->field[0]->store(tz_name->ptr(), tz_name->length(),
&my_charset_latin1);
- /*
- It is OK to ignore ha_index_init()/ha_index_end() return values since
- mysql.time_zone* tables are MyISAM and these operations always succeed
- for MyISAM.
- */
- (void)table->file->ha_index_init(0, 1);
+ if (table->file->ha_index_init(0, 1))
+ goto end;
if (table->file->ha_index_read_map(table->record[0], table->field[0]->ptr,
HA_WHOLE_KEY, HA_READ_KEY_EXACT))
@@ -1897,7 +1933,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
field->get_key_image(keybuff,
min(field->key_length(), sizeof(keybuff)),
Field::itRAW);
- (void)table->file->ha_index_init(0, 1);
+ if (table->file->ha_index_init(0, 1))
+ goto end;
if (table->file->ha_index_read_map(table->record[0], keybuff,
HA_WHOLE_KEY, HA_READ_KEY_EXACT))
@@ -1929,7 +1966,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
field->get_key_image(keybuff,
min(field->key_length(), sizeof(keybuff)),
Field::itRAW);
- (void)table->file->ha_index_init(0, 1);
+ if (table->file->ha_index_init(0, 1))
+ goto end;
res= table->file->ha_index_read_map(table->record[0], keybuff,
(key_part_map)1, HA_READ_KEY_EXACT);
@@ -1999,7 +2037,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
*/
table= tz_tables->table;
table->field[0]->store((longlong) tzid, TRUE);
- (void)table->file->ha_index_init(0, 1);
+ if (table->file->ha_index_init(0, 1))
+ goto end;
res= table->file->ha_index_read_map(table->record[0], keybuff,
(key_part_map)1, HA_READ_KEY_EXACT);
@@ -2133,8 +2172,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
end:
- if (table)
- (void)table->file->ha_index_end();
+ if (table && table->file->inited)
+ (void) table->file->ha_index_end();
DBUG_RETURN(return_val);
}
@@ -2271,14 +2310,13 @@ my_tz_find(THD *thd, const String *name)
if (!name || name->is_empty())
DBUG_RETURN(0);
- VOID(pthread_mutex_lock(&tz_LOCK));
+ mysql_mutex_lock(&tz_LOCK);
if (!str_to_offset(name->ptr(), name->length(), &offset))
{
-
- if (!(result_tz= (Time_zone_offset *)hash_search(&offset_tzs,
- (const uchar *)&offset,
- sizeof(long))))
+ if (!(result_tz= (Time_zone_offset *)my_hash_search(&offset_tzs,
+ (const uchar *)&offset,
+ sizeof(long))))
{
DBUG_PRINT("info", ("Creating new Time_zone_offset object"));
@@ -2294,16 +2332,18 @@ my_tz_find(THD *thd, const String *name)
else
{
result_tz= 0;
- if ((tmp_tzname= (Tz_names_entry *)hash_search(&tz_names,
- (const uchar *)name->ptr(),
- name->length())))
+ if ((tmp_tzname= (Tz_names_entry *)my_hash_search(&tz_names,
+ (const uchar *)
+ name->ptr(),
+ name->length())))
result_tz= tmp_tzname->tz;
else if (time_zone_tables_exist)
{
TABLE_LIST tz_tables[MY_TZ_TABLES_COUNT];
- Open_tables_state open_tables_state_backup;
+ Open_tables_backup open_tables_state_backup;
tz_init_table_list(tz_tables);
+ init_mdl_requests(tz_tables);
if (!open_system_tables_for_read(thd, tz_tables,
&open_tables_state_backup))
{
@@ -2313,7 +2353,10 @@ my_tz_find(THD *thd, const String *name)
}
}
- VOID(pthread_mutex_unlock(&tz_LOCK));
+ mysql_mutex_unlock(&tz_LOCK);
+
+ if (result_tz && result_tz != my_tz_SYSTEM && result_tz != my_tz_UTC)
+ status_var_increment(thd->status_var.feature_timezone);
DBUG_RETURN(result_tz);
}
@@ -2347,7 +2390,6 @@ void Time_zone::adjust_leap_second(MYSQL_TIME *t)
tables.
*/
-
/*
Print info about time zone described by TIME_ZONE_INFO struct as
SQL statements populating mysql.time_zone* tables.
@@ -2432,6 +2474,15 @@ MEM_ROOT tz_storage;
char fullname[FN_REFLEN + 1];
char *root_name_end;
+/*
+ known file types that exist in the zoneinfo directory that are safe to
+ silently skip
+*/
+const char *known_extensions[]= {
+ ".tab",
+ NullS
+};
+
/*
Recursively scan zoneinfo directory and print all found time zone
@@ -2440,6 +2491,8 @@ char *root_name_end;
SYNOPSIS
scan_tz_dir()
name_end - pointer to end of path to directory to be searched.
+ symlink_recursion_level How many symlink directory levels are used
+ verbose >0 if we should print warnings
DESCRIPTION
This auxiliary recursive function also uses several global
@@ -2455,7 +2508,7 @@ char *root_name_end;
*/
my_bool
-scan_tz_dir(char * name_end, uint symlink_recursion_level)
+scan_tz_dir(char * name_end, uint symlink_recursion_level, uint verbose)
{
MY_DIR *cur_dir;
char *name_end_tmp;
@@ -2495,12 +2548,21 @@ scan_tz_dir(char * name_end, uint symlink_recursion_level)
following such symlinks infinitely:
/usr/share/zoneinfo/posix/posix/posix/.../posix/
*/
- fflush(stdout);
- fprintf(stderr, "Warning: Skipping directory '%s': "
- "to avoid infinite symlink recursion.\n", fullname);
+
+ /*
+ This is a normal case and not critical. only print warning if
+ verbose mode is choosen.
+ */
+ if (verbose > 0)
+ {
+ fflush(stdout);
+ fprintf(stderr, "Warning: Skipping directory '%s': "
+ "to avoid infinite symlink recursion.\n", fullname);
+ }
continue;
}
- if (scan_tz_dir(name_end_tmp, symlink_recursion_level + is_symlink))
+ if (scan_tz_dir(name_end_tmp, symlink_recursion_level + is_symlink,
+ verbose))
{
my_dirend(cur_dir);
return 1;
@@ -2513,10 +2575,28 @@ scan_tz_dir(char * name_end, uint symlink_recursion_level)
print_tz_as_sql(root_name_end + 1, &tz_info);
else
{
- fflush(stdout);
- fprintf(stderr,
- "Warning: Unable to load '%s' as time zone. Skipping it.\n",
- fullname);
+ /*
+ Some systems (like debian, opensuse etc) have description
+ files (.tab). We skip these silently if verbose is > 0
+ */
+ const char *current_ext= fn_ext(fullname);
+ my_bool known_ext= 0;
+
+ for (const char **ext= known_extensions ; *ext ; ext++)
+ {
+ if (!strcmp(*ext, current_ext))
+ {
+ known_ext= 1;
+ break;
+ }
+ }
+ if (verbose > 0 || !known_ext)
+ {
+ fflush(stdout);
+ fprintf(stderr,
+ "Warning: Unable to load '%s' as time zone. Skipping it.\n",
+ fullname);
+ }
}
free_root(&tz_storage, MYF(0));
}
@@ -2535,35 +2615,114 @@ scan_tz_dir(char * name_end, uint symlink_recursion_level)
}
+my_bool opt_leap, opt_verbose;
+
+static const char *load_default_groups[]=
+{ "mysql_tzinfo_to_sql", 0};
+
+static struct my_option my_long_options[] =
+{
+ {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG,
+ 0, 0, 0, 0, 0, 0},
+#ifdef DBUG_OFF
+ {"debug", '#', "This is a non-debug version. Catch this and exit",
+ 0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
+#else
+ {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
+ 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+#endif
+ {"leap", 'l', "Print the leap second information from the given time zone file. By convention, when --leap is used the next argument is the timezonefile",
+ &opt_leap, &opt_leap, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"verbose", 'v', "Write non critical warnings",
+ &opt_verbose, &opt_verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"version", 'V', "Output version information and exit.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+
+
+C_MODE_START
+static my_bool get_one_option(int optid, const struct my_option *,
+ char *argument);
+C_MODE_END
+
+static void print_version(void)
+{
+ printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname, PROGRAM_VERSION,
+ MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE);
+}
+
+static void print_usage(void)
+{
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr, " %s [options] timezonedir\n", my_progname);
+ fprintf(stderr, " %s [options] timezonefile timezonename\n", my_progname);
+ print_defaults("my",load_default_groups);
+ puts("");
+ my_print_help(my_long_options);
+ my_print_variables(my_long_options);
+}
+
+
+static my_bool
+get_one_option(int optid, const struct my_option *opt, char *argument)
+{
+ switch(optid) {
+ case '#':
+#ifndef DBUG_OFF
+ DBUG_PUSH(argument ? argument : "d:t:S:i:O,/tmp/mysq_tzinfo_to_sql.trace");
+#endif
+ break;
+ case '?':
+ print_version();
+ puts("");
+ print_usage();
+ exit(0);
+ case 'V':
+ print_version();
+ exit(0);
+ }
+ return 0;
+}
+
+
int
main(int argc, char **argv)
{
-#ifndef __NETWARE__
+ char **default_argv;
MY_INIT(argv[0]);
- if (argc != 2 && argc != 3)
+ if (load_defaults("my",load_default_groups,&argc,&argv))
+ exit(1);
+
+ default_argv= argv;
+
+ if ((handle_options(&argc, &argv, my_long_options, get_one_option)))
+ exit(1);
+
+ if ((argc != 1 && argc != 2) || (opt_leap && argc != 1))
{
- fprintf(stderr, "Usage:\n");
- fprintf(stderr, " %s timezonedir\n", argv[0]);
- fprintf(stderr, " %s timezonefile timezonename\n", argv[0]);
- fprintf(stderr, " %s --leap timezonefile\n", argv[0]);
+ print_usage();
+ free_defaults(default_argv);
return 1;
}
-
- if (argc == 2)
+ if (argc == 1 && !opt_leap)
{
- root_name_end= strmake(fullname, argv[1], FN_REFLEN);
+ /* Argument is timezonedir */
+
+ root_name_end= strmake_buf(fullname, argv[0]);
printf("TRUNCATE TABLE time_zone;\n");
printf("TRUNCATE TABLE time_zone_name;\n");
printf("TRUNCATE TABLE time_zone_transition;\n");
printf("TRUNCATE TABLE time_zone_transition_type;\n");
- if (scan_tz_dir(root_name_end, 0))
+ if (scan_tz_dir(root_name_end, 0, opt_verbose))
{
fflush(stdout);
- fprintf(stderr, "There were fatal errors during processing "
- "of zoneinfo directory\n");
+ fprintf(stderr,
+ "There were fatal errors during processing "
+ "of zoneinfo directory '%s'\n", fullname);
return 1;
}
@@ -2574,36 +2733,28 @@ main(int argc, char **argv)
}
else
{
+ /*
+ First argument is timezonefile.
+ The second is timezonename if opt_leap is not given
+ */
init_alloc_root(&tz_storage, 32768, 0);
- if (strcmp(argv[1], "--leap") == 0)
+ if (tz_load(argv[0], &tz_info, &tz_storage))
{
- if (tz_load(argv[2], &tz_info, &tz_storage))
- {
- fflush(stdout);
- fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]);
- return 1;
- }
- print_tz_leaps_as_sql(&tz_info);
+ fflush(stdout);
+ fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[0]);
+ return 1;
}
+ if (opt_leap)
+ print_tz_leaps_as_sql(&tz_info);
else
- {
- if (tz_load(argv[1], &tz_info, &tz_storage))
- {
- fflush(stdout);
- fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]);
- return 1;
- }
- print_tz_as_sql(argv[2], &tz_info);
- }
+ print_tz_as_sql(argv[1], &tz_info);
free_root(&tz_storage, MYF(0));
}
-#else
- fprintf(stderr, "This tool has not been ported to NetWare\n");
-#endif /* __NETWARE__ */
-
+ free_defaults(default_argv);
+ my_end(0);
return 0;
}
@@ -2713,7 +2864,7 @@ main(int argc, char **argv)
(int)t, (int)t1);
/* Let us load time zone description */
- str_end= strmake(fullname, TZDIR, FN_REFLEN);
+ str_end= strmake_buf(fullname, TZDIR);
strmake(str_end, "/MET", FN_REFLEN - (str_end - fullname));
if (tz_load(fullname, &tz_info, &tz_storage))
diff --git a/sql/tztime.h b/sql/tztime.h
index 2b3e9c04aa6..eb7d85c48b2 100644
--- a/sql/tztime.h
+++ b/sql/tztime.h
@@ -1,6 +1,7 @@
-/*
- Copyright (c) 2004-2007 MySQL AB, 2008, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+#ifndef TZTIME_INCLUDED
+#define TZTIME_INCLUDED
+
+/* Copyright (c) 2004, 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
@@ -13,16 +14,24 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifdef USE_PRAGMA_INTERFACE
#pragma interface /* gcc class interface */
#endif
+#include "my_time.h" /* my_time_t */
+#include "mysql_time.h" /* MYSQL_TIME */
+#include "sql_list.h" /* Sql_alloc */
+#include "sql_string.h" /* String */
+
+class THD;
+
#if !defined(TESTTIME) && !defined(TZINFO2SQL)
+class THD;
+
/**
This class represents abstract time zone and provides
basic interface for MYSQL_TIME <-> my_time_t conversion.
@@ -82,3 +91,4 @@ 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 b68a9fd2b79..36a5eafb704 100644
--- a/sql/udf_example.c
+++ b/sql/udf_example.c
@@ -12,8 +12,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
** example file of UDF (user definable functions) that are dynamicly loaded
@@ -109,7 +108,7 @@
** option.
**
** If you can't get AGGREGATES to work, check that you have the column
-** 'type' in the mysql.func table. If not, run 'mysql_fix_privilege_tables'.
+** 'type' in the mysql.func table. If not, run 'mysql_upgrade'.
**
*/
@@ -135,12 +134,16 @@ typedef long long longlong;
#include <string.h>
#define strmov(a,b) stpcpy(a,b)
#define bzero(a,b) memset(a,0,b)
-#define memcpy_fixed(a,b,c) memcpy(a,b,c)
#endif
#endif
#include <mysql.h>
#include <ctype.h>
+#ifdef _WIN32
+/* inet_aton needs winsock library */
+#pragma comment(lib, "ws2_32")
+#endif
+
#ifdef HAVE_DLOPEN
#if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_SOLARIS_STYLE_GETHOST)
@@ -766,16 +769,16 @@ char *lookup(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args,
return 0;
}
#else
- VOID(pthread_mutex_lock(&LOCK_hostname));
+ pthread_mutex_lock(&LOCK_hostname);
if (!(hostent= gethostbyname((char*) name_buff)))
{
- VOID(pthread_mutex_unlock(&LOCK_hostname));
+ pthread_mutex_unlock(&LOCK_hostname);
*null_value= 1;
return 0;
}
- VOID(pthread_mutex_unlock(&LOCK_hostname));
+ pthread_mutex_unlock(&LOCK_hostname);
#endif
- memcpy_fixed((char*) &in,(char*) *hostent->h_addr_list, sizeof(in.s_addr));
+ memcpy(&in, *hostent->h_addr_list, sizeof(in.s_addr));
*res_length= (ulong) (strmov(result, inet_ntoa(in)) - result);
return result;
}
@@ -870,14 +873,14 @@ char *reverse_lookup(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args,
return 0;
}
#else
- VOID(pthread_mutex_lock(&LOCK_hostname));
+ pthread_mutex_lock(&LOCK_hostname);
if (!(hp= gethostbyaddr((char*) &taddr, sizeof(taddr), AF_INET)))
{
- VOID(pthread_mutex_unlock(&LOCK_hostname));
+ pthread_mutex_unlock(&LOCK_hostname);
*null_value= 1;
return 0;
}
- VOID(pthread_mutex_unlock(&LOCK_hostname));
+ pthread_mutex_unlock(&LOCK_hostname);
#endif
*res_length=(ulong) (strmov(result,hp->h_name) - result);
return result;
diff --git a/sql/uniques.cc b/sql/uniques.cc
index 5549d68490c..72411be5cd6 100644
--- a/sql/uniques.cc
+++ b/sql/uniques.cc
@@ -1,6 +1,4 @@
-/*
- Copyright (c) 2001-2003, 2005-2007 MySQL AB, 2009 Sun Microsystems, Inc.
- Use is subject to license terms.
+/* Copyright (c) 2001, 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
@@ -13,8 +11,7 @@
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
-*/
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
Function to handle quick removal of duplicates
@@ -33,8 +30,12 @@
deletes in disk order.
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
#include "sql_sort.h"
+#include "queues.h" // QUEUE
+#include "my_tree.h" // element_count
+#include "sql_class.h" // Unique
int unique_write_to_file(uchar* key, element_count count, Unique *unique)
{
@@ -76,7 +77,10 @@ int unique_intersect_write_to_ptrs(uchar* key, element_count count, Unique *uniq
Unique::Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg,
uint size_arg, ulonglong max_in_memory_size_arg,
uint min_dupl_count_arg)
- :max_in_memory_size(max_in_memory_size_arg), size(size_arg), elements(0)
+ :max_in_memory_size(max_in_memory_size_arg),
+ record_pointers(NULL),
+ size(size_arg),
+ elements(0)
{
min_dupl_count= min_dupl_count_arg;
full_size= size;
@@ -92,8 +96,8 @@ Unique::Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg,
*/
max_elements= (ulong) (max_in_memory_size /
ALIGN_SIZE(sizeof(TREE_ELEMENT)+size));
- VOID(open_cached_file(&file, mysql_tmpdir,TEMP_PREFIX, DISK_BUFFER_SIZE,
- MYF(MY_WME)));
+ (void) open_cached_file(&file, mysql_tmpdir,TEMP_PREFIX, DISK_BUFFER_SIZE,
+ MYF(MY_WME));
}
@@ -617,7 +621,7 @@ bool Unique::walk(TABLE *table, tree_walk_action action, void *walk_action_arg)
action, walk_action_arg,
tree.compare, tree.custom_arg, &file);
}
- my_free((char*) merge_buffer, MYF(0));
+ my_free(merge_buffer);
return res;
}
@@ -743,6 +747,6 @@ bool Unique::get(TABLE *table)
rc= 0;
err:
- x_free(sort_buffer);
+ my_free(sort_buffer);
return rc;
}
diff --git a/sql/unireg.cc b/sql/unireg.cc
index 13b0e044ef8..528c3025c57 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -24,7 +24,11 @@
str is a (long) to record position where 0 is the first position.
*/
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
+#include "sql_partition.h" // struct partition_info
+#include "sql_table.h" // check_duplicate_warning
+#include "sql_class.h" // THD, Internal_error_handler
#include "create_options.h"
#include <m_ctype.h>
#include <assert.h>
@@ -60,10 +64,12 @@ static bool make_empty_rec(THD *thd, int file, enum legacy_db_type table_type,
struct Pack_header_error_handler: public Internal_error_handler
{
- virtual bool handle_error(uint sql_errno,
- const char *message,
- MYSQL_ERROR::enum_warning_level level,
- THD *thd);
+ virtual bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg,
+ MYSQL_ERROR ** cond_hdl);
bool is_handled;
Pack_header_error_handler() :is_handled(FALSE) {}
};
@@ -71,11 +77,14 @@ struct Pack_header_error_handler: public Internal_error_handler
bool
Pack_header_error_handler::
-handle_error(uint sql_errno,
- const char * /* message */,
- MYSQL_ERROR::enum_warning_level /* level */,
- THD * /* thd */)
+handle_condition(THD *,
+ uint sql_errno,
+ const char*,
+ MYSQL_ERROR::enum_warning_level,
+ const char*,
+ MYSQL_ERROR ** cond_hdl)
{
+ *cond_hdl= NULL;
is_handled= (sql_errno == ER_TOO_MANY_FIELDS);
return is_handled;
}
@@ -145,7 +154,7 @@ bool mysql_create_frm(THD *thd, const char *file_name,
if (error)
{
- my_free(screen_buff, MYF(0));
+ my_free(screen_buff);
if (! pack_header_error_handler.is_handled)
DBUG_RETURN(1);
@@ -156,7 +165,7 @@ bool mysql_create_frm(THD *thd, const char *file_name,
create_fields,info_length,
screens, create_info->table_options, data_offset, db_file))
{
- my_free(screen_buff, MYF(0));
+ my_free(screen_buff);
DBUG_RETURN(1);
}
}
@@ -200,33 +209,6 @@ bool mysql_create_frm(THD *thd, const char *file_name,
else
create_info->table_options&= ~HA_OPTION_TEXT_CREATE_OPTIONS;
- if ((file=create_frm(thd, file_name, db, table, reclength, fileinfo,
- create_info, keys)) < 0)
- {
- my_free(screen_buff, MYF(0));
- DBUG_RETURN(1);
- }
-
- key_buff_length= uint4korr(fileinfo+47);
- keybuff=(uchar*) my_malloc(key_buff_length, MYF(0));
- key_info_length= pack_keys(keybuff, keys, key_info, data_offset);
-
- /*
- Ensure that there are no forms in this newly created form file.
- Even if the form file exists, create_frm must truncate it to
- ensure one form per form file.
- */
- DBUG_ASSERT(uint2korr(fileinfo+8) == 0);
-
- if (!(filepos= make_new_entry(file, fileinfo, NULL, "")))
- goto err;
- maxlength=(uint) next_io_size((ulong) (uint2korr(forminfo)+1000));
- int2store(forminfo+2,maxlength);
- int4store(fileinfo+10,(ulong) (filepos+maxlength));
- fileinfo[26]= (uchar) test((create_info->max_rows == 1) &&
- (create_info->min_rows == 1) && (keys == 0));
- int2store(fileinfo+28,key_info_length);
-
/*
This gives us the byte-position of the character at
(character-position, not byte-position) TABLE_COMMENT_MAXLEN.
@@ -240,34 +222,85 @@ bool mysql_create_frm(THD *thd, const char *file_name,
string), the string is too long.
For additional credit, realise that UTF-8 has 1-3 bytes before 6.0,
- and 1-4 bytes in 6.0 (6.0 also has UTF-32). This means that the
- inlined COMMENT supposedly does not exceed 60 character plus
- terminator, vulgo, 181 bytes.
+ and 1-4 bytes in 6.0 (6.0 also has UTF-32).
*/
-
tmp_len= system_charset_info->cset->charpos(system_charset_info,
create_info->comment.str,
create_info->comment.str +
- create_info->comment.length, 60);
+ create_info->comment.length,
+ TABLE_COMMENT_MAXLEN);
+
if (tmp_len < create_info->comment.length)
{
+ char *real_table_name= (char*) table;
+ List_iterator<Create_field> it(create_fields);
+ Create_field *field;
+ while ((field=it++))
+ {
+ if (field->field && field->field->table &&
+ (real_table_name= field->field->table->s->table_name.str))
+ break;
+ }
if ((thd->variables.sql_mode &
(MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)))
{
- my_error(ER_TOO_LONG_TABLE_COMMENT, MYF(0), table,
- static_cast<ulong>(tmp_len));
- goto err;
+ my_error(ER_TOO_LONG_TABLE_COMMENT, MYF(0),
+ real_table_name, static_cast<ulong>(TABLE_COMMENT_MAXLEN));
+ my_free(screen_buff);
+ DBUG_RETURN(1);
}
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_TOO_LONG_TABLE_COMMENT,
- ER(ER_TOO_LONG_TABLE_COMMENT),
- table, static_cast<ulong>(tmp_len));
+ char warn_buff[MYSQL_ERRMSG_SIZE];
+ my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_TABLE_COMMENT),
+ real_table_name, static_cast<ulong>(TABLE_COMMENT_MAXLEN));
+ /* do not push duplicate warnings */
+ if (!check_duplicate_warning(current_thd, warn_buff, strlen(warn_buff)))
+ push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_TOO_LONG_TABLE_COMMENT, warn_buff);
create_info->comment.length= tmp_len;
}
+ /*
+ If table comment is longer than TABLE_COMMENT_INLINE_MAXLEN bytes,
+ store the comment in an extra segment (up to TABLE_COMMENT_MAXLEN bytes).
+ Pre 6.0, the limit was 60 characters, with no extra segment-handling.
+ */
+ if (create_info->comment.length > TABLE_COMMENT_INLINE_MAXLEN)
+ {
+ forminfo[46]=255;
+ create_info->extra_size+= 2 + create_info->comment.length;
+ }
+ else{
+ strmake((char*) forminfo+47, create_info->comment.str ?
+ create_info->comment.str : "", create_info->comment.length);
+ forminfo[46]=(uchar) create_info->comment.length;
+ }
+
+ if ((file=create_frm(thd, file_name, db, table, reclength, fileinfo,
+ create_info, keys, key_info)) < 0)
+ {
+ my_free(screen_buff);
+ DBUG_RETURN(1);
+ }
+
+ key_buff_length= uint4korr(fileinfo+47);
+ keybuff=(uchar*) my_malloc(key_buff_length, MYF(0));
+ key_info_length= pack_keys(keybuff, keys, key_info, data_offset);
+
+ /*
+ Ensure that there are no forms in this newly created form file.
+ Even if the form file exists, create_frm must truncate it to
+ ensure one form per form file.
+ */
+ DBUG_ASSERT(uint2korr(fileinfo+8) == 0);
+
+ if (!(filepos= make_new_entry(file, fileinfo, NULL, "")))
+ goto err;
+ maxlength=(uint) next_io_size((ulong) (uint2korr(forminfo)+1000));
+ int2store(forminfo+2,maxlength);
+ int4store(fileinfo+10,(ulong) (filepos+maxlength));
+ fileinfo[26]= (uchar) test((create_info->max_rows == 1) &&
+ (create_info->min_rows == 1) && (keys == 0));
+ int2store(fileinfo+28,key_info_length);
- strmake((char*) forminfo+47, create_info->comment.str ?
- create_info->comment.str : "", create_info->comment.length);
- forminfo[46]=(uchar) create_info->comment.length;
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (part_info)
{
@@ -277,27 +310,27 @@ bool mysql_create_frm(THD *thd, const char *file_name,
#endif
int2store(fileinfo+59,db_file->extra_rec_buf_length());
- if (my_pwrite(file, fileinfo, 64, 0L, MYF_RW) ||
- my_pwrite(file, keybuff, key_info_length,
- (ulong) uint2korr(fileinfo+6),MYF_RW))
+ if (mysql_file_pwrite(file, fileinfo, 64, 0L, MYF_RW) ||
+ mysql_file_pwrite(file, keybuff, key_info_length,
+ (ulong) uint2korr(fileinfo+6), MYF_RW))
goto err;
- VOID(my_seek(file,
- (ulong) uint2korr(fileinfo+6)+ (ulong) key_buff_length,
- MY_SEEK_SET,MYF(0)));
+ mysql_file_seek(file,
+ (ulong) uint2korr(fileinfo+6) + (ulong) key_buff_length,
+ MY_SEEK_SET, MYF(0));
if (make_empty_rec(thd,file,ha_legacy_type(create_info->db_type),
create_info->table_options,
create_fields,reclength, data_offset, db_file))
goto err;
int2store(buff, create_info->connect_string.length);
- if (my_write(file, (const uchar*)buff, 2, MYF(MY_NABP)) ||
- my_write(file, (const uchar*)create_info->connect_string.str,
+ if (mysql_file_write(file, (const uchar*)buff, 2, MYF(MY_NABP)) ||
+ mysql_file_write(file, (const uchar*)create_info->connect_string.str,
create_info->connect_string.length, MYF(MY_NABP)))
goto err;
int2store(buff, str_db_type.length);
- if (my_write(file, (const uchar*)buff, 2, MYF(MY_NABP)) ||
- my_write(file, (const uchar*)str_db_type.str,
+ if (mysql_file_write(file, (const uchar*)buff, 2, MYF(MY_NABP)) ||
+ mysql_file_write(file, (const uchar*)str_db_type.str,
str_db_type.length, MYF(MY_NABP)))
goto err;
@@ -306,17 +339,17 @@ bool mysql_create_frm(THD *thd, const char *file_name,
{
char auto_partitioned= part_info->is_auto_partitioned ? 1 : 0;
int4store(buff, part_info->part_info_len);
- if (my_write(file, (const uchar*)buff, 4, MYF_RW) ||
- my_write(file, (const uchar*)part_info->part_info_string,
+ if (mysql_file_write(file, (const uchar*)buff, 4, MYF_RW) ||
+ mysql_file_write(file, (const uchar*)part_info->part_info_string,
part_info->part_info_len + 1, MYF_RW) ||
- my_write(file, (const uchar*)&auto_partitioned, 1, MYF_RW))
+ mysql_file_write(file, (const uchar*)&auto_partitioned, 1, MYF_RW))
goto err;
}
else
#endif
{
bzero((uchar*) buff, 6);
- if (my_write(file, (uchar*) buff, 6, MYF_RW))
+ if (mysql_file_write(file, (uchar*) buff, 6, MYF_RW))
goto err;
}
@@ -324,11 +357,20 @@ bool mysql_create_frm(THD *thd, const char *file_name,
{
if (key_info[i].parser_name)
{
- if (my_write(file, (const uchar*)key_info[i].parser_name->str,
- key_info[i].parser_name->length + 1, MYF(MY_NABP)))
+ if (mysql_file_write(file, (const uchar*)key_info[i].parser_name->str,
+ key_info[i].parser_name->length + 1, MYF(MY_NABP)))
goto err;
}
}
+ if (forminfo[46] == (uchar)255)
+ {
+ uchar comment_length_buff[2];
+ int2store(comment_length_buff,create_info->comment.length);
+ if (mysql_file_write(file, comment_length_buff, 2, MYF(MY_NABP)) ||
+ mysql_file_write(file, (uchar*) create_info->comment.str,
+ create_info->comment.length, MYF(MY_NABP)))
+ goto err;
+ }
if (options_len)
{
@@ -348,9 +390,9 @@ bool mysql_create_frm(THD *thd, const char *file_name,
goto err;
}
- VOID(my_seek(file,filepos,MY_SEEK_SET,MYF(0)));
- if (my_write(file, forminfo, 288, MYF_RW) ||
- my_write(file, screen_buff, info_length, MYF_RW) ||
+ mysql_file_seek(file, filepos, MY_SEEK_SET, MYF(0));
+ if (mysql_file_write(file, forminfo, 288, MYF_RW) ||
+ mysql_file_write(file, screen_buff, info_length, MYF_RW) ||
pack_fields(file, create_fields, data_offset))
goto err;
@@ -359,32 +401,32 @@ bool mysql_create_frm(THD *thd, const char *file_name,
{
char tmp=2,*disk_buff=0;
SQL_CRYPT *crypted=new SQL_CRYPT(create_info->password);
- if (!crypted || my_pwrite(file,&tmp,1,26,MYF_RW)) // Mark crypted
+ if (!crypted || mysql_file_pwrite(file, &tmp, 1, 26, MYF_RW))// Mark crypted
goto err;
uint read_length=uint2korr(forminfo)-256;
- VOID(my_seek(file,filepos+256,MY_SEEK_SET,MYF(0)));
+ mysql_file_seek(file, filepos+256, MY_SEEK_SET, MYF(0));
if (read_string(file,(uchar**) &disk_buff,read_length))
goto err;
crypted->encode(disk_buff,read_length);
delete crypted;
- if (my_pwrite(file,disk_buff,read_length,filepos+256,MYF_RW))
+ if (mysql_file_pwrite(file, disk_buff, read_length, filepos+256, MYF_RW))
{
- my_free(disk_buff,MYF(0));
+ my_free(disk_buff);
goto err;
}
- my_free(disk_buff,MYF(0));
+ my_free(disk_buff);
}
#endif
- my_free(screen_buff,MYF(0));
- my_free(keybuff, MYF(0));
+ my_free(screen_buff);
+ my_free(keybuff);
if (opt_sync_frm && !(create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
- (my_sync(file, MYF(MY_WME)) ||
+ (mysql_file_sync(file, MYF(MY_WME)) ||
my_sync_dir_by_file(file_name, MYF(MY_WME))))
goto err2;
- if (my_close(file,MYF(MY_WME)))
+ if (mysql_file_close(file, MYF(MY_WME)))
goto err3;
{
@@ -406,12 +448,12 @@ bool mysql_create_frm(THD *thd, const char *file_name,
DBUG_RETURN(0);
err:
- my_free(screen_buff, MYF(0));
- my_free(keybuff, MYF(0));
+ my_free(screen_buff);
+ my_free(keybuff);
err2:
- VOID(my_close(file,MYF(MY_WME)));
+ (void) mysql_file_close(file, MYF(MY_WME));
err3:
- my_delete(file_name,MYF(0));
+ mysql_file_delete(key_file_frm, file_name, MYF(0));
DBUG_RETURN(1);
} /* mysql_create_frm */
@@ -463,8 +505,8 @@ int rea_create_table(THD *thd, const char *path,
DBUG_RETURN(0);
err_handler:
- VOID(file->ha_create_handler_files(path, NULL, CHF_DELETE_FLAG, create_info));
- my_delete(frm_name, MYF(0));
+ (void) file->ha_create_handler_files(path, NULL, CHF_DELETE_FLAG, create_info);
+ mysql_file_delete(key_file_frm, frm_name, MYF(0));
DBUG_RETURN(1);
} /* rea_create_table */
@@ -599,6 +641,16 @@ static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo,
pos=tmp;
}
*(pos++)=0;
+ for (key=keyinfo,end=keyinfo+key_count ; key != end ; key++)
+ {
+ if (key->flags & HA_USES_COMMENT)
+ {
+ int2store(pos, key->comment.length);
+ uchar *tmp= (uchar*)strnmov((char*) pos+2,key->comment.str,
+ key->comment.length);
+ pos= tmp;
+ }
+ }
if (key_count > 127 || key_parts > 127)
{
@@ -652,20 +704,24 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
field->comment.str,
field->comment.str +
field->comment.length,
- 255);
+ COLUMN_COMMENT_MAXLEN);
if (tmp_len < field->comment.length)
{
if ((current_thd->variables.sql_mode &
(MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)))
{
my_error(ER_TOO_LONG_FIELD_COMMENT, MYF(0), field->field_name,
- static_cast<ulong>(tmp_len));
+ static_cast<ulong>(COLUMN_COMMENT_MAXLEN));
DBUG_RETURN(1);
}
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_TOO_LONG_FIELD_COMMENT,
- ER(ER_TOO_LONG_FIELD_COMMENT),
- field->field_name, static_cast<ulong>(tmp_len));
+ char warn_buff[MYSQL_ERRMSG_SIZE];
+ my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_FIELD_COMMENT),
+ field->field_name,
+ static_cast<ulong>(COLUMN_COMMENT_MAXLEN));
+ /* do not push duplicate warnings */
+ if (!check_duplicate_warning(current_thd, warn_buff, strlen(warn_buff)))
+ push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_TOO_LONG_FIELD_COMMENT, warn_buff);
field->comment.length= tmp_len;
}
if (field->vcol_info)
@@ -866,20 +922,27 @@ static bool pack_fields(File file, List<Create_field> &create_fields,
recpos= field->offset+1 + (uint) data_offset;
int3store(buff+5,recpos);
int2store(buff+8,field->pack_flag);
- int2store(buff+10,field->unireg_check);
+ DBUG_ASSERT(field->unireg_check < 256);
+ 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[11]= 0;
buff[14]= (uchar) field->geom_type;
#ifndef HAVE_SPATIAL
DBUG_ASSERT(0); // Should newer happen
#endif
}
else if (field->charset)
+ {
+ buff[11]= (uchar) (field->charset->number >> 8);
buff[14]= (uchar) field->charset->number;
+ }
else
- buff[14]= 0; // Numerical
+ {
+ buff[11]= buff[14]= 0; // Numerical
+ }
if (field->vcol_info)
{
/*
@@ -895,23 +958,23 @@ static bool pack_fields(File file, List<Create_field> &create_fields,
int2store(buff+15, field->comment.length);
comment_length+= field->comment.length;
set_if_bigger(int_count,field->interval_id);
- if (my_write(file, buff, FCOMP, MYF_RW))
+ if (mysql_file_write(file, buff, FCOMP, MYF_RW))
DBUG_RETURN(1);
}
/* Write fieldnames */
buff[0]=(uchar) NAMES_SEP_CHAR;
- if (my_write(file, buff, 1, MYF_RW))
+ if (mysql_file_write(file, buff, 1, MYF_RW))
DBUG_RETURN(1);
i=0;
it.rewind();
while ((field=it++))
{
char *pos= strmov((char*) buff,field->field_name);
- *pos++=NAMES_SEP_CHAR;
+ * (uchar*) pos++= (uchar) NAMES_SEP_CHAR;
if (i == create_fields.elements-1)
*pos++=0;
- if (my_write(file, buff, (size_t) (pos-(char*) buff),MYF_RW))
+ if (mysql_file_write(file, buff, (size_t) (pos-(char*) buff), MYF_RW))
DBUG_RETURN(1);
i++;
}
@@ -971,7 +1034,7 @@ static bool pack_fields(File file, List<Create_field> &create_fields,
tmp.append('\0'); // End of intervall
}
}
- if (my_write(file,(uchar*) tmp.ptr(),tmp.length(),MYF_RW))
+ if (mysql_file_write(file, (uchar*) tmp.ptr(), tmp.length(), MYF_RW))
DBUG_RETURN(1);
}
if (comment_length)
@@ -981,8 +1044,8 @@ static bool pack_fields(File file, List<Create_field> &create_fields,
while ((field=it++))
{
if (field->comment.length)
- if (my_write(file, (uchar*) field->comment.str, field->comment.length,
- MYF_RW))
+ if (mysql_file_write(file, (uchar*) field->comment.str,
+ field->comment.length, MYF_RW))
DBUG_RETURN(1);
}
}
@@ -1135,10 +1198,10 @@ static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type,
if (null_count & 7)
*(null_pos + null_count / 8)|= ~(((uchar) 1 << (null_count & 7)) - 1);
- error= my_write(file, buff, (size_t) reclength,MYF_RW) != 0;
+ error= mysql_file_write(file, buff, (size_t) reclength, MYF_RW) != 0;
err:
- my_free(buff, MYF(MY_FAE));
+ my_free(buff);
thd->count_cuted_fields= old_count_cuted_fields;
DBUG_RETURN(error);
} /* make_empty_rec */
diff --git a/sql/unireg.h b/sql/unireg.h
index 01aac50670a..a35c25f92ee 100644
--- a/sql/unireg.h
+++ b/sql/unireg.h
@@ -1,3 +1,6 @@
+#ifndef UNIREG_INCLUDED
+#define UNIREG_INCLUDED
+
/*
Copyright (c) 2000, 2011, Oracle and/or its affiliates.
@@ -12,13 +15,15 @@
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
-*/
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+
+#include "my_global.h" /* ulonglong */
+#include "mysql_version.h" /* FRM_VER */
/* Extra functions used by unireg library */
-#ifndef _unireg_h
+typedef struct st_ha_create_information HA_CREATE_INFO;
#ifndef NO_ALARM_LOOP
#define NO_ALARM_LOOP /* lib5 and popen can't use alarm */
@@ -41,79 +46,15 @@
#define PLUGINDIR "lib/plugin"
#endif
-#define ER(X) errmesg[(X) - ER_ERROR_FIRST]
-#define ER_SAFE(X) (((X) >= ER_ERROR_FIRST && (X) <= ER_ERROR_LAST) ? ER(X) : "Invalid error code")
-
+#define CURRENT_THD_ERRMSGS current_thd->variables.lc_messages->errmsgs->errmsgs
+#define DEFAULT_ERRMSGS my_default_lc_messages->errmsgs->errmsgs
-#define ERRMAPP 1 /* Errormap f|r my_error */
-#define LIBLEN FN_REFLEN-FN_LEN /* Max l{ngd p} dev */
-/* extra 4+4 bytes for slave tmp tables */
-#define MAX_DBKEY_LENGTH (NAME_LEN*2+1+1+4+4)
-#define MAX_ALIAS_NAME 256
-#define MAX_FIELD_NAME 34 /* Max colum name length +2 */
-#define MAX_SYS_VAR_LENGTH 32
-#define MAX_KEY MAX_INDEXES /* Max used keys */
-#define MAX_REF_PARTS 32 /* Max parts used as ref */
-#define MAX_KEY_LENGTH 3072 /* max possible key */
-#if SIZEOF_OFF_T > 4
-#define MAX_REFLENGTH 8 /* Max length for record ref */
-#else
-#define MAX_REFLENGTH 4 /* Max length for record ref */
-#endif
-#define MAX_HOSTNAME 61 /* len+1 in mysql.user */
-
-#define MAX_MBWIDTH 3 /* Max multibyte sequence */
-#define MAX_FIELD_CHARLENGTH 255
-#define MAX_FIELD_VARCHARLENGTH 65535
-#define MAX_FIELD_BLOBLENGTH UINT_MAX32 /* cf field_blob::get_length() */
-#define CONVERT_IF_BIGGER_TO_BLOB 512 /* Used for CREATE ... SELECT */
-
-/* Max column width +1 */
-#define MAX_FIELD_WIDTH (MAX_FIELD_CHARLENGTH*MAX_MBWIDTH+1)
-
-#define MAX_BIT_FIELD_LENGTH 64 /* Max length in bits for bit fields */
-
-#define MAX_DATE_WIDTH 10 /* YYYY-MM-DD */
-#define MIN_TIME_WIDTH 10 /* -HHH:MM:SS */
-#define MAX_TIME_WIDTH 16 /* -DDDDDD HH:MM:SS */
-#define MAX_TIME_FULL_WIDTH 23 /* -DDDDDD HH:MM:SS.###### */
-#define MAX_DATETIME_FULL_WIDTH 29 /* YYYY-MM-DD HH:MM:SS.###### AM */
-#define MAX_DATETIME_WIDTH 19 /* YYYY-MM-DD HH:MM:SS */
-#define MAX_DATETIME_COMPRESSED_WIDTH 14 /* YYYYMMDDHHMMSS */
-#define MAX_DATETIME_PRECISION 6
-
-#define MAX_TABLES (sizeof(table_map)*8-3) /* Max tables in join */
-#define PARAM_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-3))
-#define OUTER_REF_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-2))
-#define RAND_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-1))
-#define PSEUDO_TABLE_BITS (PARAM_TABLE_BIT | OUTER_REF_TABLE_BIT | \
- RAND_TABLE_BIT)
-#define MAX_FIELDS 4096 /* Limit in the .frm file */
-#define MAX_PARTITIONS 1024
-
-#define MAX_SELECT_NESTING (sizeof(nesting_map)*8-1)
-
-#define MAX_SORT_MEMORY (2048*1024-MALLOC_OVERHEAD)
-#define MIN_SORT_MEMORY (1024-MALLOC_OVERHEAD)
-
-/* Memory allocated when parsing a statement / saving a statement */
-#define MEM_ROOT_BLOCK_SIZE 8192
-#define MEM_ROOT_PREALLOC 8192
-#define TRANS_MEM_ROOT_BLOCK_SIZE 4096
-#define TRANS_MEM_ROOT_PREALLOC 4096
-
-#define DEFAULT_ERROR_COUNT 64
-#define EXTRA_RECORDS 10 /* Extra records in sort */
-#define SCROLL_EXTRA 5 /* Extra scroll-rows. */
-#define FIELD_NAME_USED ((uint) 32768) /* Bit set if fieldname used */
-#define FORM_NAME_USED ((uint) 16384) /* Bit set if formname used */
-#define FIELD_NR_MASK 16383 /* To get fieldnumber */
-#define FERR -1 /* Error from my_functions */
-#define CREATE_MODE 0 /* Default mode on new files */
-#define NAMES_SEP_CHAR '\377' /* Char to sep. names */
-
-#define READ_RECORD_BUFFER (uint) (IO_SIZE*8) /* Pointer_buffer_size */
-#define DISK_BUFFER_SIZE (uint) (IO_SIZE*16) /* Size of diskbuffer */
+#define ER(X) CURRENT_THD_ERRMSGS[(X) - ER_ERROR_FIRST]
+#define ER_DEFAULT(X) DEFAULT_ERRMSGS[(X) - ER_ERROR_FIRST]
+#define ER_SAFE(X) (((X) >= ER_ERROR_FIRST && (X) <= ER_ERROR_LAST) ? ER(X) : "Invalid error code")
+#define ER_THD(thd,X) ((thd)->variables.lc_messages->errmsgs->errmsgs[(X) - \
+ ER_ERROR_FIRST])
+#define ER_THD_OR_DEFAULT(thd,X) ((thd) ? ER_THD(thd, X) : ER_DEFAULT(X))
#define ME_INFO (ME_HOLDTANG+ME_OLDWIN+ME_NOREFRESH)
#define ME_ERROR (ME_BELL+ME_OLDWIN+ME_NOREFRESH)
@@ -126,7 +67,7 @@
#define SPECIAL_SAME_DB_NAME 16 /* form name = file name */
#define SPECIAL_ENGLISH 32 /* English error messages */
#define SPECIAL_NO_RESOLVE 64 /* Don't use gethostname */
-#define SPECIAL_NO_PRIOR 128 /* Don't prioritize threads */
+#define SPECIAL_NO_PRIOR 128 /* Obsolete */
#define SPECIAL_BIG_SELECTS 256 /* Don't use heap tables */
#define SPECIAL_NO_HOST_CACHE 512 /* Don't cache hosts */
#define SPECIAL_SHORT_LOG_FORMAT 1024
@@ -194,11 +135,15 @@
*/
#define OPTIMIZE_I_S_TABLE OPEN_VIEW_FULL*2
+/*
+ The flag means that we need to process trigger files only.
+*/
+#define OPEN_TRIGGER_ONLY OPTIMIZE_I_S_TABLE*2
+
#define SC_INFO_LENGTH 4 /* Form format constant */
#define TE_INFO_LENGTH 3
#define MTYP_NOEMPTY_BIT 128
-#define FRM_VER_TRUE_VARCHAR (FRM_VER+4) /* 10 */
/*
Minimum length pattern before Turbo Boyer-Moore is used
for SELECT "text" LIKE "%pattern%", excluding the two
@@ -216,13 +161,23 @@
#define DEFAULT_KEY_CACHE_NAME "default"
-/* The length of the header part for each virtual column in the .frm file */
-#define FRM_VCOL_HEADER_SIZE(b) (3 + test(b))
-
/* Include prototypes for unireg */
#include "mysqld_error.h"
#include "structs.h" /* All structs we need */
-
+#include "sql_list.h" /* List<> */
+#include "field.h" /* Create_field */
+
+bool mysql_create_frm(THD *thd, const char *file_name,
+ const char *db, const char *table,
+ HA_CREATE_INFO *create_info,
+ List<Create_field> &create_field,
+ uint key_count,KEY *key_info,handler *db_type);
+int rea_create_table(THD *thd, const char *path,
+ const char *db, const char *table_name,
+ HA_CREATE_INFO *create_info,
+ List<Create_field> &create_field,
+ uint key_count,KEY *key_info,
+ handler *file);
#endif
diff --git a/sql/winservice.c b/sql/winservice.c
index f70f8018509..1cf9f8d7823 100644
--- a/sql/winservice.c
+++ b/sql/winservice.c
@@ -1,4 +1,20 @@
/*
+ Copyright (c) 2011, 2012, Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
Get Properties of an existing mysqld Windows service
*/
@@ -8,7 +24,7 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
-
+#include <shellapi.h>
/*
Get version from an executable file
diff --git a/sql/winservice.h b/sql/winservice.h
index 8957413783f..fca7b129de5 100644
--- a/sql/winservice.h
+++ b/sql/winservice.h
@@ -1,4 +1,20 @@
/*
+ Copyright (c) 2011, 2012, Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
Extract properties of a windows service binary path
*/
#ifdef __cplusplus